| // Copyright (c) 2011 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 <map> |
| |
| #include "base/file_util.h" |
| #include "base/memory/scoped_ptr.h" |
| #include "base/stl_util-inl.h" |
| #include "base/string_number_conversions.h" |
| #include "base/string_split.h" |
| #include "base/string_util.h" |
| #include "base/stringprintf.h" |
| #include "base/threading/thread.h" |
| #include "base/version.h" |
| #include "chrome/browser/extensions/extension_error_reporter.h" |
| #include "chrome/browser/extensions/extension_updater.h" |
| #include "chrome/browser/extensions/extension_service.h" |
| #include "chrome/browser/extensions/test_extension_prefs.h" |
| #include "chrome/browser/prefs/pref_service.h" |
| #include "chrome/common/extensions/extension.h" |
| #include "chrome/common/extensions/extension_constants.h" |
| #include "chrome/common/pref_names.h" |
| #include "chrome/common/net/test_url_fetcher_factory.h" |
| #include "chrome/test/testing_profile.h" |
| #include "content/browser/browser_thread.h" |
| #include "net/base/escape.h" |
| #include "net/base/load_flags.h" |
| #include "net/url_request/url_request_status.h" |
| #include "testing/gtest/include/gtest/gtest.h" |
| #include "libxml/globals.h" |
| |
| using base::Time; |
| using base::TimeDelta; |
| |
| namespace { |
| |
| const char kEmptyUpdateUrlData[] = ""; |
| |
| int expected_load_flags = |
| net::LOAD_DO_NOT_SEND_COOKIES | |
| net::LOAD_DO_NOT_SAVE_COOKIES | |
| net::LOAD_DISABLE_CACHE; |
| |
| const ManifestFetchData::PingData kNeverPingedData( |
| ManifestFetchData::kNeverPinged, ManifestFetchData::kNeverPinged); |
| |
| } // namespace |
| |
| // Base class for further specialized test classes. |
| class MockService : public ExtensionServiceInterface { |
| public: |
| MockService() |
| : pending_extension_manager_(ALLOW_THIS_IN_INITIALIZER_LIST(*this)) {} |
| virtual ~MockService() {} |
| |
| virtual const ExtensionList* extensions() const { |
| ADD_FAILURE(); |
| return NULL; |
| } |
| |
| virtual const ExtensionList* disabled_extensions() const { |
| ADD_FAILURE(); |
| return NULL; |
| } |
| |
| virtual void UpdateExtension(const std::string& id, |
| const FilePath& path, |
| const GURL& download_url) { |
| FAIL(); |
| } |
| |
| virtual const Extension* GetExtensionById(const std::string& id, |
| bool include_disabled) const { |
| ADD_FAILURE(); |
| return NULL; |
| } |
| |
| virtual bool UninstallExtension(const std::string& extension_id, |
| bool external_uninstall, |
| std::string* error) { |
| ADD_FAILURE(); |
| return false; |
| } |
| |
| virtual bool IsExtensionEnabled(const std::string& extension_id) const { |
| ADD_FAILURE(); |
| return false; |
| } |
| |
| virtual bool IsExternalExtensionUninstalled( |
| const std::string& extension_id) const { |
| ADD_FAILURE(); |
| return false; |
| } |
| |
| virtual void EnableExtension(const std::string& extension_id) { |
| FAIL(); |
| } |
| |
| virtual void DisableExtension(const std::string& extension_id) { |
| FAIL(); |
| } |
| |
| |
| virtual void UpdateExtensionBlacklist( |
| const std::vector<std::string>& blacklist) { |
| FAIL(); |
| } |
| |
| virtual void CheckAdminBlacklist() { |
| FAIL(); |
| } |
| |
| virtual bool IsIncognitoEnabled(const std::string& id) const { |
| ADD_FAILURE(); |
| return false; |
| } |
| |
| virtual void SetIsIncognitoEnabled(const std::string& id, |
| bool enabled) { |
| FAIL(); |
| } |
| |
| virtual void CheckForUpdatesSoon() { |
| FAIL(); |
| } |
| |
| virtual PendingExtensionManager* pending_extension_manager() { |
| ADD_FAILURE() << "Subclass should override this if it will " |
| << "be accessed by a test."; |
| return &pending_extension_manager_; |
| } |
| |
| virtual void ProcessSyncData( |
| const ExtensionSyncData& extension_sync_data, |
| PendingExtensionInfo::ShouldAllowInstallPredicate |
| should_allow_install) { |
| FAIL(); |
| } |
| |
| Profile* profile() { return &profile_; } |
| |
| ExtensionPrefs* extension_prefs() { return prefs_.prefs(); } |
| |
| PrefService* pref_service() { return prefs_.pref_service(); } |
| |
| // Creates test extensions and inserts them into list. The name and |
| // version are all based on their index. If |update_url| is non-null, it |
| // will be used as the update_url for each extension. |
| // The |id| is used to distinguish extension names and make sure that |
| // no two extensions share the same name. |
| void CreateTestExtensions(int id, int count, ExtensionList *list, |
| const std::string* update_url, |
| Extension::Location location) { |
| for (int i = 1; i <= count; i++) { |
| DictionaryValue manifest; |
| manifest.SetString(extension_manifest_keys::kVersion, |
| base::StringPrintf("%d.0.0.0", i)); |
| manifest.SetString(extension_manifest_keys::kName, |
| base::StringPrintf("Extension %d.%d", id, i)); |
| if (update_url) |
| manifest.SetString(extension_manifest_keys::kUpdateURL, *update_url); |
| scoped_refptr<Extension> e = |
| prefs_.AddExtensionWithManifest(manifest, location); |
| ASSERT_TRUE(e != NULL); |
| list->push_back(e); |
| } |
| } |
| |
| protected: |
| PendingExtensionManager pending_extension_manager_; |
| TestExtensionPrefs prefs_; |
| TestingProfile profile_; |
| |
| private: |
| DISALLOW_COPY_AND_ASSIGN(MockService); |
| }; |
| |
| |
| std::string GenerateId(std::string input) { |
| std::string result; |
| EXPECT_TRUE(Extension::GenerateId(input, &result)); |
| return result; |
| } |
| |
| bool ShouldInstallExtensionsOnly(const Extension& extension) { |
| return extension.GetType() == Extension::TYPE_EXTENSION; |
| } |
| |
| bool ShouldInstallThemesOnly(const Extension& extension) { |
| return extension.is_theme(); |
| } |
| |
| bool ShouldAlwaysInstall(const Extension& extension) { |
| return true; |
| } |
| |
| // Loads some pending extension records into a pending extension manager. |
| void SetupPendingExtensionManagerForTest( |
| int count, |
| const GURL& update_url, |
| PendingExtensionManager* pending_extension_manager) { |
| for (int i = 1; i <= count; i++) { |
| PendingExtensionInfo::ShouldAllowInstallPredicate should_allow_install = |
| (i % 2 == 0) ? &ShouldInstallThemesOnly : &ShouldInstallExtensionsOnly; |
| const bool kIsFromSync = true; |
| const bool kInstallSilently = true; |
| const Extension::State kInitialState = Extension::ENABLED; |
| const bool kInitialIncognitoEnabled = false; |
| std::string id = GenerateId(base::StringPrintf("extension%i", i)); |
| |
| pending_extension_manager->AddForTesting( |
| id, |
| PendingExtensionInfo(update_url, |
| should_allow_install, |
| kIsFromSync, |
| kInstallSilently, |
| kInitialState, |
| kInitialIncognitoEnabled, |
| Extension::INTERNAL)); |
| } |
| } |
| |
| class ServiceForManifestTests : public MockService { |
| public: |
| ServiceForManifestTests() {} |
| |
| virtual ~ServiceForManifestTests() {} |
| |
| virtual const Extension* GetExtensionById(const std::string& id, |
| bool include_disabled) const { |
| for (ExtensionList::const_iterator iter = extensions_.begin(); |
| iter != extensions_.end(); ++iter) { |
| if ((*iter)->id() == id) { |
| return *iter; |
| } |
| } |
| return NULL; |
| } |
| |
| virtual const ExtensionList* extensions() const { return &extensions_; } |
| |
| virtual PendingExtensionManager* pending_extension_manager() { |
| return &pending_extension_manager_; |
| } |
| |
| void set_extensions(ExtensionList extensions) { |
| extensions_ = extensions; |
| } |
| |
| private: |
| ExtensionList extensions_; |
| }; |
| |
| class ServiceForDownloadTests : public MockService { |
| public: |
| virtual void UpdateExtension(const std::string& id, |
| const FilePath& extension_path, |
| const GURL& download_url) { |
| extension_id_ = id; |
| install_path_ = extension_path; |
| download_url_ = download_url; |
| } |
| |
| virtual PendingExtensionManager* pending_extension_manager() { |
| return &pending_extension_manager_; |
| } |
| |
| virtual const Extension* GetExtensionById(const std::string& id, bool) const { |
| last_inquired_extension_id_ = id; |
| return NULL; |
| } |
| |
| const std::string& extension_id() const { return extension_id_; } |
| const FilePath& install_path() const { return install_path_; } |
| const GURL& download_url() const { return download_url_; } |
| const std::string& last_inquired_extension_id() const { |
| return last_inquired_extension_id_; |
| } |
| |
| private: |
| std::string extension_id_; |
| FilePath install_path_; |
| GURL download_url_; |
| |
| // The last extension ID that GetExtensionById was called with. |
| // Mutable because the method that sets it (GetExtensionById) is const |
| // in the actual extension service, but must record the last extension |
| // ID in this test class. |
| mutable std::string last_inquired_extension_id_; |
| }; |
| |
| class ServiceForBlacklistTests : public MockService { |
| public: |
| ServiceForBlacklistTests() |
| : MockService(), |
| processed_blacklist_(false) { |
| } |
| virtual void UpdateExtensionBlacklist( |
| const std::vector<std::string>& blacklist) { |
| processed_blacklist_ = true; |
| return; |
| } |
| bool processed_blacklist() { return processed_blacklist_; } |
| const std::string& extension_id() { return extension_id_; } |
| |
| private: |
| bool processed_blacklist_; |
| std::string extension_id_; |
| FilePath install_path_; |
| }; |
| |
| static const int kUpdateFrequencySecs = 15; |
| |
| // Takes a string with KEY=VALUE parameters separated by '&' in |params| and |
| // puts the key/value pairs into |result|. For keys with no value, the empty |
| // string is used. So for "a=1&b=foo&c", result would map "a" to "1", "b" to |
| // "foo", and "c" to "". |
| static void ExtractParameters(const std::string& params, |
| std::map<std::string, std::string>* result) { |
| std::vector<std::string> pairs; |
| base::SplitString(params, '&', &pairs); |
| for (size_t i = 0; i < pairs.size(); i++) { |
| std::vector<std::string> key_val; |
| base::SplitString(pairs[i], '=', &key_val); |
| if (!key_val.empty()) { |
| std::string key = key_val[0]; |
| EXPECT_TRUE(result->find(key) == result->end()); |
| (*result)[key] = (key_val.size() == 2) ? key_val[1] : ""; |
| } else { |
| NOTREACHED(); |
| } |
| } |
| } |
| |
| // All of our tests that need to use private APIs of ExtensionUpdater live |
| // inside this class (which is a friend to ExtensionUpdater). |
| class ExtensionUpdaterTest : public testing::Test { |
| public: |
| static void SimulateTimerFired(ExtensionUpdater* updater) { |
| EXPECT_TRUE(updater->timer_.IsRunning()); |
| updater->timer_.Stop(); |
| updater->TimerFired(); |
| } |
| |
| static void SimulateCheckSoon(const ExtensionUpdater& updater, |
| MessageLoop* message_loop) { |
| EXPECT_TRUE(updater.will_check_soon_); |
| message_loop->RunAllPending(); |
| } |
| |
| // Adds a Result with the given data to results. |
| static void AddParseResult( |
| const std::string& id, |
| const std::string& version, |
| const std::string& url, |
| UpdateManifest::Results* results) { |
| UpdateManifest::Result result; |
| result.extension_id = id; |
| result.version = version; |
| result.crx_url = GURL(url); |
| results->list.push_back(result); |
| } |
| |
| static void TestExtensionUpdateCheckRequests(bool pending) { |
| MessageLoop message_loop; |
| BrowserThread ui_thread(BrowserThread::UI, &message_loop); |
| BrowserThread file_thread(BrowserThread::FILE, &message_loop); |
| BrowserThread io_thread(BrowserThread::IO); |
| io_thread.Start(); |
| |
| // Create an extension with an update_url. |
| ServiceForManifestTests service; |
| std::string update_url("http://foo.com/bar"); |
| ExtensionList extensions; |
| PendingExtensionManager* pending_extension_manager = |
| service.pending_extension_manager(); |
| if (pending) { |
| SetupPendingExtensionManagerForTest(1, GURL(update_url), |
| pending_extension_manager); |
| } else { |
| service.CreateTestExtensions(1, 1, &extensions, &update_url, |
| Extension::INTERNAL); |
| service.set_extensions(extensions); |
| } |
| |
| // Set up and start the updater. |
| TestURLFetcherFactory factory; |
| URLFetcher::set_factory(&factory); |
| ExtensionUpdater updater( |
| &service, service.extension_prefs(), service.pref_service(), |
| service.profile(), 60*60*24); |
| updater.Start(); |
| // Disable blacklist checks (tested elsewhere) so that we only see the |
| // update HTTP request. |
| updater.set_blacklist_checks_enabled(false); |
| |
| // Tell the update that it's time to do update checks. |
| SimulateTimerFired(&updater); |
| |
| // Get the url our mock fetcher was asked to fetch. |
| TestURLFetcher* fetcher = |
| factory.GetFetcherByID(ExtensionUpdater::kManifestFetcherId); |
| const GURL& url = fetcher->original_url(); |
| EXPECT_FALSE(url.is_empty()); |
| EXPECT_TRUE(url.is_valid()); |
| EXPECT_TRUE(url.SchemeIs("http")); |
| EXPECT_EQ("foo.com", url.host()); |
| EXPECT_EQ("/bar", url.path()); |
| |
| // Validate the extension request parameters in the query. It should |
| // look something like "?x=id%3D<id>%26v%3D<version>%26uc". |
| EXPECT_TRUE(url.has_query()); |
| std::vector<std::string> parts; |
| base::SplitString(url.query(), '=', &parts); |
| EXPECT_EQ(2u, parts.size()); |
| EXPECT_EQ("x", parts[0]); |
| std::string decoded = UnescapeURLComponent(parts[1], |
| UnescapeRule::URL_SPECIAL_CHARS); |
| std::map<std::string, std::string> params; |
| ExtractParameters(decoded, ¶ms); |
| if (pending) { |
| EXPECT_EQ(pending_extension_manager->begin()->first, params["id"]); |
| EXPECT_EQ("0.0.0.0", params["v"]); |
| } else { |
| EXPECT_EQ(extensions[0]->id(), params["id"]); |
| EXPECT_EQ(extensions[0]->VersionString(), params["v"]); |
| } |
| EXPECT_EQ("", params["uc"]); |
| } |
| |
| static void TestBlacklistUpdateCheckRequests() { |
| ServiceForManifestTests service; |
| |
| // Setup and start the updater. |
| MessageLoop message_loop; |
| BrowserThread ui_thread(BrowserThread::UI, &message_loop); |
| BrowserThread io_thread(BrowserThread::IO); |
| io_thread.Start(); |
| |
| TestURLFetcherFactory factory; |
| URLFetcher::set_factory(&factory); |
| ExtensionUpdater updater( |
| &service, service.extension_prefs(), service.pref_service(), |
| service.profile(), 60*60*24); |
| updater.Start(); |
| |
| // Tell the updater that it's time to do update checks. |
| SimulateTimerFired(&updater); |
| |
| // Get the url our mock fetcher was asked to fetch. |
| TestURLFetcher* fetcher = |
| factory.GetFetcherByID(ExtensionUpdater::kManifestFetcherId); |
| ASSERT_FALSE(fetcher == NULL); |
| const GURL& url = fetcher->original_url(); |
| |
| EXPECT_FALSE(url.is_empty()); |
| EXPECT_TRUE(url.is_valid()); |
| EXPECT_TRUE(url.SchemeIs("https")); |
| EXPECT_EQ("clients2.google.com", url.host()); |
| EXPECT_EQ("/service/update2/crx", url.path()); |
| |
| // Validate the extension request parameters in the query. It should |
| // look something like "?x=id%3D<id>%26v%3D<version>%26uc". |
| EXPECT_TRUE(url.has_query()); |
| std::vector<std::string> parts; |
| base::SplitString(url.query(), '=', &parts); |
| EXPECT_EQ(2u, parts.size()); |
| EXPECT_EQ("x", parts[0]); |
| std::string decoded = UnescapeURLComponent(parts[1], |
| UnescapeRule::URL_SPECIAL_CHARS); |
| std::map<std::string, std::string> params; |
| ExtractParameters(decoded, ¶ms); |
| EXPECT_EQ("com.google.crx.blacklist", params["id"]); |
| EXPECT_EQ("0", params["v"]); |
| EXPECT_EQ("", params["uc"]); |
| EXPECT_TRUE(ContainsKey(params, "ping")); |
| } |
| |
| static void TestUpdateUrlDataEmpty() { |
| const std::string id = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"; |
| const std::string version = "1.0"; |
| |
| // Make sure that an empty update URL data string does not cause a ap= |
| // option to appear in the x= parameter. |
| ManifestFetchData fetch_data(GURL("http://localhost/foo")); |
| fetch_data.AddExtension(id, version, |
| kNeverPingedData, ""); |
| EXPECT_EQ("http://localhost/foo\?x=id%3Daaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" |
| "%26v%3D1.0%26uc", |
| fetch_data.full_url().spec()); |
| } |
| |
| static void TestUpdateUrlDataSimple() { |
| const std::string id = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"; |
| const std::string version = "1.0"; |
| |
| // Make sure that an update URL data string causes an appropriate ap= |
| // option to appear in the x= parameter. |
| ManifestFetchData fetch_data(GURL("http://localhost/foo")); |
| fetch_data.AddExtension(id, version, |
| kNeverPingedData, "bar"); |
| EXPECT_EQ("http://localhost/foo\?x=id%3Daaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" |
| "%26v%3D1.0%26uc%26ap%3Dbar", |
| fetch_data.full_url().spec()); |
| } |
| |
| static void TestUpdateUrlDataCompound() { |
| const std::string id = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"; |
| const std::string version = "1.0"; |
| |
| // Make sure that an update URL data string causes an appropriate ap= |
| // option to appear in the x= parameter. |
| ManifestFetchData fetch_data(GURL("http://localhost/foo")); |
| fetch_data.AddExtension(id, version, |
| kNeverPingedData, "a=1&b=2&c"); |
| EXPECT_EQ("http://localhost/foo\?x=id%3Daaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" |
| "%26v%3D1.0%26uc%26ap%3Da%253D1%2526b%253D2%2526c", |
| fetch_data.full_url().spec()); |
| } |
| |
| static void TestUpdateUrlDataFromGallery(const std::string& gallery_url) { |
| MockService service; |
| ManifestFetchesBuilder builder(&service, service.extension_prefs()); |
| ExtensionList extensions; |
| std::string url(gallery_url); |
| |
| service.CreateTestExtensions(1, 1, &extensions, &url, Extension::INTERNAL); |
| builder.AddExtension(*extensions[0]); |
| std::vector<ManifestFetchData*> fetches = builder.GetFetches(); |
| EXPECT_EQ(1u, fetches.size()); |
| scoped_ptr<ManifestFetchData> fetch(fetches[0]); |
| fetches.clear(); |
| |
| // Make sure that extensions that update from the gallery ignore any |
| // update URL data. |
| const std::string& update_url = fetch->full_url().spec(); |
| std::string::size_type x = update_url.find("x="); |
| EXPECT_NE(std::string::npos, x); |
| std::string::size_type ap = update_url.find("ap%3D", x); |
| EXPECT_EQ(std::string::npos, ap); |
| } |
| |
| static void TestDetermineUpdates() { |
| MessageLoop message_loop; |
| BrowserThread ui_thread(BrowserThread::UI, &message_loop); |
| BrowserThread file_thread(BrowserThread::FILE, &message_loop); |
| |
| // Create a set of test extensions |
| ServiceForManifestTests service; |
| ExtensionList tmp; |
| service.CreateTestExtensions(1, 3, &tmp, NULL, Extension::INTERNAL); |
| service.set_extensions(tmp); |
| |
| ExtensionUpdater updater( |
| &service, service.extension_prefs(), service.pref_service(), |
| service.profile(), kUpdateFrequencySecs); |
| updater.Start(); |
| |
| // Check passing an empty list of parse results to DetermineUpdates |
| ManifestFetchData fetch_data(GURL("http://localhost/foo")); |
| UpdateManifest::Results updates; |
| std::vector<int> updateable = updater.DetermineUpdates(fetch_data, |
| updates); |
| EXPECT_TRUE(updateable.empty()); |
| |
| // Create two updates - expect that DetermineUpdates will return the first |
| // one (v1.0 installed, v1.1 available) but not the second one (both |
| // installed and available at v2.0). |
| scoped_ptr<Version> one(Version::GetVersionFromString("1.0")); |
| EXPECT_TRUE(tmp[0]->version()->Equals(*one)); |
| fetch_data.AddExtension(tmp[0]->id(), tmp[0]->VersionString(), |
| kNeverPingedData, |
| kEmptyUpdateUrlData); |
| AddParseResult(tmp[0]->id(), |
| "1.1", "http://localhost/e1_1.1.crx", &updates); |
| fetch_data.AddExtension(tmp[1]->id(), tmp[1]->VersionString(), |
| kNeverPingedData, |
| kEmptyUpdateUrlData); |
| AddParseResult(tmp[1]->id(), |
| tmp[1]->VersionString(), "http://localhost/e2_2.0.crx", &updates); |
| updateable = updater.DetermineUpdates(fetch_data, updates); |
| EXPECT_EQ(1u, updateable.size()); |
| EXPECT_EQ(0, updateable[0]); |
| } |
| |
| static void TestDetermineUpdatesPending() { |
| // Create a set of test extensions |
| ServiceForManifestTests service; |
| PendingExtensionManager* pending_extension_manager = |
| service.pending_extension_manager(); |
| SetupPendingExtensionManagerForTest(3, GURL(), pending_extension_manager); |
| |
| MessageLoop message_loop; |
| BrowserThread ui_thread(BrowserThread::UI, &message_loop); |
| ExtensionUpdater updater( |
| &service, service.extension_prefs(), service.pref_service(), |
| service.profile(), kUpdateFrequencySecs); |
| updater.Start(); |
| |
| ManifestFetchData fetch_data(GURL("http://localhost/foo")); |
| UpdateManifest::Results updates; |
| PendingExtensionManager::const_iterator it; |
| for (it = pending_extension_manager->begin(); |
| it != pending_extension_manager->end(); ++it) { |
| fetch_data.AddExtension(it->first, "1.0.0.0", |
| kNeverPingedData, |
| kEmptyUpdateUrlData); |
| AddParseResult(it->first, |
| "1.1", "http://localhost/e1_1.1.crx", &updates); |
| } |
| std::vector<int> updateable = |
| updater.DetermineUpdates(fetch_data, updates); |
| // All the apps should be updateable. |
| EXPECT_EQ(3u, updateable.size()); |
| for (std::vector<int>::size_type i = 0; i < updateable.size(); ++i) { |
| EXPECT_EQ(static_cast<int>(i), updateable[i]); |
| } |
| } |
| |
| static void TestMultipleManifestDownloading() { |
| MessageLoop ui_loop; |
| BrowserThread ui_thread(BrowserThread::UI, &ui_loop); |
| BrowserThread file_thread(BrowserThread::FILE); |
| file_thread.Start(); |
| BrowserThread io_thread(BrowserThread::IO); |
| io_thread.Start(); |
| |
| TestURLFetcherFactory factory; |
| TestURLFetcher* fetcher = NULL; |
| URLFetcher::set_factory(&factory); |
| scoped_ptr<ServiceForDownloadTests> service(new ServiceForDownloadTests); |
| ExtensionUpdater updater(service.get(), service->extension_prefs(), |
| service->pref_service(), |
| service->profile(), |
| kUpdateFrequencySecs); |
| updater.Start(); |
| |
| GURL url1("http://localhost/manifest1"); |
| GURL url2("http://localhost/manifest2"); |
| |
| // Request 2 update checks - the first should begin immediately and the |
| // second one should be queued up. |
| ManifestFetchData* fetch1 = new ManifestFetchData(url1); |
| ManifestFetchData* fetch2 = new ManifestFetchData(url2); |
| ManifestFetchData::PingData zeroDays(0, 0); |
| fetch1->AddExtension("1111", "1.0", zeroDays, kEmptyUpdateUrlData); |
| fetch2->AddExtension("12345", "2.0", kNeverPingedData, |
| kEmptyUpdateUrlData); |
| updater.StartUpdateCheck(fetch1); |
| updater.StartUpdateCheck(fetch2); |
| |
| std::string invalid_xml = "invalid xml"; |
| fetcher = factory.GetFetcherByID(ExtensionUpdater::kManifestFetcherId); |
| EXPECT_TRUE(fetcher != NULL && fetcher->delegate() != NULL); |
| EXPECT_TRUE(fetcher->load_flags() == expected_load_flags); |
| fetcher->delegate()->OnURLFetchComplete( |
| fetcher, url1, net::URLRequestStatus(), 200, ResponseCookies(), |
| invalid_xml); |
| |
| // Now that the first request is complete, make sure the second one has |
| // been started. |
| const std::string kValidXml = |
| "<?xml version='1.0' encoding='UTF-8'?>" |
| "<gupdate xmlns='http://www.google.com/update2/response'" |
| " protocol='2.0'>" |
| " <app appid='12345'>" |
| " <updatecheck codebase='http://example.com/extension_1.2.3.4.crx'" |
| " version='1.2.3.4' prodversionmin='2.0.143.0' />" |
| " </app>" |
| "</gupdate>"; |
| fetcher = factory.GetFetcherByID(ExtensionUpdater::kManifestFetcherId); |
| EXPECT_TRUE(fetcher != NULL && fetcher->delegate() != NULL); |
| EXPECT_TRUE(fetcher->load_flags() == expected_load_flags); |
| fetcher->delegate()->OnURLFetchComplete( |
| fetcher, url2, net::URLRequestStatus(), 200, ResponseCookies(), |
| kValidXml); |
| |
| // This should run the manifest parsing, then we want to make sure that our |
| // service was called with GetExtensionById with the matching id from |
| // kValidXml. |
| file_thread.Stop(); |
| io_thread.Stop(); |
| ui_loop.RunAllPending(); |
| EXPECT_EQ("12345", service->last_inquired_extension_id()); |
| xmlCleanupGlobals(); |
| |
| // The FILE thread is needed for |service|'s cleanup, |
| // because of ImportantFileWriter. |
| file_thread.Start(); |
| service.reset(); |
| } |
| |
| static void TestSingleExtensionDownloading(bool pending) { |
| MessageLoop ui_loop; |
| BrowserThread ui_thread(BrowserThread::UI, &ui_loop); |
| BrowserThread file_thread(BrowserThread::FILE); |
| file_thread.Start(); |
| BrowserThread io_thread(BrowserThread::IO); |
| io_thread.Start(); |
| |
| TestURLFetcherFactory factory; |
| TestURLFetcher* fetcher = NULL; |
| URLFetcher::set_factory(&factory); |
| scoped_ptr<ServiceForDownloadTests> service(new ServiceForDownloadTests); |
| ExtensionUpdater updater(service.get(), service->extension_prefs(), |
| service->pref_service(), |
| service->profile(), |
| kUpdateFrequencySecs); |
| updater.Start(); |
| |
| GURL test_url("http://localhost/extension.crx"); |
| |
| std::string id = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"; |
| std::string hash = ""; |
| scoped_ptr<Version> version(Version::GetVersionFromString("0.0.1")); |
| ASSERT_TRUE(version.get()); |
| updater.FetchUpdatedExtension(id, test_url, hash, version->GetString()); |
| |
| if (pending) { |
| const bool kIsFromSync = true; |
| const bool kInstallSilently = true; |
| const Extension::State kInitialState = Extension::ENABLED; |
| const bool kInitialIncognitoEnabled = false; |
| PendingExtensionManager* pending_extension_manager = |
| service->pending_extension_manager(); |
| pending_extension_manager->AddForTesting( |
| id, |
| PendingExtensionInfo(test_url, &ShouldAlwaysInstall, kIsFromSync, |
| kInstallSilently, kInitialState, |
| kInitialIncognitoEnabled, Extension::INTERNAL)); |
| } |
| |
| // Call back the ExtensionUpdater with a 200 response and some test data |
| std::string extension_data("whatever"); |
| fetcher = factory.GetFetcherByID(ExtensionUpdater::kExtensionFetcherId); |
| EXPECT_TRUE(fetcher != NULL && fetcher->delegate() != NULL); |
| EXPECT_TRUE(fetcher->load_flags() == expected_load_flags); |
| fetcher->delegate()->OnURLFetchComplete( |
| fetcher, test_url, net::URLRequestStatus(), 200, ResponseCookies(), |
| extension_data); |
| |
| file_thread.Stop(); |
| ui_loop.RunAllPending(); |
| |
| // Expect that ExtensionUpdater asked the mock extensions service to install |
| // a file with the test data for the right id. |
| EXPECT_EQ(id, service->extension_id()); |
| FilePath tmpfile_path = service->install_path(); |
| EXPECT_FALSE(tmpfile_path.empty()); |
| EXPECT_EQ(test_url, service->download_url()); |
| std::string file_contents; |
| EXPECT_TRUE(file_util::ReadFileToString(tmpfile_path, &file_contents)); |
| EXPECT_TRUE(extension_data == file_contents); |
| |
| // The FILE thread is needed for |service|'s cleanup, |
| // because of ImportantFileWriter. |
| file_thread.Start(); |
| service.reset(); |
| |
| file_util::Delete(tmpfile_path, false); |
| URLFetcher::set_factory(NULL); |
| } |
| |
| static void TestBlacklistDownloading() { |
| MessageLoop message_loop; |
| BrowserThread ui_thread(BrowserThread::UI, &message_loop); |
| BrowserThread file_thread(BrowserThread::FILE, &message_loop); |
| BrowserThread io_thread(BrowserThread::IO); |
| io_thread.Start(); |
| |
| TestURLFetcherFactory factory; |
| TestURLFetcher* fetcher = NULL; |
| URLFetcher::set_factory(&factory); |
| ServiceForBlacklistTests service; |
| ExtensionUpdater updater( |
| &service, service.extension_prefs(), service.pref_service(), |
| service.profile(), kUpdateFrequencySecs); |
| updater.Start(); |
| GURL test_url("http://localhost/extension.crx"); |
| |
| std::string id = "com.google.crx.blacklist"; |
| |
| std::string hash = |
| "2CE109E9D0FAF820B2434E166297934E6177B65AB9951DBC3E204CAD4689B39C"; |
| |
| std::string version = "0.0.1"; |
| updater.FetchUpdatedExtension(id, test_url, hash, version); |
| |
| // Call back the ExtensionUpdater with a 200 response and some test data |
| std::string extension_data("aaabbb"); |
| fetcher = factory.GetFetcherByID(ExtensionUpdater::kExtensionFetcherId); |
| EXPECT_TRUE(fetcher != NULL && fetcher->delegate() != NULL); |
| EXPECT_TRUE(fetcher->load_flags() == expected_load_flags); |
| fetcher->delegate()->OnURLFetchComplete( |
| fetcher, test_url, net::URLRequestStatus(), 200, ResponseCookies(), |
| extension_data); |
| |
| message_loop.RunAllPending(); |
| |
| // The updater should have called extension service to process the |
| // blacklist. |
| EXPECT_TRUE(service.processed_blacklist()); |
| |
| EXPECT_EQ(version, service.pref_service()-> |
| GetString(prefs::kExtensionBlacklistUpdateVersion)); |
| |
| URLFetcher::set_factory(NULL); |
| } |
| |
| static void TestMultipleExtensionDownloading() { |
| MessageLoopForUI message_loop; |
| BrowserThread ui_thread(BrowserThread::UI, &message_loop); |
| BrowserThread file_thread(BrowserThread::FILE, &message_loop); |
| BrowserThread io_thread(BrowserThread::IO); |
| io_thread.Start(); |
| |
| TestURLFetcherFactory factory; |
| TestURLFetcher* fetcher = NULL; |
| URLFetcher::set_factory(&factory); |
| ServiceForDownloadTests service; |
| ExtensionUpdater updater( |
| &service, service.extension_prefs(), service.pref_service(), |
| service.profile(), kUpdateFrequencySecs); |
| updater.Start(); |
| |
| GURL url1("http://localhost/extension1.crx"); |
| GURL url2("http://localhost/extension2.crx"); |
| |
| std::string id1 = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"; |
| std::string id2 = "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"; |
| |
| std::string hash1 = ""; |
| std::string hash2 = ""; |
| |
| std::string version1 = "0.1"; |
| std::string version2 = "0.1"; |
| // Start two fetches |
| updater.FetchUpdatedExtension(id1, url1, hash1, version1); |
| updater.FetchUpdatedExtension(id2, url2, hash2, version2); |
| |
| // Make the first fetch complete. |
| std::string extension_data1("whatever"); |
| fetcher = factory.GetFetcherByID(ExtensionUpdater::kExtensionFetcherId); |
| EXPECT_TRUE(fetcher != NULL && fetcher->delegate() != NULL); |
| EXPECT_TRUE(fetcher->load_flags() == expected_load_flags); |
| fetcher->delegate()->OnURLFetchComplete( |
| fetcher, url1, net::URLRequestStatus(), 200, ResponseCookies(), |
| extension_data1); |
| message_loop.RunAllPending(); |
| |
| // Expect that the service was asked to do an install with the right data. |
| FilePath tmpfile_path = service.install_path(); |
| EXPECT_FALSE(tmpfile_path.empty()); |
| EXPECT_EQ(id1, service.extension_id()); |
| EXPECT_EQ(url1, service.download_url()); |
| message_loop.RunAllPending(); |
| file_util::Delete(tmpfile_path, false); |
| |
| // Make sure the second fetch finished and asked the service to do an |
| // update. |
| std::string extension_data2("whatever2"); |
| fetcher = factory.GetFetcherByID(ExtensionUpdater::kExtensionFetcherId); |
| EXPECT_TRUE(fetcher != NULL && fetcher->delegate() != NULL); |
| EXPECT_TRUE(fetcher->load_flags() == expected_load_flags); |
| fetcher->delegate()->OnURLFetchComplete( |
| fetcher, url2, net::URLRequestStatus(), 200, ResponseCookies(), |
| extension_data2); |
| message_loop.RunAllPending(); |
| EXPECT_EQ(id2, service.extension_id()); |
| EXPECT_EQ(url2, service.download_url()); |
| EXPECT_FALSE(service.install_path().empty()); |
| |
| // Make sure the correct crx contents were passed for the update call. |
| std::string file_contents; |
| EXPECT_TRUE(file_util::ReadFileToString(service.install_path(), |
| &file_contents)); |
| EXPECT_TRUE(extension_data2 == file_contents); |
| file_util::Delete(service.install_path(), false); |
| } |
| |
| // Test requests to both a Google server and a non-google server. This allows |
| // us to test various combinations of installed (ie roll call) and active |
| // (ie app launch) ping scenarios. The invariant is that each type of ping |
| // value should be present at most once per day, and can be calculated based |
| // on the delta between now and the last ping time (or in the case of active |
| // pings, that delta plus whether the app has been active). |
| static void TestGalleryRequests(int rollcall_ping_days, |
| int active_ping_days, |
| bool active_bit) { |
| MessageLoop message_loop; |
| BrowserThread ui_thread(BrowserThread::UI, &message_loop); |
| BrowserThread file_thread(BrowserThread::FILE, &message_loop); |
| |
| TestURLFetcherFactory factory; |
| URLFetcher::set_factory(&factory); |
| |
| // Set up 2 mock extensions, one with a google.com update url and one |
| // without. |
| ServiceForManifestTests service; |
| ExtensionList tmp; |
| GURL url1("http://clients2.google.com/service/update2/crx"); |
| GURL url2("http://www.somewebsite.com"); |
| service.CreateTestExtensions(1, 1, &tmp, &url1.possibly_invalid_spec(), |
| Extension::INTERNAL); |
| service.CreateTestExtensions(2, 1, &tmp, &url2.possibly_invalid_spec(), |
| Extension::INTERNAL); |
| EXPECT_EQ(2u, tmp.size()); |
| service.set_extensions(tmp); |
| |
| ExtensionPrefs* prefs = service.extension_prefs(); |
| const std::string& id = tmp[0]->id(); |
| Time now = Time::Now(); |
| if (rollcall_ping_days == 0) { |
| prefs->SetLastPingDay(id, now - TimeDelta::FromSeconds(15)); |
| } else if (rollcall_ping_days > 0) { |
| Time last_ping_day = now - |
| TimeDelta::FromDays(rollcall_ping_days) - |
| TimeDelta::FromSeconds(15); |
| prefs->SetLastPingDay(id, last_ping_day); |
| } |
| |
| // Store a value for the last day we sent an active ping. |
| if (active_ping_days == 0) { |
| prefs->SetLastActivePingDay(id, now - TimeDelta::FromSeconds(15)); |
| } else if (active_ping_days > 0) { |
| Time last_active_ping_day = now - |
| TimeDelta::FromDays(active_ping_days) - |
| TimeDelta::FromSeconds(15); |
| prefs->SetLastActivePingDay(id, last_active_ping_day); |
| } |
| if (active_bit) |
| prefs->SetActiveBit(id, true); |
| |
| ExtensionUpdater updater( |
| &service, service.extension_prefs(), service.pref_service(), |
| service.profile(), kUpdateFrequencySecs); |
| updater.Start(); |
| updater.set_blacklist_checks_enabled(false); |
| |
| // Make the updater do manifest fetching, and note the urls it tries to |
| // fetch. |
| std::vector<GURL> fetched_urls; |
| updater.CheckNow(); |
| TestURLFetcher* fetcher = |
| factory.GetFetcherByID(ExtensionUpdater::kManifestFetcherId); |
| EXPECT_TRUE(fetcher != NULL && fetcher->delegate() != NULL); |
| fetched_urls.push_back(fetcher->original_url()); |
| fetcher->delegate()->OnURLFetchComplete( |
| fetcher, fetched_urls[0], net::URLRequestStatus(), 500, |
| ResponseCookies(), ""); |
| fetcher = |
| factory.GetFetcherByID(ExtensionUpdater::kManifestFetcherId); |
| fetched_urls.push_back(fetcher->original_url()); |
| |
| // The urls could have been fetched in either order, so use the host to |
| // tell them apart and note the query each used. |
| std::string url1_query; |
| std::string url2_query; |
| if (fetched_urls[0].host() == url1.host()) { |
| url1_query = fetched_urls[0].query(); |
| url2_query = fetched_urls[1].query(); |
| } else if (fetched_urls[0].host() == url2.host()) { |
| url1_query = fetched_urls[1].query(); |
| url2_query = fetched_urls[0].query(); |
| } else { |
| NOTREACHED(); |
| } |
| |
| // First make sure the non-google query had no ping parameter. |
| std::string search_string = "ping%3D"; |
| EXPECT_TRUE(url2_query.find(search_string) == std::string::npos); |
| |
| // Now make sure the google query had the correct ping parameter. |
| bool ping_expected = false; |
| bool did_rollcall = false; |
| if (rollcall_ping_days != 0) { |
| search_string += "r%253D" + base::IntToString(rollcall_ping_days); |
| did_rollcall = true; |
| ping_expected = true; |
| } |
| if (active_bit && active_ping_days != 0) { |
| if (did_rollcall) |
| search_string += "%2526"; |
| search_string += "a%253D" + base::IntToString(active_ping_days); |
| ping_expected = true; |
| } |
| bool ping_found = url1_query.find(search_string) != std::string::npos; |
| EXPECT_EQ(ping_expected, ping_found) << "query was: " << url1_query |
| << " was looking for " << search_string; |
| } |
| |
| // This makes sure that the extension updater properly stores the results |
| // of a <daystart> tag from a manifest fetch in one of two cases: 1) This is |
| // the first time we fetched the extension, or 2) We sent a ping value of |
| // >= 1 day for the extension. |
| static void TestHandleManifestResults() { |
| ServiceForManifestTests service; |
| MessageLoop message_loop; |
| BrowserThread ui_thread(BrowserThread::UI, &message_loop); |
| ExtensionUpdater updater( |
| &service, service.extension_prefs(), service.pref_service(), |
| service.profile(), kUpdateFrequencySecs); |
| updater.Start(); |
| |
| GURL update_url("http://www.google.com/manifest"); |
| ExtensionList tmp; |
| service.CreateTestExtensions(1, 1, &tmp, &update_url.spec(), |
| Extension::INTERNAL); |
| service.set_extensions(tmp); |
| |
| ManifestFetchData fetch_data(update_url); |
| const Extension* extension = tmp[0]; |
| fetch_data.AddExtension(extension->id(), extension->VersionString(), |
| kNeverPingedData, |
| kEmptyUpdateUrlData); |
| UpdateManifest::Results results; |
| results.daystart_elapsed_seconds = 750; |
| |
| updater.HandleManifestResults(fetch_data, &results); |
| Time last_ping_day = |
| service.extension_prefs()->LastPingDay(extension->id()); |
| EXPECT_FALSE(last_ping_day.is_null()); |
| int64 seconds_diff = (Time::Now() - last_ping_day).InSeconds(); |
| EXPECT_LT(seconds_diff - results.daystart_elapsed_seconds, 5); |
| } |
| }; |
| |
| // Because we test some private methods of ExtensionUpdater, it's easer for the |
| // actual test code to live in ExtenionUpdaterTest methods instead of TEST_F |
| // subclasses where friendship with ExtenionUpdater is not inherited. |
| |
| TEST(ExtensionUpdaterTest, TestExtensionUpdateCheckRequests) { |
| ExtensionUpdaterTest::TestExtensionUpdateCheckRequests(false); |
| } |
| |
| TEST(ExtensionUpdaterTest, TestExtensionUpdateCheckRequestsPending) { |
| ExtensionUpdaterTest::TestExtensionUpdateCheckRequests(true); |
| } |
| |
| // This test is disabled on Mac, see http://crbug.com/26035. |
| TEST(ExtensionUpdaterTest, TestBlacklistUpdateCheckRequests) { |
| ExtensionUpdaterTest::TestBlacklistUpdateCheckRequests(); |
| } |
| |
| TEST(ExtensionUpdaterTest, TestUpdateUrlData) { |
| MessageLoop message_loop; |
| BrowserThread file_thread(BrowserThread::FILE, &message_loop); |
| |
| ExtensionUpdaterTest::TestUpdateUrlDataEmpty(); |
| ExtensionUpdaterTest::TestUpdateUrlDataSimple(); |
| ExtensionUpdaterTest::TestUpdateUrlDataCompound(); |
| ExtensionUpdaterTest::TestUpdateUrlDataFromGallery( |
| Extension::GalleryUpdateUrl(false).spec()); |
| ExtensionUpdaterTest::TestUpdateUrlDataFromGallery( |
| Extension::GalleryUpdateUrl(true).spec()); |
| } |
| |
| TEST(ExtensionUpdaterTest, TestDetermineUpdates) { |
| ExtensionUpdaterTest::TestDetermineUpdates(); |
| } |
| |
| TEST(ExtensionUpdaterTest, TestDetermineUpdatesPending) { |
| ExtensionUpdaterTest::TestDetermineUpdatesPending(); |
| } |
| |
| TEST(ExtensionUpdaterTest, TestMultipleManifestDownloading) { |
| ExtensionUpdaterTest::TestMultipleManifestDownloading(); |
| } |
| |
| TEST(ExtensionUpdaterTest, TestSingleExtensionDownloading) { |
| ExtensionUpdaterTest::TestSingleExtensionDownloading(false); |
| } |
| |
| TEST(ExtensionUpdaterTest, TestSingleExtensionDownloadingPending) { |
| ExtensionUpdaterTest::TestSingleExtensionDownloading(true); |
| } |
| |
| // This test is disabled on Mac, see http://crbug.com/26035. |
| TEST(ExtensionUpdaterTest, TestBlacklistDownloading) { |
| ExtensionUpdaterTest::TestBlacklistDownloading(); |
| } |
| |
| TEST(ExtensionUpdaterTest, TestMultipleExtensionDownloading) { |
| ExtensionUpdaterTest::TestMultipleExtensionDownloading(); |
| } |
| |
| TEST(ExtensionUpdaterTest, TestGalleryRequests) { |
| // We want to test a variety of combinations of expected ping conditions for |
| // rollcall and active pings. |
| int ping_cases[] = { ManifestFetchData::kNeverPinged, 0, 1, 5 }; |
| |
| for (size_t i = 0; i < arraysize(ping_cases); i++) { |
| for (size_t j = 0; j < arraysize(ping_cases); j++) { |
| for (size_t k = 0; k < 2; k++) { |
| int rollcall_ping_days = ping_cases[i]; |
| int active_ping_days = ping_cases[j]; |
| // Skip cases where rollcall_ping_days == -1, but active_ping_days > 0, |
| // because rollcall_ping_days == -1 means the app was just installed and |
| // this is the first update check after installation. |
| if (rollcall_ping_days == ManifestFetchData::kNeverPinged && |
| active_ping_days > 0) |
| continue; |
| |
| bool active_bit = k > 0; |
| ExtensionUpdaterTest::TestGalleryRequests( |
| rollcall_ping_days, active_ping_days, active_bit); |
| ASSERT_FALSE(HasFailure()) << |
| " rollcall_ping_days=" << ping_cases[i] << |
| " active_ping_days=" << ping_cases[j] << |
| " active_bit=" << active_bit; |
| } |
| } |
| } |
| } |
| |
| TEST(ExtensionUpdaterTest, TestHandleManifestResults) { |
| ExtensionUpdaterTest::TestHandleManifestResults(); |
| } |
| |
| TEST(ExtensionUpdaterTest, TestManifestFetchesBuilderAddExtension) { |
| MessageLoop message_loop; |
| BrowserThread file_thread(BrowserThread::FILE, &message_loop); |
| |
| MockService service; |
| ManifestFetchesBuilder builder(&service, service.extension_prefs()); |
| |
| // Non-internal non-external extensions should be rejected. |
| { |
| ExtensionList extensions; |
| service.CreateTestExtensions(1, 1, &extensions, NULL, Extension::INVALID); |
| ASSERT_FALSE(extensions.empty()); |
| builder.AddExtension(*extensions[0]); |
| EXPECT_TRUE(builder.GetFetches().empty()); |
| } |
| |
| // Extensions with invalid update URLs should be rejected. |
| builder.AddPendingExtension( |
| GenerateId("foo"), PendingExtensionInfo(GURL("http:google.com:foo"), |
| &ShouldInstallExtensionsOnly, |
| false, false, true, false, |
| Extension::INTERNAL)); |
| EXPECT_TRUE(builder.GetFetches().empty()); |
| |
| // Extensions with empty IDs should be rejected. |
| builder.AddPendingExtension( |
| "", PendingExtensionInfo(GURL(), &ShouldInstallExtensionsOnly, |
| false, false, true, false, |
| Extension::INTERNAL)); |
| EXPECT_TRUE(builder.GetFetches().empty()); |
| |
| // TODO(akalin): Test that extensions with empty update URLs |
| // converted from user scripts are rejected. |
| |
| // Extensions with empty update URLs should have a default one |
| // filled in. |
| builder.AddPendingExtension( |
| GenerateId("foo"), PendingExtensionInfo(GURL(), |
| &ShouldInstallExtensionsOnly, |
| false, false, true, false, |
| Extension::INTERNAL)); |
| std::vector<ManifestFetchData*> fetches = builder.GetFetches(); |
| ASSERT_EQ(1u, fetches.size()); |
| scoped_ptr<ManifestFetchData> fetch(fetches[0]); |
| fetches.clear(); |
| EXPECT_FALSE(fetch->base_url().is_empty()); |
| EXPECT_FALSE(fetch->full_url().is_empty()); |
| } |
| |
| TEST(ExtensionUpdaterTest, TestStartUpdateCheckMemory) { |
| MessageLoop message_loop; |
| BrowserThread ui_thread(BrowserThread::UI, &message_loop); |
| BrowserThread file_thread(BrowserThread::FILE, &message_loop); |
| |
| ServiceForManifestTests service; |
| TestURLFetcherFactory factory; |
| URLFetcher::set_factory(&factory); |
| ExtensionUpdater updater( |
| &service, service.extension_prefs(), service.pref_service(), |
| service.profile(), kUpdateFrequencySecs); |
| updater.Start(); |
| updater.StartUpdateCheck(new ManifestFetchData(GURL())); |
| // This should delete the newly-created ManifestFetchData. |
| updater.StartUpdateCheck(new ManifestFetchData(GURL())); |
| // This should add into |manifests_pending_|. |
| updater.StartUpdateCheck(new ManifestFetchData( |
| GURL("http://www.google.com"))); |
| // This should clear out |manifests_pending_|. |
| updater.Stop(); |
| } |
| |
| TEST(ExtensionUpdaterTest, TestCheckSoon) { |
| MessageLoop message_loop; |
| BrowserThread ui_thread(BrowserThread::UI, &message_loop); |
| BrowserThread file_thread(BrowserThread::FILE, &message_loop); |
| |
| ServiceForManifestTests service; |
| TestURLFetcherFactory factory; |
| URLFetcher::set_factory(&factory); |
| ExtensionUpdater updater( |
| &service, service.extension_prefs(), service.pref_service(), |
| service.profile(), kUpdateFrequencySecs); |
| EXPECT_FALSE(updater.WillCheckSoon()); |
| updater.Start(); |
| EXPECT_FALSE(updater.WillCheckSoon()); |
| updater.CheckSoon(); |
| EXPECT_TRUE(updater.WillCheckSoon()); |
| updater.CheckSoon(); |
| EXPECT_TRUE(updater.WillCheckSoon()); |
| ExtensionUpdaterTest::SimulateCheckSoon(updater, &message_loop); |
| EXPECT_FALSE(updater.WillCheckSoon()); |
| updater.CheckSoon(); |
| EXPECT_TRUE(updater.WillCheckSoon()); |
| updater.Stop(); |
| EXPECT_FALSE(updater.WillCheckSoon()); |
| } |
| |
| // TODO(asargent) - (http://crbug.com/12780) add tests for: |
| // -prodversionmin (shouldn't update if browser version too old) |
| // -manifests & updates arriving out of order / interleaved |
| // -malformed update url (empty, file://, has query, has a # fragment, etc.) |
| // -An extension gets uninstalled while updates are in progress (so it doesn't |
| // "come back from the dead") |
| // -An extension gets manually updated to v3 while we're downloading v2 (ie |
| // you don't get downgraded accidentally) |
| // -An update manifest mentions multiple updates |