| // Copyright 2013 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 "components/precache/core/precache_fetcher.h" |
| |
| #include <stdint.h> |
| |
| #include <cstring> |
| #include <memory> |
| #include <set> |
| #include <string> |
| #include <utility> |
| #include <vector> |
| |
| #include "base/bind.h" |
| #include "base/callback.h" |
| #include "base/command_line.h" |
| #include "base/compiler_specific.h" |
| #include "base/files/file_path.h" |
| #include "base/files/scoped_temp_dir.h" |
| #include "base/memory/ptr_util.h" |
| #include "base/memory/ref_counted.h" |
| #include "base/memory/weak_ptr.h" |
| #include "base/run_loop.h" |
| #include "base/single_thread_task_runner.h" |
| #include "base/strings/stringprintf.h" |
| #include "base/test/histogram_tester.h" |
| #include "base/test/scoped_task_environment.h" |
| #include "base/threading/thread_task_runner_handle.h" |
| #include "components/precache/core/precache_database.h" |
| #include "components/precache/core/precache_switches.h" |
| #include "components/precache/core/proto/precache.pb.h" |
| #include "components/precache/core/proto/unfinished_work.pb.h" |
| #include "net/base/escape.h" |
| #include "net/base/load_flags.h" |
| #include "net/http/http_response_headers.h" |
| #include "net/http/http_response_info.h" |
| #include "net/http/http_status_code.h" |
| #include "net/url_request/test_url_fetcher_factory.h" |
| #include "net/url_request/url_request_status.h" |
| #include "net/url_request/url_request_test_util.h" |
| #include "testing/gmock/include/gmock/gmock.h" |
| #include "testing/gtest/include/gtest/gtest.h" |
| #include "url/gurl.h" |
| |
| namespace precache { |
| |
| namespace { |
| |
| using ::testing::_; |
| using ::testing::ElementsAre; |
| using ::testing::NotNull; |
| using ::testing::Property; |
| |
| const char kConfigURL[] = "http://config-url.com"; |
| const char kManifestURLPrefix[] = "http://manifest-url-prefix.com/"; |
| const char kCustomConfigURL[] = "http://custom-config-url.com"; |
| const char kCustomManifestURLPrefix[] = |
| "http://custom-manifest-url-prefix.com/"; |
| const char kManifestFetchFailureURL[] = |
| "http://manifest-url-prefix.com/manifest-fetch-failure.com"; |
| const char kBadManifestURL[] = |
| "http://manifest-url-prefix.com/bad-manifest.com"; |
| const char kGoodManifestURL[] = |
| "http://manifest-url-prefix.com/good-manifest.com"; |
| const char kCustomGoodManifestURL[] = |
| "http://custom-manifest-url-prefix.com/good-manifest.com"; |
| const char kResourceFetchFailureURL[] = "http://resource-fetch-failure.com"; |
| const char kGoodResourceURL[] = "http://good-resource.com"; |
| const char kGoodResourceURLA[] = "http://good-resource.com/a"; |
| const char kGoodResourceURLB[] = "http://good-resource.com/b"; |
| const char kGoodResourceURLC[] = "http://good-resource.com/c"; |
| const char kGoodResourceURLD[] = "http://good-resource.com/d"; |
| const char kForcedStartingURLManifestURL[] = |
| "http://manifest-url-prefix.com/forced-starting-url.com"; |
| const uint32_t kExperimentID = 123; |
| |
| } // namespace |
| |
| class TestURLFetcherCallback { |
| public: |
| TestURLFetcherCallback() : total_response_bytes_(0) {} |
| |
| std::unique_ptr<net::FakeURLFetcher> CreateURLFetcher( |
| const GURL& url, |
| net::URLFetcherDelegate* delegate, |
| const std::string& response_data, |
| net::HttpStatusCode response_code, |
| net::URLRequestStatus::Status status) { |
| std::unique_ptr<net::FakeURLFetcher> fetcher(new net::FakeURLFetcher( |
| url, delegate, response_data, response_code, status)); |
| |
| total_response_bytes_ += response_data.size(); |
| requested_urls_.push_back(url); |
| |
| return fetcher; |
| } |
| |
| const std::vector<GURL>& requested_urls() const { return requested_urls_; } |
| |
| void clear_requested_urls() { requested_urls_.clear(); } |
| |
| int total_response_bytes() const { return total_response_bytes_; } |
| |
| private: |
| std::vector<GURL> requested_urls_; |
| int total_response_bytes_; |
| }; |
| |
| class TestPrecacheDelegate : public PrecacheFetcher::PrecacheDelegate { |
| public: |
| TestPrecacheDelegate() |
| : on_done_was_called_(false) {} |
| |
| void OnDone() override { |
| LOG(INFO) << "OnDone"; |
| on_done_was_called_ = true; |
| } |
| |
| void OnManifestFetched(const std::string& host, |
| const PrecacheManifest& manifest) override { |
| hosts.push_back(host); |
| } |
| |
| bool was_on_done_called() const { |
| return on_done_was_called_; |
| } |
| |
| void clear_manifest_hosts() { hosts.clear(); } |
| |
| std::vector<std::string> get_manifest_hosts() const { return hosts; } |
| |
| private: |
| bool on_done_was_called_; |
| std::vector<std::string> hosts; |
| }; |
| |
| class MockURLFetcherFactory : public net::URLFetcherFactory { |
| public: |
| typedef net::URLFetcher* DoURLFetcher( |
| int id, |
| const GURL& url, |
| net::URLFetcher::RequestType request_type, |
| net::URLFetcherDelegate* delegate); |
| |
| std::unique_ptr<net::URLFetcher> CreateURLFetcher( |
| int id, |
| const GURL& url, |
| net::URLFetcher::RequestType request_type, |
| net::URLFetcherDelegate* delegate) override { |
| return base::WrapUnique( |
| DoCreateURLFetcher(id, url, request_type, delegate)); |
| } |
| |
| // The method to mock out, instead of CreateURLFetcher. This is necessary |
| // because gmock can't handle move-only types such as scoped_ptr. |
| MOCK_METHOD4(DoCreateURLFetcher, DoURLFetcher); |
| |
| // A fake successful response. When the action runs, it saves off a pointer to |
| // the FakeURLFetcher in its output parameter for later inspection. |
| testing::Action<DoURLFetcher> RespondWith(const std::string& body, |
| net::FakeURLFetcher** fetcher) { |
| return RespondWith(body, [](net::FakeURLFetcher* fetcher) { |
| fetcher->set_response_code(net::HTTP_OK); |
| }, fetcher); |
| } |
| |
| // A fake custom response. When the action runs, it runs the given modifier to |
| // customize the FakeURLFetcher, and then saves off a pointer to the |
| // FakeURLFetcher in its output parameter for later inspection. The modifier |
| // should be a functor that takes a FakeURLFetcher* and returns void. |
| template <typename F> |
| testing::Action<DoURLFetcher> RespondWith(const std::string& body, |
| F modifier, |
| net::FakeURLFetcher** fetcher) { |
| return testing::MakeAction( |
| new FakeResponseAction<F>(body, modifier, fetcher)); |
| } |
| |
| private: |
| template <typename F> |
| class FakeResponseAction : public testing::ActionInterface<DoURLFetcher> { |
| public: |
| FakeResponseAction(const std::string& body, |
| F modifier, |
| net::FakeURLFetcher** fetcher) |
| : body_(body), modifier_(modifier), fetcher_(fetcher) {} |
| |
| net::URLFetcher* Perform( |
| const testing::tuple<int, |
| const GURL&, |
| net::URLFetcher::RequestType, |
| net::URLFetcherDelegate*>& args) { |
| auto* fetcher = new net::FakeURLFetcher( |
| testing::get<1>(args), testing::get<3>(args), body_, net::HTTP_OK, |
| net::URLRequestStatus::SUCCESS); |
| modifier_(fetcher); |
| if (fetcher_) |
| *fetcher_ = fetcher; |
| return fetcher; |
| } |
| |
| private: |
| std::string body_; |
| F modifier_; |
| net::FakeURLFetcher** fetcher_; |
| }; |
| }; |
| |
| class PrecacheFetcherFetcherTest : public testing::Test { |
| public: |
| PrecacheFetcherFetcherTest() |
| : scoped_task_environment_( |
| base::test::ScopedTaskEnvironment::MainThreadType::UI), |
| request_context_(new net::TestURLRequestContextGetter( |
| base::ThreadTaskRunnerHandle::Get())), |
| scoped_url_fetcher_factory_(&factory_), |
| callback_(base::Bind(&PrecacheFetcherFetcherTest::Callback, |
| base::Unretained(this))) {} |
| |
| MOCK_METHOD1(Callback, void(const PrecacheFetcher::Fetcher&)); |
| |
| protected: |
| base::test::ScopedTaskEnvironment scoped_task_environment_; |
| scoped_refptr<net::TestURLRequestContextGetter> request_context_; |
| MockURLFetcherFactory factory_; |
| net::ScopedURLFetcherFactory scoped_url_fetcher_factory_; |
| base::Callback<void(const PrecacheFetcher::Fetcher&)> callback_; |
| }; |
| |
| void CacheMiss(net::FakeURLFetcher* fetcher) { |
| fetcher->set_status(net::URLRequestStatus(net::URLRequestStatus::FAILED, |
| net::ERR_CACHE_MISS)); |
| } |
| |
| void HasETag(net::FakeURLFetcher* fetcher) { |
| std::string raw_headers("HTTP/1.1 200 OK\0ETag: foo\0\0", 27); |
| fetcher->set_response_headers( |
| make_scoped_refptr(new net::HttpResponseHeaders(raw_headers))); |
| } |
| |
| TEST_F(PrecacheFetcherFetcherTest, Config) { |
| GURL url(kConfigURL); |
| |
| net::FakeURLFetcher* fetcher = nullptr; |
| EXPECT_CALL(factory_, DoCreateURLFetcher(_, url, net::URLFetcher::GET, _)) |
| .WillOnce(factory_.RespondWith("", &fetcher)); |
| EXPECT_CALL(*this, |
| Callback(Property(&PrecacheFetcher::Fetcher::network_url_fetcher, |
| NotNull()))); |
| |
| PrecacheFetcher::Fetcher precache_fetcher( |
| request_context_.get(), url, url.host(), callback_, |
| false /* is_resource_request */, SIZE_MAX, false /* revalidation_only */); |
| |
| base::RunLoop().RunUntilIdle(); |
| |
| ASSERT_NE(nullptr, fetcher); |
| EXPECT_EQ(kNoTracking, fetcher->GetLoadFlags()); |
| } |
| |
| TEST_F(PrecacheFetcherFetcherTest, ResourceNotInCache) { |
| GURL url(kGoodResourceURL); |
| |
| net::FakeURLFetcher *fetcher1 = nullptr, *fetcher2 = nullptr; |
| EXPECT_CALL(factory_, DoCreateURLFetcher(_, url, net::URLFetcher::GET, _)) |
| .WillOnce(factory_.RespondWith("", CacheMiss, &fetcher1)) |
| .WillOnce(factory_.RespondWith("", &fetcher2)); |
| EXPECT_CALL(*this, |
| Callback(Property(&PrecacheFetcher::Fetcher::network_url_fetcher, |
| NotNull()))); |
| |
| PrecacheFetcher::Fetcher precache_fetcher( |
| request_context_.get(), url, url.host(), callback_, |
| true /* is_resource_request */, SIZE_MAX, false /* revalidation_only */); |
| |
| base::RunLoop().RunUntilIdle(); |
| |
| ASSERT_NE(nullptr, fetcher1); |
| EXPECT_EQ( |
| net::LOAD_ONLY_FROM_CACHE | net::LOAD_SKIP_CACHE_VALIDATION | kNoTracking, |
| fetcher1->GetLoadFlags()); |
| ASSERT_NE(nullptr, fetcher2); |
| EXPECT_EQ(net::LOAD_VALIDATE_CACHE | kNoTracking, fetcher2->GetLoadFlags()); |
| } |
| |
| TEST_F(PrecacheFetcherFetcherTest, ResourceHasValidators) { |
| GURL url(kGoodResourceURL); |
| |
| net::FakeURLFetcher *fetcher1 = nullptr, *fetcher2 = nullptr; |
| EXPECT_CALL(factory_, DoCreateURLFetcher(_, url, net::URLFetcher::GET, _)) |
| .WillOnce(factory_.RespondWith("", HasETag, &fetcher1)) |
| .WillOnce(factory_.RespondWith("", &fetcher2)); |
| EXPECT_CALL(*this, |
| Callback(Property(&PrecacheFetcher::Fetcher::network_url_fetcher, |
| NotNull()))); |
| |
| PrecacheFetcher::Fetcher precache_fetcher( |
| request_context_.get(), url, url.host(), callback_, |
| true /* is_resource_request */, SIZE_MAX, false /* revalidation_only */); |
| |
| base::RunLoop().RunUntilIdle(); |
| |
| ASSERT_NE(nullptr, fetcher1); |
| EXPECT_EQ( |
| net::LOAD_ONLY_FROM_CACHE | net::LOAD_SKIP_CACHE_VALIDATION | kNoTracking, |
| fetcher1->GetLoadFlags()); |
| ASSERT_NE(nullptr, fetcher2); |
| EXPECT_EQ(net::LOAD_VALIDATE_CACHE | kNoTracking, fetcher2->GetLoadFlags()); |
| } |
| |
| TEST_F(PrecacheFetcherFetcherTest, ResourceHasNoValidators) { |
| GURL url(kGoodResourceURL); |
| |
| net::FakeURLFetcher* fetcher = nullptr; |
| EXPECT_CALL(factory_, DoCreateURLFetcher(_, url, net::URLFetcher::GET, _)) |
| .WillOnce(factory_.RespondWith("", &fetcher)); |
| EXPECT_CALL(*this, |
| Callback(Property(&PrecacheFetcher::Fetcher::network_url_fetcher, |
| nullptr))); // It never reached the network. |
| |
| PrecacheFetcher::Fetcher precache_fetcher( |
| request_context_.get(), url, url.host(), callback_, |
| true /* is_resource_request */, SIZE_MAX, false /* revalidation_only */); |
| |
| base::RunLoop().RunUntilIdle(); |
| |
| EXPECT_EQ( |
| net::LOAD_ONLY_FROM_CACHE | net::LOAD_SKIP_CACHE_VALIDATION | kNoTracking, |
| fetcher->GetLoadFlags()); |
| } |
| |
| TEST_F(PrecacheFetcherFetcherTest, RevalidationOnlyResourceNotInCache) { |
| GURL url(kGoodResourceURL); |
| |
| net::FakeURLFetcher* fetcher = nullptr; |
| EXPECT_CALL(factory_, DoCreateURLFetcher(_, url, net::URLFetcher::GET, _)) |
| .WillOnce(factory_.RespondWith("", CacheMiss, &fetcher)); |
| EXPECT_CALL(*this, |
| Callback(Property(&PrecacheFetcher::Fetcher::network_url_fetcher, |
| nullptr))); // It never reached the network. |
| |
| PrecacheFetcher::Fetcher precache_fetcher( |
| request_context_.get(), url, url.host(), callback_, |
| true /* is_resource_request */, SIZE_MAX, true /* revalidation_only */); |
| |
| base::RunLoop().RunUntilIdle(); |
| |
| ASSERT_NE(nullptr, fetcher); |
| EXPECT_EQ( |
| net::LOAD_ONLY_FROM_CACHE | net::LOAD_SKIP_CACHE_VALIDATION | kNoTracking, |
| fetcher->GetLoadFlags()); |
| } |
| |
| TEST_F(PrecacheFetcherFetcherTest, RevalidationOnlyResourceHasValidators) { |
| GURL url(kGoodResourceURL); |
| |
| net::FakeURLFetcher *fetcher1 = nullptr, *fetcher2 = nullptr; |
| EXPECT_CALL(factory_, DoCreateURLFetcher(_, url, net::URLFetcher::GET, _)) |
| .WillOnce(factory_.RespondWith("", HasETag, &fetcher1)) |
| .WillOnce(factory_.RespondWith("", &fetcher2)); |
| EXPECT_CALL(*this, |
| Callback(Property(&PrecacheFetcher::Fetcher::network_url_fetcher, |
| NotNull()))); |
| |
| PrecacheFetcher::Fetcher precache_fetcher( |
| request_context_.get(), url, url.host(), callback_, |
| true /* is_resource_request */, SIZE_MAX, true /* revalidation_only */); |
| |
| base::RunLoop().RunUntilIdle(); |
| |
| ASSERT_NE(nullptr, fetcher1); |
| EXPECT_EQ( |
| net::LOAD_ONLY_FROM_CACHE | net::LOAD_SKIP_CACHE_VALIDATION | kNoTracking, |
| fetcher1->GetLoadFlags()); |
| ASSERT_NE(nullptr, fetcher2); |
| EXPECT_EQ(net::LOAD_VALIDATE_CACHE | kNoTracking, fetcher2->GetLoadFlags()); |
| } |
| |
| TEST_F(PrecacheFetcherFetcherTest, RevalidationOnlyResourceHasNoValidators) { |
| GURL url(kGoodResourceURL); |
| |
| net::FakeURLFetcher* fetcher = nullptr; |
| EXPECT_CALL(factory_, DoCreateURLFetcher(_, url, net::URLFetcher::GET, _)) |
| .WillOnce(factory_.RespondWith("", &fetcher)); |
| EXPECT_CALL(*this, |
| Callback(Property(&PrecacheFetcher::Fetcher::network_url_fetcher, |
| nullptr))); // It never reached the network. |
| |
| PrecacheFetcher::Fetcher precache_fetcher( |
| request_context_.get(), url, url.host(), callback_, |
| true /* is_resource_request */, SIZE_MAX, true /* revalidation_only */); |
| |
| base::RunLoop().RunUntilIdle(); |
| |
| EXPECT_EQ( |
| net::LOAD_ONLY_FROM_CACHE | net::LOAD_SKIP_CACHE_VALIDATION | kNoTracking, |
| fetcher->GetLoadFlags()); |
| } |
| |
| TEST_F(PrecacheFetcherFetcherTest, ResourceTooBig) { |
| GURL url(kGoodResourceURL); |
| |
| EXPECT_CALL(factory_, DoCreateURLFetcher(_, url, net::URLFetcher::GET, _)) |
| // Cache request will fail, so that a network request is made. Only |
| // network requests are byte-capped. |
| .WillOnce(factory_.RespondWith("", CacheMiss, nullptr)) |
| .WillOnce(factory_.RespondWith(std::string(100, '.'), nullptr)); |
| |
| // The callback should be called even though the download was cancelled, so |
| // that the next download can start. The network_url_fetcher within should be |
| // null, to signify that either the network was never reached (which will be |
| // flagged as an error due to the expectation above) or it was requested but |
| // cancelled (which is the desired behavior). |
| EXPECT_CALL(*this, |
| Callback(Property(&PrecacheFetcher::Fetcher::network_url_fetcher, |
| nullptr))); |
| |
| PrecacheFetcher::Fetcher precache_fetcher( |
| request_context_.get(), url, url.host(), callback_, |
| true /* is_resource_request */, 99 /* max_bytes */, |
| false /* revalidation_only */); |
| |
| base::RunLoop().RunUntilIdle(); |
| } |
| |
| class PrecacheFetcherTest : public testing::Test { |
| public: |
| PrecacheFetcherTest() |
| : scoped_task_environment_( |
| base::test::ScopedTaskEnvironment::MainThreadType::UI), |
| task_runner_(base::ThreadTaskRunnerHandle::Get()), |
| request_context_(new net::TestURLRequestContextGetter( |
| base::ThreadTaskRunnerHandle::Get())), |
| factory_(NULL, |
| base::Bind(&TestURLFetcherCallback::CreateURLFetcher, |
| base::Unretained(&url_callback_))), |
| expected_total_response_bytes_(0), |
| parallel_fetches_beyond_capacity_(false) {} |
| |
| void UpdatePrecacheReferrerHost(const std::string& hostname, |
| int64_t manifest_id) { |
| precache_database_.UpdatePrecacheReferrerHost(hostname, manifest_id, |
| base::Time()); |
| } |
| |
| void RecordURLPrefetch(const GURL& url, const std::string& referrer_host) { |
| precache_database_.RecordURLPrefetch(url, referrer_host, base::Time::Now(), |
| false /* was_cached */, |
| 1000 /* size */); |
| } |
| |
| void RecordURLNonPrefetch(const GURL& url) { |
| net::HttpResponseInfo info; |
| info.was_cached = true; |
| info.headers = new net::HttpResponseHeaders(std::string()); |
| precache_database_.RecordURLNonPrefetch(url, base::Time::Now(), info, |
| 1000 /* size */, 0 /* host_rank */, |
| false /* is_connection_cellular */); |
| } |
| |
| protected: |
| void SetUp() override { |
| ASSERT_TRUE(scoped_temp_dir_.CreateUniqueTempDir()); |
| base::FilePath db_path = scoped_temp_dir_.GetPath().Append( |
| base::FilePath(FILE_PATH_LITERAL("precache_database"))); |
| precache_database_.Init(db_path); |
| } |
| void SetDefaultFlags() { |
| base::CommandLine::ForCurrentProcess()->AppendSwitchASCII( |
| switches::kPrecacheConfigSettingsURL, kConfigURL); |
| base::CommandLine::ForCurrentProcess()->AppendSwitchASCII( |
| switches::kPrecacheManifestURLPrefix, kManifestURLPrefix); |
| } |
| |
| // Posts a task to check if more parallel fetches of precache manifest and |
| // resource URLs were attempted beyond the fetcher pool maximum defined |
| // capacity. The task will be posted repeatedly until such condition is met. |
| void CheckUntilParallelFetchesBeyondCapacity( |
| const PrecacheFetcher* precache_fetcher) { |
| if (!precache_fetcher->pool_.IsAvailable() && |
| (!precache_fetcher->top_hosts_to_fetch_.empty() || |
| !precache_fetcher->resources_to_fetch_.empty())) { |
| parallel_fetches_beyond_capacity_ = true; |
| return; |
| } |
| |
| // Check again after allowing the message loop to process some messages. |
| base::ThreadTaskRunnerHandle::Get()->PostTask( |
| FROM_HERE, |
| base::Bind( |
| &PrecacheFetcherTest::CheckUntilParallelFetchesBeyondCapacity, |
| base::Unretained(this), precache_fetcher)); |
| } |
| |
| const scoped_refptr<base::SingleThreadTaskRunner>& task_runner() const { |
| return task_runner_; |
| } |
| |
| // To allow friend access. |
| void Flush() { precache_database_.Flush(); } |
| |
| // Must be declared first so that it is destroyed last. |
| base::test::ScopedTaskEnvironment scoped_task_environment_; |
| scoped_refptr<base::SingleThreadTaskRunner> task_runner_; |
| scoped_refptr<net::TestURLRequestContextGetter> request_context_; |
| TestURLFetcherCallback url_callback_; |
| net::FakeURLFetcherFactory factory_; |
| TestPrecacheDelegate precache_delegate_; |
| base::ScopedTempDir scoped_temp_dir_; |
| PrecacheDatabase precache_database_; |
| int expected_total_response_bytes_; |
| |
| // True if more parallel fetches were attempted beyond the fetcher pool |
| // maximum capacity. |
| bool parallel_fetches_beyond_capacity_; |
| }; |
| |
| TEST_F(PrecacheFetcherTest, FullPrecache) { |
| SetDefaultFlags(); |
| |
| std::unique_ptr<PrecacheUnfinishedWork> unfinished_work( |
| new PrecacheUnfinishedWork()); |
| unfinished_work->set_start_time(base::Time::UnixEpoch().ToInternalValue()); |
| unfinished_work->add_top_host()->set_hostname("manifest-fetch-failure.com"); |
| unfinished_work->add_top_host()->set_hostname("bad-manifest.com"); |
| unfinished_work->add_top_host()->set_hostname("good-manifest.com"); |
| unfinished_work->add_top_host()->set_hostname("not-in-top-3.com"); |
| |
| PrecacheConfigurationSettings config; |
| config.set_top_sites_count(3); |
| config.add_forced_site("forced-starting-url.com"); |
| // Duplicate starting URL, the manifest for this should only be fetched once. |
| config.add_forced_site("good-manifest.com"); |
| |
| PrecacheManifest good_manifest; |
| good_manifest.add_resource()->set_url(kResourceFetchFailureURL); |
| good_manifest.add_resource(); // Resource with no URL, should not be fetched. |
| good_manifest.add_resource()->set_url(kGoodResourceURL); |
| |
| factory_.SetFakeResponse(GURL(kConfigURL), config.SerializeAsString(), |
| net::HTTP_OK, net::URLRequestStatus::SUCCESS); |
| factory_.SetFakeResponse(GURL(kManifestFetchFailureURL), "", |
| net::HTTP_INTERNAL_SERVER_ERROR, |
| net::URLRequestStatus::FAILED); |
| factory_.SetFakeResponse(GURL(kBadManifestURL), "bad protobuf", net::HTTP_OK, |
| net::URLRequestStatus::SUCCESS); |
| factory_.SetFakeResponse(GURL(kGoodManifestURL), |
| good_manifest.SerializeAsString(), net::HTTP_OK, |
| net::URLRequestStatus::SUCCESS); |
| factory_.SetFakeResponse(GURL(kResourceFetchFailureURL), |
| "", net::HTTP_INTERNAL_SERVER_ERROR, |
| net::URLRequestStatus::FAILED); |
| factory_.SetFakeResponse(GURL(kGoodResourceURL), "good", net::HTTP_OK, |
| net::URLRequestStatus::SUCCESS); |
| factory_.SetFakeResponse(GURL(kForcedStartingURLManifestURL), |
| PrecacheManifest().SerializeAsString(), net::HTTP_OK, |
| net::URLRequestStatus::SUCCESS); |
| |
| base::HistogramTester histogram; |
| |
| { |
| PrecacheFetcher precache_fetcher( |
| request_context_.get(), GURL(), std::string(), |
| std::move(unfinished_work), kExperimentID, |
| precache_database_.GetWeakPtr(), task_runner(), &precache_delegate_); |
| precache_fetcher.Start(); |
| |
| base::RunLoop().RunUntilIdle(); |
| |
| // Destroy the PrecacheFetcher after it has finished, to record metrics. |
| } |
| |
| std::vector<GURL> expected_requested_urls; |
| expected_requested_urls.emplace_back(kConfigURL); |
| expected_requested_urls.emplace_back(kManifestFetchFailureURL); |
| expected_requested_urls.emplace_back(kBadManifestURL); |
| expected_requested_urls.emplace_back(kGoodManifestURL); |
| expected_requested_urls.emplace_back(kForcedStartingURLManifestURL); |
| expected_requested_urls.emplace_back(kResourceFetchFailureURL); |
| expected_requested_urls.emplace_back(kGoodResourceURL); |
| |
| EXPECT_EQ(expected_requested_urls, url_callback_.requested_urls()); |
| |
| std::vector<std::string> expected_manifest_hosts = { |
| "good-manifest.com", "forced-starting-url.com"}; |
| EXPECT_EQ(expected_manifest_hosts, precache_delegate_.get_manifest_hosts()); |
| EXPECT_TRUE(precache_delegate_.was_on_done_called()); |
| |
| histogram.ExpectUniqueSample("Precache.Fetch.PercentCompleted", 100, 1); |
| histogram.ExpectUniqueSample("Precache.Fetch.ResponseBytes.Total", |
| url_callback_.total_response_bytes(), 1); |
| histogram.ExpectTotalCount("Precache.Fetch.TimeToComplete", 1); |
| } |
| |
| class PrecacheFetcherResourceSelectionTest |
| : public PrecacheFetcherTest, |
| public testing::WithParamInterface<PrecacheResourceSelection> { |
| public: |
| // These bitsets are asymmetric and multibyte, in order to test the orderings. |
| |
| // Set bits for kGoodResourceURL, kGoodResourceURLC and kGoodResourceURLD. |
| static PrecacheResourceSelection DeprecatedBitset() { |
| PrecacheResourceSelection ret; |
| ret.set_deprecated_bitset(0b110000000001); |
| return ret; |
| } |
| |
| // Set bits for kGoodResourceURL, kGoodResourceURLC and kGoodResourceURLD. |
| static PrecacheResourceSelection Bitset() { |
| PrecacheResourceSelection ret; |
| ret.set_bitset("\x01\x0c"); |
| return ret; |
| } |
| }; |
| |
| TEST_P(PrecacheFetcherResourceSelectionTest, Basic) { |
| SetDefaultFlags(); |
| |
| std::unique_ptr<PrecacheUnfinishedWork> unfinished_work( |
| new PrecacheUnfinishedWork()); |
| unfinished_work->add_top_host()->set_hostname("good-manifest.com"); |
| unfinished_work->set_start_time(base::Time::UnixEpoch().ToInternalValue()); |
| |
| PrecacheConfigurationSettings config; |
| |
| PrecacheManifest good_manifest; |
| PrecacheResourceSelection resource_selection; |
| good_manifest.add_resource()->set_url(kGoodResourceURL); |
| good_manifest.add_resource()->set_url(kGoodResourceURLA); |
| for (int i = 0; i < 8; ++i) |
| good_manifest.add_resource()->set_url(kGoodResourceURLB); |
| good_manifest.add_resource()->set_url(kGoodResourceURLC); |
| good_manifest.add_resource()->set_url(kGoodResourceURLD); |
| |
| (*good_manifest.mutable_experiments() |
| ->mutable_resources_by_experiment_group())[kExperimentID] = GetParam(); |
| |
| factory_.SetFakeResponse(GURL(kConfigURL), config.SerializeAsString(), |
| net::HTTP_OK, net::URLRequestStatus::SUCCESS); |
| factory_.SetFakeResponse(GURL(kGoodManifestURL), |
| good_manifest.SerializeAsString(), net::HTTP_OK, |
| net::URLRequestStatus::SUCCESS); |
| factory_.SetFakeResponse(GURL(kGoodResourceURL), "good", net::HTTP_OK, |
| net::URLRequestStatus::SUCCESS); |
| factory_.SetFakeResponse(GURL(kGoodResourceURLC), "good URL B", net::HTTP_OK, |
| net::URLRequestStatus::SUCCESS); |
| factory_.SetFakeResponse(GURL(kGoodResourceURLD), "good URL D", net::HTTP_OK, |
| net::URLRequestStatus::SUCCESS); |
| |
| base::HistogramTester histogram; |
| |
| { |
| PrecacheFetcher precache_fetcher( |
| request_context_.get(), GURL(), std::string(), |
| std::move(unfinished_work), kExperimentID, |
| precache_database_.GetWeakPtr(), task_runner(), &precache_delegate_); |
| precache_fetcher.Start(); |
| |
| base::RunLoop().RunUntilIdle(); |
| |
| // Destroy the PrecacheFetcher after it has finished, to record metrics. |
| } |
| |
| std::vector<GURL> expected_requested_urls; |
| expected_requested_urls.emplace_back(kConfigURL); |
| expected_requested_urls.emplace_back(kGoodManifestURL); |
| expected_requested_urls.emplace_back(kGoodResourceURL); |
| expected_requested_urls.emplace_back(kGoodResourceURLC); |
| expected_requested_urls.emplace_back(kGoodResourceURLD); |
| |
| EXPECT_EQ(expected_requested_urls, url_callback_.requested_urls()); |
| |
| std::vector<std::string> expected_manifest_hosts = {"good-manifest.com"}; |
| EXPECT_EQ(expected_manifest_hosts, precache_delegate_.get_manifest_hosts()); |
| EXPECT_TRUE(precache_delegate_.was_on_done_called()); |
| |
| histogram.ExpectUniqueSample("Precache.Fetch.PercentCompleted", 100, 1); |
| histogram.ExpectUniqueSample("Precache.Fetch.ResponseBytes.Total", |
| url_callback_.total_response_bytes(), 1); |
| histogram.ExpectTotalCount("Precache.Fetch.TimeToComplete", 1); |
| } |
| |
| TEST_P(PrecacheFetcherResourceSelectionTest, MissingBitset) { |
| SetDefaultFlags(); |
| |
| std::unique_ptr<PrecacheUnfinishedWork> unfinished_work( |
| new PrecacheUnfinishedWork()); |
| unfinished_work->add_top_host()->set_hostname("good-manifest.com"); |
| unfinished_work->set_start_time(base::Time::UnixEpoch().ToInternalValue()); |
| |
| PrecacheConfigurationSettings config; |
| |
| PrecacheManifest good_manifest; |
| PrecacheResourceSelection resource_selection; |
| good_manifest.add_resource()->set_url(kGoodResourceURL); |
| good_manifest.add_resource()->set_url(kGoodResourceURLA); |
| good_manifest.add_resource()->set_url(kGoodResourceURLB); |
| good_manifest.add_resource()->set_url(kGoodResourceURLC); |
| good_manifest.add_resource()->set_url(kGoodResourceURLD); |
| |
| // Set bits for a different experiment group. |
| (*good_manifest.mutable_experiments() |
| ->mutable_resources_by_experiment_group())[kExperimentID + 1] = |
| GetParam(); |
| |
| // Resource selection bitset for the experiment group will be missing and all |
| // resources will be fetched. |
| factory_.SetFakeResponse(GURL(kConfigURL), config.SerializeAsString(), |
| net::HTTP_OK, net::URLRequestStatus::SUCCESS); |
| factory_.SetFakeResponse(GURL(kGoodManifestURL), |
| good_manifest.SerializeAsString(), net::HTTP_OK, |
| net::URLRequestStatus::SUCCESS); |
| factory_.SetFakeResponse(GURL(kGoodResourceURL), "good", net::HTTP_OK, |
| net::URLRequestStatus::SUCCESS); |
| factory_.SetFakeResponse(GURL(kGoodResourceURLA), "good URL A", net::HTTP_OK, |
| net::URLRequestStatus::SUCCESS); |
| factory_.SetFakeResponse(GURL(kGoodResourceURLB), "good URL B", net::HTTP_OK, |
| net::URLRequestStatus::SUCCESS); |
| factory_.SetFakeResponse(GURL(kGoodResourceURLC), "good URL C", net::HTTP_OK, |
| net::URLRequestStatus::SUCCESS); |
| factory_.SetFakeResponse(GURL(kGoodResourceURLD), "good URL D", net::HTTP_OK, |
| net::URLRequestStatus::SUCCESS); |
| |
| base::HistogramTester histogram; |
| |
| { |
| PrecacheFetcher precache_fetcher( |
| request_context_.get(), GURL(), std::string(), |
| std::move(unfinished_work), kExperimentID, |
| precache_database_.GetWeakPtr(), task_runner(), &precache_delegate_); |
| precache_fetcher.Start(); |
| |
| base::RunLoop().RunUntilIdle(); |
| |
| // Destroy the PrecacheFetcher after it has finished, to record metrics. |
| } |
| |
| std::vector<GURL> expected_requested_urls; |
| expected_requested_urls.emplace_back(kConfigURL); |
| expected_requested_urls.emplace_back(kGoodManifestURL); |
| expected_requested_urls.emplace_back(kGoodResourceURL); |
| expected_requested_urls.emplace_back(kGoodResourceURLA); |
| expected_requested_urls.emplace_back(kGoodResourceURLB); |
| expected_requested_urls.emplace_back(kGoodResourceURLC); |
| expected_requested_urls.emplace_back(kGoodResourceURLD); |
| |
| EXPECT_EQ(expected_requested_urls, url_callback_.requested_urls()); |
| |
| std::vector<std::string> expected_manifest_hosts = {"good-manifest.com"}; |
| EXPECT_EQ(expected_manifest_hosts, precache_delegate_.get_manifest_hosts()); |
| EXPECT_TRUE(precache_delegate_.was_on_done_called()); |
| |
| histogram.ExpectUniqueSample("Precache.Fetch.PercentCompleted", 100, 1); |
| histogram.ExpectUniqueSample("Precache.Fetch.ResponseBytes.Total", |
| url_callback_.total_response_bytes(), 1); |
| histogram.ExpectTotalCount("Precache.Fetch.TimeToComplete", 1); |
| } |
| |
| INSTANTIATE_TEST_CASE_P( |
| PrecacheFetcherResourceSelectionTest, |
| PrecacheFetcherResourceSelectionTest, |
| testing::Values(PrecacheFetcherResourceSelectionTest::DeprecatedBitset(), |
| PrecacheFetcherResourceSelectionTest::Bitset())); |
| |
| TEST_F(PrecacheFetcherTest, PrecachePauseResume) { |
| SetDefaultFlags(); |
| |
| PrecacheConfigurationSettings config; |
| config.set_top_sites_count(3); |
| |
| std::unique_ptr<PrecacheUnfinishedWork> initial_work( |
| new PrecacheUnfinishedWork()); |
| initial_work->add_top_host()->set_hostname("manifest1.com"); |
| initial_work->add_top_host()->set_hostname("manifest2.com"); |
| initial_work->set_start_time( |
| (base::Time::Now() - base::TimeDelta::FromHours(1)).ToInternalValue()); |
| |
| PrecacheFetcher first_fetcher(request_context_.get(), GURL(), std::string(), |
| std::move(initial_work), kExperimentID, |
| precache_database_.GetWeakPtr(), task_runner(), |
| &precache_delegate_); |
| factory_.SetFakeResponse(GURL(kConfigURL), config.SerializeAsString(), |
| net::HTTP_OK, net::URLRequestStatus::SUCCESS); |
| first_fetcher.Start(); |
| std::unique_ptr<PrecacheUnfinishedWork> unfinished_work = |
| first_fetcher.CancelPrecaching(); |
| |
| std::vector<GURL> expected_requested_urls; |
| expected_requested_urls.emplace_back(kConfigURL); |
| EXPECT_EQ(expected_requested_urls, url_callback_.requested_urls()); |
| |
| factory_.SetFakeResponse(GURL(kConfigURL), config.SerializeAsString(), |
| net::HTTP_OK, net::URLRequestStatus::SUCCESS); |
| factory_.SetFakeResponse(GURL(kBadManifestURL), "bad protobuf", net::HTTP_OK, |
| net::URLRequestStatus::SUCCESS); |
| factory_.SetFakeResponse(GURL("http://manifest-url-prefix.com/manifest1.com"), |
| "bad protobuf", net::HTTP_OK, |
| net::URLRequestStatus::SUCCESS); |
| factory_.SetFakeResponse(GURL("http://manifest-url-prefix.com/manifest2.com"), |
| "bad protobuf", net::HTTP_OK, |
| net::URLRequestStatus::SUCCESS); |
| factory_.SetFakeResponse(GURL(kGoodResourceURL), "good", net::HTTP_OK, |
| net::URLRequestStatus::SUCCESS); |
| |
| url_callback_.clear_requested_urls(); |
| PrecacheFetcher second_fetcher(request_context_.get(), GURL(), std::string(), |
| std::move(unfinished_work), kExperimentID, |
| precache_database_.GetWeakPtr(), task_runner(), |
| &precache_delegate_); |
| second_fetcher.Start(); |
| base::RunLoop().RunUntilIdle(); |
| expected_requested_urls.emplace_back( |
| "http://manifest-url-prefix.com/manifest1.com"); |
| expected_requested_urls.emplace_back( |
| "http://manifest-url-prefix.com/manifest2.com"); |
| EXPECT_EQ(expected_requested_urls, url_callback_.requested_urls()); |
| |
| EXPECT_TRUE(precache_delegate_.get_manifest_hosts().empty()); |
| EXPECT_TRUE(precache_delegate_.was_on_done_called()); |
| } |
| |
| TEST_F(PrecacheFetcherTest, ResumeWithConfigOnly) { |
| SetDefaultFlags(); |
| std::unique_ptr<PrecacheUnfinishedWork> unfinished_work( |
| new PrecacheUnfinishedWork()); |
| unfinished_work->mutable_config_settings()->add_forced_site( |
| "good-manifest.com"); |
| unfinished_work->set_start_time(base::Time::Now().ToInternalValue()); |
| PrecacheManifest good_manifest; |
| good_manifest.add_resource()->set_url(kGoodResourceURL); |
| |
| factory_.SetFakeResponse(GURL(kGoodManifestURL), |
| good_manifest.SerializeAsString(), net::HTTP_OK, |
| net::URLRequestStatus::SUCCESS); |
| factory_.SetFakeResponse(GURL(kGoodResourceURL), "good", net::HTTP_OK, |
| net::URLRequestStatus::SUCCESS); |
| { |
| PrecacheFetcher precache_fetcher( |
| request_context_.get(), GURL(), std::string(), |
| std::move(unfinished_work), kExperimentID, |
| precache_database_.GetWeakPtr(), task_runner(), &precache_delegate_); |
| precache_fetcher.Start(); |
| |
| base::RunLoop().RunUntilIdle(); |
| } |
| |
| std::vector<GURL> expected_requested_urls; |
| expected_requested_urls.emplace_back(kGoodManifestURL); |
| expected_requested_urls.emplace_back(kGoodResourceURL); |
| |
| EXPECT_EQ(expected_requested_urls, url_callback_.requested_urls()); |
| |
| std::vector<std::string> expected_manifest_hosts = {"good-manifest.com"}; |
| EXPECT_EQ(expected_manifest_hosts, precache_delegate_.get_manifest_hosts()); |
| EXPECT_TRUE(precache_delegate_.was_on_done_called()); |
| } |
| |
| |
| TEST_F(PrecacheFetcherTest, CustomURLs) { |
| SetDefaultFlags(); |
| |
| std::unique_ptr<PrecacheUnfinishedWork> unfinished_work( |
| new PrecacheUnfinishedWork()); |
| unfinished_work->add_top_host()->set_hostname("good-manifest.com"); |
| |
| PrecacheConfigurationSettings config; |
| |
| PrecacheManifest good_manifest; |
| good_manifest.add_resource()->set_url(kGoodResourceURL); |
| |
| factory_.SetFakeResponse(GURL(kCustomConfigURL), config.SerializeAsString(), |
| net::HTTP_OK, net::URLRequestStatus::SUCCESS); |
| factory_.SetFakeResponse(GURL(kCustomGoodManifestURL), |
| good_manifest.SerializeAsString(), net::HTTP_OK, |
| net::URLRequestStatus::SUCCESS); |
| factory_.SetFakeResponse(GURL(kGoodResourceURL), "good", net::HTTP_OK, |
| net::URLRequestStatus::SUCCESS); |
| |
| PrecacheFetcher precache_fetcher( |
| request_context_.get(), GURL(kCustomConfigURL), kCustomManifestURLPrefix, |
| std::move(unfinished_work), kExperimentID, |
| precache_database_.GetWeakPtr(), task_runner(), &precache_delegate_); |
| precache_fetcher.Start(); |
| |
| base::RunLoop().RunUntilIdle(); |
| |
| std::vector<GURL> expected_requested_urls; |
| expected_requested_urls.emplace_back(kCustomConfigURL); |
| expected_requested_urls.emplace_back(kCustomGoodManifestURL); |
| expected_requested_urls.emplace_back(kGoodResourceURL); |
| |
| EXPECT_EQ(expected_requested_urls, url_callback_.requested_urls()); |
| |
| std::vector<std::string> expected_manifest_hosts = {"good-manifest.com"}; |
| EXPECT_EQ(expected_manifest_hosts, precache_delegate_.get_manifest_hosts()); |
| EXPECT_TRUE(precache_delegate_.was_on_done_called()); |
| } |
| |
| TEST_F(PrecacheFetcherTest, ConfigFetchFailure) { |
| SetDefaultFlags(); |
| |
| std::unique_ptr<PrecacheUnfinishedWork> unfinished_work( |
| new PrecacheUnfinishedWork()); |
| unfinished_work->add_top_host()->set_hostname("good-manifest.com"); |
| |
| factory_.SetFakeResponse(GURL(kConfigURL), "", |
| net::HTTP_INTERNAL_SERVER_ERROR, |
| net::URLRequestStatus::FAILED); |
| factory_.SetFakeResponse(GURL(kGoodManifestURL), "", net::HTTP_OK, |
| net::URLRequestStatus::SUCCESS); |
| |
| PrecacheFetcher precache_fetcher( |
| request_context_.get(), GURL(), std::string(), std::move(unfinished_work), |
| kExperimentID, precache_database_.GetWeakPtr(), task_runner(), |
| &precache_delegate_); |
| precache_fetcher.Start(); |
| |
| base::RunLoop().RunUntilIdle(); |
| |
| std::vector<GURL> expected_requested_urls; |
| expected_requested_urls.emplace_back(kConfigURL); |
| expected_requested_urls.emplace_back(kGoodManifestURL); |
| EXPECT_EQ(expected_requested_urls, url_callback_.requested_urls()); |
| |
| std::vector<std::string> expected_manifest_hosts = {"good-manifest.com"}; |
| EXPECT_EQ(expected_manifest_hosts, precache_delegate_.get_manifest_hosts()); |
| EXPECT_TRUE(precache_delegate_.was_on_done_called()); |
| } |
| |
| TEST_F(PrecacheFetcherTest, BadConfig) { |
| SetDefaultFlags(); |
| |
| std::unique_ptr<PrecacheUnfinishedWork> unfinished_work( |
| new PrecacheUnfinishedWork()); |
| unfinished_work->add_top_host()->set_hostname("good-manifest.com"); |
| |
| factory_.SetFakeResponse(GURL(kConfigURL), "bad protobuf", net::HTTP_OK, |
| net::URLRequestStatus::SUCCESS); |
| factory_.SetFakeResponse(GURL(kGoodManifestURL), "", net::HTTP_OK, |
| net::URLRequestStatus::SUCCESS); |
| |
| PrecacheFetcher precache_fetcher( |
| request_context_.get(), GURL(), std::string(), std::move(unfinished_work), |
| kExperimentID, precache_database_.GetWeakPtr(), task_runner(), |
| &precache_delegate_); |
| precache_fetcher.Start(); |
| |
| base::RunLoop().RunUntilIdle(); |
| |
| std::vector<GURL> expected_requested_urls; |
| expected_requested_urls.emplace_back(kConfigURL); |
| expected_requested_urls.emplace_back(kGoodManifestURL); |
| EXPECT_EQ(expected_requested_urls, url_callback_.requested_urls()); |
| |
| std::vector<std::string> expected_manifest_hosts = {"good-manifest.com"}; |
| EXPECT_EQ(expected_manifest_hosts, precache_delegate_.get_manifest_hosts()); |
| EXPECT_TRUE(precache_delegate_.was_on_done_called()); |
| } |
| |
| TEST_F(PrecacheFetcherTest, Cancel) { |
| SetDefaultFlags(); |
| |
| std::unique_ptr<PrecacheUnfinishedWork> unfinished_work( |
| new PrecacheUnfinishedWork()); |
| unfinished_work->add_top_host()->set_hostname("starting-url.com"); |
| |
| PrecacheConfigurationSettings config; |
| config.set_top_sites_count(1); |
| |
| factory_.SetFakeResponse(GURL(kConfigURL), config.SerializeAsString(), |
| net::HTTP_OK, net::URLRequestStatus::SUCCESS); |
| |
| base::HistogramTester histogram; |
| |
| { |
| PrecacheFetcher precache_fetcher( |
| request_context_.get(), GURL(), std::string(), |
| std::move(unfinished_work), kExperimentID, |
| precache_database_.GetWeakPtr(), task_runner(), &precache_delegate_); |
| precache_fetcher.Start(); |
| |
| // Destroy the PrecacheFetcher, to cancel precaching. No metrics |
| // should be recorded because this should not cause OnDone to be |
| // called on the precache delegate. |
| } |
| |
| base::RunLoop().RunUntilIdle(); |
| |
| std::vector<GURL> expected_requested_urls; |
| expected_requested_urls.emplace_back(kConfigURL); |
| EXPECT_EQ(expected_requested_urls, url_callback_.requested_urls()); |
| |
| EXPECT_TRUE(precache_delegate_.get_manifest_hosts().empty()); |
| EXPECT_FALSE(precache_delegate_.was_on_done_called()); |
| |
| histogram.ExpectTotalCount("Precache.Fetch.TimeToComplete", 0); |
| } |
| |
| #if defined(PRECACHE_CONFIG_SETTINGS_URL) |
| |
| // If the default precache configuration settings URL is defined, then test that |
| // it works with the PrecacheFetcher. |
| TEST_F(PrecacheFetcherTest, PrecacheUsingDefaultConfigSettingsURL) { |
| std::unique_ptr<PrecacheUnfinishedWork> unfinished_work( |
| new PrecacheUnfinishedWork()); |
| unfinished_work->add_top_host()->set_hostname("starting-url.com"); |
| |
| PrecacheConfigurationSettings config; |
| config.set_top_sites_count(0); |
| |
| factory_.SetFakeResponse(GURL(PRECACHE_CONFIG_SETTINGS_URL), |
| config.SerializeAsString(), net::HTTP_OK, |
| net::URLRequestStatus::SUCCESS); |
| |
| PrecacheFetcher precache_fetcher( |
| request_context_.get(), GURL(), std::string(), std::move(unfinished_work), |
| kExperimentID, precache_database_.GetWeakPtr(), task_runner(), |
| &precache_delegate_); |
| precache_fetcher.Start(); |
| |
| base::RunLoop().RunUntilIdle(); |
| |
| std::vector<GURL> expected_requested_urls; |
| expected_requested_urls.emplace_back(PRECACHE_CONFIG_SETTINGS_URL); |
| EXPECT_EQ(expected_requested_urls, url_callback_.requested_urls()); |
| |
| EXPECT_TRUE(precache_delegate_.get_manifest_hosts().empty()); |
| EXPECT_TRUE(precache_delegate_.was_on_done_called()); |
| } |
| |
| #endif // PRECACHE_CONFIG_SETTINGS_URL |
| |
| #if defined(PRECACHE_MANIFEST_URL_PREFIX) |
| |
| // If the default precache manifest URL prefix is defined, then test that it |
| // works with the PrecacheFetcher. |
| TEST_F(PrecacheFetcherTest, PrecacheUsingDefaultManifestURLPrefix) { |
| base::CommandLine::ForCurrentProcess()->AppendSwitchASCII( |
| switches::kPrecacheConfigSettingsURL, kConfigURL); |
| |
| std::unique_ptr<PrecacheUnfinishedWork> unfinished_work( |
| new PrecacheUnfinishedWork()); |
| unfinished_work->add_top_host()->set_hostname("starting-url.com"); |
| |
| PrecacheConfigurationSettings config; |
| config.set_top_sites_count(1); |
| |
| GURL manifest_url(PRECACHE_MANIFEST_URL_PREFIX "starting-url.com"); |
| |
| factory_.SetFakeResponse(GURL(kConfigURL), config.SerializeAsString(), |
| net::HTTP_OK, net::URLRequestStatus::SUCCESS); |
| factory_.SetFakeResponse(manifest_url, PrecacheManifest().SerializeAsString(), |
| net::HTTP_OK, net::URLRequestStatus::SUCCESS); |
| |
| PrecacheFetcher precache_fetcher( |
| request_context_.get(), GURL(), std::string(), std::move(unfinished_work), |
| kExperimentID, precache_database_.GetWeakPtr(), task_runner(), |
| &precache_delegate_); |
| precache_fetcher.Start(); |
| |
| base::RunLoop().RunUntilIdle(); |
| |
| std::vector<GURL> expected_requested_urls; |
| expected_requested_urls.emplace_back(kConfigURL); |
| expected_requested_urls.push_back(manifest_url); |
| EXPECT_EQ(expected_requested_urls, url_callback_.requested_urls()); |
| |
| std::vector<std::string> expected_manifest_hosts = {"starting-url.com"}; |
| EXPECT_EQ(expected_manifest_hosts, precache_delegate_.get_manifest_hosts()); |
| EXPECT_TRUE(precache_delegate_.was_on_done_called()); |
| } |
| |
| #endif // PRECACHE_MANIFEST_URL_PREFIX |
| |
| TEST_F(PrecacheFetcherTest, TopResourcesCount) { |
| SetDefaultFlags(); |
| |
| std::unique_ptr<PrecacheUnfinishedWork> unfinished_work( |
| new PrecacheUnfinishedWork()); |
| unfinished_work->set_start_time(base::Time::UnixEpoch().ToInternalValue()); |
| unfinished_work->add_top_host()->set_hostname("good-manifest.com"); |
| |
| PrecacheConfigurationSettings config; |
| config.set_top_resources_count(3); |
| |
| PrecacheManifest good_manifest; |
| good_manifest.add_resource()->set_url("http://good-manifest.com/retrieved"); |
| good_manifest.add_resource()->set_url("http://good-manifest.com/retrieved"); |
| good_manifest.add_resource()->set_url("http://good-manifest.com/retrieved"); |
| good_manifest.add_resource()->set_url("http://good-manifest.com/skipped"); |
| good_manifest.add_resource()->set_url("http://good-manifest.com/skipped"); |
| |
| factory_.SetFakeResponse(GURL(kConfigURL), config.SerializeAsString(), |
| net::HTTP_OK, net::URLRequestStatus::SUCCESS); |
| factory_.SetFakeResponse(GURL(kGoodManifestURL), |
| good_manifest.SerializeAsString(), net::HTTP_OK, |
| net::URLRequestStatus::SUCCESS); |
| factory_.SetFakeResponse(GURL("http://good-manifest.com/retrieved"), "good", |
| net::HTTP_OK, net::URLRequestStatus::SUCCESS); |
| |
| base::HistogramTester histogram; |
| |
| { |
| PrecacheFetcher precache_fetcher( |
| request_context_.get(), GURL(), std::string(), |
| std::move(unfinished_work), kExperimentID, |
| precache_database_.GetWeakPtr(), task_runner(), &precache_delegate_); |
| precache_fetcher.Start(); |
| |
| base::RunLoop().RunUntilIdle(); |
| |
| // Destroy the PrecacheFetcher after it has finished, to record metrics. |
| } |
| |
| std::vector<GURL> expected_requested_urls; |
| expected_requested_urls.emplace_back(kConfigURL); |
| expected_requested_urls.emplace_back(kGoodManifestURL); |
| expected_requested_urls.emplace_back("http://good-manifest.com/retrieved"); |
| expected_requested_urls.emplace_back("http://good-manifest.com/retrieved"); |
| expected_requested_urls.emplace_back("http://good-manifest.com/retrieved"); |
| |
| EXPECT_EQ(expected_requested_urls, url_callback_.requested_urls()); |
| |
| std::vector<std::string> expected_manifest_hosts = {"good-manifest.com"}; |
| EXPECT_EQ(expected_manifest_hosts, precache_delegate_.get_manifest_hosts()); |
| EXPECT_TRUE(precache_delegate_.was_on_done_called()); |
| |
| histogram.ExpectUniqueSample("Precache.Fetch.PercentCompleted", 100, 1); |
| histogram.ExpectUniqueSample("Precache.Fetch.ResponseBytes.Total", |
| url_callback_.total_response_bytes(), 1); |
| histogram.ExpectTotalCount("Precache.Fetch.TimeToComplete", 1); |
| } |
| |
| TEST_F(PrecacheFetcherTest, TopResourcesCount_ResourceBitset) { |
| SetDefaultFlags(); |
| |
| std::unique_ptr<PrecacheUnfinishedWork> unfinished_work( |
| new PrecacheUnfinishedWork()); |
| unfinished_work->set_start_time(base::Time::UnixEpoch().ToInternalValue()); |
| unfinished_work->add_top_host()->set_hostname("good-manifest.com"); |
| |
| PrecacheConfigurationSettings config; |
| config.set_top_resources_count(2); |
| |
| PrecacheManifest good_manifest; |
| good_manifest.add_resource()->set_url("http://good-manifest.com/retrieved"); |
| good_manifest.add_resource()->set_url("http://good-manifest.com/skipped"); |
| good_manifest.add_resource()->set_url("http://good-manifest.com/retrieved"); |
| good_manifest.add_resource()->set_url("http://good-manifest.com/skipped"); |
| good_manifest.add_resource()->set_url("http://good-manifest.com/retrieved"); |
| (*good_manifest.mutable_experiments() |
| ->mutable_resources_by_experiment_group())[kExperimentID] |
| .set_deprecated_bitset(0b10101); |
| |
| factory_.SetFakeResponse(GURL(kConfigURL), config.SerializeAsString(), |
| net::HTTP_OK, net::URLRequestStatus::SUCCESS); |
| factory_.SetFakeResponse(GURL(kGoodManifestURL), |
| good_manifest.SerializeAsString(), net::HTTP_OK, |
| net::URLRequestStatus::SUCCESS); |
| factory_.SetFakeResponse(GURL("http://good-manifest.com/retrieved"), "good", |
| net::HTTP_OK, net::URLRequestStatus::SUCCESS); |
| |
| base::HistogramTester histogram; |
| |
| { |
| PrecacheFetcher precache_fetcher( |
| request_context_.get(), GURL(), std::string(), |
| std::move(unfinished_work), kExperimentID, |
| precache_database_.GetWeakPtr(), task_runner(), &precache_delegate_); |
| precache_fetcher.Start(); |
| |
| base::RunLoop().RunUntilIdle(); |
| |
| // Destroy the PrecacheFetcher after it has finished, to record metrics. |
| } |
| |
| std::vector<GURL> expected_requested_urls; |
| expected_requested_urls.emplace_back(kConfigURL); |
| expected_requested_urls.emplace_back(kGoodManifestURL); |
| expected_requested_urls.emplace_back("http://good-manifest.com/retrieved"); |
| expected_requested_urls.emplace_back("http://good-manifest.com/retrieved"); |
| |
| EXPECT_EQ(expected_requested_urls, url_callback_.requested_urls()); |
| |
| std::vector<std::string> expected_manifest_hosts = {"good-manifest.com"}; |
| EXPECT_EQ(expected_manifest_hosts, precache_delegate_.get_manifest_hosts()); |
| EXPECT_TRUE(precache_delegate_.was_on_done_called()); |
| |
| histogram.ExpectUniqueSample("Precache.Fetch.PercentCompleted", 100, 1); |
| histogram.ExpectUniqueSample("Precache.Fetch.ResponseBytes.Total", |
| url_callback_.total_response_bytes(), 1); |
| histogram.ExpectTotalCount("Precache.Fetch.TimeToComplete", 1); |
| } |
| |
| // MaxBytesPerResource is impossible to test with net::FakeURLFetcherFactory: |
| // |
| // - The PrecacheFetcher::Fetcher's max_bytes logic only applies to network |
| // requests, and not cached requests. |
| // - Forcing PrecacheFetcher::Fetcher to do a network request (i.e. a second |
| // request for the same URL) requires either setting a custom error of |
| // ERR_CACHE_MISS or setting a custom ETag response header, neither of which |
| // is possible under FakeURLFetcherFactory. |
| // |
| // PrecacheFetcherFetcherTest.ResourceTooBig tests the bulk of the code. We'll |
| // assume that PrecacheFetcher passes the right max_bytes to the |
| // PrecacheFetcher::Fetcher constructor. |
| // |
| // TODO(twifkak): Port these tests from FakeURLFetcherFactory to |
| // MockURLFetcherFactory or EmbeddedTestServer, and add a test that fetches are |
| // cancelled midstream. |
| |
| TEST_F(PrecacheFetcherTest, MaxBytesTotal) { |
| SetDefaultFlags(); |
| std::unique_ptr<PrecacheUnfinishedWork> unfinished_work( |
| new PrecacheUnfinishedWork()); |
| auto* top_host = unfinished_work->add_top_host(); |
| top_host->set_hostname("good-manifest.com"); |
| top_host->set_visits(1); |
| unfinished_work->set_start_time(base::Time::UnixEpoch().ToInternalValue()); |
| |
| // Should be greater than kMaxParallelFetches, so that we can observe |
| // PrecacheFetcher not fetching the remaining resources after max bytes is |
| // exceeded. |
| const size_t kNumResources = kMaxParallelFetches + 5; |
| // Should be smaller than kNumResources - kMaxParallelFetches, such that the |
| // max bytes is guaranteed to be exceeded before all fetches have been |
| // requested. In this case, after 3 fetches have been completed, 3 more are |
| // added to the fetcher pool, but 2 out of 5 still remain. |
| const size_t kResourcesWithinMax = 3; |
| // Should be big enough that the size of the config, manifest, and HTTP |
| // headers are negligible for max bytes computation. |
| const size_t kBytesPerResource = 500; |
| const size_t kMaxBytesTotal = kResourcesWithinMax * kBytesPerResource; |
| |
| PrecacheConfigurationSettings config; |
| config.set_max_bytes_total(kMaxBytesTotal); |
| config.set_global_ranking(true); |
| |
| factory_.SetFakeResponse(GURL(kConfigURL), config.SerializeAsString(), |
| net::HTTP_OK, net::URLRequestStatus::SUCCESS); |
| |
| PrecacheManifest good_manifest; |
| for (size_t i = 0; i < kNumResources; ++i) { |
| const std::string url = "http://good-manifest.com/" + std::to_string(i); |
| auto* resource = good_manifest.add_resource(); |
| resource->set_url(url); |
| resource->set_weight_ratio(static_cast<double>(i) / kNumResources); |
| factory_.SetFakeResponse(GURL(url), std::string(kBytesPerResource, '.'), |
| net::HTTP_OK, net::URLRequestStatus::SUCCESS); |
| } |
| |
| factory_.SetFakeResponse(GURL(kGoodManifestURL), |
| good_manifest.SerializeAsString(), net::HTTP_OK, |
| net::URLRequestStatus::SUCCESS); |
| |
| base::HistogramTester histogram; |
| |
| { |
| PrecacheFetcher precache_fetcher( |
| request_context_.get(), GURL(), std::string(), |
| std::move(unfinished_work), kExperimentID, |
| precache_database_.GetWeakPtr(), task_runner(), &precache_delegate_); |
| precache_fetcher.Start(); |
| |
| base::RunLoop().RunUntilIdle(); |
| } |
| |
| // Fetcher should request config, manifest, and all but 3 resources. For some |
| // reason, we are seeing it fetch all but 4 resources. Meh, close enough. |
| EXPECT_EQ(1 + 1 + kNumResources - 4, url_callback_.requested_urls().size()); |
| |
| std::vector<std::string> expected_manifest_hosts = {"good-manifest.com"}; |
| EXPECT_EQ(expected_manifest_hosts, precache_delegate_.get_manifest_hosts()); |
| EXPECT_TRUE(precache_delegate_.was_on_done_called()); |
| |
| histogram.ExpectTotalCount("Precache.Fetch.PercentCompleted", 1); |
| histogram.ExpectTotalCount("Precache.Fetch.TimeToComplete", 1); |
| |
| const double expected_min_weight = |
| good_manifest.resource(kNumResources - 3).weight_ratio() * |
| 1 /* # of visits to good-manifest.com */; |
| histogram.ExpectBucketCount("Precache.Fetch.MinWeight", |
| 1000.0 * expected_min_weight, 1); |
| } |
| |
| // Tests the parallel fetch behaviour when more precache resource and manifest |
| // requests are available than the maximum capacity of fetcher pool. |
| TEST_F(PrecacheFetcherTest, FetcherPoolMaxLimitReached) { |
| SetDefaultFlags(); |
| |
| const size_t kNumTopHosts = 5; |
| const size_t kNumResources = kMaxParallelFetches + 5; |
| |
| PrecacheConfigurationSettings config; |
| std::vector<GURL> expected_requested_urls; |
| std::vector<std::string> expected_manifest_hosts; |
| |
| config.set_top_sites_count(kNumTopHosts); |
| factory_.SetFakeResponse(GURL(kConfigURL), config.SerializeAsString(), |
| net::HTTP_OK, net::URLRequestStatus::SUCCESS); |
| expected_requested_urls.emplace_back(kConfigURL); |
| |
| std::unique_ptr<PrecacheUnfinishedWork> unfinished_work( |
| new PrecacheUnfinishedWork()); |
| unfinished_work->set_start_time(base::Time::UnixEpoch().ToInternalValue()); |
| |
| for (size_t i = 0; i < kNumTopHosts; ++i) { |
| const std::string top_host_url = base::StringPrintf("top-host-%zu.com", i); |
| expected_requested_urls.emplace_back(kManifestURLPrefix + top_host_url); |
| expected_manifest_hosts.push_back(top_host_url); |
| } |
| |
| for (size_t i = 0; i < kNumTopHosts; ++i) { |
| const std::string top_host_url = base::StringPrintf("top-host-%zu.com", i); |
| unfinished_work->add_top_host()->set_hostname(top_host_url); |
| |
| PrecacheManifest manifest; |
| for (size_t j = 0; j < kNumResources; ++j) { |
| const std::string resource_url = |
| base::StringPrintf("http://top-host-%zu.com/resource-%zu", i, j); |
| manifest.add_resource()->set_url(resource_url); |
| factory_.SetFakeResponse(GURL(resource_url), "good", net::HTTP_OK, |
| net::URLRequestStatus::SUCCESS); |
| expected_requested_urls.emplace_back(resource_url); |
| } |
| factory_.SetFakeResponse(GURL(kManifestURLPrefix + top_host_url), |
| manifest.SerializeAsString(), net::HTTP_OK, |
| net::URLRequestStatus::SUCCESS); |
| } |
| |
| base::HistogramTester histogram; |
| |
| { |
| PrecacheFetcher precache_fetcher( |
| request_context_.get(), GURL(), std::string(), |
| std::move(unfinished_work), kExperimentID, |
| precache_database_.GetWeakPtr(), task_runner(), &precache_delegate_); |
| precache_fetcher.Start(); |
| |
| EXPECT_GT(kNumResources, precache_fetcher.pool_.max_size()); |
| CheckUntilParallelFetchesBeyondCapacity(&precache_fetcher); |
| |
| base::RunLoop().RunUntilIdle(); |
| |
| // Destroy the PrecacheFetcher after it has finished, to record metrics. |
| } |
| |
| EXPECT_TRUE(parallel_fetches_beyond_capacity_); |
| |
| EXPECT_EQ(expected_requested_urls, url_callback_.requested_urls()); |
| |
| EXPECT_EQ(expected_manifest_hosts, precache_delegate_.get_manifest_hosts()); |
| EXPECT_TRUE(precache_delegate_.was_on_done_called()); |
| |
| histogram.ExpectUniqueSample("Precache.Fetch.PercentCompleted", 100, 1); |
| histogram.ExpectUniqueSample("Precache.Fetch.ResponseBytes.Total", |
| url_callback_.total_response_bytes(), 1); |
| histogram.ExpectTotalCount("Precache.Fetch.TimeToComplete", 1); |
| } |
| |
| TEST_F(PrecacheFetcherTest, FilterInvalidManifestUrls) { |
| SetDefaultFlags(); |
| base::CommandLine::ForCurrentProcess()->AppendSwitchASCII( |
| switches::kPrecacheManifestURLPrefix, "invalid-manifest-prefix"); |
| |
| std::unique_ptr<PrecacheUnfinishedWork> unfinished_work( |
| new PrecacheUnfinishedWork()); |
| unfinished_work->add_top_host()->set_hostname("manifest.com"); |
| unfinished_work->set_start_time(base::Time::UnixEpoch().ToInternalValue()); |
| |
| factory_.SetFakeResponse(GURL(kConfigURL), "", net::HTTP_OK, |
| net::URLRequestStatus::SUCCESS); |
| |
| base::HistogramTester histogram; |
| |
| { |
| PrecacheFetcher precache_fetcher( |
| request_context_.get(), GURL(), std::string(), |
| std::move(unfinished_work), kExperimentID, |
| precache_database_.GetWeakPtr(), task_runner(), &precache_delegate_); |
| precache_fetcher.Start(); |
| |
| base::RunLoop().RunUntilIdle(); |
| } |
| |
| // The config is fetched, but not the invalid manifest URL. |
| EXPECT_EQ(1UL, url_callback_.requested_urls().size()); |
| |
| EXPECT_TRUE(precache_delegate_.get_manifest_hosts().empty()); |
| EXPECT_TRUE(precache_delegate_.was_on_done_called()); |
| |
| // manifest.com will have been failed to complete, in this case. |
| EXPECT_THAT(histogram.GetAllSamples("Precache.Fetch.PercentCompleted"), |
| ElementsAre(base::Bucket(101, 1))); |
| histogram.ExpectTotalCount("Precache.Fetch.TimeToComplete", 1); |
| } |
| |
| TEST_F(PrecacheFetcherTest, FilterInvalidResourceUrls) { |
| SetDefaultFlags(); |
| std::unique_ptr<PrecacheUnfinishedWork> unfinished_work( |
| new PrecacheUnfinishedWork()); |
| unfinished_work->add_top_host()->set_hostname("bad-manifest.com"); |
| unfinished_work->set_start_time(base::Time::UnixEpoch().ToInternalValue()); |
| |
| factory_.SetFakeResponse(GURL(kConfigURL), "", net::HTTP_OK, |
| net::URLRequestStatus::SUCCESS); |
| |
| PrecacheManifest bad_manifest; |
| bad_manifest.add_resource()->set_url("http://"); |
| |
| factory_.SetFakeResponse(GURL(kBadManifestURL), |
| bad_manifest.SerializeAsString(), net::HTTP_OK, |
| net::URLRequestStatus::SUCCESS); |
| |
| base::HistogramTester histogram; |
| |
| { |
| PrecacheFetcher precache_fetcher( |
| request_context_.get(), GURL(), std::string(), |
| std::move(unfinished_work), kExperimentID, |
| precache_database_.GetWeakPtr(), task_runner(), &precache_delegate_); |
| precache_fetcher.Start(); |
| |
| base::RunLoop().RunUntilIdle(); |
| } |
| |
| // The config and manifest are fetched, but not the invalid resource URL. |
| EXPECT_EQ(2UL, url_callback_.requested_urls().size()); |
| |
| std::vector<std::string> expected_manifest_hosts = {"bad-manifest.com"}; |
| EXPECT_EQ(expected_manifest_hosts, precache_delegate_.get_manifest_hosts()); |
| EXPECT_TRUE(precache_delegate_.was_on_done_called()); |
| |
| // bad-manifest.com will have been completed. |
| EXPECT_THAT(histogram.GetAllSamples("Precache.Fetch.PercentCompleted"), |
| ElementsAre(base::Bucket(101, 1))); |
| histogram.ExpectTotalCount("Precache.Fetch.TimeToComplete", 1); |
| } |
| |
| TEST(PrecacheFetcherStandaloneTest, GetResourceURLBase64Hash) { |
| // Expected base64 hash for some selected URLs. |
| EXPECT_EQ("dVSI/sC1cGk=", PrecacheFetcher::GetResourceURLBase64HashForTesting( |
| {GURL("http://used-resource-1/a.js")})); |
| EXPECT_EQ("B/Jc6JvusZQ=", PrecacheFetcher::GetResourceURLBase64HashForTesting( |
| {GURL("http://used-resource-1/b.js")})); |
| EXPECT_EQ("CmvACGJ4k08=", PrecacheFetcher::GetResourceURLBase64HashForTesting( |
| {GURL("http://used-resource-1/c.js")})); |
| |
| EXPECT_EQ("dVSI/sC1cGkH8lzom+6xlA==", |
| PrecacheFetcher::GetResourceURLBase64HashForTesting( |
| {GURL("http://used-resource-1/a.js"), |
| GURL("http://used-resource-1/b.js")})); |
| } |
| |
| TEST_F(PrecacheFetcherTest, SendUsedDownloadedResourceHash) { |
| SetDefaultFlags(); |
| |
| std::unique_ptr<PrecacheUnfinishedWork> unfinished_work( |
| new PrecacheUnfinishedWork()); |
| unfinished_work->set_start_time(base::Time::UnixEpoch().ToInternalValue()); |
| unfinished_work->add_top_host()->set_hostname("top-host-1.com"); |
| unfinished_work->add_top_host()->set_hostname("top-host-2.com"); |
| unfinished_work->add_top_host()->set_hostname("top-host-3.com"); |
| |
| UpdatePrecacheReferrerHost("top-host-1.com", 1001); |
| UpdatePrecacheReferrerHost("top-host-2.com", 1002); |
| UpdatePrecacheReferrerHost("top-host-3.com", 1003); |
| |
| // Mark some resources as precached. |
| RecordURLPrefetch(GURL("http://used-resource-1/a.js"), "top-host-1.com"); |
| RecordURLPrefetch(GURL("http://used-resource-1/b.js"), "top-host-1.com"); |
| RecordURLPrefetch(GURL("http://unused-resource-1/c.js"), "top-host-1.com"); |
| RecordURLPrefetch(GURL("http://unused-resource-2/a.js"), "top-host-2.com"); |
| RecordURLPrefetch(GURL("http://unused-resource-2/b.js"), "top-host-2.com"); |
| base::RunLoop().RunUntilIdle(); |
| |
| // Mark some resources as used during user browsing. |
| RecordURLNonPrefetch(GURL("http://used-resource-1/a.js")); |
| RecordURLNonPrefetch(GURL("http://used-resource-1/b.js")); |
| base::RunLoop().RunUntilIdle(); |
| |
| factory_.SetFakeResponse(GURL(kConfigURL), std::string(), net::HTTP_OK, |
| net::URLRequestStatus::SUCCESS); |
| factory_.SetFakeResponse( |
| GURL(std::string(kManifestURLPrefix) + |
| "top-host-1.com?manifest=1001&used_resources=" + |
| net::EscapeQueryParamValue( |
| PrecacheFetcher::GetResourceURLBase64HashForTesting( |
| {GURL("http://used-resource-1/a.js"), |
| GURL("http://used-resource-1/b.js")}), |
| true) + |
| "&d=" + net::EscapeQueryParamValue( |
| PrecacheFetcher::GetResourceURLBase64HashForTesting( |
| {GURL("http://used-resource-1/a.js"), |
| GURL("http://used-resource-1/b.js"), |
| GURL("http://unused-resource-1/c.js")}), |
| true)), |
| std::string(), net::HTTP_OK, net::URLRequestStatus::SUCCESS); |
| factory_.SetFakeResponse( |
| GURL(std::string(kManifestURLPrefix) + |
| "top-host-2.com?manifest=1002&used_resources=&d=" + |
| net::EscapeQueryParamValue( |
| PrecacheFetcher::GetResourceURLBase64HashForTesting( |
| {GURL("http://unused-resource-2/a.js"), |
| GURL("http://unused-resource-2/b.js")}), |
| true)), |
| std::string(), net::HTTP_OK, net::URLRequestStatus::SUCCESS); |
| factory_.SetFakeResponse( |
| GURL(std::string(kManifestURLPrefix) + |
| "top-host-3.com?manifest=1003&used_resources=&d="), |
| std::string(), net::HTTP_OK, net::URLRequestStatus::SUCCESS); |
| |
| { |
| PrecacheFetcher precache_fetcher( |
| request_context_.get(), GURL(), std::string(), |
| std::move(unfinished_work), kExperimentID, |
| precache_database_.GetWeakPtr(), task_runner(), &precache_delegate_); |
| precache_fetcher.Start(); |
| |
| base::RunLoop().RunUntilIdle(); |
| } |
| |
| // If we run the precache again, no download should be reported. |
| factory_.ClearFakeResponses(); |
| factory_.SetFakeResponse(GURL(kConfigURL), std::string(), net::HTTP_OK, |
| net::URLRequestStatus::SUCCESS); |
| // Since we returned an empty proto, the manifest id was set to 0. |
| // The d='s are empty because precache fetches are tried first solely from the |
| // cache and, since any matching request to the fake factory succeeds, it is |
| // hardcoded to be cached even though we didn't specify it as such in the fake |
| // response. |
| factory_.SetFakeResponse(GURL(std::string(kManifestURLPrefix) + |
| "top-host-1.com?manifest=0&used_resources=&d="), |
| std::string(), net::HTTP_OK, |
| net::URLRequestStatus::SUCCESS); |
| factory_.SetFakeResponse(GURL(std::string(kManifestURLPrefix) + |
| "top-host-2.com?manifest=0&used_resources=&d="), |
| std::string(), net::HTTP_OK, |
| net::URLRequestStatus::SUCCESS); |
| factory_.SetFakeResponse(GURL(std::string(kManifestURLPrefix) + |
| "top-host-3.com?manifest=0&used_resources=&d="), |
| std::string(), net::HTTP_OK, |
| net::URLRequestStatus::SUCCESS); |
| // Flush so that previous UpdatePrecacheReferrerHost calls make it through. |
| // Otherwise, manifest_id may be non 0 for some of the hosts. |
| Flush(); |
| { |
| std::unique_ptr<PrecacheUnfinishedWork> more_work( |
| new PrecacheUnfinishedWork()); |
| more_work->set_start_time(base::Time::UnixEpoch().ToInternalValue()); |
| more_work->add_top_host()->set_hostname("top-host-1.com"); |
| more_work->add_top_host()->set_hostname("top-host-2.com"); |
| more_work->add_top_host()->set_hostname("top-host-3.com"); |
| PrecacheFetcher precache_fetcher( |
| request_context_.get(), GURL(), std::string(), std::move(more_work), |
| kExperimentID, precache_database_.GetWeakPtr(), task_runner(), |
| &precache_delegate_); |
| precache_fetcher.Start(); |
| |
| base::RunLoop().RunUntilIdle(); |
| } |
| } |
| |
| TEST(PrecacheFetcherResourceWeightTest, Naive) { |
| ASSERT_EQ( |
| 0, ResourceWeight(PrecacheConfigurationSettings::FUNCTION_NAIVE, 0, 100)); |
| ASSERT_EQ( |
| 4, ResourceWeight(PrecacheConfigurationSettings::FUNCTION_NAIVE, 1, 4)); |
| ASSERT_EQ(8, ResourceWeight(PrecacheConfigurationSettings::FUNCTION_NAIVE, |
| 0.5, 16)); |
| } |
| |
| TEST(PrecacheFetcherResourceWeightTest, Geometric) { |
| ASSERT_EQ(0, ResourceWeight(PrecacheConfigurationSettings::FUNCTION_GEOMETRIC, |
| 0, 100)); |
| ASSERT_EQ(1, ResourceWeight(PrecacheConfigurationSettings::FUNCTION_GEOMETRIC, |
| 1, 4)); |
| ASSERT_NEAR(0.9999847, |
| ResourceWeight(PrecacheConfigurationSettings::FUNCTION_GEOMETRIC, |
| 0.5, 16), |
| 0.0000001); |
| } |
| |
| class PrecacheFetcherGlobalRankingTest |
| : public PrecacheFetcherTest, |
| public testing::WithParamInterface< |
| PrecacheConfigurationSettings::ResourceWeightFunction> {}; |
| |
| TEST_P(PrecacheFetcherGlobalRankingTest, GloballyRankResources) { |
| SetDefaultFlags(); |
| |
| const size_t kNumTopHosts = 5; |
| const size_t kNumResources = 5; |
| |
| std::vector<GURL> expected_requested_urls; |
| std::vector<std::string> expected_manifest_hosts; |
| |
| PrecacheConfigurationSettings config; |
| config.set_top_sites_count(kNumTopHosts); |
| config.set_global_ranking(true); |
| config.set_resource_weight_function(GetParam()); |
| factory_.SetFakeResponse(GURL(kConfigURL), config.SerializeAsString(), |
| net::HTTP_OK, net::URLRequestStatus::SUCCESS); |
| expected_requested_urls.emplace_back(kConfigURL); |
| |
| std::unique_ptr<PrecacheUnfinishedWork> unfinished_work( |
| new PrecacheUnfinishedWork()); |
| unfinished_work->set_start_time(base::Time::UnixEpoch().ToInternalValue()); |
| |
| for (size_t i = 0; i < kNumTopHosts; ++i) { |
| const std::string top_host_url = base::StringPrintf("top-host-%zu.com", i); |
| expected_requested_urls.emplace_back(kManifestURLPrefix + top_host_url); |
| expected_manifest_hosts.push_back(top_host_url); |
| } |
| |
| // Visit counts and weights are chosen in such a way that resource requests |
| // between different hosts will be interleaved. |
| std::vector<std::pair<std::string, float>> resources; |
| for (size_t i = 0; i < kNumTopHosts; ++i) { |
| const std::string top_host_url = base::StringPrintf("top-host-%zu.com", i); |
| TopHost* top_host = unfinished_work->add_top_host(); |
| top_host->set_hostname(top_host_url); |
| top_host->set_visits(kNumTopHosts - i); |
| |
| PrecacheManifest manifest; |
| for (size_t j = 0; j < kNumResources; ++j) { |
| const float weight = 1 - static_cast<float>(j) / kNumResources; |
| const std::string resource_url = |
| base::StringPrintf("http://top-host-%zu.com/resource-%zu-weight-%.1f", |
| i, j, top_host->visits() * weight); |
| PrecacheResource* resource = manifest.add_resource(); |
| resource->set_url(resource_url); |
| resource->set_weight_ratio(weight); |
| factory_.SetFakeResponse(GURL(resource_url), "good", net::HTTP_OK, |
| net::URLRequestStatus::SUCCESS); |
| resources.emplace_back( |
| resource_url, ResourceWeight(GetParam(), weight, top_host->visits())); |
| } |
| factory_.SetFakeResponse(GURL(kManifestURLPrefix + top_host_url), |
| manifest.SerializeAsString(), net::HTTP_OK, |
| net::URLRequestStatus::SUCCESS); |
| } |
| // Sort by descending weight. |
| std::stable_sort(resources.begin(), resources.end(), |
| [](const std::pair<std::string, float>& a, |
| const std::pair<std::string, float>& b) { |
| return a.second > b.second; |
| }); |
| for (const auto& resource : resources) |
| expected_requested_urls.emplace_back(resource.first); |
| |
| { |
| PrecacheFetcher precache_fetcher( |
| request_context_.get(), GURL(), std::string(), |
| std::move(unfinished_work), kExperimentID, |
| precache_database_.GetWeakPtr(), task_runner(), &precache_delegate_); |
| precache_fetcher.Start(); |
| base::RunLoop().RunUntilIdle(); |
| } |
| |
| EXPECT_EQ(expected_requested_urls, url_callback_.requested_urls()); |
| EXPECT_EQ(expected_manifest_hosts, precache_delegate_.get_manifest_hosts()); |
| EXPECT_TRUE(precache_delegate_.was_on_done_called()); |
| } |
| |
| INSTANTIATE_TEST_CASE_P( |
| PrecacheFetcherGlobalRankingTest, |
| PrecacheFetcherGlobalRankingTest, |
| testing::Values(PrecacheConfigurationSettings::FUNCTION_NAIVE, |
| PrecacheConfigurationSettings::FUNCTION_GEOMETRIC)); |
| |
| TEST_F(PrecacheFetcherTest, GloballyRankResourcesAfterPauseResume) { |
| SetDefaultFlags(); |
| |
| const size_t kNumTopHosts = 5; |
| const size_t kNumResources = 5; |
| |
| std::vector<GURL> expected_requested_urls; |
| std::vector<std::string> expected_manifest_hosts; |
| |
| PrecacheConfigurationSettings config; |
| config.set_top_sites_count(kNumTopHosts); |
| config.set_global_ranking(true); |
| factory_.SetFakeResponse(GURL(kConfigURL), config.SerializeAsString(), |
| net::HTTP_OK, net::URLRequestStatus::SUCCESS); |
| |
| std::unique_ptr<PrecacheUnfinishedWork> unfinished_work( |
| new PrecacheUnfinishedWork()); |
| unfinished_work->set_start_time(base::Time::UnixEpoch().ToInternalValue()); |
| |
| // Visit counts and weights are chosen in such a way that resource requests |
| // between different hosts will be interleaved. |
| std::vector<std::pair<std::string, float>> resources; |
| for (size_t i = 0; i < kNumTopHosts; ++i) { |
| const std::string top_host_url = base::StringPrintf("top-host-%zu.com", i); |
| expected_manifest_hosts.push_back(top_host_url); |
| TopHost* top_host = unfinished_work->add_top_host(); |
| top_host->set_hostname(top_host_url); |
| top_host->set_visits(kNumTopHosts - i); |
| |
| PrecacheManifest manifest; |
| for (size_t j = 0; j < kNumResources; ++j) { |
| const float weight = 1 - static_cast<float>(j) / kNumResources; |
| const std::string resource_url = |
| base::StringPrintf("http://top-host-%zu.com/resource-%zu-weight-%.1f", |
| i, j, top_host->visits() * weight); |
| PrecacheResource* resource = manifest.add_resource(); |
| resource->set_url(resource_url); |
| resource->set_weight_ratio(weight); |
| factory_.SetFakeResponse(GURL(resource_url), "good", net::HTTP_OK, |
| net::URLRequestStatus::SUCCESS); |
| resources.emplace_back(resource_url, |
| top_host->visits() * resource->weight_ratio()); |
| } |
| factory_.SetFakeResponse(GURL(kManifestURLPrefix + top_host_url), |
| manifest.SerializeAsString(), net::HTTP_OK, |
| net::URLRequestStatus::SUCCESS); |
| } |
| // Sort by descending weight. |
| std::stable_sort(resources.begin(), resources.end(), |
| [](const std::pair<std::string, float>& a, |
| const std::pair<std::string, float>& b) { |
| return a.second > b.second; |
| }); |
| for (const auto& resource : resources) |
| expected_requested_urls.emplace_back(resource.first); |
| |
| std::unique_ptr<PrecacheUnfinishedWork> cancelled_work; |
| { |
| uint32_t remaining_tries = 100; |
| PrecacheFetcher precache_fetcher( |
| request_context_.get(), GURL(), std::string(), |
| std::move(unfinished_work), kExperimentID, |
| precache_database_.GetWeakPtr(), task_runner(), &precache_delegate_); |
| precache_fetcher.Start(); |
| |
| // Run the loop until all tophost manifest fetches are complete, but some |
| // resource fetches are pending. |
| while (--remaining_tries != 0 && |
| (!precache_fetcher.top_hosts_to_fetch_.empty() || |
| !precache_fetcher.top_hosts_fetching_.empty() || |
| !precache_fetcher.unfinished_work_->has_config_settings() || |
| precache_fetcher.resources_to_fetch_.empty())) { |
| LOG(INFO) << "remaining_tries: " << remaining_tries; |
| base::RunLoop run_loop; |
| base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, |
| run_loop.QuitClosure()); |
| run_loop.Run(); |
| } |
| |
| // Cancel precaching. |
| cancelled_work = precache_fetcher.CancelPrecaching(); |
| EXPECT_TRUE(precache_fetcher.top_hosts_to_fetch_.empty()); |
| EXPECT_TRUE(precache_fetcher.resources_to_fetch_.empty()); |
| } |
| EXPECT_NE(cancelled_work, nullptr); |
| EXPECT_TRUE(cancelled_work->top_host().empty()); |
| EXPECT_EQ(kNumTopHosts * kNumResources, |
| static_cast<size_t>(cancelled_work->resource().size())); |
| EXPECT_EQ(expected_manifest_hosts, precache_delegate_.get_manifest_hosts()); |
| EXPECT_FALSE(precache_delegate_.was_on_done_called()); |
| |
| url_callback_.clear_requested_urls(); |
| precache_delegate_.clear_manifest_hosts(); |
| |
| // Continuing with the precache should fetch all resources, as the previous |
| // run was cancelled before any finished. They should be fetched in global |
| // ranking order. |
| |
| base::HistogramTester histogram; |
| |
| { |
| PrecacheFetcher precache_fetcher( |
| request_context_.get(), GURL(), std::string(), |
| std::move(cancelled_work), kExperimentID, |
| precache_database_.GetWeakPtr(), task_runner(), &precache_delegate_); |
| LOG(INFO) << "Resuming prefetch."; |
| precache_fetcher.Start(); |
| base::RunLoop().RunUntilIdle(); |
| } |
| EXPECT_EQ(expected_requested_urls, url_callback_.requested_urls()); |
| EXPECT_TRUE(precache_delegate_.get_manifest_hosts().empty()); |
| EXPECT_TRUE(precache_delegate_.was_on_done_called()); |
| |
| histogram.ExpectBucketCount("Precache.Fetch.MinWeight", |
| 1000.0 * resources.back().second, 1); |
| } |
| |
| TEST_F(PrecacheFetcherTest, MaxTotalResources) { |
| SetDefaultFlags(); |
| |
| const size_t kNumResources = 5; |
| |
| std::vector<GURL> expected_requested_urls; |
| std::vector<std::string> expected_manifest_hosts; |
| |
| PrecacheConfigurationSettings config; |
| config.set_total_resources_count(2); |
| config.set_global_ranking(true); |
| factory_.SetFakeResponse(GURL(kConfigURL), config.SerializeAsString(), |
| net::HTTP_OK, net::URLRequestStatus::SUCCESS); |
| expected_requested_urls.emplace_back(kConfigURL); |
| |
| std::unique_ptr<PrecacheUnfinishedWork> unfinished_work( |
| new PrecacheUnfinishedWork()); |
| unfinished_work->set_start_time(base::Time::UnixEpoch().ToInternalValue()); |
| |
| TopHost* top_host = unfinished_work->add_top_host(); |
| top_host->set_hostname("top-host.com"); |
| top_host->set_visits(1); |
| |
| expected_requested_urls.emplace_back(kManifestURLPrefix + |
| top_host->hostname()); |
| expected_manifest_hosts.push_back(top_host->hostname()); |
| |
| PrecacheManifest manifest; |
| for (size_t i = 0; i < kNumResources; ++i) { |
| const float weight = 1 - static_cast<float>(i) / kNumResources; |
| const std::string resource_url = |
| base::StringPrintf("http://top-host.com/resource-%zu-weight-%.1f", i, |
| top_host->visits() * weight); |
| PrecacheResource* resource = manifest.add_resource(); |
| resource->set_url(resource_url); |
| resource->set_weight_ratio(weight); |
| factory_.SetFakeResponse(GURL(resource_url), "good", net::HTTP_OK, |
| net::URLRequestStatus::SUCCESS); |
| if (i < config.total_resources_count()) |
| expected_requested_urls.emplace_back(resource_url); |
| } |
| factory_.SetFakeResponse(GURL(kManifestURLPrefix + top_host->hostname()), |
| manifest.SerializeAsString(), net::HTTP_OK, |
| net::URLRequestStatus::SUCCESS); |
| |
| base::HistogramTester histogram; |
| |
| { |
| PrecacheFetcher precache_fetcher( |
| request_context_.get(), GURL(), std::string(), |
| std::move(unfinished_work), kExperimentID, |
| precache_database_.GetWeakPtr(), task_runner(), &precache_delegate_); |
| precache_fetcher.Start(); |
| base::RunLoop().RunUntilIdle(); |
| } |
| |
| EXPECT_EQ(expected_requested_urls, url_callback_.requested_urls()); |
| EXPECT_EQ(expected_manifest_hosts, precache_delegate_.get_manifest_hosts()); |
| EXPECT_TRUE(precache_delegate_.was_on_done_called()); |
| |
| const float expected_min_weight = |
| manifest.resource(config.total_resources_count() - 1).weight_ratio(); |
| histogram.ExpectUniqueSample("Precache.Fetch.MinWeight", |
| 1000.0 * expected_min_weight, 1); |
| } |
| |
| TEST_F(PrecacheFetcherTest, MinWeight) { |
| SetDefaultFlags(); |
| |
| const size_t kNumResources = 5; |
| |
| std::vector<GURL> expected_requested_urls; |
| std::vector<std::string> expected_manifest_hosts; |
| |
| PrecacheConfigurationSettings config; |
| config.set_min_weight(3); |
| config.set_global_ranking(true); |
| factory_.SetFakeResponse(GURL(kConfigURL), config.SerializeAsString(), |
| net::HTTP_OK, net::URLRequestStatus::SUCCESS); |
| expected_requested_urls.emplace_back(kConfigURL); |
| |
| std::unique_ptr<PrecacheUnfinishedWork> unfinished_work( |
| new PrecacheUnfinishedWork()); |
| unfinished_work->set_start_time(base::Time::UnixEpoch().ToInternalValue()); |
| |
| TopHost* top_host = unfinished_work->add_top_host(); |
| top_host->set_hostname("top-host.com"); |
| top_host->set_visits(5); |
| |
| expected_requested_urls.emplace_back(kManifestURLPrefix + |
| top_host->hostname()); |
| expected_manifest_hosts.push_back(top_host->hostname()); |
| |
| PrecacheManifest manifest; |
| for (size_t i = 0; i < kNumResources; ++i) { |
| const float weight = 1 - static_cast<float>(i) / kNumResources; |
| const std::string resource_url = |
| base::StringPrintf("http://top-host.com/resource-%zu-weight-%.1f", i, |
| top_host->visits() * weight); |
| PrecacheResource* resource = manifest.add_resource(); |
| resource->set_url(resource_url); |
| resource->set_weight_ratio(weight); |
| factory_.SetFakeResponse(GURL(resource_url), "good", net::HTTP_OK, |
| net::URLRequestStatus::SUCCESS); |
| // If top_host->visits() * weight > config.min_weight(): |
| if (i < 3) |
| expected_requested_urls.emplace_back(resource_url); |
| } |
| factory_.SetFakeResponse(GURL(kManifestURLPrefix + top_host->hostname()), |
| manifest.SerializeAsString(), net::HTTP_OK, |
| net::URLRequestStatus::SUCCESS); |
| |
| { |
| PrecacheFetcher precache_fetcher( |
| request_context_.get(), GURL(), std::string(), |
| std::move(unfinished_work), kExperimentID, |
| precache_database_.GetWeakPtr(), task_runner(), &precache_delegate_); |
| precache_fetcher.Start(); |
| base::RunLoop().RunUntilIdle(); |
| } |
| |
| EXPECT_EQ(expected_requested_urls, url_callback_.requested_urls()); |
| EXPECT_EQ(expected_manifest_hosts, precache_delegate_.get_manifest_hosts()); |
| EXPECT_TRUE(precache_delegate_.was_on_done_called()); |
| } |
| |
| // Tests cancel precaching when all tophost manifests are fetched, but some |
| // resource fetches are pending. |
| TEST_F(PrecacheFetcherTest, CancelPrecachingAfterAllManifestFetch) { |
| SetDefaultFlags(); |
| |
| const size_t kNumTopHosts = 5; |
| const size_t kNumResources = 5; |
| |
| PrecacheConfigurationSettings config; |
| std::vector<GURL> expected_requested_urls; |
| std::vector<std::string> expected_manifest_hosts; |
| std::unique_ptr<PrecacheUnfinishedWork> cancelled_work; |
| |
| config.set_top_sites_count(kNumTopHosts); |
| factory_.SetFakeResponse(GURL(kConfigURL), config.SerializeAsString(), |
| net::HTTP_OK, net::URLRequestStatus::SUCCESS); |
| expected_requested_urls.emplace_back(kConfigURL); |
| |
| std::unique_ptr<PrecacheUnfinishedWork> unfinished_work( |
| new PrecacheUnfinishedWork()); |
| unfinished_work->set_start_time(base::Time::UnixEpoch().ToInternalValue()); |
| |
| for (size_t i = 0; i < kNumTopHosts; ++i) { |
| const std::string top_host_url = base::StringPrintf("top-host-%zu.com", i); |
| expected_requested_urls.emplace_back(kManifestURLPrefix + top_host_url); |
| expected_manifest_hosts.push_back(top_host_url); |
| } |
| |
| int num_resources = 0; |
| for (size_t i = 0; i < kNumTopHosts; ++i) { |
| const std::string top_host_url = base::StringPrintf("top-host-%zu.com", i); |
| TopHost* top_host = unfinished_work->add_top_host(); |
| top_host->set_hostname(top_host_url); |
| top_host->set_visits(kNumTopHosts - i); |
| |
| PrecacheManifest manifest; |
| for (size_t j = 0; j < kNumResources; ++j) { |
| const std::string resource_url = |
| base::StringPrintf("http://top-host-%zu.com/resource-%zu", i, j); |
| PrecacheResource* resource = manifest.add_resource(); |
| resource->set_url(resource_url); |
| resource->set_weight_ratio(1); |
| factory_.SetFakeResponse(GURL(resource_url), "good", net::HTTP_OK, |
| net::URLRequestStatus::SUCCESS); |
| if (++num_resources <= kMaxParallelFetches) |
| expected_requested_urls.emplace_back(resource_url); |
| } |
| factory_.SetFakeResponse(GURL(kManifestURLPrefix + top_host_url), |
| manifest.SerializeAsString(), net::HTTP_OK, |
| net::URLRequestStatus::SUCCESS); |
| } |
| |
| { |
| uint32_t remaining_tries = 100; |
| PrecacheFetcher precache_fetcher( |
| request_context_.get(), GURL(), std::string(), |
| std::move(unfinished_work), kExperimentID, |
| precache_database_.GetWeakPtr(), task_runner(), &precache_delegate_); |
| precache_fetcher.Start(); |
| |
| // Run the loop until all tophost manifest fetches are complete, but some |
| // resource fetches are pending. |
| while (--remaining_tries != 0 && |
| (!precache_fetcher.top_hosts_to_fetch_.empty() || |
| !precache_fetcher.top_hosts_fetching_.empty() || |
| !precache_fetcher.unfinished_work_->has_config_settings() || |
| precache_fetcher.resources_to_fetch_.empty())) { |
| LOG(INFO) << "remaining_tries: " << remaining_tries; |
| base::RunLoop run_loop; |
| base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, |
| run_loop.QuitClosure()); |
| run_loop.Run(); |
| } |
| |
| // Cancel precaching. |
| cancelled_work = precache_fetcher.CancelPrecaching(); |
| EXPECT_TRUE(precache_fetcher.top_hosts_to_fetch_.empty()); |
| EXPECT_TRUE(precache_fetcher.resources_to_fetch_.empty()); |
| } |
| ASSERT_NE(nullptr, cancelled_work); |
| EXPECT_TRUE(cancelled_work->top_host().empty()); |
| EXPECT_EQ(kNumTopHosts * kNumResources, |
| static_cast<size_t>(cancelled_work->resource().size())); |
| |
| EXPECT_EQ(expected_requested_urls, url_callback_.requested_urls()); |
| EXPECT_EQ(expected_manifest_hosts, precache_delegate_.get_manifest_hosts()); |
| EXPECT_FALSE(precache_delegate_.was_on_done_called()); |
| |
| // Continuing with the precache should fetch all resources, as the previous |
| // run was cancelled before any finished. |
| expected_requested_urls.clear(); |
| url_callback_.clear_requested_urls(); |
| precache_delegate_.clear_manifest_hosts(); |
| for (size_t i = 0; i < kNumTopHosts; ++i) { |
| for (size_t j = 0; j < kNumResources; ++j) { |
| expected_requested_urls.emplace_back( |
| base::StringPrintf("http://top-host-%zu.com/resource-%zu", i, j)); |
| } |
| } |
| { |
| PrecacheFetcher precache_fetcher( |
| request_context_.get(), GURL(), std::string(), |
| std::move(cancelled_work), kExperimentID, |
| precache_database_.GetWeakPtr(), task_runner(), &precache_delegate_); |
| LOG(INFO) << "Resuming prefetch."; |
| precache_fetcher.Start(); |
| base::RunLoop().RunUntilIdle(); |
| } |
| EXPECT_EQ(expected_requested_urls, url_callback_.requested_urls()); |
| EXPECT_TRUE(precache_delegate_.get_manifest_hosts().empty()); |
| EXPECT_TRUE(precache_delegate_.was_on_done_called()); |
| } |
| |
| TEST_F(PrecacheFetcherTest, DailyQuota) { |
| SetDefaultFlags(); |
| |
| const size_t kNumTopHosts = 3; |
| |
| std::unique_ptr<PrecacheUnfinishedWork> unfinished_work( |
| new PrecacheUnfinishedWork()); |
| unfinished_work->set_start_time(base::Time::UnixEpoch().ToInternalValue()); |
| |
| PrecacheConfigurationSettings config; |
| config.set_top_sites_count(kNumTopHosts); |
| config.set_daily_quota_total(10000); |
| factory_.SetFakeResponse(GURL(kConfigURL), config.SerializeAsString(), |
| net::HTTP_OK, net::URLRequestStatus::SUCCESS); |
| std::vector<GURL> expected_requested_urls; |
| std::vector<std::string> expected_manifest_hosts; |
| expected_requested_urls.emplace_back(kConfigURL); |
| |
| for (size_t i = 0; i < kNumTopHosts; ++i) { |
| const std::string top_host_url = base::StringPrintf("top-host-%zu.com", i); |
| expected_requested_urls.emplace_back(std::string(kManifestURLPrefix) + |
| top_host_url); |
| expected_manifest_hosts.push_back(top_host_url); |
| } |
| |
| for (size_t i = 0; i < kNumTopHosts; ++i) { |
| const std::string top_host_url = base::StringPrintf("top-host-%zu.com", i); |
| const std::string resource_url = |
| base::StringPrintf("http://top-host-%zu.com/resource.html", i); |
| PrecacheManifest manifest; |
| manifest.add_resource()->set_url(resource_url); |
| |
| unfinished_work->add_top_host()->set_hostname(top_host_url); |
| factory_.SetFakeResponse( |
| GURL(std::string(kManifestURLPrefix) + top_host_url), |
| manifest.SerializeAsString(), net::HTTP_OK, |
| net::URLRequestStatus::SUCCESS); |
| // Set a 5000 byte resource. |
| factory_.SetFakeResponse(GURL(resource_url), std::string(5000, 'a'), |
| net::HTTP_OK, net::URLRequestStatus::SUCCESS); |
| |
| expected_requested_urls.emplace_back(resource_url); |
| } |
| |
| base::HistogramTester histogram; |
| |
| { |
| PrecacheFetcher precache_fetcher( |
| request_context_.get(), GURL(), std::string(), |
| std::move(unfinished_work), kExperimentID, |
| precache_database_.GetWeakPtr(), task_runner(), &precache_delegate_); |
| precache_fetcher.Start(); |
| |
| base::RunLoop().RunUntilIdle(); |
| |
| EXPECT_EQ(0U, precache_fetcher.quota_.remaining()); |
| unfinished_work = precache_fetcher.CancelPrecaching(); |
| } |
| |
| EXPECT_EQ(expected_requested_urls, url_callback_.requested_urls()); |
| EXPECT_EQ(expected_manifest_hosts, precache_delegate_.get_manifest_hosts()); |
| EXPECT_TRUE(precache_delegate_.was_on_done_called()); |
| |
| EXPECT_EQ(0, unfinished_work->top_host_size()); |
| EXPECT_EQ(1, unfinished_work->resource_size()); |
| |
| histogram.ExpectTotalCount("Precache.Fetch.PercentCompleted", 1); |
| histogram.ExpectTotalCount("Precache.Fetch.ResponseBytes.Total", 1); |
| histogram.ExpectTotalCount("Precache.Fetch.TimeToComplete", 1); |
| |
| // Continuing with the precache when quota limit is reached, will not fetch |
| // any resources. |
| expected_requested_urls.clear(); |
| url_callback_.clear_requested_urls(); |
| precache_delegate_.clear_manifest_hosts(); |
| { |
| PrecacheFetcher precache_fetcher( |
| request_context_.get(), GURL(), std::string(), |
| std::move(unfinished_work), kExperimentID, |
| precache_database_.GetWeakPtr(), task_runner(), &precache_delegate_); |
| precache_fetcher.Start(); |
| base::RunLoop().RunUntilIdle(); |
| |
| EXPECT_EQ(0U, precache_fetcher.quota_.remaining()); |
| } |
| EXPECT_EQ(expected_requested_urls, url_callback_.requested_urls()); |
| EXPECT_TRUE(precache_delegate_.get_manifest_hosts().empty()); |
| EXPECT_TRUE(precache_delegate_.was_on_done_called()); |
| |
| histogram.ExpectTotalCount("Precache.Fetch.PercentCompleted", 2); |
| histogram.ExpectTotalCount("Precache.Fetch.ResponseBytes.Total", 2); |
| histogram.ExpectTotalCount("Precache.Fetch.TimeToComplete", 2); |
| } |
| |
| } // namespace precache |