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