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