blob: af7964ae23216df5dd7edd5dd36a2c62297bfc2c [file] [log] [blame]
// Copyright 2017 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 <map>
#include <utility>
#include "base/bind.h"
#include "base/callback.h"
#include "base/files/file_path.h"
#include "base/files/file_util.h"
#include "base/files/scoped_temp_dir.h"
#include "base/macros.h"
#include "base/optional.h"
#include "base/run_loop.h"
#include "base/stl_util.h"
#include "base/strings/stringprintf.h"
#include "base/task/post_task.h"
#include "base/test/scoped_path_override.h"
#include "base/test/test_simple_task_runner.h"
#include "base/threading/thread_task_runner_handle.h"
#include "chrome/browser/browser_process_platform_part_chromeos.h"
#include "chrome/browser/chromeos/login/users/fake_chrome_user_manager.h"
#include "chrome/browser/component_updater/cros_component_installer_chromeos.h"
#include "chrome/browser/component_updater/metadata_table_chromeos.h"
#include "chrome/common/chrome_paths.h"
#include "chrome/test/base/testing_browser_process.h"
#include "chromeos/constants/chromeos_paths.h"
#include "chromeos/dbus/dbus_thread_manager.h"
#include "chromeos/dbus/fake_image_loader_client.h"
#include "components/component_updater/mock_component_updater_service.h"
#include "components/update_client/utils.h"
#include "components/user_manager/scoped_user_manager.h"
#include "content/public/test/test_browser_thread_bundle.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace component_updater {
namespace {
// Information about the component used in tests (not test-only; the component
// is OK to change, as long as its config still satisfies test assumptions).
constexpr char kTestComponentName[] = "demo-mode-resources";
constexpr char kTestComponentValidMinEnvVersion[] = "1.0";
constexpr char kTestComponentInvalidMinEnvVersion[] = "0.0.1";
constexpr char kTestComponentMountPath[] =
"/run/imageloader/demo-mode-resources";
MATCHER_P(CrxComponentWithName, name, "") {
return arg.name == name;
}
// Used as a callback to CrOSComponentManager::Load callback - it records the
// callback params to |result_out| and |mount_path_out|.
void RecordLoadResult(base::Optional<CrOSComponentManager::Error>* result_out,
base::FilePath* mount_path_out,
CrOSComponentManager::Error reported_result,
const base::FilePath& reported_mount_path) {
*result_out = reported_result;
*mount_path_out = reported_mount_path;
}
// Wraps update_client::Callback inside update_client::CrxInstaller::Install
// callback. It expects a success result to be reported.
void WrapInstallerCallback(update_client::Callback callback,
const update_client::CrxInstaller::Result& result) {
EXPECT_EQ(0, result.error);
std::move(callback).Run(update_client::Error::NONE);
}
class TestUpdater : public OnDemandUpdater {
public:
TestUpdater() = default;
~TestUpdater() override = default;
// Whether has a pending update request (either foreground or background).
bool HasPendingUpdate(const std::string& name) {
return base::ContainsKey(background_updates_, name) ||
base::ContainsKey(foreground_updates_, name);
}
// Finishes a foreground update request. Returns false if there is no pending
// foreground update request for the component.
// |name|: Component name.
// |error|: The error code that the update request should report.
// |unpacked_path|: On success, the path from which the component should be
// installed.
bool FinishForegroundUpdate(const std::string& name,
update_client::Error error,
const base::FilePath& unpacked_path) {
return FinishUpdate(name, error, unpacked_path, &foreground_updates_);
}
// Finishes a background update request. Returns false if there is no pending
// foreground update request for the component.
// |name|: Component name.
// |error|: The error code that the update request should report.
// |unpacked_path|: On success, the path from which the component should be
// installed.
bool FinishBackgroundUpdate(const std::string& name,
update_client::Error error,
const base::FilePath& unpacked_path) {
return FinishUpdate(name, error, unpacked_path, &background_updates_);
}
// Registers a CRX component for updates.
bool RegisterComponent(const update_client::CrxComponent& component) {
component_installers_[component.name] = component.installer;
component_id_to_name_[update_client::GetCrxComponentID(component)] =
component.name;
return true;
}
private:
// OnDemandUpdater:
void OnDemandUpdate(const std::string& id,
OnDemandUpdater::Priority priority,
Callback callback) override {
const std::string& name = component_id_to_name_[id];
ASSERT_FALSE(name.empty());
// Technically, this is a supported use case for background updates, but not
// needed for this tests.
ASSERT_FALSE(HasPendingUpdate(name));
if (priority == OnDemandUpdater::Priority::BACKGROUND) {
background_updates_.emplace(name, std::move(callback));
} else {
foreground_updates_.emplace(name, std::move(callback));
}
}
// Shared implementation for FinishForegroundUpdate() and
// FinishBackgroundUpdate().
bool FinishUpdate(const std::string& name,
update_client::Error error,
const base::FilePath& unpacked_path,
std::map<std::string, Callback>* updates) {
auto it = updates->find(name);
if (it == updates->end())
return false;
Callback callback = std::move(it->second);
updates->erase(it);
if (error != update_client::Error::NONE) {
std::move(callback).Run(error);
return true;
}
scoped_refptr<update_client::CrxInstaller> installer =
component_installers_[name];
if (!installer)
return false;
base::PostTaskWithTraits(
FROM_HERE, {base::MayBlock()},
base::BindOnce(
&update_client::CrxInstaller::Install, installer, unpacked_path, "",
base::BindOnce(&WrapInstallerCallback, std::move(callback))));
return true;
}
// Pending background updates per component.
std::map<std::string, Callback> background_updates_;
// Pending foreground updates per component.
std::map<std::string, Callback> foreground_updates_;
// Maps a component name to the component's registered CRX installer.
std::map<std::string, scoped_refptr<update_client::CrxInstaller>>
component_installers_;
// Maps a registered component ID to the component name.
std::map<std::string, std::string> component_id_to_name_;
DISALLOW_COPY_AND_ASSIGN(TestUpdater);
};
} // namespace
class CrOSComponentInstallerTest : public testing::Test {
public:
CrOSComponentInstallerTest()
: user_manager_(std::make_unique<chromeos::FakeChromeUserManager>()) {}
void SetUp() override {
ASSERT_TRUE(base_component_paths_.CreateUniqueTempDir());
preinstalled_cros_components_ = base_component_paths_.GetPath()
.AppendASCII("preinstalled")
.AppendASCII("cros-components");
preinstalled_components_path_override_ =
std::make_unique<base::ScopedPathOverride>(
chromeos::DIR_PREINSTALLED_COMPONENTS,
preinstalled_cros_components_.DirName());
user_cros_components_ =
base_component_paths_.GetPath().AppendASCII("user").AppendASCII(
"cros-components");
user_components_path_override_ = std::make_unique<base::ScopedPathOverride>(
chrome::DIR_USER_DATA, user_cros_components_.DirName());
tmp_unpack_dir_ = base_component_paths_.GetPath().AppendASCII("tmp_unpack");
auto fake_image_loader_client =
std::make_unique<chromeos::FakeImageLoaderClient>();
image_loader_client_ = fake_image_loader_client.get();
chromeos::DBusThreadManager::GetSetterForTesting()->SetImageLoaderClient(
std::move(fake_image_loader_client));
}
void TearDown() override {
image_loader_client_ = nullptr;
chromeos::DBusThreadManager::Shutdown();
preinstalled_components_path_override_.reset();
user_components_path_override_.reset();
}
protected:
// Gets expected path for a user installed component with a specific version.
base::FilePath GetInstalledComponentPath(const std::string& name,
const std::string& version) {
return user_cros_components_.AppendASCII(name).AppendASCII(version);
}
// Creates a fake "user" installed component.
// On success, it returns the path at which the component was created, nullopt
// otherwise.
base::Optional<base::FilePath> CreateInstalledComponent(
const std::string& name,
const std::string& version,
const std::string& min_env_version) {
return CreateComponentAtPath(GetInstalledComponentPath(name, version), name,
version, min_env_version);
}
// Creates a fake component at a pre-installed component path.
// On success, it returns the path at which the component was created, nullopt
// otherwise.
base::Optional<base::FilePath> CreatePreinstalledComponent(
const std::string& name,
const std::string& version,
const std::string& min_env_version) {
return CreateComponentAtPath(
preinstalled_cros_components_.AppendASCII(name), name, version,
min_env_version);
}
// Creates a fake component at a temporary path from which the component will
// be installed as a user-installed component by the test OnDemandUpdater.
// On success, it returns the path at which the component was created, nullopt
// otherwise.
base::Optional<base::FilePath> CreateUnpackedComponent(
const std::string& name,
const std::string& version,
const std::string& min_env_version) {
return CreateComponentAtPath(
tmp_unpack_dir_.AppendASCII(name).AppendASCII(version), name, version,
min_env_version);
}
// Creates a mock ComponentUpdateService. It sets the service up to expect a
// single registration request for the component |component_name|, and to
// redirect on-demand update requests to |updater|.
std::unique_ptr<MockComponentUpdateService>
CreateUpdateServiceForSingleRegistration(const std::string& component_name,
TestUpdater* updater) {
auto service = std::make_unique<MockComponentUpdateService>();
EXPECT_CALL(*service,
RegisterComponent(CrxComponentWithName(component_name)))
.Times(1)
.WillOnce(testing::Invoke(updater, &TestUpdater::RegisterComponent));
EXPECT_CALL(*service, GetOnDemandUpdater())
.WillRepeatedly(testing::ReturnRef(*updater));
return service;
}
void RunUntilIdle() { thread_bundle_.RunUntilIdle(); }
chromeos::FakeImageLoaderClient* image_loader_client() {
return image_loader_client_;
}
// Verify that cros_component_manager successfully loaded a component
// |component_name|.
// |load_result|: The result reported by CrOSComponentManager::Load().
// |component_install_path|: The path at which the component is expected to be
// installed.
void VerifyComponentLoaded(
const CrOSComponentManager& cros_component_manager,
const std::string& component_name,
base::Optional<CrOSComponentManager::Error> load_result,
const base::FilePath& component_install_path) {
ASSERT_TRUE(load_result.has_value());
ASSERT_EQ(CrOSComponentManager::Error::NONE, load_result.value());
EXPECT_EQ(component_install_path,
cros_component_manager.GetCompatiblePath(component_name));
EXPECT_TRUE(image_loader_client()->IsLoaded(component_name));
EXPECT_EQ(component_install_path,
image_loader_client()->GetComponentInstallPath(component_name));
}
private:
// Creates a fake component at the specified path. Returns the target path on
// success, nullopt otherwise.
base::Optional<base::FilePath> CreateComponentAtPath(
const base::FilePath& path,
const std::string& name,
const std::string& version,
const std::string& min_env_version) {
if (!base::CreateDirectory(path))
return base::nullopt;
const std::string manifest_template = R"({
"name": "%s",
"version": "%s",
"min_env_version": "%s"
})";
const std::string manifest =
base::StringPrintf(manifest_template.c_str(), name.c_str(),
version.c_str(), min_env_version.c_str());
if (base::WriteFile(path.AppendASCII("manifest.json"), manifest.data(),
manifest.size()) != static_cast<int>(manifest.size())) {
return base::nullopt;
}
return base::make_optional(path);
}
content::TestBrowserThreadBundle thread_bundle_;
user_manager::ScopedUserManager user_manager_;
// Image loader client that is active during the test. Owned by
// chromeos::DBusThreadManager.
chromeos::FakeImageLoaderClient* image_loader_client_ = nullptr;
base::ScopedTempDir base_component_paths_;
std::unique_ptr<base::ScopedPathOverride>
preinstalled_components_path_override_;
base::FilePath preinstalled_cros_components_;
std::unique_ptr<base::ScopedPathOverride> user_components_path_override_;
base::FilePath user_cros_components_;
base::FilePath tmp_unpack_dir_;
DISALLOW_COPY_AND_ASSIGN(CrOSComponentInstallerTest);
};
class MockCrOSComponentInstallerPolicy : public CrOSComponentInstallerPolicy {
public:
explicit MockCrOSComponentInstallerPolicy(const ComponentConfig& config)
: CrOSComponentInstallerPolicy(config, nullptr) {}
MOCK_METHOD2(IsCompatible,
bool(const std::string& env_version_str,
const std::string& min_env_version_str));
};
TEST_F(CrOSComponentInstallerTest, CompatibleCrOSComponent) {
component_updater::CrOSComponentInstaller cros_component_manager(nullptr,
nullptr);
const std::string kComponent = "a";
EXPECT_FALSE(cros_component_manager.IsCompatible(kComponent));
EXPECT_EQ(cros_component_manager.GetCompatiblePath(kComponent).value(),
std::string());
const base::FilePath kPath("/component/path/v0");
cros_component_manager.RegisterCompatiblePath(kComponent, kPath);
EXPECT_TRUE(cros_component_manager.IsCompatible(kComponent));
EXPECT_EQ(cros_component_manager.GetCompatiblePath(kComponent), kPath);
cros_component_manager.UnregisterCompatiblePath(kComponent);
EXPECT_FALSE(cros_component_manager.IsCompatible(kComponent));
}
TEST_F(CrOSComponentInstallerTest, CompatibilityOK) {
ComponentConfig config{"a", "2.1", ""};
MockCrOSComponentInstallerPolicy policy(config);
EXPECT_CALL(policy, IsCompatible(testing::_, testing::_)).Times(1);
base::Version version;
base::FilePath path;
std::unique_ptr<base::DictionaryValue> manifest =
std::make_unique<base::DictionaryValue>();
manifest->SetString("min_env_version", "2.1");
policy.ComponentReady(version, path, std::move(manifest));
}
TEST_F(CrOSComponentInstallerTest, CompatibilityMissingManifest) {
ComponentConfig config{"a", "2.1", ""};
MockCrOSComponentInstallerPolicy policy(config);
EXPECT_CALL(policy, IsCompatible(testing::_, testing::_)).Times(0);
base::Version version;
base::FilePath path;
std::unique_ptr<base::DictionaryValue> manifest =
std::make_unique<base::DictionaryValue>();
policy.ComponentReady(version, path, std::move(manifest));
}
TEST_F(CrOSComponentInstallerTest, IsCompatibleOrNot) {
ComponentConfig config{"", "", ""};
CrOSComponentInstallerPolicy policy(config, nullptr);
EXPECT_TRUE(policy.IsCompatible("1.0", "1.0"));
EXPECT_TRUE(policy.IsCompatible("1.1", "1.0"));
EXPECT_FALSE(policy.IsCompatible("1.0", "1.1"));
EXPECT_FALSE(policy.IsCompatible("1.0", "2.0"));
EXPECT_FALSE(policy.IsCompatible("1.c", "1.c"));
EXPECT_FALSE(policy.IsCompatible("1", "1.1"));
EXPECT_TRUE(policy.IsCompatible("1.1.1", "1.1"));
}
TEST_F(CrOSComponentInstallerTest, RegisterComponent) {
auto cus = std::make_unique<MockComponentUpdateService>();
ComponentConfig config{
"star-cups-driver", "1.1",
"6d24de30f671da5aee6d463d9e446cafe9ddac672800a9defe86877dcde6c466"};
EXPECT_CALL(*cus, RegisterComponent(testing::_)).Times(1);
component_updater::CrOSComponentInstaller cros_component_manager(nullptr,
cus.get());
cros_component_manager.Register(config, base::OnceClosure());
RunUntilIdle();
}
TEST_F(CrOSComponentInstallerTest, LoadPreinstalledComponent_Skip_Mount) {
base::Optional<base::FilePath> install_path = CreatePreinstalledComponent(
kTestComponentName, "1.0", kTestComponentValidMinEnvVersion);
ASSERT_TRUE(install_path.has_value());
image_loader_client()->SetMountPathForComponent(
kTestComponentName, base::FilePath(kTestComponentMountPath));
TestUpdater updater;
std::unique_ptr<MockComponentUpdateService> update_service =
CreateUpdateServiceForSingleRegistration(kTestComponentName, &updater);
component_updater::CrOSComponentInstaller cros_component_manager(
nullptr, update_service.get());
base::Optional<CrOSComponentManager::Error> load_result;
base::FilePath mount_path;
cros_component_manager.Load(
kTestComponentName, CrOSComponentManager::MountPolicy::kMount,
CrOSComponentManager::UpdatePolicy::kSkip,
base::BindOnce(&RecordLoadResult, &load_result, &mount_path));
RunUntilIdle();
ASSERT_FALSE(updater.HasPendingUpdate(kTestComponentName));
VerifyComponentLoaded(cros_component_manager, kTestComponentName, load_result,
install_path.value());
EXPECT_EQ(base::FilePath(kTestComponentMountPath), mount_path);
}
TEST_F(CrOSComponentInstallerTest,
LoadInstalledComponentWhenOlderPreinstalledVersionExists_Skip_Mount) {
base::Optional<base::FilePath> preinstalled_path =
CreatePreinstalledComponent(kTestComponentName, "1.0",
kTestComponentValidMinEnvVersion);
ASSERT_TRUE(preinstalled_path.has_value());
base::Optional<base::FilePath> install_path = CreateInstalledComponent(
kTestComponentName, "2.0", kTestComponentValidMinEnvVersion);
ASSERT_TRUE(install_path.has_value());
image_loader_client()->SetMountPathForComponent(
kTestComponentName, base::FilePath(kTestComponentMountPath));
TestUpdater updater;
std::unique_ptr<MockComponentUpdateService> update_service =
CreateUpdateServiceForSingleRegistration(kTestComponentName, &updater);
component_updater::CrOSComponentInstaller cros_component_manager(
nullptr, update_service.get());
base::Optional<CrOSComponentManager::Error> load_result;
base::FilePath mount_path;
cros_component_manager.Load(
kTestComponentName, CrOSComponentManager::MountPolicy::kMount,
CrOSComponentManager::UpdatePolicy::kSkip,
base::BindOnce(&RecordLoadResult, &load_result, &mount_path));
RunUntilIdle();
ASSERT_FALSE(updater.HasPendingUpdate(kTestComponentName));
VerifyComponentLoaded(cros_component_manager, kTestComponentName, load_result,
install_path.value());
EXPECT_EQ(base::FilePath(kTestComponentMountPath), mount_path);
}
TEST_F(CrOSComponentInstallerTest, LoadInstalledComponent) {
base::Optional<base::FilePath> install_path = CreateInstalledComponent(
kTestComponentName, "2.0", kTestComponentValidMinEnvVersion);
ASSERT_TRUE(install_path.has_value());
image_loader_client()->SetMountPathForComponent(
kTestComponentName, base::FilePath(kTestComponentMountPath));
TestUpdater updater;
std::unique_ptr<MockComponentUpdateService> update_service =
CreateUpdateServiceForSingleRegistration(kTestComponentName, &updater);
component_updater::CrOSComponentInstaller cros_component_manager(
nullptr, update_service.get());
base::Optional<CrOSComponentManager::Error> load_result;
base::FilePath mount_path;
cros_component_manager.Load(
kTestComponentName, CrOSComponentManager::MountPolicy::kMount,
CrOSComponentManager::UpdatePolicy::kSkip,
base::BindOnce(&RecordLoadResult, &load_result, &mount_path));
RunUntilIdle();
ASSERT_FALSE(updater.HasPendingUpdate(kTestComponentName));
VerifyComponentLoaded(cros_component_manager, kTestComponentName, load_result,
install_path.value());
EXPECT_EQ(base::FilePath(kTestComponentMountPath), mount_path);
}
TEST_F(CrOSComponentInstallerTest, LoadNonInstalledComponent_Skip_Mount) {
image_loader_client()->SetMountPathForComponent(
kTestComponentName, base::FilePath(kTestComponentMountPath));
TestUpdater updater;
std::unique_ptr<MockComponentUpdateService> update_service =
CreateUpdateServiceForSingleRegistration(kTestComponentName, &updater);
component_updater::CrOSComponentInstaller cros_component_manager(
nullptr, update_service.get());
base::Optional<CrOSComponentManager::Error> load_result;
base::FilePath mount_path;
cros_component_manager.Load(
kTestComponentName, CrOSComponentManager::MountPolicy::kMount,
CrOSComponentManager::UpdatePolicy::kSkip,
base::BindOnce(&RecordLoadResult, &load_result, &mount_path));
RunUntilIdle();
ASSERT_FALSE(updater.HasPendingUpdate(kTestComponentName));
ASSERT_TRUE(load_result.has_value());
EXPECT_EQ(CrOSComponentManager::Error::NOT_FOUND, load_result.value());
EXPECT_TRUE(mount_path.empty());
EXPECT_TRUE(
cros_component_manager.GetCompatiblePath(kTestComponentName).empty());
EXPECT_FALSE(image_loader_client()->IsLoaded(kTestComponentName));
}
TEST_F(CrOSComponentInstallerTest, LoadObsoleteInstalledComponent_Skip_Mount) {
base::Optional<base::FilePath> old_install_path = CreateInstalledComponent(
kTestComponentName, "0.5", kTestComponentInvalidMinEnvVersion);
ASSERT_TRUE(old_install_path.has_value());
base::Optional<base::FilePath> old_preinstall_path = CreateInstalledComponent(
kTestComponentName, "0.5", kTestComponentInvalidMinEnvVersion);
ASSERT_TRUE(old_preinstall_path.has_value());
image_loader_client()->SetMountPathForComponent(
kTestComponentName, base::FilePath(kTestComponentMountPath));
TestUpdater updater;
std::unique_ptr<MockComponentUpdateService> update_service =
CreateUpdateServiceForSingleRegistration(kTestComponentName, &updater);
component_updater::CrOSComponentInstaller cros_component_manager(
nullptr, update_service.get());
base::Optional<CrOSComponentManager::Error> load_result;
base::FilePath mount_path;
cros_component_manager.Load(
kTestComponentName, CrOSComponentManager::MountPolicy::kMount,
CrOSComponentManager::UpdatePolicy::kSkip,
base::BindOnce(&RecordLoadResult, &load_result, &mount_path));
RunUntilIdle();
ASSERT_FALSE(updater.HasPendingUpdate(kTestComponentName));
ASSERT_TRUE(load_result.has_value());
EXPECT_EQ(CrOSComponentManager::Error::NOT_FOUND, load_result.value());
EXPECT_TRUE(mount_path.empty());
EXPECT_TRUE(
cros_component_manager.GetCompatiblePath(kTestComponentName).empty());
EXPECT_FALSE(image_loader_client()->IsLoaded(kTestComponentName));
}
TEST_F(CrOSComponentInstallerTest, LoadNonInstalledComponent_DontForce_Mount) {
image_loader_client()->SetMountPathForComponent(
kTestComponentName, base::FilePath(kTestComponentMountPath));
TestUpdater updater;
std::unique_ptr<MockComponentUpdateService> update_service =
CreateUpdateServiceForSingleRegistration(kTestComponentName, &updater);
component_updater::CrOSComponentInstaller cros_component_manager(
nullptr, update_service.get());
base::Optional<CrOSComponentManager::Error> load_result;
base::FilePath mount_path;
cros_component_manager.Load(
kTestComponentName, CrOSComponentManager::MountPolicy::kMount,
CrOSComponentManager::UpdatePolicy::kDontForce,
base::BindOnce(&RecordLoadResult, &load_result, &mount_path));
RunUntilIdle();
base::Optional<base::FilePath> unpacked_path = CreateUnpackedComponent(
kTestComponentName, "2.0", kTestComponentValidMinEnvVersion);
ASSERT_TRUE(unpacked_path.has_value());
ASSERT_TRUE(updater.FinishForegroundUpdate(
kTestComponentName, update_client::Error::NONE, unpacked_path.value()));
RunUntilIdle();
EXPECT_FALSE(updater.HasPendingUpdate(kTestComponentName));
VerifyComponentLoaded(cros_component_manager, kTestComponentName, load_result,
GetInstalledComponentPath(kTestComponentName, "2.0"));
EXPECT_EQ(base::FilePath(kTestComponentMountPath), mount_path);
}
TEST_F(CrOSComponentInstallerTest,
LoadComponentWithInstallFail_DontForce_Mount) {
image_loader_client()->SetMountPathForComponent(
kTestComponentName, base::FilePath(kTestComponentMountPath));
TestUpdater updater;
std::unique_ptr<MockComponentUpdateService> update_service =
CreateUpdateServiceForSingleRegistration(kTestComponentName, &updater);
component_updater::CrOSComponentInstaller cros_component_manager(
nullptr, update_service.get());
base::Optional<CrOSComponentManager::Error> load_result;
base::FilePath mount_path;
cros_component_manager.Load(
kTestComponentName, CrOSComponentManager::MountPolicy::kMount,
CrOSComponentManager::UpdatePolicy::kDontForce,
base::BindOnce(&RecordLoadResult, &load_result, &mount_path));
RunUntilIdle();
ASSERT_TRUE(updater.FinishForegroundUpdate(
kTestComponentName, update_client::Error::SERVICE_ERROR,
base::FilePath()));
RunUntilIdle();
EXPECT_FALSE(updater.HasPendingUpdate(kTestComponentName));
ASSERT_TRUE(load_result.has_value());
EXPECT_EQ(CrOSComponentManager::Error::INSTALL_FAILURE, load_result.value());
EXPECT_TRUE(mount_path.empty());
EXPECT_TRUE(
cros_component_manager.GetCompatiblePath(kTestComponentName).empty());
EXPECT_FALSE(image_loader_client()->IsLoaded(kTestComponentName));
}
TEST_F(CrOSComponentInstallerTest,
LoadWithObsoleteInstalledComponent_DontForce_Mount) {
base::Optional<base::FilePath> old_install_path = CreateInstalledComponent(
kTestComponentName, "0.5", kTestComponentInvalidMinEnvVersion);
ASSERT_TRUE(old_install_path.has_value());
base::Optional<base::FilePath> old_preinstall_path =
CreatePreinstalledComponent(kTestComponentName, "0.5",
kTestComponentInvalidMinEnvVersion);
ASSERT_TRUE(old_preinstall_path.has_value());
image_loader_client()->SetMountPathForComponent(
kTestComponentName, base::FilePath(kTestComponentMountPath));
TestUpdater updater;
std::unique_ptr<MockComponentUpdateService> update_service =
CreateUpdateServiceForSingleRegistration(kTestComponentName, &updater);
component_updater::CrOSComponentInstaller cros_component_manager(
nullptr, update_service.get());
base::Optional<CrOSComponentManager::Error> load_result;
base::FilePath mount_path;
cros_component_manager.Load(
kTestComponentName, CrOSComponentManager::MountPolicy::kMount,
CrOSComponentManager::UpdatePolicy::kDontForce,
base::BindOnce(&RecordLoadResult, &load_result, &mount_path));
RunUntilIdle();
base::Optional<base::FilePath> unpacked_path = CreateUnpackedComponent(
kTestComponentName, "2.0", kTestComponentValidMinEnvVersion);
ASSERT_TRUE(unpacked_path.has_value());
ASSERT_TRUE(updater.FinishForegroundUpdate(
kTestComponentName, update_client::Error::NONE, unpacked_path.value()));
RunUntilIdle();
EXPECT_FALSE(updater.HasPendingUpdate(kTestComponentName));
VerifyComponentLoaded(cros_component_manager, kTestComponentName, load_result,
GetInstalledComponentPath(kTestComponentName, "2.0"));
EXPECT_EQ(base::FilePath(kTestComponentMountPath), mount_path);
}
TEST_F(CrOSComponentInstallerTest, RegisterAllRegistersInstalledComponent) {
base::Optional<base::FilePath> install_path = CreateInstalledComponent(
kTestComponentName, "1.0", kTestComponentValidMinEnvVersion);
ASSERT_TRUE(install_path.has_value());
image_loader_client()->SetMountPathForComponent(
kTestComponentName, base::FilePath(kTestComponentMountPath));
TestUpdater updater;
std::unique_ptr<MockComponentUpdateService> update_service =
CreateUpdateServiceForSingleRegistration(kTestComponentName, &updater);
component_updater::CrOSComponentInstaller cros_component_manager(
nullptr, update_service.get());
cros_component_manager.RegisterInstalled();
RunUntilIdle();
EXPECT_FALSE(updater.HasPendingUpdate(kTestComponentName));
EXPECT_EQ(install_path,
cros_component_manager.GetCompatiblePath(kTestComponentName));
EXPECT_FALSE(image_loader_client()->IsLoaded(kTestComponentName));
}
TEST_F(CrOSComponentInstallerTest, RegisterAllIgnoresPrenstalledComponent) {
base::Optional<base::FilePath> preinstall_path = CreatePreinstalledComponent(
kTestComponentName, "1.0", kTestComponentValidMinEnvVersion);
ASSERT_TRUE(preinstall_path.has_value());
auto update_service = std::make_unique<MockComponentUpdateService>();
EXPECT_CALL(*update_service, RegisterComponent(testing::_)).Times(0);
EXPECT_CALL(*update_service, GetOnDemandUpdater()).Times(0);
component_updater::CrOSComponentInstaller cros_component_manager(
nullptr, update_service.get());
cros_component_manager.RegisterInstalled();
RunUntilIdle();
EXPECT_TRUE(
cros_component_manager.GetCompatiblePath(kTestComponentName).empty());
EXPECT_FALSE(image_loader_client()->IsLoaded(kTestComponentName));
}
TEST_F(CrOSComponentInstallerTest,
LoadInstalledComponentAfterRegisterInstalled) {
base::Optional<base::FilePath> install_path = CreateInstalledComponent(
kTestComponentName, "1.0", kTestComponentValidMinEnvVersion);
ASSERT_TRUE(install_path.has_value());
image_loader_client()->SetMountPathForComponent(
kTestComponentName, base::FilePath(kTestComponentMountPath));
TestUpdater updater;
std::unique_ptr<MockComponentUpdateService> update_service =
CreateUpdateServiceForSingleRegistration(kTestComponentName, &updater);
component_updater::CrOSComponentInstaller cros_component_manager(
nullptr, update_service.get());
cros_component_manager.RegisterInstalled();
RunUntilIdle();
EXPECT_FALSE(updater.HasPendingUpdate(kTestComponentName));
EXPECT_EQ(install_path.value(),
cros_component_manager.GetCompatiblePath(kTestComponentName));
base::Optional<CrOSComponentManager::Error> load_result;
base::FilePath mount_path;
cros_component_manager.Load(
kTestComponentName, CrOSComponentManager::MountPolicy::kMount,
CrOSComponentManager::UpdatePolicy::kDontForce,
base::BindOnce(&RecordLoadResult, &load_result, &mount_path));
RunUntilIdle();
EXPECT_FALSE(updater.HasPendingUpdate(kTestComponentName));
VerifyComponentLoaded(cros_component_manager, kTestComponentName, load_result,
install_path.value());
EXPECT_EQ(base::FilePath(kTestComponentMountPath), mount_path);
}
TEST_F(CrOSComponentInstallerTest,
LoadInstalledComponentConcurrentWithRegisterInstalled) {
base::Optional<base::FilePath> install_path = CreateInstalledComponent(
kTestComponentName, "1.0", kTestComponentValidMinEnvVersion);
ASSERT_TRUE(install_path.has_value());
image_loader_client()->SetMountPathForComponent(
kTestComponentName, base::FilePath(kTestComponentMountPath));
TestUpdater updater;
auto update_service = std::make_unique<MockComponentUpdateService>();
EXPECT_CALL(*update_service,
RegisterComponent(CrxComponentWithName(kTestComponentName)))
.Times(2)
.WillRepeatedly(
testing::Invoke(&updater, &TestUpdater::RegisterComponent));
EXPECT_CALL(*update_service, GetOnDemandUpdater())
.WillRepeatedly(testing::ReturnRef(updater));
component_updater::CrOSComponentInstaller cros_component_manager(
nullptr, update_service.get());
cros_component_manager.RegisterInstalled();
EXPECT_FALSE(updater.HasPendingUpdate(kTestComponentName));
base::Optional<CrOSComponentManager::Error> load_result;
base::FilePath mount_path;
cros_component_manager.Load(
kTestComponentName, CrOSComponentManager::MountPolicy::kMount,
CrOSComponentManager::UpdatePolicy::kDontForce,
base::BindOnce(&RecordLoadResult, &load_result, &mount_path));
RunUntilIdle();
EXPECT_FALSE(updater.HasPendingUpdate(kTestComponentName));
VerifyComponentLoaded(cros_component_manager, kTestComponentName, load_result,
install_path.value());
EXPECT_EQ(base::FilePath(kTestComponentMountPath), mount_path);
}
} // namespace component_updater