// Copyright 2015 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "base/bind.h" #include "base/bind_helpers.h" #include "base/files/file_path.h" #include "base/files/file_util.h" #include "base/location.h" #include "base/memory/ref_counted.h" #include "base/memory/scoped_ptr.h" #include "base/message_loop/message_loop.h" #include "base/path_service.h" #include "base/run_loop.h" #include "base/test/sequenced_worker_pool_owner.h" #include "base/thread_task_runner_handle.h" #include "base/values.h" #include "base/version.h" #include "components/update_client/crx_update_item.h" #include "components/update_client/ping_manager.h" #include "components/update_client/test_configurator.h" #include "components/update_client/test_installer.h" #include "components/update_client/update_checker.h" #include "components/update_client/update_client_internal.h" #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" #include "url/gurl.h" namespace update_client { namespace { using base::FilePath; // Makes a copy of the file specified by |from_path| in a temporary directory // and returns the path of the copy. Returns true if successful. Cleans up if // there was an error creating the copy. bool MakeTestFile(const FilePath& from_path, FilePath* to_path) { FilePath temp_dir; bool result = CreateNewTempDirectory(FILE_PATH_LITERAL("update_client"), &temp_dir); if (!result) return false; FilePath temp_file; result = CreateTemporaryFileInDir(temp_dir, &temp_file); if (!result) return false; result = CopyFile(from_path, temp_file); if (!result) { DeleteFile(temp_file, false); return false; } *to_path = temp_file; return true; } using Events = UpdateClient::Observer::Events; class MockObserver : public UpdateClient::Observer { public: MOCK_METHOD2(OnEvent, void(Events event, const std::string&)); }; class OnDemandTester { public: OnDemandTester(const scoped_refptr& update_client, bool expected_value); void CheckOnDemand(Events event, const std::string&); private: const scoped_refptr update_client_; const bool expected_value_; }; OnDemandTester::OnDemandTester(const scoped_refptr& update_client, bool expected_value) : update_client_(update_client), expected_value_(expected_value) { } void OnDemandTester::CheckOnDemand(Events event, const std::string& id) { if (event == Events::COMPONENT_CHECKING_FOR_UPDATES) { CrxUpdateItem update_item; EXPECT_TRUE(update_client_->GetCrxUpdateState(id, &update_item)); EXPECT_EQ(update_item.on_demand, expected_value_); } } class FakePingManagerImpl : public PingManager { public: explicit FakePingManagerImpl(const Configurator& config); ~FakePingManagerImpl() override; void OnUpdateComplete(const CrxUpdateItem* item) override; const std::vector& items() const; private: std::vector items_; DISALLOW_COPY_AND_ASSIGN(FakePingManagerImpl); }; FakePingManagerImpl::FakePingManagerImpl(const Configurator& config) : PingManager(config) { } FakePingManagerImpl::~FakePingManagerImpl() { } void FakePingManagerImpl::OnUpdateComplete(const CrxUpdateItem* item) { items_.push_back(*item); } const std::vector& FakePingManagerImpl::items() const { return items_; } } // namespace using ::testing::_; using ::testing::AnyNumber; using ::testing::DoAll; using ::testing::InSequence; using ::testing::Invoke; using ::testing::Mock; using ::testing::Return; using std::string; class UpdateClientTest : public testing::Test { public: UpdateClientTest(); ~UpdateClientTest() override; protected: void RunThreads(); void StopWorkerPool(); // Returns the full path to a test file. static base::FilePath TestFilePath(const char* file); scoped_refptr config() { return config_; } base::Closure quit_closure() { return quit_closure_; } private: static const int kNumWorkerThreads_ = 2; base::MessageLoopForUI message_loop_; base::RunLoop runloop_; base::Closure quit_closure_; scoped_ptr worker_pool_; scoped_refptr config_; DISALLOW_COPY_AND_ASSIGN(UpdateClientTest); }; UpdateClientTest::UpdateClientTest() : worker_pool_( new base::SequencedWorkerPoolOwner(kNumWorkerThreads_, "test")) { quit_closure_ = runloop_.QuitClosure(); auto pool = worker_pool_->pool(); config_ = new TestConfigurator( pool->GetSequencedTaskRunner(pool->GetSequenceToken()), message_loop_.task_runner()); } UpdateClientTest::~UpdateClientTest() { } void UpdateClientTest::RunThreads() { runloop_.Run(); } void UpdateClientTest::StopWorkerPool() { worker_pool_->pool()->Shutdown(); } base::FilePath UpdateClientTest::TestFilePath(const char* file) { base::FilePath path; PathService::Get(base::DIR_SOURCE_ROOT, &path); return path.AppendASCII("components") .AppendASCII("test") .AppendASCII("data") .AppendASCII("update_client") .AppendASCII(file); } // Tests the scenario where one update check is done for one CRX. The CRX // has no update. TEST_F(UpdateClientTest, OneCrxNoUpdate) { class DataCallbackFake { public: static void Callback(const std::vector& ids, std::vector* components) { CrxComponent crx; crx.name = "test_jebg"; crx.pk_hash.assign(jebg_hash, jebg_hash + arraysize(jebg_hash)); crx.version = Version("0.9"); crx.installer = new TestInstaller; components->push_back(crx); } }; class CompletionCallbackFake { public: static void Callback(const base::Closure& quit_closure, int error) { EXPECT_EQ(0, error); quit_closure.Run(); } }; class FakeUpdateChecker : public UpdateChecker { public: static scoped_ptr Create(const Configurator& config) { return scoped_ptr(new FakeUpdateChecker()); } bool CheckForUpdates( const std::vector& items_to_check, const std::string& additional_attributes, const UpdateCheckCallback& update_check_callback) override { base::ThreadTaskRunnerHandle::Get()->PostTask( FROM_HERE, base::Bind(update_check_callback, GURL(), 0, "", UpdateResponse::Results())); return true; } }; class FakeCrxDownloader : public CrxDownloader { public: static scoped_ptr Create( bool is_background_download, net::URLRequestContextGetter* context_getter, const scoped_refptr& url_fetcher_task_runner) { return scoped_ptr(new FakeCrxDownloader()); } private: FakeCrxDownloader() : CrxDownloader(scoped_ptr().Pass()) {} ~FakeCrxDownloader() override {} void DoStartDownload(const GURL& url) override { EXPECT_TRUE(false); } }; class FakePingManager : public FakePingManagerImpl { public: explicit FakePingManager(const Configurator& config) : FakePingManagerImpl(config) {} ~FakePingManager() override { EXPECT_TRUE(items().empty()); } }; scoped_ptr ping_manager(new FakePingManager(*config())); scoped_refptr update_client(new UpdateClientImpl( config(), ping_manager.Pass(), &FakeUpdateChecker::Create, &FakeCrxDownloader::Create)); // Verify that calling Update does not set ondemand. OnDemandTester ondemand_tester(update_client, false); MockObserver observer; ON_CALL(observer, OnEvent(_, _)) .WillByDefault(Invoke(&ondemand_tester, &OnDemandTester::CheckOnDemand)); InSequence seq; EXPECT_CALL(observer, OnEvent(Events::COMPONENT_CHECKING_FOR_UPDATES, "jebgalgnebhfojomionfpkfelancnnkf")).Times(1); EXPECT_CALL(observer, OnEvent(Events::COMPONENT_NOT_UPDATED, "jebgalgnebhfojomionfpkfelancnnkf")).Times(1); update_client->AddObserver(&observer); std::vector ids; ids.push_back(std::string("jebgalgnebhfojomionfpkfelancnnkf")); update_client->Update( ids, base::Bind(&DataCallbackFake::Callback), base::Bind(&CompletionCallbackFake::Callback, quit_closure())); RunThreads(); update_client->RemoveObserver(&observer); StopWorkerPool(); } // Tests the scenario where two CRXs are checked for updates. On CRX has // an update, the other CRX does not. TEST_F(UpdateClientTest, TwoCrxUpdateNoUpdate) { class DataCallbackFake { public: static void Callback(const std::vector& ids, std::vector* components) { CrxComponent crx1; crx1.name = "test_jebg"; crx1.pk_hash.assign(jebg_hash, jebg_hash + arraysize(jebg_hash)); crx1.version = Version("0.9"); crx1.installer = new TestInstaller; CrxComponent crx2; crx2.name = "test_abag"; crx2.pk_hash.assign(abag_hash, abag_hash + arraysize(abag_hash)); crx2.version = Version("2.2"); crx2.installer = new TestInstaller; components->push_back(crx1); components->push_back(crx2); } }; class CompletionCallbackFake { public: static void Callback(const base::Closure& quit_closure, int error) { EXPECT_EQ(0, error); quit_closure.Run(); } }; class FakeUpdateChecker : public UpdateChecker { public: static scoped_ptr Create(const Configurator& config) { return scoped_ptr(new FakeUpdateChecker()); } bool CheckForUpdates( const std::vector& items_to_check, const std::string& additional_attributes, const UpdateCheckCallback& update_check_callback) override { /* Fake the following response: */ UpdateResponse::Result::Manifest::Package package; package.name = "jebgalgnebhfojomionfpkfelancnnkf.crx"; UpdateResponse::Result result; result.extension_id = "jebgalgnebhfojomionfpkfelancnnkf"; result.crx_urls.push_back(GURL("http://localhost/download/")); result.manifest.version = "1.0"; result.manifest.browser_min_version = "11.0.1.0"; result.manifest.packages.push_back(package); UpdateResponse::Results results; results.list.push_back(result); base::ThreadTaskRunnerHandle::Get()->PostTask( FROM_HERE, base::Bind(update_check_callback, GURL(), 0, "", results)); return true; } }; class FakeCrxDownloader : public CrxDownloader { public: static scoped_ptr Create( bool is_background_download, net::URLRequestContextGetter* context_getter, const scoped_refptr& url_fetcher_task_runner) { return scoped_ptr(new FakeCrxDownloader()); } private: FakeCrxDownloader() : CrxDownloader(scoped_ptr().Pass()) {} ~FakeCrxDownloader() override {} void DoStartDownload(const GURL& url) override { DownloadMetrics download_metrics; download_metrics.url = url; download_metrics.downloader = DownloadMetrics::kNone; download_metrics.error = 0; download_metrics.downloaded_bytes = 1843; download_metrics.total_bytes = 1843; download_metrics.download_time_ms = 1000; FilePath path; EXPECT_TRUE(MakeTestFile( TestFilePath("jebgalgnebhfojomionfpkfelancnnkf.crx"), &path)); Result result; result.error = 0; result.response = path; result.downloaded_bytes = 1843; result.total_bytes = 1843; base::ThreadTaskRunnerHandle::Get()->PostTask( FROM_HERE, base::Bind(&FakeCrxDownloader::OnDownloadProgress, base::Unretained(this), result)); base::ThreadTaskRunnerHandle::Get()->PostTask( FROM_HERE, base::Bind(&FakeCrxDownloader::OnDownloadComplete, base::Unretained(this), true, result, download_metrics)); } }; class FakePingManager : public FakePingManagerImpl { public: explicit FakePingManager(const Configurator& config) : FakePingManagerImpl(config) {} ~FakePingManager() override { const auto& ping_items = items(); EXPECT_EQ(1U, ping_items.size()); EXPECT_EQ("jebgalgnebhfojomionfpkfelancnnkf", ping_items[0].id); EXPECT_TRUE(base::Version("0.9").Equals(ping_items[0].previous_version)); EXPECT_TRUE(base::Version("1.0").Equals(ping_items[0].next_version)); EXPECT_EQ(0, ping_items[0].error_category); EXPECT_EQ(0, ping_items[0].error_code); } }; scoped_ptr ping_manager(new FakePingManager(*config())); scoped_refptr update_client(new UpdateClientImpl( config(), ping_manager.Pass(), &FakeUpdateChecker::Create, &FakeCrxDownloader::Create)); MockObserver observer; { InSequence seq; EXPECT_CALL(observer, OnEvent(Events::COMPONENT_CHECKING_FOR_UPDATES, "jebgalgnebhfojomionfpkfelancnnkf")).Times(1); EXPECT_CALL(observer, OnEvent(Events::COMPONENT_UPDATE_FOUND, "jebgalgnebhfojomionfpkfelancnnkf")).Times(1); EXPECT_CALL(observer, OnEvent(Events::COMPONENT_UPDATE_DOWNLOADING, "jebgalgnebhfojomionfpkfelancnnkf")).Times(1); EXPECT_CALL(observer, OnEvent(Events::COMPONENT_UPDATE_READY, "jebgalgnebhfojomionfpkfelancnnkf")).Times(1); EXPECT_CALL(observer, OnEvent(Events::COMPONENT_UPDATED, "jebgalgnebhfojomionfpkfelancnnkf")).Times(1); } { InSequence seq; EXPECT_CALL(observer, OnEvent(Events::COMPONENT_CHECKING_FOR_UPDATES, "abagagagagagagagagagagagagagagag")).Times(1); EXPECT_CALL(observer, OnEvent(Events::COMPONENT_NOT_UPDATED, "abagagagagagagagagagagagagagagag")).Times(1); } update_client->AddObserver(&observer); std::vector ids; ids.push_back(std::string("jebgalgnebhfojomionfpkfelancnnkf")); ids.push_back(std::string("abagagagagagagagagagagagagagagag")); update_client->Update( ids, base::Bind(&DataCallbackFake::Callback), base::Bind(&CompletionCallbackFake::Callback, quit_closure())); RunThreads(); update_client->RemoveObserver(&observer); StopWorkerPool(); } // Tests the update check for two CRXs scenario. Both CRXs have updates. TEST_F(UpdateClientTest, TwoCrxUpdate) { class DataCallbackFake { public: static void Callback(const std::vector& ids, std::vector* components) { CrxComponent crx1; crx1.name = "test_jebg"; crx1.pk_hash.assign(jebg_hash, jebg_hash + arraysize(jebg_hash)); crx1.version = Version("0.9"); crx1.installer = new TestInstaller; CrxComponent crx2; crx2.name = "test_ihfo"; crx2.pk_hash.assign(ihfo_hash, ihfo_hash + arraysize(ihfo_hash)); crx2.version = Version("0.8"); crx2.installer = new TestInstaller; components->push_back(crx1); components->push_back(crx2); } }; class CompletionCallbackFake { public: static void Callback(const base::Closure& quit_closure, int error) { EXPECT_EQ(0, error); quit_closure.Run(); } }; class FakeUpdateChecker : public UpdateChecker { public: static scoped_ptr Create(const Configurator& config) { return scoped_ptr(new FakeUpdateChecker()); } bool CheckForUpdates( const std::vector& items_to_check, const std::string& additional_attributes, const UpdateCheckCallback& update_check_callback) override { /* Fake the following response: */ UpdateResponse::Result::Manifest::Package package1; package1.name = "jebgalgnebhfojomionfpkfelancnnkf.crx"; UpdateResponse::Result result1; result1.extension_id = "jebgalgnebhfojomionfpkfelancnnkf"; result1.crx_urls.push_back(GURL("http://localhost/download/")); result1.manifest.version = "1.0"; result1.manifest.browser_min_version = "11.0.1.0"; result1.manifest.packages.push_back(package1); UpdateResponse::Result::Manifest::Package package2; package2.name = "ihfokbkgjpifnbbojhneepfflplebdkc_1.crx"; UpdateResponse::Result result2; result2.extension_id = "ihfokbkgjpifnbbojhneepfflplebdkc"; result2.crx_urls.push_back(GURL("http://localhost/download/")); result2.manifest.version = "1.0"; result2.manifest.browser_min_version = "11.0.1.0"; result2.manifest.packages.push_back(package2); UpdateResponse::Results results; results.list.push_back(result1); results.list.push_back(result2); base::ThreadTaskRunnerHandle::Get()->PostTask( FROM_HERE, base::Bind(update_check_callback, GURL(), 0, "", results)); return true; } }; class FakeCrxDownloader : public CrxDownloader { public: static scoped_ptr Create( bool is_background_download, net::URLRequestContextGetter* context_getter, const scoped_refptr& url_fetcher_task_runner) { return scoped_ptr(new FakeCrxDownloader()); } private: FakeCrxDownloader() : CrxDownloader(scoped_ptr().Pass()) {} ~FakeCrxDownloader() override {} void DoStartDownload(const GURL& url) override { DownloadMetrics download_metrics; FilePath path; Result result; if (url.path() == "/download/jebgalgnebhfojomionfpkfelancnnkf.crx") { download_metrics.url = url; download_metrics.downloader = DownloadMetrics::kNone; download_metrics.error = 0; download_metrics.downloaded_bytes = 1843; download_metrics.total_bytes = 1843; download_metrics.download_time_ms = 1000; EXPECT_TRUE(MakeTestFile( TestFilePath("jebgalgnebhfojomionfpkfelancnnkf.crx"), &path)); result.error = 0; result.response = path; result.downloaded_bytes = 1843; result.total_bytes = 1843; } else if (url.path() == "/download/ihfokbkgjpifnbbojhneepfflplebdkc_1.crx") { download_metrics.url = url; download_metrics.downloader = DownloadMetrics::kNone; download_metrics.error = 0; download_metrics.downloaded_bytes = 53638; download_metrics.total_bytes = 53638; download_metrics.download_time_ms = 2000; EXPECT_TRUE(MakeTestFile( TestFilePath("ihfokbkgjpifnbbojhneepfflplebdkc_1.crx"), &path)); result.error = 0; result.response = path; result.downloaded_bytes = 53638; result.total_bytes = 53638; } else { NOTREACHED(); } base::ThreadTaskRunnerHandle::Get()->PostTask( FROM_HERE, base::Bind(&FakeCrxDownloader::OnDownloadProgress, base::Unretained(this), result)); base::ThreadTaskRunnerHandle::Get()->PostTask( FROM_HERE, base::Bind(&FakeCrxDownloader::OnDownloadComplete, base::Unretained(this), true, result, download_metrics)); } }; class FakePingManager : public FakePingManagerImpl { public: explicit FakePingManager(const Configurator& config) : FakePingManagerImpl(config) {} ~FakePingManager() override { const auto& ping_items = items(); EXPECT_EQ(2U, ping_items.size()); EXPECT_EQ("jebgalgnebhfojomionfpkfelancnnkf", ping_items[0].id); EXPECT_TRUE(base::Version("0.9").Equals(ping_items[0].previous_version)); EXPECT_TRUE(base::Version("1.0").Equals(ping_items[0].next_version)); EXPECT_EQ(0, ping_items[0].error_category); EXPECT_EQ(0, ping_items[0].error_code); EXPECT_EQ("ihfokbkgjpifnbbojhneepfflplebdkc", ping_items[1].id); EXPECT_TRUE(base::Version("0.8").Equals(ping_items[1].previous_version)); EXPECT_TRUE(base::Version("1.0").Equals(ping_items[1].next_version)); EXPECT_EQ(0, ping_items[1].error_category); EXPECT_EQ(0, ping_items[1].error_code); } }; scoped_ptr ping_manager(new FakePingManager(*config())); scoped_refptr update_client(new UpdateClientImpl( config(), ping_manager.Pass(), &FakeUpdateChecker::Create, &FakeCrxDownloader::Create)); MockObserver observer; { InSequence seq; EXPECT_CALL(observer, OnEvent(Events::COMPONENT_CHECKING_FOR_UPDATES, "jebgalgnebhfojomionfpkfelancnnkf")).Times(1); EXPECT_CALL(observer, OnEvent(Events::COMPONENT_UPDATE_FOUND, "jebgalgnebhfojomionfpkfelancnnkf")).Times(1); EXPECT_CALL(observer, OnEvent(Events::COMPONENT_UPDATE_DOWNLOADING, "jebgalgnebhfojomionfpkfelancnnkf")).Times(1); EXPECT_CALL(observer, OnEvent(Events::COMPONENT_UPDATE_READY, "jebgalgnebhfojomionfpkfelancnnkf")).Times(1); EXPECT_CALL(observer, OnEvent(Events::COMPONENT_UPDATED, "jebgalgnebhfojomionfpkfelancnnkf")).Times(1); } { InSequence seq; EXPECT_CALL(observer, OnEvent(Events::COMPONENT_CHECKING_FOR_UPDATES, "ihfokbkgjpifnbbojhneepfflplebdkc")).Times(1); EXPECT_CALL(observer, OnEvent(Events::COMPONENT_UPDATE_FOUND, "ihfokbkgjpifnbbojhneepfflplebdkc")).Times(1); EXPECT_CALL(observer, OnEvent(Events::COMPONENT_WAIT, "ihfokbkgjpifnbbojhneepfflplebdkc")).Times(1); EXPECT_CALL(observer, OnEvent(Events::COMPONENT_UPDATE_DOWNLOADING, "ihfokbkgjpifnbbojhneepfflplebdkc")).Times(1); EXPECT_CALL(observer, OnEvent(Events::COMPONENT_UPDATE_READY, "ihfokbkgjpifnbbojhneepfflplebdkc")).Times(1); EXPECT_CALL(observer, OnEvent(Events::COMPONENT_UPDATED, "ihfokbkgjpifnbbojhneepfflplebdkc")).Times(1); } update_client->AddObserver(&observer); std::vector ids; ids.push_back(std::string("jebgalgnebhfojomionfpkfelancnnkf")); ids.push_back(std::string("ihfokbkgjpifnbbojhneepfflplebdkc")); update_client->Update( ids, base::Bind(&DataCallbackFake::Callback), base::Bind(&CompletionCallbackFake::Callback, quit_closure())); RunThreads(); update_client->RemoveObserver(&observer); StopWorkerPool(); } // Tests the scenario where there is a download timeout for the first // CRX. The update for the first CRX fails. The update client waits before // attempting the update for the second CRX. This update succeeds. TEST_F(UpdateClientTest, TwoCrxUpdateDownloadTimeout) { class DataCallbackFake { public: static void Callback(const std::vector& ids, std::vector* components) { CrxComponent crx1; crx1.name = "test_jebg"; crx1.pk_hash.assign(jebg_hash, jebg_hash + arraysize(jebg_hash)); crx1.version = Version("0.9"); crx1.installer = new TestInstaller; CrxComponent crx2; crx2.name = "test_ihfo"; crx2.pk_hash.assign(ihfo_hash, ihfo_hash + arraysize(ihfo_hash)); crx2.version = Version("0.8"); crx2.installer = new TestInstaller; components->push_back(crx1); components->push_back(crx2); } }; class CompletionCallbackFake { public: static void Callback(const base::Closure& quit_closure, int error) { EXPECT_EQ(0, error); quit_closure.Run(); } }; class FakeUpdateChecker : public UpdateChecker { public: static scoped_ptr Create(const Configurator& config) { return scoped_ptr(new FakeUpdateChecker()); } bool CheckForUpdates( const std::vector& items_to_check, const std::string& additional_attributes, const UpdateCheckCallback& update_check_callback) override { /* Fake the following response: */ UpdateResponse::Result::Manifest::Package package1; package1.name = "jebgalgnebhfojomionfpkfelancnnkf.crx"; UpdateResponse::Result result1; result1.extension_id = "jebgalgnebhfojomionfpkfelancnnkf"; result1.crx_urls.push_back(GURL("http://localhost/download/")); result1.manifest.version = "1.0"; result1.manifest.browser_min_version = "11.0.1.0"; result1.manifest.packages.push_back(package1); UpdateResponse::Result::Manifest::Package package2; package2.name = "ihfokbkgjpifnbbojhneepfflplebdkc_1.crx"; UpdateResponse::Result result2; result2.extension_id = "ihfokbkgjpifnbbojhneepfflplebdkc"; result2.crx_urls.push_back(GURL("http://localhost/download/")); result2.manifest.version = "1.0"; result2.manifest.browser_min_version = "11.0.1.0"; result2.manifest.packages.push_back(package2); UpdateResponse::Results results; results.list.push_back(result1); results.list.push_back(result2); base::ThreadTaskRunnerHandle::Get()->PostTask( FROM_HERE, base::Bind(update_check_callback, GURL(), 0, "", results)); return true; } }; class FakeCrxDownloader : public CrxDownloader { public: static scoped_ptr Create( bool is_background_download, net::URLRequestContextGetter* context_getter, const scoped_refptr& url_fetcher_task_runner) { return scoped_ptr(new FakeCrxDownloader()); } private: FakeCrxDownloader() : CrxDownloader(scoped_ptr().Pass()) {} ~FakeCrxDownloader() override {} void DoStartDownload(const GURL& url) override { DownloadMetrics download_metrics; FilePath path; Result result; if (url.path() == "/download/jebgalgnebhfojomionfpkfelancnnkf.crx") { download_metrics.url = url; download_metrics.downloader = DownloadMetrics::kNone; download_metrics.error = -118; download_metrics.downloaded_bytes = 0; download_metrics.total_bytes = 0; download_metrics.download_time_ms = 1000; EXPECT_TRUE(MakeTestFile( TestFilePath("jebgalgnebhfojomionfpkfelancnnkf.crx"), &path)); result.error = -118; result.response = path; result.downloaded_bytes = 0; result.total_bytes = 0; } else if (url.path() == "/download/ihfokbkgjpifnbbojhneepfflplebdkc_1.crx") { download_metrics.url = url; download_metrics.downloader = DownloadMetrics::kNone; download_metrics.error = 0; download_metrics.downloaded_bytes = 53638; download_metrics.total_bytes = 53638; download_metrics.download_time_ms = 2000; EXPECT_TRUE(MakeTestFile( TestFilePath("ihfokbkgjpifnbbojhneepfflplebdkc_1.crx"), &path)); result.error = 0; result.response = path; result.downloaded_bytes = 53638; result.total_bytes = 53638; } else { NOTREACHED(); } base::ThreadTaskRunnerHandle::Get()->PostTask( FROM_HERE, base::Bind(&FakeCrxDownloader::OnDownloadProgress, base::Unretained(this), result)); base::ThreadTaskRunnerHandle::Get()->PostTask( FROM_HERE, base::Bind(&FakeCrxDownloader::OnDownloadComplete, base::Unretained(this), true, result, download_metrics)); } }; class FakePingManager : public FakePingManagerImpl { public: explicit FakePingManager(const Configurator& config) : FakePingManagerImpl(config) {} ~FakePingManager() override { const auto& ping_items = items(); EXPECT_EQ(2U, ping_items.size()); EXPECT_EQ("jebgalgnebhfojomionfpkfelancnnkf", ping_items[0].id); EXPECT_TRUE(base::Version("0.9").Equals(ping_items[0].previous_version)); EXPECT_TRUE(base::Version("1.0").Equals(ping_items[0].next_version)); EXPECT_EQ(1, ping_items[0].error_category); // Network error. EXPECT_EQ(-118, ping_items[0].error_code); EXPECT_EQ("ihfokbkgjpifnbbojhneepfflplebdkc", ping_items[1].id); EXPECT_TRUE(base::Version("0.8").Equals(ping_items[1].previous_version)); EXPECT_TRUE(base::Version("1.0").Equals(ping_items[1].next_version)); EXPECT_EQ(0, ping_items[1].error_category); EXPECT_EQ(0, ping_items[1].error_code); } }; scoped_ptr ping_manager(new FakePingManager(*config())); scoped_refptr update_client(new UpdateClientImpl( config(), ping_manager.Pass(), &FakeUpdateChecker::Create, &FakeCrxDownloader::Create)); MockObserver observer; { InSequence seq; EXPECT_CALL(observer, OnEvent(Events::COMPONENT_CHECKING_FOR_UPDATES, "jebgalgnebhfojomionfpkfelancnnkf")).Times(1); EXPECT_CALL(observer, OnEvent(Events::COMPONENT_UPDATE_FOUND, "jebgalgnebhfojomionfpkfelancnnkf")).Times(1); EXPECT_CALL(observer, OnEvent(Events::COMPONENT_UPDATE_DOWNLOADING, "jebgalgnebhfojomionfpkfelancnnkf")).Times(1); EXPECT_CALL(observer, OnEvent(Events::COMPONENT_NOT_UPDATED, "jebgalgnebhfojomionfpkfelancnnkf")).Times(1); } { InSequence seq; EXPECT_CALL(observer, OnEvent(Events::COMPONENT_CHECKING_FOR_UPDATES, "ihfokbkgjpifnbbojhneepfflplebdkc")).Times(1); EXPECT_CALL(observer, OnEvent(Events::COMPONENT_UPDATE_FOUND, "ihfokbkgjpifnbbojhneepfflplebdkc")).Times(1); EXPECT_CALL(observer, OnEvent(Events::COMPONENT_WAIT, "ihfokbkgjpifnbbojhneepfflplebdkc")).Times(1); EXPECT_CALL(observer, OnEvent(Events::COMPONENT_UPDATE_DOWNLOADING, "ihfokbkgjpifnbbojhneepfflplebdkc")).Times(1); EXPECT_CALL(observer, OnEvent(Events::COMPONENT_UPDATE_READY, "ihfokbkgjpifnbbojhneepfflplebdkc")).Times(1); EXPECT_CALL(observer, OnEvent(Events::COMPONENT_UPDATED, "ihfokbkgjpifnbbojhneepfflplebdkc")).Times(1); } update_client->AddObserver(&observer); std::vector ids; ids.push_back(std::string("jebgalgnebhfojomionfpkfelancnnkf")); ids.push_back(std::string("ihfokbkgjpifnbbojhneepfflplebdkc")); update_client->Update( ids, base::Bind(&DataCallbackFake::Callback), base::Bind(&CompletionCallbackFake::Callback, quit_closure())); RunThreads(); update_client->RemoveObserver(&observer); StopWorkerPool(); } // Tests the differential update scenario for one CRX. TEST_F(UpdateClientTest, OneCrxDiffUpdate) { class DataCallbackFake { public: static void Callback(const std::vector& ids, std::vector* components) { static int num_calls = 0; // Must use the same stateful installer object. static scoped_refptr installer( new VersionedTestInstaller()); ++num_calls; CrxComponent crx; crx.name = "test_ihfo"; crx.pk_hash.assign(ihfo_hash, ihfo_hash + arraysize(ihfo_hash)); crx.installer = installer; if (num_calls == 1) { crx.version = Version("0.8"); } else if (num_calls == 2) { crx.version = Version("1.0"); } else { NOTREACHED(); } components->push_back(crx); } }; class CompletionCallbackFake { public: static void Callback(const base::Closure& quit_closure, int error) { EXPECT_EQ(0, error); quit_closure.Run(); } }; class FakeUpdateChecker : public UpdateChecker { public: static scoped_ptr Create(const Configurator& config) { return scoped_ptr(new FakeUpdateChecker()); } bool CheckForUpdates( const std::vector& items_to_check, const std::string& additional_attributes, const UpdateCheckCallback& update_check_callback) override { static int num_call = 0; ++num_call; UpdateResponse::Results results; if (num_call == 1) { /* Fake the following response: */ UpdateResponse::Result::Manifest::Package package; package.name = "ihfokbkgjpifnbbojhneepfflplebdkc_1.crx"; package.fingerprint = "1"; UpdateResponse::Result result; result.extension_id = "ihfokbkgjpifnbbojhneepfflplebdkc"; result.crx_urls.push_back(GURL("http://localhost/download/")); result.manifest.version = "1.0"; result.manifest.browser_min_version = "11.0.1.0"; result.manifest.packages.push_back(package); results.list.push_back(result); } else if (num_call == 2) { /* Fake the following response: */ UpdateResponse::Result::Manifest::Package package; package.name = "ihfokbkgjpifnbbojhneepfflplebdkc_2.crx"; package.namediff = "ihfokbkgjpifnbbojhneepfflplebdkc_1to2.crx"; package.fingerprint = "22"; UpdateResponse::Result result; result.extension_id = "ihfokbkgjpifnbbojhneepfflplebdkc"; result.crx_urls.push_back(GURL("http://localhost/download/")); result.crx_diffurls.push_back(GURL("http://localhost/download/")); result.manifest.version = "2.0"; result.manifest.browser_min_version = "11.0.1.0"; result.manifest.packages.push_back(package); results.list.push_back(result); } else { NOTREACHED(); } base::ThreadTaskRunnerHandle::Get()->PostTask( FROM_HERE, base::Bind(update_check_callback, GURL(), 0, "", results)); return true; } }; class FakeCrxDownloader : public CrxDownloader { public: static scoped_ptr Create( bool is_background_download, net::URLRequestContextGetter* context_getter, const scoped_refptr& url_fetcher_task_runner) { return scoped_ptr(new FakeCrxDownloader()); } private: FakeCrxDownloader() : CrxDownloader(scoped_ptr().Pass()) {} ~FakeCrxDownloader() override {} void DoStartDownload(const GURL& url) override { DownloadMetrics download_metrics; FilePath path; Result result; if (url.path() == "/download/ihfokbkgjpifnbbojhneepfflplebdkc_1.crx") { download_metrics.url = url; download_metrics.downloader = DownloadMetrics::kNone; download_metrics.error = 0; download_metrics.downloaded_bytes = 53638; download_metrics.total_bytes = 53638; download_metrics.download_time_ms = 2000; EXPECT_TRUE(MakeTestFile( TestFilePath("ihfokbkgjpifnbbojhneepfflplebdkc_1.crx"), &path)); result.error = 0; result.response = path; result.downloaded_bytes = 53638; result.total_bytes = 53638; } else if (url.path() == "/download/ihfokbkgjpifnbbojhneepfflplebdkc_1to2.crx") { download_metrics.url = url; download_metrics.downloader = DownloadMetrics::kNone; download_metrics.error = 0; download_metrics.downloaded_bytes = 2105; download_metrics.total_bytes = 2105; download_metrics.download_time_ms = 1000; EXPECT_TRUE(MakeTestFile( TestFilePath("ihfokbkgjpifnbbojhneepfflplebdkc_1to2.crx"), &path)); result.error = 0; result.response = path; result.downloaded_bytes = 2105; result.total_bytes = 2105; } else { NOTREACHED(); } base::ThreadTaskRunnerHandle::Get()->PostTask( FROM_HERE, base::Bind(&FakeCrxDownloader::OnDownloadProgress, base::Unretained(this), result)); base::ThreadTaskRunnerHandle::Get()->PostTask( FROM_HERE, base::Bind(&FakeCrxDownloader::OnDownloadComplete, base::Unretained(this), true, result, download_metrics)); } }; class FakePingManager : public FakePingManagerImpl { public: explicit FakePingManager(const Configurator& config) : FakePingManagerImpl(config) {} ~FakePingManager() override { const auto& ping_items = items(); EXPECT_EQ(2U, ping_items.size()); EXPECT_EQ("ihfokbkgjpifnbbojhneepfflplebdkc", ping_items[0].id); EXPECT_TRUE(base::Version("0.8").Equals(ping_items[0].previous_version)); EXPECT_TRUE(base::Version("1.0").Equals(ping_items[0].next_version)); EXPECT_EQ(0, ping_items[0].error_category); EXPECT_EQ(0, ping_items[0].error_code); EXPECT_EQ("ihfokbkgjpifnbbojhneepfflplebdkc", ping_items[1].id); EXPECT_TRUE(base::Version("1.0").Equals(ping_items[1].previous_version)); EXPECT_TRUE(base::Version("2.0").Equals(ping_items[1].next_version)); EXPECT_EQ(0, ping_items[1].diff_error_category); EXPECT_EQ(0, ping_items[1].diff_error_code); } }; scoped_ptr ping_manager(new FakePingManager(*config())); scoped_refptr update_client(new UpdateClientImpl( config(), ping_manager.Pass(), &FakeUpdateChecker::Create, &FakeCrxDownloader::Create)); MockObserver observer; { InSequence seq; EXPECT_CALL(observer, OnEvent(Events::COMPONENT_CHECKING_FOR_UPDATES, "ihfokbkgjpifnbbojhneepfflplebdkc")).Times(1); EXPECT_CALL(observer, OnEvent(Events::COMPONENT_UPDATE_FOUND, "ihfokbkgjpifnbbojhneepfflplebdkc")).Times(1); EXPECT_CALL(observer, OnEvent(Events::COMPONENT_UPDATE_DOWNLOADING, "ihfokbkgjpifnbbojhneepfflplebdkc")).Times(1); EXPECT_CALL(observer, OnEvent(Events::COMPONENT_UPDATE_READY, "ihfokbkgjpifnbbojhneepfflplebdkc")).Times(1); EXPECT_CALL(observer, OnEvent(Events::COMPONENT_UPDATED, "ihfokbkgjpifnbbojhneepfflplebdkc")).Times(1); EXPECT_CALL(observer, OnEvent(Events::COMPONENT_CHECKING_FOR_UPDATES, "ihfokbkgjpifnbbojhneepfflplebdkc")).Times(1); EXPECT_CALL(observer, OnEvent(Events::COMPONENT_UPDATE_FOUND, "ihfokbkgjpifnbbojhneepfflplebdkc")).Times(1); EXPECT_CALL(observer, OnEvent(Events::COMPONENT_UPDATE_DOWNLOADING, "ihfokbkgjpifnbbojhneepfflplebdkc")).Times(1); EXPECT_CALL(observer, OnEvent(Events::COMPONENT_UPDATE_READY, "ihfokbkgjpifnbbojhneepfflplebdkc")).Times(1); EXPECT_CALL(observer, OnEvent(Events::COMPONENT_UPDATED, "ihfokbkgjpifnbbojhneepfflplebdkc")).Times(1); } update_client->AddObserver(&observer); std::vector ids; ids.push_back(std::string("ihfokbkgjpifnbbojhneepfflplebdkc")); { base::RunLoop runloop; update_client->Update( ids, base::Bind(&DataCallbackFake::Callback), base::Bind(&CompletionCallbackFake::Callback, runloop.QuitClosure())); runloop.Run(); } { base::RunLoop runloop; update_client->Update( ids, base::Bind(&DataCallbackFake::Callback), base::Bind(&CompletionCallbackFake::Callback, runloop.QuitClosure())); runloop.Run(); } update_client->RemoveObserver(&observer); StopWorkerPool(); } // Tests the update scenario for one CRX where the CRX installer returns // an error. TEST_F(UpdateClientTest, OneCrxInstallError) { class MockInstaller : public CrxInstaller { public: MOCK_METHOD1(OnUpdateError, void(int error)); MOCK_METHOD2(Install, bool(const base::DictionaryValue& manifest, const base::FilePath& unpack_path)); MOCK_METHOD2(GetInstalledFile, bool(const std::string& file, base::FilePath* installed_file)); MOCK_METHOD0(Uninstall, bool()); static void OnInstall(const base::DictionaryValue& manifest, const base::FilePath& unpack_path) { base::DeleteFile(unpack_path, true); } protected: ~MockInstaller() override {} }; class DataCallbackFake { public: static void Callback(const std::vector& ids, std::vector* components) { scoped_refptr installer(new MockInstaller()); EXPECT_CALL(*installer, OnUpdateError(_)).Times(0); EXPECT_CALL(*installer, Install(_, _)) .WillOnce(DoAll(Invoke(MockInstaller::OnInstall), Return(false))); EXPECT_CALL(*installer, GetInstalledFile(_, _)).Times(0); EXPECT_CALL(*installer, Uninstall()).Times(0); CrxComponent crx; crx.name = "test_jebg"; crx.pk_hash.assign(jebg_hash, jebg_hash + arraysize(jebg_hash)); crx.version = Version("0.9"); crx.installer = installer; components->push_back(crx); } }; class CompletionCallbackFake { public: static void Callback(const base::Closure& quit_closure, int error) { EXPECT_EQ(0, error); quit_closure.Run(); } }; class FakeUpdateChecker : public UpdateChecker { public: static scoped_ptr Create(const Configurator& config) { return scoped_ptr(new FakeUpdateChecker()); } bool CheckForUpdates( const std::vector& items_to_check, const std::string& additional_attributes, const UpdateCheckCallback& update_check_callback) override { /* Fake the following response: */ UpdateResponse::Result::Manifest::Package package; package.name = "jebgalgnebhfojomionfpkfelancnnkf.crx"; UpdateResponse::Result result; result.extension_id = "jebgalgnebhfojomionfpkfelancnnkf"; result.crx_urls.push_back(GURL("http://localhost/download/")); result.manifest.version = "1.0"; result.manifest.browser_min_version = "11.0.1.0"; result.manifest.packages.push_back(package); UpdateResponse::Results results; results.list.push_back(result); base::ThreadTaskRunnerHandle::Get()->PostTask( FROM_HERE, base::Bind(update_check_callback, GURL(), 0, "", results)); return true; } }; class FakeCrxDownloader : public CrxDownloader { public: static scoped_ptr Create( bool is_background_download, net::URLRequestContextGetter* context_getter, const scoped_refptr& url_fetcher_task_runner) { return scoped_ptr(new FakeCrxDownloader()); } private: FakeCrxDownloader() : CrxDownloader(scoped_ptr().Pass()) {} ~FakeCrxDownloader() override {} void DoStartDownload(const GURL& url) override { DownloadMetrics download_metrics; download_metrics.url = url; download_metrics.downloader = DownloadMetrics::kNone; download_metrics.error = 0; download_metrics.downloaded_bytes = 1843; download_metrics.total_bytes = 1843; download_metrics.download_time_ms = 1000; FilePath path; EXPECT_TRUE(MakeTestFile( TestFilePath("jebgalgnebhfojomionfpkfelancnnkf.crx"), &path)); Result result; result.error = 0; result.response = path; result.downloaded_bytes = 1843; result.total_bytes = 1843; base::ThreadTaskRunnerHandle::Get()->PostTask( FROM_HERE, base::Bind(&FakeCrxDownloader::OnDownloadProgress, base::Unretained(this), result)); base::ThreadTaskRunnerHandle::Get()->PostTask( FROM_HERE, base::Bind(&FakeCrxDownloader::OnDownloadComplete, base::Unretained(this), true, result, download_metrics)); } }; class FakePingManager : public FakePingManagerImpl { public: explicit FakePingManager(const Configurator& config) : FakePingManagerImpl(config) {} ~FakePingManager() override { const auto& ping_items = items(); EXPECT_EQ(1U, ping_items.size()); EXPECT_EQ("jebgalgnebhfojomionfpkfelancnnkf", ping_items[0].id); EXPECT_TRUE(base::Version("0.9").Equals(ping_items[0].previous_version)); EXPECT_TRUE(base::Version("1.0").Equals(ping_items[0].next_version)); EXPECT_EQ(3, ping_items[0].error_category); // kInstallError. EXPECT_EQ(9, ping_items[0].error_code); // kInstallerError. } }; scoped_ptr ping_manager(new FakePingManager(*config())); scoped_refptr update_client(new UpdateClientImpl( config(), ping_manager.Pass(), &FakeUpdateChecker::Create, &FakeCrxDownloader::Create)); MockObserver observer; { InSequence seq; EXPECT_CALL(observer, OnEvent(Events::COMPONENT_CHECKING_FOR_UPDATES, "jebgalgnebhfojomionfpkfelancnnkf")).Times(1); EXPECT_CALL(observer, OnEvent(Events::COMPONENT_UPDATE_FOUND, "jebgalgnebhfojomionfpkfelancnnkf")).Times(1); EXPECT_CALL(observer, OnEvent(Events::COMPONENT_UPDATE_DOWNLOADING, "jebgalgnebhfojomionfpkfelancnnkf")).Times(1); EXPECT_CALL(observer, OnEvent(Events::COMPONENT_UPDATE_READY, "jebgalgnebhfojomionfpkfelancnnkf")).Times(1); EXPECT_CALL(observer, OnEvent(Events::COMPONENT_NOT_UPDATED, "jebgalgnebhfojomionfpkfelancnnkf")).Times(1); } update_client->AddObserver(&observer); std::vector ids; ids.push_back(std::string("jebgalgnebhfojomionfpkfelancnnkf")); update_client->Update( ids, base::Bind(&DataCallbackFake::Callback), base::Bind(&CompletionCallbackFake::Callback, quit_closure())); RunThreads(); update_client->RemoveObserver(&observer); StopWorkerPool(); } // Tests the fallback from differential to full update scenario for one CRX. TEST_F(UpdateClientTest, OneCrxDiffUpdateFailsFullUpdateSucceeds) { class DataCallbackFake { public: static void Callback(const std::vector& ids, std::vector* components) { static int num_calls = 0; // Must use the same stateful installer object. static scoped_refptr installer( new VersionedTestInstaller()); ++num_calls; CrxComponent crx; crx.name = "test_ihfo"; crx.pk_hash.assign(ihfo_hash, ihfo_hash + arraysize(ihfo_hash)); crx.installer = installer; if (num_calls == 1) { crx.version = Version("0.8"); } else if (num_calls == 2) { crx.version = Version("1.0"); } else { NOTREACHED(); } components->push_back(crx); } }; class CompletionCallbackFake { public: static void Callback(const base::Closure& quit_closure, int error) { EXPECT_EQ(0, error); quit_closure.Run(); } }; class FakeUpdateChecker : public UpdateChecker { public: static scoped_ptr Create(const Configurator& config) { return scoped_ptr(new FakeUpdateChecker()); } bool CheckForUpdates( const std::vector& items_to_check, const std::string& additional_attributes, const UpdateCheckCallback& update_check_callback) override { static int num_call = 0; ++num_call; UpdateResponse::Results results; if (num_call == 1) { /* Fake the following response: */ UpdateResponse::Result::Manifest::Package package; package.name = "ihfokbkgjpifnbbojhneepfflplebdkc_1.crx"; package.fingerprint = "1"; UpdateResponse::Result result; result.extension_id = "ihfokbkgjpifnbbojhneepfflplebdkc"; result.crx_urls.push_back(GURL("http://localhost/download/")); result.manifest.version = "1.0"; result.manifest.browser_min_version = "11.0.1.0"; result.manifest.packages.push_back(package); results.list.push_back(result); } else if (num_call == 2) { /* Fake the following response: */ UpdateResponse::Result::Manifest::Package package; package.name = "ihfokbkgjpifnbbojhneepfflplebdkc_2.crx"; package.namediff = "ihfokbkgjpifnbbojhneepfflplebdkc_1to2.crx"; package.fingerprint = "22"; UpdateResponse::Result result; result.extension_id = "ihfokbkgjpifnbbojhneepfflplebdkc"; result.crx_urls.push_back(GURL("http://localhost/download/")); result.crx_diffurls.push_back(GURL("http://localhost/download/")); result.manifest.version = "2.0"; result.manifest.browser_min_version = "11.0.1.0"; result.manifest.packages.push_back(package); results.list.push_back(result); } else { NOTREACHED(); } base::ThreadTaskRunnerHandle::Get()->PostTask( FROM_HERE, base::Bind(update_check_callback, GURL(), 0, "", results)); return true; } }; class FakeCrxDownloader : public CrxDownloader { public: static scoped_ptr Create( bool is_background_download, net::URLRequestContextGetter* context_getter, const scoped_refptr& url_fetcher_task_runner) { return scoped_ptr(new FakeCrxDownloader()); } private: FakeCrxDownloader() : CrxDownloader(scoped_ptr().Pass()) {} ~FakeCrxDownloader() override {} void DoStartDownload(const GURL& url) override { DownloadMetrics download_metrics; FilePath path; Result result; if (url.path() == "/download/ihfokbkgjpifnbbojhneepfflplebdkc_1.crx") { download_metrics.url = url; download_metrics.downloader = DownloadMetrics::kNone; download_metrics.error = 0; download_metrics.downloaded_bytes = 53638; download_metrics.total_bytes = 53638; download_metrics.download_time_ms = 2000; EXPECT_TRUE(MakeTestFile( TestFilePath("ihfokbkgjpifnbbojhneepfflplebdkc_1.crx"), &path)); result.error = 0; result.response = path; result.downloaded_bytes = 53638; result.total_bytes = 53638; } else if (url.path() == "/download/ihfokbkgjpifnbbojhneepfflplebdkc_1to2.crx") { // A download error is injected on this execution path. download_metrics.url = url; download_metrics.downloader = DownloadMetrics::kNone; download_metrics.error = -1; download_metrics.downloaded_bytes = 0; download_metrics.total_bytes = 2105; download_metrics.download_time_ms = 1000; EXPECT_TRUE(MakeTestFile( TestFilePath("ihfokbkgjpifnbbojhneepfflplebdkc_1to2.crx"), &path)); result.error = -1; result.response = path; result.downloaded_bytes = 0; result.total_bytes = 2105; } else if (url.path() == "/download/ihfokbkgjpifnbbojhneepfflplebdkc_2.crx") { download_metrics.url = url; download_metrics.downloader = DownloadMetrics::kNone; download_metrics.error = 0; download_metrics.downloaded_bytes = 53855; download_metrics.total_bytes = 53855; download_metrics.download_time_ms = 1000; EXPECT_TRUE(MakeTestFile( TestFilePath("ihfokbkgjpifnbbojhneepfflplebdkc_2.crx"), &path)); result.error = 0; result.response = path; result.downloaded_bytes = 53855; result.total_bytes = 53855; } base::ThreadTaskRunnerHandle::Get()->PostTask( FROM_HERE, base::Bind(&FakeCrxDownloader::OnDownloadProgress, base::Unretained(this), result)); base::ThreadTaskRunnerHandle::Get()->PostTask( FROM_HERE, base::Bind(&FakeCrxDownloader::OnDownloadComplete, base::Unretained(this), true, result, download_metrics)); } }; class FakePingManager : public FakePingManagerImpl { public: explicit FakePingManager(const Configurator& config) : FakePingManagerImpl(config) {} ~FakePingManager() override { const auto& ping_items = items(); EXPECT_EQ(2U, ping_items.size()); EXPECT_EQ("ihfokbkgjpifnbbojhneepfflplebdkc", ping_items[0].id); EXPECT_TRUE(base::Version("0.8").Equals(ping_items[0].previous_version)); EXPECT_TRUE(base::Version("1.0").Equals(ping_items[0].next_version)); EXPECT_EQ(0, ping_items[0].error_category); EXPECT_EQ(0, ping_items[0].error_code); EXPECT_EQ("ihfokbkgjpifnbbojhneepfflplebdkc", ping_items[1].id); EXPECT_TRUE(base::Version("1.0").Equals(ping_items[1].previous_version)); EXPECT_TRUE(base::Version("2.0").Equals(ping_items[1].next_version)); EXPECT_TRUE(ping_items[1].diff_update_failed); EXPECT_EQ(1, ping_items[1].diff_error_category); // kNetworkError. EXPECT_EQ(-1, ping_items[1].diff_error_code); } }; scoped_ptr ping_manager(new FakePingManager(*config())); scoped_refptr update_client(new UpdateClientImpl( config(), ping_manager.Pass(), &FakeUpdateChecker::Create, &FakeCrxDownloader::Create)); MockObserver observer; { InSequence seq; EXPECT_CALL(observer, OnEvent(Events::COMPONENT_CHECKING_FOR_UPDATES, "ihfokbkgjpifnbbojhneepfflplebdkc")).Times(1); EXPECT_CALL(observer, OnEvent(Events::COMPONENT_UPDATE_FOUND, "ihfokbkgjpifnbbojhneepfflplebdkc")).Times(1); EXPECT_CALL(observer, OnEvent(Events::COMPONENT_UPDATE_DOWNLOADING, "ihfokbkgjpifnbbojhneepfflplebdkc")).Times(1); EXPECT_CALL(observer, OnEvent(Events::COMPONENT_UPDATE_READY, "ihfokbkgjpifnbbojhneepfflplebdkc")).Times(1); EXPECT_CALL(observer, OnEvent(Events::COMPONENT_UPDATED, "ihfokbkgjpifnbbojhneepfflplebdkc")).Times(1); EXPECT_CALL(observer, OnEvent(Events::COMPONENT_CHECKING_FOR_UPDATES, "ihfokbkgjpifnbbojhneepfflplebdkc")).Times(1); EXPECT_CALL(observer, OnEvent(Events::COMPONENT_UPDATE_FOUND, "ihfokbkgjpifnbbojhneepfflplebdkc")).Times(1); EXPECT_CALL(observer, OnEvent(Events::COMPONENT_UPDATE_DOWNLOADING, "ihfokbkgjpifnbbojhneepfflplebdkc")).Times(1); EXPECT_CALL(observer, OnEvent(Events::COMPONENT_UPDATE_DOWNLOADING, "ihfokbkgjpifnbbojhneepfflplebdkc")).Times(1); EXPECT_CALL(observer, OnEvent(Events::COMPONENT_UPDATE_READY, "ihfokbkgjpifnbbojhneepfflplebdkc")).Times(1); EXPECT_CALL(observer, OnEvent(Events::COMPONENT_UPDATED, "ihfokbkgjpifnbbojhneepfflplebdkc")).Times(1); } update_client->AddObserver(&observer); std::vector ids; ids.push_back(std::string("ihfokbkgjpifnbbojhneepfflplebdkc")); { base::RunLoop runloop; update_client->Update( ids, base::Bind(&DataCallbackFake::Callback), base::Bind(&CompletionCallbackFake::Callback, runloop.QuitClosure())); runloop.Run(); } { base::RunLoop runloop; update_client->Update( ids, base::Bind(&DataCallbackFake::Callback), base::Bind(&CompletionCallbackFake::Callback, runloop.QuitClosure())); runloop.Run(); } update_client->RemoveObserver(&observer); StopWorkerPool(); } // Tests the queuing of update checks. In this scenario, two update checks are // done for one CRX. The second update check call is queued up and will run // after the first check has completed. The CRX has no updates. TEST_F(UpdateClientTest, OneCrxNoUpdateQueuedCall) { class DataCallbackFake { public: static void Callback(const std::vector& ids, std::vector* components) { CrxComponent crx; crx.name = "test_jebg"; crx.pk_hash.assign(jebg_hash, jebg_hash + arraysize(jebg_hash)); crx.version = Version("0.9"); crx.installer = new TestInstaller; components->push_back(crx); } }; class CompletionCallbackFake { public: static void Callback(const base::Closure& quit_closure, int error) { static int num_call = 0; ++num_call; EXPECT_EQ(0, error); if (num_call == 2) quit_closure.Run(); } }; class FakeUpdateChecker : public UpdateChecker { public: static scoped_ptr Create(const Configurator& config) { return scoped_ptr(new FakeUpdateChecker()); } bool CheckForUpdates( const std::vector& items_to_check, const std::string& additional_attributes, const UpdateCheckCallback& update_check_callback) override { base::ThreadTaskRunnerHandle::Get()->PostTask( FROM_HERE, base::Bind(update_check_callback, GURL(), 0, "", UpdateResponse::Results())); return true; } }; class FakeCrxDownloader : public CrxDownloader { public: static scoped_ptr Create( bool is_background_download, net::URLRequestContextGetter* context_getter, const scoped_refptr& url_fetcher_task_runner) { return scoped_ptr(new FakeCrxDownloader()); } private: FakeCrxDownloader() : CrxDownloader(scoped_ptr().Pass()) {} ~FakeCrxDownloader() override {} void DoStartDownload(const GURL& url) override { EXPECT_TRUE(false); } }; class FakePingManager : public FakePingManagerImpl { public: explicit FakePingManager(const Configurator& config) : FakePingManagerImpl(config) {} ~FakePingManager() override { EXPECT_TRUE(items().empty()); } }; scoped_ptr ping_manager(new FakePingManager(*config())); scoped_refptr update_client(new UpdateClientImpl( config(), ping_manager.Pass(), &FakeUpdateChecker::Create, &FakeCrxDownloader::Create)); MockObserver observer; InSequence seq; EXPECT_CALL(observer, OnEvent(Events::COMPONENT_CHECKING_FOR_UPDATES, "jebgalgnebhfojomionfpkfelancnnkf")).Times(1); EXPECT_CALL(observer, OnEvent(Events::COMPONENT_NOT_UPDATED, "jebgalgnebhfojomionfpkfelancnnkf")).Times(1); EXPECT_CALL(observer, OnEvent(Events::COMPONENT_CHECKING_FOR_UPDATES, "jebgalgnebhfojomionfpkfelancnnkf")).Times(1); EXPECT_CALL(observer, OnEvent(Events::COMPONENT_NOT_UPDATED, "jebgalgnebhfojomionfpkfelancnnkf")).Times(1); update_client->AddObserver(&observer); std::vector ids; ids.push_back(std::string("jebgalgnebhfojomionfpkfelancnnkf")); update_client->Update( ids, base::Bind(&DataCallbackFake::Callback), base::Bind(&CompletionCallbackFake::Callback, quit_closure())); update_client->Update( ids, base::Bind(&DataCallbackFake::Callback), base::Bind(&CompletionCallbackFake::Callback, quit_closure())); RunThreads(); update_client->RemoveObserver(&observer); StopWorkerPool(); } // Tests the install of one CRX. TEST_F(UpdateClientTest, OneCrxInstall) { class DataCallbackFake { public: static void Callback(const std::vector& ids, std::vector* components) { CrxComponent crx; crx.name = "test_jebg"; crx.pk_hash.assign(jebg_hash, jebg_hash + arraysize(jebg_hash)); crx.version = Version("0.0"); crx.installer = new TestInstaller; components->push_back(crx); } }; class CompletionCallbackFake { public: static void Callback(const base::Closure& quit_closure, int error) { EXPECT_EQ(0, error); quit_closure.Run(); } }; class FakeUpdateChecker : public UpdateChecker { public: static scoped_ptr Create(const Configurator& config) { return scoped_ptr(new FakeUpdateChecker()); } bool CheckForUpdates( const std::vector& items_to_check, const std::string& additional_attributes, const UpdateCheckCallback& update_check_callback) override { /* Fake the following response: */ UpdateResponse::Result::Manifest::Package package; package.name = "jebgalgnebhfojomionfpkfelancnnkf.crx"; UpdateResponse::Result result; result.extension_id = "jebgalgnebhfojomionfpkfelancnnkf"; result.crx_urls.push_back(GURL("http://localhost/download/")); result.manifest.version = "1.0"; result.manifest.browser_min_version = "11.0.1.0"; result.manifest.packages.push_back(package); UpdateResponse::Results results; results.list.push_back(result); base::ThreadTaskRunnerHandle::Get()->PostTask( FROM_HERE, base::Bind(update_check_callback, GURL(), 0, "", results)); return true; } }; class FakeCrxDownloader : public CrxDownloader { public: static scoped_ptr Create( bool is_background_download, net::URLRequestContextGetter* context_getter, const scoped_refptr& url_fetcher_task_runner) { return scoped_ptr(new FakeCrxDownloader()); } private: FakeCrxDownloader() : CrxDownloader(scoped_ptr().Pass()) {} ~FakeCrxDownloader() override {} void DoStartDownload(const GURL& url) override { DownloadMetrics download_metrics; FilePath path; Result result; if (url.path() == "/download/jebgalgnebhfojomionfpkfelancnnkf.crx") { download_metrics.url = url; download_metrics.downloader = DownloadMetrics::kNone; download_metrics.error = 0; download_metrics.downloaded_bytes = 1843; download_metrics.total_bytes = 1843; download_metrics.download_time_ms = 1000; EXPECT_TRUE(MakeTestFile( TestFilePath("jebgalgnebhfojomionfpkfelancnnkf.crx"), &path)); result.error = 0; result.response = path; result.downloaded_bytes = 1843; result.total_bytes = 1843; } else { NOTREACHED(); } base::ThreadTaskRunnerHandle::Get()->PostTask( FROM_HERE, base::Bind(&FakeCrxDownloader::OnDownloadProgress, base::Unretained(this), result)); base::ThreadTaskRunnerHandle::Get()->PostTask( FROM_HERE, base::Bind(&FakeCrxDownloader::OnDownloadComplete, base::Unretained(this), true, result, download_metrics)); } }; class FakePingManager : public FakePingManagerImpl { public: explicit FakePingManager(const Configurator& config) : FakePingManagerImpl(config) {} ~FakePingManager() override { const auto& ping_items = items(); EXPECT_EQ(1U, ping_items.size()); EXPECT_EQ("jebgalgnebhfojomionfpkfelancnnkf", ping_items[0].id); EXPECT_TRUE(base::Version("0.0").Equals(ping_items[0].previous_version)); EXPECT_TRUE(base::Version("1.0").Equals(ping_items[0].next_version)); EXPECT_EQ(0, ping_items[0].error_category); EXPECT_EQ(0, ping_items[0].error_code); } }; scoped_ptr ping_manager(new FakePingManager(*config())); scoped_refptr update_client(new UpdateClientImpl( config(), ping_manager.Pass(), &FakeUpdateChecker::Create, &FakeCrxDownloader::Create)); // Verify that calling Install sets ondemand. OnDemandTester ondemand_tester(update_client, true); MockObserver observer; ON_CALL(observer, OnEvent(_, _)) .WillByDefault(Invoke(&ondemand_tester, &OnDemandTester::CheckOnDemand)); InSequence seq; EXPECT_CALL(observer, OnEvent(Events::COMPONENT_CHECKING_FOR_UPDATES, "jebgalgnebhfojomionfpkfelancnnkf")).Times(1); EXPECT_CALL(observer, OnEvent(Events::COMPONENT_UPDATE_FOUND, "jebgalgnebhfojomionfpkfelancnnkf")).Times(1); EXPECT_CALL(observer, OnEvent(Events::COMPONENT_UPDATE_DOWNLOADING, "jebgalgnebhfojomionfpkfelancnnkf")).Times(1); EXPECT_CALL(observer, OnEvent(Events::COMPONENT_UPDATE_READY, "jebgalgnebhfojomionfpkfelancnnkf")).Times(1); EXPECT_CALL(observer, OnEvent(Events::COMPONENT_UPDATED, "jebgalgnebhfojomionfpkfelancnnkf")).Times(1); update_client->AddObserver(&observer); update_client->Install( std::string("jebgalgnebhfojomionfpkfelancnnkf"), base::Bind(&DataCallbackFake::Callback), base::Bind(&CompletionCallbackFake::Callback, quit_closure())); RunThreads(); update_client->RemoveObserver(&observer); StopWorkerPool(); } // Tests that overlapping installs of the same CRX result in an error. TEST_F(UpdateClientTest, ConcurrentInstallSameCRX) { class DataCallbackFake { public: static void Callback(const std::vector& ids, std::vector* components) { CrxComponent crx; crx.name = "test_jebg"; crx.pk_hash.assign(jebg_hash, jebg_hash + arraysize(jebg_hash)); crx.version = Version("0.0"); crx.installer = new TestInstaller; components->push_back(crx); } }; class CompletionCallbackFake { public: static void Callback(const base::Closure& quit_closure, int error) { static int num_call = 0; ++num_call; EXPECT_LE(num_call, 2); if (num_call == 1) { EXPECT_EQ(Error::ERROR_UPDATE_IN_PROGRESS, error); return; } if (num_call == 2) { EXPECT_EQ(0, error); quit_closure.Run(); } } }; class FakeUpdateChecker : public UpdateChecker { public: static scoped_ptr Create(const Configurator& config) { return scoped_ptr(new FakeUpdateChecker()); } bool CheckForUpdates( const std::vector& items_to_check, const std::string& additional_attributes, const UpdateCheckCallback& update_check_callback) override { base::ThreadTaskRunnerHandle::Get()->PostTask( FROM_HERE, base::Bind(update_check_callback, GURL(), 0, "", UpdateResponse::Results())); return true; } }; class FakeCrxDownloader : public CrxDownloader { public: static scoped_ptr Create( bool is_background_download, net::URLRequestContextGetter* context_getter, const scoped_refptr& url_fetcher_task_runner) { return scoped_ptr(new FakeCrxDownloader()); } private: FakeCrxDownloader() : CrxDownloader(scoped_ptr().Pass()) {} ~FakeCrxDownloader() override {} void DoStartDownload(const GURL& url) override { EXPECT_TRUE(false); } }; class FakePingManager : public FakePingManagerImpl { public: explicit FakePingManager(const Configurator& config) : FakePingManagerImpl(config) {} ~FakePingManager() override { EXPECT_TRUE(items().empty()); } }; scoped_ptr ping_manager(new FakePingManager(*config())); scoped_refptr update_client(new UpdateClientImpl( config(), ping_manager.Pass(), &FakeUpdateChecker::Create, &FakeCrxDownloader::Create)); // Verify that calling Install sets ondemand. OnDemandTester ondemand_tester(update_client, true); MockObserver observer; ON_CALL(observer, OnEvent(_, _)) .WillByDefault(Invoke(&ondemand_tester, &OnDemandTester::CheckOnDemand)); EXPECT_CALL(observer, OnEvent(Events::COMPONENT_CHECKING_FOR_UPDATES, "jebgalgnebhfojomionfpkfelancnnkf")) .Times(1); EXPECT_CALL(observer, OnEvent(Events::COMPONENT_NOT_UPDATED, "jebgalgnebhfojomionfpkfelancnnkf")) .Times(1); update_client->AddObserver(&observer); update_client->Install( std::string("jebgalgnebhfojomionfpkfelancnnkf"), base::Bind(&DataCallbackFake::Callback), base::Bind(&CompletionCallbackFake::Callback, quit_closure())); update_client->Install( std::string("jebgalgnebhfojomionfpkfelancnnkf"), base::Bind(&DataCallbackFake::Callback), base::Bind(&CompletionCallbackFake::Callback, quit_closure())); RunThreads(); update_client->RemoveObserver(&observer); StopWorkerPool(); } // Make sure that we don't get any crashes when trying to update an empty list // of ids. TEST_F(UpdateClientTest, EmptyIdList) { class DataCallbackFake { public: static void Callback(const std::vector& ids, std::vector* components) {} }; class CompletionCallbackFake { public: static void Callback(const base::Closure& quit_closure, int error) { quit_closure.Run(); } }; class FakeUpdateChecker : public UpdateChecker { public: static scoped_ptr Create(const Configurator& config) { return scoped_ptr(new FakeUpdateChecker()); } bool CheckForUpdates( const std::vector& items_to_check, const std::string& additional_attributes, const UpdateCheckCallback& update_check_callback) override { return false; } }; class FakeCrxDownloader : public CrxDownloader { public: static scoped_ptr Create( bool is_background_download, net::URLRequestContextGetter* context_getter, const scoped_refptr& url_fetcher_task_runner) { return scoped_ptr(new FakeCrxDownloader()); } private: FakeCrxDownloader() : CrxDownloader(scoped_ptr().Pass()) {} ~FakeCrxDownloader() override {} void DoStartDownload(const GURL& url) override { EXPECT_TRUE(false); } }; scoped_refptr update_client(new UpdateClientImpl( config(), make_scoped_ptr(new FakePingManagerImpl(*config())), &FakeUpdateChecker::Create, &FakeCrxDownloader::Create)); std::vector empty_id_list; base::RunLoop runloop; update_client->Update( empty_id_list, base::Bind(&DataCallbackFake::Callback), base::Bind(&CompletionCallbackFake::Callback, runloop.QuitClosure())); runloop.Run(); StopWorkerPool(); } } // namespace update_client