[Mac] AppController needs to update its "last profile" pointer when the active profile is deleted.

Otherwise, when the next browser window is opened, it will try to use the old
"last used" profile, which is the one that just got deleted. This also means that
the ProfileManager needs to pre-load the next active profile, if one is not loaded
already.

BUG=194415
TEST=On Mac, create two profiles. Ensure only the active profile has opened browser windows. Delete the active profile. All windows should close, but the Chrome icon should still be highlighted in the dock. Clicking it should not crash, and should open a window for the remaining profile.

Review URL: https://chromiumcodereview.appspot.com/14923004

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@209035 0039d316-1c4b-4281-b951-d872f2087c98
diff --git a/chrome/browser/profiles/profile_manager_unittest.cc b/chrome/browser/profiles/profile_manager_unittest.cc
index c7cde563..dd46660 100644
--- a/chrome/browser/profiles/profile_manager_unittest.cc
+++ b/chrome/browser/profiles/profile_manager_unittest.cc
@@ -15,7 +15,6 @@
 #include "chrome/browser/bookmarks/bookmark_model_factory.h"
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/chromeos/settings/cros_settings.h"
-#include "chrome/browser/extensions/event_router_forwarder.h"
 #include "chrome/browser/history/history_service.h"
 #include "chrome/browser/history/history_service_factory.h"
 #include "chrome/browser/io_thread.h"
@@ -97,7 +96,6 @@
 
   ProfileManagerTest()
       : local_state_(TestingBrowserProcess::GetGlobal()),
-        extension_event_router_forwarder_(new extensions::EventRouterForwarder),
         ui_thread_(BrowserThread::UI, &message_loop_),
         db_thread_(BrowserThread::DB, &message_loop_),
         file_thread_(BrowserThread::FILE, &message_loop_) {
@@ -120,6 +118,19 @@
     message_loop_.RunUntilIdle();
   }
 
+  // Helper function to create a profile with |name| for a profile |manager|.
+  void CreateProfileAsync(ProfileManager* manager,
+                          const std::string& name,
+                          MockObserver* mock_observer) {
+    manager->CreateProfileAsync(
+        temp_dir_.path().AppendASCII(name),
+        base::Bind(&MockObserver::OnProfileCreated,
+                   base::Unretained(mock_observer)),
+        UTF8ToUTF16(name),
+        string16(),
+        false);
+  }
+
 #if defined(OS_CHROMEOS)
   chromeos::ScopedTestDeviceSettingsService test_device_settings_service_;
   chromeos::ScopedTestCrosSettings test_cros_settings_;
@@ -128,9 +139,6 @@
   // The path to temporary directory used to contain the test operations.
   base::ScopedTempDir temp_dir_;
   ScopedTestingLocalState local_state_;
-  scoped_refptr<extensions::EventRouterForwarder>
-      extension_event_router_forwarder_;
-
   base::MessageLoopForUI message_loop_;
   content::TestBrowserThread ui_thread_;
   content::TestBrowserThread db_thread_;
@@ -240,17 +248,12 @@
 // Tests asynchronous profile creation mechanism.
 // Crashes: http://crbug.com/89421
 TEST_F(ProfileManagerTest, DISABLED_CreateProfileAsync) {
-  base::FilePath dest_path =
-      temp_dir_.path().Append(FILE_PATH_LITERAL("New Profile"));
-
   MockObserver mock_observer;
   EXPECT_CALL(mock_observer, OnProfileCreated(
       testing::NotNull(), NotFail())).Times(testing::AtLeast(1));
 
-  g_browser_process->profile_manager()->CreateProfileAsync(dest_path,
-      base::Bind(&MockObserver::OnProfileCreated,
-                 base::Unretained(&mock_observer)),
-      string16(), string16(), false);
+  CreateProfileAsync(g_browser_process->profile_manager(),
+                     "New Profile", &mock_observer);
 
   message_loop_.RunUntilIdle();
 }
@@ -262,9 +265,6 @@
 }
 
 TEST_F(ProfileManagerTest, CreateProfileAsyncMultipleRequests) {
-  base::FilePath dest_path =
-      temp_dir_.path().Append(FILE_PATH_LITERAL("New Profile"));
-
   g_created_profile = NULL;
 
   MockObserver mock_observer1;
@@ -278,28 +278,17 @@
       SameNotNull(), NotFail())).Times(testing::AtLeast(1));
 
   ProfileManager* profile_manager = g_browser_process->profile_manager();
