blob: 150bf0c0cc24c4c59ad871182e73cfa69f5e58e7 [file] [log] [blame]
// Copyright 2014 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 "content/browser/appcache/appcache_update_job.h"
#include <stddef.h>
#include <stdint.h>
#include <memory>
#include <utility>
#include "base/bind.h"
#include "base/callback_helpers.h"
#include "base/location.h"
#include "base/memory/ptr_util.h"
#include "base/run_loop.h"
#include "base/single_thread_task_runner.h"
#include "base/stl_util.h"
#include "base/strings/strcat.h"
#include "base/synchronization/waitable_event.h"
#include "base/test/scoped_feature_list.h"
#include "base/test/task_environment.h"
#include "base/threading/thread_task_runner_handle.h"
#include "content/browser/appcache/appcache_cache_test_helper.h"
#include "content/browser/appcache/appcache_disk_cache_ops.h"
#include "content/browser/appcache/appcache_group.h"
#include "content/browser/appcache/appcache_host.h"
#include "content/browser/appcache/appcache_response_info.h"
#include "content/browser/appcache/appcache_update_url_loader_request.h"
#include "content/browser/appcache/mock_appcache_service.h"
#include "content/browser/appcache/test_origin_trial_policy.h"
#include "content/browser/child_process_security_policy_impl.h"
#include "content/browser/storage_partition_impl.h"
#include "content/browser/url_loader_factory_getter.h"
#include "content/public/browser/browser_task_traits.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/common/origin_util.h"
#include "content/public/test/browser_task_environment.h"
#include "content/public/test/test_browser_context.h"
#include "content/public/test/url_loader_interceptor.h"
#include "mojo/public/cpp/bindings/pending_remote.h"
#include "mojo/public/cpp/bindings/remote.h"
#include "mojo/public/cpp/system/data_pipe.h"
#include "net/base/net_errors.h"
#include "net/http/http_request_headers.h"
#include "net/http/http_response_headers.h"
#include "net/http/http_util.h"
#include "services/network/public/mojom/url_loader.mojom.h"
#include "services/network/public/mojom/url_loader_factory.mojom.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/blink/public/common/features.h"
#include "third_party/blink/public/common/origin_trials/origin_trial_policy.h"
#include "third_party/blink/public/common/origin_trials/trial_token_validator.h"
#include "third_party/blink/public/mojom/appcache/appcache.mojom.h"
#include "third_party/blink/public/mojom/appcache/appcache_info.mojom.h"
#include "third_party/blink/public/mojom/devtools/console_message.mojom.h"
namespace {
// tools/origin_trials/generate_token.py http://mockhost AppCache
// --expire-days=2000
//
// tools/origin_trials/check_token.py extracts token expiry: 1761166418.
#define APPCACHE_ORIGIN_TRIAL_TOKEN \
"AhiiB7vi3JiEO1/" \
"RQIytQslLSN3WYVu3Xd32abYhTia+91ladjnXSClfU981x+" \
"aoPimEqYVy6tWoeMZZYTpqlggAAABNeyJvcmlnaW4iOiAiaHR0cDovL21vY2tob3N0OjgwIiwg" \
"ImZlYXR1cmUiOiAiQXBwQ2FjaGUiLCAiZXhwaXJ5IjogMTc2MTE2NjQxOH0="
const base::Time kTestOriginTrialTokenExpiry =
base::Time::FromDoubleT(1761166418);
} // namespace
namespace content {
static TestOriginTrialPolicy g_origin_trial_policy;
namespace appcache_update_job_unittest {
class AppCacheUpdateJobTest;
// Values should match values used in appcache_update_job.cc.
const base::TimeDelta kFullUpdateInterval = base::TimeDelta::FromHours(24);
const base::TimeDelta kMaxEvictableErrorDuration =
base::TimeDelta::FromDays(14);
const base::TimeDelta kOneHour = base::TimeDelta::FromHours(1);
const base::TimeDelta kOneYear = base::TimeDelta::FromDays(365);
const char kManifest1Contents[] =
"CACHE MANIFEST\n"
"explicit1\n"
"FALLBACK:\n"
"fallback1 fallback1a\n"
"NETWORK:\n"
"*\n";
const char kManifest1OriginTrialContents[] =
"CACHE MANIFEST\n"
"explicit1\n"
"ORIGIN-TRIAL:\n" APPCACHE_ORIGIN_TRIAL_TOKEN
"\n"
"FALLBACK:\n"
"fallback1 fallback1a\n"
"NETWORK:\n"
"*\n";
const char kManifest1WithNotModifiedContents[] =
"CACHE MANIFEST\n"
"explicit1\n"
"FALLBACK:\n"
"fallback1 fallback1a\n"
"NETWORK:\n"
"*\n"
"CACHE:\n"
"notmodified\n";
const char kManifest1WithMaybeModifiedContents[] =
"CACHE MANIFEST\n"
"explicit1\n"
"FALLBACK:\n"
"fallback1 fallback1a\n"
"NETWORK:\n"
"*\n"
"CACHE:\n"
"maybemodified\n";
const char kExplicit1Contents[] = "explicit1";
// By default, kManifest2Contents is served from a path in /files/, so any
// resource listing in it that references outside of that path will require a
// scope override to be stored.
const char kManifest2Contents[] =
"CACHE MANIFEST\n"
"explicit1\n"
"CHROMIUM-INTERCEPT:\n"
"intercept1 return intercept1a\n"
"/bar/intercept2 return intercept2a\n"
"FALLBACK:\n"
"fallback1 fallback1a\n"
"/bar/fallback2 fallback2a\n"
"NETWORK:\n"
"*\n";
const char kManifest2OriginTrialContents[] =
"CACHE MANIFEST\n"
"explicit1\n"
"CHROMIUM-INTERCEPT:\n"
"intercept1 return intercept1a\n"
"/bar/intercept2 return intercept2a\n"
"ORIGIN-TRIAL:\n" APPCACHE_ORIGIN_TRIAL_TOKEN
"\n"
"FALLBACK:\n"
"fallback1 fallback1a\n"
"/bar/fallback2 fallback2a\n"
"NETWORK:\n"
"*\n";
const char kScopeManifestContents[] =
"CACHE MANIFEST\n"
"CHROMIUM-INTERCEPT:\n"
"/bar/foo return /intercept-newbar/foo\n"
"/bar/baz/foo return /intercept-newbar/newbaz/foo\n"
"/other/foo return /intercept-newother/foo\n"
"/ return /intercept-newroot/foo\n"
"FALLBACK:\n"
"/bar/foo /fallback-newbar/foo\n"
"/bar/baz/foo /fallback-newbar/newbaz/foo\n"
"/other/foo /fallback-newother/foo\n"
"/ /fallback-newroot/foo\n";
// There are a handful of http accessible resources that we need to conduct
// these tests. Instead of running a separate server to host these resources,
// we mock them up.
class MockHttpServer {
public:
static url::Origin GetOrigin() {
return url::Origin::Create(GURL("http://mockhost"));
}
static GURL GetMockUrl(const std::string& path) {
return GURL("http://mockhost/" + path);
}
static GURL GetMockHttpsUrl(const std::string& path) {
return GURL("https://mockhost/" + path);
}
static GURL GetMockCrossOriginHttpsUrl(const std::string& path) {
return GURL("https://cross_origin_host/" + path);
}
static std::string GetManifestHeaders(const bool ok,
const std::string& scope) {
std::string headers;
if (ok)
headers += "HTTP/1.1 200 OK\n";
else
headers += "HTTP/1.1 304 NOT MODIFIED\n";
headers += "Content-type: text/cache-manifest\n";
if (!scope.empty())
headers += "X-AppCache-Allowed: " + scope + "\n";
headers += "\n";
return headers;
}
static void GetMockResponse(const std::string& path,
std::string* headers,
std::string* body) {
const char ok_headers[] =
"HTTP/1.1 200 OK\n"
"\n";
const char error_headers[] =
"HTTP/1.1 500 BOO HOO\n"
"\n";
const char manifest_headers[] =
"HTTP/1.1 200 OK\n"
"Content-type: text/cache-manifest\n"
"\n";
const char not_modified_headers[] =
"HTTP/1.1 304 NOT MODIFIED\n"
"X-AppCache-Allowed: /\n"
"\n";
const char gone_headers[] =
"HTTP/1.1 410 GONE\n"
"\n";
const char not_found_headers[] =
"HTTP/1.1 404 NOT FOUND\n"
"\n";
const char no_store_headers[] =
"HTTP/1.1 200 OK\n"
"Cache-Control: no-store\n"
"\n";
if (path == "/files/missing-mime-manifest") {
(*headers) = std::string(ok_headers, base::size(ok_headers));
(*body) = "CACHE MANIFEST\n";
} else if (path == "/files/bad-manifest") {
(*headers) = std::string(manifest_headers, base::size(manifest_headers));
(*body) = "BAD CACHE MANIFEST";
} else if (path == "/files/empty1") {
(*headers) = std::string(ok_headers, base::size(ok_headers));
(*body) = "";
} else if (path == "/files/empty-file-manifest") {
(*headers) = std::string(manifest_headers, base::size(manifest_headers));
(*body) =
"CACHE MANIFEST\n"
"empty1\n";
} else if (path == "/files/empty-manifest") {
(*headers) = std::string(manifest_headers, base::size(manifest_headers));
(*body) = "CACHE MANIFEST\n";
} else if (path == "/files/explicit1") {
(*headers) = std::string(ok_headers, base::size(ok_headers));
(*body) = kExplicit1Contents;
} else if (path == "/files/explicit2") {
(*headers) = std::string(ok_headers, base::size(ok_headers));
(*body) = "explicit2";
} else if (path == "/files/fallback1a") {
(*headers) = std::string(ok_headers, base::size(ok_headers));
(*body) = "fallback1a";
} else if (path == "/files/fallback2a") {
(*headers) = std::string(ok_headers, base::size(ok_headers));
(*body) = "fallback2a";
} else if (path == "/files/intercept1a") {
(*headers) = std::string(ok_headers, base::size(ok_headers));
(*body) = "intercept1a";
} else if (path == "/files/intercept2a") {
(*headers) = std::string(ok_headers, base::size(ok_headers));
(*body) = "intercept2a";
} else if (path == "/files/gone") {
(*headers) = std::string(gone_headers, base::size(gone_headers));
(*body) = "";
} else if (path == "/files/manifest1") {
(*headers) = std::string(manifest_headers, base::size(manifest_headers));
(*body) = kManifest1Contents;
} else if (path == "/files/manifest2") {
(*headers) = std::string(manifest_headers, base::size(manifest_headers));
(*body) = kManifest2Contents;
} else if (path == "/files/manifest2-origin-trial") {
(*headers) = std::string(manifest_headers, base::size(manifest_headers));
(*body) = kManifest2OriginTrialContents;
} else if (path == "/files/manifest2-with-root-override") {
(*headers) = GetManifestHeaders(/*ok=*/true, /*scope=*/"/");
(*body) = kManifest2Contents;
} else if (path == "/files/manifest1-with-notmodified") {
(*headers) = std::string(manifest_headers, base::size(manifest_headers));
(*body) = kManifest1WithNotModifiedContents;
} else if (path == "/files/manifest1-with-maybemodified") {
(*headers) = std::string(manifest_headers, base::size(manifest_headers));
(*body) = kManifest1WithMaybeModifiedContents;
} else if (path == "/files/manifest-fb-404") {
(*headers) = std::string(manifest_headers, base::size(manifest_headers));
(*body) =
"CACHE MANIFEST\n"
"explicit1\n"
"FALLBACK:\n"
"fallback1 fallback1a\n"
"fallback404 fallback-404\n"
"NETWORK:\n"
"online1\n";
} else if (path == "/files/manifest-merged-types") {
(*headers) = std::string(manifest_headers, base::size(manifest_headers));
(*body) =
"CACHE MANIFEST\n"
"explicit1\n"
"# manifest is also an explicit entry\n"
"manifest-merged-types\n"
"FALLBACK:\n"
"# fallback is also explicit entry\n"
"fallback1 explicit1\n"
"NETWORK:\n"
"online1\n";
} else if (path == "/files/manifest-with-404") {
(*headers) = std::string(manifest_headers, base::size(manifest_headers));
(*body) =
"CACHE MANIFEST\n"
"explicit-404\n"
"explicit1\n"
"explicit2\n"
"explicit3\n"
"FALLBACK:\n"
"fallback1 fallback1a\n"
"NETWORK:\n"
"online1\n";
} else if (path == "/files/manifest-with-intercept") {
(*headers) = std::string(manifest_headers, base::size(manifest_headers));
(*body) =
"CACHE MANIFEST\n"
"CHROMIUM-INTERCEPT:\n"
"intercept1 return intercept1a\n";
} else if (path == "/files/maybemodified") {
(*headers) =
std::string(not_modified_headers, base::size(not_modified_headers));
(*body) = "maybemodified";
} else if (path == "/files/modified") {
(*headers) = std::string(ok_headers, base::size(ok_headers));
(*body) = "modified";
} else if (path == "/files/notmodified") {
(*headers) =
std::string(not_modified_headers, base::size(not_modified_headers));
(*body) = "";
} else if (path == "/files/servererror") {
(*headers) = std::string(error_headers, base::size(error_headers));
(*body) = "error";
} else if (path == "/files/valid_cross_origin_https_manifest") {
(*headers) = std::string(manifest_headers, base::size(manifest_headers));
(*body) =
"CACHE MANIFEST\n"
"https://cross_origin_host/files/explicit1\n";
} else if (path == "/files/invalid_cross_origin_https_manifest") {
(*headers) = std::string(manifest_headers, base::size(manifest_headers));
(*body) =
"CACHE MANIFEST\n"
"https://cross_origin_host/files/no-store-headers\n";
} else if (path == "/files/no-store-headers") {
(*headers) = std::string(no_store_headers, base::size(no_store_headers));
(*body) = "no-store";
} else if (path == "/intercept-newbar/foo" ||
path == "/intercept-newbar/newbaz/foo" ||
path == "/intercept-newother/foo" ||
path == "/intercept-newroot/foo" ||
path == "/fallback-newbar/foo" ||
path == "/fallback-newbar/newbaz/foo" ||
path == "/fallback-newother/foo" ||
path == "/fallback-newroot/foo") {
// Store mock responses for fallback resources needed by
// kScopeManifestContents.
(*headers) = std::string(ok_headers, base::size(ok_headers));
(*body) = "intercept/fallback contents";
} else if (path == "/manifest-no-override" ||
path == "/bar/manifest-no-override") {
(*headers) = GetManifestHeaders(/*ok=*/true, /*scope=*/"");
(*body) = kScopeManifestContents;
} else if (path == "/manifest-root-override" ||
path == "/bar/manifest-root-override") {
(*headers) = GetManifestHeaders(/*ok=*/true, /*scope=*/"/");
(*body) = kScopeManifestContents;
} else if (path == "/manifest-bar-override" ||
path == "/bar/manifest-bar-override") {
(*headers) = GetManifestHeaders(/*ok=*/true, /*scope=*/"/bar/");
(*body) = kScopeManifestContents;
} else if (path == "/manifest-other-override" ||
path == "/bar/manifest-other-override") {
(*headers) = GetManifestHeaders(/*ok=*/true, /*scope=*/"/other/");
(*body) = kScopeManifestContents;
} else if (path == "/manifest-304-no-override" ||
path == "/bar/manifest-304-no-override") {
(*headers) = GetManifestHeaders(/*ok=*/false, /*scope=*/"");
(*body) = kScopeManifestContents;
} else if (path == "/manifest-304-root-override" ||
path == "/bar/manifest-304-root-override") {
(*headers) = GetManifestHeaders(/*ok=*/false, /*scope=*/"/");
(*body) = kScopeManifestContents;
} else if (path == "/manifest-304-bar-override" ||
path == "/bar/manifest-304-bar-override") {
(*headers) = GetManifestHeaders(/*ok=*/false, /*scope=*/"/bar/");
(*body) = kScopeManifestContents;
} else if (path == "/manifest-304-other-override" ||
path == "/bar/manifest-304-other-override") {
(*headers) = GetManifestHeaders(/*ok=*/false, /*scope=*/"/other/");
(*body) = kScopeManifestContents;
} else {
(*headers) =
std::string(not_found_headers, base::size(not_found_headers));
(*body) = "";
}
}
};
inline bool operator==(const AppCacheNamespace& lhs,
const AppCacheNamespace& rhs) {
return lhs.type == rhs.type && lhs.namespace_url == rhs.namespace_url &&
lhs.target_url == rhs.target_url;
}
class MockFrontend : public blink::mojom::AppCacheFrontend {
public:
MockFrontend()
: ignore_progress_events_(false),
verify_progress_events_(false),
last_progress_total_(-1),
last_progress_complete_(-1),
start_update_trigger_(
blink::mojom::AppCacheEventID::APPCACHE_CHECKING_EVENT),
update_(nullptr) {}
void CacheSelected(blink::mojom::AppCacheInfoPtr info) override {}
void EventRaised(blink::mojom::AppCacheEventID event_id) override {
raised_events_.push_back(event_id);
// Trigger additional updates if requested.
if (event_id == start_update_trigger_ && update_) {
for (AppCacheHost* host : update_hosts_) {
update_->StartUpdate(
host, (host ? host->pending_master_entry_url() : GURL()));
}
update_hosts_.clear(); // only trigger once
}
}
void ErrorEventRaised(
blink::mojom::AppCacheErrorDetailsPtr details) override {
error_message_ = details->message;
EventRaised(blink::mojom::AppCacheEventID::APPCACHE_ERROR_EVENT);
}
void ProgressEventRaised(const GURL& url,
int32_t num_total,
int32_t num_complete) override {
if (!ignore_progress_events_)
EventRaised(blink::mojom::AppCacheEventID::APPCACHE_PROGRESS_EVENT);
if (verify_progress_events_) {
EXPECT_GE(num_total, num_complete);
EXPECT_GE(num_complete, 0);
if (last_progress_total_ == -1) {
// Should start at zero.
EXPECT_EQ(0, num_complete);
} else {
// Total should be stable and complete should bump up by one at a time.
EXPECT_EQ(last_progress_total_, num_total);
EXPECT_EQ(last_progress_complete_ + 1, num_complete);
}
// Url should be valid for all except the 'final' event.
if (num_total == num_complete)
EXPECT_TRUE(url.is_empty());
else
EXPECT_TRUE(url.is_valid());
last_progress_total_ = num_total;
last_progress_complete_ = num_complete;
}
}
void LogMessage(blink::mojom::ConsoleMessageLevel log_level,
const std::string& message) override {}
void SetSubresourceFactory(
mojo::PendingRemote<network::mojom::URLLoaderFactory> url_loader_factory)
override {}
void AddExpectedEvent(blink::mojom::AppCacheEventID event_id) {
DCHECK(!ignore_progress_events_ ||
event_id != blink::mojom::AppCacheEventID::APPCACHE_PROGRESS_EVENT);
expected_events_.push_back(event_id);
}
void SetIgnoreProgressEvents(bool ignore) {
// Some tests involve joining new hosts to an already running update job
// or intentionally failing. The timing and sequencing of the progress
// events generated by an update job are dependent on the behavior of
// an external HTTP server. For jobs that do not run fully till completion,
// due to either joining late or early exit, we skip monitoring the
// progress events to avoid flakiness.
ignore_progress_events_ = ignore;
}
void SetVerifyProgressEvents(bool verify) {
verify_progress_events_ = verify;
}
void TriggerAdditionalUpdates(blink::mojom::AppCacheEventID trigger_event,
AppCacheUpdateJob* update) {
start_update_trigger_ = trigger_event;
update_ = update;
}
void AdditionalUpdateHost(AppCacheHost* host) {
update_hosts_.push_back(host);
}
using RaisedEvents = std::vector<blink::mojom::AppCacheEventID>;
RaisedEvents raised_events_;
std::string error_message_;
// Set the expected events if verification needs to happen asynchronously.
RaisedEvents expected_events_;
std::string expected_error_message_;
bool ignore_progress_events_;
bool verify_progress_events_;
int last_progress_total_;
int last_progress_complete_;
// Add ability for frontend to add master entries to an inprogress update.
blink::mojom::AppCacheEventID start_update_trigger_;
AppCacheUpdateJob* update_;
std::vector<AppCacheHost*> update_hosts_;
};
// Helper class to simulate a URL that returns retry or success.
class RetryRequestTestJob {
public:
enum RetryHeader {
NO_RETRY_AFTER,
NONZERO_RETRY_AFTER,
RETRY_AFTER_0,
};
static constexpr char kRetryUrl[] = "http://retry/";
// Call this at the start of each retry test.
static void Initialize(int num_retry_responses,
RetryHeader header,
int expected_requests) {
num_requests_ = 0;
num_retries_ = num_retry_responses;
retry_after_ = header;
expected_requests_ = expected_requests;
}
// Verifies results at end of test and resets counters.
static void Verify() {
EXPECT_EQ(expected_requests_, num_requests_);
num_requests_ = 0;
expected_requests_ = 0;
}
static void GetResponseForURL(const GURL& url,
std::string* headers,
std::string* data) {
++num_requests_;
if (num_retries_ > 0 && IsRetryUrl(url)) {
--num_retries_;
*headers = RetryRequestTestJob::retry_headers();
} else {
*headers = RetryRequestTestJob::manifest_headers();
}
if (data)
*data = RetryRequestTestJob::data();
}
static bool IsRetryUrl(const GURL& url) { return url.spec() == kRetryUrl; }
private:
static std::string retry_headers() {
const char no_retry_after[] =
"HTTP/1.1 503 BOO HOO\n"
"\n";
const char nonzero[] =
"HTTP/1.1 503 BOO HOO\n"
"Retry-After: 60\n"
"\n";
const char retry_after_0[] =
"HTTP/1.1 503 BOO HOO\n"
"Retry-After: 0\n"
"\n";
switch (retry_after_) {
case NO_RETRY_AFTER:
return std::string(no_retry_after, base::size(no_retry_after));
case NONZERO_RETRY_AFTER:
return std::string(nonzero, base::size(nonzero));
case RETRY_AFTER_0:
default:
return std::string(retry_after_0, base::size(retry_after_0));
}
}
static std::string manifest_headers() {
const char headers[] =
"HTTP/1.1 200 OK\n"
"Content-type: text/cache-manifest\n"
"\n";
return std::string(headers, base::size(headers));
}
static std::string data() {
return base::StrCat({"CACHE MANIFEST\r", kRetryUrl, "\r"});
}
static int num_requests_;
static int num_retries_;
static RetryHeader retry_after_;
static int expected_requests_;
};
// static
constexpr char RetryRequestTestJob::kRetryUrl[];
int RetryRequestTestJob::num_requests_ = 0;
int RetryRequestTestJob::num_retries_;
RetryRequestTestJob::RetryHeader RetryRequestTestJob::retry_after_;
int RetryRequestTestJob::expected_requests_ = 0;
// Helper class to check for certain HTTP headers.
class HttpHeadersRequestTestJob {
public:
HttpHeadersRequestTestJob() {
saw_if_modified_since_ = false;
saw_if_none_match_ = false;
headers_allowed_ = true;
saw_headers_ = false;
already_checked_ = false;
}
HttpHeadersRequestTestJob(const std::string& expect_if_modified_since,
const std::string& expect_if_none_match,
bool headers_allowed = true)
: expect_if_modified_since_(expect_if_modified_since),
saw_if_modified_since_(false),
expect_if_none_match_(expect_if_none_match),
saw_if_none_match_(false),
headers_allowed_(headers_allowed),
saw_headers_(false),
already_checked_(false),
verify_called_(false) {}
~HttpHeadersRequestTestJob() { DCHECK(verify_called_); }
// Verifies results at end of test.
void Verify(const GURL& url) {
verify_called_ = true;
if (!expect_if_modified_since_.empty())
EXPECT_TRUE(saw_if_modified_since_) << "URL " << url.spec() << " failed";
if (!expect_if_none_match_.empty())
EXPECT_TRUE(saw_if_none_match_) << "URL " << url.spec() << " failed";
if (!headers_allowed_)
EXPECT_FALSE(saw_headers_) << "URL " << url.spec() << " failed";
}
void ValidateExtraHeaders(const net::HttpRequestHeaders& extra_headers) {
if (already_checked_)
return;
already_checked_ = true; // only check once for a test
std::string header_value;
if (extra_headers.GetHeader(net::HttpRequestHeaders::kIfModifiedSince,
&header_value)) {
saw_headers_ = true;
saw_if_modified_since_ = header_value == expect_if_modified_since_;
}
if (extra_headers.GetHeader(net::HttpRequestHeaders::kIfNoneMatch,
&header_value)) {
saw_headers_ = true;
saw_if_none_match_ = header_value == expect_if_none_match_;
}
}
private:
std::string expect_if_modified_since_;
bool saw_if_modified_since_;
std::string expect_if_none_match_;
bool saw_if_none_match_;
bool headers_allowed_;
bool saw_headers_;
bool already_checked_;
bool verify_called_;
};
class AppCacheUpdateJobTest : public testing::Test,
public AppCacheGroup::UpdateObserver {
public:
AppCacheUpdateJobTest()
: do_checks_after_update_finished_(false),
expect_group_obsolete_(false),
expect_group_has_cache_(false),
expect_group_is_being_deleted_(false),
expect_evictable_error_(false),
expect_eviction_(false),
expect_old_cache_(nullptr),
expect_newest_cache_(nullptr),
expect_non_null_update_time_(false),
tested_manifest_(NONE),
tested_manifest_path_override_(nullptr),
interceptor_(
base::BindRepeating(&AppCacheUpdateJobTest::InterceptRequest,
base::Unretained(this))),
process_id_(123) {
appcache_require_origin_trial_feature_.InitAndDisableFeature(
blink::features::kAppCacheRequireOriginTrial);
appcache_disable_corruption_feature_.InitAndDisableFeature(
kAppCacheCorruptionRecoveryFeature);
}
// TODO(ananta/michaeln): Remove dependencies on URLRequest based
// classes by refactoring the response headers/data into a common class.
bool InterceptRequest(URLLoaderInterceptor::RequestParams* params) {
const auto& url_request = params->url_request;
if (url_request.url.host() == "failme" ||
url_request.url.host() == "testme") {
params->client->OnComplete(network::URLLoaderCompletionStatus(-100));
return true;
}
auto it = http_headers_request_test_jobs_.find(url_request.url);
if (it != http_headers_request_test_jobs_.end())
it->second->ValidateExtraHeaders(url_request.headers);
// Copy the URL so we can optionally override it under certain conditions.
GURL requested_url = url_request.url;
// If "files/maybemodified" is requested without a conditional header,
// treat the request as if it's a request for "files/modified". The
// result will be a 200 OK response (rather than maybemodified's normal
// 304 response).
if (requested_url.path() == "/files/maybemodified" &&
!url_request.headers.HasHeader("If-Modified-Since")) {
requested_url = GURL("http://mockhost/files/modified");
}
std::string headers;
std::string body;
if (RetryRequestTestJob::IsRetryUrl(requested_url)) {
RetryRequestTestJob::GetResponseForURL(requested_url, &headers, &body);
} else {
MockHttpServer::GetMockResponse(requested_url.path(), &headers, &body);
}
net::HttpResponseInfo info;
info.headers = base::MakeRefCounted<net::HttpResponseHeaders>(
net::HttpUtil::AssembleRawHeaders(headers));
auto response = network::mojom::URLResponseHead::New();
response->headers = info.headers;
response->headers->GetMimeType(&response->mime_type);
// Provide valid request and response times to
// UpdateUrlLoaderRequest. It's still up to that class's
// |OnReceiveResponse| to capture these values in the net::HttpResponseInfo.
response->request_time = base::Time::Now();
response->response_time = base::Time::Now();
params->client->OnReceiveResponse(std::move(response));
mojo::ScopedDataPipeProducerHandle producer_handle;
mojo::ScopedDataPipeConsumerHandle consumer_handle;
mojo::CreateDataPipe(nullptr, producer_handle, consumer_handle);
uint32_t bytes_written = body.size();
producer_handle->WriteData(body.data(), &bytes_written,
MOJO_WRITE_DATA_FLAG_ALL_OR_NONE);
params->client->OnStartLoadingResponseBody(std::move(consumer_handle));
return true;
}
void SetUp() override {
browser_context_ = std::make_unique<content::TestBrowserContext>();
weak_partition_factory_ =
std::make_unique<base::WeakPtrFactory<StoragePartitionImpl>>(
static_cast<StoragePartitionImpl*>(
BrowserContext::GetDefaultStoragePartition(
browser_context_.get())));
ChildProcessSecurityPolicyImpl::GetInstance()->AddForTesting(
process_id_, browser_context_.get());
blink::TrialTokenValidator::SetOriginTrialPolicyGetter(base::BindRepeating(
[]() -> blink::OriginTrialPolicy* { return &g_origin_trial_policy; }));
}
void TearDown() override {
blink::TrialTokenValidator::ResetOriginTrialPolicyGetter();
weak_partition_factory_.reset();
browser_context_.reset();
ChildProcessSecurityPolicyImpl::GetInstance()->Remove(process_id_);
}
// Use a separate IO thread to run a test. Thread will be destroyed
// when it goes out of scope.
template <class Method>
void RunTestOnUIThread(Method method) {
base::RunLoop run_loop;
test_completed_cb_ = run_loop.QuitClosure();
GetUIThreadTaskRunner({})->PostTask(
FROM_HERE, base::BindOnce(method, base::Unretained(this)));
run_loop.Run();
}
void StartCacheAttemptTest() {
MakeService();
group_ = base::MakeRefCounted<AppCacheGroup>(
service_->storage(), GURL("http://failme"),
service_->storage()->NewGroupId());
AppCacheUpdateJob* update =
new AppCacheUpdateJob(service_.get(), group_.get());
group_->update_job_ = update;
MockFrontend mock_frontend;
const int kMockProcessId1 = 1;
AppCacheHost host(
/*host_id=*/base::UnguessableToken::Create(), kMockProcessId1,
/*render_frame_id=*/1,
ChildProcessSecurityPolicyImpl::GetInstance()->CreateHandle(
kMockProcessId1),
mojo::NullRemote(), service_.get());
host.set_frontend_for_testing(&mock_frontend);
update->StartUpdate(&host, GURL());
// Verify state.
EXPECT_EQ(AppCacheUpdateJob::CACHE_ATTEMPT, update->update_type_);
EXPECT_EQ(AppCacheUpdateJobState::FETCH_MANIFEST, update->internal_state_);
EXPECT_EQ(AppCacheGroup::CHECKING, group_->update_status());
EXPECT_TRUE(update->doing_full_update_check_);
// Verify notifications.
MockFrontend::RaisedEvents& events = mock_frontend.raised_events_;
ASSERT_EQ(1u, events.size());
EXPECT_EQ(blink::mojom::AppCacheEventID::APPCACHE_CHECKING_EVENT,
events[0]);
// Abort as we're not testing actual URL fetches in this test.
delete update;
UpdateFinished();
}
void StartUpgradeAttemptTest() {
{
MakeService();
group_ = base::MakeRefCounted<AppCacheGroup>(
service_->storage(), GURL("http://failme"),
service_->storage()->NewGroupId());
// Give the group some existing caches.
AppCache* cache1 = MakeCacheForGroup(1, 111);
AppCache* cache2 = MakeCacheForGroup(2, 222);
// Associate some hosts with caches in the group.
MockFrontend mock_frontend1;
MockFrontend mock_frontend2;
MockFrontend mock_frontend3;
MockFrontend mock_frontend4;
const int kMockProcessId1 = 1;
const int kMockProcessId2 = 2;
const int kMockProcessId3 = 3;
const int kMockProcessId4 = 4;
AppCacheHost host1(
/*host_id=*/base::UnguessableToken::Create(), kMockProcessId1,
/*render_frame_id=*/1,
ChildProcessSecurityPolicyImpl::GetInstance()->CreateHandle(
kMockProcessId1),
mojo::NullRemote(), service_.get());
host1.set_frontend_for_testing(&mock_frontend1);
host1.AssociateCompleteCache(cache1);
AppCacheHost host2(
/*host_id=*/base::UnguessableToken::Create(), kMockProcessId2,
/*render_frame_id=*/2,
ChildProcessSecurityPolicyImpl::GetInstance()->CreateHandle(
kMockProcessId2),
mojo::NullRemote(), service_.get());
host2.set_frontend_for_testing(&mock_frontend2);
host2.AssociateCompleteCache(cache2);
AppCacheHost host3(
/*host_id=*/base::UnguessableToken::Create(), kMockProcessId3,
/*render_frame_id=*/3,
ChildProcessSecurityPolicyImpl::GetInstance()->CreateHandle(
kMockProcessId3),
mojo::NullRemote(), service_.get());
host3.set_frontend_for_testing(&mock_frontend3);
host3.AssociateCompleteCache(cache1);
AppCacheHost host4(
/*host_id=*/base::UnguessableToken::Create(), kMockProcessId4,
/*render_frame_id=*/4,
ChildProcessSecurityPolicyImpl::GetInstance()->CreateHandle(
kMockProcessId4),
mojo::NullRemote(), service_.get());
host4.set_frontend_for_testing(&mock_frontend4);
AppCacheUpdateJob* update =
new AppCacheUpdateJob(service_.get(), group_.get());
group_->update_job_ = update;
update->StartUpdate(&host4, GURL());
// Verify state after starting an update.
EXPECT_EQ(AppCacheUpdateJob::UPGRADE_ATTEMPT, update->update_type_);
EXPECT_EQ(AppCacheUpdateJobState::FETCH_MANIFEST,
update->internal_state_);
EXPECT_EQ(AppCacheGroup::CHECKING, group_->update_status());
EXPECT_FALSE(update->doing_full_update_check_);
// Verify notifications.
MockFrontend::RaisedEvents events = mock_frontend1.raised_events_;
ASSERT_EQ(1u, events.size());
EXPECT_EQ(blink::mojom::AppCacheEventID::APPCACHE_CHECKING_EVENT,
events[0]);
events = mock_frontend2.raised_events_;
ASSERT_EQ(1u, events.size());
EXPECT_EQ(blink::mojom::AppCacheEventID::APPCACHE_CHECKING_EVENT,
events[0]);
events = mock_frontend3.raised_events_;
ASSERT_EQ(1u, events.size());
EXPECT_EQ(blink::mojom::AppCacheEventID::APPCACHE_CHECKING_EVENT,
events[0]);
events = mock_frontend4.raised_events_;
EXPECT_TRUE(events.empty());
// Abort as we're not testing actual URL fetches in this test.
delete update;
}
UpdateFinished();
}
void CacheAttemptFetchManifestFailTest() {
MakeService();
group_ = base::MakeRefCounted<AppCacheGroup>(
service_->storage(), GURL("http://failme"),
service_->storage()->NewGroupId());
AppCacheUpdateJob* update =
new AppCacheUpdateJob(service_.get(), group_.get());
group_->update_job_ = update;
MockFrontend* frontend = MakeMockFrontend();
AppCacheHost* host = MakeHost(frontend);
update->StartUpdate(host, GURL());
// Set up checks for when update job finishes.
do_checks_after_update_finished_ = true;
expect_group_obsolete_ = false;
expect_group_has_cache_ = false;
frontend->AddExpectedEvent(
blink::mojom::AppCacheEventID::APPCACHE_CHECKING_EVENT);
WaitForUpdateToFinish();
}
void UpgradeFetchManifestFailTest() {
MakeService();
group_ = base::MakeRefCounted<AppCacheGroup>(
service_->storage(), MockHttpServer::GetMockUrl("files/servererror"),
service_->storage()->NewGroupId());
AppCacheUpdateJob* update =
new AppCacheUpdateJob(service_.get(), group_.get());
group_->update_job_ = update;
AppCache* cache = MakeCacheForGroup(1, 111);
MockFrontend* frontend1 = MakeMockFrontend();
MockFrontend* frontend2 = MakeMockFrontend();
AppCacheHost* host1 = MakeHost(frontend1);
AppCacheHost* host2 = MakeHost(frontend2);
host1->AssociateCompleteCache(cache);
host2->AssociateCompleteCache(cache);
group_->set_last_full_update_check_time(base::Time::Now() -
kFullUpdateInterval - kOneHour);
update->StartUpdate(nullptr, GURL());
EXPECT_TRUE(update->doing_full_update_check_);
// Set up checks for when update job finishes.
do_checks_after_update_finished_ = true;
expect_group_obsolete_ = false;
expect_group_has_cache_ = true;
expect_evictable_error_ = true;
expect_newest_cache_ = cache; // newest cache unaffected by update
expect_full_update_time_equal_to_ = group_->last_full_update_check_time();
frontend1->AddExpectedEvent(
blink::mojom::AppCacheEventID::APPCACHE_CHECKING_EVENT);
frontend1->AddExpectedEvent(
blink::mojom::AppCacheEventID::APPCACHE_ERROR_EVENT);
frontend2->AddExpectedEvent(
blink::mojom::AppCacheEventID::APPCACHE_CHECKING_EVENT);
frontend2->AddExpectedEvent(
blink::mojom::AppCacheEventID::APPCACHE_ERROR_EVENT);
WaitForUpdateToFinish();
}
void ManifestRedirectTest() {
MakeService();
group_ = base::MakeRefCounted<AppCacheGroup>(
service_->storage(), GURL("http://testme"),
service_->storage()->NewGroupId());
AppCacheUpdateJob* update =
new AppCacheUpdateJob(service_.get(), group_.get());
group_->update_job_ = update;
MockFrontend* frontend = MakeMockFrontend();
AppCacheHost* host = MakeHost(frontend);
update->StartUpdate(host, GURL());
// Set up checks for when update job finishes.
do_checks_after_update_finished_ = true;
expect_group_obsolete_ = false;
expect_group_has_cache_ = false; // redirect is like a failed request
frontend->AddExpectedEvent(
blink::mojom::AppCacheEventID::APPCACHE_CHECKING_EVENT);
WaitForUpdateToFinish();
}
void ManifestMissingMimeTypeTest() {
MakeService();
group_ = base::MakeRefCounted<AppCacheGroup>(
service_->storage(),
MockHttpServer::GetMockUrl("files/missing-mime-manifest"),
service_->storage()->NewGroupId());
AppCacheUpdateJob* update =
new AppCacheUpdateJob(service_.get(), group_.get());
group_->update_job_ = update;
AppCache* cache = MakeCacheForGroup(service_->storage()->NewCacheId(), 33);
MockFrontend* frontend = MakeMockFrontend();
AppCacheHost* host = MakeHost(frontend);
host->AssociateCompleteCache(cache);
frontend->SetVerifyProgressEvents(true);
update->StartUpdate(nullptr, GURL());
// Set up checks for when update job finishes.
do_checks_after_update_finished_ = true;
expect_group_obsolete_ = false;
expect_group_has_cache_ = true;
expect_old_cache_ = cache;
tested_manifest_ = EMPTY_MANIFEST;
tested_manifest_path_override_ = "files/missing-mime-manifest";
frontend->AddExpectedEvent(
blink::mojom::AppCacheEventID::APPCACHE_CHECKING_EVENT);
frontend->AddExpectedEvent(
blink::mojom::AppCacheEventID::APPCACHE_DOWNLOADING_EVENT);
frontend->AddExpectedEvent(
blink::mojom::AppCacheEventID::APPCACHE_PROGRESS_EVENT); // final
frontend->AddExpectedEvent(
blink::mojom::AppCacheEventID::APPCACHE_UPDATE_READY_EVENT);
WaitForUpdateToFinish();
}
void ManifestNotFoundTest() {
MakeService();
group_ = base::MakeRefCounted<AppCacheGroup>(
service_->storage(), MockHttpServer::GetMockUrl("files/nosuchfile"),
service_->storage()->NewGroupId());
AppCacheUpdateJob* update =
new AppCacheUpdateJob(service_.get(), group_.get());
group_->update_job_ = update;
AppCache* cache = MakeCacheForGroup(1, 111);
MockFrontend* frontend1 = MakeMockFrontend();
MockFrontend* frontend2 = MakeMockFrontend();
AppCacheHost* host1 = MakeHost(frontend1);
AppCacheHost* host2 = MakeHost(frontend2);
host1->AssociateCompleteCache(cache);
host2->AssociateCompleteCache(cache);
update->StartUpdate(nullptr, GURL());
// Set up checks for when update job finishes.
do_checks_after_update_finished_ = true;
expect_group_obsolete_ = true;
expect_group_has_cache_ = true;
expect_newest_cache_ = cache; // newest cache unaffected by update
frontend1->AddExpectedEvent(
blink::mojom::AppCacheEventID::APPCACHE_CHECKING_EVENT);
frontend1->AddExpectedEvent(
blink::mojom::AppCacheEventID::APPCACHE_OBSOLETE_EVENT);
frontend2->AddExpectedEvent(
blink::mojom::AppCacheEventID::APPCACHE_CHECKING_EVENT);
frontend2->AddExpectedEvent(
blink::mojom::AppCacheEventID::APPCACHE_OBSOLETE_EVENT);
WaitForUpdateToFinish();
}
void ManifestGoneFetchTest() {
MakeService();
group_ = base::MakeRefCounted<AppCacheGroup>(
service_->storage(), MockHttpServer::GetMockUrl("files/gone"),
service_->storage()->NewGroupId());
AppCacheUpdateJob* update =
new AppCacheUpdateJob(service_.get(), group_.get());
group_->update_job_ = update;
MockFrontend* frontend = MakeMockFrontend();
AppCacheHost* host = MakeHost(frontend);
update->StartUpdate(host, GURL());
// Set up checks for when update job finishes.
do_checks_after_update_finished_ = true;
expect_group_obsolete_ = false;
expect_group_has_cache_ = false;
frontend->AddExpectedEvent(
blink::mojom::AppCacheEventID::APPCACHE_CHECKING_EVENT);
WaitForUpdateToFinish();
}
void ManifestGoneUpgradeTest() {
MakeService();
group_ = base::MakeRefCounted<AppCacheGroup>(
service_->storage(), MockHttpServer::GetMockUrl("files/gone"),
service_->storage()->NewGroupId());
AppCacheUpdateJob* update =
new AppCacheUpdateJob(service_.get(), group_.get());
group_->update_job_ = update;
MockFrontend* frontend = MakeMockFrontend();
AppCacheHost* host = MakeHost(frontend);
AppCache* cache = MakeCacheForGroup(1, 111);
host->AssociateCompleteCache(cache);
update->StartUpdate(nullptr, GURL());
// Set up checks for when update job finishes.
do_checks_after_update_finished_ = true;
expect_group_obsolete_ = true;
expect_group_has_cache_ = true;
expect_newest_cache_ = cache; // newest cache unaffected by update
frontend->AddExpectedEvent(
blink::mojom::AppCacheEventID::APPCACHE_CHECKING_EVENT);
frontend->AddExpectedEvent(
blink::mojom::AppCacheEventID::APPCACHE_OBSOLETE_EVENT);
WaitForUpdateToFinish();
}
void CacheAttemptNotModifiedTest() {
MakeService();
group_ = base::MakeRefCounted<AppCacheGroup>(
service_->storage(), MockHttpServer::GetMockUrl("files/notmodified"),
service_->storage()->NewGroupId());
AppCacheUpdateJob* update =
new AppCacheUpdateJob(service_.get(), group_.get());
group_->update_job_ = update;
MockFrontend* frontend = MakeMockFrontend();
AppCacheHost* host = MakeHost(frontend);
update->StartUpdate(host, GURL());
// Set up checks for when update job finishes.
do_checks_after_update_finished_ = true;
expect_group_obsolete_ = false;
expect_group_has_cache_ = false; // treated like cache failure
frontend->AddExpectedEvent(
blink::mojom::AppCacheEventID::APPCACHE_CHECKING_EVENT);
WaitForUpdateToFinish();
}
void UpgradeNotModifiedTest() {
MakeService();
group_ = base::MakeRefCounted<AppCacheGroup>(
service_->storage(), MockHttpServer::GetMockUrl("files/notmodified"),
service_->storage()->NewGroupId());
AppCacheUpdateJob* update =
new AppCacheUpdateJob(service_.get(), group_.get());
group_->update_job_ = update;
AppCache* cache = MakeCacheForGroup(1, 111);
MockFrontend* frontend1 = MakeMockFrontend();
MockFrontend* frontend2 = MakeMockFrontend();
AppCacheHost* host1 = MakeHost(frontend1);
AppCacheHost* host2 = MakeHost(frontend2);
host1->AssociateCompleteCache(cache);
host2->AssociateCompleteCache(cache);
group_->set_last_full_update_check_time(base::Time::Now() -
kFullUpdateInterval - kOneHour);
group_->set_first_evictable_error_time(base::Time::Now());
update->StartUpdate(nullptr, GURL());
EXPECT_TRUE(update->doing_full_update_check_);
// Set up checks for when update job finishes.
do_checks_after_update_finished_ = true;
expect_group_obsolete_ = false;
expect_group_has_cache_ = true;
expect_newest_cache_ = cache; // newest cache unaffected by update
expect_evictable_error_ = false; // should be reset
expect_full_update_time_newer_than_ = group_->last_full_update_check_time();
frontend1->AddExpectedEvent(
blink::mojom::AppCacheEventID::APPCACHE_CHECKING_EVENT);
frontend1->AddExpectedEvent(
blink::mojom::AppCacheEventID::APPCACHE_NO_UPDATE_EVENT);
frontend2->AddExpectedEvent(
blink::mojom::AppCacheEventID::APPCACHE_CHECKING_EVENT);
frontend2->AddExpectedEvent(
blink::mojom::AppCacheEventID::APPCACHE_NO_UPDATE_EVENT);
WaitForUpdateToFinish();
}
void UpgradeNotModifiedVersion1Test() {
MakeService();
group_ = base::MakeRefCounted<AppCacheGroup>(
service_->storage(), MockHttpServer::GetMockUrl("files/notmodified"),
service_->storage()->NewGroupId());
AppCacheUpdateJob* update =
new AppCacheUpdateJob(service_.get(), group_.get());
group_->update_job_ = update;
// Create response writer to get a response id.
response_writer_ =
service_->storage()->CreateResponseWriter(group_->manifest_url());
AppCache* cache = MakeCacheForGroup(service_->storage()->NewCacheId(),
response_writer_->response_id());
cache->set_manifest_parser_version(1);
EXPECT_EQ(cache->token_expires(), base::Time());
MockFrontend* frontend = MakeMockFrontend();
AppCacheHost* host = MakeHost(frontend);
host->AssociateCompleteCache(cache);
// Set up checks for when update job finishes.
do_checks_after_update_finished_ = true;
expect_group_obsolete_ = false;
expect_group_has_cache_ = true;
tested_manifest_path_override_ = "files/notmodified";
tested_manifest_ = MANIFEST1;
expect_token_expires_ = kTestOriginTrialTokenExpiry;
frontend->AddExpectedEvent(
blink::mojom::AppCacheEventID::APPCACHE_CHECKING_EVENT);
frontend->AddExpectedEvent(
blink::mojom::AppCacheEventID::APPCACHE_DOWNLOADING_EVENT);
frontend->AddExpectedEvent(
blink::mojom::AppCacheEventID::APPCACHE_PROGRESS_EVENT);
frontend->AddExpectedEvent(
blink::mojom::AppCacheEventID::APPCACHE_PROGRESS_EVENT);
frontend->AddExpectedEvent(
blink::mojom::AppCacheEventID::APPCACHE_PROGRESS_EVENT);
frontend->AddExpectedEvent(
blink::mojom::AppCacheEventID::APPCACHE_UPDATE_READY_EVENT);
// Seed the response_info working set with canned data so that an existing
// manifest is reused by the update job.
const char kData[] =
"HTTP/1.1 200 OK\0"
"Last-Modified: Sat, 29 Oct 1994 19:43:31 GMT\0"
"\0";
const std::string kRawHeaders(kData, base::size(kData));
MakeAppCacheResponseInfo(group_->manifest_url(),
response_writer_->response_id(), kRawHeaders);
// Last data parsed pretends to be version 1, and doesn't know about
// the origin trial (verified above). Even with a 304, this should
// refresh the token expires field.
const std::string seed_data(kManifest1OriginTrialContents);
scoped_refptr<net::StringIOBuffer> io_buffer =
base::MakeRefCounted<net::StringIOBuffer>(seed_data);
response_writer_->WriteData(
io_buffer.get(), seed_data.length(),
base::BindOnce(
&AppCacheUpdateJobTest::StartUpdateAfterSeedingStorageData,
base::Unretained(this)));
// Start update after data write completes asynchronously.
}
void UpgradeManifestDataChangedScopeUnchangedTest() {
MakeService();
// URL path /files/manifest2-with-root-override has a cached scope of "/".
// The path has a scope override of "/", so the fetched scope will be "/"
// and no scope change will occur.
group_ = base::MakeRefCounted<AppCacheGroup>(
service_->storage(),
MockHttpServer::GetMockUrl("files/manifest2-with-root-override"),
service_->storage()->NewGroupId());
AppCacheUpdateJob* update =
new AppCacheUpdateJob(service_.get(), group_.get());
group_->update_job_ = update;
// Create response writer to get a response id.
response_writer_ =
service_->storage()->CreateResponseWriter(group_->manifest_url());
AppCache* cache = MakeCacheForGroup(service_->storage()->NewCacheId(),
response_writer_->response_id());
MockFrontend* frontend1 = MakeMockFrontend();
MockFrontend* frontend2 = MakeMockFrontend();
AppCacheHost* host1 = MakeHost(frontend1);
AppCacheHost* host2 = MakeHost(frontend2);
host1->AssociateCompleteCache(cache);
host2->AssociateCompleteCache(cache);
// Set up checks for when update job finishes.
do_checks_after_update_finished_ = true;
expect_group_obsolete_ = false;
expect_group_has_cache_ = true;
expect_old_cache_ = cache;
tested_manifest_ = MANIFEST2_WITH_ROOT_SCOPE;
frontend1->AddExpectedEvent(
blink::mojom::AppCacheEventID::APPCACHE_CHECKING_EVENT);
frontend1->AddExpectedEvent(
blink::mojom::AppCacheEventID::APPCACHE_DOWNLOADING_EVENT);
frontend1->AddExpectedEvent(
blink::mojom::AppCacheEventID::APPCACHE_PROGRESS_EVENT);
frontend1->AddExpectedEvent(
blink::mojom::AppCacheEventID::APPCACHE_PROGRESS_EVENT);
frontend1->AddExpectedEvent(
blink::mojom::AppCacheEventID::APPCACHE_PROGRESS_EVENT);
frontend1->AddExpectedEvent(
blink::mojom::AppCacheEventID::APPCACHE_PROGRESS_EVENT);
frontend1->AddExpectedEvent(
blink::mojom::AppCacheEventID::APPCACHE_PROGRESS_EVENT);
frontend1->AddExpectedEvent(
blink::mojom::AppCacheEventID::APPCACHE_PROGRESS_EVENT); // final
frontend1->AddExpectedEvent(
blink::mojom::AppCacheEventID::APPCACHE_UPDATE_READY_EVENT);
frontend2->AddExpectedEvent(
blink::mojom::AppCacheEventID::APPCACHE_CHECKING_EVENT);
frontend2->AddExpectedEvent(
blink::mojom::AppCacheEventID::APPCACHE_DOWNLOADING_EVENT);
frontend2->AddExpectedEvent(
blink::mojom::AppCacheEventID::APPCACHE_PROGRESS_EVENT);
frontend2->AddExpectedEvent(
blink::mojom::AppCacheEventID::APPCACHE_PROGRESS_EVENT);
frontend2->AddExpectedEvent(
blink::mojom::AppCacheEventID::APPCACHE_PROGRESS_EVENT);
frontend2->AddExpectedEvent(
blink::mojom::AppCacheEventID::APPCACHE_PROGRESS_EVENT);
frontend2->AddExpectedEvent(
blink::mojom::AppCacheEventID::APPCACHE_PROGRESS_EVENT);
frontend2->AddExpectedEvent(
blink::mojom::AppCacheEventID::APPCACHE_PROGRESS_EVENT); // final
frontend2->AddExpectedEvent(
blink::mojom::AppCacheEventID::APPCACHE_UPDATE_READY_EVENT);
// Seed storage with manifest1 contents. The result should be that the
// manifest2 fetch results in different byte-for-byte data.
const std::string seed_data(kManifest1Contents);
scoped_refptr<net::StringIOBuffer> io_buffer =
base::MakeRefCounted<net::StringIOBuffer>(seed_data);
response_writer_->WriteData(
io_buffer.get(), seed_data.length(),
base::BindOnce(
&AppCacheUpdateJobTest::StartUpdateAfterSeedingStorageData,
base::Unretained(this)));
// Start update after data write completes asynchronously.
}
void UpgradeManifestDataChangedScopeChangedTest() {
MakeService();
// URL path /files/manifest2 has a cached scope of "/". The path has no
// scope override, so the fetched scope will be "/files/" and a scope change
// will occur.
group_ = base::MakeRefCounted<AppCacheGroup>(
service_->storage(), MockHttpServer::GetMockUrl("files/manifest2"),
service_->storage()->NewGroupId());
AppCacheUpdateJob* update =
new AppCacheUpdateJob(service_.get(), group_.get());
group_->update_job_ = update;
// Create response writer to get a response id.
response_writer_ =
service_->storage()->CreateResponseWriter(group_->manifest_url());
AppCache* cache = MakeCacheForGroup(service_->storage()->NewCacheId(),
response_writer_->response_id());
MockFrontend* frontend1 = MakeMockFrontend();
MockFrontend* frontend2 = MakeMockFrontend();
AppCacheHost* host1 = MakeHost(frontend1);
AppCacheHost* host2 = MakeHost(frontend2);
host1->AssociateCompleteCache(cache);
host2->AssociateCompleteCache(cache);
// Set up checks for when update job finishes.
do_checks_after_update_finished_ = true;
expect_group_obsolete_ = false;
expect_group_has_cache_ = true;
expect_old_cache_ = cache;
tested_manifest_ = MANIFEST2_WITH_FILES_SCOPE;
frontend1->AddExpectedEvent(
blink::mojom::AppCacheEventID::APPCACHE_CHECKING_EVENT);
frontend1->AddExpectedEvent(
blink::mojom::AppCacheEventID::APPCACHE_DOWNLOADING_EVENT);
frontend1->AddExpectedEvent(
blink::mojom::AppCacheEventID::APPCACHE_PROGRESS_EVENT);
frontend1->AddExpectedEvent(
blink::mojom::AppCacheEventID::APPCACHE_PROGRESS_EVENT);
frontend1->AddExpectedEvent(
blink::mojom::AppCacheEventID::APPCACHE_PROGRESS_EVENT);
frontend1->AddExpectedEvent(
blink::mojom::AppCacheEventID::APPCACHE_PROGRESS_EVENT); // final
frontend1->AddExpectedEvent(
blink::mojom::AppCacheEventID::APPCACHE_UPDATE_READY_EVENT);
frontend2->AddExpectedEvent(
blink::mojom::AppCacheEventID::APPCACHE_CHECKING_EVENT);
frontend2->AddExpectedEvent(
blink::mojom::AppCacheEventID::APPCACHE_DOWNLOADING_EVENT);
frontend2->AddExpectedEvent(
blink::mojom::AppCacheEventID::APPCACHE_PROGRESS_EVENT);
frontend2->AddExpectedEvent(
blink::mojom::AppCacheEventID::APPCACHE_PROGRESS_EVENT);
frontend2->AddExpectedEvent(
blink::mojom::AppCacheEventID::APPCACHE_PROGRESS_EVENT);
frontend2->AddExpectedEvent(
blink::mojom::AppCacheEventID::APPCACHE_PROGRESS_EVENT); // final
frontend2->AddExpectedEvent(
blink::mojom::AppCacheEventID::APPCACHE_UPDATE_READY_EVENT);
// Seed storage with manifest1 contents. The result should be that the
// manifest2 fetch results in different byte-for-byte data.
const std::string seed_data(kManifest1Contents);
scoped_refptr<net::StringIOBuffer> io_buffer =
base::MakeRefCounted<net::StringIOBuffer>(seed_data);
response_writer_->WriteData(
io_buffer.get(), seed_data.length(),
base::BindOnce(
&AppCacheUpdateJobTest::StartUpdateAfterSeedingStorageData,
base::Unretained(this)));
// Start update after data write completes asynchronously.
}
void UpgradeManifestDataUnchangedScopeUnchangedTest() {
MakeService();
// URL path /files/manifest2-with-root-override has a cached scope of "/".
// The path has a scope override of "/", so the fetched scope will be "/"
// and no scope change will occur.
group_ = base::MakeRefCounted<AppCacheGroup>(
service_->storage(),
MockHttpServer::GetMockUrl("files/manifest2-with-root-override"),
service_->storage()->NewGroupId());
AppCacheUpdateJob* update =
new AppCacheUpdateJob(service_.get(), group_.get());
group_->update_job_ = update;
// Create response writer to get a response id.
response_writer_ =
service_->storage()->CreateResponseWriter(group_->manifest_url());
AppCache* cache = MakeCacheForGroup(service_->storage()->NewCacheId(),
response_writer_->response_id());
MockFrontend* frontend1 = MakeMockFrontend();
MockFrontend* frontend2 = MakeMockFrontend();
AppCacheHost* host1 = MakeHost(frontend1);
AppCacheHost* host2 = MakeHost(frontend2);
host1->AssociateCompleteCache(cache);
host2->AssociateCompleteCache(cache);
// Set up checks for when update job finishes.
do_checks_after_update_finished_ = true;
expect_group_obsolete_ = false;
expect_group_has_cache_ = true;
expect_newest_cache_ = cache; // newest cache unaffected by update
frontend1->AddExpectedEvent(
blink::mojom::AppCacheEventID::APPCACHE_CHECKING_EVENT);
frontend1->AddExpectedEvent(
blink::mojom::AppCacheEventID::APPCACHE_NO_UPDATE_EVENT);
frontend2->AddExpectedEvent(
blink::mojom::AppCacheEventID::APPCACHE_CHECKING_EVENT);
frontend2->AddExpectedEvent(
blink::mojom::AppCacheEventID::APPCACHE_NO_UPDATE_EVENT);
// Seed storage with expected manifest data.
const std::string seed_data(kManifest2Contents);
scoped_refptr<net::StringIOBuffer> io_buffer =
base::MakeRefCounted<net::StringIOBuffer>(seed_data);
response_writer_->WriteData(
io_buffer.get(), seed_data.length(),
base::BindOnce(
&AppCacheUpdateJobTest::StartUpdateAfterSeedingStorageData,
base::Unretained(this)));
// Start update after data write completes asynchronously.
}
void UpgradeManifestDataUnchangedScopeChangedTest() {
MakeService();
// URL path /files/manifest2 has a cached scope of "/". The path has no
// scope override, so the fetched scope will be "/files/" and a scope change
// will occur.
group_ = base::MakeRefCounted<AppCacheGroup>(
service_->storage(), MockHttpServer::GetMockUrl("files/manifest2"),
service_->storage()->NewGroupId());
AppCacheUpdateJob* update =
new AppCacheUpdateJob(service_.get(), group_.get());
group_->update_job_ = update;
// Create response writer to get a response id.
response_writer_ =
service_->storage()->CreateResponseWriter(group_->manifest_url());
AppCache* cache = MakeCacheForGroup(service_->storage()->NewCacheId(),
response_writer_->response_id());
MockFrontend* frontend1 = MakeMockFrontend();
MockFrontend* frontend2 = MakeMockFrontend();
AppCacheHost* host1 = MakeHost(frontend1);
AppCacheHost* host2 = MakeHost(frontend2);
host1->AssociateCompleteCache(cache);
host2->AssociateCompleteCache(cache);
// Set up checks for when update job finishes.
do_checks_after_update_finished_ = true;
expect_group_obsolete_ = false;
expect_group_has_cache_ = true;
expect_old_cache_ = cache;
tested_manifest_ = MANIFEST2_WITH_FILES_SCOPE;
frontend1->AddExpectedEvent(
blink::mojom::AppCacheEventID::APPCACHE_CHECKING_EVENT);
frontend1->AddExpectedEvent(
blink::mojom::AppCacheEventID::APPCACHE_DOWNLOADING_EVENT);
frontend1->AddExpectedEvent(
blink::mojom::AppCacheEventID::APPCACHE_PROGRESS_EVENT);
frontend1->AddExpectedEvent(
blink::mojom::AppCacheEventID::APPCACHE_PROGRESS_EVENT);
frontend1->AddExpectedEvent(
blink::mojom::AppCacheEventID::APPCACHE_PROGRESS_EVENT);
frontend1->AddExpectedEvent(
blink::mojom::AppCacheEventID::APPCACHE_PROGRESS_EVENT); // final
frontend1->AddExpectedEvent(
blink::mojom::AppCacheEventID::APPCACHE_UPDATE_READY_EVENT);
frontend2->AddExpectedEvent(
blink::mojom::AppCacheEventID::APPCACHE_CHECKING_EVENT);
frontend2->AddExpectedEvent(
blink::mojom::AppCacheEventID::APPCACHE_DOWNLOADING_EVENT);
frontend2->AddExpectedEvent(
blink::mojom::AppCacheEventID::APPCACHE_PROGRESS_EVENT);
frontend2->AddExpectedEvent(
blink::mojom::AppCacheEventID::APPCACHE_PROGRESS_EVENT);
frontend2->AddExpectedEvent(
blink::mojom::AppCacheEventID::APPCACHE_PROGRESS_EVENT);
frontend2->AddExpectedEvent(
blink::mojom::AppCacheEventID::APPCACHE_PROGRESS_EVENT); // final
frontend2->AddExpectedEvent(
blink::mojom::AppCacheEventID::APPCACHE_UPDATE_READY_EVENT);
// Seed storage with expected manifest data.
const std::string seed_data(kManifest2Contents);
scoped_refptr<net::StringIOBuffer> io_buffer =
base::MakeRefCounted<net::StringIOBuffer>(seed_data);
response_writer_->WriteData(
io_buffer.get(), seed_data.length(),
base::BindOnce(
&AppCacheUpdateJobTest::StartUpdateAfterSeedingStorageData,
base::Unretained(this)));
// Start update after data write completes asynchronously.
}
// See http://code.google.com/p/chromium/issues/detail?id=95101
void Bug95101Test() {
MakeService();
group_ = base::MakeRefCounted<AppCacheGroup>(
service_->storage(), MockHttpServer::GetMockUrl("files/empty-manifest"),
service_->storage()->NewGroupId());
AppCacheUpdateJob* update =
new AppCacheUpdateJob(service_.get(), group_.get());
group_->update_job_ = update;
// Create a malformed cache with a missing manifest entry.
GURL wrong_manifest_url =
MockHttpServer::GetMockUrl("files/missing-mime-manifest");
AppCache* cache = MakeCacheForGroup(1, wrong_manifest_url, 111);
MockFrontend* frontend = MakeMockFrontend();
AppCacheHost* host = MakeHost(frontend);
host->AssociateCompleteCache(cache);
update->StartUpdate(nullptr, GURL());
// Set up checks for when update job finishes.
do_checks_after_update_finished_ = true;
expect_group_is_being_deleted_ = true;
expect_group_has_cache_ = true;
expect_newest_cache_ = cache; // newest cache unaffected by update
frontend->AddExpectedEvent(
blink::mojom::AppCacheEventID::APPCACHE_CHECKING_EVENT);
frontend->AddExpectedEvent(
blink::mojom::AppCacheEventID::APPCACHE_ERROR_EVENT);
frontend->expected_error_message_ =
"Manifest entry not found in existing cache";
WaitForUpdateToFinish();
}
void StartUpdateAfterSeedingStorageData(int result) {
ASSERT_GT(result, 0);
response_writer_.reset();
AppCacheUpdateJob* update = group_->update_job_;
update->StartUpdate(nullptr, GURL());
WaitForUpdateToFinish();
}
void BasicCacheAttemptSuccessTest() {
GURL manifest_url = MockHttpServer::GetMockUrl("files/manifest1");
MakeService();
group_ = base::MakeRefCounted<AppCacheGroup>(
service_->storage(), manifest_url, service_->storage()->NewGroupId());
ASSERT_TRUE(group_->last_full_update_check_time().is_null());
AppCacheUpdateJob* update =
new AppCacheUpdateJob(service_.get(), group_.get());
group_->update_job_ = update;
MockFrontend* frontend = MakeMockFrontend();
AppCacheHost* host = MakeHost(frontend);
update->StartUpdate(host, GURL());
// Set up checks for when update job finishes.
do_checks_after_update_finished_ = true;
expect_group_obsolete_ = false;
expect_group_has_cache_ = true;
expect_full_update_time_newer_than_ = base::Time::Now() - kOneHour;
tested_manifest_ = MANIFEST1;
frontend->AddExpectedEvent(
blink::mojom::AppCacheEventID::APPCACHE_CHECKING_EVENT);
WaitForUpdateToFinish();
}
// Tests that 200 OK responses with varying scopes result in the expected
// AppCache data stored in the cache.
void ScopeManifestRootWithNoOverrideTest() {
ScopeTest("manifest-no-override", SCOPE_MANIFEST_ROOT);
}
void ScopeManifestBarWithNoOverrideTest() {
ScopeTest("bar/manifest-no-override", SCOPE_MANIFEST_BAR);
}
void ScopeManifestRootWithRootOverrideTest() {
ScopeTest("manifest-root-override", SCOPE_MANIFEST_ROOT);
}
void ScopeManifestBarWithRootOverrideTest() {
ScopeTest("bar/manifest-root-override", SCOPE_MANIFEST_ROOT);
}
void ScopeManifestRootWithBarOverrideTest() {
ScopeTest("manifest-bar-override", SCOPE_MANIFEST_BAR);
}
void ScopeManifestBarWithBarOverrideTest() {
ScopeTest("bar/manifest-bar-override", SCOPE_MANIFEST_BAR);
}
void ScopeManifestRootWithOtherOverrideTest() {
ScopeTest("manifest-other-override", SCOPE_MANIFEST_OTHER);
}
void ScopeManifestBarWithOtherOverrideTest() {
ScopeTest("bar/manifest-other-override", SCOPE_MANIFEST_OTHER);
}
// Tests that 304 NOT MODIFIED responses with varying scopes result in the
// expected AppCache data stored in the cache.
void ScopeManifest304RootWithNoOverrideTest() {
Scope304Test("manifest-304-no-override", /*previous_scope=*/"/bar/",
SCOPE_MANIFEST_ROOT);
}
void ScopeManifest304BarWithNoOverrideTest() {
Scope304Test("bar/manifest-304-no-override", /*previous_scope=*/"/baz/",
SCOPE_MANIFEST_BAR);
}
void ScopeManifest304RootWithRootOverrideTest() {
Scope304Test("manifest-304-root-override", /*previous_scope=*/"/bar/",
SCOPE_MANIFEST_ROOT);
}
void ScopeManifest304BarWithRootOverrideTest() {
Scope304Test("bar/manifest-304-root-override", /*previous_scope=*/"/baz/",
SCOPE_MANIFEST_ROOT);
}
void ScopeManifest304RootWithBarOverrideTest() {
Scope304Test("manifest-304-bar-override", /*previous_scope=*/"/baz/",
SCOPE_MANIFEST_BAR);
}
void ScopeManifest304BarWithBarOverrideTest() {
Scope304Test("bar/manifest-304-bar-override", /*previous_scope=*/"/baz/",
SCOPE_MANIFEST_BAR);
}
void ScopeManifest304RootWithOtherOverrideTest() {
Scope304Test("manifest-304-other-override", /*previous_scope=*/"/bar/",
SCOPE_MANIFEST_OTHER);
}
void ScopeManifest304BarWithOtherOverrideTest() {
Scope304Test("bar/manifest-304-other-override", /*previous_scope=*/"/baz/",
SCOPE_MANIFEST_OTHER);
}
void DownloadInterceptEntriesTest() {
// Ensures we download intercept entries too.
GURL manifest_url =
MockHttpServer::GetMockUrl("files/manifest-with-intercept");
MakeService();
group_ = base::MakeRefCounted<AppCacheGroup>(
service_->storage(), manifest_url, service_->storage()->NewGroupId());
AppCacheUpdateJob* update =
new AppCacheUpdateJob(service_.get(), group_.get());
group_->update_job_ = update;
MockFrontend* frontend = MakeMockFrontend();
AppCacheHost* host = MakeHost(frontend);
update->StartUpdate(host, GURL());
// Set up checks for when update job finishes.
do_checks_after_update_finished_ = true;
expect_group_obsolete_ = false;
expect_group_has_cache_ = true;
tested_manifest_ = MANIFEST_WITH_INTERCEPT;
frontend->AddExpectedEvent(
blink::mojom::AppCacheEventID::APPCACHE_CHECKING_EVENT);
WaitForUpdateToFinish();
}
void BasicUpgradeSuccessTest() {
MakeService();
group_ = base::MakeRefCounted<AppCacheGroup>(
service_->storage(), MockHttpServer::GetMockUrl("files/manifest1"),
service_->storage()->NewGroupId());
AppCacheUpdateJob* update =
new AppCacheUpdateJob(service_.get(), group_.get());
group_->update_job_ = update;
// Create a response writer to get a response id.
response_writer_ =
service_->storage()->CreateResponseWriter(group_->manifest_url());
AppCache* cache = MakeCacheForGroup(service_->storage()->NewCacheId(),
response_writer_->response_id());
MockFrontend* frontend1 = MakeMockFrontend();
MockFrontend* frontend2 = MakeMockFrontend();
AppCacheHost* host1 = MakeHost(frontend1);
AppCacheHost* host2 = MakeHost(frontend2);
host1->AssociateCompleteCache(cache);
host2->AssociateCompleteCache(cache);
frontend1->SetVerifyProgressEvents(true);
frontend2->SetVerifyProgressEvents(true);
group_->set_last_full_update_check_time(base::Time::Now() -
kFullUpdateInterval - kOneHour);
// Set up checks for when update job finishes.
do_checks_after_update_finished_ = true;
expect_group_obsolete_ = false;
expect_group_has_cache_ = true;
expect_old_cache_ = cache;
tested_manifest_ = MANIFEST1;
expect_full_update_time_newer_than_ = group_->last_full_update_check_time();
frontend1->AddExpectedEvent(
blink::mojom::AppCacheEventID::APPCACHE_CHECKING_EVENT);
frontend1->AddExpectedEvent(
blink::mojom::AppCacheEventID::APPCACHE_DOWNLOADING_EVENT);
frontend1->AddExpectedEvent(
blink::mojom::AppCacheEventID::APPCACHE_PROGRESS_EVENT);
frontend1->AddExpectedEvent(
blink::mojom::AppCacheEventID::APPCACHE_PROGRESS_EVENT);
frontend1->AddExpectedEvent(
blink::mojom::AppCacheEventID::APPCACHE_PROGRESS_EVENT); // final
frontend1->AddExpectedEvent(
blink::mojom::AppCacheEventID::APPCACHE_UPDATE_READY_EVENT);
frontend2->AddExpectedEvent(
blink::mojom::AppCacheEventID::APPCACHE_CHECKING_EVENT);
frontend2->AddExpectedEvent(
blink::mojom::AppCacheEventID::APPCACHE_DOWNLOADING_EVENT);
frontend2->AddExpectedEvent(
blink::mojom::AppCacheEventID::APPCACHE_PROGRESS_EVENT);
frontend2->AddExpectedEvent(
blink::mojom::AppCacheEventID::APPCACHE_PROGRESS_EVENT);
frontend2->AddExpectedEvent(
blink::mojom::AppCacheEventID::APPCACHE_PROGRESS_EVENT); // final
frontend2->AddExpectedEvent(
blink::mojom::AppCacheEventID::APPCACHE_UPDATE_READY_EVENT);
// Seed storage with expected manifest data different from manifest1.
const std::string seed_data("different");
scoped_refptr<net::StringIOBuffer> io_buffer =
base::MakeRefCounted<net::StringIOBuffer>(seed_data);
response_writer_->WriteData(
io_buffer.get(), seed_data.length(),
base::BindOnce(
&AppCacheUpdateJobTest::StartUpdateAfterSeedingStorageData,
base::Unretained(this)));
// Start update after data write completes asynchronously.
}
void UpgradeLoadFromNewestCacheTest() {
MakeService();
group_ = base::MakeRefCounted<AppCacheGroup>(
service_->storage(), MockHttpServer::GetMockUrl("files/manifest1"),
service_->storage()->NewGroupId());
AppCacheUpdateJob* update =
new AppCacheUpdateJob(service_.get(), group_.get());
group_->update_job_ = update;
AppCache* cache = MakeCacheForGroup(service_->storage()->NewCacheId(), 42);
MockFrontend* frontend = MakeMockFrontend();
AppCacheHost* host = MakeHost(frontend);
host->AssociateCompleteCache(cache);
// Give the newest cache an entry that is in storage.
response_writer_ =
service_->storage()->CreateResponseWriter(group_->manifest_url());
cache->AddEntry(MockHttpServer::GetMockUrl("files/explicit1"),
AppCacheEntry(AppCacheEntry::EXPLICIT,
response_writer_->response_id()));
// Set up checks for when update job finishes.
do_checks_after_update_finished_ = true;
expect_group_obsolete_ = false;
expect_group_has_cache_ = true;
expect_old_cache_ = cache;
expect_response_ids_.insert(std::map<GURL, int64_t>::value_type(
MockHttpServer::GetMockUrl("files/explicit1"),
response_writer_->response_id()));
tested_manifest_ = MANIFEST1;
frontend->AddExpectedEvent(
blink::mojom::AppCacheEventID::APPCACHE_CHECKING_EVENT);
frontend->AddExpectedEvent(
blink::mojom::AppCacheEventID::APPCACHE_DOWNLOADING_EVENT);
frontend->AddExpectedEvent(
blink::mojom::AppCacheEventID::APPCACHE_PROGRESS_EVENT);
frontend->AddExpectedEvent(
blink::mojom::AppCacheEventID::APPCACHE_PROGRESS_EVENT);
frontend->AddExpectedEvent(
blink::mojom::AppCacheEventID::APPCACHE_PROGRESS_EVENT); // final
frontend->AddExpectedEvent(
blink::mojom::AppCacheEventID::APPCACHE_UPDATE_READY_EVENT);
// Seed storage with expected http response info for entry. Allow reuse.
const char data[] =
"HTTP/1.1 200 OK\0"
"Cache-Control: max-age=8675309\0"
"\0";
scoped_refptr<net::HttpResponseHeaders> headers =
base::MakeRefCounted<net::HttpResponseHeaders>(
std::string(data, base::size(data)));
std::unique_ptr<net::HttpResponseInfo> response_info =
std::make_unique<net::HttpResponseInfo>();
response_info->request_time = base::Time::Now();
response_info->response_time = base::Time::Now();
response_info->headers = std::move(headers);
scoped_refptr<HttpResponseInfoIOBuffer> io_buffer =
base::MakeRefCounted<HttpResponseInfoIOBuffer>(
std::move(response_info));
response_writer_->WriteInfo(
io_buffer.get(),
base::BindOnce(
&AppCacheUpdateJobTest::StartUpdateAfterSeedingStorageData,
base::Unretained(this)));
// Start update after data write completes asynchronously.
}
void UpgradeNoLoadFromNewestCacheTest() {
MakeService();
group_ = base::MakeRefCounted<AppCacheGroup>(
service_->storage(), MockHttpServer::GetMockUrl("files/manifest1"),
service_->storage()->NewGroupId());
AppCacheUpdateJob* update =
new AppCacheUpdateJob(service_.get(), group_.get());
group_->update_job_ = update;
AppCache* cache = MakeCacheForGroup(service_->storage()->NewCacheId(), 42);
MockFrontend* frontend = MakeMockFrontend();
AppCacheHost* host = MakeHost(frontend);
host->AssociateCompleteCache(cache);
// Give the newest cache an entry that is in storage.
response_writer_ =
service_->storage()->CreateResponseWriter(group_->manifest_url());
cache->AddEntry(MockHttpServer::GetMockUrl("files/explicit1"),
AppCacheEntry(AppCacheEntry::EXPLICIT,
response_writer_->response_id()));
// Set up checks for when update job finishes.
do_checks_after_update_finished_ = true;
expect_group_obsolete_ = false;
expect_group_has_cache_ = true;
expect_old_cache_ = cache;
tested_manifest_ = MANIFEST1;
frontend->AddExpectedEvent(
blink::mojom::AppCacheEventID::APPCACHE_CHECKING_EVENT);
frontend->AddExpectedEvent(
blink::mojom::AppCacheEventID::APPCACHE_DOWNLOADING_EVENT);
frontend->AddExpectedEvent(
blink::mojom::AppCacheEventID::APPCACHE_PROGRESS_EVENT);
frontend->AddExpectedEvent(
blink::mojom::AppCacheEventID::APPCACHE_PROGRESS_EVENT);
frontend->AddExpectedEvent(
blink::mojom::AppCacheEventID::APPCACHE_PROGRESS_EVENT); // final
frontend->AddExpectedEvent(
blink::mojom::AppCacheEventID::APPCACHE_UPDATE_READY_EVENT);
// Seed storage with expected http response info for entry. Do NOT
// allow reuse by setting an expires header in the past.
const char data[] =
"HTTP/1.1 200 OK\0"
"Expires: Thu, 01 Dec 1994 16:00:00 GMT\0"
"\0";
scoped_refptr<net::HttpResponseHeaders> headers =
base::MakeRefCounted<net::HttpResponseHeaders>(
std::string(data, base::size(data)));
std::unique_ptr<net::HttpResponseInfo> response_info =
std::make_unique<net::HttpResponseInfo>();
response_info->request_time = base::Time::Now();
response_info->response_time = base::Time::Now();
response_info->headers = std::move(headers);
scoped_refptr<HttpResponseInfoIOBuffer> io_buffer =
base::MakeRefCounted<HttpResponseInfoIOBuffer>(
std::move(response_info));
response_writer_->WriteInfo(
io_buffer.get(),
base::BindOnce(
&AppCacheUpdateJobTest::StartUpdateAfterSeedingStorageData,
base::Unretained(this)));
// Start update after data write completes asynchronously.
}
void UpgradeLoadFromNewestCacheVaryHeaderTest() {
MakeService();
group_ = base::MakeRefCounted<AppCacheGroup>(
service_->storage(), MockHttpServer::GetMockUrl("files/manifest1"),
service_->storage()->NewGroupId());
AppCacheUpdateJob* update =
new AppCacheUpdateJob(service_.get(), group_.get());
group_->update_job_ = update;
AppCache* cache = MakeCacheForGroup(service_->storage()->NewCacheId(), 42);
MockFrontend* frontend = MakeMockFrontend();
AppCacheHost* host = MakeHost(frontend);
host->AssociateCompleteCache(cache);
// Give the newest cache an entry that is in storage.
response_writer_ =
service_->storage()->CreateResponseWriter(group_->manifest_url());
cache->AddEntry(MockHttpServer::GetMockUrl("files/explicit1"),
AppCacheEntry(AppCacheEntry::EXPLICIT,
response_writer_->response_id()));
// Set up checks for when update job finishes.
do_checks_after_update_finished_ = true;
expect_group_obsolete_ = false;
expect_group_has_cache_ = true;
expect_old_cache_ = cache;
tested_manifest_ = MANIFEST1;
frontend->AddExpectedEvent(
blink::mojom::AppCacheEventID::APPCACHE_CHECKING_EVENT);
frontend->AddExpectedEvent(
blink::mojom::AppCacheEventID::APPCACHE_DOWNLOADING_EVENT);
frontend->AddExpectedEvent(
blink::mojom::AppCacheEventID::APPCACHE_PROGRESS_EVENT);
frontend->AddExpectedEvent(
blink::mojom::AppCacheEventID::APPCACHE_PROGRESS_EVENT);
frontend->AddExpectedEvent(
blink::mojom::AppCacheEventID::APPCACHE_PROGRESS_EVENT); // final
frontend->AddExpectedEvent(
blink::mojom::AppCacheEventID::APPCACHE_UPDATE_READY_EVENT);
// Seed storage with expected http response info for entry: a vary header.
const char data[] =
"HTTP/1.1 200 OK\0"
"Cache-Control: max-age=8675309\0"
"Vary: blah\0"
"\0";
scoped_refptr<net::HttpResponseHeaders> headers =
base::MakeRefCounted<net::HttpResponseHeaders>(
std::string(data, base::size(data)));
std::unique_ptr<net::HttpResponseInfo> response_info =
std::make_unique<net::HttpResponseInfo>();
response_info->request_time = base::Time::Now();
response_info->response_time = base::Time::Now();
response_info->headers = std::move(headers);
scoped_refptr<HttpResponseInfoIOBuffer> io_buffer =
base::MakeRefCounted<HttpResponseInfoIOBuffer>(
std::move(response_info));
response_writer_->WriteInfo(
io_buffer.get(),
base::BindOnce(
&AppCacheUpdateJobTest::StartUpdateAfterSeedingStorageData,
base::Unretained(this)));
// Start update after data write completes asynchronously.
}
void UpgradeLoadFromNewestCacheReuseVaryHeaderTest() {
MakeService();
group_ = base::MakeRefCounted<AppCacheGroup>(
service_->storage(), MockHttpServer::GetMockUrl("files/manifest1"),
service_->storage()->NewGroupId());
AppCacheUpdateJob* update =
new AppCacheUpdateJob(service_.get(), group_.get());
group_->update_job_ = update;
AppCache* cache = MakeCacheForGroup(service_->storage()->NewCacheId(), 42);
MockFrontend* frontend = MakeMockFrontend();
AppCacheHost* host = MakeHost(frontend);
host->AssociateCompleteCache(cache);
// Give the newest cache an entry that is in storage.
response_writer_ =
service_->storage()->CreateResponseWriter(group_->manifest_url());
cache->AddEntry(MockHttpServer::GetMockUrl("files/explicit1"),
AppCacheEntry(AppCacheEntry::EXPLICIT,
response_writer_->response_id()));
// Set up checks for when update job finishes.
do_checks_after_update_finished_ = true;
expect_group_obsolete_ = false;
expect_group_has_cache_ = true;
expect_old_cache_ = cache;
expect_response_ids_.insert(std::map<GURL, int64_t>::value_type(
MockHttpServer::GetMockUrl("files/explicit1"),
response_writer_->response_id()));
tested_manifest_ = MANIFEST1;
frontend->AddExpectedEvent(
blink::mojom::AppCacheEventID::APPCACHE_CHECKING_EVENT);
frontend->AddExpectedEvent(
blink::mojom::AppCacheEventID::APPCACHE_DOWNLOADING_EVENT);
frontend->AddExpectedEvent(
blink::mojom::AppCacheEventID::APPCACHE_PROGRESS_EVENT);
frontend->AddExpectedEvent(
blink::mojom::AppCacheEventID::APPCACHE_PROGRESS_EVENT);
frontend->AddExpectedEvent(
blink::mojom::AppCacheEventID::APPCACHE_PROGRESS_EVENT); // final
frontend->AddExpectedEvent(
blink::mojom::AppCacheEventID::APPCACHE_UPDATE_READY_EVENT);
// Seed storage with expected http response info for an entry
// with a vary header for which we allow reuse.
const char data[] =
"HTTP/1.1 200 OK\0"
"Cache-Control: max-age=8675309\0"
"Vary: origin, accept-encoding\0"
"\0";
scoped_refptr<net::HttpResponseHeaders> headers =
base::MakeRefCounted<net::HttpResponseHeaders>(
std::string(data, base::size(data)));
std::unique_ptr<net::HttpResponseInfo> response_info =
std::make_unique<net::HttpResponseInfo>();
response_info->request_time = base::Time::Now();
response_info->response_time = base::Time::Now();
response_info->headers = std::move(headers);
scoped_refptr<HttpResponseInfoIOBuffer> io_buffer =
base::MakeRefCounted<HttpResponseInfoIOBuffer>(
std::move(response_info));
response_writer_->WriteInfo(
io_buffer.get(),
base::BindOnce(
&AppCacheUpdateJobTest::StartUpdateAfterSeedingStorageData,
base::Unretained(this)));
// Start update after data write completes asynchronously.
}
void UpgradeSuccessMergedTypesTest() {
MakeService();
group_ = base::MakeRefCounted<AppCacheGroup>(
service_->storage(),
MockHttpServer::GetMockUrl("files/manifest-merged-types"),
service_->storage()->NewGroupId());
AppCacheUpdateJob* update =
new AppCacheUpdateJob(service_.get(), group_.get());
group_->update_job_ = update;
AppCache* cache = MakeCacheForGroup(service_->storage()->NewCacheId(), 42);
MockFrontend* frontend1 = MakeMockFrontend();
MockFrontend* frontend2 = MakeMockFrontend();
AppCacheHost* host1 = MakeHost(frontend1);
AppCacheHost* host2 = MakeHost(frontend2);
host1->AssociateCompleteCache(cache);
host2->AssociateCompleteCache(cache);
// Give the newest cache a master entry that is also one of the explicit
// entries in the manifest.
cache->AddEntry(MockHttpServer::GetMockUrl("files/explicit1"),
AppCacheEntry(AppCacheEntry::MASTER, 111));
update->StartUpdate(nullptr, GURL());
// Set up checks for when update job finishes.
do_checks_after_update_finished_ = true;
expect_group_obsolete_ = false;
expect_group_has_cache_ = true;
expect_old_cache_ = cache;
tested_manifest_ = MANIFEST_MERGED_TYPES;
frontend1->AddExpectedEvent(
blink::mojom::AppCacheEventID::APPCACHE_CHECKING_EVENT);
frontend1->AddExpectedEvent(
blink::mojom::AppCacheEventID::APPCACHE_DOWNLOADING_EVENT);
frontend1->AddExpectedEvent(
blink::mojom::AppCacheEventID::APPCACHE_PROGRESS_EVENT); // explicit1
frontend1->AddExpectedEvent(
blink::mojom::AppCacheEventID::APPCACHE_PROGRESS_EVENT); // manifest
frontend1->AddExpectedEvent(
blink::mojom::AppCacheEventID::APPCACHE_PROGRESS_EVENT); // final
frontend1->AddExpectedEvent(
blink::mojom::AppCacheEventID::APPCACHE_UPDATE_READY_EVENT);
frontend2->AddExpectedEvent(
blink::mojom::AppCacheEventID::APPCACHE_CHECKING_EVENT);
frontend2->AddExpectedEvent(
blink::mojom::AppCacheEventID::APPCACHE_DOWNLOADING_EVENT);
frontend2->AddExpectedEvent(
blink::mojom::AppCacheEventID::APPCACHE_PROGRESS_EVENT);
frontend2->AddExpectedEvent(
blink::mojom::AppCacheEventID::APPCACHE_PROGRESS_EVENT);
frontend2->AddExpectedEvent(
blink::mojom::AppCacheEventID::APPCACHE_PROGRESS_EVENT); // final
frontend2->AddExpectedEvent(
blink::mojom::AppCacheEventID::APPCACHE_UPDATE_READY_EVENT);
WaitForUpdateToFinish();
}
void CacheAttemptFailUrlFetchTest() {
MakeService();
group_ = base::MakeRefCounted<AppCacheGroup>(
service_->storage(),
MockHttpServer::GetMockUrl("files/manifest-with-404"),
service_->storage()->NewGroupId());
AppCacheUpdateJob* update =
new AppCacheUpdateJob(service_.get(), group_.get());
group_->update_job_ = update;
MockFrontend* frontend = MakeMockFrontend();
AppCacheHost* host = MakeHost(frontend);
update->StartUpdate(host, GURL());
// Set up checks for when update job finishes.
do_checks_after_update_finished_ = true;
expect_group_obsolete_ = false;
expect_group_has_cache_ = false; // 404 explicit url is cache failure
frontend->AddExpectedEvent(
blink::mojom::AppCacheEventID::APPCACHE_CHECKING_EVENT);
WaitForUpdateToFinish();
}
void UpgradeFailUrlFetchTest() {
MakeService();
group_ = base::MakeRefCounted<AppCacheGroup>(
service_->storage(),
MockHttpServer::GetMockUrl("files/manifest-fb-404"),
service_->storage()->NewGroupId());
AppCacheUpdateJob* update =
new AppCacheUpdateJob(service_.get(), group_.get());
group_->update_job_ = update;
AppCache* cache = MakeCacheForGroup(service_->storage()->NewCacheId(), 99);
group_->set_first_evictable_error_time(
base::Time::Now() - kMaxEvictableErrorDuration - kOneHour);
MockFrontend* frontend1 = MakeMockFrontend();
MockFrontend* frontend2 = MakeMockFrontend();
frontend1->SetIgnoreProgressEvents(true);
frontend2->SetIgnoreProgressEvents(true);
AppCacheHost* host1 = MakeHost(frontend1);
AppCacheHost* host2 = MakeHost(frontend2);
host1->AssociateCompleteCache(cache);
host2->AssociateCompleteCache(cache);
update->StartUpdate(nullptr, GURL());
// Set up checks for when update job finishes.
do_checks_after_update_finished_ = true;
expect_group_obsolete_ = false;
expect_group_has_cache_ = true;
expect_newest_cache_ = cache; // newest cache unaffectd by failed update
expect_eviction_ = true;
frontend1->AddExpectedEvent(
blink::mojom::AppCacheEventID::APPCACHE_CHECKING_EVENT);
frontend1->AddExpectedEvent(
blink::mojom::AppCacheEventID::APPCACHE_DOWNLOADING_EVENT);
frontend1->AddExpectedEvent(
blink::mojom::AppCacheEventID::APPCACHE_ERROR_EVENT);
frontend2->AddExpectedEvent(
blink::mojom::AppCacheEventID::APPCACHE_CHECKING_EVENT);
frontend2->AddExpectedEvent(
blink::mojom::AppCacheEventID::APPCACHE_DOWNLOADING_EVENT);
frontend2->AddExpectedEvent(
blink::mojom::AppCacheEventID::APPCACHE_ERROR_EVENT);
WaitForUpdateToFinish();
}
void UpgradeFailMasterUrlFetchTest() {
tested_manifest_path_override_ = "files/manifest1-with-notmodified";
MakeService();
const GURL kManifestUrl =
MockHttpServer::GetMockUrl(tested_manifest_path_override_);
group_ = base::MakeRefCounted<AppCacheGroup>(
service_->storage(), kManifestUrl, service_->storage()->NewGroupId());
AppCacheUpdateJob* update =
new AppCacheUpdateJob(service_.get(), group_.get());
group_->update_job_ = update;
AppCache* cache = MakeCacheForGroup(service_->storage()->NewCacheId(), 25);
MockFrontend* frontend1 = MakeMockFrontend();
MockFrontend* frontend2 = MakeMockFrontend();
AppCacheHost* host1 = MakeHost(frontend1);
AppCacheHost* host2 = MakeHost(frontend2);
host1->AssociateCompleteCache(cache);
host2->AssociateCompleteCache(cache);
// Give the newest cache some existing entries; one will fail with a 404.
cache->AddEntry(MockHttpServer::GetMockUrl("files/notfound"),
AppCacheEntry(AppCacheEntry::MASTER, 222));
cache->AddEntry(
MockHttpServer::GetMockUrl("files/explicit2"),
AppCacheEntry(AppCacheEntry::MASTER | AppCacheEntry::FOREIGN, 333));
cache->AddEntry(MockHttpServer::GetMockUrl("files/servererror"),
AppCacheEntry(AppCacheEntry::MASTER, 444));
cache->AddEntry(MockHttpServer::GetMockUrl("files/notmodified"),
AppCacheEntry(AppCacheEntry::EXPLICIT, 555));
// Seed the response_info working set with canned data for
// files/servererror and for files/notmodified to test that the
// existing entries for those resource are reused by the update job.
const char kData[] =
"HTTP/1.1 200 OK\0"
"Last-Modified: Sat, 29 Oct 1994 19:43:31 GMT\0"
"\0";
const std::string kRawHeaders(kData, base::size(kData));
MakeAppCacheResponseInfo(kManifestUrl, 444, kRawHeaders);
MakeAppCacheResponseInfo(kManifestUrl, 555, kRawHeaders);
update->StartUpdate(nullptr, GURL());
// Set up checks for when update job finishes.
do_checks_after_update_finished_ = true;
expect_group_obsolete_ = false;
expect_group_has_cache_ = true;
expect_old_cache_ = cache;
tested_manifest_ = MANIFEST1;
expect_extra_entries_.insert(AppCache::EntryMap::value_type(
MockHttpServer::GetMockUrl("files/explicit2"),
AppCacheEntry(AppCacheEntry::MASTER))); // foreign flag is dropped
expect_extra_entries_.insert(AppCache::EntryMap::value_type(
MockHttpServer::GetMockUrl("files/servererror"),
AppCacheEntry(AppCacheEntry::MASTER)));
expect_extra_entries_.insert(AppCache::EntryMap::value_type(
MockHttpServer::GetMockUrl("files/notmodified"),
AppCacheEntry(AppCacheEntry::EXPLICIT)));
expect_response_ids_.insert(std::map<GURL, int64_t>::value_type(
MockHttpServer::GetMockUrl("files/servererror"), 444)); // copied
expect_response_ids_.insert(std::map<GURL, int64_t>::value_type(
MockHttpServer::GetMockUrl("files/notmodified"), 555)); // copied
frontend1->AddExpectedEvent(
blink::mojom::AppCacheEventID::APPCACHE_CHECKING_EVENT);
frontend1->AddExpectedEvent(
blink::mojom::AppCacheEventID::APPCACHE_DOWNLOADING_EVENT);
frontend1->AddExpectedEvent(
blink::mojom::AppCacheEventID::APPCACHE_PROGRESS_EVENT); // explicit1
frontend1->AddExpectedEvent(
blink::mojom::AppCacheEventID::APPCACHE_PROGRESS_EVENT); // fallback1a
frontend1->AddExpectedEvent(
blink::mojom::AppCacheEventID::APPCACHE_PROGRESS_EVENT); // notfound
frontend1->AddExpectedEvent(
blink::mojom::AppCacheEventID::APPCACHE_PROGRESS_EVENT); // explicit2
frontend1->AddExpectedEvent(
blink::mojom::AppCacheEventID::APPCACHE_PROGRESS_EVENT); // servererror
frontend1->AddExpectedEvent(
blink::mojom::AppCacheEventID::APPCACHE_PROGRESS_EVENT); // notmodified
frontend1->AddExpectedEvent(
blink::mojom::AppCacheEventID::APPCACHE_PROGRESS_EVENT); // final
frontend1->AddExpectedEvent(
blink::mojom::AppCacheEventID::APPCACHE_UPDATE_READY_EVENT);
frontend2->AddExpectedEvent(
blink::mojom::AppCacheEventID::APPCACHE_CHECKING_EVENT);
frontend2->AddExpectedEvent(
blink::mojom::AppCacheEventID::APPCACHE_DOWNLOADING_EVENT);
frontend2->AddExpectedEvent(
blink::mojom::AppCacheEventID::APPCACHE_PROGRESS_EVENT); // explicit1
frontend2->AddExpectedEvent(
blink::mojom::AppCacheEventID::APPCACHE_PROGRESS_EVENT); // fallback1a
frontend2->AddExpectedEvent(
blink::mojom::AppCacheEventID::APPCACHE_PROGRESS_EVENT); // notfound
frontend2->AddExpectedEvent(
blink::mojom::AppCacheEventID::APPCACHE_PROGRESS_EVENT); // explicit2
frontend2->AddExpectedEvent(
blink::mojom::AppCacheEventID::APPCACHE_PROGRESS_EVENT); // servererror
frontend2->AddExpectedEvent(
blink::mojom::AppCacheEventID::APPCACHE_PROGRESS_EVENT); // notmodified
frontend2->AddExpectedEvent(
blink::mojom::AppCacheEventID::APPCACHE_PROGRESS_EVENT); // final
frontend2->AddExpectedEvent(
blink::mojom::AppCacheEventID::APPCACHE_UPDATE_READY_EVENT);
WaitForUpdateToFinish();
}
void EmptyManifestTest() {
MakeService();
group_ = base::MakeRefCounted<AppCacheGroup>(
service_->storage(), MockHttpServer::GetMockUrl("files/empty-manifest"),
service_->storage()->NewGroupId());
AppCacheUpdateJob* update =
new AppCacheUpdateJob(service_.get(), group_.get());
group_->update_job_ = update;
AppCache* cache = MakeCacheForGroup(service_->storage()->NewCacheId(), 33);
MockFrontend* frontend1 = MakeMockFrontend();
MockFrontend* frontend2 = MakeMockFrontend();
AppCacheHost* host1 = MakeHost(frontend1);
AppCacheHost* host2 = MakeHost(frontend2);
host1->AssociateCompleteCache(cache);
host2->AssociateCompleteCache(cache);
frontend1->SetVerifyProgressEvents(true);
update->StartUpdate(nullptr, GURL());
// Set up checks for when update job finishes.
do_checks_after_update_finished_ = true;
expect_group_obsolete_ = false;
expect_group_has_cache_ = true;
expect_old_cache_ = cache;
tested_manifest_ = EMPTY_MANIFEST;
frontend1->AddExpectedEvent(
blink::mojom::AppCacheEventID::APPCACHE_CHECKING_EVENT);
frontend1->AddExpectedEvent(
blink::mojom::AppCacheEventID::APPCACHE_DOWNLOADING_EVENT);
frontend1->AddExpectedEvent(
blink::mojom::AppCacheEventID::APPCACHE_PROGRESS_EVENT); // final
frontend1->AddExpectedEvent(
blink::mojom::AppCacheEventID::APPCACHE_UPDATE_READY_EVENT);
frontend2->AddExpectedEvent(
blink::mojom::AppCacheEventID::APPCACHE_CHECKING_EVENT);
frontend2->AddExpectedEvent(
blink::mojom::AppCacheEventID::APPCACHE_DOWNLOADING_EVENT);
frontend2->AddExpectedEvent(
blink::mojom::AppCacheEventID::APPCACHE_PROGRESS_EVENT); // final
frontend2->AddExpectedEvent(
blink::mojom::AppCacheEventID::APPCACHE_UPDATE_READY_EVENT);
WaitForUpdateToFinish();
}
void EmptyFileTest() {
MakeService();
group_ = base::MakeRefCounted<AppCacheGroup>(
service_->storage(),
MockHttpServer::GetMockUrl("files/empty-file-manifest"),
service_->storage()->NewGroupId());
AppCacheUpdateJob* update =
new AppCacheUpdateJob(service_.get(), group_.get());
group_->update_job_ = update;
AppCache* cache = MakeCacheForGroup(service_->storage()->NewCacheId(), 22);
MockFrontend* frontend = MakeMockFrontend();
AppCacheHost* host = MakeHost(frontend);
host->AssociateCompleteCache(cache);
frontend->SetVerifyProgressEvents(true);
update->StartUpdate(host, GURL());
// Set up checks for when update job finishes.
do_checks_after_update_finished_ = true;
expect_group_obsolete_ = false;
expect_group_has_cache_ = true;
tested_manifest_ = EMPTY_FILE_MANIFEST;
frontend->AddExpectedEvent(
blink::mojom::AppCacheEventID::APPCACHE_CHECKING_EVENT);
frontend->AddExpectedEvent(
blink::mojom::AppCacheEventID::APPCACHE_DOWNLOADING_EVENT);
frontend->AddExpectedEvent(
blink::mojom::AppCacheEventID::APPCACHE_PROGRESS_EVENT);
frontend->AddExpectedEvent(
blink::mojom::AppCacheEventID::APPCACHE_PROGRESS_EVENT); // final
frontend->AddExpectedEvent(
blink::mojom::AppCacheEventID::APPCACHE_UPDATE_READY_EVENT);
WaitForUpdateToFinish();
}
void RetryRetryAfterTest() {
// Set some large number of times to return retry.
// Expect 1 manifest fetch and 3 retries.
RetryRequestTestJob::Initialize(5, RetryRequestTestJob::RETRY_AFTER_0, 4);
RetryRequestTest(false);
}
void RetryNoRetryAfterTest() {
// Set some large number of times to return retry.
// Expect 1 manifest fetch and 0 retries.
RetryRequestTestJob::Initialize(5, RetryRequestTestJob::NO_RETRY_AFTER, 1);
RetryRequestTest(false);
}
void RetryNonzeroRetryAfterTest() {
// Set some large number of times to return retry.
// Expect 1 request and 0 retry attempts.
RetryRequestTestJob::Initialize(5, RetryRequestTestJob::NONZERO_RETRY_AFTER,
1);
RetryRequestTest(false);
}
void RetrySuccessTest() {
// Set 2 as the retry limit (does not exceed the max).
// Expect 1 manifest fetch, 2 retries, 1 url fetch, 1 manifest refetch.
RetryRequestTestJob::Initialize(2, RetryRequestTestJob::RETRY_AFTER_0, 5);
RetryRequestTest(true);
}
void RetryUrlTest() {
// Set 1 as the retry limit (does not exceed the max).
// Expect 1 manifest fetch, 1 url fetch, 1 url retry, 1 manifest refetch.
RetryRequestTestJob::Initialize(1, RetryRequestTestJob::RETRY_AFTER_0, 4);
RetryRequestTest(true);
}
void FailStoreNewestCacheTest() {
MakeService();
MockAppCacheStorage* storage =
static_cast<MockAppCacheStorage*>(service_->storage());
storage->SimulateStoreGroupAndNewestCacheFailure();
group_ = base::MakeRefCounted<AppCacheGroup>(
service_->storage(), MockHttpServer::GetMockUrl("files/manifest1"),
service_->storage()->NewGroupId());
AppCacheUpdateJob* update =
new AppCacheUpdateJob(service_.get(), group_.get());
group_->update_job_ = update;
MockFrontend* frontend = MakeMockFrontend();
AppCacheHost* host = MakeHost(frontend);
update->StartUpdate(host, GURL());
// Set up checks for when update job finishes.
do_checks_after_update_finished_ = true;
expect_group_obsolete_ = false;
expect_group_has_cache_ = false; // storage failed
frontend->AddExpectedEvent(
blink::mojom::AppCacheEventID::APPCACHE_CHECKING_EVENT);
WaitForUpdateToFinish();
}
void UpgradeFailStoreNewestCacheTest() {
MakeService();
MockAppCacheStorage* storage =
static_cast<MockAppCacheStorage*>(service_->storage());
storage->SimulateStoreGroupAndNewestCacheFailure();
group_ = base::MakeRefCounted<AppCacheGroup>(
service_->storage(), MockHttpServer::GetMockUrl("files/manifest1"),
service_->storage()->NewGroupId());
AppCacheUpdateJob* update =
new AppCacheUpdateJob(service_.get(), group_.get());
group_->update_job_ = update;
AppCache* cache = MakeCacheForGroup(service_->storage()->NewCacheId(), 11);
MockFrontend* frontend1 = MakeMockFrontend();
MockFrontend* frontend2 = MakeMockFrontend();
AppCacheHost* host1 = MakeHost(frontend1);
AppCacheHost* host2 = MakeHost(frontend2);
host1->AssociateCompleteCache(cache);
host2->AssociateCompleteCache(cache);
update->StartUpdate(nullptr, GURL());
// Set up checks for when update job finishes.
do_checks_after_update_finished_ = true;
expect_group_obsolete_ = false;
expect_group_has_cache_ = true;
expect_newest_cache_ = cache; // unchanged
frontend1->AddExpectedEvent(
blink::mojom::AppCacheEventID::APPCACHE_CHECKING_EVENT);
frontend1->AddExpectedEvent(
blink::mojom::AppCacheEventID::APPCACHE_DOWNLOADING_EVENT);
frontend1->AddExpectedEvent(
blink::mojom::AppCacheEventID::APPCACHE_PROGRESS_EVENT);
frontend1->AddExpectedEvent(
blink::mojom::AppCacheEventID::APPCACHE_PROGRESS_EVENT);
frontend1->AddExpectedEvent(
blink::mojom::AppCacheEventID::APPCACHE_ERROR_EVENT);
frontend2->AddExpectedEvent(
blink::mojom::AppCacheEventID::APPCACHE_CHECKING_EVENT);
frontend2->AddExpectedEvent(
blink::mojom::AppCacheEventID::APPCACHE_DOWNLOADING_EVENT);
frontend2->AddExpectedEvent(
blink::mojom::AppCacheEventID::APPCACHE_PROGRESS_EVENT);
frontend2->AddExpectedEvent(
blink::mojom::AppCacheEventID::APPCACHE_PROGRESS_EVENT);
frontend2->AddExpectedEvent(
blink::mojom::AppCacheEventID::APPCACHE_ERROR_EVENT);
WaitForUpdateToFinish();
}
void MasterEntryFailStoreNewestCacheTest() {
MakeService();
MockAppCacheStorage* storage =
static_cast<MockAppCacheStorage*>(service_->storage());
storage->SimulateStoreGroupAndNewestCacheFailure();
const GURL kManifestUrl = MockHttpServer::GetMockUrl("files/notmodified");
const int64_t kManifestResponseId = 11;
// Seed the response_info working set with canned data for
// files/servererror and for files/notmodified to test that the
// existing entries for those resource are reused by the update job.
const char kData[] =
"HTTP/1.1 200 OK\0"
"Content-type: text/cache-manifest\0"
"Last-Modified: Sat, 29 Oct 1994 19:43:31 GMT\0"
"\0";
const std::string kRawHeaders(kData, base::size(kData));
MakeAppCacheResponseInfo(kManifestUrl, kManifestResponseId, kRawHeaders);
group_ = base::MakeRefCounted<AppCacheGroup>(
service_->storage(), kManifestUrl, service_->storage()->NewGroupId());
scoped_refptr<AppCache> cache(MakeCacheForGroup(
service_->storage()->NewCacheId(), kManifestResponseId));
MockFrontend* frontend = MakeMockFrontend();
AppCacheHost* host = MakeHost(frontend);
host->SetSiteForCookiesForTesting(
net::SiteForCookies::FromUrl(kManifestUrl));
host->SelectCache(MockHttpServer::GetMockUrl("files/empty1"),
blink::mojom::kAppCacheNoCacheId, kManifestUrl);
// Set up checks for when update job finishes.
do_checks_after_update_finished_ = true;
tested_manifest_ = EMPTY_MANIFEST;
tested_manifest_path_override_ = "files/notmodified";
expect_group_obsolete_ = false;
expect_group_has_cache_ = true;
expect_newest_cache_ = cache.get(); // unchanged
frontend->AddExpectedEvent(
blink::mojom::AppCacheEventID::APPCACHE_ERROR_EVENT);
frontend->expected_error_message_ = "Failed to commit new cache to storage";
WaitForUpdateToFinish();
}
void UpgradeFailMakeGroupObsoleteTest() {
MakeService();
MockAppCacheStorage* storage =
static_cast<MockAppCacheStorage*>(service_->storage());
storage->SimulateMakeGroupObsoleteFailure();
group_ = base::MakeRefCounted<AppCacheGroup>(
service_->storage(), MockHttpServer::GetMockUrl("files/nosuchfile"),
service_->storage()->NewGroupId());
AppCacheUpdateJob* update =
new AppCacheUpdateJob(service_.get(), group_.get());
group_->update_job_ = update;
AppCache* cache = MakeCacheForGroup(1, 111);
MockFrontend* frontend1 = MakeMockFrontend();
MockFrontend* frontend2 = MakeMockFrontend();
AppCacheHost* host1 = MakeHost(frontend1);
AppCacheHost* host2 = MakeHost(frontend2);
host1->AssociateCompleteCache(cache);
host2->AssociateCompleteCache(cache);
update->StartUpdate(nullptr, GURL());
// Set up checks for when update job finishes.
do_checks_after_update_finished_ = true;
expect_group_obsolete_ = false;
expect_group_has_cache_ = true;
expect_newest_cache_ = cache; // newest cache unaffected by update
frontend1->AddExpectedEvent(
blink::mojom::AppCacheEventID::APPCACHE_CHECKING_EVENT);
frontend1->AddExpectedEvent(
blink::mojom::AppCacheEventID::APPCACHE_ERROR_EVENT);
frontend2->AddExpectedEvent(
blink::mojom::AppCacheEventID::APPCACHE_CHECKING_EVENT);
frontend2->AddExpectedEvent(
blink::mojom::AppCacheEventID::APPCACHE_ERROR_EVENT);
WaitForUpdateToFinish();
}
void MasterEntryFetchManifestFailTest() {
MakeService();
group_ = base::MakeRefCounted<AppCacheGroup>(service_->storage(),
GURL("http://failme"), 111);
AppCacheUpdateJob* update =
new AppCacheUpdateJob(service_.get(), group_.get());
group_->update_job_ = update;
MockFrontend* frontend = MakeMockFrontend();
AppCacheHost* host = MakeHost(frontend);
host->new_master_entry_url_ = GURL("http://failme/blah");
update->StartUpdate(host, host->new_master_entry_url_);
// Set up checks for when update job finishes.
do_checks_after_update_finished_ = true;
expect_group_obsolete_ = false;
expect_group_has_cache_ = false;
frontend->AddExpectedEvent(
blink::mojom::AppCacheEventID::APPCACHE_CHECKING_EVENT);
frontend->AddExpectedEvent(
blink::mojom::AppCacheEventID::APPCACHE_ERROR_EVENT);
WaitForUpdateToFinish();
}
void MasterEntryBadManifestTest() {
MakeService();
group_ = base::MakeRefCounted<AppCacheGroup>(
service_->storage(), MockHttpServer::GetMockUrl("files/bad-manifest"),
111);
AppCacheUpdateJob* update =
new AppCacheUpdateJob(service_.get(), group_.get());
group_->update_job_ = update;
MockFrontend* frontend = MakeMockFrontend();
AppCacheHost* host = MakeHost(frontend);
host->new_master_entry_url_ = MockHttpServer::GetMockUrl("files/blah");
update->StartUpdate(host, host->new_master_entry_url_);
// Set up checks for when update job finishes.
do_checks_after_update_finished_ = true;
expect_group_obsolete_ = false;
expect_group_has_cache_ = false;
frontend->AddExpectedEvent(
blink::mojom::AppCacheEventID::APPCACHE_CHECKING_EVENT);
frontend->AddExpectedEvent(
blink::mojom::AppCacheEventID::APPCACHE_ERROR_EVENT);
WaitForUpdateToFinish();
}
void MasterEntryManifestNotFoundTest() {
MakeService();
group_ = base::MakeRefCounted<AppCacheGroup>(
service_->storage(), MockHttpServer::GetMockUrl("files/nosuchfile"),
111);
AppCacheUpdateJob* update =
new AppCacheUpdateJob(service_.get(), group_.get());
group_->update_job_ = update;
MockFrontend* frontend = MakeMockFrontend();
AppCacheHost* host = MakeHost(frontend);
host->new_master_entry_url_ = MockHttpServer::GetMockUrl("files/blah");
update->StartUpdate(host, host->new_master_entry_url_);
// Set up checks for when update job finishes.
do_checks_after_update_finished_ = true;
expect_group_obsolete_ = false;
expect_group_has_cache_ = false;
frontend->AddExpectedEvent(
blink::mojom::AppCacheEventID::APPCACHE_CHECKING_EVENT);
frontend->AddExpectedEvent(
blink::mojom::AppCacheEventID::APPCACHE_ERROR_EVENT);
WaitForUpdateToFinish();
}
void MasterEntryFailUrlFetchTest() {
MakeService();
group_ = base::MakeRefCounted<AppCacheGroup>(
service_->storage(),
MockHttpServer::GetMockUrl("files/manifest-fb-404"), 111);
AppCacheUpdateJob* update =
new AppCacheUpdateJob(service_.get(), group_.get());
group_->update_job_ = update;
MockFrontend* frontend = MakeMockFrontend();
frontend->SetIgnoreProgressEvents(true);
AppCacheHost* host = MakeHost(frontend);
host->new_master_entry_url_ = MockHttpServer::GetMockUrl("files/explicit1");
update->StartUpdate(host, host->new_master_entry_url_);
// Set up checks for when update job finishes.
do_checks_after_update_finished_ = true;
expect_group_obsolete_ = false;
expect_group_has_cache_ = false; // 404 fallback url is cache failure
frontend->AddExpectedEvent(
blink::mojom::AppCacheEventID::APPCACHE_CHECKING_EVENT);
frontend->AddExpectedEvent(
blink::mojom::AppCacheEventID::APPCACHE_DOWNLOADING_EVENT);
frontend->AddExpectedEvent(
blink::mojom::AppCacheEventID::APPCACHE_ERROR_EVENT);
WaitForUpdateToFinish();
}
void MasterEntryAllFailTest() {
MakeService();
group_ = base::MakeRefCounted<AppCacheGroup>(
service_->storage(), MockHttpServer::GetMockUrl("files/manifest1"),
111);
AppCacheUpdateJob* update =
new AppCacheUpdateJob(service_.get(), group_.get());
group_->update_job_ = update;
MockFrontend* frontend1 = MakeMockFrontend();
frontend1->SetIgnoreProgressEvents(true);
AppCacheHost* host1 = MakeHost(frontend1);
host1->new_master_entry_url_ =
MockHttpServer::GetMockUrl("files/nosuchfile");
update->StartUpdate(host1, host1->new_master_entry_url_);
MockFrontend* frontend2 = MakeMockFrontend();
frontend2->SetIgnoreProgressEvents(true);
AppCacheHost* host2 = MakeHost(frontend2);
host2->new_master_entry_url_ =
MockHttpServer::GetMockUrl("files/servererror");
update->StartUpdate(host2, host2->new_master_entry_url_);
// Set up checks for when update job finishes.
do_checks_after_update_finished_ = true;
expect_group_obsolete_ = false;
expect_group_has_cache_ = false; // all pending masters failed
frontend1->AddExpectedEvent(
blink::mojom::AppCacheEventID::APPCACHE_CHECKING_EVENT);
frontend1->AddExpectedEvent(
blink::mojom::AppCacheEventID::APPCACHE_DOWNLOADING_EVENT);
frontend1->AddExpectedEvent(
blink::mojom::AppCacheEventID::APPCACHE_ERROR_EVENT);
frontend2->AddExpectedEvent(
blink::mojom::AppCacheEventID::APPCACHE_CHECKING_EVENT);
frontend2->AddExpectedEvent(
blink::mojom::AppCacheEventID::APPCACHE_DOWNLOADING_EVENT);
frontend2->AddExpectedEvent(
blink::mojom::AppCacheEventID::APPCACHE_ERROR_EVENT);
WaitForUpdateToFinish();
}
void UpgradeMasterEntryAllFailTest() {
MakeService();
group_ = base::MakeRefCounted<AppCacheGroup>(
service_->storage(), MockHttpServer::GetMockUrl("files/manifest1"),
111);
AppCacheUpdateJob* update =
new AppCacheUpdateJob(service_.get(), group_.get());
group_->update_job_ = update;
AppCache* cache = MakeCacheForGroup(service_->storage()->NewCacheId(), 42);
MockFrontend* frontend1 = MakeMockFrontend();
AppCacheHost* host1 = MakeHost(frontend1);
host1->AssociateCompleteCache(cache);
MockFrontend* frontend2 = MakeMockFrontend();
frontend2->SetIgnoreProgressEvents(true);
AppCacheHost* host2 = MakeHost(frontend2);
host2->new_master_entry_url_ =
MockHttpServer::GetMockUrl("files/nosuchfile");
update->StartUpdate(host2, host2->new_master_entry_url_);
MockFrontend* frontend3 = MakeMockFrontend();
frontend3->SetIgnoreProgressEvents(true);
AppCacheHost* host3 = MakeHost(frontend3);
host3->new_master_entry_url_ =
MockHttpServer::GetMockUrl("files/servererror");
update->StartUpdate(host3, host3->new_master_entry_url_);
// Set up checks for when update job finishes.
do_checks_after_update_finished_ = true;
expect_group_obsolete_ = false;
expect_group_has_cache_ = true;
expect_old_cache_ = cache;
tested_manifest_ = MANIFEST1;
frontend1->AddExpectedEvent(
blink::mojom::AppCacheEventID::APPCACHE_CHECKING_EVENT);
frontend1->AddExpectedEvent(
blink::mojom::AppCacheEventID::APPCACHE_DOWNLOADING_EVENT);
frontend1->AddExpectedEvent(
blink::mojom::AppCacheEventID::APPCACHE_PROGRESS_EVENT);
frontend1->AddExpectedEvent(
blink::mojom::AppCacheEventID::APPCACHE_PROGRESS_EVENT);
frontend1->AddExpectedEvent(
blink::mojom::AppCacheEventID::APPCACHE_PROGRESS_EVENT); // final
frontend1->AddExpectedEvent(
blink::mojom::AppCacheEventID::APPCACHE_UPDATE_READY_EVENT);
frontend2->AddExpectedEvent(
blink::mojom::AppCacheEventID::APPCACHE_DOWNLOADING_EVENT);
frontend2->AddExpectedEvent(
blink::mojom::AppCacheEventID::APPCACHE_ERROR_EVENT);
frontend3->AddExpectedEvent(
blink::mojom::AppCacheEventID::APPCACHE_CHECKING_EVENT);
frontend3->AddExpectedEvent(
blink::mojom::AppCacheEventID::APPCACHE_DOWNLOADING_EVENT);
frontend3->AddExpectedEvent(
blink::mojom::AppCacheEventID::APPCACHE_ERROR_EVENT);
WaitForUpdateToFinish();
}
void MasterEntrySomeFailTest() {
MakeService();
group_ = base::MakeRefCounted<AppCacheGroup>(
service_->storage(), MockHttpServer::GetMockUrl("files/manifest1"),
111);
AppCacheUpdateJob* update =
new AppCacheUpdateJob(service_.get(), group_.get());
group_->update_job_ = update;
MockFrontend* frontend1 = MakeMockFrontend();
frontend1->SetIgnoreProgressEvents(true);
AppCacheHost* host1 = MakeHost(frontend1);
host1->new_master_entry_url_ =
MockHttpServer::GetMockUrl("files/nosuchfile");
update->StartUpdate(host1, host1->new_master_entry_url_);
MockFrontend* frontend2 = MakeMockFrontend();
AppCacheHost* host2 = MakeHost(frontend2);
host2->new_master_entry_url_ =
MockHttpServer::GetMockUrl("files/explicit2");
update->StartUpdate(host2, host2->new_master_entry_url_);
// Set up checks for when update job finishes.
do_checks_after_update_finished_ = true;
expect_group_obsolete_ = false;
expect_group_has_cache_ = true; // as long as one pending master succeeds
tested_manifest_ = MANIFEST1;
expect_extra_entries_.insert(AppCache::EntryMap::value_type(
MockHttpServer::GetMockUrl("files/explicit2"),
AppCacheEntry(AppCacheEntry::MASTER)));
frontend1->AddExpectedEvent(
blink::mojom::AppCacheEventID::APPCACHE_CHECKING_EVENT);
frontend1->AddExpectedEvent(
blink::mojom::AppCacheEventID::APPCACHE_DOWNLOADING_EVENT);
frontend1->AddExpectedEvent(
blink::mojom::AppCacheEventID::APPCACHE_ERROR_EVENT);
frontend2->AddExpectedEvent(
blink::mojom::AppCacheEventID::APPCACHE_CHECKING_EVENT);
frontend2->AddExpectedEvent(
blink::mojom::AppCacheEventID::APPCACHE_DOWNLOADING_EVENT);
frontend2->AddExpectedEvent(
blink::mojom::AppCacheEventID::APPCACHE_PROGRESS_EVENT);
frontend2->AddExpectedEvent(
blink::mojom::AppCacheEventID::APPCACHE_PROGRESS_EVENT);
frontend2->AddExpectedEvent(
blink::mojom::AppCacheEventID::APPCACHE_PROGRESS_EVENT); // final
frontend2->AddExpectedEvent(
blink::mojom::AppCacheEventID::APPCACHE_CACHED_EVENT);
WaitForUpdateToFinish();
}
void UpgradeMasterEntrySomeFailTest() {
MakeService();
group_ = base::MakeRefCounted<AppCacheGroup>(
service_->storage(), MockHttpServer::GetMockUrl("files/manifest1"),
111);
AppCacheUpdateJob* update =
new AppCacheUpdateJob(service_.get(), group_.get());
group_->update_job_ = update;
AppCache* cache = MakeCacheForGroup(service_->storage()->NewCacheId(), 42);
MockFrontend* frontend1 = MakeMockFrontend();
AppCacheHost* host1 = MakeHost(frontend1);
host1->AssociateCompleteCache(cache);
MockFrontend* frontend2 = MakeMockFrontend();
frontend2->SetIgnoreProgressEvents(true);
AppCacheHost* host2 = MakeHost(frontend2);
host2->new_master_entry_url_ =
MockHttpServer::GetMockUrl("files/nosuchfile");
update->StartUpdate(host2, host2->new_master_entry_url_);
MockFrontend* frontend3 = MakeMockFrontend();
AppCacheHost* host3 = MakeHost(frontend3);
host3->new_master_entry_url_ =
MockHttpServer::GetMockUrl("files/explicit2");
update->StartUpdate(host3, host3->new_master_entry_url_);
// Set up checks for when update job finishes.
do_checks_after_update_finished_ = true;
expect_group_obsolete_ = false;
expect_group_has_cache_ = true;
expect_old_cache_ = cache;
tested_manifest_ = MANIFEST1;
expect_extra_entries_.insert(AppCache::EntryMap::value_type(
MockHttpServer::GetMockUrl("files/explicit2"),
AppCacheEntry(AppCacheEntry::MASTER)));
frontend1->AddExpectedEvent(
blink::mojom::AppCacheEventID::APPCACHE_CHECKING_EVENT);
frontend1->AddExpectedEvent(
blink::mojom::AppCacheEventID::APPCACHE_DOWNLOADING_EVENT);
frontend1->AddExpectedEvent(
blink::mojom::AppCacheEventID::APPCACHE_PROGRESS_EVENT);
frontend1->AddExpectedEvent(
blink::mojom::AppCacheEventID::APPCACHE_PROGRESS_EVENT);
frontend1->AddExpectedEvent(
blink::mojom::AppCacheEventID::APPCACHE_PROGRESS_EVENT); // final
frontend1->AddExpectedEvent(
blink::mojom::AppCacheEventID::APPCACHE_UPDATE_READY_EVENT);
frontend2->AddExpectedEvent(
blink::mojom::AppCacheEventID::APPCACHE_DOWNLOADING_EVENT);
frontend2->AddExpectedEvent(
blink::mojom::AppCacheEventID::APPCACHE_ERROR_EVENT);
frontend3->AddExpectedEvent(
blink::mojom::AppCacheEventID::APPCACHE_CHECKING_EVENT);
frontend3->AddExpectedEvent(
blink::mojom::AppCacheEventID::APPCACHE_DOWNLOADING_EVENT);
frontend3->AddExpectedEvent(
blink::mojom::AppCacheEventID::APPCACHE_PROGRESS_EVENT);
frontend3->AddExpectedEvent(
blink::mojom::AppCacheEventID::APPCACHE_PROGRESS_EVENT);
frontend3->AddExpectedEvent(
blink::mojom::AppCacheEventID::APPCACHE_PROGRESS_EVENT); // final
frontend3->AddExpectedEvent(
blink::mojom::AppCacheEventID::APPCACHE_UPDATE_READY_EVENT);
WaitForUpdateToFinish();
}
void MasterEntryNoUpdateTest() {
MakeService();
group_ = base::MakeRefCounted<AppCacheGroup>(
service_->storage(), MockHttpServer::GetMockUrl("files/notmodified"),
111);
AppCacheUpdateJob* update =
new AppCacheUpdateJob(service_.get(), group_.get());
group_->update_job_ = update;
AppCache* cache = MakeCacheForGroup(1, 111);
MockFrontend* frontend1 = MakeMockFrontend();
AppCacheHost* host1 = MakeHost(frontend1);
host1->AssociateCompleteCache(cache);
// Give cache an existing entry that can also be fetched.
cache->AddEntry(MockHttpServer::GetMockUrl("files/explicit2"),
AppCacheEntry(AppCacheEntry::EXPLICIT, 222));
// Reset the update time to null so we can verify it gets
// modified in this test case by the UpdateJob.
cache->set_update_time(base::Time());
MockFrontend* frontend2 = MakeMockFrontend();
AppCacheHost* host2 = MakeHost(frontend2);
host2->new_master_entry_url_ =
MockHttpServer::GetMockUrl("files/explicit1");
update->StartUpdate(host2, host2->new_master_entry_url_);
MockFrontend* frontend3 = MakeMockFrontend();
AppCacheHost* host3 = MakeHost(frontend3);
host3->new_master_entry_url_ =
MockHttpServer::GetMockUrl("files/explicit2");
update->StartUpdate(host3, host3->new_master_entry_url_);
// Set up checks for when update job finishes.
do_checks_after_update_finished_ = true;
expect_group_obsolete_ = false;
expect_group_has_cache_ = true;
expect_newest_cache_ = cache; // newest cache still the same cache
expect_non_null_update_time_ = true;
tested_manifest_ = PENDING_MASTER_NO_UPDATE;
frontend1->AddExpectedEvent(
blink::mojom::AppCacheEventID::APPCACHE_CHECKING_EVENT);
frontend1->AddExpectedEvent(
blink::mojom::AppCacheEventID::APPCACHE_NO_UPDATE_EVENT);
frontend2->AddExpectedEvent(
blink::mojom::AppCacheEventID::APPCACHE_NO_UPDATE_EVENT);
frontend3->AddExpectedEvent(
blink::mojom::AppCacheEventID::APPCACHE_CHECKING_EVENT);
frontend3->AddExpectedEvent(
blink::mojom::AppCacheEventID::APPCACHE_NO_UPDATE_EVENT);
WaitForUpdateToFinish();
}
void StartUpdateMidCacheAttemptTest() {
MakeService();
group_ = base::MakeRefCounted<AppCacheGroup>(
service_->storage(), MockHttpServer::GetMockUrl("files/manifest1"),
service_->storage()->NewGroupId());
AppCacheUpdateJob* update =
new AppCacheUpdateJob(service_.get(), group_.get());
group_->update_job_ = update;
MockFrontend* frontend1 = MakeMockFrontend();
AppCacheHost* host1 = MakeHost(frontend1);
host1->new_master_entry_url_ =
MockHttpServer::GetMockUrl("files/explicit2");
update->StartUpdate(host1, host1->new_master_entry_url_);
// Set up additional updates to be started while update is in progress.
MockFrontend* frontend2 = MakeMockFrontend();
frontend2->SetIgnoreProgressEvents(true);
AppCacheHost* host2 = MakeHost(frontend2);
host2->new_master_entry_url_ =
MockHttpServer::GetMockUrl("files/nosuchfile");
MockFrontend* frontend3 = MakeMockFrontend();
AppCacheHost* host3 = MakeHost(frontend3);
host3->new_master_entry_url_ =
MockHttpServer::GetMockUrl("files/explicit1");
MockFrontend* frontend4 = MakeMockFrontend();
AppCacheHost* host4 = MakeHost(frontend4);
host4->new_master_entry_url_ =
MockHttpServer::GetMockUrl("files/explicit2");
MockFrontend* frontend5 = MakeMockFrontend();
AppCacheHost* host5 = MakeHost(frontend5); // no master entry url
frontend1->TriggerAdditionalUpdates(
blink::mojom::AppCacheEventID::APPCACHE_DOWNLOADING_EVENT, update);
frontend1->AdditionalUpdateHost(host2); // fetch will fail
frontend1->AdditionalUpdateHost(host3); // same as an explicit entry
frontend1->AdditionalUpdateHost(host4); // same as another master entry
frontend1->AdditionalUpdateHost(nullptr); // no host
frontend1->AdditionalUpdateHost(host5); // no master entry url
// Set up checks for when update job finishes.
do_checks_after_update_finished_ = true;
expect_group_obsolete_ = false;
expect_group_has_cache_ = true;
tested_manifest_ = MANIFEST1;
expect_extra_entries_.insert(AppCache::EntryMap::value_type(
MockHttpServer::GetMockUrl("files/explicit2"),
AppCacheEntry(AppCacheEntry::MASTER)));
frontend1->AddExpectedEvent(
blink::mojom::AppCacheEventID::APPCACHE_CHECKING_EVENT);
frontend1->AddExpectedEvent(
blink::mojom::AppCacheEventID::APPCACHE_DOWNLOADING_EVENT);
frontend1->AddExpectedEvent(
blink::mojom::AppCacheEventID::APPCACHE_PROGRESS_EVENT);
frontend1->AddExpectedEvent(
blink::mojom::AppCacheEventID::APPCACHE_PROGRESS_EVENT);
frontend1->AddExpectedEvent(
blink::mojom::AppCacheEventID::APPCACHE_PROGRESS_EVENT); // final
frontend1->AddExpectedEvent(
blink::mojom::AppCacheEventID::APPCACHE_CACHED_EVENT);
frontend2->AddExpectedEvent(
blink::mojom::AppCacheEventID::APPCACHE_CHECKING_EVENT);
frontend2->AddExpectedEvent(
blink::mojom::AppCacheEventID::APPCACHE_DOWNLOADING_EVENT);
frontend2->AddExpectedEvent(
blink::mojom::AppCacheEventID::APPCACHE_ERROR_EVENT);
frontend3->AddExpectedEvent(
blink::mojom::AppCacheEventID::APPCACHE_CHECKING_EVENT);
frontend3->AddExpectedEvent(
blink::mojom::AppCacheEventID::APPCACHE_DOWNLOADING_EVENT);
frontend3->AddExpectedEvent(
blink::mojom::AppCacheEventID::APPCACHE_PROGRESS_EVENT);
frontend3->AddExpectedEvent(
blink::mojom::AppCacheEventID::APPCACHE_PROGRESS_EVENT);
frontend3->AddExpectedEvent(
blink::mojom::AppCacheEventID::APPCACHE_PROGRESS_EVENT); // final
frontend3->AddExpectedEvent(
blink::mojom::AppCacheEventID::APPCACHE_CACHED_EVENT);
frontend4->AddExpectedEvent(
blink::mojom::AppCacheEventID::APPCACHE_CHECKING_EVENT);
frontend4->AddExpectedEvent(
blink::mojom::AppCacheEventID::APPCACHE_DOWNLOADING_EVENT);
frontend4->AddExpectedEvent(
blink::mojom::AppCacheEventID::APPCACHE_PROGRESS_EVENT);
frontend4->AddExpectedEvent(
blink::mojom::AppCacheEventID::APPCACHE_PROGRESS_EVENT);
frontend4->AddExpectedEvent(
blink::mojom::AppCacheEventID::APPCACHE_PROGRESS_EVENT); // final
frontend4->AddExpectedEvent(
blink::mojom::AppCacheEventID::APPCACHE_CACHED_EVENT);
// Host 5 is not associated with cache so no progress/cached events.
frontend5->AddExpectedEvent(
blink::mojom::AppCacheEventID::APPCACHE_CHECKING_EVENT);
frontend5->AddExpectedEvent(
blink::mojom::AppCacheEventID::APPCACHE_DOWNLOADING_EVENT);
WaitForUpdateToFinish();
}
void StartUpdateMidNoUpdateTest() {
MakeService();
group_ = base::MakeRefCounted<AppCacheGroup>(
service_->storage(), MockHttpServer::GetMockUrl("files/notmodified"),
service_->storage()->NewGroupId());
AppCacheUpdateJob* update =
new AppCacheUpdateJob(service_.get(), group_.get());
group_->update_job_ = update;
AppCache* cache = MakeCacheForGroup(1, 111);
MockFrontend* frontend1 = MakeMockFrontend();
AppCacheHost* host1 = MakeHost(frontend1);
host1->AssociateCompleteCache(cache);
// Give cache an existing entry.
cache->AddEntry(MockHttpServer::GetMockUrl("files/explicit2"),
AppCacheEntry(AppCacheEntry::EXPLICIT, 222));
// Start update with a pending master entry that will fail to give us an
// event to trigger other updates.
MockFrontend* frontend2 = MakeMockFrontend();
AppCacheHost* host2 = MakeHost(frontend2);
host2->new_master_entry_url_ =
MockHttpServer::GetMockUrl("files/nosuchfile");
update->StartUpdate(host2, host2->new_master_entry_url_);
// Set up additional updates to be started while update is in progress.
MockFrontend* frontend3 = MakeMockFrontend();
AppCacheHost* host3 = MakeHost(frontend3);
host3->new_master_entry_url_ =
MockHttpServer::GetMockUrl("files/explicit1");
MockFrontend* frontend4 = MakeMockFrontend();
AppCacheHost* host4 = MakeHost(frontend4); // no master entry url
MockFrontend* frontend5 = MakeMockFrontend();
AppCacheHost* host5 = MakeHost(frontend5);
host5->new_master_entry_url_ =
MockHttpServer::GetMockUrl("files/explicit2"); // existing entry
MockFrontend* frontend6 = MakeMockFrontend();
AppCacheHost* host6 = MakeHost(frontend6);
host6->new_master_entry_url_ =
MockHttpServer::GetMockUrl("files/explicit1");
frontend2->TriggerAdditionalUpdates(
blink::mojom::AppCacheEventID::APPCACHE_ERROR_EVENT, update);
frontend2->AdditionalUpdateHost(host3);
frontend2->AdditionalUpdateHost(nullptr); // no host
frontend2->AdditionalUpdateHost(host4); // no master entry url
frontend2->AdditionalUpdateHost(host5); // same as existing cache entry
frontend2->AdditionalUpdateHost(host6); // same as another master entry
// Set up checks for when update job finishes.
do_checks_after_update_finished_ = true;
expect_group_obsolete_ = false;
expect_group_has_cache_ = true;
expect_newest_cache_ = cache; // newest cache unaffected by update
tested_manifest_ = PENDING_MASTER_NO_UPDATE;
// prior associated host
frontend1->AddExpectedEvent(
blink::mojom::AppCacheEventID::APPCACHE_CHECKING_EVENT);
frontend1->AddExpectedEvent(
blink::mojom::AppCacheEventID::APPCACHE_NO_UPDATE_EVENT);
frontend2->AddExpectedEvent(
blink::mojom::AppCacheEventID::APPCACHE_ERROR_EVENT);
frontend3->AddExpectedEvent(
blink::mojom::AppCacheEventID::APPCACHE_CHECKING_EVENT);
frontend3->AddExpectedEvent(
blink::mojom::AppCacheEventID::APPCACHE_NO_UPDATE_EVENT);
// unassociated w/cache
frontend4->AddExpectedEvent(
blink::mojom::AppCacheEventID::APPCACHE_CHECKING_EVENT);
frontend5->AddExpectedEvent(
blink::mojom::AppCacheEventID::APPCACHE_CHECKING_EVENT);
frontend5->AddExpectedEvent(
blink::mojom::AppCacheEventID::APPCACHE_NO_UPDATE_EVENT);
frontend6->AddExpectedEvent(
blink::mojom::AppCacheEventID::APPCACHE_CHECKING_EVENT);
frontend6->AddExpectedEvent(
blink::mojom::AppCacheEventID::APPCACHE_NO_UPDATE_EVENT);
WaitForUpdateToFinish();
}
void StartUpdateMidDownloadTest() {
MakeService();
group_ = base::MakeRefCounted<AppCacheGroup>(
service_->storage(), MockHttpServer::GetMockUrl("files/manifest1"),
111);
AppCacheUpdateJob* update =
new AppCacheUpdateJob(service_.get(), group_.get());
group_->update_job_ = update;
AppCache* cache = MakeCacheForGroup(service_->storage()->NewCacheId(), 42);
MockFrontend* frontend1 = MakeMockFrontend();
AppCacheHost* host1 = MakeHost(frontend1);
host1->AssociateCompleteCache(cache);
update->StartUpdate(nullptr, GURL());
// Set up additional updates to be started while update is in progress.
MockFrontend* frontend2 = MakeMockFrontend();
AppCacheHost* host2 = MakeHost(frontend2);
host2->new_master_entry_url_ =
MockHttpServer::GetMockUrl("files/explicit1");
MockFrontend* frontend3 = MakeMockFrontend();
AppCacheHost* host3 = MakeHost(frontend3);
host3->new_master_entry_url_ =
MockHttpServer::GetMockUrl("files/explicit2");
MockFrontend* frontend4 = MakeMockFrontend();
AppCacheHost* host4 = MakeHost(frontend4); // no master entry url
MockFrontend* frontend5 = MakeMockFrontend();
AppCacheHost* host5 = MakeHost(frontend5);
host5->new_master_entry_url_ =
MockHttpServer::GetMockUrl("files/explicit2");
frontend1->TriggerAdditionalUpdates(
blink::mojom::AppCacheEventID::APPCACHE_PROGRESS_EVENT, update);
frontend1->AdditionalUpdateHost(host2); // same as entry in manifest
frontend1->AdditionalUpdateHost(nullptr); // no host
frontend1->AdditionalUpdateHost(host3); // new master entry
frontend1->AdditionalUpdateHost(host4); // no master entry url
frontend1->AdditionalUpdateHost(host5); // same as another master entry
// Set up checks for when update job finishes.
do_checks_after_update_finished_ = true;
expect_group_obsolete_ = false;
expect_group_has_cache_ = true;
tested_manifest_ = MANIFEST1;
expect_extra_entries_.insert(AppCache::EntryMap::value_type(
MockHttpServer::GetMockUrl("files/explicit2"),
AppCacheEntry(AppCacheEntry::MASTER)));
// prior associated host
frontend1->AddExpectedEvent(
blink::mojom::AppCacheEventID::APPCACHE_CHECKING_EVENT);
frontend1->AddExpectedEvent(
blink::mojom::AppCacheEventID::APPCACHE_DOWNLOADING_EVENT);
frontend1->AddExpectedEvent(
blink::mojom::AppCacheEventID::APPCACHE_PROGRESS_EVENT);
frontend1->AddExpectedEvent(
blink::mojom::AppCacheEventID::APPCACHE_PROGRESS_EVENT);
frontend1->AddExpectedEvent(
blink::mojom::AppCacheEventID::APPCACHE_PROGRESS_EVENT); // final
frontend1->AddExpectedEvent(
blink::mojom::AppCacheEventID::APPCACHE_UPDATE_READY_EVENT);
frontend2->AddExpectedEvent(
blink::mojom::AppCacheEventID::APPCACHE_CHECKING_EVENT);
frontend2->AddExpectedEvent(
blink::mojom::AppCacheEventID::APPCACHE_DOWNLOADING_EVENT);
frontend2->AddExpectedEvent(
blink::mojom::AppCacheEventID::APPCACHE_PROGRESS_EVENT);
frontend2->AddExpectedEvent(
blink::mojom::AppCacheEventID::APPCACHE_PROGRESS_EVENT); // final
frontend2->AddExpectedEvent(
blink::mojom::AppCacheEventID::APPCACHE_UPDATE_READY_EVENT);
frontend3->AddExpectedEvent(
blink::mojom::AppCacheEventID::APPCACHE_CHECKING_EVENT);
frontend3->AddExpectedEvent(
blink::mojom::AppCacheEventID::APPCACHE_DOWNLOADING_EVENT);
frontend3->AddExpectedEvent(
blink::mojom::AppCacheEventID::APPCACHE_PROGRESS_EVENT);
frontend3->AddExpectedEvent(
blink::mojom::AppCacheEventID::APPCACHE_PROGRESS_EVENT); // final
frontend3->AddExpectedEvent(
blink::mojom::AppCacheEventID::APPCACHE_UPDATE_READY_EVENT);
// unassociated w/cache
frontend4->AddExpectedEvent(
blink::mojom::AppCacheEventID::APPCACHE_CHECKING_EVENT);
frontend4->AddExpectedEvent(
blink::mojom::AppCacheEventID::APPCACHE_DOWNLOADING_EVENT);
frontend5->AddExpectedEvent(
blink::mojom::AppCacheEventID::APPCACHE_CHECKING_EVENT);
frontend5->AddExpectedEvent(
blink::mojom::AppCacheEventID::APPCACHE_DOWNLOADING_EVENT);
frontend5->AddExpectedEvent(
blink::mojom::AppCacheEventID::APPCACHE_PROGRESS_EVENT);
frontend5->AddExpectedEvent(
blink::mojom::AppCacheEventID::APPCACHE_PROGRESS_EVENT); // final
frontend5->AddExpectedEvent(
blink::mojom::AppCacheEventID::APPCACHE_UPDATE_READY_EVENT);
WaitForUpdateToFinish();
}
void QueueMasterEntryTest() {
MakeService();
group_ = base::MakeRefCounted<AppCacheGroup>(
service_->storage(), MockHttpServer::GetMockUrl("files/manifest1"),
111);
AppCacheUpdateJob* update =
new AppCacheUpdateJob(service_.get(), group_.get());
group_->update_job_ = update;
// Pretend update job has been running and is about to terminate.
group_->update_status_ = AppCacheGroup::DOWNLOADING;
update->internal_state_ = AppCacheUpdateJobState::REFETCH_MANIFEST;
EXPECT_TRUE(update->IsTerminating());
// Start an update. Should be queued.
MockFrontend* frontend = MakeMockFrontend();
AppCacheHost* host = MakeHost(frontend);
host->new_master_entry_url_ = MockHttpServer::GetMockUrl("files/explicit2");
update->StartUpdate(host, host->new_master_entry_url_);
EXPECT_TRUE(update->pending_master_entries_.empty());
EXPECT_FALSE(group_->queued_updates_.empty());
// Delete update, causing it to finish, which should trigger a new update
// for the queued host and master entry after a delay.
delete update;
EXPECT_FALSE(group_->restart_update_task_.IsCancelled());
// Set up checks for when queued update job finishes.
do_checks_after_update_finished_ = true;
expect_group_obsolete_ = false;
expect_group_has_cache_ = true;
tested_manifest_ = MANIFEST1;
expect_extra_entries_.insert(AppCache::EntryMap::value_type(
host->new_master_entry_url_, AppCacheEntry(AppCacheEntry::MASTER)));
frontend->AddExpectedEvent(
blink::mojom::AppCacheEventID::APPCACHE_CHECKING_EVENT);
frontend->AddExpectedEvent(
blink::mojom::AppCacheEventID::APPCACHE_DOWNLOADING_EVENT);
frontend->AddExpectedEvent(
blink::mojom::AppCacheEventID::APPCACHE_PROGRESS_EVENT);
frontend->AddExpectedEvent(
blink::mojom::AppCacheEventID::APPCACHE_PROGRESS_EVENT);
frontend->AddExpectedEvent(
blink::mojom::AppCacheEventID::APPCACHE_PROGRESS_EVENT); // final
frontend->AddExpectedEvent(
blink::mojom::AppCacheEventID::APPCACHE_CACHED_EVENT);
// Group status will be blink::mojom::AppCacheStatus::APPCACHE_STATUS_IDLE
// so cannot call WaitForUpdateToFinish.
group_->AddUpdateObserver(this);
}
void VerifyHeadersAndDeleteUpdate(AppCacheUpdateJob* update) {
for (const auto& kvp : http_headers_request_test_jobs_)
kvp.second->Verify(kvp.first);
delete update;
}
void IfModifiedSinceTestCache() {
MakeService();
group_ = base::MakeRefCounted<AppCacheGroup>(
service_->storage(), GURL("http://headertest"), 111);
AppCacheUpdateJob* update =
new AppCacheUpdateJob(service_.get(), group_.get());
group_->update_job_ = update;
// First test against a cache attempt. Will start manifest fetch
// synchronously.
http_headers_request_test_jobs_.emplace(
group_->manifest_url(), std::make_unique<HttpHeadersRequestTestJob>(
std::string(), std::string()));
MockFrontend mock_frontend;
const int kMockProcessId1 = 1;
AppCacheHost host(
/*host_id=*/base::UnguessableToken::Create(), kMockProcessId1,
/*render_frame_id=*/1,
ChildProcessSecurityPolicyImpl::GetInstance()->CreateHandle(
kMockProcessId1),
mojo::NullRemote(), service_.get());
host.set_frontend_for_testing(&mock_frontend);
update->StartUpdate(&host, GURL());
// We need to wait for the URL load requests to make it to the
// URLLoaderInterceptor.
base::ThreadTaskRunnerHandle::Get()->PostTask(
FROM_HERE,
base::BindOnce(&AppCacheUpdateJobTest::VerifyHeadersAndDeleteUpdate,
base::Unretained(this), update));
TriggerTestComplete();
}
void IfModifiedTestRefetch() {
// Now simulate a refetch manifest request. Will start fetch request
// synchronously.
const char data[] =
"HTTP/1.1 200 OK\0"
"\0";
auto response_info = std::make_unique<net::HttpResponseInfo>();
response_info->headers = base::MakeRefCounted<net::HttpResponseHeaders>(
std::string(data, base::size(data)));
MakeService();
group_ = base::MakeRefCounted<AppCacheGroup>(
service_->storage(), GURL("http://headertest"), 111);
http_headers_request_test_jobs_.emplace(
group_->manifest_url(), std::make_unique<HttpHeadersRequestTestJob>(
std::string(), std::string()));
AppCacheUpdateJob* update =
new AppCacheUpdateJob(service_.get(), group_.get());
group_->update_job_ = update;
group_->update_status_ = AppCacheGroup::DOWNLOADING;
update->manifest_response_info_ = std::move(response_info);
update->internal_state_ = AppCacheUpdateJobState::REFETCH_MANIFEST;
update->RefetchManifest();
// We need to wait for the URL load requests to make it to the
// URLLoaderInterceptor.
base::ThreadTaskRunnerHandle::Get()->PostTask(
FROM_HERE,
base::BindOnce(&AppCacheUpdateJobTest::VerifyHeadersAndDeleteUpdate,
base::Unretained(this), update));
TriggerTestComplete();
}
void IfModifiedTestLastModified() {
// Change the headers to include a Last-Modified header. Manifest refetch
// should include If-Modified-Since header.
const char data2[] =
"HTTP/1.1 200 OK\0"
"Last-Modified: Sat, 29 Oct 1994 19:43:31 GMT\0"
"\0";
auto response_info = std::make_unique<net::HttpResponseInfo>();
response_info->headers = base::MakeRefCounted<net::HttpResponseHeaders>(
std::string(data2, base::size(data2)));
MakeService();
group_ = base::MakeRefCounted<AppCacheGroup>(
service_->storage(), GURL("http://headertest"), 111);
http_headers_request_test_jobs_.emplace(
group_->manifest_url(),
std::make_unique<HttpHeadersRequestTestJob>(
"Sat, 29 Oct 1994 19:43:31 GMT", std::string()));
AppCacheUpdateJob* update =
new AppCacheUpdateJob(service_.get(), group_.get());
group_->update_job_ = update;
group_->update_status_ = AppCacheGroup::DOWNLOADING;
update->manifest_response_info_ = std::move(response_info);
update->internal_state_ = AppCacheUpdateJobState::REFETCH_MANIFEST;
update->RefetchManifest();
// We need to wait for the URL load requests to make it to the
// URLLoaderInterceptor.
base::ThreadTaskRunnerHandle::Get()->PostTask(
FROM_HERE,
base::BindOnce(&AppCacheUpdateJobTest::VerifyHeadersAndDeleteUpdate,
base::Unretained(this), update));
TriggerTestComplete();
}
// AppCaches built with manifest parser version 0 should update without
// conditional request headers to force the server to send a 200 response
// back to the client rather than 304. This test ensures that, when the cache
// has a response info with cached Last-Modified headers, the request does not
// include an If-Modified-Since conditioanl header.
void IfModifiedSinceUpgradeParserVersion0Test() {
MakeService();
group_ = base::MakeRefCounted<AppCacheGroup>(
service_->storage(), MockHttpServer::GetMockUrl("files/manifest1"),
111);
http_headers_request_test_jobs_.emplace(
group_->manifest_url(), std::make_unique<HttpHeadersRequestTestJob>(
std::string(), std::string(),
/*headers_allowed=*/false));
AppCacheUpdateJob* update =
new AppCacheUpdateJob(service_.get(), group_.get());
group_->update_job_ = update;
// Give the newest cache a manifest entry that is in storage.
response_writer_ =
service_->storage()->CreateResponseWriter(group_->manifest_url());
AppCache* cache = MakeCacheForGroup(service_->storage()->NewCacheId(),
response_writer_->response_id());
cache->set_manifest_parser_version(0);
MockFrontend* frontend = MakeMockFrontend();
AppCacheHost* host = MakeHost(frontend);
host->AssociateCompleteCache(cache);
// Set up checks for when update job finishes.
do_checks_after_update_finished_ = true;
expect_group_obsolete_ = false;
expect_group_has_cache_ = true;
expect_old_cache_ = cache;
tested_manifest_ = MANIFEST1;
frontend->AddExpectedEvent(
blink::mojom::AppCacheEventID::APPCACHE_CHECKING_EVENT);
frontend->AddExpectedEvent(
blink::mojom::AppCacheEventID::APPCACHE_DOWNLOADING_EVENT);
frontend->AddExpectedEvent(
blink::mojom::AppCacheEventID::APPCACHE_PROGRESS_EVENT);
frontend->AddExpectedEvent(
blink::mojom::AppCacheEventID::APPCACHE_PROGRESS_EVENT);
frontend->AddExpectedEvent(
blink::mojom::AppCacheEventID::APPCACHE_PROGRESS_EVENT); // final
frontend->AddExpectedEvent(
blink::mojom::AppCacheEventID::APPCACHE_UPDATE_READY_EVENT);
// Seed storage with expected manifest response info that will cause
// an If-Modified-Since header to be put in the manifest fetch request.
const char data[] =
"HTTP/1.1 200 OK\0"
"Last-Modified: Sat, 29 Oct 1994 19:43:31 GMT\0"
"\0";
scoped_refptr<net::HttpResponseHeaders> headers =
base::MakeRefCounted<net::HttpResponseHeaders>(
std::string(data, base::size(data)));
std::unique_ptr<net::HttpResponseInfo> response_info =
std::make_unique<net::HttpResponseInfo>();
response_info->headers = std::move(headers);
scoped_refptr<HttpResponseInfoIOBuffer> io_buffer =
base::MakeRefCounted<HttpResponseInfoIOBuffer>(
std::move(response_info));
response_writer_->WriteInfo(
io_buffer.get(),
base::BindOnce(
&AppCacheUpdateJobTest::StartUpdateAfterSeedingStorageData,
base::Unretained(this)));
// Start update after data write completes asynchronously.
}
void IfModifiedSinceUpgradeParserVersion1Test() {
MakeService();
group_ = base::MakeRefCounted<AppCacheGroup>(
service_->storage(), MockHttpServer::GetMockUrl("files/manifest1"),
111);
AppCacheUpdateJob* update =
new AppCacheUpdateJob(service_.get(), group_.get());
group_->update_job_ = update;
// Create a cache without a manifest entry. The manifest entry will be
// added later.
AppCache* cache = MakeCacheForGroup(service_->storage()->NewCacheId(), -1);
MockFrontend* frontend = MakeMockFrontend();
AppCacheHost* host = MakeHost(frontend);
host->AssociateCompleteCache(cache);
// Set up checks for when update job finishes.
do_checks_after_update_finished_ = true;
expect_group_obsolete_ = false;
expect_group_has_cache_ = true;
expect_old_cache_ = cache;
tested_manifest_ = MANIFEST1;
frontend->AddExpectedEvent(
blink::mojom::AppCacheEventID::APPCACHE_CHECKING_EVENT);
frontend->AddExpectedEvent(
blink::mojom::AppCacheEventID::APPCACHE_DOWNLOADING_EVENT);
frontend->AddExpectedEvent(
blink::mojom::AppCacheEventID::APPCACHE_PROGRESS_EVENT);
frontend->AddExpectedEvent(
blink::mojom::AppCacheEventID::APPCACHE_PROGRESS_EVENT);
frontend->AddExpectedEvent(
blink::mojom::AppCacheEventID::APPCACHE_PROGRESS_EVENT); // final
frontend->AddExpectedEvent(
blink::mojom::AppCacheEventID::APPCACHE_UPDATE_READY_EVENT);
AppCacheCacheTestHelper::CacheEntries cache_entries;
// Add cache entry for manifest.
// Seed storage with expected manifest response info that will cause
// an If-Modified-Since header to be put in the manifest fetch request.
const char data[] =
"HTTP/1.1 200 OK\0"
"Last-Modified: Sat, 29 Oct 1994 19:43:31 GMT\0";
scoped_refptr<net::HttpResponseHeaders> headers =
base::MakeRefCounted<net::HttpResponseHeaders>(
std::string(data, base::size(data)));
std::unique_ptr<net::HttpResponseInfo> response_info =
std::make_unique<net::HttpResponseInfo>();
response_info->headers = std::move(headers);
AppCacheCacheTestHelper::AddCacheEntry(
&cache_entries, group_->manifest_url(), AppCacheEntry::EXPLICIT,
/*expect_if_modified_since=*/"Sat, 29 Oct 1994 19:43:31 GMT",
/*expect_if_none_match=*/std::string(), /*headers_allowed=*/true,
std::move(response_info), kManifest1Contents);
// Add all header checks from |cache_entries|.
for (const auto& kvp : cache_entries) {
const GURL& cache_entry_url = kvp.first;
const AppCacheCacheTestHelper::CacheEntry* cache_entry = kvp.second.get();
http_headers_request_test_jobs_.emplace(
cache_entry_url,
std::make_unique<HttpHeadersRequestTestJob>(
cache_entry->expect_if_modified_since,
cache_entry->expect_if_none_match, cache_entry->headers_allowed));
}
cache_helper_ = std::make_unique<AppCacheCacheTestHelper>(
service_.get(), group_->manifest_url(), cache, std::move(cache_entries),
base::BindOnce(
&AppCacheUpdateJobTest::StartUpdateAfterSeedingStorageData,
base::Unretained(this)));
cache_helper_->Write();
// Start update after data write completes asynchronously.
}
// AppCaches built with manifest parser version 0 should update without
// conditional request headers to force the server to send a 200 response
// back to the client rather than 304. This test ensures that, when the cache
// has a response info with cached ETag headers, the request does not include
// an If-None-Match conditioanl header.
void IfNoneMatchUpgradeParserVersion0Test() {
MakeService();
group_ = base::MakeRefCounted<AppCacheGroup>(
service_->storage(), MockHttpServer::GetMockUrl("files/manifest1"),
111);
http_headers_request_test_jobs_.emplace(
group_->manifest_url(), std::make_unique<HttpHeadersRequestTestJob>(
std::string(), std::string(),
/*headers_allowed=*/false));
AppCacheUpdateJob* update =
new AppCacheUpdateJob(service_.get(), group_.get());
group_->update_job_ = update;
// Give the newest cache a manifest entry that is in storage.
response_writer_ =
service_->storage()->CreateResponseWriter(group_->manifest_url());
AppCache* cache = MakeCacheForGroup(service_->storage()->NewCacheId(),
response_writer_->response_id());
cache->set_manifest_parser_version(0);
MockFrontend* frontend = MakeMockFrontend();
AppCacheHost* host = MakeHost(frontend);
host->AssociateCompleteCache(cache);
// Set up checks for when update job finishes.
do_checks_after_update_finished_ = true;
expect_group_obsolete_ = false;
expect_group_has_cache_ = true;
expect_old_cache_ = cache;
tested_manifest_ = MANIFEST1;
frontend->AddExpectedEvent(
blink::mojom::AppCacheEventID::APPCACHE_CHECKING_EVENT);
frontend->AddExpectedEvent(
blink::mojom::AppCacheEventID::APPCACHE_DOWNLOADING_EVENT);
frontend->AddExpectedEvent(
blink::mojom::AppCacheEventID::APPCACHE_PROGRESS_EVENT);
frontend->AddExpectedEvent(
blink::mojom::AppCacheEventID::APPCACHE_PROGRESS_EVENT);
frontend->AddExpectedEvent(
blink::mojom::AppCacheEventID::APPCACHE_PROGRESS_EVENT); // final
frontend->AddExpectedEvent(
blink::mojom::AppCacheEventID::APPCACHE_UPDATE_READY_EVENT);
// Seed storage with expected manifest response info that will cause
// an If-None-Match header to be put in the manifest fetch request.
const char data[] =
"HTTP/1.1 200 OK\0"
"ETag: \"LadeDade\"\0"
"\0";
scoped_refptr<net::HttpResponseHeaders> headers =
base::MakeRefCounted<net::HttpResponseHeaders>(
std::string(data, base::size(data)));
std::unique_ptr<net::HttpResponseInfo> response_info =
std::make_unique<net::HttpResponseInfo>();
response_info->headers = std::move(headers);
scoped_refptr<HttpResponseInfoIOBuffer> io_buffer =
base::MakeRefCounted<HttpResponseInfoIOBuffer>(
std::move(response_info));
response_writer_->WriteInfo(
io_buffer.get(),
base::BindOnce(
&AppCacheUpdateJobTest::StartUpdateAfterSeedingStorageData,
base::Unretained(this)));
// Start update after data write completes asynchronously.
}
void IfNoneMatchUpgradeParserVersion1Test() {
MakeService();
group_ = base::MakeRefCounted<AppCacheGroup>(
service_->storage(), MockHttpServer::GetMockUrl("files/manifest1"),
111);
http_headers_request_test_jobs_.emplace(
group_->manifest_url(), std::make_unique<HttpHeadersRequestTestJob>(
std::string(), "\"LadeDade\""));
AppCacheUpdateJob* update =
new AppCacheUpdateJob(service_.get(), group_.get());
group_->update_job_ = update;
// Give the newest cache a manifest entry that is in storage.
response_writer_ =
service_->storage()->CreateResponseWriter(group_->manifest_url());
AppCache* cache = MakeCacheForGroup(service_->storage()->NewCacheId(),
response_writer_->response_id());
MockFrontend* frontend = MakeMockFrontend();
AppCacheHost* host = MakeHost(frontend);
host->AssociateCompleteCache(cache);
// Set up checks for when update job finishes.
do_checks_after_update_finished_ = true;
expect_group_obsolete_ = false;
expect_group_has_cache_ = true;
expect_old_cache_ = cache;
tested_manifest_ = MANIFEST1;
frontend->AddExpectedEvent(
blink::mojom::AppCacheEventID::APPCACHE_CHECKING_EVENT);
frontend->AddExpectedEvent(
blink::mojom::AppCacheEventID::APPCACHE_DOWNLOADING_EVENT);
frontend->AddExpectedEvent(
blink::mojom::AppCacheEventID::APPCACHE_PROGRESS_EVENT);
frontend->AddExpectedEvent(
blink::mojom::AppCacheEventID::APPCACHE_PROGRESS_EVENT);
frontend->AddExpectedEvent(
blink::mojom::AppCacheEventID::APPCACHE_PROGRESS_EVENT); // final
frontend->AddExpectedEvent(
blink::mojom::AppCacheEventID::APPCACHE_UPDATE_READY_EVENT);
// Seed storage with expected manifest response info that will cause
// an If-None-Match header to be put in the manifest fetch request.
const char data[] =
"HTTP/1.1 200 OK\0"
"ETag: \"LadeDade\"\0"
"\0";
scoped_refptr<net::HttpResponseHeaders> headers =
base::MakeRefCounted<net::HttpResponseHeaders>(
std::string(data, base::size(data)));
std::unique_ptr<net::HttpResponseInfo> response_info =
std::make_unique<net::HttpResponseInfo>();
response_info->headers = std::move(headers);
scoped_refptr<HttpResponseInfoIOBuffer> io_buffer =
base::MakeRefCounted<HttpResponseInfoIOBuffer>(
std::move(response_info));
response_writer_->WriteInfo(
io_buffer.get(),
base::BindOnce(
&AppCacheUpdateJobTest::StartUpdateAfterSeedingStorageData,
base::Unretained(this)));
// Start update after data write completes asynchronously.
}
void RequestResponseTimesAreSetTest() {
MakeService();
group_ = base::MakeRefCounted<AppCacheGroup>(
service_->storage(), MockHttpServer::GetMockUrl("files/manifest1"),
111);
AppCacheUpdateJob* update =
new AppCacheUpdateJob(service_.get(), group_.get());
group_->update_job_ = update;
// Create a cache without a manifest entry. The manifest entry will be
// added later.
AppCache* cache = MakeCacheForGroup(service_->storage()->NewCacheId(), -1);
MockFrontend* frontend = MakeMockFrontend();
AppCacheHost* host = MakeHost(frontend);
host->AssociateCompleteCache(cache);
// Set up checks for when update job finishes.
do_checks_after_update_finished_ = false;
AppCacheCacheTestHelper::CacheEntries cache_entries;
// Add cache entry for manifest.
// Seed storage with expected manifest response info that will cause
// an If-Modified-Since header to be put in the manifest fetch request.
{
const char data[] =
"HTTP/1.1 200 OK\0"
"Last-Modified: Sat, 29 Oct 2019 19:43:31 GMT\0";
scoped_refptr<net::HttpResponseHeaders> headers =
base::MakeRefCounted<net::HttpResponseHeaders>(
std::string(data, base::size(data)));
std::unique_ptr<net::HttpResponseInfo> response_info =
std::make_unique<net::HttpResponseInfo>();
response_info->headers = std::move(headers);
response_info->request_time = base::Time::Now() - kOneYear;
response_info->response_time = base::Time::Now() - kOneYear;
AppCacheCacheTestHelper::AddCacheEntry(
&cache_entries, group_->manifest_url(), AppCacheEntry::EXPLICIT,
/*expect_if_modified_since=*/std::string(),
/*expect_if_none_match=*/std::string(), /*headers_allowed=*/true,
std::move(response_info), kManifest1Contents);
}
cache_helper_ = std::make_unique<AppCacheCacheTestHelper>(
service_.get(), group_->manifest_url(), cache, std::move(cache_entries),
base::BindOnce(
&AppCacheUpdateJobTest::StartUpdateAfterSeedingStorageData,
base::Unretained(this)));
cache_helper_->Write();
post_update_finished_cb_ = base::BindOnce(
&AppCacheUpdateJobTest::RequestResponseTimesAreSetUpdateFinished,
base::Unretained(this));
// Start update after data write completes asynchronously.
// After update is finished, continues async in
// |RequestResponseTimesAreSetUpdateFinished|.
}
void RequestResponseTimesAreSetUpdateFinished() {
ASSERT_NE(group_->newest_complete_cache(), cache_helper_->write_cache());
ASSERT_NE(group_->newest_complete_cache(), nullptr);
cache_helper_->PrepareForRead(
group_->newest_complete_cache(),
base::BindOnce(
&AppCacheUpdateJobTest::RequestResponseTimesAreSetReadFinished,
base::Unretained(this)));
cache_helper_->Read();
// Continues async in |RequestResponseTimesAreSetReadFinished|.
}
void RequestResponseTimesAreSetReadFinished() {
auto it = cache_helper_->read_cache_entries().find(
MockHttpServer::GetMockUrl("files/explicit1"));
ASSERT_NE(it, cache_helper_->read_cache_entries().end());
CHECK_GT(it->second->response_info->request_time,
base::Time::Now() - kOneHour);
CHECK_GT(it->second->response_info->response_time,
base::Time::Now() - kOneHour);
TriggerTestComplete();
// Continues async in |TestComplete|.
}
void RequestResponseTimesAreModifiedTest() {
RequestResponseTimesModified(/*feature_enabled=*/true);
}
void RequestResponseTimesAreNotModifiedTest() {
RequestResponseTimesModified(/*feature_enabled=*/false);
}
void RequestResponseTimesModified(bool feature_enabled) {
MakeService();
group_ = base::MakeRefCounted<AppCacheGroup>(
service_->storage(),
MockHttpServer::GetMockUrl("files/manifest1-with-notmodified"), 111);
AppCacheUpdateJob* update =
new AppCacheUpdateJob(service_.get(), group_.get());
group_->update_job_ = update;
// Create a cache without a manifest entry. The manifest entry will be
// added later.
AppCache* cache = MakeCacheForGroup(service_->storage()->NewCacheId(), -1);
MockFrontend* frontend = MakeMockFrontend();
AppCacheHost* host = MakeHost(frontend);
host->AssociateCompleteCache(cache);
// Set up checks for when update job finishes.
do_checks_after_update_finished_ = true;
expect_group_obsolete_ = false;
expect_group_has_cache_ = true;
tested_manifest_ = MANIFEST1_WITH_NOTMODIFIED;
if (feature_enabled) {
expect_old_cache_ = cache;
}
frontend->AddExpectedEvent(
blink::mojom::AppCacheEventID::APPCACHE_CHECKING_EVENT);
frontend->AddExpectedEvent(
blink::mojom::AppCacheEventID::APPCACHE_DOWNLOADING_EVENT);
frontend->AddExpectedEvent(
blink::mojom::AppCacheEventID::APPCACHE_PROGRESS_EVENT);
frontend->AddExpectedEvent(
blink::mojom::AppCacheEventID::APPCACHE_PROGRESS_EVENT);
frontend->AddExpectedEvent(
blink::mojom::AppCacheEventID::APPCACHE_PROGRESS_EVENT);
frontend->AddExpectedEvent(
blink::mojom::AppCacheEventID::APPCACHE_PROGRESS_EVENT); // final
frontend->AddExpectedEvent(
blink::mojom::AppCacheEventID::APPCACHE_UPDATE_READY_EVENT);
AppCacheCacheTestHelper::CacheEntries cache_entries;
// Add cache entry for manifest.
// Seed storage with expected manifest response info that will cause
// an If-Modified-Since header to be put in the manifest fetch request.
{
const char data[] =
"HTTP/1.1 200 OK\0"
"Last-Modified: Sat, 29 Oct 2019 19:43:31 GMT\0";
scoped_refptr<net::HttpResponseHeaders> headers =
base::MakeRefCounted<net::HttpResponseHeaders>(
std::string(data, base::size(data)));
std::unique_ptr<net::HttpResponseInfo> response_info =
std::make_unique<net::HttpResponseInfo>();
response_info->headers = std::move(headers);
response_info->request_time = base::Time::Now() - kOneYear;
response_info->response_time = base::Time::Now() - kOneYear;
AppCacheCacheTestHelper::AddCacheEntry(
&cache_entries, group_->manifest_url(), AppCacheEntry::EXPLICIT,
/*expect_if_modified_since=*/std::string(),
/*expect_if_none_match=*/std::string(), /*headers_allowed=*/true,
std::move(response_info), kManifest1WithNotModifiedContents);
}
// Add cache entry for notmodified.
// Seed storage with expected manifest response info that will cause
// an If-Modified-Since header to be put in the manifest fetch request.
{
const char data[] =
"HTTP/1.1 200 OK\0"
"Last-Modified: Sat, 29 Oct 2019 19:43:31 GMT\0";
scoped_refptr<net::HttpResponseHeaders> headers =
base::MakeRefCounted<net::HttpResponseHeaders>(
std::string(data, base::size(data)));
std::unique_ptr<net::HttpResponseInfo> response_info =
std::make_unique<net::HttpResponseInfo>();
response_info->headers = std::move(headers);
// Leave the request and response time fields unset to match corrupt
// cases in the field.
CHECK_EQ(response_info->request_time, base::Time());
CHECK_EQ(response_info->response_time, base::Time());
AppCacheCacheTestHelper::AddCacheEntry(
&cache_entries, MockHttpServer::GetMockUrl("files/notmodified"),
AppCacheEntry::EXPLICIT,
/*expect_if_modified_since=*/"Sat, 29 Oct 2019 19:43:31 GMT",
/*expect_if_none_match=*/std::string(), /*headers_allowed=*/true,
std::move(response_info), /*body=*/"laughing-giraffe");
}
// Add all header checks from |cache_entries|.
for (auto& it : cache_entries) {
http_headers_request_test_jobs_.emplace(
it.first,
std::make_unique<HttpHeadersRequestTestJob>(
it.second->expect_if_modified_since,
it.second->expect_if_none_match, it.second->headers_allowed));
}
cache_helper_ = std::make_unique<AppCacheCacheTestHelper>(
service_.get(), group_->manifest_url(), cache, std::move(cache_entries),
base::BindOnce(
&AppCacheUpdateJobTest::StartUpdateAfterSeedingStorageData,
base::Unretained(this)));
cache_helper_->Write();
post_update_finished_cb_ = base::BindOnce(
&AppCacheUpdateJobTest::RequestResponseTimesModifiedUpdateFinished,
base::Unretained(this), feature_enabled);
// Start update after data write completes asynchronously.
// After update is finished, continues async in
// |RequestResponseTimesModifiedUpdateFinished|.
}
void RequestResponseTimesModifiedUpdateFinished(bool feature_enabled) {
ASSERT_NE(group_->newest_complete_cache(), cache_helper_->write_cache());
ASSERT_NE(group_->newest_complete_cache(), nullptr);
cache_helper_->PrepareForRead(
group_->newest_complete_cache(),
base::BindOnce(
&AppCacheUpdateJobTest::RequestResponseTimesModifiedReadFinished,
base::Unretained(this), feature_enabled));
cache_helper_->Read();
// Continues async in |RequestResponseTimesModifiedReadFinished|.
}
void RequestResponseTimesModifiedReadFinished(bool feature_enabled) {
auto it = cache_helper_->read_cache_entries().find(
MockHttpServer::GetMockUrl("files/notmodified"));
ASSERT_NE(it, cache_helper_->read_cache_entries().end());
// Verify that the cache body on the entry matches the originally written
// cache body.
CHECK_EQ(it->second->body, "laughing-giraffe");
if (feature_enabled) {
CHECK_GT(it->second->response_info->request_time,
base::Time::Now() - kOneHour);
CHECK_GT(it->second->response_info->response_time,
base::Time::Now() - kOneHour);
} else {
CHECK_EQ(it->second->response_info->request_time, base::Time());
CHECK_EQ(it->second->response_info->response_time, base::Time());
}
TriggerTestComplete();
// Continues async in |TestComplete|.
}
void RequestResponseTimesCorruptionFixedTest() {
RequestResponseTimesCorruption(/*expect_modified=*/true);
}
void RequestResponseTimesCorruptionNotFixedTest() {
RequestResponseTimesCorruption(/*expect_modified=*/false);
}
void RequestResponseTimesCorruption(bool expect_modified) {
MakeService();
group_ = base::MakeRefCounted<AppCacheGroup>(
service_->storage(),
MockHttpServer::GetMockUrl("files/manifest1-with-maybemodified"), 111);
AppCacheUpdateJob* update =
new AppCacheUpdateJob(service_.get(), group_.get());
group_->update_job_ = update;
// Create a cache without a manifest entry. The manifest entry will be
// added later.
AppCache* cache = MakeCacheForGroup(service_->storage()->NewCacheId(), -1);
MockFrontend* frontend = MakeMockFrontend();
AppCacheHost* host = MakeHost(frontend);
host->AssociateCompleteCache(cache);
// Set up checks for when update job finishes.
do_checks_after_update_finished_ = true;
expect_group_obsolete_ = false;
expect_group_has_cache_ = true;
tested_manifest_ = MANIFEST1_WITH_MAYBEMODIFIED;
if (!expect_modified) {
expect_old_cache_ = cache;
}
frontend->AddExpectedEvent(
blink::mojom::AppCacheEventID::APPCACHE_CHECKING_EVENT);
frontend->AddExpectedEvent(
blink::mojom::AppCacheEventID::APPCACHE_DOWNLOADING_EVENT);
frontend->AddExpectedEvent(
blink::mojom::AppCacheEventID::APPCACHE_PROGRESS_EVENT);
frontend->AddExpectedEvent(
blink::mojom::AppCacheEventID::APPCACHE_PROGRESS_EVENT);
frontend->AddExpectedEvent(
blink::mojom::AppCacheEventID::APPCACHE_PROGRESS_EVENT);
frontend->AddExpectedEvent(
blink::mojom::AppCacheEventID::APPCACHE_PROGRESS_EVENT); // final
frontend->AddExpectedEvent(
blink::mojom::AppCacheEventID::APPCACHE_UPDATE_READY_EVENT);
AppCacheCacheTestHelper::CacheEntries cache_entries;
// Add cache entry for manifest.
// Seed storage with expected manifest response info that will cause
// an If-Modified-Since header to be put in the manifest fetch request.
{
const char data[] =
"HTTP/1.1 200 OK\0"
"Last-Modified: Sat, 29 Oct 2019 19:43:31 GMT\0";
scoped_refptr<net::HttpResponseHeaders> headers =
base::MakeRefCounted<net::HttpResponseHeaders>(
std::string(data, base::size(data)));
std::unique_ptr<net::HttpResponseInfo> response_info =
std::make_unique<net::HttpResponseInfo>();
response_info->headers = std::move(headers);
response_info->request_time = base::Time::Now() - kOneYear;
response_info->response_time = base::Time::Now() - kOneYear;
AppCacheCacheTestHelper::AddCacheEntry(
&cache_entries, group_->manifest_url(), AppCacheEntry::EXPLICIT,
/*expect_if_modified_since=*/std::string(),
/*expect_if_none_match=*/std::string(), /*headers_allowed=*/true,
std::move(response_info), kManifest1WithMaybeModifiedContents);
}
// Add cache entry for maybemodified.
// Seed storage with expected cache response info that will cause
// an If-Modified-Since header to be put in the fetch request.
{
const char data[] =
"HTTP/1.1 200 OK\0"
"Last-Modified: Sat, 29 Oct 2019 19:43:31 GMT\0";
scoped_refptr<net::HttpResponseHeaders> headers =
base::MakeRefCounted<net::HttpResponseHeaders>(
std::string(data, base::size(data)));
std::unique_ptr<net::HttpResponseInfo> response_info =
std::make_unique<net::HttpResponseInfo>();
response_info->headers = std::move(headers);
CHECK_EQ(response_info->request_time, base::Time());
CHECK_EQ(response_info->response_time, base::Time());
std::string expect_if_modified_since;
if (!expect_modified) {
expect_if_modified_since = "Sat, 29 Oct 2019 19:43:31 GMT";
}
AppCacheCacheTestHelper::AddCacheEntry(
&cache_entries, MockHttpServer::GetMockUrl("files/maybemodified"),
AppCacheEntry::EXPLICIT,
/*expect_if_modified_since=*/expect_if_modified_since,
/*expect_if_none_match=*/std::string(), /*headers_allowed=*/true,
std::move(response_info), /*body=*/"cached-maybemodified");
}
// Add all header checks from |cache_entries|.
for (auto& it : cache_entries) {
http_headers_request_test_jobs_.emplace(
it.first,
std::make_unique<HttpHeadersRequestTestJob>(
it.second->expect_if_modified_since,
it.second->expect_if_none_match, it.second->headers_allowed));
}
cache_helper_ = std::make_unique<AppCacheCacheTestHelper>(
service_.get(), group_->manifest_url(), cache, std::move(cache_entries),
base::BindOnce(
&AppCacheUpdateJobTest::StartUpdateAfterSeedingStorageData,
base::Unretained(this)));
cache_helper_->Write();
post_update_finished_cb_ = base::BindOnce(
&AppCacheUpdateJobTest::RequestResponseTimesCorruptionUpdateFinished,
base::Unretained(this), expect_modified);
// Start update after data write completes asynchronously.
// After update is finished, continues async in
// |RequestResponseTimesCorruptionUpdateFinished|.
}
void RequestResponseTimesCorruptionUpdateFinished(bool expect_modified) {
// After cache write was complete, we note that we expect an item to be
// copied for the not modified case.
if (!expect_modified) {
expect_response_ids_.insert(std::map<GURL, int64_t>::value_type(
MockHttpServer::GetMockUrl("files/maybemodified"),
expect_old_cache_
->GetEntry(MockHttpServer::GetMockUrl("files/maybemodified"))
->response_id())); // copied
}
ASSERT_NE(group_->newest_complete_cache(), cache_helper_->write_cache());
ASSERT_NE(group_->newest_complete_cache(), nullptr);
cache_helper_->PrepareForRead(
group_->newest_complete_cache(),
base::BindOnce(
&AppCacheUpdateJobTest::RequestResponseTimesCorruptionReadFinished,
base::Unretained(this), expect_modified));
cache_helper_->Read();
// Continues async in |RequestResponseTimesCorruptionReadFinished|.
}
void RequestResponseTimesCorruptionReadFinished(bool expect_modified) {
std::string resource_name;
resource_name = "files/maybemodified";
auto it = cache_helper_->read_cache_entries().find(
MockHttpServer::GetMockUrl(resource_name));
ASSERT_NE(it, cache_helper_->read_cache_entries().end());
if (expect_modified) {
// Verify that the cache body on the entry matches the expected mock
// return body.
CHECK_EQ(it->second->body, "modified");
CHECK_GT(it->second->response_info->request_time,
base::Time::Now() - kOneHour);
CHECK_GT(it->second->response_info->response_time,
base::Time::Now() - kOneHour);
} else {
CHECK_EQ(it->second->body, "cached-maybemodified");
CHECK_EQ(it->second->response_info->request_time, base::Time());
CHECK_EQ(it->second->response_info->response_time, base::Time());
}
TriggerTestComplete();
// Continues async in |TestComplete|.
}
void IfNoneMatchRefetchTest() {
MakeService();
group_ = base::MakeRefCounted<AppCacheGroup>(
service_->storage(), GURL("http://headertest"), 111);
http_headers_request_test_jobs_.emplace(
group_->manifest_url(), std::make_unique<HttpHeadersRequestTestJob>(
std::string(), "\"LadeDade\""));
AppCacheUpdateJob* update =
new AppCacheUpdateJob(service_.get(), group_.get());
group_->update_job_ = update;
// Simulate a refetch manifest request that uses an ETag header.
const char data[] =
"HTTP/1.1 200 OK\0"
"ETag: \"LadeDade\"\0"
"\0";
auto response_info = std::make_unique<net::HttpResponseInfo>();
response_info->headers = base::MakeRefCounted<net::HttpResponseHeaders>(
std::string(data, base::size(data)));
group_->update_status_ = AppCacheGroup::DOWNLOADING;
update->manifest_response_info_ = std::move(response_info);
update->internal_state_ = AppCacheUpdateJobState::REFETCH_MANIFEST;
update->RefetchManifest();
// We need to wait for the URL load requests to make it to the
// URLLoaderInterceptor.
base::ThreadTaskRunnerHandle::Get()->PostTask(
FROM_HERE,
base::BindOnce(&AppCacheUpdateJobTest::VerifyHeadersAndDeleteUpdate,
base::Unretained(this), update));
TriggerTestComplete();
}
void MultipleHeadersRefetchTest() {
// Verify that code is correct when building multiple extra headers.
MakeService();
group_ = base::MakeRefCounted<AppCacheGroup>(
service_->storage(), GURL("http://headertest"), 111);
http_headers_request_test_jobs_.emplace(
group_->manifest_url(),
std::make_unique<HttpHeadersRequestTestJob>(
"Sat, 29 Oct 1994 19:43:31 GMT", "\"LadeDade\""));
AppCacheUpdateJob* update =
new AppCacheUpdateJob(service_.get(), group_.get());
group_->update_job_ = update;
// Simulate a refetch manifest request that uses an ETag header.
const char data[] =
"HTTP/1.1 200 OK\0"
"Last-Modified: Sat, 29 Oct 1994 19:43:31 GMT\0"
"ETag: \"LadeDade\"\0"
"\0";
auto response_info = std::make_unique<net::HttpResponseInfo>();
response_info->headers = base::MakeRefCounted<net::HttpResponseHeaders>(
std::string(data, base::size(data)));
group_->update_status_ = AppCacheGroup::DOWNLOADING;
update->manifest_response_info_ = std::move(response_info);
update->internal_state_ = AppCacheUpdateJobState::REFETCH_MANIFEST;
update->RefetchManifest();
// We need to wait for the URL load requests to make it to the
// URLLoaderInterceptor.
base::ThreadTaskRunnerHandle::Get()->PostTask(
FROM_HERE,
base::BindOnce(&AppCacheUpdateJobTest::VerifyHeadersAndDeleteUpdate,
base::Unretained(this), update));
TriggerTestComplete();
}
void CrossOriginHttpsSuccessTest() {
GURL manifest_url = MockHttpServer::GetMockHttpsUrl(
"files/valid_cross_origin_https_manifest");
MakeService();
group_ = base::MakeRefCounted<AppCacheGroup>(
service_->storage(), manifest_url, service_->storage()->NewGroupId());
AppCacheUpdateJob* update =
new AppCacheUpdateJob(service_.get(), group_.get());
group_->update_job_ = update;
MockFrontend* frontend = MakeMockFrontend();
AppCacheHost* host = MakeHost(frontend);
update->StartUpdate(host, GURL());
// Set up checks for when update job finishes.
do_checks_after_update_finished_ = true;
expect_group_obsolete_ = false;
expect_group_has_cache_ = true;
tested_manifest_ = NONE;
frontend->AddExpectedEvent(
blink::mojom::AppCacheEventID::APPCACHE_CHECKING_EVENT);
WaitForUpdateToFinish();
}
void CrossOriginHttpsDeniedTest() {
GURL manifest_url = MockHttpServer::GetMockHttpsUrl(
"files/invalid_cross_origin_https_manifest");
MakeService();
group_ = base::MakeRefCounted<AppCacheGroup>(
service_->storage(), manifest_url, service_->storage()->NewGroupId());
AppCacheUpdateJob* update =
new AppCacheUpdateJob(service_.get(), group_.get());
group_->update_job_ = update;
MockFrontend* frontend = MakeMockFrontend();
AppCacheHost* host = MakeHost(frontend);
update->StartUpdate(host, GURL());
// Set up checks for when update job finishes.
do_checks_after_update_finished_ = true;
expect_group_obsolete_ = false;
expect_group_has_cache_ = false;
tested_manifest_ = NONE;
frontend->AddExpectedEvent(
blink::mojom::AppCacheEventID::APPCACHE_CHECKING_EVENT);
WaitForUpdateToFinish();
}
void OriginTrialUpdateTest() {
expect_token_expires_ = kTestOriginTrialTokenExpiry;
MakeService();
tested_manifest_path_override_ = "files/manifest2-origin-trial";
group_ = base::MakeRefCounted<AppCacheGroup>(
service_->storage(),
MockHttpServer::GetMockUrl(tested_manifest_path_override_),
service_->storage()->NewGroupId());
AppCacheUpdateJob* update =
new AppCacheUpdateJob(service_.get(), group_.get());
group_->update_job_ = update;
// Create response writer to get a response id.
response_writer_ =
service_->storage()->CreateResponseWriter(group_->manifest_url());
AppCache* cache = MakeCacheForGroup(service_->storage()->NewCacheId(),
response_writer_->response_id());
MockFrontend* frontend1 = MakeMockFrontend();
AppCacheHost* host1 = MakeHost(frontend1);
host1->AssociateCompleteCache(cache);
// Set up checks for when update job finishes.
do_checks_after_update_finished_ = true;
expect_group_obsolete_ = false;
expect_group_has_cache_ = true;
expect_old_cache_ = cache;
tested_manifest_ = MANIFEST2_WITH_FILES_SCOPE;
frontend1->AddExpectedEvent(
blink::mojom::AppCacheEventID::APPCACHE_CHECKING_EVENT);
frontend1->AddExpectedEvent(
blink::mojom::AppCacheEventID::APPCACHE_DOWNLOADING_EVENT);
frontend1->AddExpectedEvent(
blink::mojom::AppCacheEventID::APPCACHE_PROGRESS_EVENT);
frontend1->AddExpectedEvent(
blink::mojom::AppCacheEventID::APPCACHE_PROGRESS_EVENT);
frontend1->AddExpectedEvent(
blink::mojom::AppCacheEventID::APPCACHE_PROGRESS_EVENT);
frontend1->AddExpectedEvent(
blink::mojom::AppCacheEventID::APPCACHE_PROGRESS_EVENT); // final
frontend1->AddExpectedEvent(
blink::mojom::AppCacheEventID::APPCACHE_UPDATE_READY_EVENT);
// Seed storage with identical manifest2 contents
const std::string seed_data(kManifest2Contents);
scoped_refptr<net::StringIOBuffer> io_buffer =
base::MakeRefCounted<net::StringIOBuffer>(seed_data);
response_writer_->WriteData(
io_buffer.get(), seed_data.length(),
base::BindOnce(
&AppCacheUpdateJobTest::StartUpdateAfterSeedingStorageData,
base::Unretained(this)));
// Start update after data write completes asynchronously.
}
void OriginTrialRequiredNoTokenTest() {
GURL manifest_url = MockHttpServer::GetMockUrl("files/manifest1");
MakeService();
group_ = base::MakeRefCounted<AppCacheGroup>(
service_->storage(), manifest_url, service_->storage()->NewGroupId());
ASSERT_TRUE(group_->last_full_update_check_time().is_null());
AppCacheUpdateJob* update =
new AppCacheUpdateJob(service_.get(), group_.get());
group_->update_job_ = update;
MockFrontend* frontend = MakeMockFrontend();
AppCacheHost* host = MakeHost(frontend);
update->StartUpdate(host, GURL());
// Set up checks for when update job finishes.
do_checks_after_update_finished_ = true;
expect_group_obsolete_ = false;
expect_group_has_cache_ = false;
frontend->AddExpectedEvent(
blink::mojom::AppCacheEventID::APPCACHE_CHECKING_EVENT);
WaitForUpdateToFinish();
}
void WaitForUpdateToFinish() {
if (group_->update_status() == AppCacheGroup::IDLE)
UpdateFinished();
else
group_->AddUpdateObserver(this);
}
void OnUpdateComplete(AppCacheGroup* group) override {
ASSERT_EQ(group_.get(), group);
protect_newest_cache_ = group->newest_complete_cache();
UpdateFinished();
}
void UpdateFinished() {
if (post_update_finished_cb_) {
std::move(post_update_finished_cb_).Run();
return;
}
TriggerTestComplete();
}
void TriggerTestComplete() {
// We unwind the stack prior to finishing up to let stack-based objects
// get deleted.
base::ThreadTaskRunnerHandle::Get()->PostTask(
FROM_HERE, base::BindOnce(&AppCacheUpdateJobTest::TestComplete,
base::Unretained(this)));
}
void TestComplete() {
EXPECT_EQ(AppCacheGroup::IDLE, group_->update_status());
EXPECT_TRUE(group_->update_job() == nullptr);
if (do_checks_after_update_finished_)
VerifyExpectations();
// Clean up everything that was created on the IO thread.
cache_helper_.reset();
protect_newest_cache_ = nullptr;
group_ = nullptr;
hosts_.clear();
frontends_.clear();
response_infos_.clear();
service_.reset(nullptr);
std::move(test_completed_cb_).Run();
}
void MakeService() {
service_ = std::make_unique<MockAppCacheService>(
weak_partition_factory_->GetWeakPtr());
}
AppCache* MakeCacheForGroup(int64_t cache_id, int64_t manifest_response_id) {
return MakeCacheForGroup(cache_id, group_->manifest_url(),
manifest_response_id);
}
AppCache* MakeCacheForGroup(int64_t cache_id,
const GURL& manifest_entry_url,
int64_t manifest_response_id) {
AppCache* cache = new AppCache(service_->storage(), cache_id);
cache->set_complete(true);
cache->set_manifest_parser_version(2);
cache->set_manifest_scope("/");
cache->set_update_time(base::Time::Now() - kOneHour);
group_->AddCache(cache);
group_->set_last_full_update_check_time(cache->update_time());
// Add manifest entry to cache.
if (manifest_response_id >= 0) {
cache->AddEntry(manifest_entry_url, AppCacheEntry(AppCacheEntry::MANIFEST,
manifest_response_id));
}
// Specific tests that expect a newer time should set
// expect_full_update_time_newer_than_ which causes this
// equality expectation to be ignored.
expect_full_update_time_equal_to_ = cache->update_time();
return cache;
}
AppCacheHost* MakeHost(blink::mojom::AppCacheFrontend* frontend) {
constexpr int kRenderFrameIdForTests = 456;
hosts_.push_back(std::make_unique<AppCacheHost>(
base::UnguessableToken::Create(), process_id_, kRenderFrameIdForTests,
ChildProcessSecurityPolicyImpl::GetInstance()->CreateHandle(
process_id_),
mojo::NullRemote(), service_.get()));
hosts_.back()->set_frontend_for_testing(frontend);
return hosts_.back().get();
}
AppCacheResponseInfo* MakeAppCacheResponseInfo(
const GURL& manifest_url,
int64_t response_id,
const std::string& raw_headers) {
std::unique_ptr<net::HttpResponseInfo> http_info =
std::make_unique<net::HttpResponseInfo>();
http_info->headers =
base::MakeRefCounted<net::HttpResponseHeaders>(raw_headers);
auto info = base::MakeRefCounted<AppCacheResponseInfo>(
service_->storage()->GetWeakPtr(), manifest_url, response_id,
std::move(http_info), 0);
response_infos_.emplace_back(info);
return info.get();
}
MockFrontend* MakeMockFrontend() {
frontends_.push_back(std::make_unique<MockFrontend>());
return frontends_.back().get();
}
// Verifies conditions about the group and notifications after an update
// has finished. Cannot verify update job internals as update is deleted.
void VerifyExpectations() {
RetryRequestTestJob::Verify();
for (auto& kvp : http_headers_request_test_jobs_) {
const GURL& test_job_url = kvp.first;
HttpHeadersRequestTestJob* test_job = kvp.second.get();
test_job->Verify(test_job_url);
}
EXPECT_EQ(expect_group_obsolete_, group_->is_obsolete());
EXPECT_EQ(expect_group_is_being_deleted_ || expect_eviction_,
group_->is_being_deleted());
if (!expect_eviction_) {
EXPECT_EQ(expect_evictable_error_,
!group_->first_evictable_error_time().is_null());
if (expect_evictable_error_) {
MockAppCacheStorage* storage =
static_cast<MockAppCacheStorage*>(service_->storage());
EXPECT_EQ(group_->first_evictable_error_time(),
storage->stored_eviction_times_[group_->group_id()].second);
}
}
if (!expect_full_update_time_newer_than_.is_null()) {
EXPECT_LT(expect_full_update_time_newer_than_,
group_->last_full_update_check_time());
} else if (!expect_full_update_time_equal_to_.is_null()) {
EXPECT_EQ(expect_full_update_time_equal_to_,
group_->last_full_update_check_time());
}
if (expect_group_has_cache_) {
ASSERT_TRUE(group_->newest_complete_cache() != nullptr);
EXPECT_EQ(group_->newest_complete_cache()->manifest_parser_version(), 2);
if (expect_non_null_update_time_)
EXPECT_TRUE(!group_->newest_complete_cache()->update_time().is_null());
if (expect_old_cache_) {
EXPECT_NE(expect_old_cache_, group_->newest_complete_cache());
EXPECT_TRUE(base::Contains(group_->old_caches(), expect_old_cache_));
}
if (expect_newest_cache_) {
EXPECT_EQ(expect_newest_cache_, group_->newest_complete_cache());
EXPECT_FALSE(
base::Contains(group_->old_caches(), expect_newest_cache_));
} else {
// Tests that don't know which newest cache to expect contain updates
// that succeed (because the update creates a new cache whose pointer
// is unknown to the test). Check group and newest cache were stored
// when update succeeds.
MockAppCacheStorage* storage =
static_cast<MockAppCacheStorage*>(service_->storage());
EXPECT_TRUE(storage->IsGroupStored(group_.get()));
EXPECT_TRUE(storage->IsCacheStored(group_->newest_complete_cache()));
// Check that all entries in the newest cache were stored.
for (const auto& pair : group_->newest_complete_cache()->entries()) {
EXPECT_NE(blink::mojom::kAppCacheNoResponseId,
pair.second.response_id());
// Check that any copied entries have the expected response id
// and that entries that are not copied have a different response id.
auto found = expect_response_ids_.find(pair.first);
if (found != expect_response_ids_.end()) {
EXPECT_EQ(found->second, pair.second.response_id());
} else if (expect_old_cache_) {
AppCacheEntry* old_entry = expect_old_cache_->GetEntry(pair.first);
if (old_entry)
EXPECT_NE(old_entry->response_id(), pair.second.response_id());
}
}
}
} else {
EXPECT_TRUE(group_->newest_complete_cache() == nullptr);
}
// Verify token_expires times on cache.
if (expect_group_has_cache_) {
AppCache* cache = group_->newest_complete_cache();
ASSERT_TRUE(cache);
EXPECT_EQ(cache->token_expires(), expect_token_expires_);
}
// Check expected events.
for (const std::unique_ptr<MockFrontend>& frontend : frontends_) {
MockFrontend::RaisedEvents& expected_events = frontend->expected_events_;
MockFrontend::RaisedEvents& actual_events = frontend->raised_events_;
ASSERT_EQ(expected_events.size(), actual_events.size());
// Check each expected event.
for (size_t j = 0; j < expected_events.size() && j < actual_events.size();
++j) {
EXPECT_EQ(expected_events[j], actual_events[j]);
}
if (!frontend->expected_error_message_.empty()) {
EXPECT_EQ(frontend->expected_error_message_, frontend->error_message_);
}
}
// Verify expected cache contents last as some checks are asserts
// and will abort the test if they fail.
if (tested_manifest_) {
AppCache* cache = group_->newest_complete_cache();
ASSERT_TRUE(cache != nullptr);
EXPECT_EQ(group_.get(), cache->owning_group());
EXPECT_TRUE(cache->is_complete());
switch (tested_manifest_) {
case MANIFEST1:
VerifyManifest1(cache);
break;
case MANIFEST1_WITH_NOTMODIFIED:
VerifyManifest1WithNotModified(cache);
break;
case MANIFEST1_WITH_MAYBEMODIFIED:
VerifyManifest1WithMaybeModified(cache);
break;
case MANIFEST2_WITH_ROOT_SCOPE:
VerifyManifest2WithRootScope(cache);
break;
case MANIFEST2_WITH_FILES_SCOPE:
VerifyManifest2WithFilesScope(cache);
break;
case MANIFEST_MERGED_TYPES:
VerifyManifestMergedTypes(cache);
break;
case EMPTY_MANIFEST:
VerifyEmptyManifest(cache);
break;
case EMPTY_FILE_MANIFEST:
VerifyEmptyFileManifest(cache);
break;
case PENDING_MASTER_NO_UPDATE:
VerifyMasterEntryNoUpdate(cache);
break;
case MANIFEST_WITH_INTERCEPT:
VerifyManifestWithIntercept(cache);
break;
case SCOPE_MANIFEST_ROOT:
VerifyScopeManifest(cache);
break;
case SCOPE_MANIFEST_BAR:
VerifyScopeManifest(cache);
break;
case SCOPE_MANIFEST_OTHER:
VerifyScopeManifest(cache);
break;
case NONE:
default:
break;
}
}
}
void VerifyManifest1(AppCache* cache) {
size_t expected = 3 + expect_extra_entries_.size();
EXPECT_EQ(expected, cache->entries().size());
const char* kManifestPath = tested_manifest_path_override_
? tested_manifest_path_override_
: "files/manifest1";
AppCacheEntry* entry =
cache->GetEntry(MockHttpServer::GetMockUrl(kManifestPath));
ASSERT_TRUE(entry);
EXPECT_EQ(AppCacheEntry::MANIFEST, entry->types());
entry = cache->GetEntry(MockHttpServer::GetMockUrl("files/explicit1"));
ASSERT_TRUE(entry);
EXPECT_TRUE(entry->IsExplicit());
entry = cache->GetEntry(MockHttpServer::GetMockUrl("files/fallback1a"));
ASSERT_TRUE(entry);
EXPECT_EQ(AppCacheEntry::FALLBACK, entry->types());
for (const auto& pair : expect_extra_entries_) {
entry = cache->GetEntry(pair.first);
ASSERT_TRUE(entry);
EXPECT_EQ(pair.second.types(), entry->types());
}
expected = 1;
ASSERT_EQ(expected, cache->fallback_namespaces_.size());
EXPECT_TRUE(
cache->fallback_namespaces_[0] ==
AppCacheNamespace(APPCACHE_FALLBACK_NAMESPACE,
MockHttpServer::GetMockUrl("files/fallback1"),
MockHttpServer::GetMockUrl("files/fallback1a")));
EXPECT_TRUE(cache->online_safelist_namespaces_.empty());
EXPECT_TRUE(cache->online_safelist_all_);
EXPECT_TRUE(cache->update_time_ > base::Time());
}
void VerifyManifest1WithNotModified(AppCache* cache) {
size_t expected = 4 + expect_extra_entries_.size();
EXPECT_EQ(expected, cache->entries().size());
const char* kManifestPath = tested_manifest_path_override_
? tested_manifest_path_override_
: "files/manifest1-with-notmodified";
AppCacheEntry* entry =
cache->GetEntry(MockHttpServer::GetMockUrl(kManifestPath));
ASSERT_TRUE(entry);
EXPECT_EQ(AppCacheEntry::MANIFEST, entry->types());
entry = cache->GetEntry(MockHttpServer::GetMockUrl("files/notmodified"));
ASSERT_TRUE(entry);
EXPECT_TRUE(entry->IsExplicit());
entry = cache->GetEntry(MockHttpServer::GetMockUrl("files/explicit1"));
ASSERT_TRUE(entry);
EXPECT_TRUE(entry->IsExplicit());
entry = cache->GetEntry(MockHttpServer::GetMockUrl("files/fallback1a"));
ASSERT_TRUE(entry);
EXPECT_EQ(AppCacheEntry::FALLBACK, entry->types());
for (const auto& pair : expect_extra_entries_) {
entry = cache->GetEntry(pair.first);
ASSERT_TRUE(entry);
EXPECT_EQ(pair.second.types(), entry->types());
}
expected = 1;
ASSERT_EQ(expected, cache->fallback_namespaces_.size());
EXPECT_TRUE(
cache->fallback_namespaces_[0] ==
AppCacheNamespace(APPCACHE_FALLBACK_NAMESPACE,
MockHttpServer::GetMockUrl("files/fallback1"),
MockHttpServer::GetMockUrl("files/fallback1a")));
EXPECT_TRUE(cache->online_safelist_namespaces_.empty());
EXPECT_TRUE(cache->online_safelist_all_);
EXPECT_TRUE(cache->update_time_ > base::Time());
}
void VerifyManifest1WithMaybeModified(AppCache* cache) {
size_t expected = 4 + expect_extra_entries_.size();
EXPECT_EQ(expected, cache->entries().size());
const char* kManifestPath = tested_manifest_path_override_
? tested_manifest_path_override_
: "files/manifest1-with-maybemodified";
AppCacheEntry* entry =
cache->GetEntry(MockHttpServer::GetMockUrl(kManifestPath));
ASSERT_TRUE(entry);
EXPECT_EQ(AppCacheEntry::MANIFEST, entry->types());
entry = cache->GetEntry(MockHttpServer::GetMockUrl("files/maybemodified"));
ASSERT_TRUE(entry);
EXPECT_TRUE(entry->IsExplicit());
entry = cache->GetEntry(MockHttpServer::GetMockUrl("files/explicit1"));
ASSERT_TRUE(entry);
EXPECT_TRUE(entry->IsExplicit());
entry = cache->GetEntry(MockHttpServer::GetMockUrl("files/fallback1a"));
ASSERT_TRUE(entry);
EXPECT_EQ(AppCacheEntry::FALLBACK, entry->types());
for (const auto& pair : expect_extra_entries_) {
entry = cache->GetEntry(pair.first);
ASSERT_TRUE(entry);
EXPECT_EQ(pair.second.types(), entry->types());
}
expected = 1;
ASSERT_EQ(expected, cache->fallback_namespaces_.size());
EXPECT_TRUE(
cache->fallback_namespaces_[0] ==
AppCacheNamespace(APPCACHE_FALLBACK_NAMESPACE,
MockHttpServer::GetMockUrl("files/fallback1"),
MockHttpServer::GetMockUrl("files/fallback1a")));
EXPECT_TRUE(cache->online_safelist_namespaces_.empty());
EXPECT_TRUE(cache->online_safelist_all_);
EXPECT_TRUE(cache->update_time_ > base::Time());
}
void VerifyManifest2WithRootScope(AppCache* cache) {
size_t expected = 6 + expect_extra_entries_.size();
EXPECT_EQ(expected, cache->entries().size());
const char* kManifestPath = tested_manifest_path_override_
? tested_manifest_path_override_
: "files/manifest2-with-root-override";
AppCacheEntry* entry =
cache->GetEntry(MockHttpServer::GetMockUrl(kManifestPath));
ASSERT_TRUE(entry);
EXPECT_TRUE(entry->IsManifest());
entry = cache->GetEntry(MockHttpServer::GetMockUrl("files/explicit1"));
ASSERT_TRUE(entry);
EXPECT_TRUE(entry->IsExplicit());
entry = cache->GetEntry(MockHttpServer::GetMockUrl("files/fallback1a"));
ASSERT_TRUE(entry);
EXPECT_TRUE(entry->IsFallback());
entry = cache->GetEntry(MockHttpServer::GetMockUrl("files/fallback2a"));
ASSERT_TRUE(entry);
EXPECT_TRUE(entry->IsFallback());
entry = cache->GetEntry(MockHttpServer::GetMockUrl("files/intercept1a"));
ASSERT_TRUE(entry);
EXPECT_TRUE(entry->IsIntercept());
entry = cache->GetEntry(MockHttpServer::GetMockUrl("files/intercept2a"));
ASSERT_TRUE(entry);
EXPECT_TRUE(entry->IsIntercept());
for (const auto& pair : expect_extra_entries_) {
entry = cache->GetEntry(pair.first);
ASSERT_TRUE(entry);
EXPECT_EQ(pair.second.types(), entry->types());
}
expected = 2;
ASSERT_EQ(expected, cache->fallback_namespaces_.size());
EXPECT_TRUE(
cache->fallback_namespaces_[0] ==
AppCacheNamespace(APPCACHE_FALLBACK_NAMESPACE,
MockHttpServer::GetMockUrl("files/fallback1"),
MockHttpServer::GetMockUrl("files/fallback1a")));
EXPECT_TRUE(
cache->fallback_namespaces_[1] ==
AppCacheNamespace(APPCACHE_FALLBACK_NAMESPACE,
MockHttpServer::GetMockUrl("bar/fallback2"),
MockHttpServer::GetMockUrl("files/fallback2a")));
EXPECT_TRUE(cache->online_safelist_namespaces_.empty());
EXPECT_TRUE(cache->online_safelist_all_);
EXPECT_TRUE(cache->update_time_ > base::Time());
}
void VerifyManifest2WithFilesScope(AppCache* cache) {
size_t expected = 4 + expect_extra_entries_.size();
EXPECT_EQ(expected, cache->entries().size());
const char* kManifestPath = tested_manifest_path_override_
? tested_manifest_path_override_
: "files/manifest2";
AppCacheEntry* entry =
cache->GetEntry(MockHttpServer::GetMockUrl(kManifestPath));
ASSERT_TRUE(entry);
EXPECT_EQ(AppCacheEntry::MANIFEST, entry->types());
EXPECT_TRUE(entry->IsManifest());
entry = cache->GetEntry(MockHttpServer::GetMockUrl("files/explicit1"));
ASSERT_TRUE(entry);
EXPECT_TRUE(entry->IsExplicit());
entry = cache->GetEntry(MockHttpServer::GetMockUrl("files/intercept1a"));
ASSERT_TRUE(entry);
EXPECT_TRUE(entry->IsIntercept());
entry = cache->GetEntry(MockHttpServer::GetMockUrl("files/fallback1a"));
ASSERT_TRUE(entry);
EXPECT_TRUE(entry->IsFallback());
for (const auto& pair : expect_extra_entries_) {
entry = cache->GetEntry(pair.first);
ASSERT_TRUE(entry);
EXPECT_EQ(pair.second.types(), entry->types());
}
expected = 1;
ASSERT_EQ(expected, cache->fallback_namespaces_.size());
EXPECT_TRUE(
cache->fallback_namespaces_[0] ==
AppCacheNamespace(APPCACHE_FALLBACK_NAMESPACE,
MockHttpServer::GetMockUrl("files/fallback1"),
MockHttpServer::GetMockUrl("files/fallback1a")));
EXPECT_TRUE(cache->online_safelist_namespaces_.empty());
EXPECT_TRUE(cache->online_safelist_all_);
EXPECT_TRUE(cache->update_time_ > base::Time());
}
void VerifyManifestMergedTypes(AppCache* cache) {
size_t expected = 2;
EXPECT_EQ(expected, cache->entries().size());
AppCacheEntry* entry = cache->GetEntry(
MockHttpServer::GetMockUrl("files/manifest-merged-types"));
ASSERT_TRUE(entry);
EXPECT_EQ(AppCacheEntry::EXPLICIT | AppCacheEntry::MANIFEST,
entry->types());
entry = cache->GetEntry(MockHttpServer::GetMockUrl("files/explicit1"));
ASSERT_TRUE(entry);
EXPECT_EQ(AppCacheEntry::EXPLICIT | AppCacheEntry::FALLBACK |
AppCacheEntry::MASTER,
entry->types());
expected = 1;
ASSERT_EQ(expected, cache->fallback_namespaces_.size());
EXPECT_TRUE(
cache->fallback_namespaces_[0] ==
AppCacheNamespace(APPCACHE_FALLBACK_NAMESPACE,
MockHttpServer::GetMockUrl("files/fallback1"),
MockHttpServer::GetMockUrl("files/explicit1")));
EXPECT_EQ(expected, cache->online_safelist_namespaces_.size());
EXPECT_TRUE(cache->online_safelist_namespaces_[0] ==
AppCacheNamespace(APPCACHE_NETWORK_NAMESPACE,
MockHttpServer::GetMockUrl("files/online1"),
GURL()));
EXPECT_FALSE(cache->online_safelist_all_);
EXPECT_TRUE(cache->update_time_ > base::Time());
}
void VerifyEmptyManifest(AppCache* cache) {
const char* kManifestPath = tested_manifest_path_override_
? tested_manifest_path_override_
: "files/empty-manifest";
size_t expected = 1;
EXPECT_EQ(expected, cache->entries().size());
AppCacheEntry* entry =
cache->GetEntry(MockHttpServer::GetMockUrl(kManifestPath));
ASSERT_TRUE(entry);
EXPECT_EQ(AppCacheEntry::MANIFEST, entry->types());
EXPECT_TRUE(cache->fallback_namespaces_.empty());
EXPECT_TRUE(cache->online_safelist_namespaces_.empty());
EXPECT_FALSE(cache->online_safelist_all_);
EXPECT_TRUE(cache->update_time_ > base::Time());
}
void VerifyEmptyFileManifest(AppCache* cache) {
EXPECT_EQ(size_t(2), cache->entries().size());
AppCacheEntry* entry = cache->GetEntry(
MockHttpServer::GetMockUrl("files/empty-file-manifest"));
ASSERT_TRUE(entry);
EXPECT_EQ(AppCacheEntry::MANIFEST, entry->types());
entry = cache->GetEntry(MockHttpServer::GetMockUrl("files/empty1"));
ASSERT_TRUE(entry);
EXPECT_EQ(AppCacheEntry::EXPLICIT, entry->types());
EXPECT_TRUE(entry->has_response_id());
EXPECT_TRUE(cache->fallback_namespaces_.empty());
EXPECT_TRUE(cache->online_safelist_namespaces_.empty());
EXPECT_FALSE(cache->online_safelist_all_);
EXPECT_TRUE(cache->update_time_ > base::Time());
}
void VerifyMasterEntryNoUpdate(AppCache* cache) {
EXPECT_EQ(size_t(3), cache->entries().size());
AppCacheEntry* entry =
cache->GetEntry(MockHttpServer::GetMockUrl("files/notmodified"));
ASSERT_TRUE(entry);
EXPECT_EQ(AppCacheEntry::MANIFEST, entry->types());
entry = cache->GetEntry(MockHttpServer::GetMockUrl("files/explicit1"));
ASSERT_TRUE(entry);
EXPECT_EQ(AppCacheEntry::MASTER, entry->types());
EXPECT_TRUE(entry->has_response_id());
entry = cache->GetEntry(MockHttpServer::GetMockUrl("files/explicit2"));
ASSERT_TRUE(entry);
EXPECT_EQ(AppCacheEntry::EXPLICIT | AppCacheEntry::MASTER, entry->types());
EXPECT_TRUE(entry->has_response_id());
EXPECT_TRUE(cache->fallback_namespaces_.empty());
EXPECT_TRUE(cache->online_safelist_namespaces_.empty());
EXPECT_FALSE(cache->online_safelist_all_);
EXPECT_TRUE(cache->update_time_ > base::Time());
}
void VerifyManifestWithIntercept(AppCache* cache) {
EXPECT_EQ(2u, cache->entries().size());
const char* kManifestPath = "files/manifest-with-intercept";
AppCacheEntry* entry =
cache->GetEntry(MockHttpServer::GetMockUrl(kManifestPath));
ASSERT_TRUE(entry);
EXPECT_EQ(AppCacheEntry::MANIFEST, entry->types());
entry = cache->GetEntry(MockHttpServer::GetMockUrl("files/intercept1a"));
ASSERT_TRUE(entry);
EXPECT_TRUE(entry->IsIntercept());
}
bool CheckNamespaceExists(const std::vector<AppCacheNamespace>& namespaces,
const AppCacheNamespace& namespace_entry) {
for (const AppCacheNamespace& appcache_namespace : namespaces) {
if (appcache_namespace == namespace_entry)
return true;
}
return false;
}
std::vector<AppCacheNamespace> GetExpectedScopeIntercepts() {
std::vector<AppCacheNamespace> expected;
// Set up the expected intercepts.
std::vector<AppCacheNamespace> expected_root = {
AppCacheNamespace(APPCACHE_INTERCEPT_NAMESPACE,
MockHttpServer::GetMockUrl(""),
MockHttpServer::GetMockUrl("intercept-newroot/foo")),
};
std::vector<AppCacheNamespace> expected_bar = {
AppCacheNamespace(APPCACHE_INTERCEPT_NAMESPACE,
MockHttpServer::GetMockUrl("bar/foo"),
MockHttpServer::GetMockUrl("intercept-newbar/foo")),
AppCacheNamespace(
APPCACHE_INTERCEPT_NAMESPACE,
MockHttpServer::GetMockUrl("bar/baz/foo"),
MockHttpServer::GetMockUrl("intercept-newbar/newbaz/foo")),
};
std::vector<AppCacheNamespace> expected_other = {
AppCacheNamespace(APPCACHE_INTERCEPT_NAMESPACE,
MockHttpServer::GetMockUrl("other/foo"),
MockHttpServer::GetMockUrl("intercept-newother/foo")),
};
switch (tested_manifest_) {
case SCOPE_MANIFEST_ROOT:
expected.insert(expected.end(), std::begin(expected_root),
std::end(expected_root));
expected.insert(expected.end(), std::begin(expected_bar),
std::end(expected_bar));
expected.insert(expected.end(), std::begin(expected_other),
std::end(expected_other));
break;
case SCOPE_MANIFEST_BAR:
expected.insert(expected.end(), std::begin(expected_bar),
std::end(expected_bar));
break;
case SCOPE_MANIFEST_OTHER:
expected.insert(expected.end(), std::begin(expected_other),
std::end(expected_other));
break;
default:
NOTREACHED();
}
return expected;
}
std::vector<AppCacheNamespace> GetExpectedScopeFallbacks() {
std::vector<AppCacheNamespace> expected;
// Set up the expected fallbacks.
std::vector<AppCacheNamespace> expected_root = {
AppCacheNamespace(APPCACHE_FALLBACK_NAMESPACE,
MockHttpServer::GetMockUrl(""),
MockHttpServer::GetMockUrl("fallback-newroot/foo")),
};
std::vector<AppCacheNamespace> expected_bar = {
AppCacheNamespace(APPCACHE_FALLBACK_NAMESPACE,
MockHttpServer::GetMockUrl("bar/foo"),
MockHttpServer::GetMockUrl("fallback-newbar/foo")),
AppCacheNamespace(
APPCACHE_FALLBACK_NAMESPACE,
MockHttpServer::GetMockUrl("bar/baz/foo"),
MockHttpServer::GetMockUrl("fallback-newbar/newbaz/foo")),
};
std::vector<AppCacheNamespace> expected_other = {
AppCacheNamespace(APPCACHE_FALLBACK_NAMESPACE,
MockHttpServer::GetMockUrl("other/foo"),
MockHttpServer::GetMockUrl("fallback-newother/foo")),
};
switch (tested_manifest_) {
case SCOPE_MANIFEST_ROOT:
expected.insert(expected.end(), std::begin(expected_root),
std::end(expected_root));
expected.insert(expected.end(), std::begin(expected_bar),
std::end(expected_bar));
expected.insert(expected.end(), std::begin(expected_other),
std::end(expected_other));
break;
case SCOPE_MANIFEST_BAR:
expected.insert(expected.end(), std::begin(expected_bar),
std::end(expected_bar));
break;
case SCOPE_MANIFEST_OTHER:
expected.insert(expected.end(), std::begin(expected_other),
std::end(expected_other));
break;
default:
NOTREACHED();
}
return expected;
}
void VerifyScopeManifest(AppCache* cache) {
std::vector<AppCacheNamespace> expected_intercepts =
GetExpectedScopeIntercepts();
std::vector<AppCacheNamespace> expected_fallbacks =
GetExpectedScopeFallbacks();
// Verify the number of cache entries. There should be cache entries for
// the manifest, all of the expected intercepts (because each defines a
// separate target), and all of the expected fallbacks (because each also
// defines a separate target).
size_t expected_size =
1 + expected_intercepts.size() + expected_fallbacks.size();
EXPECT_EQ(expected_size, cache->entries().size());
// Verify basic cache details.
EXPECT_TRUE(cache->online_safelist_namespaces_.empty());
EXPECT_FALSE(cache->online_safelist_all_);
EXPECT_TRUE(cache->update_time_ > base::Time());
// Verify manifest.
ASSERT_TRUE(!std::string(tested_manifest_path_override_).empty());
AppCacheEntry* entry = cache->GetEntry(
MockHttpServer::GetMockUrl(tested_manifest_path_override_));
ASSERT_TRUE(entry);
EXPECT_EQ(AppCacheEntry::MANIFEST, entry->types());
// Verify intercepts.
ASSERT_EQ(expected_intercepts.size(), cache->intercept_namespaces_.size());
for (auto it = expected_intercepts.begin(); it != expected_intercepts.end();
++it) {
EXPECT_EQ(it->type, APPCACHE_INTERCEPT_NAMESPACE);
entry = cache->GetEntry(it->target_url);
ASSERT_TRUE(entry);
EXPECT_TRUE(entry->IsIntercept());
EXPECT_TRUE(CheckNamespaceExists(cache->intercept_namespaces_, *it));
}
// Verify fallbacks.
for (auto it = expected_fallbacks.begin(); it != expected_fallbacks.end();
++it) {
EXPECT_EQ(it->type, APPCACHE_FALLBACK_NAMESPACE);
entry = cache->GetEntry(it->target_url);
ASSERT_TRUE(entry);
EXPECT_TRUE(entry->IsFallback());
EXPECT_TRUE(CheckNamespaceExists(cache->fallback_namespaces_, *it));
}
}
private:
// Various manifest files used in this test.
enum TestedManifest {
NONE,
MANIFEST1,
MANIFEST1_WITH_MAYBEMODIFIED,
MANIFEST1_WITH_NOTMODIFIED,
MANIFEST2_WITH_ROOT_SCOPE,
MANIFEST2_WITH_FILES_SCOPE,
MANIFEST_MERGED_TYPES,
EMPTY_MANIFEST,
EMPTY_FILE_MANIFEST,
PENDING_MASTER_NO_UPDATE,
MANIFEST_WITH_INTERCEPT,
SCOPE_MANIFEST_ROOT,
SCOPE_MANIFEST_BAR,
SCOPE_MANIFEST_OTHER,
};
void ScopeTest(const char* tested_manifest_path,
const TestedManifest& tested_manifest) {
GURL manifest_url = MockHttpServer::GetMockUrl(tested_manifest_path);
MakeService();
group_ = base::MakeRefCounted<AppCacheGroup>(
service_->storage(), manifest_url, service_->storage()->NewGroupId());
AppCacheUpdateJob* update =
new AppCacheUpdateJob(service_.get(), group_.get());
group_->update_job_ = update;
ASSERT_TRUE(group_->last_full_update_check_time().is_null());
MockFrontend* frontend = MakeMockFrontend();
AppCacheHost* host = MakeHost(frontend);
update->StartUpdate(host, GURL());
// Set up checks for when update job finishes.
do_checks_after_update_finished_ = true;
expect_group_obsolete_ = false;
expect_group_has_cache_ = true;
expect_full_update_time_newer_than_ = base::Time::Now() - kOneHour;
tested_manifest_ = tested_manifest;
tested_manifest_path_override_ = tested_manifest_path;
frontend->AddExpectedEvent(
blink::mojom::AppCacheEventID::APPCACHE_CHECKING_EVENT);
// Other events are only sent to associated hosts.
WaitForUpdateToFinish();
}
void AddExpectedProgressEvents(MockFrontend* frontend,
const int& number_of_progress_events) {
for (auto i = 0; i < number_of_progress_events; ++i) {
frontend->AddExpectedEvent(
blink::mojom::AppCacheEventID::APPCACHE_PROGRESS_EVENT);
}
}
void Scope304Test(const char* tested_manifest_path,
const std::string& previous_scope,
const TestedManifest& tested_manifest) {
GURL manifest_url = MockHttpServer::GetMockUrl(tested_manifest_path);
MakeService();
group_ = base::MakeRefCounted<AppCacheGroup>(
service_->storage(), manifest_url, service_->storage()->NewGroupId());
AppCacheUpdateJob* update =
new AppCacheUpdateJob(service_.get(), group_.get());
group_->update_job_ = update;
// Create response writer to get a response id.
response_writer_ =
service_->storage()->CreateResponseWriter(group_->manifest_url());
int64_t manifest_response_id = response_writer_->response_id();
AppCache* cache = MakeCacheForGroup(service_->storage()->NewCacheId(),
manifest_response_id);
cache->set_manifest_scope(previous_scope);
MockFrontend* frontend1 = MakeMockFrontend();
MockFrontend* frontend2 = MakeMockFrontend();
AppCacheHost* host1 = MakeHost(frontend1);
AppCacheHost* host2 = MakeHost(frontend2);
host1->AssociateCompleteCache(cache);
host2->AssociateCompleteCache(cache);
// Seed the response_info working set with canned data so that an existing
// manifest is reused by the update job.
const char kData[] =
"HTTP/1.1 200 OK\0"
"Last-Modified: Sat, 29 Oct 1994 19:43:31 GMT\0"
"\0";
const std::string kRawHeaders(kData, base::size(kData));
MakeAppCacheResponseInfo(manifest_url, manifest_response_id, kRawHeaders);
// Set up checks for when update job finishes.
do_checks_after_update_finished_ = true;
expect_group_obsolete_ = false;
expect_group_has_cache_ = true;
expect_old_cache_ = cache;
tested_manifest_ = tested_manifest;
tested_manifest_path_override_ = tested_manifest_path;
int number_of_progress_events = 0;
switch (tested_manifest_) {
case SCOPE_MANIFEST_ROOT:
number_of_progress_events = 9;
break;
case SCOPE_MANIFEST_BAR:
number_of_progress_events = 5;
break;
case SCOPE_MANIFEST_OTHER:
number_of_progress_events = 3;
break;
default:
NOTREACHED();
}
frontend1->AddExpectedEvent(
blink::mojom::AppCacheEventID::APPCACHE_CHECKING_EVENT);
frontend1->AddExpectedEvent(
blink::mojom::AppCacheEventID::APPCACHE_DOWNLOADING_EVENT);
AddExpectedProgressEvents(frontend1, number_of_progress_events);
frontend1->AddExpectedEvent(
blink::mojom::AppCacheEventID::APPCACHE_UPDATE_READY_EVENT);
frontend2->AddExpectedEvent(
blink::mojom::AppCacheEventID::APPCACHE_CHECKING_EVENT);
frontend2->AddExpectedEvent(
blink::mojom::AppCacheEventID::APPCACHE_DOWNLOADING_EVENT);
AddExpectedProgressEvents(frontend2, number_of_progress_events);
frontend2->AddExpectedEvent(
blink::mojom::AppCacheEventID::APPCACHE_UPDATE_READY_EVENT);
// Seed storage with kScopeManifestContents so a possible scope change will
// result in reading this manifest data from the cache and re-parsing it
// with the new scope.
const std::string seed_data(kScopeManifestContents);
scoped_refptr<net::StringIOBuffer> io_buffer =
base::MakeRefCounted<net::StringIOBuffer>(seed_data);
response_writer_->WriteData(
io_buffer.get(), seed_data.length(),
base::BindOnce(
&AppCacheUpdateJobTest::StartUpdateAfterSeedingStorageData,
base::Unretained(this)));
// Start update after data write completes asynchronously.
}
void RetryRequestTest(bool expect_group_has_cache) {
MakeService();
group_ = base::MakeRefCounted<AppCacheGroup>(
service_->storage(), GURL(RetryRequestTestJob::kRetryUrl),
service_->storage()->NewGroupId());
AppCacheUpdateJob* update =
new AppCacheUpdateJob(service_.get(), group_.get());
group_->update_job_ = update;
MockFrontend* frontend = MakeMockFrontend();
AppCacheHost* host = MakeHost(frontend);
update->StartUpdate(host, GURL());
// Set up checks for when update job finishes.
do_checks_after_update_finished_ = true;
expect_group_obsolete_ = false;
expect_group_has_cache_ = expect_group_has_cache;
frontend->AddExpectedEvent(
blink::mojom::AppCacheEventID::APPCACHE_CHECKING_EVENT);
WaitForUpdateToFinish();
}
content::BrowserTaskEnvironment task_environment_;
std::unique_ptr<MockAppCacheService> service_;
scoped_refptr<AppCacheGroup> group_;
scoped_refptr<AppCache> protect_newest_cache_;
base::OnceClosure test_completed_cb_;
base::OnceClosure post_update_finished_cb_;
std::unique_ptr<AppCacheResponseWriter> response_writer_;
std::unique_ptr<AppCacheCacheTestHelper> cache_helper_;
// Hosts used by an async test that need to live until update job finishes.
// Otherwise, test can put host on the stack instead of here.
std::vector<std::unique_ptr<AppCacheHost>> hosts_;
// Response infos used by an async test that need to live until update job
// finishes.
std::vector<scoped_refptr<AppCacheResponseInfo>> response_infos_;
// Flag indicating if test cares to verify the update after update finishes.
bool do_checks_after_update_finished_;
bool expect_group_obsolete_;
bool expect_group_has_cache_;
bool expect_group_is_being_deleted_;
bool expect_evictable_error_;
bool expect_eviction_;
base::Time expect_full_update_time_newer_than_;
base::Time expect_full_update_time_equal_to_;
base::Time expect_token_expires_;
AppCache* expect_old_cache_;
AppCache* expect_newest_cache_;
bool expect_non_null_update_time_;
std::vector<std::unique_ptr<MockFrontend>>
frontends_; // to check expected events
TestedManifest tested_manifest_;
const char* tested_manifest_path_override_;
AppCache::EntryMap expect_extra_entries_;
std::map<GURL, int64_t> expect_response_ids_;
URLLoaderInterceptor interceptor_;
const int process_id_;
std::map<GURL, std::unique_ptr<HttpHeadersRequestTestJob>>
http_headers_request_test_jobs_;
base::test::ScopedFeatureList appcache_require_origin_trial_feature_;
base::test::ScopedFeatureList appcache_disable_corruption_feature_;
// Lazily create these to avoid data races in the FeatureList between
// service workers (reading) and appcache tests (writing).
std::unique_ptr<content::TestBrowserContext> browser_context_;
std::unique_ptr<base::WeakPtrFactory<StoragePartitionImpl>>
weak_partition_factory_;
};
class AppCacheUpdateJobOriginTrialTest : public AppCacheUpdateJobTest {
public:
AppCacheUpdateJobOriginTrialTest() {
scoped_feature_list_.InitAndEnableFeature(
blink::features::kAppCacheRequireOriginTrial);
}
private:
base::test::ScopedFeatureList scoped_feature_list_;
};
class AppCacheUpdateJobNoUpdateOn304Test : public AppCacheUpdateJobTest {
public:
AppCacheUpdateJobNoUpdateOn304Test() = default;
};
class AppCacheUpdateJobWithCorruptionRecoveryTest
: public AppCacheUpdateJobTest {
public:
AppCacheUpdateJobWithCorruptionRecoveryTest() {
scoped_feature_list_.InitAndEnableFeature(
kAppCacheCorruptionRecoveryFeature);
}
private:
base::test::ScopedFeatureList scoped_feature_list_;
};
class AppCacheUpdateJobWithoutCorruptionRecoveryTest
: public AppCacheUpdateJobTest {
public:
AppCacheUpdateJobWithoutCorruptionRecoveryTest() {
scoped_feature_list_.InitAndDisableFeature(
kAppCacheCorruptionRecoveryFeature);
}
private:
base::test::ScopedFeatureList scoped_feature_list_;
};
TEST_F(AppCacheUpdateJobTest, AlreadyChecking) {
MockAppCacheService service;
auto group = base::MakeRefCounted<AppCacheGroup>(
service.storage(), GURL("http://manifesturl.com"),
service.storage()->NewGroupId());
AppCacheUpdateJob update(&service, group.get());
// Pretend group is in checking state.
group->update_job_ = &update;
group->update_status_ = AppCacheGroup::CHECKING;
update.StartUpdate(nullptr, GURL());
EXPECT_EQ(AppCacheGroup::CHECKING, group->update_status());
MockFrontend mock_frontend;
const int kMockProcessId1 = 1;
AppCacheHost host(/*host_id=*/base::UnguessableToken::Create(),
kMockProcessId1, /*render_frame_id=*/1,
ChildProcessSecurityPolicyImpl::GetInstance()->CreateHandle(
kMockProcessId1),
mojo::NullRemote(), &service);
host.set_frontend_for_testing(&mock_frontend);
update.StartUpdate(&host, GURL());
MockFrontend::RaisedEvents events = mock_frontend.raised_events_;
ASSERT_EQ(1u, events.size());
EXPECT_EQ(blink::mojom::AppCacheEventID::APPCACHE_CHECKING_EVENT, events[0]);
EXPECT_EQ(AppCacheGroup::CHECKING, group->update_status());
}
TEST_F(AppCacheUpdateJobTest, AlreadyDownloading) {
MockAppCacheService service;
auto group = base::MakeRefCounted<AppCacheGroup>(
service.storage(), GURL("http://manifesturl.com"),
service.storage()->NewGroupId());
AppCacheUpdateJob update(&service, group.get());
// Pretend group is in downloading state.
group->update_job_ = &update;
group->update_status_ = AppCacheGroup::DOWNLOADING;
update.StartUpdate(nullptr, GURL());
EXPECT_EQ(AppCacheGroup::DOWNLOADING, group->update_status());
MockFrontend mock_frontend;
const int kMockProcessId1 = 1;
AppCacheHost host(/*host_id=*/base::UnguessableToken::Create(),
kMockProcessId1, /*render_frame_id=*/1,
ChildProcessSecurityPolicyImpl::GetInstance()->CreateHandle(
kMockProcessId1),
mojo::NullRemote(), &service);
host.set_frontend_for_testing(&mock_frontend);
update.StartUpdate(&host, GURL());
MockFrontend::RaisedEvents events = mock_frontend.raised_events_;
ASSERT_EQ(2u, events.size());
EXPECT_EQ(blink::mojom::AppCacheEventID::APPCACHE_CHECKING_EVENT, events[0]);
EXPECT_EQ(blink::mojom::AppCacheEventID::APPCACHE_DOWNLOADING_EVENT,
events[1]);
EXPECT_EQ(AppCacheGroup::DOWNLOADING, group->update_status());
}
TEST_F(AppCacheUpdateJobTest, StartCacheAttempt) {
RunTestOnUIThread(&AppCacheUpdateJobTest::StartCacheAttemptTest);
}
TEST_F(AppCacheUpdateJobTest, StartUpgradeAttempt) {
RunTestOnUIThread(&AppCacheUpdateJobTest::StartUpgradeAttemptTest);
}
TEST_F(AppCacheUpdateJobTest, CacheAttemptFetchManifestFail) {
RunTestOnUIThread(&AppCacheUpdateJobTest::CacheAttemptFetchManifestFailTest);
}
TEST_F(AppCacheUpdateJobTest, UpgradeFetchManifestFail) {
RunTestOnUIThread(&AppCacheUpdateJobTest::UpgradeFetchManifestFailTest);
}
TEST_F(AppCacheUpdateJobTest, ManifestRedirect) {
RunTestOnUIThread(&AppCacheUpdateJobTest::ManifestRedirectTest);
}
TEST_F(AppCacheUpdateJobTest, ManifestMissingMimeTypeTest) {
RunTestOnUIThread(&AppCacheUpdateJobTest::ManifestMissingMimeTypeTest);
}
TEST_F(AppCacheUpdateJobOriginTrialTest, ManifestNotFound) {
RunTestOnUIThread(&AppCacheUpdateJobTest::ManifestNotFoundTest);
}
TEST_F(AppCacheUpdateJobOriginTrialTest, ManifestGoneFetch) {
RunTestOnUIThread(&AppCacheUpdateJobTest::ManifestGoneFetchTest);
}
TEST_F(AppCacheUpdateJobOriginTrialTest, ManifestGoneUpgrade) {
RunTestOnUIThread(&AppCacheUpdateJobTest::ManifestGoneUpgradeTest);
}
TEST_F(AppCacheUpdateJobTest, CacheAttemptNotModified) {
RunTestOnUIThread(&AppCacheUpdateJobTest::CacheAttemptNotModifiedTest);
}
TEST_F(AppCacheUpdateJobTest, UpgradeNotModified) {
RunTestOnUIThread(&AppCacheUpdateJobTest::UpgradeNotModifiedTest);
}
TEST_F(AppCacheUpdateJobTest, UpgradeNotModifiedVersion1) {
RunTestOnUIThread(&AppCacheUpdateJobTest::UpgradeNotModifiedVersion1Test);
}
TEST_F(AppCacheUpdateJobTest, UpgradeManifestDataChangedScopeUnchanged) {
RunTestOnUIThread(
&AppCacheUpdateJobTest::UpgradeManifestDataChangedScopeUnchangedTest);
}
TEST_F(AppCacheUpdateJobTest, UpgradeManifestDataChangedScopeChanged) {
RunTestOnUIThread(
&AppCacheUpdateJobTest::UpgradeManifestDataChangedScopeChangedTest);
}
TEST_F(AppCacheUpdateJobTest, UpgradeManifestDataUnchangedScopeUnchanged) {
RunTestOnUIThread(
&AppCacheUpdateJobTest::UpgradeManifestDataUnchangedScopeUnchangedTest);
}
TEST_F(AppCacheUpdateJobTest, UpgradeManifestDataUnchangedScopeChanged) {
RunTestOnUIThread(
&AppCacheUpdateJobTest::UpgradeManifestDataUnchangedScopeChangedTest);
}
TEST_F(AppCacheUpdateJobTest, Bug95101Test) {
RunTestOnUIThread(&AppCacheUpdateJobTest::Bug95101Test);
}
TEST_F(AppCacheUpdateJobTest, BasicCacheAttemptSuccess) {
RunTestOnUIThread(&AppCacheUpdateJobTest::BasicCacheAttemptSuccessTest);
}
TEST_F(AppCacheUpdateJobTest, ScopeManifestRootWithNoOverrideTest) {
RunTestOnUIThread(
&AppCacheUpdateJobTest::ScopeManifestRootWithNoOverrideTest);
}
TEST_F(AppCacheUpdateJobTest, ScopeManifestBarWithNoOverrideTest) {
RunTestOnUIThread(&AppCacheUpdateJobTest::ScopeManifestBarWithNoOverrideTest);
}
TEST_F(AppCacheUpdateJobTest, ScopeManifestRootWithRootOverrideTest) {
RunTestOnUIThread(
&AppCacheUpdateJobTest::ScopeManifestRootWithRootOverrideTest);
}
TEST_F(AppCacheUpdateJobTest, ScopeManifestBarWithRootOverrideTest) {
RunTestOnUIThread(
&AppCacheUpdateJobTest::ScopeManifestBarWithRootOverrideTest);
}
TEST_F(AppCacheUpdateJobTest, ScopeManifestRootWithBarOverrideTest) {
RunTestOnUIThread(
&AppCacheUpdateJobTest::ScopeManifestRootWithBarOverrideTest);
}
TEST_F(AppCacheUpdateJobTest, ScopeManifestBarWithBarOverrideTest) {
RunTestOnUIThread(
&AppCacheUpdateJobTest::ScopeManifestBarWithBarOverrideTest);
}
TEST_F(AppCacheUpdateJobTest, ScopeManifestRootWithOtherOverrideTest) {
RunTestOnUIThread(
&AppCacheUpdateJobTest::ScopeManifestRootWithOtherOverrideTest);
}
TEST_F(AppCacheUpdateJobTest, ScopeManifestBarWithOtherOverrideTest) {
RunTestOnUIThread(
&AppCacheUpdateJobTest::ScopeManifestBarWithOtherOverrideTest);
}
TEST_F(AppCacheUpdateJobTest, ScopeManifest304RootWithNoOverrideTest) {
RunTestOnUIThread(
&AppCacheUpdateJobTest::ScopeManifest304RootWithNoOverrideTest);
}
TEST_F(AppCacheUpdateJobTest, ScopeManifest304BarWithNoOverrideTest) {
RunTestOnUIThread(
&AppCacheUpdateJobTest::ScopeManifest304BarWithNoOverrideTest);
}
TEST_F(AppCacheUpdateJobTest, ScopeManifest304RootWithRootOverrideTest) {
RunTestOnUIThread(
&AppCacheUpdateJobTest::ScopeManifest304RootWithRootOverrideTest);
}
TEST_F(AppCacheUpdateJobTest, ScopeManifest304BarWithRootOverrideTest) {
RunTestOnUIThread(
&AppCacheUpdateJobTest::ScopeManifest304BarWithRootOverrideTest);
}
TEST_F(AppCacheUpdateJobTest, ScopeManifest304RootWithBarOverrideTest) {
RunTestOnUIThread(
&AppCacheUpdateJobTest::ScopeManifest304RootWithBarOverrideTest);
}
TEST_F(AppCacheUpdateJobTest, ScopeManifest304BarWithBarOverrideTest) {
RunTestOnUIThread(
&AppCacheUpdateJobTest::ScopeManifest304BarWithBarOverrideTest);
}
TEST_F(AppCacheUpdateJobTest, ScopeManifest304RootWithOtherOverrideTest) {
RunTestOnUIThread(
&AppCacheUpdateJobTest::ScopeManifest304RootWithOtherOverrideTest);
}
TEST_F(AppCacheUpdateJobTest, ScopeManifest304BarWithOtherOverrideTest) {
RunTestOnUIThread(
&AppCacheUpdateJobTest::ScopeManifest304BarWithOtherOverrideTest);
}
TEST_F(AppCacheUpdateJobTest, DownloadInterceptEntriesTest) {
RunTestOnUIThread(&AppCacheUpdateJobTest::DownloadInterceptEntriesTest);
}
TEST_F(AppCacheUpdateJobTest, BasicUpgradeSuccess) {
RunTestOnUIThread(&AppCacheUpdateJobTest::BasicUpgradeSuccessTest);
}
TEST_F(AppCacheUpdateJobTest, UpgradeLoadFromNewestCache) {
RunTestOnUIThread(&AppCacheUpdateJobTest::UpgradeLoadFromNewestCacheTest);
}
TEST_F(AppCacheUpdateJobTest, UpgradeNoLoadFromNewestCache) {
RunTestOnUIThread(&AppCacheUpdateJobTest::UpgradeNoLoadFromNewestCacheTest);
}
TEST_F(AppCacheUpdateJobTest, UpgradeLoadFromNewestCacheVaryHeader) {
RunTestOnUIThread(
&AppCacheUpdateJobTest::UpgradeLoadFromNewestCacheVaryHeaderTest);
}
TEST_F(AppCacheUpdateJobTest, UpgradeLoadFromNewestCacheReuseVaryHeader) {
RunTestOnUIThread(
&AppCacheUpdateJobTest::UpgradeLoadFromNewestCacheReuseVaryHeaderTest);
}
TEST_F(AppCacheUpdateJobTest, UpgradeSuccessMergedTypes) {
RunTestOnUIThread(&AppCacheUpdateJobTest::UpgradeSuccessMergedTypesTest);
}
TEST_F(AppCacheUpdateJobTest, CacheAttemptFailUrlFetch) {
RunTestOnUIThread(&AppCacheUpdateJobTest::CacheAttemptFailUrlFetchTest);
}
TEST_F(AppCacheUpdateJobTest, UpgradeFailUrlFetch) {
RunTestOnUIThread(&AppCacheUpdateJobTest::UpgradeFailUrlFetchTest);
}
TEST_F(AppCacheUpdateJobTest, UpgradeFailMasterUrlFetch) {
RunTestOnUIThread(&AppCacheUpdateJobTest::UpgradeFailMasterUrlFetchTest);
}
TEST_F(AppCacheUpdateJobTest, EmptyManifest) {
RunTestOnUIThread(&AppCacheUpdateJobTest::EmptyManifestTest);
}
TEST_F(AppCacheUpdateJobTest, EmptyFile) {
RunTestOnUIThread(&AppCacheUpdateJobTest::EmptyFileTest);
}
TEST_F(AppCacheUpdateJobTest, RetryRetryAfter) {
RunTestOnUIThread(&AppCacheUpdateJobTest::RetryRetryAfterTest);
}
TEST_F(AppCacheUpdateJobTest, RetryNoRetryAfter) {
RunTestOnUIThread(&AppCacheUpdateJobTest::RetryNoRetryAfterTest);
}
TEST_F(AppCacheUpdateJobTest, RetryNonzeroRetryAfter) {
RunTestOnUIThread(&AppCacheUpdateJobTest::RetryNonzeroRetryAfterTest);
}
TEST_F(AppCacheUpdateJobTest, RetrySuccess) {
RunTestOnUIThread(&AppCacheUpdateJobTest::RetrySuccessTest);
}
TEST_F(AppCacheUpdateJobTest, RetryUrl) {
RunTestOnUIThread(&AppCacheUpdateJobTest::RetryUrlTest);
}
TEST_F(AppCacheUpdateJobTest, FailStoreNewestCache) {
RunTestOnUIThread(&AppCacheUpdateJobTest::FailStoreNewestCacheTest);
}
TEST_F(AppCacheUpdateJobTest, MasterEntryFailStoreNewestCacheTest) {
RunTestOnUIThread(
&AppCacheUpdateJobTest::MasterEntryFailStoreNewestCacheTest);
}
TEST_F(AppCacheUpdateJobTest, UpgradeFailStoreNewestCache) {
RunTestOnUIThread(&AppCacheUpdateJobTest::UpgradeFailStoreNewestCacheTest);
}
TEST_F(AppCacheUpdateJobOriginTrialTest, UpgradeFailMakeGroupObsolete) {
RunTestOnUIThread(&AppCacheUpdateJobTest::UpgradeFailMakeGroupObsoleteTest);
}
TEST_F(AppCacheUpdateJobTest, MasterEntryFetchManifestFail) {
RunTestOnUIThread(&AppCacheUpdateJobTest::MasterEntryFetchManifestFailTest);
}
TEST_F(AppCacheUpdateJobTest, MasterEntryBadManifest) {
RunTestOnUIThread(&AppCacheUpdateJobTest::MasterEntryBadManifestTest);
}
TEST_F(AppCacheUpdateJobTest, MasterEntryManifestNotFound) {
RunTestOnUIThread(&AppCacheUpdateJobTest::MasterEntryManifestNotFoundTest);
}
TEST_F(AppCacheUpdateJobTest, MasterEntryFailUrlFetch) {
RunTestOnUIThread(&AppCacheUpdateJobTest::MasterEntryFailUrlFetchTest);
}
TEST_F(AppCacheUpdateJobTest, MasterEntryAllFail) {
RunTestOnUIThread(&AppCacheUpdateJobTest::MasterEntryAllFailTest);
}
TEST_F(AppCacheUpdateJobTest, UpgradeMasterEntryAllFail) {
RunTestOnUIThread(&AppCacheUpdateJobTest::UpgradeMasterEntryAllFailTest);
}
TEST_F(AppCacheUpdateJobTest, MasterEntrySomeFail) {
RunTestOnUIThread(&AppCacheUpdateJobTest::MasterEntrySomeFailTest);
}
TEST_F(AppCacheUpdateJobTest, UpgradeMasterEntrySomeFail) {
RunTestOnUIThread(&AppCacheUpdateJobTest::UpgradeMasterEntrySomeFailTest);
}
TEST_F(AppCacheUpdateJobTest, MasterEntryNoUpdate) {
RunTestOnUIThread(&AppCacheUpdateJobTest::MasterEntryNoUpdateTest);
}
TEST_F(AppCacheUpdateJobTest, StartUpdateMidCacheAttempt) {
RunTestOnUIThread(&AppCacheUpdateJobTest::StartUpdateMidCacheAttemptTest);
}
TEST_F(AppCacheUpdateJobTest, StartUpdateMidNoUpdate) {
RunTestOnUIThread(&AppCacheUpdateJobTest::StartUpdateMidNoUpdateTest);
}
TEST_F(AppCacheUpdateJobTest, StartUpdateMidDownload) {
RunTestOnUIThread(&AppCacheUpdateJobTest::StartUpdateMidDownloadTest);
}
TEST_F(AppCacheUpdateJobTest, QueueMasterEntry) {
RunTestOnUIThread(&AppCacheUpdateJobTest::QueueMasterEntryTest);
}
TEST_F(AppCacheUpdateJobTest, IfModifiedSinceCache) {
RunTestOnUIThread(&AppCacheUpdateJobTest::IfModifiedSinceTestCache);
}
TEST_F(AppCacheUpdateJobTest, IfModifiedRefetch) {
RunTestOnUIThread(&AppCacheUpdateJobTest::IfModifiedTestRefetch);
}
TEST_F(AppCacheUpdateJobTest, IfModifiedLastModified) {
RunTestOnUIThread(&AppCacheUpdateJobTest::IfModifiedTestLastModified);
}
TEST_F(AppCacheUpdateJobTest, IfModifiedSinceUpgradeParserVersion0) {
RunTestOnUIThread(
&AppCacheUpdateJobTest::IfModifiedSinceUpgradeParserVersion0Test);
}
TEST_F(AppCacheUpdateJobTest, IfModifiedSinceUpgradeParserVersion1) {
RunTestOnUIThread(
&AppCacheUpdateJobTest::IfModifiedSinceUpgradeParserVersion1Test);
}
TEST_F(AppCacheUpdateJobTest, IfNoneMatchUpgradeParserVersion0) {
RunTestOnUIThread(
&AppCacheUpdateJobTest::IfNoneMatchUpgradeParserVersion0Test);
}
TEST_F(AppCacheUpdateJobTest, IfNoneMatchUpgradeParserVersion1) {
RunTestOnUIThread(
&AppCacheUpdateJobTest::IfNoneMatchUpgradeParserVersion1Test);
}
TEST_F(AppCacheUpdateJobTest, RequestResponseTimesAreSet) {
RunTestOnUIThread(&AppCacheUpdateJobTest::RequestResponseTimesAreSetTest);
}
TEST_F(AppCacheUpdateJobNoUpdateOn304Test, RequestResponseTimesAreNotModified) {
RunTestOnUIThread(
&AppCacheUpdateJobTest::RequestResponseTimesAreNotModifiedTest);
}
TEST_F(AppCacheUpdateJobWithCorruptionRecoveryTest,
RequestResponseTimesCorruptionFixed) {
RunTestOnUIThread(
&AppCacheUpdateJobTest::RequestResponseTimesCorruptionFixedTest);
}
TEST_F(AppCacheUpdateJobWithoutCorruptionRecoveryTest,
RequestResponseTimesCorruptionNotFixed) {
RunTestOnUIThread(
&AppCacheUpdateJobTest::RequestResponseTimesCorruptionNotFixedTest);
}
TEST_F(AppCacheUpdateJobTest, IfNoneMatchRefetch) {
RunTestOnUIThread(&AppCacheUpdateJobTest::IfNoneMatchRefetchTest);
}
TEST_F(AppCacheUpdateJobTest, MultipleHeadersRefetch) {
RunTestOnUIThread(&AppCacheUpdateJobTest::MultipleHeadersRefetchTest);
}
TEST_F(AppCacheUpdateJobTest, CrossOriginHttpsSuccess) {
RunTestOnUIThread(&AppCacheUpdateJobTest::CrossOriginHttpsSuccessTest);
}
TEST_F(AppCacheUpdateJobTest, CrossOriginHttpsDenied) {
RunTestOnUIThread(&AppCacheUpdateJobTest::CrossOriginHttpsDeniedTest);
}
TEST_F(AppCacheUpdateJobOriginTrialTest, OriginTrialUpdateWithFeature) {
// Updating a manifest with a valid token should work
// with or without the kAppCacheRequireOriginTrial feature.
RunTestOnUIThread(&AppCacheUpdateJobTest::OriginTrialUpdateTest);
}
TEST_F(AppCacheUpdateJobTest, OriginTrialUpdateWithoutFeature) {
// The default AppCacheUpdateJobTest disables the origin trial.
RunTestOnUIThread(&AppCacheUpdateJobTest::OriginTrialUpdateTest);
}
TEST_F(AppCacheUpdateJobOriginTrialTest, OriginTrialRequiredNoToken) {
RunTestOnUIThread(&AppCacheUpdateJobTest::OriginTrialRequiredNoTokenTest);
}
} // namespace appcache_update_job_unittest
} // namespace content