// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "chrome/browser/sync/sync_setup_wizard.h"

#include "base/json/json_writer.h"
#include "base/memory/scoped_ptr.h"
#include "base/stl_util.h"
#include "base/utf_string_conversions.h"
#include "chrome/browser/prefs/pref_service.h"
#include "chrome/browser/prefs/pref_service_mock_builder.h"
#include "chrome/browser/prefs/testing_pref_store.h"
#include "chrome/browser/signin/signin_manager_fake.h"
#include "chrome/browser/sync/profile_sync_components_factory_mock.h"
#include "chrome/browser/sync/profile_sync_service.h"
#include "chrome/browser/sync/profile_sync_service_factory.h"
#include "chrome/browser/sync/sync_setup_flow.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/browser/ui/browser_list.h"
#include "chrome/browser/ui/webui/options/options_sync_setup_handler.h"
#include "chrome/common/net/gaia/google_service_auth_error.h"
#include "chrome/common/pref_names.h"
#include "chrome/test/base/browser_with_test_window_test.h"
#include "chrome/test/base/test_browser_window.h"
#include "chrome/test/base/testing_profile.h"
#include "testing/gtest/include/gtest/gtest.h"

static const char kTestUser[] = "chrome.p13n.test@gmail.com";
static const char kTestPassword[] = "passwd";
static const char kTestCaptcha[] = "pizzamyheart";
static const char kTestCaptchaUrl[] = "http://pizzamyheart/";

typedef GoogleServiceAuthError AuthError;

class MockSyncSetupHandler : public OptionsSyncSetupHandler {
 public:
  MockSyncSetupHandler() : OptionsSyncSetupHandler(NULL) {}

  // SyncSetupFlowHandler implementation.
  virtual void ShowGaiaLogin(const DictionaryValue& args) OVERRIDE {}
  virtual void ShowGaiaSuccessAndClose() OVERRIDE {
    flow()->OnDialogClosed("");
  }
  virtual void ShowGaiaSuccessAndSettingUp() OVERRIDE {}
  virtual void ShowConfigure(const DictionaryValue& args) OVERRIDE {}
  virtual void ShowPassphraseEntry(const DictionaryValue& args) OVERRIDE {}
  virtual void ShowSettingUp() OVERRIDE {}
  virtual void ShowSetupDone(const string16& user) OVERRIDE {
    flow()->OnDialogClosed("");
  }

  void CloseSetupUI() {
    ShowSetupDone(string16());
  }

 private:
  DISALLOW_COPY_AND_ASSIGN(MockSyncSetupHandler);
};

class SigninManagerMock : public FakeSigninManager {
 public:
  SigninManagerMock() : auth_error_(AuthError::None()) {}

  virtual void StartSignIn(const std::string& username,
                           const std::string& password,
                           const std::string& token,
                           const std::string& captcha) OVERRIDE {
    FakeSigninManager::StartSignIn(username, password, token, captcha);
    username_ = username;
    password_ = password;
    captcha_ = captcha;
  }

  virtual const AuthError& GetLoginAuthError() const OVERRIDE {
    return auth_error_;
  }

  void ResetTestStats() {
    username_.clear();
    password_.clear();
    captcha_.clear();
  }

  AuthError auth_error_;
  std::string username_;
  std::string password_;
  std::string captcha_;
};

// A PSS subtype to inject.
class ProfileSyncServiceForWizardTest : public ProfileSyncService {
 public:
  virtual ~ProfileSyncServiceForWizardTest() {}

  static ProfileKeyedBase* BuildManual(Profile* profile) {
    return new ProfileSyncServiceForWizardTest(profile,
        ProfileSyncService::MANUAL_START);
  }

  static ProfileKeyedBase* BuildAuto(Profile* profile) {
    return new ProfileSyncServiceForWizardTest(profile,
        ProfileSyncService::AUTO_START);
  }

  virtual void OnUserChoseDatatypes(
      bool sync_everything, syncable::ModelTypeSet chosen_types) OVERRIDE {
    user_chose_data_types_ = true;
    chosen_data_types_ = chosen_types;
  }

  virtual void OnUserCancelledDialog() OVERRIDE {
    user_cancelled_dialog_ = true;
  }

