blob: fef863fa4b5f06160e222d39ab35a4d72db2d276 [file] [log] [blame]
// Copyright 2022 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 <vector>
#include "base/files/file_path.h"
#include "base/memory/raw_ptr.h"
#include "base/strings/strcat.h"
#include "base/test/bind.h"
#include "build/build_config.h"
#include "build/chromeos_buildflags.h"
#include "chrome/browser/browser_features.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/chrome_browser_main.h"
#include "chrome/browser/chrome_browser_main_extra_parts.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_test_util.h"
#include "chrome/browser/profiles/profile_window.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/browser/ui/browser_finder.h"
#include "chrome/browser/ui/profile_picker.h"
#include "chrome/common/chrome_constants.h"
#include "chrome/common/pref_names.h"
#include "chrome/test/base/in_process_browser_test.h"
#include "components/prefs/pref_service.h"
#include "content/public/test/browser_test.h"
#include "testing/gmock/include/gmock/gmock.h"
using ::testing::_;
using ::testing::InSequence;
using ::testing::Matcher;
using ::testing::Mock;
using ::testing::Property;
using ::testing::ValuesIn;
#if BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_CHROMEOS_ASH)
#error Not supported on this platform.
#endif
namespace {
class MockMainExtraParts : public ChromeBrowserMainExtraParts {
public:
MOCK_METHOD(void, PreProfileInit, ());
MOCK_METHOD(void, PostProfileInit, (Profile*, bool));
MOCK_METHOD(void, PreBrowserStart, ());
MOCK_METHOD(void, PostBrowserStart, ());
MOCK_METHOD(void, PreMainMessageLoopRun, ());
};
const char kOtherProfileDirPath[] = "Other";
MATCHER_P(BaseNameEquals,
basename,
base::StrCat({negation ? "doesn't equal " : "equals ", basename})) {
return arg == base::FilePath::FromASCII(basename);
}
Matcher<Profile*> HasBaseName(const char* basename) {
return Property("basename", &Profile::GetBaseName, BaseNameEquals(basename));
}
struct MultiProfileStartupTestParam {
// Whether features::kObserverBasedPostProfileInit should be enabled.
const bool should_enable_profile_observer;
// Whether the profile picker should be shown on startup.
const bool should_show_profile_picker;
struct PostInitExpectedCall {
// Matcher for the expected `profile` argument to `PostProfileInit()`
const Matcher<Profile*> profile_matcher;
// Expected value for the `is_initial_profile` argument to
// `PostProfileInit()`
const bool is_initial_profile;
};
// Call expectations for the `PostProfileInit()` method. The expectations
// should themselves be listed in the expected call order.
//
// The first one is checked in `CreatedBrowserMainParts()` as part of startup,
// and the remaining ones in the test body.
const std::vector<PostInitExpectedCall> expected_post_profile_init_call_args;
};
const MultiProfileStartupTestParam kTestParams[] = {
{false, false, {{HasBaseName(chrome::kInitialProfile), true}}},
{false, true, {{Property(&Profile::IsGuestSession, true), true}}},
{true,
false,
{{HasBaseName(chrome::kInitialProfile), true},
{HasBaseName(kOtherProfileDirPath), false}}},
{true,
true,
{// TODO(https://crbug.com/1150326): The first call with guest profile
// should be skipped.
{Property(&Profile::IsGuestSession, true), true},
#if !BUILDFLAG(IS_CHROMEOS_LACROS)
// Lacros loads the primary profile earlier and it is already loaded when
// `PostProfileInit()` is called for the first time.
// TODO(https://crbug.com/1150326): Re-add the primary profile once
// `PostProfileInit()` is called for profiles that were created before
// the initial startup profile.
{HasBaseName(chrome::kInitialProfile), false},
#endif
{HasBaseName(kOtherProfileDirPath), false}}}};
// Creates a new profile to be picked up on the actual test.
void SetUpSecondaryProfileForPreTest(
const base::FilePath& profile_dir_basename) {
ProfileManager* profile_manager = g_browser_process->profile_manager();
base::FilePath profile_path =
profile_manager->user_data_dir().Append(profile_dir_basename);
profiles::testing::CreateProfileSync(profile_manager, profile_path);
// Mark newly created profile as active.
ProfileAttributesEntry* entry =
profile_manager->GetProfileAttributesStorage()
.GetProfileAttributesWithPath(profile_path);
ASSERT_NE(entry, nullptr);
entry->SetActiveTimeToNow();
}
void CreateBrowserForProfileDir(const base::FilePath& profile_dir_basename) {
profiles::testing::SwitchToProfileSync(
g_browser_process->profile_manager()->user_data_dir().Append(
profile_dir_basename));
}
} // namespace
class ChromeMultiProfileStartupBrowserTestBase
: public InProcessBrowserTest,
public testing::WithParamInterface<MultiProfileStartupTestParam> {
public:
ChromeMultiProfileStartupBrowserTestBase() {
// Avoid providing a URL for the browser to open, allows the profile picker
// to be displayed on startup when it is enabled.
set_open_about_blank_on_browser_launch(false);
if (GetParam().should_enable_profile_observer) {
feature_list_.InitAndEnableFeature(
features::kObserverBasedPostProfileInit);
} else {
feature_list_.InitWithFeatures({},
{features::kObserverBasedPostProfileInit});
}
}
void CreatedBrowserMainParts(content::BrowserMainParts* parts) override {
InProcessBrowserTest::CreatedBrowserMainParts(parts);
// Skip expectations preparation for the PRE_ step.
if (GetTestPreCount() != 0)
return;
auto mock_part = std::make_unique<MockMainExtraParts>();
mock_part_ = mock_part.get();
static_cast<ChromeBrowserMainParts*>(parts)->AddParts(std::move(mock_part));
// At least one entry for the initial call is needed.
ASSERT_FALSE(GetParam().expected_post_profile_init_call_args.empty());
// The basic callbacks should be called only once.
EXPECT_CALL(*mock_part_, PreProfileInit()).Times(1);
EXPECT_CALL(*mock_part_, PreBrowserStart()).Times(1);
EXPECT_CALL(*mock_part_, PostBrowserStart()).Times(1);
EXPECT_CALL(*mock_part_, PreMainMessageLoopRun()).Times(1);
{
const auto& call_args = GetParam().expected_post_profile_init_call_args;
InSequence s;
for (const auto& expected_args : call_args) {
EXPECT_CALL(*mock_part_,
PostProfileInit(expected_args.profile_matcher,
expected_args.is_initial_profile));
}
}
}
raw_ptr<MockMainExtraParts> mock_part_;
protected:
base::test::ScopedFeatureList feature_list_;
};
IN_PROC_BROWSER_TEST_P(ChromeMultiProfileStartupBrowserTestBase,
PRE_PostProfileInitInvocation) {
SetUpSecondaryProfileForPreTest(
base::FilePath::FromASCII(kOtherProfileDirPath));
g_browser_process->local_state()->SetBoolean(
prefs::kBrowserShowProfilePickerOnStartup,
GetParam().should_show_profile_picker);
// Need to close the browser window manually so that the real test does not
// treat it as session restore.
CloseAllBrowsers();
}
// Make sure that the second profile creation causes `PostProfileInit()` to be
// called a second time.
IN_PROC_BROWSER_TEST_P(ChromeMultiProfileStartupBrowserTestBase,
PostProfileInitInvocation) {
EXPECT_EQ(2u, g_browser_process->profile_manager()->GetNumberOfProfiles());
if (GetParam().should_show_profile_picker) {
EXPECT_EQ(0u, chrome::GetTotalBrowserCount());
EXPECT_TRUE(ProfilePicker::IsOpen());
} else {
EXPECT_EQ(1u, chrome::GetTotalBrowserCount());
EXPECT_NE(base::FilePath::FromASCII(kOtherProfileDirPath),
browser()->profile()->GetPath().BaseName());
EXPECT_FALSE(ProfilePicker::IsOpen());
}
// TODO(https://crbug.com/1288766): In some cases, profile creation is
// triggered by restoring the previously opened profile, and the test
// expectations in terms of `PostProfileInit()` calls can
// be met without opening browsers. We still open them for consistency, at
// least until we can make the test behaviour stricter.
if (GetParam().should_show_profile_picker) {
// No browser was previously open, as verified at the beginning of the test.
// So we start by opening the browser for the default profile.
CreateBrowserForProfileDir(
base::FilePath::FromASCII(chrome::kInitialProfile));
}
CreateBrowserForProfileDir(base::FilePath::FromASCII(kOtherProfileDirPath));
EXPECT_EQ(2u, chrome::GetTotalBrowserCount());
}
INSTANTIATE_TEST_SUITE_P(All,
ChromeMultiProfileStartupBrowserTestBase,
ValuesIn(kTestParams));