blob: cdeed0d8a8d73367a697b75c8da6af6618b96ccf [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"
sorin7c717622015-05-26 19:59:0928#include "testing/gmock/include/gmock/gmock.h"
29#include "testing/gtest/include/gtest/gtest.h"
30
31using Configurator = update_client::Configurator;
32using TestConfigurator = update_client::TestConfigurator;
33using UpdateClient = update_client::UpdateClient;
34
35using ::testing::_;
36using ::testing::AnyNumber;
37using ::testing::Invoke;
38using ::testing::Mock;
39using ::testing::Return;
40
41namespace component_updater {
42
43class MockInstaller : public CrxInstaller {
44 public:
45 MockInstaller();
46
47 MOCK_METHOD1(OnUpdateError, void(int error));
48 MOCK_METHOD2(Install,
49 bool(const base::DictionaryValue& manifest,
50 const base::FilePath& unpack_path));
51 MOCK_METHOD2(GetInstalledFile,
52 bool(const std::string& file, base::FilePath* installed_file));
53 MOCK_METHOD0(Uninstall, bool());
54
55 private:
56 ~MockInstaller() override;
57};
58
59class MockUpdateClient : public UpdateClient {
60 public:
61 MockUpdateClient();
62 MOCK_METHOD1(AddObserver, void(Observer* observer));
63 MOCK_METHOD1(RemoveObserver, void(Observer* observer));
64 MOCK_METHOD3(Install,
65 void(const std::string& id,
66 const CrxDataCallback& crx_data_callback,
67 const CompletionCallback& completion_callback));
68 MOCK_METHOD3(Update,
69 void(const std::vector<std::string>& ids,
70 const CrxDataCallback& crx_data_callback,
71 const CompletionCallback& completion_callback));
72 MOCK_CONST_METHOD2(GetCrxUpdateState,
73 bool(const std::string& id, CrxUpdateItem* update_item));
74 MOCK_CONST_METHOD1(IsUpdating, bool(const std::string& id));
sorinecaad3e2015-11-13 19:15:5275 MOCK_METHOD0(Stop, void());
pwnall15745b312016-08-19 21:45:2976 MOCK_METHOD3(
77 SendUninstallPing,
78 void(const std::string& id, const base::Version& version, int reason));
sorin7c717622015-05-26 19:59:0979
80 private:
81 ~MockUpdateClient() override;
82};
83
84class MockServiceObserver : public ServiceObserver {
85 public:
86 MockServiceObserver();
87 ~MockServiceObserver() override;
88
89 MOCK_METHOD2(OnEvent, void(Events event, const std::string&));
90};
91
92class ComponentUpdaterTest : public testing::Test {
93 public:
94 ComponentUpdaterTest();
95 ~ComponentUpdaterTest() override;
96
97 void SetUp() override;
98
99 void TearDown() override;
100
101 // Makes the full path to a component updater test file.
102 const base::FilePath test_file(const char* file);
103
104 MockUpdateClient& update_client() { return *update_client_; }
105 ComponentUpdateService& component_updater() { return *component_updater_; }
106 scoped_refptr<TestConfigurator> configurator() const { return config_; }
107 base::Closure quit_closure() const { return quit_closure_; }
108 static void ReadyCallback() {}
109
110 protected:
111 void RunThreads();
112
113 private:
114 static const int kNumWorkerThreads_ = 2;
115
116 base::MessageLoopForUI message_loop_;
117 base::RunLoop runloop_;
118 base::Closure quit_closure_;
119
dchenga0ee5fb82016-04-26 02:46:55120 std::unique_ptr<base::SequencedWorkerPoolOwner> worker_pool_;
sorin7c717622015-05-26 19:59:09121
122 scoped_refptr<TestConfigurator> config_;
123 scoped_refptr<MockUpdateClient> update_client_;
dchenga0ee5fb82016-04-26 02:46:55124 std::unique_ptr<ComponentUpdateService> component_updater_;
sorin7c717622015-05-26 19:59:09125
126 DISALLOW_COPY_AND_ASSIGN(ComponentUpdaterTest);
127};
128
129class OnDemandTester {
130 public:
sorin4c520182016-08-19 17:27:44131 void OnDemand(ComponentUpdateService* cus, const std::string& id);
132 int error() const { return error_; }
133
134 private:
135 void OnDemandComplete(int error);
136
137 int error_ = 0;
sorin7c717622015-05-26 19:59:09138};
139
140MockInstaller::MockInstaller() {
141}
142
143MockInstaller::~MockInstaller() {
144}
145
146MockUpdateClient::MockUpdateClient() {
147}
148
149MockUpdateClient::~MockUpdateClient() {
150}
151
152MockServiceObserver::MockServiceObserver() {
153}
154
155MockServiceObserver::~MockServiceObserver() {
156}
157
sorin4c520182016-08-19 17:27:44158void OnDemandTester::OnDemand(ComponentUpdateService* cus,
sorin7c717622015-05-26 19:59:09159 const std::string& id) {
sorin4c520182016-08-19 17:27:44160 cus->GetOnDemandUpdater().OnDemandUpdate(
161 id,
162 base::Bind(&OnDemandTester::OnDemandComplete, base::Unretained(this)));
163}
164
165void OnDemandTester::OnDemandComplete(int error) {
166 error_ = error;
sorin7c717622015-05-26 19:59:09167}
168
dchenga0ee5fb82016-04-26 02:46:55169std::unique_ptr<ComponentUpdateService> TestComponentUpdateServiceFactory(
sorin7c717622015-05-26 19:59:09170 const scoped_refptr<Configurator>& config) {
171 DCHECK(config);
dchenga0ee5fb82016-04-26 02:46:55172 return base::WrapUnique(new CrxUpdateService(config, new MockUpdateClient()));
sorin7c717622015-05-26 19:59:09173}
174
175ComponentUpdaterTest::ComponentUpdaterTest()
sorin85e68032015-05-27 19:50:51176 : worker_pool_(
177 new base::SequencedWorkerPoolOwner(kNumWorkerThreads_, "test")) {
sorin7c717622015-05-26 19:59:09178 quit_closure_ = runloop_.QuitClosure();
179
sorin85e68032015-05-27 19:50:51180 auto pool = worker_pool_->pool();
sorin7c717622015-05-26 19:59:09181 config_ = new TestConfigurator(
sorin85e68032015-05-27 19:50:51182 pool->GetSequencedTaskRunner(pool->GetSequenceToken()),
sorin7c717622015-05-26 19:59:09183 message_loop_.task_runner());
184
185 update_client_ = new MockUpdateClient();
186 EXPECT_CALL(update_client(), AddObserver(_)).Times(1);
187 component_updater_.reset(new CrxUpdateService(config_, update_client_));
188}
189
190ComponentUpdaterTest::~ComponentUpdaterTest() {
191 EXPECT_CALL(update_client(), RemoveObserver(_)).Times(1);
sorin7c717622015-05-26 19:59:09192 component_updater_.reset();
193}
194
195void ComponentUpdaterTest::SetUp() {
196}
197
198void ComponentUpdaterTest::TearDown() {
199}
200
201void ComponentUpdaterTest::RunThreads() {
202 runloop_.Run();
203}
204
205TEST_F(ComponentUpdaterTest, AddObserver) {
206 MockServiceObserver observer;
207 EXPECT_CALL(update_client(), AddObserver(&observer)).Times(1);
sorinecaad3e2015-11-13 19:15:52208 EXPECT_CALL(update_client(), Stop()).Times(1);
sorin7c717622015-05-26 19:59:09209 component_updater().AddObserver(&observer);
210}
211
212TEST_F(ComponentUpdaterTest, RemoveObserver) {
213 MockServiceObserver observer;
214 EXPECT_CALL(update_client(), RemoveObserver(&observer)).Times(1);
sorinecaad3e2015-11-13 19:15:52215 EXPECT_CALL(update_client(), Stop()).Times(1);
sorin7c717622015-05-26 19:59:09216 component_updater().RemoveObserver(&observer);
217}
218
219// Tests that UpdateClient::Update is called by the timer loop when
220// components are registered, and the component update starts.
221// Also tests that Uninstall is called when a component is unregistered.
222TEST_F(ComponentUpdaterTest, RegisterComponent) {
223 class LoopHandler {
224 public:
225 LoopHandler(int max_cnt, const base::Closure& quit_closure)
226 : max_cnt_(max_cnt), quit_closure_(quit_closure) {}
227
228 void OnUpdate(const std::vector<std::string>& ids,
229 const UpdateClient::CrxDataCallback& crx_data_callback,
230 const UpdateClient::CompletionCallback& completion_callback) {
sorin85953dc2016-03-10 00:32:48231 completion_callback.Run(0);
sorin7c717622015-05-26 19:59:09232 static int cnt = 0;
233 ++cnt;
234 if (cnt >= max_cnt_)
235 quit_closure_.Run();
236 }
237
238 private:
239 const int max_cnt_;
240 base::Closure quit_closure_;
241 };
242
sorin85953dc2016-03-10 00:32:48243 base::HistogramTester ht;
244
sorin7c717622015-05-26 19:59:09245 scoped_refptr<MockInstaller> installer(new MockInstaller());
246 EXPECT_CALL(*installer, Uninstall()).WillOnce(Return(true));
247
248 using update_client::jebg_hash;
249 using update_client::abag_hash;
250
251 const std::string id1 = "abagagagagagagagagagagagagagagag";
252 const std::string id2 = "jebgalgnebhfojomionfpkfelancnnkf";
253 std::vector<std::string> ids;
254 ids.push_back(id1);
255 ids.push_back(id2);
256
257 CrxComponent crx_component1;
258 crx_component1.pk_hash.assign(abag_hash, abag_hash + arraysize(abag_hash));
pwnall15745b312016-08-19 21:45:29259 crx_component1.version = base::Version("1.0");
sorin7c717622015-05-26 19:59:09260 crx_component1.installer = installer;
261
262 CrxComponent crx_component2;
263 crx_component2.pk_hash.assign(jebg_hash, jebg_hash + arraysize(jebg_hash));
pwnall15745b312016-08-19 21:45:29264 crx_component2.version = base::Version("0.9");
sorin7c717622015-05-26 19:59:09265 crx_component2.installer = installer;
266
267 // Quit after two update checks have fired.
268 LoopHandler loop_handler(2, quit_closure());
269 EXPECT_CALL(update_client(), Update(ids, _, _))
270 .WillRepeatedly(Invoke(&loop_handler, &LoopHandler::OnUpdate));
271
272 EXPECT_CALL(update_client(), IsUpdating(id1)).Times(1);
sorinecaad3e2015-11-13 19:15:52273 EXPECT_CALL(update_client(), Stop()).Times(1);
sorin7c717622015-05-26 19:59:09274
275 EXPECT_TRUE(component_updater().RegisterComponent(crx_component1));
276 EXPECT_TRUE(component_updater().RegisterComponent(crx_component2));
277
278 RunThreads();
sorin7c717622015-05-26 19:59:09279 EXPECT_TRUE(component_updater().UnregisterComponent(id1));
sorin85953dc2016-03-10 00:32:48280
281 ht.ExpectUniqueSample("ComponentUpdater.Calls", 1, 2);
282 ht.ExpectUniqueSample("ComponentUpdater.UpdateCompleteResult", 0, 2);
283 ht.ExpectTotalCount("ComponentUpdater.UpdateCompleteTime", 2);
sorin7c717622015-05-26 19:59:09284}
285
286// Tests that on-demand updates invoke UpdateClient::Install.
287TEST_F(ComponentUpdaterTest, OnDemandUpdate) {
288 class LoopHandler {
289 public:
sorin4c520182016-08-19 17:27:44290 LoopHandler(int max_cnt)
291 : 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) {
sorin85953dc2016-03-10 00:32:48297 completion_callback.Run(0);
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
346 EXPECT_EQ(
347 static_cast<int>(update_client::Error::ERROR_UPDATE_INVALID_ARGUMENT),
348 ondemand_tester_component_not_registered.error());
349 EXPECT_EQ(0, ondemand_tester.error());
sorin85953dc2016-03-10 00:32:48350
351 ht.ExpectUniqueSample("ComponentUpdater.Calls", 0, 1);
352 ht.ExpectUniqueSample("ComponentUpdater.UpdateCompleteResult", 0, 1);
353 ht.ExpectTotalCount("ComponentUpdater.UpdateCompleteTime", 1);
sorin7c717622015-05-26 19:59:09354}
355
356// Tests that throttling an update invokes UpdateClient::Install.
357TEST_F(ComponentUpdaterTest, MaybeThrottle) {
358 class LoopHandler {
359 public:
360 LoopHandler(int max_cnt, const base::Closure& quit_closure)
361 : max_cnt_(max_cnt), quit_closure_(quit_closure) {}
362
363 void OnInstall(
364 const std::string& ids,
365 const UpdateClient::CrxDataCallback& crx_data_callback,
366 const UpdateClient::CompletionCallback& completion_callback) {
sorin85953dc2016-03-10 00:32:48367 completion_callback.Run(0);
sorin7c717622015-05-26 19:59:09368 static int cnt = 0;
369 ++cnt;
370 if (cnt >= max_cnt_)
371 quit_closure_.Run();
372 }
373
374 private:
375 const int max_cnt_;
376 base::Closure quit_closure_;
377 };
378
sorin85953dc2016-03-10 00:32:48379 base::HistogramTester ht;
380
sorin7c717622015-05-26 19:59:09381 auto config = configurator();
382 config->SetInitialDelay(3600);
383
384 scoped_refptr<MockInstaller> installer(new MockInstaller());
385
386 using update_client::jebg_hash;
387 CrxComponent crx_component;
388 crx_component.pk_hash.assign(jebg_hash, jebg_hash + arraysize(jebg_hash));
pwnall15745b312016-08-19 21:45:29389 crx_component.version = base::Version("0.9");
sorin7c717622015-05-26 19:59:09390 crx_component.installer = installer;
391
392 LoopHandler loop_handler(1, quit_closure());
393 EXPECT_CALL(update_client(),
394 Install("jebgalgnebhfojomionfpkfelancnnkf", _, _))
395 .WillOnce(Invoke(&loop_handler, &LoopHandler::OnInstall));
sorinecaad3e2015-11-13 19:15:52396 EXPECT_CALL(update_client(), Stop()).Times(1);
sorin7c717622015-05-26 19:59:09397
398 EXPECT_TRUE(component_updater().RegisterComponent(crx_component));
399 component_updater().MaybeThrottle(
400 "jebgalgnebhfojomionfpkfelancnnkf",
401 base::Bind(&ComponentUpdaterTest::ReadyCallback));
402
403 RunThreads();
sorin85953dc2016-03-10 00:32:48404
405 ht.ExpectUniqueSample("ComponentUpdater.Calls", 0, 1);
406 ht.ExpectUniqueSample("ComponentUpdater.UpdateCompleteResult", 0, 1);
407 ht.ExpectTotalCount("ComponentUpdater.UpdateCompleteTime", 1);
sorin7c717622015-05-26 19:59:09408}
409
410} // namespace component_updater