  virtual void SetPassphrase(const std::string& passphrase,
                             PassphraseType type,
                             PassphraseSource source) OVERRIDE {
    passphrase_ = passphrase;
  }

  virtual bool IsUsingSecondaryPassphrase() const OVERRIDE {
    return is_using_secondary_passphrase_;
  }

  void set_last_auth_error(const AuthError& error) {
    // Set the cached auth error in the SigninManager and ProfileSyncService.
    last_auth_error_ = error;
    mock_signin_.auth_error_ = error;
  }

  void set_is_using_secondary_passphrase(bool secondary) {
    is_using_secondary_passphrase_ = secondary;
  }

  void set_passphrase_required_reason(
      sync_api::PassphraseRequiredReason reason) {
    passphrase_required_reason_ = reason;
  }

  virtual bool sync_initialized() const OVERRIDE {
    return true;
  }

  virtual bool EncryptEverythingEnabled() const OVERRIDE {
    return encrypt_everything_;
  }

  void set_encrypt_everything(bool encrypt_everything) {
    encrypt_everything_ = encrypt_everything;
  }

  void ResetTestStats() {
    mock_signin_.ResetTestStats();
    user_cancelled_dialog_ = false;
    user_chose_data_types_ = false;
    keep_everything_synced_ = false;
    chosen_data_types_.Clear();
  }

  virtual void ShowSyncSetup(const std::string& sub_page) OVERRIDE {
    // Do Nothing.
  }

  void ClearObservers() {
    observers_.Clear();
  }

  SyncSetupWizard* GetWizard() {
    return &wizard_;
  }

  SigninManagerMock mock_signin_;
  bool user_cancelled_dialog_;
  bool user_chose_data_types_;
  bool keep_everything_synced_;
  bool is_using_secondary_passphrase_;
  bool encrypt_everything_;
  syncable::ModelTypeSet chosen_data_types_;
  std::string passphrase_;

 private:
  ProfileSyncServiceForWizardTest(Profile* profile,
                                  ProfileSyncService::StartBehavior behavior)
      : ProfileSyncService(NULL, profile, NULL, behavior),
        user_cancelled_dialog_(false),
        is_using_secondary_passphrase_(false),
        encrypt_everything_(false) {
    signin_ = &mock_signin_;
    ResetTestStats();
  }

  DISALLOW_COPY_AND_ASSIGN(ProfileSyncServiceForWizardTest);
};

// TODO(jhawkins): Subclass Browser (specifically, ShowOptionsTab) and inject it
// here to test the visibility of the Sync UI.
class SyncSetupWizardTest : public BrowserWithTestWindowTest {
 public:
  SyncSetupWizardTest()
      : wizard_(NULL),
        service_(NULL),
        flow_(NULL) {}
  virtual ~SyncSetupWizardTest() {}
  virtual TestingProfile* BuildProfile() {
    TestingProfile* profile = new TestingProfile();
    ProfileSyncServiceFactory::GetInstance()->SetTestingFactory(profile,
        ProfileSyncServiceForWizardTest::BuildManual);
    return profile;
  }
  virtual void SetUp() {
    set_profile(BuildProfile());
    profile()->CreateBookmarkModel(false);
    // Wait for the bookmarks model to load.
    profile()->BlockUntilBookmarkModelLoaded();
    PrefService* prefs = profile()->GetPrefs();
    prefs->SetString(prefs::kGoogleServicesUsername, kTestUser);

    set_browser(new Browser(Browser::TYPE_TABBED, profile()));
    browser()->SetWindowForTesting(window());
    BrowserList::SetLastActive(browser());
    service_ = static_cast<ProfileSyncServiceForWizardTest*>(
        ProfileSyncServiceFactory::GetInstance()->GetForProfile(profile()));
    wizard_ = service_->GetWizard();
  }

  virtual void TearDown() {
    wizard_ = NULL;
    service_ = NULL;
    flow_ = NULL;
  }

 protected:
  void AttachSyncSetupHandler() {
    flow_ = wizard_->AttachSyncSetupHandler(&handler_);
  }

