1 // Copyright 2016 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
11 #include "base/files/file_path.h"
12 #include "base/files/file_util.h"
13 #include "base/functional/bind.h"
14 #include "base/functional/callback.h"
15 #include "base/functional/callback_helpers.h"
16 #include "base/memory/raw_ptr.h"
17 #include "base/memory/ref_counted.h"
18 #include "base/path_service.h"
19 #include "base/run_loop.h"
20 #include "base/sequence_checker.h"
21 #include "base/strings/strcat.h"
22 #include "base/strings/stringprintf.h"
23 #include "base/task/sequenced_task_runner.h"
24 #include "base/test/bind.h"
25 #include "base/test/scoped_path_override.h"
26 #include "base/test/task_environment.h"
27 #include "base/values.h"
28 #include "base/version.h"
29 #include "components/component_updater/component_installer.h"
30 #include "components/component_updater/component_updater_paths.h"
31 #include "components/component_updater/component_updater_service.h"
32 #include "components/component_updater/component_updater_service_internal.h"
33 #include "components/crx_file/crx_verifier.h"
34 #include "components/prefs/testing_pref_service.h"
35 #include "components/update_client/component_unpacker.h"
36 #include "components/update_client/crx_update_item.h"
37 #include "components/update_client/patcher.h"
38 #include "components/update_client/test_configurator.h"
39 #include "components/update_client/test_utils.h"
40 #include "components/update_client/unzipper.h"
41 #include "components/update_client/update_client.h"
42 #include "components/update_client/update_client_errors.h"
43 #include "testing/gmock/include/gmock/gmock.h"
44 #include "testing/gtest/include/gtest/gtest.h"
45 #include "third_party/abseil-cpp/absl/types/optional.h"
47 using ComponentUnpacker = update_client::ComponentUnpacker;
48 using Configurator = update_client::Configurator;
49 using CrxUpdateItem = update_client::CrxUpdateItem;
50 using TestConfigurator = update_client::TestConfigurator;
51 using UpdateClient = update_client::UpdateClient;
54 using ::testing::Invoke;
56 namespace component_updater {
59 // This hash corresponds to jebgalgnebhfojomionfpkfelancnnkf.crx.
60 constexpr uint8_t kSha256Hash[] = {
61 0x94, 0x16, 0x0b, 0x6d, 0x41, 0x75, 0xe9, 0xec, 0x8e, 0xd5, 0xfa,
62 0x54, 0xb0, 0xd2, 0xdd, 0xa5, 0x6e, 0x05, 0x6b, 0xe8, 0x73, 0x47,
63 0xf6, 0xc4, 0x11, 0x9f, 0xbc, 0xb3, 0x09, 0xb3, 0x5b, 0x40};
64 constexpr base::FilePath::CharType relative_install_dir[] =
65 FILE_PATH_LITERAL("fake");
67 class MockUpdateClient : public UpdateClient {
69 MockUpdateClient() = default;
71 base::RepeatingClosure Install(
72 const std::string& id,
73 CrxDataCallback crx_data_callback,
74 CrxStateChangeCallback crx_state_change_callback,
75 Callback callback) override {
76 DoInstall(id, std::move(crx_data_callback));
77 std::move(callback).Run(update_client::Error::NONE);
78 return base::DoNothing();
81 void Update(const std::vector<std::string>& ids,
82 CrxDataCallback crx_data_callback,
83 CrxStateChangeCallback crx_state_change_callback,
85 Callback callback) override {
86 DoUpdate(ids, std::move(crx_data_callback));
87 std::move(callback).Run(update_client::Error::NONE);
90 void SendUninstallPing(const CrxComponent& crx_component,
92 Callback callback) override {
93 DoSendUninstallPing(crx_component, reason);
94 std::move(callback).Run(update_client::Error::NONE);
97 MOCK_METHOD5(SendInstallPing,
98 void(const CrxComponent& crx_component,
103 MOCK_METHOD1(AddObserver, void(Observer* observer));
104 MOCK_METHOD1(RemoveObserver, void(Observer* observer));
105 MOCK_METHOD2(DoInstall,
106 void(const std::string& id,
107 const CrxDataCallback& crx_data_callback));
108 MOCK_METHOD2(DoUpdate,
109 void(const std::vector<std::string>& ids,
110 const CrxDataCallback& crx_data_callback));
111 MOCK_METHOD5(CheckForUpdate,
112 void(const std::string& ids,
113 CrxDataCallback crx_data_callback,
114 CrxStateChangeCallback crx_state_change_callback,
117 MOCK_CONST_METHOD2(GetCrxUpdateState,
118 bool(const std::string& id, CrxUpdateItem* update_item));
119 MOCK_CONST_METHOD1(IsUpdating, bool(const std::string& id));
120 MOCK_METHOD0(Stop, void());
121 MOCK_METHOD2(DoSendUninstallPing,
122 void(const CrxComponent& crx_component, int reason));
125 ~MockUpdateClient() override = default;
128 class MockInstallerPolicy : public ComponentInstallerPolicy {
130 using ComponentReadyCallback =
131 base::OnceCallback<void(const base::Version& version,
132 const base::FilePath& install_dir,
133 base::Value::Dict manifest)>;
134 explicit MockInstallerPolicy(
135 ComponentReadyCallback component_ready_cb = ComponentReadyCallback())
136 : component_ready_cb_(std::move(component_ready_cb)) {}
137 ~MockInstallerPolicy() override = default;
139 bool VerifyInstallation(const base::Value::Dict& manifest,
140 const base::FilePath& dir) const override {
144 bool SupportsGroupPolicyEnabledComponentUpdates() const override {
148 bool RequiresNetworkEncryption() const override { return true; }
150 update_client::CrxInstaller::Result OnCustomInstall(
151 const base::Value::Dict& manifest,
152 const base::FilePath& install_dir) override {
153 return update_client::CrxInstaller::Result(0);
156 void OnCustomUninstall() override {}
158 void ComponentReady(const base::Version& version,
159 const base::FilePath& install_dir,
160 base::Value::Dict manifest) override {
161 if (component_ready_cb_) {
162 std::move(component_ready_cb_)
163 .Run(version, install_dir, std::move(manifest));
167 base::FilePath GetRelativeInstallDir() const override {
168 return base::FilePath(relative_install_dir);
171 void GetHash(std::vector<uint8_t>* hash) const override { GetPkHash(hash); }
173 std::string GetName() const override { return "fake name"; }
175 update_client::InstallerAttributes GetInstallerAttributes() const override {
176 update_client::InstallerAttributes installer_attributes;
177 installer_attributes["ap"] = "fake-ap";
178 installer_attributes["is-enterprise"] = "1";
179 return installer_attributes;
183 static void GetPkHash(std::vector<uint8_t>* hash) {
184 hash->assign(std::begin(kSha256Hash), std::end(kSha256Hash));
187 ComponentReadyCallback component_ready_cb_;
190 class MockUpdateScheduler : public UpdateScheduler {
192 MOCK_METHOD4(Schedule,
193 void(const base::TimeDelta& initial_delay,
194 const base::TimeDelta& delay,
195 const UserTask& user_task,
196 const OnStopTaskCallback& on_stop));
197 MOCK_METHOD0(Stop, void());
200 class ComponentInstallerTest : public testing::Test {
202 ComponentInstallerTest();
203 ~ComponentInstallerTest() override;
205 MockUpdateClient& update_client() { return *update_client_; }
206 ComponentUpdateService* component_updater() {
207 return component_updater_.get();
209 scoped_refptr<TestConfigurator> configurator() const { return config_; }
210 base::OnceClosure quit_closure() { return runloop_.QuitClosure(); }
211 MockUpdateScheduler& scheduler() { return *scheduler_; }
215 void Unpack(const base::FilePath& crx_path);
216 ComponentUnpacker::Result result() const { return result_; }
218 base::test::TaskEnvironment task_environment_;
221 void UnpackComplete(const ComponentUnpacker::Result& result);
222 void Schedule(const base::TimeDelta& initial_delay,
223 const base::TimeDelta& delay,
224 const UpdateScheduler::UserTask& user_task,
225 const UpdateScheduler::OnStopTaskCallback& on_stop);
227 const scoped_refptr<base::SequencedTaskRunner> main_thread_task_runner_ =
228 base::SequencedTaskRunner::GetCurrentDefault();
229 base::RunLoop runloop_;
231 std::unique_ptr<TestingPrefServiceSimple> pref_ =
232 std::make_unique<TestingPrefServiceSimple>();
234 scoped_refptr<TestConfigurator> config_ =
235 base::MakeRefCounted<TestConfigurator>(pref_.get());
236 scoped_refptr<MockUpdateClient> update_client_ =
237 base::MakeRefCounted<MockUpdateClient>();
238 ComponentUnpacker::Result result_;
239 std::unique_ptr<ComponentUpdateService> component_updater_;
240 raw_ptr<MockUpdateScheduler> scheduler_ = nullptr;
243 ComponentInstallerTest::ComponentInstallerTest() {
244 EXPECT_CALL(update_client(), AddObserver(_)).Times(1);
245 auto scheduler = std::make_unique<MockUpdateScheduler>();
246 scheduler_ = scheduler.get();
247 ON_CALL(*scheduler_, Schedule(_, _, _, _))
248 .WillByDefault(Invoke(this, &ComponentInstallerTest::Schedule));
249 component_updater_ = std::make_unique<CrxUpdateService>(
250 config_, std::move(scheduler), update_client_, "");
251 RegisterComponentUpdateServicePrefs(pref_->registry());
252 update_client::RegisterPrefs(pref_->registry());
255 ComponentInstallerTest::~ComponentInstallerTest() {
256 EXPECT_CALL(update_client(), RemoveObserver(_)).Times(1);
259 void ComponentInstallerTest::RunThreads() {
263 void ComponentInstallerTest::Unpack(const base::FilePath& crx_path) {
264 auto config = base::MakeRefCounted<TestConfigurator>();
265 auto component_unpacker = base::MakeRefCounted<ComponentUnpacker>(
266 std::vector<uint8_t>(std::begin(kSha256Hash), std::end(kSha256Hash)),
267 crx_path, nullptr, config->GetUnzipperFactory()->Create(),
268 config->GetPatcherFactory()->Create(), crx_file::VerifierFormat::CRX3);
269 component_unpacker->Unpack(base::BindOnce(
270 &ComponentInstallerTest::UnpackComplete, base::Unretained(this)));
274 void ComponentInstallerTest::UnpackComplete(
275 const ComponentUnpacker::Result& result) {
278 EXPECT_EQ(update_client::UnpackerError::kNone, result_.error);
279 EXPECT_EQ(0, result_.extended_error);
281 main_thread_task_runner_->PostTask(FROM_HERE, quit_closure());
284 void ComponentInstallerTest::Schedule(
285 const base::TimeDelta& initial_delay,
286 const base::TimeDelta& delay,
287 const UpdateScheduler::UserTask& user_task,
288 const UpdateScheduler::OnStopTaskCallback& on_stop) {
289 user_task.Run(base::DoNothing());
294 absl::optional<base::FilePath> CreateComponentDirectory(
295 const base::FilePath& base_dir,
296 const std::string& name,
297 const std::string& version,
298 const std::string& min_env_version) {
299 base::FilePath component_dir =
300 base_dir.AppendASCII(name).AppendASCII(version);
302 if (!base::CreateDirectory(component_dir))
303 return absl::nullopt;
305 if (!base::WriteFile(component_dir.AppendASCII("manifest.json"),
306 base::StringPrintf(R"({
309 "min_env_version": "%s"
311 name.c_str(), version.c_str(),
312 min_env_version.c_str())))
313 return absl::nullopt;
315 return absl::make_optional(component_dir);
318 // Tests that the component metadata is propagated from the component installer
319 // and its component policy, through the instance of the CrxComponent, to the
320 // component updater service.
321 TEST_F(ComponentInstallerTest, RegisterComponent) {
324 LoopHandler(int max_cnt, base::OnceClosure quit_closure)
325 : max_cnt_(max_cnt), quit_closure_(std::move(quit_closure)) {}
327 void OnUpdate(const std::vector<std::string>& ids,
328 const UpdateClient::CrxDataCallback& crx_data_callback) {
332 std::move(quit_closure_).Run();
337 base::OnceClosure quit_closure_;
340 base::ScopedPathOverride scoped_path_override(DIR_COMPONENT_USER);
342 const std::string id("jebgalgnebhfojomionfpkfelancnnkf");
344 // Quit after one update check has been fired.
345 LoopHandler loop_handler(1, quit_closure());
346 EXPECT_CALL(update_client(), DoUpdate(_, _))
347 .WillRepeatedly(Invoke(&loop_handler, &LoopHandler::OnUpdate));
349 EXPECT_CALL(update_client(), GetCrxUpdateState(id, _)).Times(1);
350 EXPECT_CALL(update_client(), Stop()).Times(1);
351 EXPECT_CALL(scheduler(), Schedule(_, _, _, _)).Times(1);
352 EXPECT_CALL(scheduler(), Stop()).Times(1);
354 auto installer = base::MakeRefCounted<ComponentInstaller>(
355 std::make_unique<MockInstallerPolicy>());
356 installer->Register(component_updater(), base::OnceClosure());
361 EXPECT_TRUE(component_updater()->GetComponentDetails(id, &item));
362 ASSERT_TRUE(item.component);
363 const CrxComponent& component = *item.component;
365 update_client::InstallerAttributes expected_attrs;
366 expected_attrs["ap"] = "fake-ap";
367 expected_attrs["is-enterprise"] = "1";
370 std::vector<uint8_t>(std::begin(kSha256Hash), std::end(kSha256Hash)),
372 EXPECT_EQ(base::Version("0.0.0.0"), component.version);
373 EXPECT_TRUE(component.fingerprint.empty());
374 EXPECT_STREQ("fake name", component.name.c_str());
375 EXPECT_EQ(expected_attrs, component.installer_attributes);
376 EXPECT_TRUE(component.requires_network_encryption);
379 // Tests that `ComponentInstallerPolicy::ComponentReady` and the completion
380 // callback of `ComponentInstaller::Register` are called in sequence.
381 TEST_F(ComponentInstallerTest, InstallerRegister_CheckSequence) {
382 class RegisterHandler {
384 virtual ~RegisterHandler() = default;
386 virtual void ComponentReady() = 0;
387 virtual void RegisterComplete() = 0;
390 // Allows defining call expectations on its functions when the functions
391 // are invoked by callbacks posted from `ComponentInstaller::Register`.
392 class MockRegisterHandler : public RegisterHandler {
394 MockRegisterHandler() {
395 ON_CALL(*this, ComponentReady)
396 .WillByDefault(Invoke(this, &MockRegisterHandler::CheckSequence));
397 ON_CALL(*this, RegisterComplete)
398 .WillByDefault(Invoke(this, &MockRegisterHandler::CheckSequence));
401 MOCK_METHOD(void, ComponentReady, (), (override));
402 MOCK_METHOD(void, RegisterComplete, (), (override));
405 void CheckSequence() { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); }
406 SEQUENCE_CHECKER(sequence_checker_);
409 base::ScopedPathOverride scoped_path_override(DIR_COMPONENT_USER);
411 // Install a CRX component so that `ComponentInstallerPolicy::ComponentReady`
412 // can be invoked later on.
414 base::RunLoop run_loop;
415 auto installer = base::MakeRefCounted<ComponentInstaller>(
416 std::make_unique<MockInstallerPolicy>());
418 update_client::GetTestFilePath("jebgalgnebhfojomionfpkfelancnnkf.crx"));
419 ASSERT_EQ(result().error, update_client::UnpackerError::kNone);
420 base::FilePath base_dir;
421 ASSERT_TRUE(base::PathService::Get(DIR_COMPONENT_USER, &base_dir));
422 base_dir = base_dir.Append(relative_install_dir);
423 ASSERT_TRUE(base::CreateDirectory(base_dir));
425 result().unpack_path, update_client::jebg_public_key, nullptr,
427 base::BindLambdaForTesting(
428 [&run_loop](const update_client::CrxInstaller::Result& result) {
429 ASSERT_EQ(result.error, 0);
430 run_loop.QuitClosure().Run();
435 base::RunLoop run_loop;
436 EXPECT_CALL(update_client(), DoUpdate(_, _)).WillOnce(Invoke([&run_loop]() {
437 run_loop.QuitClosure().Run();
440 // Set up expectations for uninteresting calls on the mocks due to component
441 // updater waking up after the component is registered.
442 EXPECT_CALL(scheduler(), Schedule(_, _, _, _)).Times(1);
443 EXPECT_CALL(scheduler(), Stop()).Times(1);
444 EXPECT_CALL(update_client(), Stop()).Times(1);
446 MockRegisterHandler mock_register_handler;
448 ::testing::InSequence seq;
449 EXPECT_CALL(mock_register_handler, ComponentReady()).Times(1);
450 EXPECT_CALL(mock_register_handler, RegisterComplete()).Times(1);
453 auto installer_policy =
454 std::make_unique<MockInstallerPolicy>(base::BindLambdaForTesting(
455 [&mock_register_handler](const base::Version& version,
456 const base::FilePath& install_dir,
457 base::Value::Dict manifest) {
458 EXPECT_EQ(version.GetString(), "1.0");
459 mock_register_handler.ComponentReady();
462 base::MakeRefCounted<ComponentInstaller>(std::move(installer_policy));
463 installer->Register(component_updater(),
464 base::BindLambdaForTesting([&mock_register_handler]() {
465 mock_register_handler.RegisterComplete();
470 // Tests that the unpack path is removed when the install succeeded.
471 TEST_F(ComponentInstallerTest, UnpackPathInstallSuccess) {
472 auto installer = base::MakeRefCounted<ComponentInstaller>(
473 std::make_unique<MockInstallerPolicy>());
476 update_client::GetTestFilePath("jebgalgnebhfojomionfpkfelancnnkf.crx"));
478 const auto unpack_path = result().unpack_path;
479 EXPECT_TRUE(base::DirectoryExists(unpack_path));
480 EXPECT_EQ(update_client::jebg_public_key, result().public_key);
482 base::ScopedPathOverride scoped_path_override(DIR_COMPONENT_USER);
483 base::FilePath base_dir;
484 EXPECT_TRUE(base::PathService::Get(DIR_COMPONENT_USER, &base_dir));
485 base_dir = base_dir.Append(relative_install_dir);
486 EXPECT_TRUE(base::CreateDirectory(base_dir));
488 unpack_path, update_client::jebg_public_key, nullptr, base::DoNothing(),
489 base::BindOnce([](const update_client::CrxInstaller::Result& result) {
490 EXPECT_EQ(0, result.error);
493 task_environment_.RunUntilIdle();
495 EXPECT_FALSE(base::PathExists(unpack_path));
496 EXPECT_CALL(update_client(), Stop()).Times(1);
497 EXPECT_CALL(scheduler(), Stop()).Times(1);
500 // Tests that the unpack path is removed when the install failed.
501 TEST_F(ComponentInstallerTest, UnpackPathInstallError) {
502 auto installer = base::MakeRefCounted<ComponentInstaller>(
503 std::make_unique<MockInstallerPolicy>());
506 update_client::GetTestFilePath("jebgalgnebhfojomionfpkfelancnnkf.crx"));
508 const auto unpack_path = result().unpack_path;
509 EXPECT_TRUE(base::DirectoryExists(unpack_path));
511 // Test the precondition that DIR_COMPONENT_USER is not registered with
513 base::FilePath base_dir;
514 EXPECT_FALSE(base::PathService::Get(DIR_COMPONENT_USER, &base_dir));
516 // Calling |Install| fails since DIR_COMPONENT_USER does not exist.
518 unpack_path, update_client::jebg_public_key, nullptr, base::DoNothing(),
519 base::BindOnce([](const update_client::CrxInstaller::Result& result) {
520 EXPECT_EQ(static_cast<int>(
521 update_client::InstallError::NO_DIR_COMPONENT_USER),
525 task_environment_.RunUntilIdle();
527 EXPECT_FALSE(base::PathExists(unpack_path));
528 EXPECT_CALL(update_client(), Stop()).Times(1);
529 EXPECT_CALL(scheduler(), Stop()).Times(1);
532 TEST_F(ComponentInstallerTest, SelectComponentVersion) {
533 auto installer = base::MakeRefCounted<ComponentInstaller>(
534 std::make_unique<MockInstallerPolicy>());
536 base::FilePath base_dir;
537 base::ScopedPathOverride scoped_path_override(DIR_COMPONENT_USER);
538 ASSERT_TRUE(base::PathService::Get(DIR_COMPONENT_USER, &base_dir));
539 base_dir = base_dir.AppendASCII("select_component_version_test");
541 for (const auto* n : {"1", "2", "3", "4", "5", "6", "7"}) {
542 CreateComponentDirectory(base_dir, "test_component",
543 base::StrCat({n, ".0.0.0"}), "0.0.1");
546 base_dir = base_dir.AppendASCII("test_component");
548 absl::optional<base::Version> selected_component;
550 auto registration_info =
551 base::MakeRefCounted<ComponentInstaller::RegistrationInfo>();
552 selected_component = installer->SelectComponentVersion(
553 base::Version("1.0.0.0"), base_dir, registration_info);
554 ASSERT_TRUE(selected_component &&
555 selected_component == base::Version("1.0.0.0"));
556 ASSERT_EQ(registration_info->version, base::Version("1.0.0.0"));
558 // Case where no valid bundled or registered version.
560 base::MakeRefCounted<ComponentInstaller::RegistrationInfo>();
561 selected_component = installer->SelectComponentVersion(
562 base::Version("0.0.0.0"), base_dir, registration_info);
563 ASSERT_TRUE(selected_component &&
564 *selected_component == base::Version("7.0.0.0"));
565 ASSERT_EQ(registration_info->version, base::Version("7.0.0.0"));
567 registration_info->version = base::Version("3.0.0.0");
568 selected_component = installer->SelectComponentVersion(
569 base::Version("5.0.0.0"), base_dir, registration_info);
570 ASSERT_TRUE(selected_component &&
571 *selected_component == base::Version("5.0.0.0"));
572 ASSERT_EQ(registration_info->version, base::Version("5.0.0.0"));
574 registration_info->version = base::Version("4.0.0.0");
575 selected_component = installer->SelectComponentVersion(
576 base::Version("0.0.0.0"), base_dir, registration_info);
577 ASSERT_TRUE(selected_component &&
578 *selected_component == base::Version("7.0.0.0"));
579 ASSERT_EQ(registration_info->version, base::Version("7.0.0.0"));
581 registration_info->version = base::Version("12.0.0.0");
582 selected_component = installer->SelectComponentVersion(
583 base::Version("1.0.0.0"), base_dir, registration_info);
584 ASSERT_FALSE(selected_component);
585 ASSERT_EQ(registration_info->version, base::Version("12.0.0.0"));
587 registration_info->version = base::Version("6.0.0.0");
588 selected_component = installer->SelectComponentVersion(
589 base::Version("1.0.0.0"), base_dir, registration_info);
590 ASSERT_TRUE(selected_component &&
591 *selected_component == base::Version("7.0.0.0"));
592 ASSERT_EQ(registration_info->version, base::Version("7.0.0.0"));
595 } // namespace component_updater