component_updater_service_unittest.cc revision ca12bfac764ba476d6cd062bf1dde12cc64c3f40
1// Copyright (c) 2012 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 "chrome/browser/component_updater/test/component_updater_service_unittest.h"
6#include "base/file_util.h"
7#include "base/files/file_path.h"
8#include "base/memory/scoped_vector.h"
9#include "base/path_service.h"
10#include "base/strings/string_number_conversions.h"
11#include "base/strings/stringprintf.h"
12#include "base/values.h"
13#include "chrome/browser/chrome_notification_types.h"
14#include "chrome/browser/component_updater/component_updater_service.h"
15#include "chrome/browser/component_updater/test/test_installer.h"
16#include "chrome/common/chrome_paths.h"
17#include "content/public/browser/notification_service.h"
18#include "content/test/net/url_request_prepackaged_interceptor.h"
19#include "libxml/globals.h"
20#include "net/base/upload_bytes_element_reader.h"
21#include "net/url_request/url_fetcher.h"
22#include "url/gurl.h"
23
24using content::BrowserThread;
25using content::TestNotificationTracker;
26
27TestConfigurator::TestConfigurator()
28    : times_(1),
29      recheck_time_(0),
30      ondemand_time_(0),
31      cus_(NULL),
32      context_(new net::TestURLRequestContextGetter(
33          BrowserThread::GetMessageLoopProxyForThread(BrowserThread::IO))) {
34}
35
36TestConfigurator::~TestConfigurator() {
37}
38
39int TestConfigurator::InitialDelay() { return 0; }
40
41int TestConfigurator::NextCheckDelay() {
42  // This is called when a new full cycle of checking for updates is going
43  // to happen. In test we normally only test one cycle so it is a good
44  // time to break from the test messageloop Run() method so the test can
45  // finish.
46  if (--times_ <= 0) {
47    base::MessageLoop::current()->Quit();
48    return 0;
49  }
50
51  // Look for checks to issue in the middle of the loop.
52  for (std::list<CheckAtLoopCount>::iterator
53           i = components_to_check_.begin();
54       i != components_to_check_.end(); ) {
55    if (i->second == times_) {
56      cus_->CheckForUpdateSoon(*i->first);
57      i = components_to_check_.erase(i);
58    } else {
59      ++i;
60    }
61  }
62  return 1;
63}
64
65int TestConfigurator::StepDelay() {
66  return 0;
67}
68
69int TestConfigurator::MinimumReCheckWait() {
70  return recheck_time_;
71}
72
73int TestConfigurator::OnDemandDelay() {
74  return ondemand_time_;
75}
76
77GURL TestConfigurator::UpdateUrl() {
78  return GURL("http://localhost/upd");
79}
80
81GURL TestConfigurator::PingUrl() {
82  return GURL("http://localhost2/ping");
83}
84
85const char* TestConfigurator::ExtraRequestParams() { return "extra=foo"; }
86
87size_t TestConfigurator::UrlSizeLimit() { return 256; }
88
89net::URLRequestContextGetter* TestConfigurator::RequestContext() {
90  return context_.get();
91}
92
93// Don't use the utility process to decode files.
94bool TestConfigurator::InProcess() { return true; }
95
96ComponentPatcher* TestConfigurator::CreateComponentPatcher() {
97  return new MockComponentPatcher();
98}
99
100bool TestConfigurator::DeltasEnabled() const {
101  return true;
102}
103
104// Set how many update checks are called, the default value is just once.
105void TestConfigurator::SetLoopCount(int times) { times_ = times; }
106
107void TestConfigurator::SetRecheckTime(int seconds) {
108  recheck_time_ = seconds;
109}
110
111void TestConfigurator::SetOnDemandTime(int seconds) {
112  ondemand_time_ = seconds;
113}
114
115void TestConfigurator::AddComponentToCheck(CrxComponent* com,
116                                           int at_loop_iter) {
117  components_to_check_.push_back(std::make_pair(com, at_loop_iter));
118}
119
120void TestConfigurator::SetComponentUpdateService(ComponentUpdateService* cus) {
121  cus_ = cus;
122}
123
124ComponentUpdaterTest::ComponentUpdaterTest()
125    : test_config_(NULL),
126      ui_thread_(BrowserThread::UI, &message_loop_),
127      file_thread_(BrowserThread::FILE),
128      io_thread_(BrowserThread::IO) {
129  // The component updater instance under test.
130  test_config_ = new TestConfigurator;
131  component_updater_.reset(ComponentUpdateServiceFactory(test_config_));
132  test_config_->SetComponentUpdateService(component_updater_.get());
133
134  // The test directory is chrome/test/data/components.
135  PathService::Get(chrome::DIR_TEST_DATA, &test_data_dir_);
136  test_data_dir_ = test_data_dir_.AppendASCII("components");
137
138  // Subscribe to all component updater notifications.
139  const int notifications[] = {
140    chrome::NOTIFICATION_COMPONENT_UPDATER_STARTED,
141    chrome::NOTIFICATION_COMPONENT_UPDATER_SLEEPING,
142    chrome::NOTIFICATION_COMPONENT_UPDATE_FOUND,
143    chrome::NOTIFICATION_COMPONENT_UPDATE_READY
144  };
145
146  for (int ix = 0; ix != arraysize(notifications); ++ix) {
147    notification_tracker_.ListenFor(
148        notifications[ix], content::NotificationService::AllSources());
149  }
150  net::URLFetcher::SetEnableInterceptionForTests(true);
151
152  io_thread_.StartIOThread();
153  file_thread_.Start();
154}
155
156ComponentUpdaterTest::~ComponentUpdaterTest() {
157  net::URLFetcher::SetEnableInterceptionForTests(false);
158}
159
160void ComponentUpdaterTest::TearDown() {
161  xmlCleanupGlobals();
162}
163
164ComponentUpdateService* ComponentUpdaterTest::component_updater() {
165  return component_updater_.get();
166}
167
168  // Makes the full path to a component updater test file.
169const base::FilePath ComponentUpdaterTest::test_file(const char* file) {
170  return test_data_dir_.AppendASCII(file);
171}
172
173TestNotificationTracker& ComponentUpdaterTest::notification_tracker() {
174  return notification_tracker_;
175}
176
177TestConfigurator* ComponentUpdaterTest::test_configurator() {
178  return test_config_;
179}
180
181ComponentUpdateService::Status ComponentUpdaterTest::RegisterComponent(
182    CrxComponent* com,
183    TestComponents component,
184    const Version& version,
185    TestInstaller* installer) {
186  if (component == kTestComponent_abag) {
187    com->name = "test_abag";
188    com->pk_hash.assign(abag_hash, abag_hash + arraysize(abag_hash));
189  } else if (component == kTestComponent_jebg) {
190    com->name = "test_jebg";
191    com->pk_hash.assign(jebg_hash, jebg_hash + arraysize(jebg_hash));
192  } else {
193    com->name = "test_ihfo";
194    com->pk_hash.assign(ihfo_hash, ihfo_hash + arraysize(ihfo_hash));
195  }
196  com->version = version;
197  com->installer = installer;
198  return component_updater_->RegisterComponent(*com);
199}
200
201PingChecker::PingChecker(const std::map<std::string, std::string>& attributes)
202    : num_hits_(0), num_misses_(0), attributes_(attributes) {
203}
204
205PingChecker::~PingChecker() {}
206
207void PingChecker::Trial(net::URLRequest* request) {
208  if (Test(request))
209    ++num_hits_;
210  else
211    ++num_misses_;
212}
213
214bool PingChecker::Test(net::URLRequest* request) {
215  if (request->has_upload()) {
216    const net::UploadDataStream* stream = request->get_upload();
217    const net::UploadBytesElementReader* reader =
218        stream->element_readers()[0]->AsBytesReader();
219    int size = reader->length();
220    scoped_refptr <net::IOBuffer> buffer = new net::IOBuffer(size);
221    std::string data(reader->bytes());
222    // For now, we assume that there is only one ping per POST.
223    std::string::size_type start = data.find("<o:event");
224    if (start != std::string::npos) {
225      std::string::size_type end = data.find(">", start);
226      if (end != std::string::npos) {
227        std::string ping = data.substr(start, end - start);
228        std::map<std::string, std::string>::const_iterator iter;
229        for (iter = attributes_.begin(); iter != attributes_.end(); ++iter) {
230          // does the ping contain the specified attribute/value?
231          if (ping.find(std::string(" ") + (iter->first) +
232              std::string("=") + (iter->second)) == string::npos) {
233            return false;
234          }
235        }
236        return true;
237      }
238    }
239  }
240  return false;
241}
242
243// Verify that our test fixture work and the component updater can
244// be created and destroyed with no side effects.
245TEST_F(ComponentUpdaterTest, VerifyFixture) {
246  EXPECT_TRUE(component_updater() != NULL);
247  EXPECT_EQ(0ul, notification_tracker().size());
248}
249
250// Verify that the component updater can be caught in a quick
251// start-shutdown situation. Failure of this test will be a crash. Also
252// if there is no work to do, there are no notifications generated.
253TEST_F(ComponentUpdaterTest, StartStop) {
254  component_updater()->Start();
255  message_loop_.RunUntilIdle();
256  component_updater()->Stop();
257
258  EXPECT_EQ(0ul, notification_tracker().size());
259}
260
261// Verify that when the server has no updates, we go back to sleep and
262// the COMPONENT_UPDATER_STARTED and COMPONENT_UPDATER_SLEEPING notifications
263// are generated.
264TEST_F(ComponentUpdaterTest, CheckCrxSleep) {
265  content::URLLocalHostRequestPrepackagedInterceptor interceptor;
266
267  TestInstaller installer;
268  CrxComponent com;
269  EXPECT_EQ(ComponentUpdateService::kOk,
270            RegisterComponent(&com,
271                              kTestComponent_abag,
272                              Version("1.1"),
273                              &installer));
274
275  const GURL expected_update_url(
276      "http://localhost/upd?extra=foo"
277      "&x=id%3Dabagagagagagagagagagagagagagagag%26v%3D1.1%26fp%3D%26uc");
278
279  interceptor.SetResponse(expected_update_url,
280                          test_file("updatecheck_reply_1.xml"));
281
282  // We loop twice, but there are no updates so we expect two sleep messages.
283  test_configurator()->SetLoopCount(2);
284  component_updater()->Start();
285
286  ASSERT_EQ(1ul, notification_tracker().size());
287  TestNotificationTracker::Event ev1 = notification_tracker().at(0);
288  EXPECT_EQ(chrome::NOTIFICATION_COMPONENT_UPDATER_STARTED, ev1.type);
289
290  message_loop_.Run();
291
292  ASSERT_EQ(3ul, notification_tracker().size());
293  TestNotificationTracker::Event ev2 = notification_tracker().at(1);
294  EXPECT_EQ(chrome::NOTIFICATION_COMPONENT_UPDATER_SLEEPING, ev2.type);
295  TestNotificationTracker::Event ev3 = notification_tracker().at(2);
296  EXPECT_EQ(chrome::NOTIFICATION_COMPONENT_UPDATER_SLEEPING, ev2.type);
297  EXPECT_EQ(2, interceptor.GetHitCount());
298
299  EXPECT_EQ(0, static_cast<TestInstaller*>(com.installer)->error());
300  EXPECT_EQ(0, static_cast<TestInstaller*>(com.installer)->install_count());
301
302  component_updater()->Stop();
303
304  // Loop twice again but this case we simulate a server error by returning
305  // an empty file.
306
307  interceptor.SetResponse(expected_update_url,
308                          test_file("updatecheck_reply_empty"));
309
310  notification_tracker().Reset();
311  test_configurator()->SetLoopCount(2);
312  component_updater()->Start();
313
314  message_loop_.Run();
315
316  ASSERT_EQ(3ul, notification_tracker().size());
317  ev1 = notification_tracker().at(0);
318  EXPECT_EQ(chrome::NOTIFICATION_COMPONENT_UPDATER_STARTED, ev1.type);
319  ev2 = notification_tracker().at(1);
320  EXPECT_EQ(chrome::NOTIFICATION_COMPONENT_UPDATER_SLEEPING, ev2.type);
321  ev3 = notification_tracker().at(2);
322  EXPECT_EQ(chrome::NOTIFICATION_COMPONENT_UPDATER_SLEEPING, ev2.type);
323  EXPECT_EQ(4, interceptor.GetHitCount());
324
325  EXPECT_EQ(0, static_cast<TestInstaller*>(com.installer)->error());
326  EXPECT_EQ(0, static_cast<TestInstaller*>(com.installer)->install_count());
327
328  component_updater()->Stop();
329}
330
331// Verify that we can check for updates and install one component. Besides
332// the notifications above NOTIFICATION_COMPONENT_UPDATE_FOUND and
333// NOTIFICATION_COMPONENT_UPDATE_READY should have been fired. We do two loops
334// so the second time around there should be nothing left to do.
335// We also check that only 3 non-ping network requests are issued:
336// 1- manifest check
337// 2- download crx
338// 3- second manifest check.
339TEST_F(ComponentUpdaterTest, InstallCrx) {
340  std::map<std::string, std::string> map;
341  map.insert(std::pair<std::string, std::string>("eventtype", "\"3\""));
342  map.insert(std::pair<std::string, std::string>("eventresult", "\"1\""));
343  map.insert(std::pair<std::string, std::string>("previousversion",
344                                                 "\"0.9\""));
345  map.insert(std::pair<std::string, std::string>("nextversion", "\"1.0\""));
346  PingChecker ping_checker(map);
347  URLRequestPostInterceptor post_interceptor(&ping_checker);
348  content::URLLocalHostRequestPrepackagedInterceptor interceptor;
349
350  TestInstaller installer1;
351  CrxComponent com1;
352  RegisterComponent(&com1, kTestComponent_jebg, Version("0.9"), &installer1);
353  TestInstaller installer2;
354  CrxComponent com2;
355  RegisterComponent(&com2, kTestComponent_abag, Version("2.2"), &installer2);
356
357  const GURL expected_update_url_1(
358      "http://localhost/upd?extra=foo"
359      "&x=id%3Djebgalgnebhfojomionfpkfelancnnkf%26v%3D0.9%26fp%3D%26uc"
360      "&x=id%3Dabagagagagagagagagagagagagagagag%26v%3D2.2%26fp%3D%26uc");
361
362  const GURL expected_update_url_2(
363      "http://localhost/upd?extra=foo"
364      "&x=id%3Dabagagagagagagagagagagagagagagag%26v%3D2.2%26fp%3D%26uc"
365      "&x=id%3Djebgalgnebhfojomionfpkfelancnnkf%26v%3D1.0%26fp%3D%26uc");
366
367  interceptor.SetResponse(expected_update_url_1,
368                          test_file("updatecheck_reply_1.xml"));
369  interceptor.SetResponse(expected_update_url_2,
370                          test_file("updatecheck_reply_1.xml"));
371  interceptor.SetResponse(GURL(expected_crx_url),
372                          test_file("jebgalgnebhfojomionfpkfelancnnkf.crx"));
373
374  test_configurator()->SetLoopCount(2);
375
376  component_updater()->Start();
377  message_loop_.Run();
378
379  EXPECT_EQ(0, static_cast<TestInstaller*>(com1.installer)->error());
380  EXPECT_EQ(1, static_cast<TestInstaller*>(com1.installer)->install_count());
381  EXPECT_EQ(0, static_cast<TestInstaller*>(com2.installer)->error());
382  EXPECT_EQ(0, static_cast<TestInstaller*>(com2.installer)->install_count());
383
384  EXPECT_EQ(3, interceptor.GetHitCount());
385  EXPECT_EQ(1, ping_checker.NumHits());
386  EXPECT_EQ(0, ping_checker.NumMisses());
387
388  ASSERT_EQ(5ul, notification_tracker().size());
389
390  TestNotificationTracker::Event ev1 = notification_tracker().at(1);
391  EXPECT_EQ(chrome::NOTIFICATION_COMPONENT_UPDATE_FOUND, ev1.type);
392
393  TestNotificationTracker::Event ev2 = notification_tracker().at(2);
394  EXPECT_EQ(chrome::NOTIFICATION_COMPONENT_UPDATE_READY, ev2.type);
395
396  TestNotificationTracker::Event ev3 = notification_tracker().at(3);
397  EXPECT_EQ(chrome::NOTIFICATION_COMPONENT_UPDATER_SLEEPING, ev3.type);
398
399  TestNotificationTracker::Event ev4 = notification_tracker().at(4);
400  EXPECT_EQ(chrome::NOTIFICATION_COMPONENT_UPDATER_SLEEPING, ev4.type);
401
402  component_updater()->Stop();
403}
404
405// This test checks that the "prodversionmin" value is handled correctly. In
406// particular there should not be an install because the minimum product
407// version is much higher than of chrome.
408TEST_F(ComponentUpdaterTest, ProdVersionCheck) {
409  std::map<std::string, std::string> map;
410  PingChecker ping_checker(map);
411  URLRequestPostInterceptor post_interceptor(&ping_checker);
412  content::URLLocalHostRequestPrepackagedInterceptor interceptor;
413
414  TestInstaller installer;
415  CrxComponent com;
416  RegisterComponent(&com, kTestComponent_jebg, Version("0.9"), &installer);
417
418  const GURL expected_update_url(
419      "http://localhost/upd?extra=foo&x=id%3D"
420      "jebgalgnebhfojomionfpkfelancnnkf%26v%3D0.9%26fp%3D%26uc");
421
422  interceptor.SetResponse(expected_update_url,
423                          test_file("updatecheck_reply_2.xml"));
424  interceptor.SetResponse(GURL(expected_crx_url),
425                          test_file("jebgalgnebhfojomionfpkfelancnnkf.crx"));
426
427  test_configurator()->SetLoopCount(1);
428  component_updater()->Start();
429  message_loop_.Run();
430
431  EXPECT_EQ(0, ping_checker.NumHits());
432  EXPECT_EQ(0, ping_checker.NumMisses());
433  EXPECT_EQ(1, interceptor.GetHitCount());
434  EXPECT_EQ(0, static_cast<TestInstaller*>(com.installer)->error());
435  EXPECT_EQ(0, static_cast<TestInstaller*>(com.installer)->install_count());
436
437  component_updater()->Stop();
438}
439
440// Test that a ping for an update check can cause installs.
441// Here is the timeline:
442//  - First loop: we return a reply that indicates no update, so
443//    nothing happens.
444//  - We ping.
445//  - This triggers a second loop, which has a reply that triggers an install.
446TEST_F(ComponentUpdaterTest, CheckForUpdateSoon) {
447  std::map<std::string, std::string> map;
448  map.insert(std::pair<std::string, std::string>("eventtype", "\"3\""));
449  map.insert(std::pair<std::string, std::string>("eventresult", "\"1\""));
450  map.insert(std::pair<std::string, std::string>("previousversion",
451                                                 "\"0.9\""));
452  map.insert(std::pair<std::string, std::string>("nextversion", "\"1.0\""));
453  PingChecker ping_checker(map);
454  URLRequestPostInterceptor post_interceptor(&ping_checker);
455  content::URLLocalHostRequestPrepackagedInterceptor interceptor;
456
457  TestInstaller installer1;
458  CrxComponent com1;
459  RegisterComponent(&com1, kTestComponent_abag, Version("2.2"), &installer1);
460  TestInstaller installer2;
461  CrxComponent com2;
462  RegisterComponent(&com2, kTestComponent_jebg, Version("0.9"), &installer2);
463
464  const GURL expected_update_url_1(
465      "http://localhost/upd?extra=foo"
466      "&x=id%3Dabagagagagagagagagagagagagagagag%26v%3D2.2%26fp%3D%26uc"
467      "&x=id%3Djebgalgnebhfojomionfpkfelancnnkf%26v%3D0.9%26fp%3D%26uc");
468
469  const GURL expected_update_url_2(
470      "http://localhost/upd?extra=foo"
471      "&x=id%3Djebgalgnebhfojomionfpkfelancnnkf%26v%3D0.9%26fp%3D%26uc"
472      "%26installsource%3Dondemand"
473      "&x=id%3Dabagagagagagagagagagagagagagagag%26v%3D2.2%26fp%3D%26uc");
474
475  interceptor.SetResponse(expected_update_url_1,
476                          test_file("updatecheck_reply_empty"));
477  interceptor.SetResponse(expected_update_url_2,
478                          test_file("updatecheck_reply_1.xml"));
479  interceptor.SetResponse(GURL(expected_crx_url),
480                          test_file("jebgalgnebhfojomionfpkfelancnnkf.crx"));
481  // Test success.
482  test_configurator()->SetLoopCount(2);
483  test_configurator()->AddComponentToCheck(&com2, 1);
484  component_updater()->Start();
485  message_loop_.Run();
486
487  EXPECT_EQ(0, static_cast<TestInstaller*>(com1.installer)->error());
488  EXPECT_EQ(0, static_cast<TestInstaller*>(com1.installer)->install_count());
489  EXPECT_EQ(0, static_cast<TestInstaller*>(com2.installer)->error());
490  EXPECT_EQ(1, static_cast<TestInstaller*>(com2.installer)->install_count());
491
492  EXPECT_EQ(3, interceptor.GetHitCount());
493
494  ASSERT_EQ(5ul, notification_tracker().size());
495
496  TestNotificationTracker::Event ev0= notification_tracker().at(0);
497  EXPECT_EQ(chrome::NOTIFICATION_COMPONENT_UPDATER_STARTED, ev0.type);
498
499  TestNotificationTracker::Event ev1 = notification_tracker().at(1);
500  EXPECT_EQ(chrome::NOTIFICATION_COMPONENT_UPDATER_SLEEPING, ev1.type);
501
502  TestNotificationTracker::Event ev2 = notification_tracker().at(2);
503  EXPECT_EQ(chrome::NOTIFICATION_COMPONENT_UPDATE_FOUND, ev2.type);
504
505  TestNotificationTracker::Event ev3 = notification_tracker().at(3);
506  EXPECT_EQ(chrome::NOTIFICATION_COMPONENT_UPDATE_READY, ev3.type);
507
508  TestNotificationTracker::Event ev4 = notification_tracker().at(4);
509  EXPECT_EQ(chrome::NOTIFICATION_COMPONENT_UPDATER_SLEEPING, ev4.type);
510
511  // Also check what happens if previous check too soon.
512  test_configurator()->SetOnDemandTime(60 * 60);
513  EXPECT_EQ(ComponentUpdateService::kError,
514            component_updater()->CheckForUpdateSoon(com2));
515  // Okay, now reset to 0 for the other tests.
516  test_configurator()->SetOnDemandTime(0);
517  component_updater()->Stop();
518
519  // Test a few error cases. NOTE: We don't have callbacks for
520  // when the updates failed yet.
521  const GURL expected_update_url_3(
522      "http://localhost/upd?extra=foo"
523      "&x=id%3Djebgalgnebhfojomionfpkfelancnnkf%26v%3D1.0%26fp%3D%26uc"
524      "&x=id%3Dabagagagagagagagagagagagagagagag%26v%3D2.2%26fp%3D%26uc");
525
526  // No update: error from no server response
527  interceptor.SetResponse(expected_update_url_3,
528                          test_file("updatecheck_reply_empty"));
529  notification_tracker().Reset();
530  test_configurator()->SetLoopCount(1);
531  component_updater()->Start();
532  EXPECT_EQ(ComponentUpdateService::kOk,
533            component_updater()->CheckForUpdateSoon(com2));
534
535  message_loop_.Run();
536
537  ASSERT_EQ(2ul, notification_tracker().size());
538  ev0 = notification_tracker().at(0);
539  EXPECT_EQ(chrome::NOTIFICATION_COMPONENT_UPDATER_STARTED, ev0.type);
540  ev1 = notification_tracker().at(1);
541  EXPECT_EQ(chrome::NOTIFICATION_COMPONENT_UPDATER_SLEEPING, ev1.type);
542  component_updater()->Stop();
543
544  // No update: already updated to 1.0 so nothing new
545  interceptor.SetResponse(expected_update_url_3,
546                          test_file("updatecheck_reply_1.xml"));
547  notification_tracker().Reset();
548  test_configurator()->SetLoopCount(1);
549  component_updater()->Start();
550  EXPECT_EQ(ComponentUpdateService::kOk,
551            component_updater()->CheckForUpdateSoon(com2));
552
553  message_loop_.Run();
554
555  EXPECT_EQ(1, ping_checker.NumHits());
556  EXPECT_EQ(0, ping_checker.NumMisses());
557  ASSERT_EQ(2ul, notification_tracker().size());
558  ev0 = notification_tracker().at(0);
559  EXPECT_EQ(chrome::NOTIFICATION_COMPONENT_UPDATER_STARTED, ev0.type);
560  ev1 = notification_tracker().at(1);
561  EXPECT_EQ(chrome::NOTIFICATION_COMPONENT_UPDATER_SLEEPING, ev1.type);
562  component_updater()->Stop();
563}
564
565// Verify that a previously registered component can get re-registered
566// with a different version.
567TEST_F(ComponentUpdaterTest, CheckReRegistration) {
568  std::map<std::string, std::string> map;
569  map.insert(std::pair<std::string, std::string>("eventtype", "\"3\""));
570  map.insert(std::pair<std::string, std::string>("eventresult", "\"1\""));
571  map.insert(std::pair<std::string, std::string>("previousversion",
572                                                 "\"0.9\""));
573  map.insert(std::pair<std::string, std::string>("nextversion", "\"1.0\""));
574  PingChecker ping_checker(map);
575  URLRequestPostInterceptor post_interceptor(&ping_checker);
576  content::URLLocalHostRequestPrepackagedInterceptor interceptor;
577
578  TestInstaller installer1;
579  CrxComponent com1;
580  RegisterComponent(&com1, kTestComponent_jebg, Version("0.9"), &installer1);
581  TestInstaller installer2;
582  CrxComponent com2;
583  RegisterComponent(&com2, kTestComponent_abag, Version("2.2"), &installer2);
584
585  // Start with 0.9, and update to 1.0
586  const GURL expected_update_url_1(
587      "http://localhost/upd?extra=foo"
588      "&x=id%3Djebgalgnebhfojomionfpkfelancnnkf%26v%3D0.9%26fp%3D%26uc"
589      "&x=id%3Dabagagagagagagagagagagagagagagag%26v%3D2.2%26fp%3D%26uc");
590
591  const GURL expected_update_url_2(
592      "http://localhost/upd?extra=foo"
593      "&x=id%3Dabagagagagagagagagagagagagagagag%26v%3D2.2%26fp%3D%26uc"
594      "&x=id%3Djebgalgnebhfojomionfpkfelancnnkf%26v%3D1.0%26fp%3D%26uc");
595
596  interceptor.SetResponse(expected_update_url_1,
597                          test_file("updatecheck_reply_1.xml"));
598  interceptor.SetResponse(expected_update_url_2,
599                          test_file("updatecheck_reply_1.xml"));
600  interceptor.SetResponse(GURL(expected_crx_url),
601                          test_file("jebgalgnebhfojomionfpkfelancnnkf.crx"));
602
603  // Loop twice to issue two checks: (1) with original 0.9 version
604  // and (2) with the updated 1.0 version.
605  test_configurator()->SetLoopCount(2);
606
607  component_updater()->Start();
608  message_loop_.Run();
609
610  EXPECT_EQ(0, static_cast<TestInstaller*>(com1.installer)->error());
611  EXPECT_EQ(1, static_cast<TestInstaller*>(com1.installer)->install_count());
612  EXPECT_EQ(0, static_cast<TestInstaller*>(com2.installer)->error());
613  EXPECT_EQ(0, static_cast<TestInstaller*>(com2.installer)->install_count());
614
615  EXPECT_EQ(1, ping_checker.NumHits());
616  EXPECT_EQ(0, ping_checker.NumMisses());
617  EXPECT_EQ(3, interceptor.GetHitCount());
618
619  ASSERT_EQ(5ul, notification_tracker().size());
620
621  TestNotificationTracker::Event ev0 = notification_tracker().at(0);
622  EXPECT_EQ(chrome::NOTIFICATION_COMPONENT_UPDATER_STARTED, ev0.type);
623
624  TestNotificationTracker::Event ev1 = notification_tracker().at(1);
625  EXPECT_EQ(chrome::NOTIFICATION_COMPONENT_UPDATE_FOUND, ev1.type);
626
627  TestNotificationTracker::Event ev2 = notification_tracker().at(2);
628  EXPECT_EQ(chrome::NOTIFICATION_COMPONENT_UPDATE_READY, ev2.type);
629
630  TestNotificationTracker::Event ev3 = notification_tracker().at(3);
631  EXPECT_EQ(chrome::NOTIFICATION_COMPONENT_UPDATER_SLEEPING, ev3.type);
632
633  TestNotificationTracker::Event ev4 = notification_tracker().at(4);
634  EXPECT_EQ(chrome::NOTIFICATION_COMPONENT_UPDATER_SLEEPING, ev4.type);
635
636  // Now re-register, pretending to be an even newer version (2.2)
637  TestInstaller installer3;
638  component_updater()->Stop();
639  EXPECT_EQ(ComponentUpdateService::kReplaced,
640            RegisterComponent(&com1,
641                              kTestComponent_jebg,
642                              Version("2.2"),
643                              &installer3));
644
645  // Check that we send out 2.2 as our version.
646  // Interceptor's hit count should go up by 1.
647  const GURL expected_update_url_3(
648      "http://localhost/upd?extra=foo"
649      "&x=id%3Djebgalgnebhfojomionfpkfelancnnkf%26v%3D2.2%26fp%3D%26uc"
650      "&x=id%3Dabagagagagagagagagagagagagagagag%26v%3D2.2%26fp%3D%26uc");
651
652  interceptor.SetResponse(expected_update_url_3,
653                          test_file("updatecheck_reply_1.xml"));
654
655  notification_tracker().Reset();
656
657  // Loop once just to notice the check happening with the re-register version.
658  test_configurator()->SetLoopCount(1);
659  component_updater()->Start();
660  message_loop_.Run();
661
662  ASSERT_EQ(2ul, notification_tracker().size());
663
664  ev0 = notification_tracker().at(0);
665  EXPECT_EQ(chrome::NOTIFICATION_COMPONENT_UPDATER_STARTED, ev0.type);
666
667  ev1 = notification_tracker().at(1);
668  EXPECT_EQ(chrome::NOTIFICATION_COMPONENT_UPDATER_SLEEPING, ev1.type);
669
670  EXPECT_EQ(4, interceptor.GetHitCount());
671
672  // We created a new installer, so the counts go back to 0.
673  EXPECT_EQ(0, static_cast<TestInstaller*>(com1.installer)->error());
674  EXPECT_EQ(0, static_cast<TestInstaller*>(com1.installer)->install_count());
675  EXPECT_EQ(0, static_cast<TestInstaller*>(com2.installer)->error());
676  EXPECT_EQ(0, static_cast<TestInstaller*>(com2.installer)->install_count());
677
678  component_updater()->Stop();
679}
680
681// Verify that we can download and install a component and a differential
682// update to that component. We do three loops; the final loop should do
683// nothing.
684// We also check that exactly 5 non-ping network requests are issued:
685// 1- update check (response: v1 available)
686// 2- download crx (v1)
687// 3- update check (response: v2 available)
688// 4- download differential crx (v1 to v2)
689// 5- update check (response: no further update available)
690// There should be two pings, one for each update. The second will bear a
691// diffresult=1, while the first will not.
692TEST_F(ComponentUpdaterTest, DifferentialUpdate) {
693  std::map<std::string, std::string> map;
694  map.insert(std::pair<std::string, std::string>("eventtype", "\"3\""));
695  map.insert(std::pair<std::string, std::string>("eventresult", "\"1\""));
696  map.insert(std::pair<std::string, std::string>("diffresult", "\"1\""));
697  PingChecker ping_checker(map);
698  URLRequestPostInterceptor post_interceptor(&ping_checker);
699  content::URLLocalHostRequestPrepackagedInterceptor interceptor;
700
701  VersionedTestInstaller installer;
702  CrxComponent com;
703  RegisterComponent(&com, kTestComponent_ihfo, Version("0.0"), &installer);
704
705  const GURL expected_update_url_0(
706      "http://localhost/upd?extra=foo"
707      "&x=id%3Dihfokbkgjpifnbbojhneepfflplebdkc%26v%3D0.0%26fp%3D%26uc");
708  const GURL expected_update_url_1(
709      "http://localhost/upd?extra=foo"
710      "&x=id%3Dihfokbkgjpifnbbojhneepfflplebdkc%26v%3D1.0%26fp%3D1%26uc");
711  const GURL expected_update_url_2(
712      "http://localhost/upd?extra=foo"
713      "&x=id%3Dihfokbkgjpifnbbojhneepfflplebdkc%26v%3D2.0%26fp%3Df22%26uc");
714  const GURL expected_crx_url_1(
715      "http://localhost/download/ihfokbkgjpifnbbojhneepfflplebdkc_1.crx");
716  const GURL expected_crx_url_1_diff_2(
717      "http://localhost/download/ihfokbkgjpifnbbojhneepfflplebdkc_1to2.crx");
718
719  interceptor.SetResponse(expected_update_url_0,
720                          test_file("updatecheck_diff_reply_1.xml"));
721  interceptor.SetResponse(expected_update_url_1,
722                          test_file("updatecheck_diff_reply_2.xml"));
723  interceptor.SetResponse(expected_update_url_2,
724                          test_file("updatecheck_diff_reply_3.xml"));
725  interceptor.SetResponse(expected_crx_url_1,
726                          test_file("ihfokbkgjpifnbbojhneepfflplebdkc_1.crx"));
727  interceptor.SetResponse(
728      expected_crx_url_1_diff_2,
729      test_file("ihfokbkgjpifnbbojhneepfflplebdkc_1to2.crx"));
730
731  test_configurator()->SetLoopCount(3);
732
733  component_updater()->Start();
734  message_loop_.Run();
735
736  EXPECT_EQ(0, static_cast<TestInstaller*>(com.installer)->error());
737  EXPECT_EQ(2, static_cast<TestInstaller*>(com.installer)->install_count());
738
739  // One ping has the diffresult=1, the other does not.
740  EXPECT_EQ(1, ping_checker.NumHits());
741  EXPECT_EQ(1, ping_checker.NumMisses());
742
743  EXPECT_EQ(5, interceptor.GetHitCount());
744
745  component_updater()->Stop();
746}
747
748// Verify that component installation falls back to downloading and installing
749// a full update if the differential update fails (in this case, because the
750// installer does not know about the existing files). We do two loops; the final
751// loop should do nothing.
752// We also check that exactly 4 non-ping network requests are issued:
753// 1- update check (loop 1)
754// 2- download differential crx
755// 3- download full crx
756// 4- update check (loop 2 - no update available)
757// There should be one ping for the first attempted update.
758TEST_F(ComponentUpdaterTest, DifferentialUpdateFails) {
759  std::map<std::string, std::string> map;
760  map.insert(std::pair<std::string, std::string>("eventtype", "\"3\""));
761  map.insert(std::pair<std::string, std::string>("eventresult", "\"1\""));
762  map.insert(std::pair<std::string, std::string>("diffresult", "\"0\""));
763  map.insert(std::pair<std::string, std::string>("differrorcode", "\"16\""));
764  PingChecker ping_checker(map);
765  URLRequestPostInterceptor post_interceptor(&ping_checker);
766  content::URLLocalHostRequestPrepackagedInterceptor interceptor;
767
768  TestInstaller installer;
769  CrxComponent com;
770  RegisterComponent(&com, kTestComponent_ihfo, Version("1.0"), &installer);
771
772  const GURL expected_update_url_1(
773      "http://localhost/upd?extra=foo"
774      "&x=id%3Dihfokbkgjpifnbbojhneepfflplebdkc%26v%3D1.0%26fp%3D%26uc");
775  const GURL expected_update_url_2(
776      "http://localhost/upd?extra=foo"
777      "&x=id%3Dihfokbkgjpifnbbojhneepfflplebdkc%26v%3D2.0%26fp%3Df22%26uc");
778  const GURL expected_crx_url_1(
779      "http://localhost/download/ihfokbkgjpifnbbojhneepfflplebdkc_1.crx");
780  const GURL expected_crx_url_1_diff_2(
781      "http://localhost/download/ihfokbkgjpifnbbojhneepfflplebdkc_1to2.crx");
782  const GURL expected_crx_url_2(
783      "http://localhost/download/ihfokbkgjpifnbbojhneepfflplebdkc_2.crx");
784
785  interceptor.SetResponse(expected_update_url_1,
786                          test_file("updatecheck_diff_reply_2.xml"));
787  interceptor.SetResponse(expected_update_url_2,
788                          test_file("updatecheck_diff_reply_3.xml"));
789  interceptor.SetResponse(expected_crx_url_1,
790                          test_file("ihfokbkgjpifnbbojhneepfflplebdkc_1.crx"));
791  interceptor.SetResponse(
792      expected_crx_url_1_diff_2,
793      test_file("ihfokbkgjpifnbbojhneepfflplebdkc_1to2.crx"));
794  interceptor.SetResponse(expected_crx_url_2,
795                          test_file("ihfokbkgjpifnbbojhneepfflplebdkc_2.crx"));
796
797  test_configurator()->SetLoopCount(2);
798
799  component_updater()->Start();
800  message_loop_.Run();
801
802  // A failed differential update does not count as a failed install.
803  EXPECT_EQ(0, static_cast<TestInstaller*>(com.installer)->error());
804  EXPECT_EQ(1, static_cast<TestInstaller*>(com.installer)->install_count());
805
806  EXPECT_EQ(1, ping_checker.NumHits());
807  EXPECT_EQ(0, ping_checker.NumMisses());
808  EXPECT_EQ(4, interceptor.GetHitCount());
809
810  component_updater()->Stop();
811}
812
813// Verify that a failed installation causes an install failure ping.
814TEST_F(ComponentUpdaterTest, CheckFailedInstallPing) {
815  std::map<std::string, std::string> map;
816  map.insert(std::pair<std::string, std::string>("eventtype", "\"3\""));
817  map.insert(std::pair<std::string, std::string>("eventresult", "\"0\""));
818  map.insert(std::pair<std::string, std::string>("errorcode", "\"9\""));
819  map.insert(std::pair<std::string, std::string>("previousversion",
820                                                 "\"0.9\""));
821  map.insert(std::pair<std::string, std::string>("nextversion", "\"1.0\""));
822  PingChecker ping_checker(map);
823  URLRequestPostInterceptor post_interceptor(&ping_checker);
824  content::URLLocalHostRequestPrepackagedInterceptor interceptor;
825
826  // This test installer reports installation failure.
827  class : public TestInstaller {
828    virtual bool Install(const base::DictionaryValue& manifest,
829                         const base::FilePath& unpack_path) OVERRIDE {
830      ++install_count_;
831      base::DeleteFile(unpack_path, true);
832      return false;
833    }
834  } installer;
835
836  CrxComponent com;
837  RegisterComponent(&com, kTestComponent_jebg, Version("0.9"), &installer);
838
839  // Start with 0.9, and attempt update to 1.0
840  const GURL expected_update_url_1(
841      "http://localhost/upd?extra=foo"
842      "&x=id%3Djebgalgnebhfojomionfpkfelancnnkf%26v%3D0.9%26fp%3D%26uc");
843
844  interceptor.SetResponse(expected_update_url_1,
845                          test_file("updatecheck_reply_1.xml"));
846  interceptor.SetResponse(GURL(expected_crx_url),
847                          test_file("jebgalgnebhfojomionfpkfelancnnkf.crx"));
848
849  // Loop twice to issue two checks: (1) with original 0.9 version
850  // and (2), which should retry with 0.9.
851  test_configurator()->SetLoopCount(2);
852  component_updater()->Start();
853  message_loop_.Run();
854
855  // Loop once more, but expect no ping because a noupdate response is issued.
856  // This is necessary to clear out the fire-and-forget ping from the previous
857  // iteration.
858  interceptor.SetResponse(expected_update_url_1,
859                          test_file("updatecheck_reply_noupdate.xml"));
860  test_configurator()->SetLoopCount(1);
861  component_updater()->Start();
862  message_loop_.Run();
863
864  EXPECT_EQ(0, static_cast<TestInstaller*>(com.installer)->error());
865  EXPECT_EQ(2, static_cast<TestInstaller*>(com.installer)->install_count());
866
867  EXPECT_EQ(2, ping_checker.NumHits());
868  EXPECT_EQ(0, ping_checker.NumMisses());
869  EXPECT_EQ(5, interceptor.GetHitCount());
870
871  component_updater()->Stop();
872}
873
874// Verify that we successfully propagate a patcher error.
875// ihfokbkgjpifnbbojhneepfflplebdkc_1to2_bad.crx contains an incorrect
876// patching instruction that should fail.
877TEST_F(ComponentUpdaterTest, DifferentialUpdateFailErrorcode) {
878  std::map<std::string, std::string> map;
879  map.insert(std::pair<std::string, std::string>("eventtype", "\"3\""));
880  map.insert(std::pair<std::string, std::string>("eventresult", "\"1\""));
881  map.insert(std::pair<std::string, std::string>("diffresult", "\"0\""));
882  map.insert(std::pair<std::string, std::string>("differrorcode", "\"14\""));
883  map.insert(std::pair<std::string, std::string>("diffextracode1", "\"305\""));
884  PingChecker ping_checker(map);
885  URLRequestPostInterceptor post_interceptor(&ping_checker);
886  content::URLLocalHostRequestPrepackagedInterceptor interceptor;
887
888  VersionedTestInstaller installer;
889  CrxComponent com;
890  RegisterComponent(&com, kTestComponent_ihfo, Version("0.0"), &installer);
891
892  const GURL expected_update_url_0(
893      "http://localhost/upd?extra=foo"
894      "&x=id%3Dihfokbkgjpifnbbojhneepfflplebdkc%26v%3D0.0%26fp%3D%26uc");
895  const GURL expected_update_url_1(
896      "http://localhost/upd?extra=foo"
897      "&x=id%3Dihfokbkgjpifnbbojhneepfflplebdkc%26v%3D1.0%26fp%3D1%26uc");
898  const GURL expected_update_url_2(
899      "http://localhost/upd?extra=foo"
900      "&x=id%3Dihfokbkgjpifnbbojhneepfflplebdkc%26v%3D2.0%26fp%3Df22%26uc");
901  const GURL expected_crx_url_1(
902      "http://localhost/download/ihfokbkgjpifnbbojhneepfflplebdkc_1.crx");
903  const GURL expected_crx_url_1_diff_2(
904      "http://localhost/download/ihfokbkgjpifnbbojhneepfflplebdkc_1to2.crx");
905  const GURL expected_crx_url_2(
906      "http://localhost/download/ihfokbkgjpifnbbojhneepfflplebdkc_2.crx");
907
908  interceptor.SetResponse(expected_update_url_0,
909                          test_file("updatecheck_diff_reply_1.xml"));
910  interceptor.SetResponse(expected_update_url_1,
911                          test_file("updatecheck_diff_reply_2.xml"));
912  interceptor.SetResponse(expected_update_url_2,
913                          test_file("updatecheck_diff_reply_3.xml"));
914  interceptor.SetResponse(expected_crx_url_1,
915                          test_file("ihfokbkgjpifnbbojhneepfflplebdkc_1.crx"));
916  interceptor.SetResponse(
917      expected_crx_url_1_diff_2,
918      test_file("ihfokbkgjpifnbbojhneepfflplebdkc_1to2_bad.crx"));
919  interceptor.SetResponse(expected_crx_url_2,
920                          test_file("ihfokbkgjpifnbbojhneepfflplebdkc_2.crx"));
921
922  test_configurator()->SetLoopCount(3);
923
924  component_updater()->Start();
925  message_loop_.Run();
926
927  EXPECT_EQ(0, static_cast<TestInstaller*>(com.installer)->error());
928  EXPECT_EQ(2, static_cast<TestInstaller*>(com.installer)->install_count());
929
930  EXPECT_EQ(1, ping_checker.NumHits());
931  EXPECT_EQ(1, ping_checker.NumMisses());
932  EXPECT_EQ(6, interceptor.GetHitCount());
933
934  component_updater()->Stop();
935}
936