  void CompleteSetup() {
    // For a discrete run, we need to have ran through setup once.
    wizard_->Step(SyncSetupWizard::GAIA_LOGIN);
    AttachSyncSetupHandler();
    wizard_->Step(SyncSetupWizard::GAIA_SUCCESS);
    wizard_->Step(SyncSetupWizard::SYNC_EVERYTHING);
    wizard_->Step(SyncSetupWizard::SETTING_UP);
    wizard_->Step(SyncSetupWizard::DONE);
  }

  void CloseSetupUI() {
    handler_.CloseSetupUI();
  }

  // This pointer is owned by the |Service_|.
  SyncSetupWizard* wizard_;
  ProfileSyncServiceForWizardTest* service_;
  SyncSetupFlow* flow_;
  MockSyncSetupHandler handler_;
};

TEST_F(SyncSetupWizardTest, InitialStepLogin) {
  ListValue credentials;
  std::string auth = "{\"user\":\"";
  auth += std::string(kTestUser) + "\",\"pass\":\"";
  auth += std::string(kTestPassword) + "\",\"captcha\":\"";
  auth += std::string(kTestCaptcha) + "\",\"access_code\":\"";
  auth += std::string() + "\"}";
  credentials.Append(new StringValue(auth));

  EXPECT_FALSE(wizard_->IsVisible());
  EXPECT_EQ(static_cast<SyncSetupFlow*>(NULL), flow_);
  wizard_->Step(SyncSetupWizard::GAIA_LOGIN);
  AttachSyncSetupHandler();

  EXPECT_TRUE(wizard_->IsVisible());
  EXPECT_EQ(SyncSetupWizard::GAIA_LOGIN, flow_->current_state_);
  EXPECT_EQ(SyncSetupWizard::DONE, flow_->end_state_);

  // Simulate the user submitting credentials.
  handler_.HandleSubmitAuth(&credentials);
  EXPECT_TRUE(wizard_->IsVisible());
  EXPECT_EQ(SyncSetupWizard::GAIA_LOGIN, flow_->current_state_);
  EXPECT_EQ(kTestUser, service_->mock_signin_.username_);
  EXPECT_EQ(kTestPassword, service_->mock_signin_.password_);
  EXPECT_EQ(kTestCaptcha, service_->mock_signin_.captcha_);
  EXPECT_FALSE(service_->user_cancelled_dialog_);
  service_->ResetTestStats();

  // Simulate failed credentials.
  AuthError invalid_gaia(AuthError::INVALID_GAIA_CREDENTIALS);
  flow_->last_attempted_user_email_ = kTestUser;
  service_->set_last_auth_error(invalid_gaia);
  wizard_->Step(SyncSetupWizard::GAIA_LOGIN);
  EXPECT_TRUE(wizard_->IsVisible());
  EXPECT_EQ(SyncSetupWizard::GAIA_LOGIN, flow_->current_state_);
  DictionaryValue dialog_args;
  flow_->GetArgsForGaiaLogin(&dialog_args);
  EXPECT_EQ(4U, dialog_args.size());
  std::string actual_user;
  dialog_args.GetString("user", &actual_user);
  EXPECT_EQ(kTestUser, actual_user);
  int error = -1;
  dialog_args.GetInteger("error", &error);
  EXPECT_EQ(static_cast<int>(AuthError::INVALID_GAIA_CREDENTIALS), error);
  flow_->last_attempted_user_email_ = kTestUser;
  service_->set_last_auth_error(AuthError::None());

  // Simulate captcha.
  AuthError captcha_error(AuthError::FromCaptchaChallenge(
      std::string(), GURL(kTestCaptchaUrl), GURL()));
  flow_->last_attempted_user_email_ = kTestUser;
  service_->set_last_auth_error(captcha_error);
  wizard_->Step(SyncSetupWizard::GAIA_LOGIN);
  flow_->GetArgsForGaiaLogin(&dialog_args);
  EXPECT_EQ(4U, dialog_args.size());
  std::string captcha_url;
  dialog_args.GetString("captchaUrl", &captcha_url);
  EXPECT_EQ(kTestCaptchaUrl, GURL(captcha_url).spec());
  error = -1;
  dialog_args.GetInteger("error", &error);
  EXPECT_EQ(static_cast<int>(AuthError::CAPTCHA_REQUIRED), error);
  flow_->last_attempted_user_email_ = kTestUser;
  service_->set_last_auth_error(AuthError::None());

  // Simulate success.
  wizard_->Step(SyncSetupWizard::GAIA_SUCCESS);
  EXPECT_TRUE(wizard_->IsVisible());
  wizard_->Step(SyncSetupWizard::SYNC_EVERYTHING);
  EXPECT_EQ(SyncSetupWizard::SYNC_EVERYTHING, flow_->current_state_);

  // That's all we're testing here, just move on to DONE.  We'll test the
  // "choose data types" scenarios elsewhere.
  wizard_->Step(SyncSetupWizard::SETTING_UP);  // No merge and sync.
  wizard_->Step(SyncSetupWizard::DONE);  // No merge and sync.
  EXPECT_FALSE(wizard_->IsVisible());
}

