chromium/chrome/browser/profiles/profile_manager_browsertest.cc

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

#include <stddef.h>
#include <string>

#include "base/command_line.h"
#include "base/containers/to_vector.h"
#include "base/files/file_path_watcher.h"
#include "base/files/file_util.h"
#include "base/functional/bind.h"
#include "base/functional/callback.h"
#include "base/functional/callback_helpers.h"
#include "base/ranges/algorithm.h"
#include "base/run_loop.h"
#include "base/strings/utf_string_conversions.h"
#include "base/test/bind.h"
#include "base/test/test_future.h"
#include "base/test/test_timeouts.h"
#include "build/build_config.h"
#include "build/chromeos_buildflags.h"
#include "chrome/browser/apps/platform_apps/shortcut_manager.h"
#include "chrome/browser/browser_features.h"
#include "chrome/browser/password_manager/profile_password_store_factory.h"
#include "chrome/browser/policy/policy_test_utils.h"
#include "chrome/browser/profiles/keep_alive/profile_keep_alive_types.h"
#include "chrome/browser/profiles/keep_alive/scoped_profile_keep_alive.h"
#include "chrome/browser/profiles/profile_attributes_entry.h"
#include "chrome/browser/profiles/profile_attributes_storage.h"
#include "chrome/browser/profiles/profile_manager.h"
#include "chrome/browser/profiles/profile_metrics.h"
#include "chrome/browser/profiles/profile_test_util.h"
#include "chrome/browser/profiles/profile_window.h"
#include "chrome/browser/profiles/profiles_state.h"
#include "chrome/browser/signin/identity_test_environment_profile_adaptor.h"
#include "chrome/browser/ui/browser_finder.h"
#include "chrome/browser/ui/browser_list.h"
#include "chrome/browser/ui/browser_window.h"
#include "chrome/common/chrome_constants.h"
#include "chrome/common/chrome_switches.h"
#include "chrome/common/pref_names.h"
#include "chrome/test/base/in_process_browser_test.h"
#include "chrome/test/base/profile_deletion_observer.h"
#include "chrome/test/base/testing_browser_process.h"
#include "components/keep_alive_registry/keep_alive_types.h"
#include "components/keep_alive_registry/scoped_keep_alive.h"
#include "components/keyed_service/content/browser_context_dependency_manager.h"
#include "components/password_manager/core/browser/password_form.h"
#include "components/password_manager/core/browser/password_store/password_store_consumer.h"
#include "components/password_manager/core/browser/password_store/password_store_interface.h"
#include "components/policy/core/common/policy_map.h"
#include "components/policy/policy_constants.h"
#include "components/prefs/pref_service.h"
#include "components/supervised_user/core/common/pref_names.h"
#include "content/public/browser/browsing_data_remover.h"
#include "content/public/test/browser_test.h"
#include "content/public/test/test_utils.h"

#if BUILDFLAG(IS_CHROMEOS_ASH)
#include "ash/constants/ash_switches.h"
#include "base/path_service.h"
#include "chrome/common/chrome_paths.h"
#include "chromeos/ash/components/browser_context_helper/browser_context_helper.h"
#include "chromeos/ash/components/browser_context_helper/browser_context_types.h"
#include "testing/gtest/include/gtest/gtest.h"
#endif

#if BUILDFLAG(IS_CHROMEOS_LACROS)
#include "chromeos/crosapi/mojom/crosapi.mojom.h"
#include "chromeos/lacros/lacros_test_helper.h"
#include "chromeos/startup/browser_init_params.h"
#include "components/supervised_user/core/common/supervised_user_constants.h"
#include "content/public/test/test_launcher.h"
#endif  // BUILDFLAG(IS_CHROMEOS_LACROS)

namespace {

void ProfileCreationComplete(base::OnceClosure completion_callback,
                             Profile* profile) {}

// An observer that returns back to test code after one or more profiles was
// deleted. It also creates ScopedKeepAlive and ScopedProfileKeepAlive objects
// to prevent browser shutdown started in case browser has become windowless.
class MultipleProfileDeletionObserver
    : public ProfileAttributesStorage::Observer {};

void EphemeralProfileCreationComplete(base::OnceClosure completion_callback,
                                      Profile* profile) {}

class ProfileRemovalObserver : public ProfileAttributesStorage::Observer {};

// The class serves to retrieve passwords from PasswordStore asynchronously. It
// used by ProfileManagerBrowserTest.DeletePasswords on some platforms.
class PasswordStoreConsumerVerifier
    : public password_manager::PasswordStoreConsumer {};

base::FilePath GetFirstNonSigninNonLockScreenAppProfile(
    ProfileAttributesStorage* storage) {}

}  // namespace

// This file contains tests for the ProfileManager that require a heavyweight
// InProcessBrowserTest.  These include tests involving profile deletion.

class ProfileManagerBrowserTestBase : public InProcessBrowserTest {};

class ProfileManagerBrowserTest : public ProfileManagerBrowserTestBase,
                                  public testing::WithParamInterface<bool> {};

