blob: 7d703c4370b948c1e9bf16ae59075cca549fc51a [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>
Sorin Jianu990ee142017-06-02 22:34:088#include <memory>
sorin7c717622015-05-26 19:59:099#include <string>
10#include <vector>
11
12#include "base/bind.h"
13#include "base/bind_helpers.h"
14#include "base/files/file_path.h"
15#include "base/files/file_util.h"
16#include "base/macros.h"
dchenga0ee5fb82016-04-26 02:46:5517#include "base/memory/ptr_util.h"
sorin7c717622015-05-26 19:59:0918#include "base/memory/ref_counted.h"
sorin7c717622015-05-26 19:59:0919#include "base/message_loop/message_loop.h"
20#include "base/run_loop.h"
fdoraydc4d9942017-05-12 20:13:5021#include "base/task_scheduler/post_task.h"
sorin85953dc2016-03-10 00:32:4822#include "base/test/histogram_tester.h"
fdoraydc4d9942017-05-12 20:13:5023#include "base/test/scoped_task_environment.h"
gab7966d312016-05-11 20:35:0124#include "base/threading/thread_task_runner_handle.h"
sorin7c717622015-05-26 19:59:0925#include "base/values.h"
sorin7c717622015-05-26 19:59:0926#include "components/component_updater/component_updater_service_internal.h"
27#include "components/update_client/test_configurator.h"
28#include "components/update_client/test_installer.h"
29#include "components/update_client/update_client.h"
sorin7b8650522016-11-02 18:23:4130#include "components/update_client/update_client_errors.h"
sorin7c717622015-05-26 19:59:0931#include "testing/gmock/include/gmock/gmock.h"
32#include "testing/gtest/include/gtest/gtest.h"
33
34using Configurator = update_client::Configurator;
sorin2892f7212016-11-07 18:59:4335using Result = update_client::CrxInstaller::Result;
sorin7c717622015-05-26 19:59:0936using TestConfigurator = update_client::TestConfigurator;
37using UpdateClient = update_client::UpdateClient;
38
39using ::testing::_;
40using ::testing::AnyNumber;
41using ::testing::Invoke;
42using ::testing::Mock;
43using ::testing::Return;
44
45namespace component_updater {
46
47class MockInstaller : public CrxInstaller {
48 public:
49 MockInstaller();
50
Sorin Jianu990ee142017-06-02 22:34:0851 // gMock does not support mocking functions with parameters which have
52 // move semantics. This function is a shim to work around it.
53 Result Install(std::unique_ptr<base::DictionaryValue> manifest,
54 const base::FilePath& unpack_path) {
55 return Install_(manifest, unpack_path);
56 }
57
sorin7c717622015-05-26 19:59:0958 MOCK_METHOD1(OnUpdateError, void(int error));
Sorin Jianu990ee142017-06-02 22:34:0859 MOCK_METHOD2(Install_,
60 Result(const std::unique_ptr<base::DictionaryValue>& manifest,
sorin2892f7212016-11-07 18:59:4361 const base::FilePath& unpack_path));
sorin7c717622015-05-26 19:59:0962 MOCK_METHOD2(GetInstalledFile,
63 bool(const std::string& file, base::FilePath* installed_file));
64 MOCK_METHOD0(Uninstall, bool());
65
66 private:
67 ~MockInstaller() override;
68};
69
70class MockUpdateClient : public UpdateClient {
71 public:
72 MockUpdateClient();
73 MOCK_METHOD1(AddObserver, void(Observer* observer));
74 MOCK_METHOD1(RemoveObserver, void(Observer* observer));
75 MOCK_METHOD3(Install,
76 void(const std::string& id,
77 const CrxDataCallback& crx_data_callback,
sorin842703b2016-11-02 23:59:2378 const Callback& callback));
sorin7c717622015-05-26 19:59:0979 MOCK_METHOD3(Update,
80 void(const std::vector<std::string>& ids,
81 const CrxDataCallback& crx_data_callback,
sorin842703b2016-11-02 23:59:2382 const Callback& callback));
sorin7c717622015-05-26 19:59:0983 MOCK_CONST_METHOD2(GetCrxUpdateState,
84 bool(const std::string& id, CrxUpdateItem* update_item));
85 MOCK_CONST_METHOD1(IsUpdating, bool(const std::string& id));
sorinecaad3e2015-11-13 19:15:5286 MOCK_METHOD0(Stop, void());
sorin8037ac8c2017-04-19 16:28:0087 MOCK_METHOD4(SendUninstallPing,
88 void(const std::string& id,
89 const base::Version& version,
90 int reason,
91 const Callback& callback));
sorin7c717622015-05-26 19:59:0992
93 private:
94 ~MockUpdateClient() override;
95};
96
97class MockServiceObserver : public ServiceObserver {
98 public:
99 MockServiceObserver();
100 ~MockServiceObserver() override;
101
102 MOCK_METHOD2(OnEvent, void(Events event, const std::string&));
103};
104
105class ComponentUpdaterTest : public testing::Test {
106 public:
107 ComponentUpdaterTest();
108 ~ComponentUpdaterTest() override;
109
110 void SetUp() override;
111
112 void TearDown() override;
113
114 // Makes the full path to a component updater test file.
115 const base::FilePath test_file(const char* file);
116
117 MockUpdateClient& update_client() { return *update_client_; }
118 ComponentUpdateService& component_updater() { return *component_updater_; }
119 scoped_refptr<TestConfigurator> configurator() const { return config_; }
120 base::Closure quit_closure() const { return quit_closure_; }
121 static void ReadyCallback() {}
122
123 protected:
124 void RunThreads();
125
126 private:
fdoraydc4d9942017-05-12 20:13:50127 base::test::ScopedTaskEnvironment scoped_task_environment_;
sorin7c717622015-05-26 19:59:09128 base::RunLoop runloop_;
129 base::Closure quit_closure_;
130
sorin7c717622015-05-26 19:59:09131 scoped_refptr<TestConfigurator> config_;
132 scoped_refptr<MockUpdateClient> update_client_;
dchenga0ee5fb82016-04-26 02:46:55133 std::unique_ptr<ComponentUpdateService> component_updater_;
sorin7c717622015-05-26 19:59:09134
135 DISALLOW_COPY_AND_ASSIGN(ComponentUpdaterTest);
136};
137
138class OnDemandTester {
139 public:
sorin4c520182016-08-19 17:27:44140 void OnDemand(ComponentUpdateService* cus, const std::string& id);
sorin7b8650522016-11-02 18:23:41141 update_client::Error error() const { return error_; }
sorin4c520182016-08-19 17:27:44142
143 private:
sorin7b8650522016-11-02 18:23:41144 void OnDemandComplete(update_client::Error error);
sorin4c520182016-08-19 17:27:44145
sorin7b8650522016-11-02 18:23:41146 update_client::Error error_ = update_client::Error::NONE;
sorin7c717622015-05-26 19:59:09147};
148
149MockInstaller::MockInstaller() {
150}
151
152MockInstaller::~MockInstaller() {
153}
154
155MockUpdateClient::MockUpdateClient() {
156}
157
158MockUpdateClient::~MockUpdateClient() {
159}
160
161MockServiceObserver::MockServiceObserver() {
162}
163
164MockServiceObserver::~MockServiceObserver() {
165}
166
sorin4c520182016-08-19 17:27:44167void OnDemandTester::OnDemand(ComponentUpdateService* cus,
sorin7c717622015-05-26 19:59:09168 const std::string& id) {
sorin4c520182016-08-19 17:27:44169 cus->GetOnDemandUpdater().OnDemandUpdate(
170 id,
171 base::Bind(&OnDemandTester::OnDemandComplete, base::Unretained(this)));
172}
173
sorin7b8650522016-11-02 18:23:41174void OnDemandTester::OnDemandComplete(update_client::Error error) {
sorin4c520182016-08-19 17:27:44175 error_ = error;
sorin7c717622015-05-26 19:59:09176}
177
dchenga0ee5fb82016-04-26 02:46:55178std::unique_ptr<ComponentUpdateService> TestComponentUpdateServiceFactory(
sorin7c717622015-05-26 19:59:09179 const scoped_refptr<Configurator>& config) {
180 DCHECK(config);
ricea85ec57952016-08-31 09:34:10181 return base::MakeUnique<CrxUpdateService>(config, new MockUpdateClient());
sorin7c717622015-05-26 19:59:09182}
183
184ComponentUpdaterTest::ComponentUpdaterTest()
fdoraydc4d9942017-05-12 20:13:50185 : scoped_task_environment_(
186 base::test::ScopedTaskEnvironment::MainThreadType::UI) {
sorin7c717622015-05-26 19:59:09187 quit_closure_ = runloop_.QuitClosure();
188
189 config_ = new TestConfigurator(
fdoraydc4d9942017-05-12 20:13:50190 base::CreateSequencedTaskRunnerWithTraits({base::MayBlock()}),
191 base::ThreadTaskRunnerHandle::Get());
sorin7c717622015-05-26 19:59:09192
193 update_client_ = new MockUpdateClient();
194 EXPECT_CALL(update_client(), AddObserver(_)).Times(1);
195 component_updater_.reset(new CrxUpdateService(config_, update_client_));
196}
197
198ComponentUpdaterTest::~ComponentUpdaterTest() {
199 EXPECT_CALL(update_client(), RemoveObserver(_)).Times(1);
sorin7c717622015-05-26 19:59:09200 component_updater_.reset();
201}
202
203void ComponentUpdaterTest::SetUp() {
204}
205
206void ComponentUpdaterTest::TearDown() {
207}
208
209void ComponentUpdaterTest::RunThreads() {
210 runloop_.Run();
211}
212
213TEST_F(ComponentUpdaterTest, AddObserver) {
214 MockServiceObserver observer;
215 EXPECT_CALL(update_client(), AddObserver(&observer)).Times(1);
sorinecaad3e2015-11-13 19:15:52216 EXPECT_CALL(update_client(), Stop()).Times(1);
sorin7c717622015-05-26 19:59:09217 component_updater().AddObserver(&observer);
218}
219
220TEST_F(ComponentUpdaterTest, RemoveObserver) {
221 MockServiceObserver observer;
222 EXPECT_CALL(update_client(), RemoveObserver(&observer)).Times(1);
sorinecaad3e2015-11-13 19:15:52223 EXPECT_CALL(update_client(), Stop()).Times(1);
sorin7c717622015-05-26 19:59:09224 component_updater().RemoveObserver(&observer);
225}
226
227// Tests that UpdateClient::Update is called by the timer loop when
228// components are registered, and the component update starts.
229// Also tests that Uninstall is called when a component is unregistered.
230TEST_F(ComponentUpdaterTest, RegisterComponent) {
231 class LoopHandler {
232 public:
233 LoopHandler(int max_cnt, const base::Closure& quit_closure)
234 : max_cnt_(max_cnt), quit_closure_(quit_closure) {}
235
236 void OnUpdate(const std::vector<std::string>& ids,
237 const UpdateClient::CrxDataCallback& crx_data_callback,
sorin842703b2016-11-02 23:59:23238 const Callback& callback) {
239 callback.Run(update_client::Error::NONE);
sorin7c717622015-05-26 19:59:09240 static int cnt = 0;
241 ++cnt;
242 if (cnt >= max_cnt_)
243 quit_closure_.Run();
244 }
245
246 private:
247 const int max_cnt_;
248 base::Closure quit_closure_;
249 };
250
sorin85953dc2016-03-10 00:32:48251 base::HistogramTester ht;
252
sorin7c717622015-05-26 19:59:09253 scoped_refptr<MockInstaller> installer(new MockInstaller());
254 EXPECT_CALL(*installer, Uninstall()).WillOnce(Return(true));
255
256 using update_client::jebg_hash;
257 using update_client::abag_hash;
258
259 const std::string id1 = "abagagagagagagagagagagagagagagag";
260 const std::string id2 = "jebgalgnebhfojomionfpkfelancnnkf";
261 std::vector<std::string> ids;
262 ids.push_back(id1);
263 ids.push_back(id2);
264
265 CrxComponent crx_component1;
266 crx_component1.pk_hash.assign(abag_hash, abag_hash + arraysize(abag_hash));
pwnall15745b312016-08-19 21:45:29267 crx_component1.version = base::Version("1.0");
sorin7c717622015-05-26 19:59:09268 crx_component1.installer = installer;
269
270 CrxComponent crx_component2;
271 crx_component2.pk_hash.assign(jebg_hash, jebg_hash + arraysize(jebg_hash));
pwnall15745b312016-08-19 21:45:29272 crx_component2.version = base::Version("0.9");
sorin7c717622015-05-26 19:59:09273 crx_component2.installer = installer;
274
275 // Quit after two update checks have fired.
276 LoopHandler loop_handler(2, quit_closure());
277 EXPECT_CALL(update_client(), Update(ids, _, _))
278 .WillRepeatedly(Invoke(&loop_handler, &LoopHandler::OnUpdate));
279
280 EXPECT_CALL(update_client(), IsUpdating(id1)).Times(1);
sorinecaad3e2015-11-13 19:15:52281 EXPECT_CALL(update_client(), Stop()).Times(1);
sorin7c717622015-05-26 19:59:09282
283 EXPECT_TRUE(component_updater().RegisterComponent(crx_component1));
284 EXPECT_TRUE(component_updater().RegisterComponent(crx_component2));
285
286 RunThreads();
sorin7c717622015-05-26 19:59:09287 EXPECT_TRUE(component_updater().UnregisterComponent(id1));
sorin85953dc2016-03-10 00:32:48288
289 ht.ExpectUniqueSample("ComponentUpdater.Calls", 1, 2);
290 ht.ExpectUniqueSample("ComponentUpdater.UpdateCompleteResult", 0, 2);
291 ht.ExpectTotalCount("ComponentUpdater.UpdateCompleteTime", 2);
sorin7c717622015-05-26 19:59:09292}
293
294// Tests that on-demand updates invoke UpdateClient::Install.
295TEST_F(ComponentUpdaterTest, OnDemandUpdate) {
296 class LoopHandler {
297 public:
sorin7b8650522016-11-02 18:23:41298 explicit LoopHandler(int max_cnt) : max_cnt_(max_cnt) {}
sorin7c717622015-05-26 19:59:09299
sorin842703b2016-11-02 23:59:23300 void OnInstall(const std::string& ids,
301 const UpdateClient::CrxDataCallback& crx_data_callback,
302 const Callback& callback) {
303 callback.Run(update_client::Error::NONE);
sorin7c717622015-05-26 19:59:09304 static int cnt = 0;
305 ++cnt;
sorin4c520182016-08-19 17:27:44306 if (cnt >= max_cnt_) {
307 base::ThreadTaskRunnerHandle::Get()->PostTask(
308 FROM_HERE, base::Bind(&LoopHandler::Quit, base::Unretained(this)));
309 }
sorin7c717622015-05-26 19:59:09310 }
311
312 private:
sorin4c520182016-08-19 17:27:44313 void Quit() { base::MessageLoop::current()->QuitWhenIdle(); }
314
sorin7c717622015-05-26 19:59:09315 const int max_cnt_;
sorin7c717622015-05-26 19:59:09316 };
317
sorin85953dc2016-03-10 00:32:48318 base::HistogramTester ht;
319
sorin7c717622015-05-26 19:59:09320 auto config = configurator();
321 config->SetInitialDelay(3600);
322
323 auto& cus = component_updater();
324
sorin4c520182016-08-19 17:27:44325 // Tests calling OnDemand for an unregistered component. This call results in
326 // an error, which is recorded by the OnDemandTester instance. Since the
327 // component was not registered, the call is ignored for UMA metrics.
328 OnDemandTester ondemand_tester_component_not_registered;
329 ondemand_tester_component_not_registered.OnDemand(
330 &cus, "ihfokbkgjpifnbbojhneepfflplebdkc");
sorin7c717622015-05-26 19:59:09331
sorin4c520182016-08-19 17:27:44332 const std::string id = "jebgalgnebhfojomionfpkfelancnnkf";
sorin7c717622015-05-26 19:59:09333
334 using update_client::jebg_hash;
335 CrxComponent crx_component;
336 crx_component.pk_hash.assign(jebg_hash, jebg_hash + arraysize(jebg_hash));
pwnall15745b312016-08-19 21:45:29337 crx_component.version = base::Version("0.9");
sorin4c520182016-08-19 17:27:44338 crx_component.installer = new MockInstaller();
sorin7c717622015-05-26 19:59:09339
sorin4c520182016-08-19 17:27:44340 LoopHandler loop_handler(1);
sorin7c717622015-05-26 19:59:09341 EXPECT_CALL(update_client(),
342 Install("jebgalgnebhfojomionfpkfelancnnkf", _, _))
343 .WillOnce(Invoke(&loop_handler, &LoopHandler::OnInstall));
sorinecaad3e2015-11-13 19:15:52344 EXPECT_CALL(update_client(), Stop()).Times(1);
sorin7c717622015-05-26 19:59:09345
346 EXPECT_TRUE(cus.RegisterComponent(crx_component));
sorin4c520182016-08-19 17:27:44347 OnDemandTester ondemand_tester;
348 ondemand_tester.OnDemand(&cus, id);
sorin7c717622015-05-26 19:59:09349
sorin4c520182016-08-19 17:27:44350 base::RunLoop().Run();
351
sorin7b8650522016-11-02 18:23:41352 EXPECT_EQ(update_client::Error::INVALID_ARGUMENT,
353 ondemand_tester_component_not_registered.error());
354 EXPECT_EQ(update_client::Error::NONE, ondemand_tester.error());
sorin85953dc2016-03-10 00:32:48355
356 ht.ExpectUniqueSample("ComponentUpdater.Calls", 0, 1);
357 ht.ExpectUniqueSample("ComponentUpdater.UpdateCompleteResult", 0, 1);
358 ht.ExpectTotalCount("ComponentUpdater.UpdateCompleteTime", 1);
sorin7c717622015-05-26 19:59:09359}
360
361// Tests that throttling an update invokes UpdateClient::Install.
362TEST_F(ComponentUpdaterTest, MaybeThrottle) {
363 class LoopHandler {
364 public:
365 LoopHandler(int max_cnt, const base::Closure& quit_closure)
366 : max_cnt_(max_cnt), quit_closure_(quit_closure) {}
367
sorin842703b2016-11-02 23:59:23368 void OnInstall(const std::string& ids,
369 const UpdateClient::CrxDataCallback& crx_data_callback,
370 const Callback& callback) {
371 callback.Run(update_client::Error::NONE);
sorin7c717622015-05-26 19:59:09372 static int cnt = 0;
373 ++cnt;
374 if (cnt >= max_cnt_)
375 quit_closure_.Run();
376 }
377
378 private:
379 const int max_cnt_;
380 base::Closure quit_closure_;
381 };
382
sorin85953dc2016-03-10 00:32:48383 base::HistogramTester ht;
384
sorin7c717622015-05-26 19:59:09385 auto config = configurator();
386 config->SetInitialDelay(3600);
387
388 scoped_refptr<MockInstaller> installer(new MockInstaller());
389
390 using update_client::jebg_hash;
391 CrxComponent crx_component;
392 crx_component.pk_hash.assign(jebg_hash, jebg_hash + arraysize(jebg_hash));
pwnall15745b312016-08-19 21:45:29393 crx_component.version = base::Version("0.9");
sorin7c717622015-05-26 19:59:09394 crx_component.installer = installer;
395
396 LoopHandler loop_handler(1, quit_closure());
397 EXPECT_CALL(update_client(),
398 Install("jebgalgnebhfojomionfpkfelancnnkf", _, _))
399 .WillOnce(Invoke(&loop_handler, &LoopHandler::OnInstall));
sorinecaad3e2015-11-13 19:15:52400 EXPECT_CALL(update_client(), Stop()).Times(1);
sorin7c717622015-05-26 19:59:09401
402 EXPECT_TRUE(component_updater().RegisterComponent(crx_component));
403 component_updater().MaybeThrottle(
404 "jebgalgnebhfojomionfpkfelancnnkf",
405 base::Bind(&ComponentUpdaterTest::ReadyCallback));
406
407 RunThreads();
sorin85953dc2016-03-10 00:32:48408
409 ht.ExpectUniqueSample("ComponentUpdater.Calls", 0, 1);
410 ht.ExpectUniqueSample("ComponentUpdater.UpdateCompleteResult", 0, 1);
411 ht.ExpectTotalCount("ComponentUpdater.UpdateCompleteTime", 1);
sorin7c717622015-05-26 19:59:09412}
413
414} // namespace component_updater