TEST_F(SyncSetupWizardTest, ChooseDataTypesSetsPrefs) {
  wizard_->Step(SyncSetupWizard::GAIA_LOGIN);
  AttachSyncSetupHandler();
  wizard_->Step(SyncSetupWizard::GAIA_SUCCESS);
  wizard_->Step(SyncSetupWizard::CONFIGURE);

  ListValue data_type_choices_value;
  std::string data_type_choices =
      "{\"syncAllDataTypes\":false,\"syncBookmarks\":true,"
      "\"syncPreferences\":true,\"syncThemes\":false,\"syncPasswords\":false,"
      "\"syncAutofill\":false,\"syncExtensions\":false,\"syncTypedUrls\":true,"
      "\"syncApps\":true,\"syncSessions\":false,\"usePassphrase\":false,"
      "\"encryptAllData\":false}";
  data_type_choices_value.Append(new StringValue(data_type_choices));

  // Simulate the user choosing data types; bookmarks, prefs, typed URLS, and
  // apps are on, the rest are off.
  handler_.HandleConfigure(&data_type_choices_value);
  // Since we don't need a passphrase, wizard should have transitioned to
  // DONE state and closed the UI.
  EXPECT_FALSE(wizard_->IsVisible());
  EXPECT_FALSE(service_->keep_everything_synced_);
  EXPECT_TRUE(service_->chosen_data_types_.Has(syncable::BOOKMARKS));
  EXPECT_TRUE(service_->chosen_data_types_.Has(syncable::PREFERENCES));
  EXPECT_FALSE(service_->chosen_data_types_.Has(syncable::THEMES));
  EXPECT_FALSE(service_->chosen_data_types_.Has(syncable::PASSWORDS));
  EXPECT_FALSE(service_->chosen_data_types_.Has(syncable::AUTOFILL));
  EXPECT_FALSE(service_->chosen_data_types_.Has(syncable::EXTENSIONS));
  EXPECT_TRUE(service_->chosen_data_types_.Has(syncable::TYPED_URLS));
  EXPECT_TRUE(service_->chosen_data_types_.Has(syncable::APPS));
  EXPECT_FALSE(service_->chosen_data_types_.Has(
      syncable::APP_NOTIFICATIONS));
}

TEST_F(SyncSetupWizardTest, ShowErrorUIForPasswordTest) {
  service_->ClearObservers();
  CompleteSetup();

  // Simulate an auth error and make sure the start and end state are set
  // right.
  service_->set_last_auth_error(
      AuthError(GoogleServiceAuthError::INVALID_GAIA_CREDENTIALS));
  service_->ShowErrorUI();
  AttachSyncSetupHandler();
  EXPECT_EQ(SyncSetupWizard::GAIA_LOGIN, flow_->current_state_);
  EXPECT_EQ(SyncSetupWizard::GAIA_SUCCESS, flow_->end_state_);
  ASSERT_TRUE(wizard_->IsVisible());

  // Make sure the wizard is dismissed.
  wizard_->Step(SyncSetupWizard::GAIA_SUCCESS);
  ASSERT_FALSE(wizard_->IsVisible());
}

TEST_F(SyncSetupWizardTest, ShowErrorUIForPassphraseTest) {
  service_->ClearObservers();
  CompleteSetup();

  // Simulate a passphrase error and make sure the start and end state are set
  // right and wizard is shown.
  service_->set_passphrase_required_reason(sync_api::REASON_ENCRYPTION);
  service_->set_is_using_secondary_passphrase(true);
  service_->ShowErrorUI();
  AttachSyncSetupHandler();
  EXPECT_EQ(SyncSetupWizard::ENTER_PASSPHRASE, flow_->current_state_);
  EXPECT_EQ(SyncSetupWizard::DONE, flow_->end_state_);
  ASSERT_TRUE(wizard_->IsVisible());

  // Make sure the wizard is dismissed.
  wizard_->Step(SyncSetupWizard::DONE);
  ASSERT_FALSE(wizard_->IsVisible());
}

