text: // 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 "base/bind.h"
  #include "base/command_line.h"
  #include "base/prefs/pref_service.h"
  #include "base/strings/utf_string_conversions.h"
  #include "chrome/browser/password_manager/password_store_factory.h"
  #include "chrome/browser/profiles/profile_info_cache.h"
  #include "chrome/browser/profiles/profile_info_cache_observer.h"
  #include "chrome/browser/profiles/profile_manager.h"
  #include "chrome/browser/profiles/profile_window.h"
  #include "chrome/browser/profiles/profiles_state.h"
  #include "chrome/browser/ui/browser_finder.h"
  #include "chrome/browser/ui/browser_list.h"
  #include "chrome/browser/ui/browser_window.h"
  #include "chrome/browser/ui/host_desktop.h"
  #include "chrome/common/pref_names.h"
  #include "chrome/test/base/in_process_browser_test.h"
  #include "chrome/test/base/test_switches.h"
  #include "chrome/test/base/testing_browser_process.h"
  #include "chrome/test/base/ui_test_utils.h"
  #include "components/autofill/core/common/password_form.h"
  #include "components/password_manager/core/browser/password_store.h"
  #include "components/password_manager/core/browser/password_store_consumer.h"
  
  #if defined(OS_CHROMEOS)
  #include "base/path_service.h"
  #include "chrome/common/chrome_constants.h"
  #include "chrome/common/chrome_paths.h"
  #include "chromeos/chromeos_switches.h"
  #include "testing/gtest/include/gtest/gtest.h"
  #endif
  
  namespace {
  
  const profiles::ProfileSwitchingDoneCallback kOnProfileSwitchDoNothing;
  
  // An observer that returns back to test code after a new profile is
  // initialized.
  void OnUnblockOnProfileCreation(Profile* profile,
                                  Profile::CreateStatus status) {
    if (status == Profile::CREATE_STATUS_INITIALIZED)
      base::MessageLoop::current()->Quit();
  }
  
  void ProfileCreationComplete(Profile* profile, Profile::CreateStatus status) {
    ASSERT_NE(status, Profile::CREATE_STATUS_LOCAL_FAIL);
    ASSERT_NE(status, Profile::CREATE_STATUS_REMOTE_FAIL);
    // No browser should have been created for this profile yet.
    EXPECT_EQ(chrome::GetTotalBrowserCountForProfile(profile), 0U);
    EXPECT_EQ(chrome::GetTotalBrowserCount(), 1U);
    if (status == Profile::CREATE_STATUS_INITIALIZED)
      base::MessageLoop::current()->Quit();
  }
  
  void EphemeralProfileCreationComplete(Profile* profile,
                                        Profile::CreateStatus status) {
    if (status == Profile::CREATE_STATUS_INITIALIZED)
      profile->GetPrefs()->SetBoolean(prefs::kForceEphemeralProfiles, true);
    ProfileCreationComplete(profile, status);
  }
  
  class ProfileRemovalObserver : public ProfileInfoCacheObserver {
   public:
    ProfileRemovalObserver() {
      g_browser_process->profile_manager()->GetProfileInfoCache().AddObserver(
          this);
    }
  
    virtual ~ProfileRemovalObserver() {
      g_browser_process->profile_manager()->GetProfileInfoCache().RemoveObserver(
          this);
    }
  
    std::string last_used_profile_name() { return last_used_profile_name_; }
  
    // ProfileInfoCacheObserver overrides:
    virtual void OnProfileWillBeRemoved(
        const base::FilePath& profile_path) OVERRIDE {
      last_used_profile_name_ = g_browser_process->local_state()->GetString(
          prefs::kProfileLastUsed);
    }
  
   private:
    std::string last_used_profile_name_;
  
    DISALLOW_COPY_AND_ASSIGN(ProfileRemovalObserver);
  };
  
  // The class serves to retrieve passwords from PasswordStore asynchronously. It
  // used by ProfileManagerBrowserTest.DeletePasswords on some platforms.
  class PasswordStoreConsumerVerifier :
      public password_manager::PasswordStoreConsumer {
   public:
    PasswordStoreConsumerVerifier() : called_(false) {}
  
    virtual void OnGetPasswordStoreResults(
        const std::vector<autofill::PasswordForm*>& results) OVERRIDE {
      EXPECT_FALSE(called_);
      called_ = true;
      password_entries_.clear();
      password_entries_.assign(results.begin(), results.end());
    }
  
    bool IsCalled() const { return called_; }
  
    const std::vector<autofill::PasswordForm*>& GetPasswords() const {
      return password_entries_.get();
    }
   private:
    ScopedVector<autofill::PasswordForm> password_entries_;
    bool called_;
  };
  
  } // namespace
  
  // This file contains tests for the ProfileManager that require a heavyweight
  // InProcessBrowserTest.  These include tests involving profile deletion.
  
  // TODO(jeremy): crbug.com/103355 - These tests should be enabled on all
  // platforms.
  class ProfileManagerBrowserTest : public InProcessBrowserTest {
   protected:
    virtual void SetUpCommandLine(CommandLine* command_line) OVERRIDE {
  #if defined(OS_CHROMEOS)
      command_line->AppendSwitch(
          chromeos::switches::kIgnoreUserProfileMappingForTests);
  #endif
    }
  };
  
  #if defined(OS_MACOSX)
  
  // Delete single profile and make sure a new one is created.
  IN_PROC_BROWSER_TEST_F(ProfileManagerBrowserTest, DeleteSingletonProfile) {
    ProfileManager* profile_manager = g_browser_process->profile_manager();
    ProfileInfoCache& cache = profile_manager->GetProfileInfoCache();
    ProfileRemovalObserver observer;
  
    // We should start out with 1 profile.
    ASSERT_EQ(cache.GetNumberOfProfiles(), 1U);
  
    // Delete singleton profile.
    base::FilePath singleton_profile_path = cache.GetPathOfProfileAtIndex(0);
    EXPECT_FALSE(singleton_profile_path.empty());
    profile_manager->ScheduleProfileForDeletion(singleton_profile_path,
                                                ProfileManager::CreateCallback());
  
    // Spin things till profile is actually deleted.
    content::RunAllPendingInMessageLoop();
  
    // Make sure a new profile was created automatically.
    EXPECT_EQ(cache.GetNumberOfProfiles(), 1U);
    base::FilePath new_profile_path = cache.GetPathOfProfileAtIndex(0);
    EXPECT_NE(new_profile_path, singleton_profile_path);
  
    // Make sure that last used profile preference is set correctly.
    Profile* last_used = ProfileManager::GetLastUsedProfile();
    EXPECT_EQ(new_profile_path, last_used->GetPath());
  
    // Make sure the last used profile was set correctly before the notification
    // was sent.
    std::string last_used_profile_name =
        last_used->GetPath().BaseName().MaybeAsASCII();
    EXPECT_EQ(last_used_profile_name, observer.last_used_profile_name());
  }
  
  // Delete all profiles in a multi profile setup and make sure a new one is
  // created.
  // Crashes/CHECKs. See crbug.com/104851
  IN_PROC_BROWSER_TEST_F(ProfileManagerBrowserTest, DISABLED_DeleteAllProfiles) {
    ProfileManager* profile_manager = g_browser_process->profile_manager();
    ProfileInfoCache& cache = profile_manager->GetProfileInfoCache();
  
    // Create an additional profile.
    base::FilePath new_path = profile_manager->GenerateNextProfileDirectoryPath();
    profile_manager->CreateProfileAsync(new_path,
                                        base::Bind(&OnUnblockOnProfileCreation),
                                        base::string16(), base::string16(),
                                        std::string());
  
    // Spin to allow profile creation to take place, loop is terminated
    // by OnUnblockOnProfileCreation when the profile is created.
    content::RunMessageLoop();
  
    ASSERT_EQ(cache.GetNumberOfProfiles(), 2U);
  
    // Delete all profiles.
    base::FilePath profile_path1 = cache.GetPathOfProfileAtIndex(0);
    base::FilePath profile_path2 = cache.GetPathOfProfileAtIndex(1);
    EXPECT_FALSE(profile_path1.empty());
    EXPECT_FALSE(profile_path2.empty());
    profile_manager->ScheduleProfileForDeletion(profile_path1,
                                                ProfileManager::CreateCallback());
    profile_manager->ScheduleProfileForDeletion(profile_path2,
                                                ProfileManager::CreateCallback());
  
    // Spin things so deletion can take place.
    content::RunAllPendingInMessageLoop();
  
    // Make sure a new profile was created automatically.
    EXPECT_EQ(cache.GetNumberOfProfiles(), 1U);
    base::FilePath new_profile_path = cache.GetPathOfProfileAtIndex(0);
    EXPECT_NE(new_profile_path, profile_path1);
    EXPECT_NE(new_profile_path, profile_path2);
  
    // Make sure that last used profile preference is set correctly.
    Profile* last_used = ProfileManager::GetLastUsedProfile();
    EXPECT_EQ(new_profile_path, last_used->GetPath());
  }
  #endif  // OS_MACOSX
  
  #if defined(OS_CHROMEOS)
  
  class ProfileManagerCrOSBrowserTest : public ProfileManagerBrowserTest {
   protected:
    virtual void SetUpCommandLine(CommandLine* command_line) OVERRIDE {
      // Use a user hash other than the default chrome::kTestUserProfileDir
      // so that the prefix case is tested.
      command_line->AppendSwitchASCII(chromeos::switches::kLoginProfile,
                                      "test-user-hash");
    }
  };
  
  IN_PROC_BROWSER_TEST_F(ProfileManagerCrOSBrowserTest, GetLastUsedProfile) {
    // Make sure that last used profile is correct.
    Profile* last_used_profile = ProfileManager::GetLastUsedProfile();
    EXPECT_TRUE(last_used_profile != NULL);
  
    base::FilePath profile_path;
    PathService::Get(chrome::DIR_USER_DATA, &profile_path);
  
    profile_path = profile_path.AppendASCII(
        std::string(chrome::kProfileDirPrefix) + "test-user-hash");
    EXPECT_EQ(profile_path.value(), last_used_profile->GetPath().value());
  }
  
  #endif  // OS_CHROMEOS
  
  // Times out (http://crbug.com/159002)
  IN_PROC_BROWSER_TEST_F(ProfileManagerBrowserTest,
                         DISABLED_CreateProfileWithCallback) {
    ProfileManager* profile_manager = g_browser_process->profile_manager();
  
    ASSERT_EQ(profile_manager->GetNumberOfProfiles(), 1U);
    EXPECT_EQ(chrome::GetTotalBrowserCount(), 1U);
  
    // Create a profile, make sure callback is invoked before any callbacks are
    // invoked (so they can do things like sign in the profile, etc).
    ProfileManager::CreateMultiProfileAsync(
        base::string16(), // name
        base::string16(), // icon url
        base::Bind(ProfileCreationComplete),
        std::string());
    // Wait for profile to finish loading.
    content::RunMessageLoop();
    EXPECT_EQ(profile_manager->GetNumberOfProfiles(), 2U);
    EXPECT_EQ(chrome::GetTotalBrowserCount(), 2U);
  
    // Now close all browser windows.
    std::vector<Profile*> profiles = profile_manager->GetLoadedProfiles();
    for (std::vector<Profile*>::const_iterator it = profiles.begin();
         it != profiles.end(); ++it) {
      BrowserList::CloseAllBrowsersWithProfile(*it);
    }
  }
  
  IN_PROC_BROWSER_TEST_F(ProfileManagerBrowserTest,
                         SwitchToProfile) {
  #if defined(OS_WIN) && defined(USE_ASH)
    // Disable this test in Metro+Ash for now (http://crbug.com/262796).
    if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kAshBrowserTests))
      return;
  #endif
  
    // If multiprofile mode is not enabled, you can't switch between profiles.
    if (!profiles::IsMultipleProfilesEnabled())
      return;
  
    ProfileManager* profile_manager = g_browser_process->profile_manager();
    ProfileInfoCache& cache = profile_manager->GetProfileInfoCache();
    base::FilePath path_profile1 = cache.GetPathOfProfileAtIndex(0);
  
    ASSERT_EQ(profile_manager->GetNumberOfProfiles(), 1U);
    EXPECT_EQ(chrome::GetTotalBrowserCount(), 1U);
  
    // Create an additional profile.
    base::FilePath path_profile2 =
        profile_manager->GenerateNextProfileDirectoryPath();
    profile_manager->CreateProfileAsync(path_profile2,
                                        base::Bind(&OnUnblockOnProfileCreation),
                                        base::string16(), base::string16(),
                                        std::string());
  
    // Spin to allow profile creation to take place, loop is terminated
    // by OnUnblockOnProfileCreation when the profile is created.
    content::RunMessageLoop();
  
    chrome::HostDesktopType desktop_type = chrome::GetActiveDesktop();
    BrowserList* browser_list = BrowserList::GetInstance(desktop_type);
    ASSERT_EQ(cache.GetNumberOfProfiles(), 2U);
    EXPECT_EQ(1U, browser_list->size());
  
    // Open a browser window for the first profile.
    profiles::SwitchToProfile(path_profile1, desktop_type, false,
                              kOnProfileSwitchDoNothing,
                              ProfileMetrics::SWITCH_PROFILE_ICON);
    EXPECT_EQ(chrome::GetTotalBrowserCount(), 1U);
    EXPECT_EQ(1U, browser_list->size());
    EXPECT_EQ(path_profile1, browser_list->get(0)->profile()->GetPath());
  
    // Open a browser window for the second profile.
    profiles::SwitchToProfile(path_profile2, desktop_type, false,
                              kOnProfileSwitchDoNothing,
                              ProfileMetrics::SWITCH_PROFILE_ICON);
    EXPECT_EQ(chrome::GetTotalBrowserCount(), 2U);
    EXPECT_EQ(2U, browser_list->size());
    EXPECT_EQ(path_profile2, browser_list->get(1)->profile()->GetPath());
  
    // Switch to the first profile without opening a new window.
    profiles::SwitchToProfile(path_profile1, desktop_type, false,
                              kOnProfileSwitchDoNothing,
                              ProfileMetrics::SWITCH_PROFILE_ICON);
    EXPECT_EQ(chrome::GetTotalBrowserCount(), 2U);
    EXPECT_EQ(2U, browser_list->size());
  
    EXPECT_EQ(path_profile1, browser_list->get(0)->profile()->GetPath());
    EXPECT_EQ(path_profile2, browser_list->get(1)->profile()->GetPath());
  }
  
  // This test used to be flakily timing out on Windows: http://crbug.com/314905.
  // If this happens again please make it a MAYBE_ test and reopen that bug.
  IN_PROC_BROWSER_TEST_F(ProfileManagerBrowserTest, EphemeralProfile) {
  #if defined(OS_WIN) && defined(USE_ASH)
    // Disable this test in Metro+Ash for now (http://crbug.com/262796).
    if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kAshBrowserTests))
      return;
  #endif
  
    // If multiprofile mode is not enabled, you can't switch between profiles.
    if (!profiles::IsMultipleProfilesEnabled())
      return;
  
    ProfileManager* profile_manager = g_browser_process->profile_manager();
    ProfileInfoCache& cache = profile_manager->GetProfileInfoCache();
    base::FilePath path_profile1 = cache.GetPathOfProfileAtIndex(0);
  
    ASSERT_EQ(1U, profile_manager->GetNumberOfProfiles());
    EXPECT_EQ(1U, chrome::GetTotalBrowserCount());
  
    // Create an ephemeral profile.
    base::FilePath path_profile2 =
        profile_manager->GenerateNextProfileDirectoryPath();
    profile_manager->CreateProfileAsync(
        path_profile2,
        base::Bind(&EphemeralProfileCreationComplete),
        base::string16(), base::string16(), std::string());
  
    // Spin to allow profile creation to take place.
    content::RunMessageLoop();
  
    chrome::HostDesktopType desktop_type = chrome::GetActiveDesktop();
    BrowserList* browser_list = BrowserList::GetInstance(desktop_type);
    ASSERT_EQ(2U, cache.GetNumberOfProfiles());
    EXPECT_EQ(1U, browser_list->size());
  
    // Open a browser window for the second profile.
    profiles::SwitchToProfile(path_profile2, desktop_type, false,
                              kOnProfileSwitchDoNothing,
                              ProfileMetrics::SWITCH_PROFILE_ICON);
    EXPECT_EQ(2U, chrome::GetTotalBrowserCount());
    EXPECT_EQ(2U, browser_list->size());
    EXPECT_EQ(path_profile2, browser_list->get(1)->profile()->GetPath());
  
    // Create a second window for the ephemeral profile.
    profiles::SwitchToProfile(path_profile2, desktop_type, true,
                              kOnProfileSwitchDoNothing,
                              ProfileMetrics::SWITCH_PROFILE_ICON);
    EXPECT_EQ(3U, chrome::GetTotalBrowserCount());
    EXPECT_EQ(3U, browser_list->size());
  
    EXPECT_EQ(path_profile1, browser_list->get(0)->profile()->GetPath());
    EXPECT_EQ(path_profile2, browser_list->get(1)->profile()->GetPath());
    EXPECT_EQ(path_profile2, browser_list->get(2)->profile()->GetPath());
  
    // Closing the first window of the ephemeral profile should not delete it.
    browser_list->get(2)->window()->Close();
    content::RunAllPendingInMessageLoop();
    EXPECT_EQ(2U, browser_list->size());
    ASSERT_EQ(2U, cache.GetNumberOfProfiles());
  
    // The second should though.
    browser_list->get(1)->window()->Close();
    content::RunAllPendingInMessageLoop();
    EXPECT_EQ(1U, browser_list->size());
    ASSERT_EQ(1U, cache.GetNumberOfProfiles());
  }
  
  // The test makes sense on those platforms where the keychain exists.
  #if !defined(OS_WIN) && !defined(OS_ANDROID) && !defined(OS_CHROMEOS)
  IN_PROC_BROWSER_TEST_F(ProfileManagerBrowserTest, DeletePasswords) {
    Profile* profile = ProfileManager::GetActiveUserProfile();
    ASSERT_TRUE(profile);
  
    autofill::PasswordForm form;
    form.scheme = autofill::PasswordForm::SCHEME_HTML;
    form.origin = GURL("http://accounts.google.com/LoginAuth");
    form.signon_realm = "http://accounts.google.com/";
    form.username_value = base::ASCIIToUTF16("my_username");
    form.password_value = base::ASCIIToUTF16("my_password");
    form.ssl_valid = false;
    form.preferred = true;
    form.blacklisted_by_user = false;
  
    scoped_refptr<password_manager::PasswordStore> password_store =
        PasswordStoreFactory::GetForProfile(profile, Profile::EXPLICIT_ACCESS)
            .get();
    ASSERT_TRUE(password_store);
  
    password_store->AddLogin(form);
    PasswordStoreConsumerVerifier verify_add;
    password_store->GetAutofillableLogins(&verify_add);
  
    ProfileManager* profile_manager = g_browser_process->profile_manager();
    profile_manager->ScheduleProfileForDeletion(profile->GetPath(),
                                                ProfileManager::CreateCallback());
    content::RunAllPendingInMessageLoop();
    PasswordStoreConsumerVerifier verify_delete;
    password_store->GetAutofillableLogins(&verify_delete);
  
    // Run the password background thread.
    base::RunLoop run_loop;
    base::Closure task = base::Bind(
        base::IgnoreResult(&content::BrowserThread::PostTask),
        content::BrowserThread::UI,
        FROM_HERE,
        run_loop.QuitClosure());
    EXPECT_TRUE(password_store->ScheduleTask(task));
    run_loop.Run();
  
    EXPECT_TRUE(verify_add.IsCalled());
    EXPECT_EQ(1u, verify_add.GetPasswords().size());
    EXPECT_TRUE(verify_delete.IsCalled());
    EXPECT_EQ(0u, verify_delete.GetPasswords().size());
  }
  #endif  // !defined(OS_WIN) && !defined(OS_ANDROID) && !defined(OS_CHROMEOS)
  