component_updater_service_unittest.cc revision 5821806d5e7f356e8fa4b058a389a808ea183019
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/component_updater_service.h" 6 7#include "base/compiler_specific.h" 8#include "base/file_path.h" 9#include "base/file_util.h" 10#include "base/memory/scoped_vector.h" 11#include "base/message_loop.h" 12#include "base/path_service.h" 13#include "base/values.h" 14#include "chrome/browser/component_updater/component_updater_interceptor.h" 15#include "chrome/common/chrome_notification_types.h" 16#include "chrome/common/chrome_paths.h" 17#include "content/public/browser/notification_observer.h" 18#include "content/public/browser/notification_service.h" 19#include "content/public/test/test_browser_thread.h" 20#include "content/public/test/test_notification_tracker.h" 21#include "googleurl/src/gurl.h" 22#include "libxml/globals.h" 23#include "net/url_request/url_fetcher.h" 24#include "net/url_request/url_request_test_util.h" 25#include "testing/gtest/include/gtest/gtest.h" 26 27using content::BrowserThread; 28using content::TestNotificationTracker; 29 30namespace { 31// Overrides some of the component updater behaviors so it is easier to test 32// and loops faster. In actual usage it takes hours do to a full cycle. 33class TestConfigurator : public ComponentUpdateService::Configurator { 34 public: 35 TestConfigurator() : times_(1) { 36 } 37 38 virtual int InitialDelay() OVERRIDE { return 0; } 39 40 virtual int NextCheckDelay() OVERRIDE { 41 // This is called when a new full cycle of checking for updates is going 42 // to happen. In test we normally only test one cycle so it is a good 43 // time to break from the test messageloop Run() method so the test can 44 // finish. 45 if (--times_ > 0) 46 return 1; 47 48 MessageLoop::current()->Quit(); 49 return 0; 50 } 51 52 virtual int StepDelay() OVERRIDE { 53 return 0; 54 } 55 56 virtual int MinimumReCheckWait() OVERRIDE { 57 return 0; 58 } 59 60 virtual GURL UpdateUrl() OVERRIDE { return GURL("http://localhost/upd"); } 61 62 virtual const char* ExtraRequestParams() OVERRIDE { return "extra=foo"; } 63 64 virtual size_t UrlSizeLimit() OVERRIDE { return 256; } 65 66 virtual net::URLRequestContextGetter* RequestContext() OVERRIDE { 67 return new TestURLRequestContextGetter( 68 BrowserThread::GetMessageLoopProxyForThread(BrowserThread::IO)); 69 } 70 71 // Don't use the utility process to decode files. 72 virtual bool InProcess() OVERRIDE { return true; } 73 74 virtual void OnEvent(Events event, int extra) OVERRIDE { } 75 76 // Set how many update checks are called, the default value is just once. 77 void SetLoopCount(int times) { times_ = times; } 78 79 private: 80 int times_; 81}; 82 83class TestInstaller : public ComponentInstaller { 84 public : 85 explicit TestInstaller() 86 : error_(0), install_count_(0) { 87 } 88 89 virtual void OnUpdateError(int error) OVERRIDE { 90 EXPECT_NE(0, error); 91 error_ = error; 92 } 93 94 virtual bool Install(base::DictionaryValue* manifest, 95 const FilePath& unpack_path) OVERRIDE { 96 ++install_count_; 97 delete manifest; 98 return file_util::Delete(unpack_path, true); 99 } 100 101 int error() const { return error_; } 102 103 int install_count() const { return install_count_; } 104 105 private: 106 int error_; 107 int install_count_; 108}; 109 110// component 1 has extension id "jebgalgnebhfojomionfpkfelancnnkf", and 111// the RSA public key the following hash: 112const uint8 jebg_hash[] = {0x94,0x16,0x0b,0x6d,0x41,0x75,0xe9,0xec,0x8e,0xd5, 113 0xfa,0x54,0xb0,0xd2,0xdd,0xa5,0x6e,0x05,0x6b,0xe8, 114 0x73,0x47,0xf6,0xc4,0x11,0x9f,0xbc,0xb3,0x09,0xb3, 115 0x5b,0x40}; 116// component 2 has extension id "abagagagagagagagagagagagagagagag", and 117// the RSA public key the following hash: 118const uint8 abag_hash[] = {0x01,0x06,0x06,0x06,0x06,0x06,0x06,0x06,0x06,0x06, 119 0x06,0x06,0x06,0x06,0x06,0x06,0x06,0x06,0x06,0x06, 120 0x06,0x06,0x06,0x06,0x06,0x06,0x06,0x06,0x06,0x06, 121 0x06,0x01}; 122 123const char header_ok_reply[] = 124 "HTTP/1.1 200 OK\0" 125 "Content-type: text/html\0" 126 "\0"; 127 128const char expected_crx_url[] = 129 "http://localhost/download/jebgalgnebhfojomionfpkfelancnnkf.crx"; 130 131} // namespace 132 133// Common fixture for all the component updater tests. 134class ComponentUpdaterTest : public testing::Test { 135 public: 136 enum TestComponents { 137 kTestComponent_abag, 138 kTestComponent_jebg 139 }; 140 141 ComponentUpdaterTest() : component_updater_(NULL), test_config_(NULL) { 142 // The component updater instance under test. 143 test_config_ = new TestConfigurator; 144 component_updater_.reset(ComponentUpdateServiceFactory(test_config_)); 145 // The test directory is chrome/test/data/components. 146 PathService::Get(chrome::DIR_TEST_DATA, &test_data_dir_); 147 test_data_dir_ = test_data_dir_.AppendASCII("components"); 148 149 // Subscribe to all component updater notifications. 150 const int notifications[] = { 151 chrome::NOTIFICATION_COMPONENT_UPDATER_STARTED, 152 chrome::NOTIFICATION_COMPONENT_UPDATER_SLEEPING, 153 chrome::NOTIFICATION_COMPONENT_UPDATE_FOUND, 154 chrome::NOTIFICATION_COMPONENT_UPDATE_READY 155 }; 156 157 for (int ix = 0; ix != arraysize(notifications); ++ix) { 158 notification_tracker_.ListenFor( 159 notifications[ix], content::NotificationService::AllSources()); 160 } 161 net::URLFetcher::SetEnableInterceptionForTests(true); 162 } 163 164 ~ComponentUpdaterTest() { 165 net::URLFetcher::SetEnableInterceptionForTests(false); 166 } 167 168 void TearDown() { 169 xmlCleanupGlobals(); 170 } 171 172 ComponentUpdateService* component_updater() { 173 return component_updater_.get(); 174 } 175 176 // Makes the full path to a component updater test file. 177 const FilePath test_file(const char* file) { 178 return test_data_dir_.AppendASCII(file); 179 } 180 181 TestNotificationTracker& notification_tracker() { 182 return notification_tracker_; 183 } 184 185 TestConfigurator* test_configurator() { 186 return test_config_; 187 } 188 189 void RegisterComponent(CrxComponent* com, 190 TestComponents component, 191 const Version& version) { 192 if (component == kTestComponent_abag) { 193 com->name = "test_abag"; 194 com->pk_hash.assign(abag_hash, abag_hash + arraysize(abag_hash)); 195 } else { 196 com->name = "test_jebg"; 197 com->pk_hash.assign(jebg_hash, jebg_hash + arraysize(jebg_hash)); 198 } 199 com->version = version; 200 TestInstaller* installer = new TestInstaller; 201 com->installer = installer; 202 test_installers_.push_back(installer); 203 component_updater_->RegisterComponent(*com); 204 } 205 206 private: 207 scoped_ptr<ComponentUpdateService> component_updater_; 208 FilePath test_data_dir_; 209 TestNotificationTracker notification_tracker_; 210 TestConfigurator* test_config_; 211 // ComponentInstaller objects to delete after each test. 212 ScopedVector<TestInstaller> test_installers_; 213}; 214 215// Verify that our test fixture work and the component updater can 216// be created and destroyed with no side effects. 217TEST_F(ComponentUpdaterTest, VerifyFixture) { 218 EXPECT_TRUE(component_updater() != NULL); 219 EXPECT_EQ(0ul, notification_tracker().size()); 220} 221 222// Verify that the component updater can be caught in a quick 223// start-shutdown situation. Failure of this test will be a crash. Also 224// if there is no work to do, there are no notifications generated. 225TEST_F(ComponentUpdaterTest, StartStop) { 226 MessageLoop message_loop; 227 content::TestBrowserThread ui_thread(BrowserThread::UI, &message_loop); 228 229 component_updater()->Start(); 230 message_loop.RunAllPending(); 231 component_updater()->Stop(); 232 233 EXPECT_EQ(0ul, notification_tracker().size()); 234} 235 236// Verify that when the server has no updates, we go back to sleep and 237// the COMPONENT_UPDATER_STARTED and COMPONENT_UPDATER_SLEEPING notifications 238// are generated. 239TEST_F(ComponentUpdaterTest, CheckCrxSleep) { 240 MessageLoop message_loop; 241 content::TestBrowserThread ui_thread(BrowserThread::UI, &message_loop); 242 content::TestBrowserThread file_thread(BrowserThread::FILE); 243 content::TestBrowserThread io_thread(BrowserThread::IO); 244 245 io_thread.StartIOThread(); 246 file_thread.Start(); 247 248 scoped_refptr<ComponentUpdateInterceptor> 249 interceptor(new ComponentUpdateInterceptor()); 250 251 CrxComponent com; 252 RegisterComponent(&com, kTestComponent_abag, Version("1.1")); 253 254 const char expected_update_url[] = 255 "http://localhost/upd?extra=foo&x=id%3D" 256 "abagagagagagagagagagagagagagagag%26v%3D1.1%26uc"; 257 258 interceptor->SetResponse(expected_update_url, 259 header_ok_reply, 260 test_file("updatecheck_reply_1.xml")); 261 262 // We loop twice, but there are no updates so we expect two sleep messages. 263 test_configurator()->SetLoopCount(2); 264 component_updater()->Start(); 265 266 ASSERT_EQ(1ul, notification_tracker().size()); 267 TestNotificationTracker::Event ev1 = notification_tracker().at(0); 268 EXPECT_EQ(chrome::NOTIFICATION_COMPONENT_UPDATER_STARTED, ev1.type); 269 270 message_loop.Run(); 271 272 ASSERT_EQ(3ul, notification_tracker().size()); 273 TestNotificationTracker::Event ev2 = notification_tracker().at(1); 274 EXPECT_EQ(chrome::NOTIFICATION_COMPONENT_UPDATER_SLEEPING, ev2.type); 275 TestNotificationTracker::Event ev3 = notification_tracker().at(2); 276 EXPECT_EQ(chrome::NOTIFICATION_COMPONENT_UPDATER_SLEEPING, ev2.type); 277 EXPECT_EQ(2, interceptor->hit_count()); 278 279 EXPECT_EQ(0, static_cast<TestInstaller*>(com.installer)->error()); 280 EXPECT_EQ(0, static_cast<TestInstaller*>(com.installer)->install_count()); 281 282 component_updater()->Stop(); 283 284 // Loop twice again but this case we simulate a server error by returning 285 // an empty file. 286 287 interceptor->SetResponse(expected_update_url, 288 header_ok_reply, 289 test_file("updatecheck_reply_empty")); 290 291 notification_tracker().Reset(); 292 test_configurator()->SetLoopCount(2); 293 component_updater()->Start(); 294 295 message_loop.Run(); 296 297 ASSERT_EQ(3ul, notification_tracker().size()); 298 ev1 = notification_tracker().at(0); 299 EXPECT_EQ(chrome::NOTIFICATION_COMPONENT_UPDATER_STARTED, ev1.type); 300 ev2 = notification_tracker().at(1); 301 EXPECT_EQ(chrome::NOTIFICATION_COMPONENT_UPDATER_SLEEPING, ev2.type); 302 ev3 = notification_tracker().at(2); 303 EXPECT_EQ(chrome::NOTIFICATION_COMPONENT_UPDATER_SLEEPING, ev2.type); 304 EXPECT_EQ(4, interceptor->hit_count()); 305 306 EXPECT_EQ(0, static_cast<TestInstaller*>(com.installer)->error()); 307 EXPECT_EQ(0, static_cast<TestInstaller*>(com.installer)->install_count()); 308 309 component_updater()->Stop(); 310} 311 312// Verify that we can check for updates and install one component. Besides 313// the notifications above NOTIFICATION_COMPONENT_UPDATE_FOUND and 314// NOTIFICATION_COMPONENT_UPDATE_READY should have been fired. We do two loops 315// so the second time around there should be nothing left to do. 316// We also check that only 3 network requests are issued: 317// 1- manifest check 318// 2- download crx 319// 3- second manifest check. 320TEST_F(ComponentUpdaterTest, InstallCrx) { 321 MessageLoop message_loop; 322 content::TestBrowserThread ui_thread(BrowserThread::UI, &message_loop); 323 content::TestBrowserThread file_thread(BrowserThread::FILE); 324 content::TestBrowserThread io_thread(BrowserThread::IO); 325 326 io_thread.StartIOThread(); 327 file_thread.Start(); 328 329 scoped_refptr<ComponentUpdateInterceptor> 330 interceptor(new ComponentUpdateInterceptor()); 331 332 CrxComponent com1; 333 RegisterComponent(&com1, kTestComponent_jebg, Version("0.9")); 334 CrxComponent com2; 335 RegisterComponent(&com2, kTestComponent_abag, Version("2.2")); 336 337 const char expected_update_url_1[] = 338 "http://localhost/upd?extra=foo&x=id%3D" 339 "jebgalgnebhfojomionfpkfelancnnkf%26v%3D0.9%26uc&x=id%3D" 340 "abagagagagagagagagagagagagagagag%26v%3D2.2%26uc"; 341 342 const char expected_update_url_2[] = 343 "http://localhost/upd?extra=foo&x=id%3D" 344 "abagagagagagagagagagagagagagagag%26v%3D2.2%26uc&x=id%3D" 345 "jebgalgnebhfojomionfpkfelancnnkf%26v%3D1.0%26uc"; 346 347 interceptor->SetResponse(expected_update_url_1, header_ok_reply, 348 test_file("updatecheck_reply_1.xml")); 349 interceptor->SetResponse(expected_update_url_2, header_ok_reply, 350 test_file("updatecheck_reply_1.xml")); 351 interceptor->SetResponse(expected_crx_url, header_ok_reply, 352 test_file("jebgalgnebhfojomionfpkfelancnnkf.crx")); 353 354 test_configurator()->SetLoopCount(2); 355 356 component_updater()->Start(); 357 message_loop.Run(); 358 359 EXPECT_EQ(0, static_cast<TestInstaller*>(com1.installer)->error()); 360 EXPECT_EQ(1, static_cast<TestInstaller*>(com1.installer)->install_count()); 361 EXPECT_EQ(3, interceptor->hit_count()); 362 363 ASSERT_EQ(5ul, notification_tracker().size()); 364 365 TestNotificationTracker::Event ev1 = notification_tracker().at(1); 366 EXPECT_EQ(chrome::NOTIFICATION_COMPONENT_UPDATE_FOUND, ev1.type); 367 368 TestNotificationTracker::Event ev2 = notification_tracker().at(2); 369 EXPECT_EQ(chrome::NOTIFICATION_COMPONENT_UPDATE_READY, ev2.type); 370 371 TestNotificationTracker::Event ev3 = notification_tracker().at(3); 372 EXPECT_EQ(chrome::NOTIFICATION_COMPONENT_UPDATER_SLEEPING, ev3.type); 373 374 TestNotificationTracker::Event ev4 = notification_tracker().at(4); 375 EXPECT_EQ(chrome::NOTIFICATION_COMPONENT_UPDATER_SLEEPING, ev3.type); 376 377 component_updater()->Stop(); 378} 379 380// This test checks that the "prodversionmin" value is handled correctly. In 381// particular there should not be an install because the minimum product 382// version is much higher than of chrome. 383TEST_F(ComponentUpdaterTest, ProdVersionCheck) { 384 MessageLoop message_loop; 385 content::TestBrowserThread ui_thread(BrowserThread::UI, &message_loop); 386 content::TestBrowserThread file_thread(BrowserThread::FILE); 387 content::TestBrowserThread io_thread(BrowserThread::IO); 388 389 io_thread.StartIOThread(); 390 file_thread.Start(); 391 392 scoped_refptr<ComponentUpdateInterceptor> 393 interceptor(new ComponentUpdateInterceptor()); 394 395 CrxComponent com; 396 RegisterComponent(&com, kTestComponent_jebg, Version("0.9")); 397 398 const char expected_update_url[] = 399 "http://localhost/upd?extra=foo&x=id%3D" 400 "jebgalgnebhfojomionfpkfelancnnkf%26v%3D0.9%26uc"; 401 402 interceptor->SetResponse(expected_update_url, 403 header_ok_reply, 404 test_file("updatecheck_reply_2.xml")); 405 interceptor->SetResponse(expected_crx_url, header_ok_reply, 406 test_file("jebgalgnebhfojomionfpkfelancnnkf.crx")); 407 408 test_configurator()->SetLoopCount(1); 409 component_updater()->Start(); 410 message_loop.Run(); 411 412 EXPECT_EQ(1, interceptor->hit_count()); 413 EXPECT_EQ(0, static_cast<TestInstaller*>(com.installer)->error()); 414 EXPECT_EQ(0, static_cast<TestInstaller*>(com.installer)->install_count()); 415 416 component_updater()->Stop(); 417} 418