-
-  profile_manager->CreateProfileAsync(dest_path,
-      base::Bind(&MockObserver::OnProfileCreated,
-                 base::Unretained(&mock_observer1)),
-                 string16(), string16(), false);
-  profile_manager->CreateProfileAsync(dest_path,
-      base::Bind(&MockObserver::OnProfileCreated,
-                 base::Unretained(&mock_observer2)),
-                 string16(), string16(), false);
-  profile_manager->CreateProfileAsync(dest_path,
-      base::Bind(&MockObserver::OnProfileCreated,
-                 base::Unretained(&mock_observer3)),
-                 string16(), string16(), false);
+  const std::string profile_name = "New Profile";
+  CreateProfileAsync(profile_manager, profile_name, &mock_observer1);
+  CreateProfileAsync(profile_manager, profile_name, &mock_observer2);
+  CreateProfileAsync(profile_manager, profile_name, &mock_observer3);
 
   message_loop_.RunUntilIdle();
 }
 
 TEST_F(ProfileManagerTest, CreateProfilesAsync) {
-  base::FilePath dest_path1 =
-      temp_dir_.path().Append(FILE_PATH_LITERAL("New Profile 1"));
-  base::FilePath dest_path2 =
-      temp_dir_.path().Append(FILE_PATH_LITERAL("New Profile 2"));
+  const std::string profile_name1 = "New Profile 1";
+  const std::string profile_name2 = "New Profile 2";
 
   MockObserver mock_observer;
   EXPECT_CALL(mock_observer, OnProfileCreated(
@@ -307,14 +296,8 @@
 
   ProfileManager* profile_manager = g_browser_process->profile_manager();
 
-  profile_manager->CreateProfileAsync(dest_path1,
-      base::Bind(&MockObserver::OnProfileCreated,
-                 base::Unretained(&mock_observer)),
-                 string16(), string16(), false);
-  profile_manager->CreateProfileAsync(dest_path2,
-      base::Bind(&MockObserver::OnProfileCreated,
-                 base::Unretained(&mock_observer)),
-                 string16(), string16(), false);
+  CreateProfileAsync(profile_manager, profile_name1, &mock_observer);
+  CreateProfileAsync(profile_manager, profile_name2, &mock_observer);
 
   message_loop_.RunUntilIdle();
 }
@@ -634,3 +617,155 @@
   ASSERT_EQ(0U, last_opened_profiles.size());
 }
 #endif  // !defined(OS_ANDROID)
+
+#if !defined(OS_ANDROID) && !defined(OS_CHROMEOS)
+// There's no Browser object on Android and there's no multi-profiles on Chrome.
+TEST_F(ProfileManagerTest, ActiveProfileDeleted) {
+  ProfileManager* profile_manager = g_browser_process->profile_manager();
+  ASSERT_TRUE(profile_manager);
+
+  // Create and load two profiles.
+  const std::string profile_name1 = "New Profile 1";
+  const std::string profile_name2 = "New Profile 2";
+  base::FilePath dest_path1 =
+      temp_dir_.path().AppendASCII(profile_name1);
+  base::FilePath dest_path2 =
+      temp_dir_.path().AppendASCII(profile_name2);
+
+  MockObserver mock_observer;
+  EXPECT_CALL(mock_observer, OnProfileCreated(
+      testing::NotNull(), NotFail())).Times(testing::AtLeast(3));
+
+  CreateProfileAsync(profile_manager, profile_name1, &mock_observer);
+  CreateProfileAsync(profile_manager, profile_name2, &mock_observer);
+  message_loop_.RunUntilIdle();
+
+  EXPECT_EQ(2u, profile_manager->GetLoadedProfiles().size());
+  EXPECT_EQ(2u, profile_manager->GetProfileInfoCache().GetNumberOfProfiles());
+
+  // Set the active profile.
+  PrefService* local_state = g_browser_process->local_state();
+  local_state->SetString(prefs::kProfileLastUsed, profile_name1);
+
+  // Delete the active profile.
+  profile_manager->ScheduleProfileForDeletion(dest_path1,
+                                              ProfileManager::CreateCallback());
+  // Spin the message loop so that all the callbacks can finish running.
+  message_loop_.RunUntilIdle();
+
+  EXPECT_EQ(dest_path2, profile_manager->GetLastUsedProfile()->GetPath());
+  EXPECT_EQ(profile_name2, local_state->GetString(prefs::kProfileLastUsed));
+}
+#endif  // !defined(OS_ANDROID) && !defined(OS_CHROMEOS)
+
+#if defined(OS_MACOSX)
+// These tests are for a Mac-only code path that assumes the browser
+// process isn't killed when all browser windows are closed.
+TEST_F(ProfileManagerTest, ActiveProfileDeletedNeedsToLoadNextProfile) {
+  ProfileManager* profile_manager = g_browser_process->profile_manager();
+  ASSERT_TRUE(profile_manager);
+
+  // Create and load one profile, and just create a second profile.
+  const std::string profile_name1 = "New Profile 1";
+  const std::string profile_name2 = "New Profile 2";
+  base::FilePath dest_path1 =
+      temp_dir_.path().AppendASCII(profile_name1);
+  base::FilePath dest_path2 =
+      temp_dir_.path().AppendASCII(profile_name2);
+
+  MockObserver mock_observer;
+  EXPECT_CALL(mock_observer, OnProfileCreated(
+      testing::NotNull(), NotFail())).Times(testing::AtLeast(2));
+  CreateProfileAsync(profile_manager, profile_name1, &mock_observer);
+  message_loop_.RunUntilIdle();
+
+  // Track the profile, but don't load it.
+  ProfileInfoCache& cache = profile_manager->GetProfileInfoCache();
+  cache.AddProfileToCache(dest_path2, ASCIIToUTF16(profile_name2),
+                          string16(), 0, false);
+  message_loop_.RunUntilIdle();
+
+  EXPECT_EQ(1u, profile_manager->GetLoadedProfiles().size());
+  EXPECT_EQ(2u, cache.GetNumberOfProfiles());
+
+  // Set the active profile.
+  PrefService* local_state = g_browser_process->local_state();
+  local_state->SetString(prefs::kProfileLastUsed,
+                         dest_path1.BaseName().MaybeAsASCII());
+
+  // Delete the active profile. This should switch and load the unloaded
+  // profile.
+  profile_manager->ScheduleProfileForDeletion(dest_path1,
+                                              ProfileManager::CreateCallback());
+
+  // Spin the message loop so that all the callbacks can finish running.
+  message_loop_.RunUntilIdle();
+
+  EXPECT_EQ(dest_path2, profile_manager->GetLastUsedProfile()->GetPath());
+  EXPECT_EQ(profile_name2, local_state->GetString(prefs::kProfileLastUsed));
+}
+
+// This tests the recursive call in ProfileManager::OnNewActiveProfileLoaded
+// by simulating a scenario in which the profile that is being loaded as
+// the next active profile has also been marked for deletion, so the
+// ProfileManager needs to recursively select a different next profile.
+TEST_F(ProfileManagerTest, ActiveProfileDeletedNextProfileDeletedToo) {
+  ProfileManager* profile_manager = g_browser_process->profile_manager();
+  ASSERT_TRUE(profile_manager);
+
+  // Create and load one profile, and create two more profiles.
+  const std::string profile_name1 = "New Profile 1";
+  const std::string profile_name2 = "New Profile 2";
+  const std::string profile_name3 = "New Profile 3";
+  base::FilePath dest_path1 =
+      temp_dir_.path().AppendASCII(profile_name1);
+  base::FilePath dest_path2 =
+      temp_dir_.path().AppendASCII(profile_name2);
+  base::FilePath dest_path3 =
+      temp_dir_.path().AppendASCII(profile_name3);
+
+  MockObserver mock_observer;
+  EXPECT_CALL(mock_observer, OnProfileCreated(
+      testing::NotNull(), NotFail())).Times(testing::AtLeast(2));
+  CreateProfileAsync(profile_manager, profile_name1, &mock_observer);
+  message_loop_.RunUntilIdle();
+
+  // Create the other profiles, but don't load them. Assign a fake avatar icon
+  // to ensure that profiles in the info cache are sorted by the profile name,
+  // and not randomly by the avatar name.
+  ProfileInfoCache& cache = profile_manager->GetProfileInfoCache();
+  cache.AddProfileToCache(dest_path2, ASCIIToUTF16(profile_name2),
+                          ASCIIToUTF16(profile_name2), 1, false);
+  cache.AddProfileToCache(dest_path3, ASCIIToUTF16(profile_name3),
+                          ASCIIToUTF16(profile_name3), 2, false);
+
+  message_loop_.RunUntilIdle();
+
+  EXPECT_EQ(1u, profile_manager->GetLoadedProfiles().size());
+  EXPECT_EQ(3u, cache.GetNumberOfProfiles());
+
+  // Set the active profile.
+  PrefService* local_state = g_browser_process->local_state();
+  local_state->SetString(prefs::kProfileLastUsed,
+                         dest_path1.BaseName().MaybeAsASCII());
+
+  // Delete the active profile, Profile1.
+  // This will post a CreateProfileAsync message, that tries to load Profile2,
+  // which checks that the profile is not being deleted, and then calls back
+  // FinishDeletingProfile for Profile1.
+  // Try to break this flow by setting the active profile to Profile2 in the
+  // middle (so after the first posted message), and trying to delete Profile2,
+  // so that the ProfileManager has to look for a different profile to load.
+  profile_manager->ScheduleProfileForDeletion(dest_path1,
+                                              ProfileManager::CreateCallback());
+  local_state->SetString(prefs::kProfileLastUsed,
+                         dest_path2.BaseName().MaybeAsASCII());
+  profile_manager->ScheduleProfileForDeletion(dest_path2,
+                                              ProfileManager::CreateCallback());
+  // Spin the message loop so that all the callbacks can finish running.
+  message_loop_.RunUntilIdle();
+
+  EXPECT_EQ(dest_path3, profile_manager->GetLastUsedProfile()->GetPath());
+  EXPECT_EQ(profile_name3, local_state->GetString(prefs::kProfileLastUsed));
+}
+#endif  // !defined(OS_MACOSX)