TEST_F(SyncSetupWizardTest, EnterPassphraseRequired) {
  wizard_->Step(SyncSetupWizard::GAIA_LOGIN);
  AttachSyncSetupHandler();
  wizard_->Step(SyncSetupWizard::GAIA_SUCCESS);
  wizard_->Step(SyncSetupWizard::CONFIGURE);
  wizard_->Step(SyncSetupWizard::SETTING_UP);
  service_->set_passphrase_required_reason(sync_api::REASON_ENCRYPTION);
  wizard_->Step(SyncSetupWizard::ENTER_PASSPHRASE);
  EXPECT_EQ(SyncSetupWizard::ENTER_PASSPHRASE, flow_->current_state_);

  ListValue value;
  value.Append(new StringValue("{\"passphrase\":\"myPassphrase\","
                                "\"mode\":\"gaia\"}"));
  handler_.HandlePassphraseEntry(&value);
  EXPECT_EQ("myPassphrase", service_->passphrase_);
  CloseSetupUI();
}

TEST_F(SyncSetupWizardTest, DialogCancelled) {
  wizard_->Step(SyncSetupWizard::GAIA_LOGIN);
  AttachSyncSetupHandler();
  // Simulate the user closing the dialog.
  CloseSetupUI();
  EXPECT_FALSE(wizard_->IsVisible());
  EXPECT_TRUE(service_->user_cancelled_dialog_);
  EXPECT_EQ(std::string(), service_->mock_signin_.username_);
  EXPECT_EQ(std::string(), service_->mock_signin_.password_);

  wizard_->Step(SyncSetupWizard::GAIA_LOGIN);
  AttachSyncSetupHandler();
  EXPECT_TRUE(wizard_->IsVisible());
  wizard_->Step(SyncSetupWizard::GAIA_LOGIN);

  CloseSetupUI();
  EXPECT_FALSE(wizard_->IsVisible());
  EXPECT_TRUE(service_->user_cancelled_dialog_);
  EXPECT_EQ(std::string(), service_->mock_signin_.username_);
  EXPECT_EQ(std::string(), service_->mock_signin_.password_);
}

TEST_F(SyncSetupWizardTest, InvalidTransitions) {
  wizard_->Step(SyncSetupWizard::GAIA_SUCCESS);
  EXPECT_FALSE(wizard_->IsVisible());

  wizard_->Step(SyncSetupWizard::DONE);
  EXPECT_FALSE(wizard_->IsVisible());

  wizard_->Step(SyncSetupWizard::GAIA_LOGIN);
  AttachSyncSetupHandler();

  wizard_->Step(SyncSetupWizard::DONE);
  EXPECT_EQ(SyncSetupWizard::GAIA_LOGIN, flow_->current_state_);

  wizard_->Step(SyncSetupWizard::GAIA_SUCCESS);
  wizard_->Step(SyncSetupWizard::SYNC_EVERYTHING);
  EXPECT_EQ(SyncSetupWizard::SYNC_EVERYTHING, flow_->current_state_);

  wizard_->Step(SyncSetupWizard::FATAL_ERROR);
  // Stepping to FATAL_ERROR sends us back to GAIA_LOGIN.
  EXPECT_EQ(SyncSetupWizard::GAIA_LOGIN, flow_->current_state_);
  CloseSetupUI();
}

TEST_F(SyncSetupWizardTest, FullSuccessfulRunSetsPref) {
  CompleteSetup();
  EXPECT_FALSE(wizard_->IsVisible());
  EXPECT_TRUE(service_->profile()->GetPrefs()->GetBoolean(
      prefs::kSyncHasSetupCompleted));
}

