blob: d012be118d8a6923ab51c72898dd28f6fb1d4267 [file] [log] [blame]
sorin6a57db92016-06-27 22:28:151// Copyright 2016 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include <iterator>
6#include <string>
waffles77255cc2016-08-02 17:25:127#include <utility>
sorin6a57db92016-06-27 22:28:158#include <vector>
9
10#include "base/callback.h"
Sorin Jianu316232a2017-05-26 20:22:3011#include "base/files/file_path.h"
12#include "base/files/file_util.h"
sorin6a57db92016-06-27 22:28:1513#include "base/macros.h"
Sorin Jianu316232a2017-05-26 20:22:3014#include "base/memory/ptr_util.h"
sorin6a57db92016-06-27 22:28:1515#include "base/memory/ref_counted.h"
Sorin Jianu316232a2017-05-26 20:22:3016#include "base/path_service.h"
sorin6a57db92016-06-27 22:28:1517#include "base/run_loop.h"
fdorayb26583c2017-05-16 18:47:0118#include "base/task_scheduler/post_task.h"
Sorin Jianu316232a2017-05-26 20:22:3019#include "base/test/scoped_path_override.h"
fdorayb26583c2017-05-16 18:47:0120#include "base/test/scoped_task_environment.h"
21#include "base/threading/thread_task_runner_handle.h"
sorin6a57db92016-06-27 22:28:1522#include "base/version.h"
Sorin Jianu316232a2017-05-26 20:22:3023#include "components/component_updater/component_updater_paths.h"
sorin6a57db92016-06-27 22:28:1524#include "components/component_updater/component_updater_service.h"
25#include "components/component_updater/component_updater_service_internal.h"
26#include "components/component_updater/default_component_installer.h"
Sorin Jianu316232a2017-05-26 20:22:3027#include "components/update_client/component_unpacker.h"
sorin6a57db92016-06-27 22:28:1528#include "components/update_client/crx_update_item.h"
29#include "components/update_client/test_configurator.h"
30#include "components/update_client/update_client.h"
sorin7b8650522016-11-02 18:23:4131#include "components/update_client/update_client_errors.h"
sorin6a57db92016-06-27 22:28:1532#include "testing/gmock/include/gmock/gmock.h"
33#include "testing/gtest/include/gtest/gtest.h"
34
Sorin Jianu316232a2017-05-26 20:22:3035using ComponentUnpacker = update_client::ComponentUnpacker;
sorin6a57db92016-06-27 22:28:1536using Configurator = update_client::Configurator;
37using CrxUpdateItem = update_client::CrxUpdateItem;
38using TestConfigurator = update_client::TestConfigurator;
39using UpdateClient = update_client::UpdateClient;
40
41using ::testing::_;
42using ::testing::Invoke;
43
44namespace component_updater {
45
46namespace {
47
48// This hash corresponds to jebgalgnebhfojomionfpkfelancnnkf.crx.
49const uint8_t kSha256Hash[] = {0x94, 0x16, 0x0b, 0x6d, 0x41, 0x75, 0xe9, 0xec,
50 0x8e, 0xd5, 0xfa, 0x54, 0xb0, 0xd2, 0xdd, 0xa5,
51 0x6e, 0x05, 0x6b, 0xe8, 0x73, 0x47, 0xf6, 0xc4,
52 0x11, 0x9f, 0xbc, 0xb3, 0x09, 0xb3, 0x5b, 0x40};
53
Sorin Jianu316232a2017-05-26 20:22:3054constexpr base::FilePath::CharType relative_install_dir[] =
55 FILE_PATH_LITERAL("fake");
56
57base::FilePath test_file(const char* file) {
58 base::FilePath path;
59 PathService::Get(base::DIR_SOURCE_ROOT, &path);
60 return path.AppendASCII("components")
61 .AppendASCII("test")
62 .AppendASCII("data")
63 .AppendASCII("update_client")
64 .AppendASCII(file);
65}
66
sorin6a57db92016-06-27 22:28:1567class MockUpdateClient : public UpdateClient {
68 public:
sorin2adb2ca2016-06-29 01:44:3569 MockUpdateClient() {}
sorin6a57db92016-06-27 22:28:1570 MOCK_METHOD1(AddObserver, void(Observer* observer));
71 MOCK_METHOD1(RemoveObserver, void(Observer* observer));
72 MOCK_METHOD3(Install,
73 void(const std::string& id,
74 const CrxDataCallback& crx_data_callback,
sorin842703b2016-11-02 23:59:2375 const Callback& callback));
sorin6a57db92016-06-27 22:28:1576 MOCK_METHOD3(Update,
77 void(const std::vector<std::string>& ids,
78 const CrxDataCallback& crx_data_callback,
sorin842703b2016-11-02 23:59:2379 const Callback& callback));
sorin6a57db92016-06-27 22:28:1580 MOCK_CONST_METHOD2(GetCrxUpdateState,
81 bool(const std::string& id, CrxUpdateItem* update_item));
82 MOCK_CONST_METHOD1(IsUpdating, bool(const std::string& id));
83 MOCK_METHOD0(Stop, void());
sorin8037ac8c2017-04-19 16:28:0084 MOCK_METHOD4(SendUninstallPing,
85 void(const std::string& id,
86 const base::Version& version,
87 int reason,
88 const Callback& callback));
sorin6a57db92016-06-27 22:28:1589
90 private:
sorin2adb2ca2016-06-29 01:44:3591 ~MockUpdateClient() override {}
sorin6a57db92016-06-27 22:28:1592};
93
94class FakeInstallerTraits : public ComponentInstallerTraits {
95 public:
Sorin Jianu316232a2017-05-26 20:22:3096 FakeInstallerTraits() {}
sorin6a57db92016-06-27 22:28:1597 ~FakeInstallerTraits() override {}
98
99 bool VerifyInstallation(const base::DictionaryValue& manifest,
100 const base::FilePath& dir) const override {
101 return true;
102 }
103
sorin3574ef92016-08-03 16:46:01104 bool SupportsGroupPolicyEnabledComponentUpdates() const override {
sorin098a4a72016-08-30 04:43:24105 return true;
sorin3574ef92016-08-03 16:46:01106 }
sorin6a57db92016-06-27 22:28:15107
108 bool RequiresNetworkEncryption() const override { return true; }
109
sorin2892f7212016-11-07 18:59:43110 update_client::CrxInstaller::Result OnCustomInstall(
111 const base::DictionaryValue& manifest,
112 const base::FilePath& install_dir) override {
113 return update_client::CrxInstaller::Result(0);
sorin6a57db92016-06-27 22:28:15114 }
115
116 void ComponentReady(
117 const base::Version& version,
118 const base::FilePath& install_dir,
119 std::unique_ptr<base::DictionaryValue> manifest) override {}
120
121 base::FilePath GetRelativeInstallDir() const override {
Sorin Jianu316232a2017-05-26 20:22:30122 return base::FilePath(relative_install_dir);
sorin6a57db92016-06-27 22:28:15123 }
124
125 void GetHash(std::vector<uint8_t>* hash) const override { GetPkHash(hash); }
126
127 std::string GetName() const override { return "fake name"; }
128
sorin2adb2ca2016-06-29 01:44:35129 update_client::InstallerAttributes GetInstallerAttributes() const override {
130 update_client::InstallerAttributes installer_attributes;
131 installer_attributes["ap"] = "fake-ap";
132 installer_attributes["is-enterprise"] = "1";
133 return installer_attributes;
134 }
sorin6a57db92016-06-27 22:28:15135
waffles77255cc2016-08-02 17:25:12136 std::vector<std::string> GetMimeTypes() const override {
137 return std::vector<std::string>();
138 }
139
sorin6a57db92016-06-27 22:28:15140 private:
141 static void GetPkHash(std::vector<uint8_t>* hash) {
142 hash->assign(std::begin(kSha256Hash), std::end(kSha256Hash));
143 }
144};
145
146class DefaultComponentInstallerTest : public testing::Test {
147 public:
148 DefaultComponentInstallerTest();
149 ~DefaultComponentInstallerTest() override;
150
151 MockUpdateClient& update_client() { return *update_client_; }
152 ComponentUpdateService* component_updater() {
153 return component_updater_.get();
154 }
155 scoped_refptr<TestConfigurator> configurator() const { return config_; }
156 base::Closure quit_closure() const { return quit_closure_; }
157
158 protected:
159 void RunThreads();
Sorin Jianu316232a2017-05-26 20:22:30160 void Unpack(const base::FilePath& crx_path);
161 ComponentUnpacker::Result result() const { return result_; }
sorin6a57db92016-06-27 22:28:15162
163 private:
Sorin Jianu316232a2017-05-26 20:22:30164 void UnpackComplete(const ComponentUnpacker::Result& result);
165
fdorayb26583c2017-05-16 18:47:01166 base::test::ScopedTaskEnvironment scoped_task_environment_;
Sorin Jianu316232a2017-05-26 20:22:30167 const scoped_refptr<base::SingleThreadTaskRunner> main_thread_task_runner_ =
168 base::ThreadTaskRunnerHandle::Get();
sorin6a57db92016-06-27 22:28:15169 base::RunLoop runloop_;
170 base::Closure quit_closure_;
171
sorin6a57db92016-06-27 22:28:15172 scoped_refptr<TestConfigurator> config_;
173 scoped_refptr<MockUpdateClient> update_client_;
174 std::unique_ptr<ComponentUpdateService> component_updater_;
Sorin Jianu316232a2017-05-26 20:22:30175 ComponentUnpacker::Result result_;
sorin6a57db92016-06-27 22:28:15176};
177
178DefaultComponentInstallerTest::DefaultComponentInstallerTest()
fdorayb26583c2017-05-16 18:47:01179 : scoped_task_environment_(
180 base::test::ScopedTaskEnvironment::MainThreadType::UI) {
sorin6a57db92016-06-27 22:28:15181 quit_closure_ = runloop_.QuitClosure();
182
Sorin Jianu316232a2017-05-26 20:22:30183 config_ = base::MakeRefCounted<TestConfigurator>(
fdorayb26583c2017-05-16 18:47:01184 base::CreateSequencedTaskRunnerWithTraits({base::MayBlock()}),
185 base::ThreadTaskRunnerHandle::Get());
sorin6a57db92016-06-27 22:28:15186
Sorin Jianu316232a2017-05-26 20:22:30187 update_client_ = base::MakeRefCounted<MockUpdateClient>();
sorin6a57db92016-06-27 22:28:15188 EXPECT_CALL(update_client(), AddObserver(_)).Times(1);
Sorin Jianu316232a2017-05-26 20:22:30189 component_updater_ =
190 base::MakeUnique<CrxUpdateService>(config_, update_client_);
sorin6a57db92016-06-27 22:28:15191}
192
193DefaultComponentInstallerTest::~DefaultComponentInstallerTest() {
194 EXPECT_CALL(update_client(), RemoveObserver(_)).Times(1);
195 component_updater_.reset();
196}
197
198void DefaultComponentInstallerTest::RunThreads() {
199 runloop_.Run();
200}
201
Sorin Jianu316232a2017-05-26 20:22:30202void DefaultComponentInstallerTest::Unpack(const base::FilePath& crx_path) {
203 auto component_unpacker = base::MakeRefCounted<ComponentUnpacker>(
204 std::vector<uint8_t>(std::begin(kSha256Hash), std::end(kSha256Hash)),
205 crx_path, nullptr, nullptr, config_->GetSequencedTaskRunner());
206 component_unpacker->Unpack(base::Bind(
207 &DefaultComponentInstallerTest::UnpackComplete, base::Unretained(this)));
208 RunThreads();
209}
210
211void DefaultComponentInstallerTest::UnpackComplete(
212 const ComponentUnpacker::Result& result) {
213 result_ = result;
214
215 EXPECT_EQ(update_client::UnpackerError::kNone, result_.error);
216 EXPECT_EQ(0, result_.extended_error);
217
218 main_thread_task_runner_->PostTask(FROM_HERE, quit_closure_);
219}
220
sorin6a57db92016-06-27 22:28:15221} // namespace
222
223// Tests that the component metadata is propagated from the default
224// component installer and its component traits, through the instance of the
225// CrxComponent, to the component updater service.
226TEST_F(DefaultComponentInstallerTest, RegisterComponent) {
227 class LoopHandler {
228 public:
229 LoopHandler(int max_cnt, const base::Closure& quit_closure)
230 : max_cnt_(max_cnt), quit_closure_(quit_closure) {}
231
232 void OnUpdate(const std::vector<std::string>& ids,
233 const UpdateClient::CrxDataCallback& crx_data_callback,
sorin842703b2016-11-02 23:59:23234 const Callback& callback) {
235 callback.Run(update_client::Error::NONE);
sorin6a57db92016-06-27 22:28:15236 static int cnt = 0;
237 ++cnt;
238 if (cnt >= max_cnt_)
239 quit_closure_.Run();
240 }
241
242 private:
243 const int max_cnt_;
244 base::Closure quit_closure_;
245 };
246
247 const std::string id("jebgalgnebhfojomionfpkfelancnnkf");
248
249 // Quit after one update check has been fired.
250 LoopHandler loop_handler(1, quit_closure());
251 EXPECT_CALL(update_client(), Update(_, _, _))
252 .WillRepeatedly(Invoke(&loop_handler, &LoopHandler::OnUpdate));
253
254 EXPECT_CALL(update_client(), GetCrxUpdateState(id, _)).Times(1);
255 EXPECT_CALL(update_client(), Stop()).Times(1);
256
Sorin Jianu316232a2017-05-26 20:22:30257 auto installer = base::MakeRefCounted<DefaultComponentInstaller>(
258 base::MakeUnique<FakeInstallerTraits>());
sorin6a57db92016-06-27 22:28:15259 installer->Register(component_updater(), base::Closure());
260
261 RunThreads();
262
263 CrxUpdateItem item;
264 EXPECT_TRUE(component_updater()->GetComponentDetails(id, &item));
265 const CrxComponent& component(item.component);
sorin2adb2ca2016-06-29 01:44:35266
267 update_client::InstallerAttributes expected_attrs;
268 expected_attrs["ap"] = "fake-ap";
269 expected_attrs["is-enterprise"] = "1";
270
sorin6a57db92016-06-27 22:28:15271 EXPECT_EQ(
272 std::vector<uint8_t>(std::begin(kSha256Hash), std::end(kSha256Hash)),
273 component.pk_hash);
274 EXPECT_EQ(base::Version("0.0.0.0"), component.version);
275 EXPECT_TRUE(component.fingerprint.empty());
276 EXPECT_STREQ("fake name", component.name.c_str());
sorin2adb2ca2016-06-29 01:44:35277 EXPECT_EQ(expected_attrs, component.installer_attributes);
sorin6a57db92016-06-27 22:28:15278 EXPECT_TRUE(component.requires_network_encryption);
sorin098a4a72016-08-30 04:43:24279 EXPECT_TRUE(component.supports_group_policy_enable_component_updates);
sorin6a57db92016-06-27 22:28:15280}
281
Sorin Jianu316232a2017-05-26 20:22:30282// Tests that the unpack path is removed when the install succeeded.
283TEST_F(DefaultComponentInstallerTest, UnpackPathInstallSuccess) {
284 auto installer = base::MakeRefCounted<DefaultComponentInstaller>(
285 base::MakeUnique<FakeInstallerTraits>());
286
287 Unpack(test_file("jebgalgnebhfojomionfpkfelancnnkf.crx"));
288
289 const auto unpack_path = result().unpack_path;
290 EXPECT_TRUE(base::DirectoryExists(unpack_path));
291
292 const auto manifest = update_client::ReadManifest(unpack_path);
293
294 base::ScopedPathOverride scoped_path_override(DIR_COMPONENT_USER);
295 base::FilePath base_dir;
296 EXPECT_TRUE(PathService::Get(DIR_COMPONENT_USER, &base_dir));
297 base_dir = base_dir.Append(relative_install_dir);
298 EXPECT_TRUE(base::CreateDirectory(base_dir));
299 const auto result = installer->Install(*manifest, unpack_path);
300 EXPECT_EQ(0, result.error);
301 EXPECT_FALSE(base::PathExists(unpack_path));
302
303 EXPECT_CALL(update_client(), Stop()).Times(1);
304}
305
306// Tests that the unpack path is removed when the install failed.
307TEST_F(DefaultComponentInstallerTest, UnpackPathInstallError) {
308 auto installer = base::MakeRefCounted<DefaultComponentInstaller>(
309 base::MakeUnique<FakeInstallerTraits>());
310
311 Unpack(test_file("jebgalgnebhfojomionfpkfelancnnkf.crx"));
312
313 const auto unpack_path = result().unpack_path;
314 EXPECT_TRUE(base::DirectoryExists(unpack_path));
315
316 const auto manifest = update_client::ReadManifest(unpack_path);
317
318 // Test the precondition that DIR_COMPONENT_USER is not registered with
319 // the path service.
320 base::FilePath base_dir;
321 EXPECT_FALSE(PathService::Get(DIR_COMPONENT_USER, &base_dir));
322
323 // Calling |Install| fails since DIR_COMPONENT_USER does not exist.
324 const auto result = installer->Install(*manifest, unpack_path);
325 EXPECT_EQ(
326 static_cast<int>(update_client::InstallError::NO_DIR_COMPONENT_USER),
327 result.error);
328 EXPECT_FALSE(base::PathExists(unpack_path));
329
330 EXPECT_CALL(update_client(), Stop()).Times(1);
331}
332
sorin6a57db92016-06-27 22:28:15333} // namespace component_updater