// CrOS multi-profiles implementation is too different for these tests.
#if !BUILDFLAG(IS_CHROMEOS_ASH)
// TODO(crbug.com/40818380): Test failed on Mac.
#if BUILDFLAG(IS_MAC)
#define MAYBE_DeleteSingletonProfile
#else
#define MAYBE_DeleteSingletonProfile
#endif

// On Lacros, the single profile would be the main profile which should never be
// deleted.
#if !BUILDFLAG(IS_CHROMEOS_LACROS)
IN_PROC_BROWSER_TEST_P(ProfileManagerBrowserTest,
                       MAYBE_DeleteSingletonProfile) {}
#endif  // !BUILDFLAG(IS_CHROMEOS_LACROS)

// Delete inactive profile in a multi profile setup and make sure current
// browser is not affected.
IN_PROC_BROWSER_TEST_P(ProfileManagerBrowserTest, DeleteInactiveProfile) {}

IN_PROC_BROWSER_TEST_P(ProfileManagerBrowserTest, DeleteCurrentProfile) {}

// Test is flaky. https://crbug.com/1206184
#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_MAC) || BUILDFLAG(IS_WIN)
#define MAYBE_DeleteAllProfiles
#else
#define MAYBE_DeleteAllProfiles
#endif
IN_PROC_BROWSER_TEST_P(ProfileManagerBrowserTest, MAYBE_DeleteAllProfiles) {}
#endif  // !BUILDFLAG(IS_CHROMEOS_ASH)

IN_PROC_BROWSER_TEST_P(ProfileManagerBrowserTest, ProfileFromProfileKey) {}

#if BUILDFLAG(IS_CHROMEOS_ASH)

class ProfileManagerCrOSBrowserTest : public ProfileManagerBrowserTest {
 protected:
  void SetUpCommandLine(base::CommandLine* command_line) override {
    // Use a user hash other than the default
    // ash::BrowserContextHelper::kTestUserBrowserContextDirName so that
    // the prefix case is tested.
    command_line->AppendSwitchASCII(ash::switches::kLoginProfile,
                                    "test-user-hash");
  }
};

IN_PROC_BROWSER_TEST_P(ProfileManagerCrOSBrowserTest, GetLastUsedProfile) {
  // Make sure that last used profile is correct.
  Profile* last_used_profile = ProfileManager::GetLastUsedProfile();
  EXPECT_TRUE(last_used_profile != nullptr);

  base::FilePath profile_path;
  base::PathService::Get(chrome::DIR_USER_DATA, &profile_path);

  profile_path = profile_path.Append(
      ash::BrowserContextHelper::GetUserBrowserContextDirName(
          "test-user-hash"));
  EXPECT_EQ(profile_path.value(), last_used_profile->GetPath().value());
}

#endif  // BUILDFLAG(IS_CHROMEOS_ASH)

// ChromeOS doesn't support multiple profiles.
#if !BUILDFLAG(IS_CHROMEOS_ASH)
IN_PROC_BROWSER_TEST_P(ProfileManagerBrowserTest, CreateProfileWithCallback) {}
#endif  // !BUILDFLAG(IS_CHROMEOS_ASH)

IN_PROC_BROWSER_TEST_P(ProfileManagerBrowserTest, SwitchToProfile) {}

// Prepares the setup for AddMultipleProfiles test, creates multiple browser
// windows with multiple browser windows.
IN_PROC_BROWSER_TEST_P(ProfileManagerBrowserTest, PRE_AddMultipleProfiles) {}

IN_PROC_BROWSER_TEST_P(ProfileManagerBrowserTest, AddMultipleProfiles) {}

// Regression test for https://crbug.com/1472849
IN_PROC_BROWSER_TEST_F(ProfileManagerBrowserTestBase,
                       ConcurrentCreationAsyncAndSync) {}

IN_PROC_BROWSER_TEST_P(ProfileManagerBrowserTest, EphemeralProfile) {}

// The test makes sense on those platforms where the keychain exists.
#if !BUILDFLAG(IS_WIN) && !BUILDFLAG(IS_CHROMEOS_ASH)
IN_PROC_BROWSER_TEST_P(ProfileManagerBrowserTest, DeletePasswords) {}
#endif  // !BUILDFLAG(IS_WIN) && !BUILDFLAG(IS_CHROMEOS_ASH)

// Tests Profile::HasOffTheRecordProfile, Profile::IsValidProfile and the
// profile counts in ProfileManager with respect to the creation and destruction
// of incognito profiles.
IN_PROC_BROWSER_TEST_P(ProfileManagerBrowserTest, IncognitoProfile) {}

#if BUILDFLAG(IS_CHROMEOS_ASH)
INSTANTIATE_TEST_SUITE_P(DestroyProfileOnBrowserClose,
                         ProfileManagerBrowserTest,
                         testing::Values(false));

INSTANTIATE_TEST_SUITE_P(DestroyProfileOnBrowserClose,
                         ProfileManagerCrOSBrowserTest,
                         testing::Bool());