TEST_F(SyncSetupWizardTest, AbortedByPendingClear) {
  wizard_->Step(SyncSetupWizard::GAIA_LOGIN);
  AttachSyncSetupHandler();
  wizard_->Step(SyncSetupWizard::GAIA_SUCCESS);
  wizard_->Step(SyncSetupWizard::SYNC_EVERYTHING);
  wizard_->Step(SyncSetupWizard::SETUP_ABORTED_BY_PENDING_CLEAR);
  // Stepping to SETUP_ABORTED should redirect us to GAIA_LOGIN state, since
  // that's where we display the error.
  EXPECT_EQ(SyncSetupWizard::GAIA_LOGIN, flow_->current_state_);
  CloseSetupUI();
  EXPECT_FALSE(wizard_->IsVisible());
}

TEST_F(SyncSetupWizardTest, DiscreteRunChooseDataTypes) {
  CompleteSetup();

  wizard_->Step(SyncSetupWizard::CONFIGURE);
  AttachSyncSetupHandler();
  EXPECT_EQ(SyncSetupWizard::DONE, flow_->end_state_);

  wizard_->Step(SyncSetupWizard::SETTING_UP);
  wizard_->Step(SyncSetupWizard::DONE);
  EXPECT_FALSE(wizard_->IsVisible());
}

TEST_F(SyncSetupWizardTest, DiscreteRunChooseDataTypesAbortedByPendingClear) {
  CompleteSetup();

  wizard_->Step(SyncSetupWizard::CONFIGURE);
  AttachSyncSetupHandler();
  EXPECT_EQ(SyncSetupWizard::DONE, flow_->end_state_);
  wizard_->Step(SyncSetupWizard::SETUP_ABORTED_BY_PENDING_CLEAR);
  // Stepping to SETUP_ABORTED should redirect us to GAIA_LOGIN state, since
  // that's where we display the error.
  EXPECT_EQ(SyncSetupWizard::GAIA_LOGIN, flow_->current_state_);

  CloseSetupUI();
  EXPECT_FALSE(wizard_->IsVisible());
}

TEST_F(SyncSetupWizardTest, DiscreteRunGaiaLogin) {
  CompleteSetup();

  wizard_->Step(SyncSetupWizard::GAIA_LOGIN);
  AttachSyncSetupHandler();
  EXPECT_EQ(SyncSetupWizard::GAIA_SUCCESS, flow_->end_state_);

  AuthError invalid_gaia(AuthError::INVALID_GAIA_CREDENTIALS);
  flow_->last_attempted_user_email_ = kTestUser;
  service_->set_last_auth_error(invalid_gaia);
  wizard_->Step(SyncSetupWizard::GAIA_LOGIN);
  EXPECT_TRUE(wizard_->IsVisible());

  DictionaryValue dialog_args;
  flow_->GetArgsForGaiaLogin(&dialog_args);
  EXPECT_EQ(4U, dialog_args.size());
  std::string actual_user;
  dialog_args.GetString("user", &actual_user);
  EXPECT_EQ(kTestUser, actual_user);
  int error = -1;
  dialog_args.GetInteger("error", &error);
  EXPECT_EQ(static_cast<int>(AuthError::INVALID_GAIA_CREDENTIALS), error);
  flow_->last_attempted_user_email_ = kTestUser;
  service_->set_last_auth_error(AuthError::None());

  wizard_->Step(SyncSetupWizard::GAIA_SUCCESS);
}

