blob: 47c7982a14c94f4ea07460917e06d9af2e1e2789 [file] [log] [blame]
sorin7c717622015-05-26 19:59:091// Copyright 2015 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
dchenga0ee5fb82016-04-26 02:46:555#include "components/component_updater/component_updater_service.h"
6
sorin7c717622015-05-26 19:59:097#include <limits>
8#include <string>
9#include <vector>
10
11#include "base/bind.h"
12#include "base/bind_helpers.h"
13#include "base/files/file_path.h"
14#include "base/files/file_util.h"
15#include "base/macros.h"
dchenga0ee5fb82016-04-26 02:46:5516#include "base/memory/ptr_util.h"
sorin7c717622015-05-26 19:59:0917#include "base/memory/ref_counted.h"
sorin7c717622015-05-26 19:59:0918#include "base/message_loop/message_loop.h"
19#include "base/run_loop.h"
sorin85953dc2016-03-10 00:32:4820#include "base/test/histogram_tester.h"
sorin85e68032015-05-27 19:50:5121#include "base/test/sequenced_worker_pool_owner.h"
gab7966d312016-05-11 20:35:0122#include "base/threading/thread_task_runner_handle.h"
sorin7c717622015-05-26 19:59:0923#include "base/values.h"
sorin7c717622015-05-26 19:59:0924#include "components/component_updater/component_updater_service_internal.h"
25#include "components/update_client/test_configurator.h"
26#include "components/update_client/test_installer.h"
27#include "components/update_client/update_client.h"
sorin7b8650522016-11-02 18:23:4128#include "components/update_client/update_client_errors.h"
sorin7c717622015-05-26 19:59:0929#include "testing/gmock/include/gmock/gmock.h"
30#include "testing/gtest/include/gtest/gtest.h"
31
32using Configurator = update_client::Configurator;
33using TestConfigurator = update_client::TestConfigurator;
34using UpdateClient = update_client::UpdateClient;
35
36using ::testing::_;
37using ::testing::AnyNumber;
38using ::testing::Invoke;
39using ::testing::Mock;
40using ::testing::Return;
41
42namespace component_updater {
43
44class MockInstaller : public CrxInstaller {
45 public:
46 MockInstaller();
47
48 MOCK_METHOD1(OnUpdateError, void(int error));
49 MOCK_METHOD2(Install,
50 bool(const base::DictionaryValue& manifest,
51 const base::FilePath& unpack_path));
52 MOCK_METHOD2(GetInstalledFile,
53 bool(const std::string& file, base::FilePath* installed_file));
54 MOCK_METHOD0(Uninstall, bool());
55
56 private:
57 ~MockInstaller() override;
58};
59
60class MockUpdateClient : public UpdateClient {
61 public:
62 MockUpdateClient();
63 MOCK_METHOD1(AddObserver, void(Observer* observer));
64 MOCK_METHOD1(RemoveObserver, void(Observer* observer));
65 MOCK_METHOD3(Install,
66 void(const std::string& id,
67 const CrxDataCallback& crx_data_callback,
68 const CompletionCallback& completion_callback));
69 MOCK_METHOD3(Update,
70 void(const std::vector<std::string>& ids,
71 const CrxDataCallback& crx_data_callback,
72 const CompletionCallback& completion_callback));
73 MOCK_CONST_METHOD2(GetCrxUpdateState,
74 bool(const std::string& id, CrxUpdateItem* update_item));
75 MOCK_CONST_METHOD1(IsUpdating, bool(const std::string& id));
sorinecaad3e2015-11-13 19:15:5276 MOCK_METHOD0(Stop, void());
pwnall15745b312016-08-19 21:45:2977 MOCK_METHOD3(
78 SendUninstallPing,
79 void(const std::string& id, const base::Version& version, int reason));
sorin7c717622015-05-26 19:59:0980
81 private:
82 ~MockUpdateClient() override;
83};
84
85class MockServiceObserver : public ServiceObserver {
86 public:
87 MockServiceObserver();
88 ~MockServiceObserver() override;
89
90 MOCK_METHOD2(OnEvent, void(Events event, const std::string&));
91};
92
93class ComponentUpdaterTest : public testing::Test {
94 public:
95 ComponentUpdaterTest();
96 ~ComponentUpdaterTest() override;
97
98 void SetUp() override;
99
100 void TearDown() override;
101
102 // Makes the full path to a component updater test file.
103 const base::FilePath test_file(const char* file);
104
105 MockUpdateClient& update_client() { return *update_client_; }
106 ComponentUpdateService& component_updater() { return *component_updater_; }
107 scoped_refptr<TestConfigurator> configurator() const { return config_; }
108 base::Closure quit_closure() const { return quit_closure_; }
109 static void ReadyCallback() {}
110
111 protected:
112 void RunThreads();
113
114 private:
115 static const int kNumWorkerThreads_ = 2;
116
117 base::MessageLoopForUI message_loop_;
118 base::RunLoop runloop_;
119 base::Closure quit_closure_;
120
dchenga0ee5fb82016-04-26 02:46:55121 std::unique_ptr<base::SequencedWorkerPoolOwner> worker_pool_;
sorin7c717622015-05-26 19:59:09122
123 scoped_refptr<TestConfigurator> config_;
124 scoped_refptr<MockUpdateClient> update_client_;
dchenga0ee5fb82016-04-26 02:46:55125 std::unique_ptr<ComponentUpdateService> component_updater_;
sorin7c717622015-05-26 19:59:09126
127 DISALLOW_COPY_AND_ASSIGN(ComponentUpdaterTest);
128};
129
130class OnDemandTester {
131 public:
sorin4c520182016-08-19 17:27:44132 void OnDemand(ComponentUpdateService* cus, const std::string& id);
sorin7b8650522016-11-02 18:23:41133 update_client::Error error() const { return error_; }
sorin4c520182016-08-19 17:27:44134
135 private:
sorin7b8650522016-11-02 18:23:41136 void OnDemandComplete(update_client::Error error);
sorin4c520182016-08-19 17:27:44137
sorin7b8650522016-11-02 18:23:41138 update_client::Error error_ = update_client::Error::NONE;
sorin7c717622015-05-26 19:59:09139};
140
141MockInstaller::MockInstaller() {
142}
143
144MockInstaller::~MockInstaller() {
145}
146
147MockUpdateClient::MockUpdateClient() {
148}
149
150MockUpdateClient::~MockUpdateClient() {
151}
152
153MockServiceObserver::MockServiceObserver() {
154}
155
156MockServiceObserver::~MockServiceObserver() {
157}
158
sorin4c520182016-08-19 17:27:44159void OnDemandTester::OnDemand(ComponentUpdateService* cus,
sorin7c717622015-05-26 19:59:09160 const std::string& id) {
sorin4c520182016-08-19 17:27:44161 cus->GetOnDemandUpdater().OnDemandUpdate(
162 id,
163 base::Bind(&OnDemandTester::OnDemandComplete, base::Unretained(this)));
164}
165
sorin7b8650522016-11-02 18:23:41166void OnDemandTester::OnDemandComplete(update_client::Error error) {
sorin4c520182016-08-19 17:27:44167 error_ = error;
sorin7c717622015-05-26 19:59:09168}
169
dchenga0ee5fb82016-04-26 02:46:55170std::unique_ptr<ComponentUpdateService> TestComponentUpdateServiceFactory(
sorin7c717622015-05-26 19:59:09171 const scoped_refptr<Configurator>& config) {
172 DCHECK(config);
ricea85ec57952016-08-31 09:34:10173 return base::MakeUnique<CrxUpdateService>(config, new MockUpdateClient());
sorin7c717622015-05-26 19:59:09174}
175
176ComponentUpdaterTest::ComponentUpdaterTest()
sorin85e68032015-05-27 19:50:51177 : worker_pool_(
178 new base::SequencedWorkerPoolOwner(kNumWorkerThreads_, "test")) {
sorin7c717622015-05-26 19:59:09179 quit_closure_ = runloop_.QuitClosure();
180
sorin85e68032015-05-27 19:50:51181 auto pool = worker_pool_->pool();
sorin7c717622015-05-26 19:59:09182 config_ = new TestConfigurator(
sorin85e68032015-05-27 19:50:51183 pool->GetSequencedTaskRunner(pool->GetSequenceToken()),
sorin7c717622015-05-26 19:59:09184 message_loop_.task_runner());
185
186 update_client_ = new MockUpdateClient();
187 EXPECT_CALL(update_client(), AddObserver(_)).Times(1);
188 component_updater_.reset(new CrxUpdateService(config_, update_client_));
189}
190
191ComponentUpdaterTest::~ComponentUpdaterTest() {
192 EXPECT_CALL(update_client(), RemoveObserver(_)).Times(1);
sorin7c717622015-05-26 19:59:09193 component_updater_.reset();
194}
195
196void ComponentUpdaterTest::SetUp() {
197}
198
199void ComponentUpdaterTest::TearDown() {
200}
201
202void ComponentUpdaterTest::RunThreads() {
203 runloop_.Run();
204}
205
206TEST_F(ComponentUpdaterTest, AddObserver) {
207 MockServiceObserver observer;
208 EXPECT_CALL(update_client(), AddObserver(&observer)).Times(1);
sorinecaad3e2015-11-13 19:15:52209 EXPECT_CALL(update_client(), Stop()).Times(1);
sorin7c717622015-05-26 19:59:09210 component_updater().AddObserver(&observer);
211}
212
213TEST_F(ComponentUpdaterTest, RemoveObserver) {
214 MockServiceObserver observer;
215 EXPECT_CALL(update_client(), RemoveObserver(&observer)).Times(1);
sorinecaad3e2015-11-13 19:15:52216 EXPECT_CALL(update_client(), Stop()).Times(1);
sorin7c717622015-05-26 19:59:09217 component_updater().RemoveObserver(&observer);
218}
219
220// Tests that UpdateClient::Update is called by the timer loop when
221// components are registered, and the component update starts.
222// Also tests that Uninstall is called when a component is unregistered.
223TEST_F(ComponentUpdaterTest, RegisterComponent) {
224 class LoopHandler {
225 public:
226 LoopHandler(int max_cnt, const base::Closure& quit_closure)
227 : max_cnt_(max_cnt), quit_closure_(quit_closure) {}
228
229 void OnUpdate(const std::vector<std::string>& ids,
230 const UpdateClient::CrxDataCallback& crx_data_callback,
231 const UpdateClient::CompletionCallback& completion_callback) {
sorin7b8650522016-11-02 18:23:41232 completion_callback.Run(update_client::Error::NONE);
sorin7c717622015-05-26 19:59:09233 static int cnt = 0;
234 ++cnt;
235 if (cnt >= max_cnt_)
236 quit_closure_.Run();
237 }
238
239 private:
240 const int max_cnt_;
241 base::Closure quit_closure_;
242 };
243
sorin85953dc2016-03-10 00:32:48244 base::HistogramTester ht;
245
sorin7c717622015-05-26 19:59:09246 scoped_refptr<MockInstaller> installer(new MockInstaller());
247 EXPECT_CALL(*installer, Uninstall()).WillOnce(Return(true));
248
249 using update_client::jebg_hash;
250 using update_client::abag_hash;
251
252 const std::string id1 = "abagagagagagagagagagagagagagagag";
253 const std::string id2 = "jebgalgnebhfojomionfpkfelancnnkf";
254 std::vector<std::string> ids;
255 ids.push_back(id1);
256 ids.push_back(id2);
257
258 CrxComponent crx_component1;
259 crx_component1.pk_hash.assign(abag_hash, abag_hash + arraysize(abag_hash));
pwnall15745b312016-08-19 21:45:29260 crx_component1.version = base::Version("1.0");
sorin7c717622015-05-26 19:59:09261 crx_component1.installer = installer;
262
263 CrxComponent crx_component2;
264 crx_component2.pk_hash.assign(jebg_hash, jebg_hash + arraysize(jebg_hash));
pwnall15745b312016-08-19 21:45:29265 crx_component2.version = base::Version("0.9");
sorin7c717622015-05-26 19:59:09266 crx_component2.installer = installer;
267
268 // Quit after two update checks have fired.
269 LoopHandler loop_handler(2, quit_closure());
270 EXPECT_CALL(update_client(), Update(ids, _, _))
271 .WillRepeatedly(Invoke(&loop_handler, &LoopHandler::OnUpdate));
272
273 EXPECT_CALL(update_client(), IsUpdating(id1)).Times(1);
sorinecaad3e2015-11-13 19:15:52274 EXPECT_CALL(update_client(), Stop()).Times(1);
sorin7c717622015-05-26 19:59:09275
276 EXPECT_TRUE(component_updater().RegisterComponent(crx_component1));
277 EXPECT_TRUE(component_updater().RegisterComponent(crx_component2));
278
279 RunThreads();
sorin7c717622015-05-26 19:59:09280 EXPECT_TRUE(component_updater().UnregisterComponent(id1));
sorin85953dc2016-03-10 00:32:48281
282 ht.ExpectUniqueSample("ComponentUpdater.Calls", 1, 2);
283 ht.ExpectUniqueSample("ComponentUpdater.UpdateCompleteResult", 0, 2);
284 ht.ExpectTotalCount("ComponentUpdater.UpdateCompleteTime", 2);
sorin7c717622015-05-26 19:59:09285}
286
287// Tests that on-demand updates invoke UpdateClient::Install.
288TEST_F(ComponentUpdaterTest, OnDemandUpdate) {
289 class LoopHandler {
290 public:
sorin7b8650522016-11-02 18:23:41291 explicit LoopHandler(int max_cnt) : max_cnt_(max_cnt) {}
sorin7c717622015-05-26 19:59:09292
293 void OnInstall(
294 const std::string& ids,
295 const UpdateClient::CrxDataCallback& crx_data_callback,
296 const UpdateClient::CompletionCallback& completion_callback) {
sorin7b8650522016-11-02 18:23:41297 completion_callback.Run(update_client::Error::NONE);
sorin7c717622015-05-26 19:59:09298 static int cnt = 0;
299 ++cnt;
sorin4c520182016-08-19 17:27:44300 if (cnt >= max_cnt_) {
301 base::ThreadTaskRunnerHandle::Get()->PostTask(
302 FROM_HERE, base::Bind(&LoopHandler::Quit, base::Unretained(this)));
303 }
sorin7c717622015-05-26 19:59:09304 }
305
306 private:
sorin4c520182016-08-19 17:27:44307 void Quit() { base::MessageLoop::current()->QuitWhenIdle(); }
308
sorin7c717622015-05-26 19:59:09309 const int max_cnt_;
sorin7c717622015-05-26 19:59:09310 };
311
sorin85953dc2016-03-10 00:32:48312 base::HistogramTester ht;
313
sorin7c717622015-05-26 19:59:09314 auto config = configurator();
315 config->SetInitialDelay(3600);
316
317 auto& cus = component_updater();
318
sorin4c520182016-08-19 17:27:44319 // Tests calling OnDemand for an unregistered component. This call results in
320 // an error, which is recorded by the OnDemandTester instance. Since the
321 // component was not registered, the call is ignored for UMA metrics.
322 OnDemandTester ondemand_tester_component_not_registered;
323 ondemand_tester_component_not_registered.OnDemand(
324 &cus, "ihfokbkgjpifnbbojhneepfflplebdkc");
sorin7c717622015-05-26 19:59:09325
sorin4c520182016-08-19 17:27:44326 const std::string id = "jebgalgnebhfojomionfpkfelancnnkf";
sorin7c717622015-05-26 19:59:09327
328 using update_client::jebg_hash;
329 CrxComponent crx_component;
330 crx_component.pk_hash.assign(jebg_hash, jebg_hash + arraysize(jebg_hash));
pwnall15745b312016-08-19 21:45:29331 crx_component.version = base::Version("0.9");
sorin4c520182016-08-19 17:27:44332 crx_component.installer = new MockInstaller();
sorin7c717622015-05-26 19:59:09333
sorin4c520182016-08-19 17:27:44334 LoopHandler loop_handler(1);
sorin7c717622015-05-26 19:59:09335 EXPECT_CALL(update_client(),
336 Install("jebgalgnebhfojomionfpkfelancnnkf", _, _))
337 .WillOnce(Invoke(&loop_handler, &LoopHandler::OnInstall));
sorinecaad3e2015-11-13 19:15:52338 EXPECT_CALL(update_client(), Stop()).Times(1);
sorin7c717622015-05-26 19:59:09339
340 EXPECT_TRUE(cus.RegisterComponent(crx_component));
sorin4c520182016-08-19 17:27:44341 OnDemandTester ondemand_tester;
342 ondemand_tester.OnDemand(&cus, id);
sorin7c717622015-05-26 19:59:09343
sorin4c520182016-08-19 17:27:44344 base::RunLoop().Run();
345
sorin7b8650522016-11-02 18:23:41346 EXPECT_EQ(update_client::Error::INVALID_ARGUMENT,
347 ondemand_tester_component_not_registered.error());
348 EXPECT_EQ(update_client::Error::NONE, ondemand_tester.error());
sorin85953dc2016-03-10 00:32:48349
350 ht.ExpectUniqueSample("ComponentUpdater.Calls", 0, 1);
351 ht.ExpectUniqueSample("ComponentUpdater.UpdateCompleteResult", 0, 1);
352 ht.ExpectTotalCount("ComponentUpdater.UpdateCompleteTime", 1);
sorin7c717622015-05-26 19:59:09353}
354
355// Tests that throttling an update invokes UpdateClient::Install.
356TEST_F(ComponentUpdaterTest, MaybeThrottle) {
357 class LoopHandler {
358 public:
359 LoopHandler(int max_cnt, const base::Closure& quit_closure)
360 : max_cnt_(max_cnt), quit_closure_(quit_closure) {}
361
362 void OnInstall(
363 const std::string& ids,
364 const UpdateClient::CrxDataCallback& crx_data_callback,
365 const UpdateClient::CompletionCallback& completion_callback) {
sorin7b8650522016-11-02 18:23:41366 completion_callback.Run(update_client::Error::NONE);
sorin7c717622015-05-26 19:59:09367 static int cnt = 0;
368 ++cnt;
369 if (cnt >= max_cnt_)
370 quit_closure_.Run();
371 }
372
373 private:
374 const int max_cnt_;
375 base::Closure quit_closure_;
376 };
377
sorin85953dc2016-03-10 00:32:48378 base::HistogramTester ht;
379
sorin7c717622015-05-26 19:59:09380 auto config = configurator();
381 config->SetInitialDelay(3600);
382
383 scoped_refptr<MockInstaller> installer(new MockInstaller());
384
385 using update_client::jebg_hash;
386 CrxComponent crx_component;
387 crx_component.pk_hash.assign(jebg_hash, jebg_hash + arraysize(jebg_hash));
pwnall15745b312016-08-19 21:45:29388 crx_component.version = base::Version("0.9");
sorin7c717622015-05-26 19:59:09389 crx_component.installer = installer;
390
391 LoopHandler loop_handler(1, quit_closure());
392 EXPECT_CALL(update_client(),
393 Install("jebgalgnebhfojomionfpkfelancnnkf", _, _))
394 .WillOnce(Invoke(&loop_handler, &LoopHandler::OnInstall));
sorinecaad3e2015-11-13 19:15:52395 EXPECT_CALL(update_client(), Stop()).Times(1);
sorin7c717622015-05-26 19:59:09396
397 EXPECT_TRUE(component_updater().RegisterComponent(crx_component));
398 component_updater().MaybeThrottle(
399 "jebgalgnebhfojomionfpkfelancnnkf",
400 base::Bind(&ComponentUpdaterTest::ReadyCallback));
401
402 RunThreads();
sorin85953dc2016-03-10 00:32:48403
404 ht.ExpectUniqueSample("ComponentUpdater.Calls", 0, 1);
405 ht.ExpectUniqueSample("ComponentUpdater.UpdateCompleteResult", 0, 1);
406 ht.ExpectTotalCount("ComponentUpdater.UpdateCompleteTime", 1);
sorin7c717622015-05-26 19:59:09407}
408
409} // namespace component_updater