#else
INSTANTIATE_TEST_SUITE_P();
#endif  // BUILDFLAG(IS_CHROMEOS_ASH)

#if !BUILDFLAG(IS_CHROMEOS_ASH)

const base::FilePath::CharType kNonAsciiProfileDir[] =);

class ProfileManagerNonAsciiBrowserTest : public ProfileManagerBrowserTestBase {};

IN_PROC_BROWSER_TEST_F(ProfileManagerNonAsciiBrowserTest,
                       LaunchInNonAsciiProfileDirectoryDoesntCrash) {}

#endif  //! BUILDFLAG(IS_CHROMEOS_ASH)

#if BUILDFLAG(IS_CHROMEOS_LACROS)
// Tests transition between child and regular user profile that happens when
// supervision is added to or removed from the user account.
// Uses PRE tests to setup a new profile and the actual test to test the profile
// type transition.
// Uses parametrization to cover two transition directions.
enum class TransitionType {
  kChildToRegular,
  kRegularToChild,
};

class ChildProfileTransitionBrowserTest
    : public ProfileManagerBrowserTestBase,
      public testing::WithParamInterface<enum TransitionType> {
 protected:
  ChildProfileTransitionBrowserTest() = default;
  ~ChildProfileTransitionBrowserTest() = default;

  void CreatedBrowserMainParts(
      content::BrowserMainParts* browser_main_parts) override {
    crosapi::mojom::BrowserInitParamsPtr init_params =
        crosapi::mojom::BrowserInitParams::New();

    const TransitionType transition = GetParam();
    if (transition == TransitionType::kChildToRegular) {
      init_params->session_type =
          content::IsPreTest() ? crosapi::mojom::SessionType::kChildSession
                               : crosapi::mojom::SessionType::kRegularSession;
    } else if (transition == TransitionType::kRegularToChild) {
      init_params->session_type =
          content::IsPreTest() ? crosapi::mojom::SessionType::kRegularSession
                               : crosapi::mojom::SessionType::kChildSession;
    } else {
      NOTREACHED_IN_MIGRATION();
    }

    chromeos::BrowserInitParams::SetInitParamsForTests(std::move(init_params));

    ProfileManagerBrowserTestBase::CreatedBrowserMainParts(browser_main_parts);
  }

  bool IsChildProfileExpected() const {
    const TransitionType transition = GetParam();
    const bool is_pre_test = content::IsPreTest();

    if (transition == TransitionType::kChildToRegular) {
      return is_pre_test;
    }
    if (transition == TransitionType::kRegularToChild) {
      return !is_pre_test;
    }
    NOTREACHED_IN_MIGRATION();
    return false;
  }

  const ProfileAttributesEntry* GetProfileAttributesEntry(
      const Profile* profile) const {
    ProfileAttributesStorage& storage =
        g_browser_process->profile_manager()->GetProfileAttributesStorage();
    return storage.GetProfileAttributesWithPath(profile->GetPath());
  }
};

INSTANTIATE_TEST_SUITE_P(ChildToRegular,
                         ChildProfileTransitionBrowserTest,
                         testing::Values(TransitionType::kChildToRegular));

INSTANTIATE_TEST_SUITE_P(RegularToChild,
                         ChildProfileTransitionBrowserTest,
                         testing::Values(TransitionType::kRegularToChild));

IN_PROC_BROWSER_TEST_P(ChildProfileTransitionBrowserTest, PRE_Transition) {
  const bool is_child_profile_expected = IsChildProfileExpected();

  const Profile* profile = browser()->profile();
  // Check profile object.
  ASSERT_TRUE(profile);
  EXPECT_EQ(is_child_profile_expected, profile->IsChild());
  EXPECT_EQ(profile->GetPrefs()->GetString(prefs::kSupervisedUserId),
            is_child_profile_expected ? supervised_user::kChildAccountSUID
                                      : std::string());

  // Check stored profile attributes.
  const ProfileAttributesEntry* entry = GetProfileAttributesEntry(profile);
  ASSERT_NE(entry, nullptr);
  EXPECT_EQ(is_child_profile_expected, entry->IsSupervised());
}

IN_PROC_BROWSER_TEST_P(ChildProfileTransitionBrowserTest, Transition) {
  const bool is_child_profile_expected = IsChildProfileExpected();

  const Profile* profile = browser()->profile();
  // Check profile object.
  ASSERT_TRUE(profile);
  EXPECT_EQ(is_child_profile_expected, profile->IsChild());
  EXPECT_EQ(profile->GetPrefs()->GetString(prefs::kSupervisedUserId),
            is_child_profile_expected ? supervised_user::kChildAccountSUID
                                      : std::string());

  // Check stored profile attributes.
  const ProfileAttributesEntry* entry = GetProfileAttributesEntry(profile);
  ASSERT_NE(entry, nullptr);
  EXPECT_EQ(is_child_profile_expected, entry->IsSupervised());
}
#endif