TEST_F(SyncSetupWizardTest, NonFatalError) {
  CompleteSetup();

  // Set up the ENTER_PASSPHRASE case.
  service_->set_passphrase_required_reason(sync_api::REASON_ENCRYPTION);
  service_->set_is_using_secondary_passphrase(true);
  wizard_->Step(SyncSetupWizard::NONFATAL_ERROR);
  AttachSyncSetupHandler();
  EXPECT_EQ(SyncSetupWizard::ENTER_PASSPHRASE, flow_->current_state_);
  EXPECT_EQ(SyncSetupWizard::DONE, flow_->end_state_);
  CloseSetupUI();
  EXPECT_FALSE(wizard_->IsVisible());

  // Reset.
  service_->set_passphrase_required_reason(
      sync_api::REASON_PASSPHRASE_NOT_REQUIRED);
  service_->set_is_using_secondary_passphrase(false);

  // Test the various auth error states that lead to GAIA_LOGIN.

  service_->set_last_auth_error(
      AuthError(GoogleServiceAuthError::INVALID_GAIA_CREDENTIALS));
  wizard_->Step(SyncSetupWizard::NONFATAL_ERROR);
  AttachSyncSetupHandler();
  EXPECT_EQ(SyncSetupWizard::GAIA_LOGIN, flow_->current_state_);
  EXPECT_EQ(SyncSetupWizard::DONE, flow_->end_state_);
  CloseSetupUI();
  EXPECT_FALSE(wizard_->IsVisible());

  service_->set_last_auth_error(
      AuthError(GoogleServiceAuthError::CAPTCHA_REQUIRED));
  wizard_->Step(SyncSetupWizard::NONFATAL_ERROR);
  AttachSyncSetupHandler();
  EXPECT_EQ(SyncSetupWizard::GAIA_LOGIN, flow_->current_state_);
  EXPECT_EQ(SyncSetupWizard::DONE, flow_->end_state_);
  CloseSetupUI();
  EXPECT_FALSE(wizard_->IsVisible());

  service_->set_last_auth_error(
      AuthError(GoogleServiceAuthError::ACCOUNT_DELETED));
  wizard_->Step(SyncSetupWizard::NONFATAL_ERROR);
  AttachSyncSetupHandler();
  EXPECT_EQ(SyncSetupWizard::GAIA_LOGIN, flow_->current_state_);
  EXPECT_EQ(SyncSetupWizard::DONE, flow_->end_state_);
  CloseSetupUI();
  EXPECT_FALSE(wizard_->IsVisible());

  service_->set_last_auth_error(
      AuthError(GoogleServiceAuthError::ACCOUNT_DISABLED));
  wizard_->Step(SyncSetupWizard::NONFATAL_ERROR);
  AttachSyncSetupHandler();
  EXPECT_EQ(SyncSetupWizard::GAIA_LOGIN, flow_->current_state_);
  EXPECT_EQ(SyncSetupWizard::DONE, flow_->end_state_);
  CloseSetupUI();
  EXPECT_FALSE(wizard_->IsVisible());

  service_->set_last_auth_error(
      AuthError(GoogleServiceAuthError::SERVICE_UNAVAILABLE));
  wizard_->Step(SyncSetupWizard::NONFATAL_ERROR);
  AttachSyncSetupHandler();
  EXPECT_EQ(SyncSetupWizard::GAIA_LOGIN, flow_->current_state_);
  EXPECT_EQ(SyncSetupWizard::DONE, flow_->end_state_);
  CloseSetupUI();
  EXPECT_FALSE(wizard_->IsVisible());
}

class SyncSetupWizardCrosTest : public SyncSetupWizardTest {
 public:
  virtual TestingProfile* BuildProfile() {
    TestingProfile* profile = new TestingProfile();
    ProfileSyncServiceFactory* f = ProfileSyncServiceFactory::GetInstance();
    f->SetTestingFactory(profile, ProfileSyncServiceForWizardTest::BuildAuto);
    f->GetForProfile(profile)->signin()->SetAuthenticatedUsername(kTestUser);
    return profile;
  }
};

// Tests a scenario where sync is disabled on chrome os on startup due to
// an auth error (application specific password is needed).
TEST_F(SyncSetupWizardCrosTest, CrosAuthSetup) {
  wizard_->Step(SyncSetupWizard::GAIA_LOGIN);

  AttachSyncSetupHandler();
  EXPECT_EQ(SyncSetupWizard::GAIA_SUCCESS, flow_->end_state_);

  DictionaryValue dialog_args;
  flow_->GetArgsForGaiaLogin(&dialog_args);
  EXPECT_EQ(4U, dialog_args.size());
  std::string actual_user;
  dialog_args.GetString("user", &actual_user);
  EXPECT_EQ(kTestUser, actual_user);
  int error = -1;
  dialog_args.GetInteger("error", &error);
  EXPECT_EQ(0, error);
  bool editable = true;
  dialog_args.GetBoolean("editable_user", &editable);
  EXPECT_FALSE(editable);
  wizard_->Step(SyncSetupWizard::GAIA_SUCCESS);
  EXPECT_TRUE(service_->user_cancelled_dialog_);
  EXPECT_TRUE(service_->profile()->GetPrefs()->GetBoolean(
      prefs::kSyncHasSetupCompleted));
}