blob: ad524ad74a28fed7c8d40a25c039fb657dbce728 [file] [log] [blame]
// Copyright 2022 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <optional>
#include <string>
#include <utility>
#include <variant>
#include "base/command_line.h"
#include "base/feature_list.h"
#include "base/task/thread_pool/thread_pool_instance.h"
#include "build/build_config.h"
#include "content/public/app/content_main_delegate.h"
#include "content/public/browser/content_browser_client.h"
#include "content/public/common/content_client.h"
#include "content/public/common/main_function_params.h"
#include "content/public/gpu/content_gpu_client.h"
#include "content/public/renderer/content_renderer_client.h"
#include "content/public/test/browser_test.h"
#include "content/public/test/content_browser_test.h"
#include "content/public/test/content_browser_test_shell_main_delegate.h"
#include "content/public/utility/content_utility_client.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/abseil-cpp/absl/functional/overload.h"
namespace variations {
class VariationsIdsProvider;
}
namespace content {
namespace {
using ::testing::_;
using ::testing::AtMost;
using ::testing::DoAll;
using ::testing::Invoke;
using ::testing::Return;
using InvokedIn = ContentMainDelegate::InvokedIn;
using VariationsIdsProvider = variations::VariationsIdsProvider;
// Mocks only the cross-platform methods of ContentMainDelegate. Also calls the
// parent implementation of each method, since the test setup may depend on it.
class MockContentMainDelegate : public ContentBrowserTestShellMainDelegate {
public:
using Super = ContentBrowserTestShellMainDelegate;
MOCK_METHOD(std::optional<int>, MockBasicStartupComplete, ());
std::optional<int> BasicStartupComplete() override {
std::optional<int> result = MockBasicStartupComplete();
// Check for early exit code.
if (result.has_value())
return result;
return Super::BasicStartupComplete();
}
MOCK_METHOD(void, MockPreSandboxStartup, ());
void PreSandboxStartup() override {
MockPreSandboxStartup();
Super::PreSandboxStartup();
}
MOCK_METHOD(void, MockSandboxInitialized, (const std::string&));
void SandboxInitialized(const std::string& process_type) override {
MockSandboxInitialized(process_type);
Super::SandboxInitialized(process_type);
}
// The return value of RunProcess is platform-dependent and the startup
// sequence depends heavily on it, so don't allow it to be mocked.
MOCK_METHOD(void, MockRunProcess, (const std::string&, MainFunctionParams));
std::variant<int, MainFunctionParams> RunProcess(
const std::string& process_type,
MainFunctionParams main_function_params) override {
// MainFunctionParams is move-only so pass a dummy to the mock.
MainFunctionParams dummy_main_function_params(
base::CommandLine::ForCurrentProcess());
MockRunProcess(process_type, std::move(dummy_main_function_params));
return Super::RunProcess(process_type, std::move(main_function_params));
}
MOCK_METHOD(void, MockProcessExiting, (const std::string&));
void ProcessExiting(const std::string& process_type) override {
MockProcessExiting(process_type);
Super::ProcessExiting(process_type);
}
// The return value of ShouldLockSchemeRegistry is dangerous to override so
// don't allow it to be mocked.
MOCK_METHOD(void, MockShouldLockSchemeRegistry, ());
bool ShouldLockSchemeRegistry() override {
MockShouldLockSchemeRegistry();
return Super::ShouldLockSchemeRegistry();
}
MOCK_METHOD(std::optional<int>, MockPreBrowserMain, ());
std::optional<int> PreBrowserMain() override {
std::optional<int> result = MockPreBrowserMain();
// Check for early exit code.
if (result.has_value())
return result;
return Super::PreBrowserMain();
}
// No need to call the parent delegate for these methods since they have no
// side effects.
MOCK_METHOD(bool, ShouldCreateFeatureList, (InvokedIn), (override));
MOCK_METHOD(bool, ShouldInitializeMojo, (InvokedIn), (override));
MOCK_METHOD(VariationsIdsProvider*, MockCreateVariationsIdsProvider, ());
VariationsIdsProvider* CreateVariationsIdsProvider() override {
VariationsIdsProvider* result = MockCreateVariationsIdsProvider();
if (result)
return result;
return Super::CreateVariationsIdsProvider();
}
MOCK_METHOD(std::optional<int>, MockPostEarlyInitialization, (InvokedIn));
std::optional<int> PostEarlyInitialization(InvokedIn invoked_in) override {
std::optional<int> result = MockPostEarlyInitialization(invoked_in);
// Check for early exit code.
if (result.has_value())
return result;
return Super::PostEarlyInitialization(invoked_in);
}
MOCK_METHOD(ContentClient*, MockCreateContentClient, ());
ContentClient* CreateContentClient() override {
ContentClient* result = MockCreateContentClient();
if (result)
return result;
return Super::CreateContentClient();
}
MOCK_METHOD(ContentBrowserClient*, MockCreateContentBrowserClient, ());
ContentBrowserClient* CreateContentBrowserClient() override {
ContentBrowserClient* result = MockCreateContentBrowserClient();
if (result)
return result;
return Super::CreateContentBrowserClient();
}
MOCK_METHOD(ContentGpuClient*, MockCreateContentGpuClient, ());
ContentGpuClient* CreateContentGpuClient() override {
ContentGpuClient* result = MockCreateContentGpuClient();
if (result)
return result;
return Super::CreateContentGpuClient();
}
MOCK_METHOD(ContentRendererClient*, MockCreateContentRendererClient, ());
ContentRendererClient* CreateContentRendererClient() override {
ContentRendererClient* result = MockCreateContentRendererClient();
if (result)
return result;
return Super::CreateContentRendererClient();
}
MOCK_METHOD(ContentUtilityClient*, MockCreateContentUtilityClient, ());
ContentUtilityClient* CreateContentUtilityClient() override {
ContentUtilityClient* result = MockCreateContentUtilityClient();
if (result)
return result;
return Super::CreateContentUtilityClient();
}
};
MATCHER_P(InvokedInMatcher, process_type, "") {
// `arg` is an std::variant. Return true if the type held by the variant is
// correct for `process_type` (empty means the browser process).
return std::visit(absl::Overload{
[&](ContentMainDelegate::InvokedInBrowserProcess) {
return process_type.empty();
},
[&](ContentMainDelegate::InvokedInChildProcess) {
return !process_type.empty();
},
},
arg);
}
// Tests that methods of ContentMainDelegate are called in the expected order.
class ContentMainRunnerImplBrowserTest : public ContentBrowserTest {
protected:
using Self = ContentMainRunnerImplBrowserTest;
using Super = ContentBrowserTest;
void SetUp() override {
// Empty process name means the browser process.
const std::string kBrowserProcessType = "";
// These methods may or may not be called, depending on configuration.
EXPECT_CALL(mock_delegate_, MockShouldLockSchemeRegistry())
.Times(AtMost(1));
EXPECT_CALL(mock_delegate_, MockCreateVariationsIdsProvider())
.Times(AtMost(1));
// CreateContentClient() is only called if GetContentClient() returns null.
EXPECT_CALL(mock_delegate_, MockCreateContentClient()).Times(AtMost(1));
// ContentBrowserTestShellMainDelegate calls these internally, so allow
// extra calls to them out of sequence.
EXPECT_CALL(mock_delegate_, ShouldCreateFeatureList(_))
.WillRepeatedly(Return(true));
EXPECT_CALL(mock_delegate_, ShouldInitializeMojo(_))
.WillRepeatedly(Return(true));
// Expect the following entry points to be called, in order.
//
// BrowserTestBase::SetUp() calls ContentMain(), which instantiates a
// ContentMainRunnerImpl, which calls the entry points in
// ContentMainDelegate. So test expectations must be installed before
// calling the inherited SetUp().
::testing::InSequence s;
EXPECT_CALL(mock_delegate_, MockBasicStartupComplete())
.WillOnce(DoAll(
// Test the starting state of ContentMainRunnerImpl.
Invoke(this, &Self::TestBasicStartupComplete),
Return(std::nullopt)));
EXPECT_CALL(mock_delegate_, MockCreateContentBrowserClient());
EXPECT_CALL(mock_delegate_, MockPreSandboxStartup());
EXPECT_CALL(mock_delegate_, MockSandboxInitialized(kBrowserProcessType));
EXPECT_CALL(mock_delegate_,
ShouldCreateFeatureList(InvokedInMatcher(kBrowserProcessType)))
.WillOnce(Return(true));
EXPECT_CALL(mock_delegate_,
ShouldInitializeMojo(InvokedInMatcher(kBrowserProcessType)))
.WillOnce(Return(true));
EXPECT_CALL(mock_delegate_, MockPreBrowserMain())
.WillOnce(Return(std::nullopt));
EXPECT_CALL(mock_delegate_, MockPostEarlyInitialization(
InvokedInMatcher(kBrowserProcessType)))
.WillOnce(DoAll(Invoke(this, &Self::TestPostEarlyInitialization),
Return(std::nullopt)));
EXPECT_CALL(mock_delegate_, MockRunProcess(kBrowserProcessType, _));
#if !BUILDFLAG(IS_ANDROID)
// Android never calls ProcessExiting, since it leaks its ContentMainRunner
// and ProcessExiting is called from the destructor.
EXPECT_CALL(mock_delegate_, MockProcessExiting(kBrowserProcessType));
#endif
// This will call ContentMain(), which should satisfy the expectations
// above.
Super::SetUp();
}
ContentMainDelegate* GetOptionalContentMainDelegateOverride() override {
return &mock_delegate_;
}
void TestBasicStartupComplete() {
// The PostEarlyInitialization test checks that ContentMainRunnerImpl set up
// the FeatureList. This test is invalid if it already exists
// before starting.
EXPECT_FALSE(base::FeatureList::GetInstance());
}
void TestPostEarlyInitialization() {
// ContentMainRunnerImpl should have set up the ThreadPoolInstance and
// FeatureList by this point.
EXPECT_TRUE(base::ThreadPoolInstance::Get());
EXPECT_TRUE(base::FeatureList::GetInstance());
}
::testing::StrictMock<MockContentMainDelegate> mock_delegate_;
};
IN_PROC_BROWSER_TEST_F(ContentMainRunnerImplBrowserTest, StartupSequence) {
// All of the work is done in SetUp().
}
} // namespace
} // namespace content