Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(3833)

Unified Diff: chrome/browser/ui/startup/startup_browser_creator_corrupt_profiles_browsertest_win.cc

Issue 2047483003: Add fallback behavior if the last used profile cannot initialize (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@bug-614753-fix
Patch Set: Handle the case where the selected profile is not in ProfileAttributesStorage Created 4 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: chrome/browser/ui/startup/startup_browser_creator_corrupt_profiles_browsertest_win.cc
diff --git a/chrome/browser/ui/startup/startup_browser_creator_corrupt_profiles_browsertest_win.cc b/chrome/browser/ui/startup/startup_browser_creator_corrupt_profiles_browsertest_win.cc
new file mode 100644
index 0000000000000000000000000000000000000000..bdda5a83eec3ecbe1c60d4134a9e0aa825a7f2ed
--- /dev/null
+++ b/chrome/browser/ui/startup/startup_browser_creator_corrupt_profiles_browsertest_win.cc
@@ -0,0 +1,419 @@
+// Copyright (c) 2016 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 <algorithm>
+#include <string>
+#include <vector>
+
+#include "base/base_switches.h"
+#include "base/bind.h"
+#include "base/callback.h"
+#include "base/command_line.h"
+#include "base/files/file_util.h"
+#include "base/path_service.h"
+#include "base/run_loop.h"
+#include "base/strings/string_util.h"
+#include "base/test/test_file_util.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/profiles/profile_manager.h"
+#include "chrome/browser/profiles/profile_window.h"
+#include "chrome/browser/ui/browser_list.h"
+#include "chrome/browser/ui/user_manager.h"
+#include "chrome/common/chrome_paths.h"
+#include "chrome/common/chrome_switches.h"
+#include "chrome/test/base/in_process_browser_test.h"
+#include "chrome/test/base/testing_browser_process.h"
+
+namespace {
+
+const ProfileManager::CreateCallback kOnProfileSwitchDoNothing;
+
+void UnblockOnProfileCreation(Profile::CreateStatus expected_final_status,
+ const base::Closure& quit_closure,
+ Profile* profile,
+ Profile::CreateStatus status) {
+ // If the status is CREATE_STATUS_CREATED, then the function will be called
+ // again with CREATE_STATUS_INITIALIZED.
+ if (status == Profile::CREATE_STATUS_CREATED)
+ return;
+
+ EXPECT_EQ(expected_final_status, status);
+ quit_closure.Run();
+}
+
+void UnblockOnProfileInitialized(const base::Closure& quit_closure,
+ Profile* profile,
+ Profile::CreateStatus status) {
+ UnblockOnProfileCreation(Profile::CREATE_STATUS_INITIALIZED, quit_closure,
+ profile, status);
+}
+
+void CloseBrowsersSuccessCallback(const base::Closure& quit_closure,
+ const base::FilePath& path) {
+ quit_closure.Run();
+}
+
+void CreateAndSwitchToProfile(const std::string& basepath) {
+ ProfileManager* profile_manager = g_browser_process->profile_manager();
+ ASSERT_TRUE(profile_manager);
+
+ base::FilePath path = profile_manager->user_data_dir().AppendASCII(basepath);
+ base::RunLoop run_loop;
+ profile_manager->CreateProfileAsync(
+ path, base::Bind(&UnblockOnProfileInitialized, run_loop.QuitClosure()),
+ base::string16(), std::string(), std::string());
+ // Run the message loop to allow profile creation to take place; the loop is
+ // terminated by OnUnblockOnProfileCreation when the profile is created.
+ run_loop.Run();
+
+ profiles::SwitchToProfile(path, false, kOnProfileSwitchDoNothing,
+ ProfileMetrics::SWITCH_PROFILE_ICON);
+}
+
+void CheckBrowserWindows(const std::vector<std::string>& expected_basepaths) {
+ BrowserList* browser_list = BrowserList::GetInstance();
+ std::vector<std::string> actual_basepaths;
+
+ for (const Browser* browser : *browser_list) {
+ actual_basepaths.push_back(
+ browser->profile()->GetPath().BaseName().AsUTF8Unsafe());
+ }
+
+ if (actual_basepaths.size() != expected_basepaths.size() ||
+ !std::is_permutation(actual_basepaths.cbegin(), actual_basepaths.cend(),
+ expected_basepaths.cbegin())) {
Peter Kasting 2016/07/01 00:53:24 Nit: If you use the four-arg version of is_permuta
WC Leung 2016/07/07 17:01:54 The four-arg version is C++14, so it is not accept
+ ADD_FAILURE()
+ << "Expected profile paths are different from actual profile paths."
+ "\n Actual profile paths: "
+ << base::JoinString(actual_basepaths, ", ")
+ << "\n Expected profile paths: "
+ << base::JoinString(expected_basepaths, ", ");
+ }
+}
+
+// If the user manager is shown when the browser process die, a crash will
+// happen. If a user manager is expected to show, it is needed to hide the user
+// manager before ending the test.
+void WaitForAndHideUserManager() {
+ if (!UserManager::IsShowing()) {
+ base::RunLoop run_loop;
+ UserManager::AddOnUserManagerShownCallbackForTesting(
+ run_loop.QuitClosure());
+ run_loop.Run();
+ }
+ ASSERT_TRUE(UserManager::IsShowing());
+
+ UserManager::Hide();
+}
+
+} // namespace
+
+class StartupBrowserCreatorCorruptProfileTest : public InProcessBrowserTest {
+ public:
+ StartupBrowserCreatorCorruptProfileTest()
+ : test_body_has_run_(false), expect_test_body_to_run_(true) {}
+
+ void SetExpectTestBodyToRun(bool expected_result) {
+ expect_test_body_to_run_ = expected_result;
+ }
+
+ bool DeleteProfileData(const std::string& basepath) {
+ base::FilePath user_data_dir;
+ if (!PathService::Get(chrome::DIR_USER_DATA, &user_data_dir))
+ return false;
+
+ base::FilePath dir_to_delete = user_data_dir.AppendASCII(basepath);
+ return base::DirectoryExists(dir_to_delete) &&
+ base::DeleteFile(dir_to_delete, true);
+ }
+
+ bool RemoveCreateDirectoryPermissionForUserDataDirectory() {
+ base::FilePath user_data_dir;
+ if (!PathService::Get(chrome::DIR_USER_DATA, &user_data_dir))
+ return false;
+
+ return base::DenyFilePermission(user_data_dir, FILE_ADD_SUBDIRECTORY);
Peter Kasting 2016/07/01 00:53:24 Nit: return PathService::Get(chrome::DIR_USER_D
brettw 2016/07/01 22:21:53 I prefer WC's version to this. Please keep it as-i
Peter Kasting 2016/07/02 00:15:42 I'm sorry for not being clear. My suggestion is m
WC Leung 2016/07/07 17:01:54 To me both forms are readable. Which one is easier
+ }
+
+ protected:
+ // For each test, create a bool SetUpUserDataDirectory[testname] function. Use
+ // these functions to do things related to the user data directory, most
+ // importantly remove directories before the browser process starts.
+ bool SetUpUserDataDirectoryForLastOpenedProfileMissing();
+ bool SetUpUserDataDirectoryForLastUsedProfileFallbackToLastOpenedProfiles();
+ bool SetUpUserDataDirectoryForLastUsedProfileFallbackToUserManager();
+ bool SetUpUserDataDirectoryForCannotCreateSystemProfile();
+ bool SetUpUserDataDirectoryForLastUsedProfileFallbackToAnyProfile();
+ bool SetUpUserDataDirectoryForLastUsedProfileFallbackFail();
+ bool SetUpUserDataDirectoryForDoNotStartSignedInProfile();
+ bool SetUpUserDataDirectoryForNoFallbackForUserSelectedProfile();
+ bool SetUpUserDataDirectoryForDeletedProfileFallbackToUserManager();
+
+ void CloseBrowsersSynchronouslyForProfileBasePath(
+ const std::string& basepath) {
+ ProfileManager* profile_manager = g_browser_process->profile_manager();
+ ASSERT_TRUE(profile_manager);
+
+ Profile* profile =
+ profile_manager->GetProfileByPath(
+ profile_manager->user_data_dir().AppendASCII(basepath));
+ ASSERT_TRUE(profile);
+
+ base::RunLoop run_loop;
+ BrowserList::GetInstance()->CloseAllBrowsersWithProfile(
+ profile, base::Bind(&CloseBrowsersSuccessCallback,
+ run_loop.QuitClosure()));
+ }
+
+ void SetUpCommandLine(base::CommandLine* command_line) override {
+ command_line->AppendSwitch(switches::kRestoreLastSession);
+ command_line->AppendSwitch(switches::kNoErrorDialogs);
+ }
+
+ // In this test fixture, SetUpUserDataDirectory must be handled for all
+ // non-PRE_ tests.
+ bool SetUpUserDataDirectory() override {
+#define SET_UP_USER_DATA_DIRECTORY_FOR(testname) \
+if (testing::UnitTest::GetInstance()->current_test_info()->name() == \
+ std::string(#testname)) {\
+ return this->SetUpUserDataDirectoryFor ## testname(); \
+}
+
+ SET_UP_USER_DATA_DIRECTORY_FOR(LastOpenedProfileMissing);
+ SET_UP_USER_DATA_DIRECTORY_FOR(LastUsedProfileFallbackToLastOpenedProfiles);
+ SET_UP_USER_DATA_DIRECTORY_FOR(LastUsedProfileFallbackToUserManager);
+ SET_UP_USER_DATA_DIRECTORY_FOR(CannotCreateSystemProfile);
+ SET_UP_USER_DATA_DIRECTORY_FOR(LastUsedProfileFallbackToAnyProfile);
+ SET_UP_USER_DATA_DIRECTORY_FOR(LastUsedProfileFallbackFail);
+ SET_UP_USER_DATA_DIRECTORY_FOR(DoNotStartSignedInProfile);
+ SET_UP_USER_DATA_DIRECTORY_FOR(NoFallbackForUserSelectedProfile);
+ SET_UP_USER_DATA_DIRECTORY_FOR(DeletedProfileFallbackToUserManager);
+
+#undef SET_UP_USER_DATA_DIRECTORY_FOR
+
+ // If control goes here, it means SetUpUserDataDirectory is not handled.
+ // This is okay for PRE_ tests, but not acceptable for main tests.
+ if (base::StartsWith(
+ testing::UnitTest::GetInstance()->current_test_info()->name(),
+ "PRE_", base::CompareCase::SENSITIVE)) {
+ return true;
+ }
+
+ ADD_FAILURE() << "SetUpUserDataDirectory is not handled by the test.";
+ return false;
+ }
+
+ void TearDownOnMainThread() override {
+ test_body_has_run_ = true;
+ InProcessBrowserTest::TearDownOnMainThread();
+ }
+
+ void TearDown() override {
+ EXPECT_EQ(expect_test_body_to_run_, test_body_has_run_);
+ InProcessBrowserTest::TearDown();
+ }
+
+ private:
+ bool test_body_has_run_;
+ bool expect_test_body_to_run_;
+ DISALLOW_COPY_AND_ASSIGN(StartupBrowserCreatorCorruptProfileTest);
+};
+
+// Most of the tests below has three sections:
+// (1) PRE_ test, which is used to create profiles;
+// (2) StartupBrowserCreatorCorruptProfileTest::SetUpUserDataDirectoryFor...
+// which set up the user data directory for the main test;
+// (3) The main test, where the real test is taken.
+
+// LastOpenedProfileMissing : If any last opened profile is missing, that
+// profile is skipped, but other last opened profiles should be opened in the
+// browser.
+IN_PROC_BROWSER_TEST_F(StartupBrowserCreatorCorruptProfileTest,
+ PRE_LastOpenedProfileMissing) {
+ CreateAndSwitchToProfile("Profile 1");
+ CreateAndSwitchToProfile("Profile 2");
+}
+
+bool StartupBrowserCreatorCorruptProfileTest::
+ SetUpUserDataDirectoryForLastOpenedProfileMissing() {
+ return DeleteProfileData("Profile 1") &&
+ RemoveCreateDirectoryPermissionForUserDataDirectory();
+}
+
+IN_PROC_BROWSER_TEST_F(StartupBrowserCreatorCorruptProfileTest,
+ LastOpenedProfileMissing) {
+ CheckBrowserWindows({"Default", "Profile 2"});
+}
+
+// LastUsedProfileFallbackToLastOpenedProfiles : If the last used profile is
+// missing, it should fallback to any last opened profiles.
+IN_PROC_BROWSER_TEST_F(StartupBrowserCreatorCorruptProfileTest,
+ PRE_LastUsedProfileFallbackToLastOpenedProfiles) {
+ CreateAndSwitchToProfile("Profile 1");
+ CreateAndSwitchToProfile("Profile 2");
+}
+
+bool StartupBrowserCreatorCorruptProfileTest::
+ SetUpUserDataDirectoryForLastUsedProfileFallbackToLastOpenedProfiles() {
+ return DeleteProfileData("Profile 2") &&
+ RemoveCreateDirectoryPermissionForUserDataDirectory();
+}
+
+IN_PROC_BROWSER_TEST_F(StartupBrowserCreatorCorruptProfileTest,
+ LastUsedProfileFallbackToLastOpenedProfiles) {
+ CheckBrowserWindows({"Default", "Profile 1"});
+}
+
+// LastUsedProfileFallbackToUserManager : If all last used profile is
+// missing, it should fallback to user manager. To open the user manager, both
+// the guesst profile and the system profile must be creatable.
+IN_PROC_BROWSER_TEST_F(StartupBrowserCreatorCorruptProfileTest,
+ PRE_LastUsedProfileFallbackToUserManager) {
+ CreateAndSwitchToProfile("Profile 1");
+ CloseBrowsersSynchronouslyForProfileBasePath("Profile 1");
+ CreateAndSwitchToProfile("Profile 2");
+
+ ASSERT_TRUE(base::CreateDirectory(ProfileManager::GetGuestProfilePath()));
+ ASSERT_TRUE(base::CreateDirectory(ProfileManager::GetSystemProfilePath()));
+}
+
+bool StartupBrowserCreatorCorruptProfileTest::
+ SetUpUserDataDirectoryForLastUsedProfileFallbackToUserManager() {
+ return DeleteProfileData("Default") &&
+ DeleteProfileData("Profile 2") &&
+ RemoveCreateDirectoryPermissionForUserDataDirectory();
+}
+
+IN_PROC_BROWSER_TEST_F(StartupBrowserCreatorCorruptProfileTest,
+ LastUsedProfileFallbackToUserManager) {
+ CheckBrowserWindows({});
+ WaitForAndHideUserManager();
+}
+
+
+// CannotCreateSystemProfile : If the system profile cannot be created, the user
+// manager should not be shown. Fallback to any other profile.
+IN_PROC_BROWSER_TEST_F(StartupBrowserCreatorCorruptProfileTest,
+ PRE_CannotCreateSystemProfile) {
+ CreateAndSwitchToProfile("Profile 1");
+ CloseBrowsersSynchronouslyForProfileBasePath("Profile 1");
+ CreateAndSwitchToProfile("Profile 2");
+
+ // Create the guest profile path only. The system profile cannot be created.
+ ASSERT_TRUE(base::CreateDirectory(ProfileManager::GetGuestProfilePath()));
+}
+
+bool StartupBrowserCreatorCorruptProfileTest::
+ SetUpUserDataDirectoryForCannotCreateSystemProfile() {
+ return DeleteProfileData("Default") &&
+ DeleteProfileData("Profile 2") &&
+ RemoveCreateDirectoryPermissionForUserDataDirectory();
+}
+
+IN_PROC_BROWSER_TEST_F(StartupBrowserCreatorCorruptProfileTest,
+ CannotCreateSystemProfile) {
+ CheckBrowserWindows({"Profile 1"});
+ // WaitForAndHideUserManager();
Peter Kasting 2016/07/01 00:53:24 Uncomment or remove
WC Leung 2016/07/07 17:01:54 Done. Sorry for leaving testing code here.
+}
+
+// LastUsedProfileFallbackToAnyProfile : If all the last opened profiles and the
+// guest profile cannot be opened, fallback to any non-signin profile.
+IN_PROC_BROWSER_TEST_F(StartupBrowserCreatorCorruptProfileTest,
+ PRE_LastUsedProfileFallbackToAnyProfile) {
+ CreateAndSwitchToProfile("Profile 1");
+ CloseBrowsersSynchronouslyForProfileBasePath("Profile 1");
+ CreateAndSwitchToProfile("Profile 2");
+}
+
+bool StartupBrowserCreatorCorruptProfileTest::
+ SetUpUserDataDirectoryForLastUsedProfileFallbackToAnyProfile() {
+ return DeleteProfileData("Default") &&
+ DeleteProfileData("Profile 2") &&
+ RemoveCreateDirectoryPermissionForUserDataDirectory();
+}
+
+IN_PROC_BROWSER_TEST_F(StartupBrowserCreatorCorruptProfileTest,
+ LastUsedProfileFallbackToAnyProfile) {
+ CheckBrowserWindows({"Profile 1"});
+}
+
+// LastUsedProfileFallbackFail : If all the options fail, quit the browser with
+// an appropriate exit code (0).
+bool StartupBrowserCreatorCorruptProfileTest::
+ SetUpUserDataDirectoryForLastUsedProfileFallbackFail() {
+ SetExpectTestBodyToRun(false);
+ return RemoveCreateDirectoryPermissionForUserDataDirectory();
+}
+
+IN_PROC_BROWSER_TEST_F(StartupBrowserCreatorCorruptProfileTest,
+ LastUsedProfileFallbackFail) {
+ ADD_FAILURE() << "Test body is not expected to run.";
+}
+
+// DoNotStartSignedInProfile : Profile that are locked are not expected to be
+// started in any stage of the fallback. Since there is no other profile to
+// fallback to in this test, chromium should not start.
+IN_PROC_BROWSER_TEST_F(StartupBrowserCreatorCorruptProfileTest,
+ PRE_DoNotStartSignedInProfile) {
+ // Sign out the default profile and wait for the user manager.
+ profiles::LockProfile(browser()->profile());
+ WaitForAndHideUserManager();
+}
+
+bool StartupBrowserCreatorCorruptProfileTest::
+ SetUpUserDataDirectoryForDoNotStartSignedInProfile() {
+ SetExpectTestBodyToRun(false);
+ return RemoveCreateDirectoryPermissionForUserDataDirectory();
+}
+
+IN_PROC_BROWSER_TEST_F(StartupBrowserCreatorCorruptProfileTest,
+ DoNotStartSignedInProfile) {
+ ADD_FAILURE() << "Test body is not expected to run.";
+}
+
+// NoFallbackForUserSelectedProfile : No fallback should be attempted if the
+// profile is selected by --profile-directory switch. Chromium should not start
+// if the specified profile could not initialize.
+IN_PROC_BROWSER_TEST_F(StartupBrowserCreatorCorruptProfileTest,
+ PRE_NoFallbackForUserSelectedProfile) {
+ CreateAndSwitchToProfile("Profile 1");
+ CreateAndSwitchToProfile("Profile 2");
+}
+
+bool StartupBrowserCreatorCorruptProfileTest::
+ SetUpUserDataDirectoryForNoFallbackForUserSelectedProfile() {
+ base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
+ command_line->AppendSwitchASCII(switches::kProfileDirectory, "Profile 1");
+ SetExpectTestBodyToRun(false);
+ return DeleteProfileData("Profile 1") &&
+ RemoveCreateDirectoryPermissionForUserDataDirectory();
+}
+
+IN_PROC_BROWSER_TEST_F(StartupBrowserCreatorCorruptProfileTest,
+ NoFallbackForUserSelectedProfile) {
+ ADD_FAILURE() << "Test body is not expected to run.";
+}
+
+// DeletedProfileFallbackToUserManager : If the profile is deleted, which is
+// known by having its entry missing in ProfileAttributesStorage, we do not
+// attempt to open the profile. Instead shows the user manager.
+IN_PROC_BROWSER_TEST_F(StartupBrowserCreatorCorruptProfileTest,
+ PRE_DeletedProfileFallbackToUserManager) {
+ ASSERT_TRUE(base::CreateDirectory(ProfileManager::GetGuestProfilePath()));
+ ASSERT_TRUE(base::CreateDirectory(ProfileManager::GetSystemProfilePath()));
+}
+
+bool StartupBrowserCreatorCorruptProfileTest::
+ SetUpUserDataDirectoryForDeletedProfileFallbackToUserManager() {
+ base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
+ // Simulate a deleted profile by not creating the profile at the first place.
+ command_line->AppendSwitchASCII(switches::kProfileDirectory, "Not Found");
+ return RemoveCreateDirectoryPermissionForUserDataDirectory();
+}
+
+IN_PROC_BROWSER_TEST_F(StartupBrowserCreatorCorruptProfileTest,
+ DeletedProfileFallbackToUserManager) {
+ CheckBrowserWindows({});
+ WaitForAndHideUserManager();
+}

Powered by Google App Engine
This is Rietveld 408576698