// Copyright 2023 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/ash/crosapi/stateful_lacros_loader.h"
#include <memory>
#include <string_view>
#include "base/auto_reset.h"
#include "base/files/file_path.h"
#include "base/files/file_util.h"
#include "base/path_service.h"
#include "base/run_loop.h"
#include "base/strings/utf_string_conversions.h"
#include "base/test/bind.h"
#include "base/test/scoped_path_override.h"
#include "base/test/test_future.h"
#include "base/version.h"
#include "chrome/browser/ash/crosapi/browser_loader.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/component_updater/cros_component_installer_chromeos.h"
#include "chrome/test/base/browser_process_platform_part_test_api_chromeos.h"
#include "chromeos/ash/components/standalone_browser/browser_support.h"
#include "components/component_updater/ash/fake_component_manager_ash.h"
#include "components/component_updater/component_updater_paths.h"
#include "components/component_updater/mock_component_updater_service.h"
#include "components/update_client/update_client.h"
#include "content/public/test/browser_task_environment.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace crosapi {
namespace {
using testing::Return;
constexpr char kLacrosComponentName[] = "lacros-dogfood-dev";
constexpr char kLacrosComponentId[] = "ldobopbhiamakmncndpkeelenhdmgfhk";
class StatefulLacrosLoaderTest : public testing::Test {
public:
StatefulLacrosLoaderTest() {
// Create dependencies for object under test.
component_manager_ =
base::MakeRefCounted<component_updater::FakeComponentManagerAsh>();
component_manager_->set_supported_components({kLacrosComponentName});
component_manager_->ResetComponentState(
kLacrosComponentName,
component_updater::FakeComponentManagerAsh::ComponentInfo(
component_updater::ComponentManagerAsh::Error::NONE,
base::FilePath("/install/path"), base::FilePath("/mount/path"),
version));
browser_part_ = std::make_unique<BrowserProcessPlatformPartTestApi>(
g_browser_process->platform_part());
browser_part_->InitializeComponentManager(component_manager_);
// Set up component path.
base::FilePath root_dir;
CHECK(base::PathService::Get(component_updater::DIR_COMPONENT_USER,
&root_dir));
base::FilePath component_root =
root_dir.Append(component_updater::kComponentsRootPath)
.Append(kLacrosComponentName);
base::CreateDirectory(component_root.Append("sub-dir"));
stateful_lacros_loader_ = std::make_unique<StatefulLacrosLoader>(
component_manager_, &mock_component_update_service_,
kLacrosComponentName);
EXPECT_TRUE(BrowserLoader::WillLoadStatefulComponentBuilds());
}
~StatefulLacrosLoaderTest() override {
stateful_lacros_loader_.reset();
browser_part_->ShutdownComponentManager();
}
const base::Version version = base::Version("1.0.0");
protected:
content::BrowserTaskEnvironment task_environment_;
base::ScopedPathOverride scoped_component_user_dir_{
component_updater::DIR_COMPONENT_USER};
component_updater::MockComponentUpdateService mock_component_update_service_;
scoped_refptr<component_updater::FakeComponentManagerAsh> component_manager_;
std::unique_ptr<BrowserProcessPlatformPartTestApi> browser_part_;
std::unique_ptr<StatefulLacrosLoader> stateful_lacros_loader_;
};
TEST_F(StatefulLacrosLoaderTest,
LoadStatefulLacrosSelectedByCompatibilityCheck) {
std::u16string lacros_component_name =
base::UTF8ToUTF16(std::string_view(kLacrosComponentName));
EXPECT_CALL(mock_component_update_service_, GetComponents())
.WillOnce(Return(std::vector<component_updater::ComponentInfo>{
{kLacrosComponentId, "", lacros_component_name, version, ""}}));
EXPECT_EQ(StatefulLacrosLoader::State::kNotLoaded,
stateful_lacros_loader_->GetState());
// If stateful is selected by compatibility check, it first calls GetVersion
// and complete loading, and then Load is called.
base::test::TestFuture<const base::Version&> future;
stateful_lacros_loader_->GetVersion(
future.GetCallback<const base::Version&>());
EXPECT_EQ(version, future.Get<0>());
EXPECT_EQ(StatefulLacrosLoader::State::kLoaded,
stateful_lacros_loader_->GetState());
// Load is already completed in GetVersion, so this call is synchronous.
base::Version result_version;
stateful_lacros_loader_->Load(
base::BindOnce([](base::Version* result_version, base::Version version,
const base::FilePath&) { *result_version = version; },
&result_version),
/*forced=*/false);
EXPECT_EQ(version, result_version);
}
TEST_F(StatefulLacrosLoaderTest, LoadStatefulLacrosSelectedByPolicy) {
std::u16string lacros_component_name =
base::UTF8ToUTF16(std::string_view(kLacrosComponentName));
EXPECT_CALL(mock_component_update_service_, GetComponents())
.WillOnce(Return(std::vector<component_updater::ComponentInfo>{
{kLacrosComponentId, "", lacros_component_name, version, ""}}));
EXPECT_EQ(StatefulLacrosLoader::State::kNotLoaded,
stateful_lacros_loader_->GetState());
// If stateful is forced by policy, it does not call GetVersion. Instead, it
// calls Load directly and compute the version inside Load together.
base::test::TestFuture<base::Version, const base::FilePath&> future;
stateful_lacros_loader_->Load(
future.GetCallback<base::Version, const base::FilePath&>(),
/*forced=*/true);
EXPECT_EQ(version, future.Get<0>());
}
} // namespace
} // namespace crosapi