blob: 417a0b7f237f2cb4ba7cc3dfc4273a272f6e1022 [file] [log] [blame]
// Copyright 2013 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <string>
#include "base/logging.h"
#include "base/message_loop/message_loop.h"
#include "base/run_loop.h"
#include "content/browser/browser_thread_impl.h"
#include "content/browser/service_worker/service_worker_context_core.h"
#include "content/browser/service_worker/service_worker_disk_cache.h"
#include "content/browser/service_worker/service_worker_registration.h"
#include "content/browser/service_worker/service_worker_storage.h"
#include "content/browser/service_worker/service_worker_version.h"
#include "content/common/service_worker/service_worker_status_code.h"
#include "content/public/test/test_browser_thread_bundle.h"
#include "net/base/io_buffer.h"
#include "net/base/net_errors.h"
#include "net/http/http_response_headers.h"
#include "testing/gtest/include/gtest/gtest.h"
using net::IOBuffer;
using net::WrappedIOBuffer;
namespace content {
namespace {
typedef ServiceWorkerDatabase::RegistrationData RegistrationData;
typedef ServiceWorkerDatabase::ResourceRecord ResourceRecord;
void StatusCallback(bool* was_called,
ServiceWorkerStatusCode* result,
ServiceWorkerStatusCode status) {
*was_called = true;
*result = status;
}
ServiceWorkerStorage::StatusCallback MakeStatusCallback(
bool* was_called,
ServiceWorkerStatusCode* result) {
return base::Bind(&StatusCallback, was_called, result);
}
void FindCallback(
bool* was_called,
ServiceWorkerStatusCode* result,
scoped_refptr<ServiceWorkerRegistration>* found,
ServiceWorkerStatusCode status,
const scoped_refptr<ServiceWorkerRegistration>& registration) {
*was_called = true;
*result = status;
*found = registration;
}
ServiceWorkerStorage::FindRegistrationCallback MakeFindCallback(
bool* was_called,
ServiceWorkerStatusCode* result,
scoped_refptr<ServiceWorkerRegistration>* found) {
return base::Bind(&FindCallback, was_called, result, found);
}
void GetAllCallback(
bool* was_called,
std::vector<ServiceWorkerRegistrationInfo>* all_out,
const std::vector<ServiceWorkerRegistrationInfo>& all) {
*was_called = true;
*all_out = all;
}
ServiceWorkerStorage::GetAllRegistrationInfosCallback MakeGetAllCallback(
bool* was_called,
std::vector<ServiceWorkerRegistrationInfo>* all) {
return base::Bind(&GetAllCallback, was_called, all);
}
void OnIOComplete(int* rv_out, int rv) {
*rv_out = rv;
}
void WriteBasicResponse(ServiceWorkerStorage* storage, int64 id) {
scoped_ptr<ServiceWorkerResponseWriter> writer =
storage->CreateResponseWriter(id);
const char kHttpHeaders[] =
"HTTP/1.0 200 HONKYDORY\0Content-Length: 6\0\0";
const char kHttpBody[] = "Hello\0";
scoped_refptr<IOBuffer> body(new WrappedIOBuffer(kHttpBody));
std::string raw_headers(kHttpHeaders, arraysize(kHttpHeaders));
scoped_ptr<net::HttpResponseInfo> info(new net::HttpResponseInfo);
info->request_time = base::Time::Now();
info->response_time = base::Time::Now();
info->was_cached = false;
info->headers = new net::HttpResponseHeaders(raw_headers);
scoped_refptr<HttpResponseInfoIOBuffer> info_buffer =
new HttpResponseInfoIOBuffer(info.release());
int rv = -1234;
writer->WriteInfo(info_buffer, base::Bind(&OnIOComplete, &rv));
base::RunLoop().RunUntilIdle();
EXPECT_LT(0, rv);
rv = -1234;
writer->WriteData(body, arraysize(kHttpBody),
base::Bind(&OnIOComplete, &rv));
base::RunLoop().RunUntilIdle();
EXPECT_EQ(static_cast<int>(arraysize(kHttpBody)), rv);
}
bool VerifyBasicResponse(ServiceWorkerStorage* storage, int64 id,
bool expected_positive_result) {
const char kExpectedHttpBody[] = "Hello\0";
scoped_ptr<ServiceWorkerResponseReader> reader =
storage->CreateResponseReader(id);
scoped_refptr<HttpResponseInfoIOBuffer> info_buffer =
new HttpResponseInfoIOBuffer();
int rv = -1234;
reader->ReadInfo(info_buffer, base::Bind(&OnIOComplete, &rv));
base::RunLoop().RunUntilIdle();
if (expected_positive_result)
EXPECT_LT(0, rv);
if (rv <= 0)
return false;
const int kBigEnough = 512;
scoped_refptr<net::IOBuffer> buffer = new IOBuffer(kBigEnough);
rv = -1234;
reader->ReadData(buffer, kBigEnough, base::Bind(&OnIOComplete, &rv));
base::RunLoop().RunUntilIdle();
EXPECT_EQ(static_cast<int>(arraysize(kExpectedHttpBody)), rv);
if (rv <= 0)
return false;
bool status_match =
std::string("HONKYDORY") ==
info_buffer->http_info->headers->GetStatusText();
bool data_match =
std::string(kExpectedHttpBody) == std::string(buffer->data());
EXPECT_TRUE(status_match);
EXPECT_TRUE(data_match);
return status_match && data_match;
}
} // namespace
class ServiceWorkerStorageTest : public testing::Test {
public:
ServiceWorkerStorageTest()
: browser_thread_bundle_(TestBrowserThreadBundle::IO_MAINLOOP) {
}
virtual void SetUp() OVERRIDE {
context_.reset(
new ServiceWorkerContextCore(base::FilePath(),
base::MessageLoopProxy::current(),
base::MessageLoopProxy::current(),
NULL,
NULL,
NULL));
context_ptr_ = context_->AsWeakPtr();
}
virtual void TearDown() OVERRIDE {
context_.reset();
}
ServiceWorkerStorage* storage() { return context_->storage(); }
// A static class method for friendliness.
static void VerifyPurgeableListStatusCallback(
ServiceWorkerDatabase* database,
std::set<int64> *purgeable_ids,
bool* was_called,
ServiceWorkerStatusCode* result,
ServiceWorkerStatusCode status) {
*was_called = true;
*result = status;
EXPECT_EQ(ServiceWorkerDatabase::STATUS_OK,
database->GetPurgeableResourceIds(purgeable_ids));
}
protected:
ServiceWorkerStatusCode StoreRegistration(
scoped_refptr<ServiceWorkerRegistration> registration,
scoped_refptr<ServiceWorkerVersion> version) {
bool was_called = false;
ServiceWorkerStatusCode result = SERVICE_WORKER_ERROR_FAILED;
storage()->StoreRegistration(
registration, version, MakeStatusCallback(&was_called, &result));
EXPECT_FALSE(was_called); // always async
base::RunLoop().RunUntilIdle();
EXPECT_TRUE(was_called);
return result;
}
ServiceWorkerStatusCode DeleteRegistration(
int64 registration_id,
const GURL& origin) {
bool was_called = false;
ServiceWorkerStatusCode result = SERVICE_WORKER_ERROR_FAILED;
storage()->DeleteRegistration(
registration_id, origin, MakeStatusCallback(&was_called, &result));
EXPECT_FALSE(was_called); // always async
base::RunLoop().RunUntilIdle();
EXPECT_TRUE(was_called);
return result;
}
void GetAllRegistrations(
std::vector<ServiceWorkerRegistrationInfo>* registrations) {
bool was_called = false;
storage()->GetAllRegistrations(
MakeGetAllCallback(&was_called, registrations));
EXPECT_FALSE(was_called); // always async
base::RunLoop().RunUntilIdle();
EXPECT_TRUE(was_called);
}
ServiceWorkerStatusCode UpdateToActiveState(
scoped_refptr<ServiceWorkerRegistration> registration) {
bool was_called = false;
ServiceWorkerStatusCode result = SERVICE_WORKER_ERROR_FAILED;
storage()->UpdateToActiveState(
registration, MakeStatusCallback(&was_called, &result));
EXPECT_FALSE(was_called); // always async
base::RunLoop().RunUntilIdle();
EXPECT_TRUE(was_called);
return result;
}
ServiceWorkerStatusCode FindRegistrationForDocument(
const GURL& document_url,
scoped_refptr<ServiceWorkerRegistration>* registration) {
bool was_called = false;
ServiceWorkerStatusCode result = SERVICE_WORKER_ERROR_FAILED;
storage()->FindRegistrationForDocument(
document_url, MakeFindCallback(&was_called, &result, registration));
base::RunLoop().RunUntilIdle();
EXPECT_TRUE(was_called);
return result;
}
ServiceWorkerStatusCode FindRegistrationForPattern(
const GURL& scope,
scoped_refptr<ServiceWorkerRegistration>* registration) {
bool was_called = false;
ServiceWorkerStatusCode result = SERVICE_WORKER_ERROR_FAILED;
storage()->FindRegistrationForPattern(
scope, MakeFindCallback(&was_called, &result, registration));
EXPECT_FALSE(was_called); // always async
base::RunLoop().RunUntilIdle();
EXPECT_TRUE(was_called);
return result;
}
ServiceWorkerStatusCode FindRegistrationForId(
int64 registration_id,
const GURL& origin,
scoped_refptr<ServiceWorkerRegistration>* registration) {
bool was_called = false;
ServiceWorkerStatusCode result = SERVICE_WORKER_ERROR_FAILED;
storage()->FindRegistrationForId(
registration_id, origin,
MakeFindCallback(&was_called, &result, registration));
base::RunLoop().RunUntilIdle();
EXPECT_TRUE(was_called);
return result;
}
scoped_ptr<ServiceWorkerContextCore> context_;
base::WeakPtr<ServiceWorkerContextCore> context_ptr_;
TestBrowserThreadBundle browser_thread_bundle_;
};
TEST_F(ServiceWorkerStorageTest, StoreFindUpdateDeleteRegistration) {
const GURL kScope("http://www.test.not/scope/*");
const GURL kScript("http://www.test.not/script.js");
const GURL kDocumentUrl("http://www.test.not/scope/document.html");
const int64 kRegistrationId = 0;
const int64 kVersionId = 0;
scoped_refptr<ServiceWorkerRegistration> found_registration;
// We shouldn't find anything without having stored anything.
EXPECT_EQ(SERVICE_WORKER_ERROR_NOT_FOUND,
FindRegistrationForDocument(kDocumentUrl, &found_registration));
EXPECT_FALSE(found_registration);
EXPECT_EQ(SERVICE_WORKER_ERROR_NOT_FOUND,
FindRegistrationForPattern(kScope, &found_registration));
EXPECT_FALSE(found_registration);
EXPECT_EQ(SERVICE_WORKER_ERROR_NOT_FOUND,
FindRegistrationForId(
kRegistrationId, kScope.GetOrigin(), &found_registration));
EXPECT_FALSE(found_registration);
// Store something.
scoped_refptr<ServiceWorkerRegistration> live_registration =
new ServiceWorkerRegistration(
kScope, kScript, kRegistrationId, context_ptr_);
scoped_refptr<ServiceWorkerVersion> live_version =
new ServiceWorkerVersion(
live_registration, kVersionId, context_ptr_);
live_version->SetStatus(ServiceWorkerVersion::INSTALLED);
live_registration->set_waiting_version(live_version);
EXPECT_EQ(SERVICE_WORKER_OK,
StoreRegistration(live_registration, live_version));
// Now we should find it and get the live ptr back immediately.
EXPECT_EQ(SERVICE_WORKER_OK,
FindRegistrationForDocument(kDocumentUrl, &found_registration));
EXPECT_EQ(live_registration, found_registration);
found_registration = NULL;
// But FindRegistrationForPattern is always async.
EXPECT_EQ(SERVICE_WORKER_OK,
FindRegistrationForPattern(kScope, &found_registration));
EXPECT_EQ(live_registration, found_registration);
found_registration = NULL;
// Can be found by id too.
EXPECT_EQ(SERVICE_WORKER_OK,
FindRegistrationForId(
kRegistrationId, kScope.GetOrigin(), &found_registration));
ASSERT_TRUE(found_registration);
EXPECT_EQ(kRegistrationId, found_registration->id());
EXPECT_EQ(live_registration, found_registration);
found_registration = NULL;
// Drop the live registration, but keep the version live.
live_registration = NULL;
// Now FindRegistrationForDocument should be async.
EXPECT_EQ(SERVICE_WORKER_OK,
FindRegistrationForDocument(kDocumentUrl, &found_registration));
ASSERT_TRUE(found_registration);
EXPECT_EQ(kRegistrationId, found_registration->id());
EXPECT_TRUE(found_registration->HasOneRef());
EXPECT_EQ(live_version, found_registration->waiting_version());
found_registration = NULL;
// Drop the live version too.
live_version = NULL;
// And FindRegistrationForPattern is always async.
EXPECT_EQ(SERVICE_WORKER_OK,
FindRegistrationForPattern(kScope, &found_registration));
ASSERT_TRUE(found_registration);
EXPECT_EQ(kRegistrationId, found_registration->id());
EXPECT_TRUE(found_registration->HasOneRef());
EXPECT_FALSE(found_registration->active_version());
ASSERT_TRUE(found_registration->waiting_version());
EXPECT_EQ(ServiceWorkerVersion::INSTALLED,
found_registration->waiting_version()->status());
// Update to active.
scoped_refptr<ServiceWorkerVersion> temp_version =
found_registration->waiting_version();
found_registration->set_waiting_version(NULL);
temp_version->SetStatus(ServiceWorkerVersion::ACTIVE);
found_registration->set_active_version(temp_version);
temp_version = NULL;
EXPECT_EQ(SERVICE_WORKER_OK, UpdateToActiveState(found_registration));
found_registration = NULL;
// Trying to update a unstored registration to active should fail.
scoped_refptr<ServiceWorkerRegistration> unstored_registration =
new ServiceWorkerRegistration(
kScope, kScript, kRegistrationId + 1, context_ptr_);
EXPECT_EQ(SERVICE_WORKER_ERROR_NOT_FOUND,
UpdateToActiveState(unstored_registration));
unstored_registration = NULL;
// The Find methods should return a registration with an active version.
EXPECT_EQ(SERVICE_WORKER_OK,
FindRegistrationForDocument(kDocumentUrl, &found_registration));
ASSERT_TRUE(found_registration);
EXPECT_EQ(kRegistrationId, found_registration->id());
EXPECT_TRUE(found_registration->HasOneRef());
EXPECT_FALSE(found_registration->waiting_version());
ASSERT_TRUE(found_registration->active_version());
EXPECT_EQ(ServiceWorkerVersion::ACTIVE,
found_registration->active_version()->status());
// Delete from storage but with a instance still live.
EXPECT_TRUE(context_->GetLiveVersion(kRegistrationId));
EXPECT_EQ(SERVICE_WORKER_OK,
DeleteRegistration(kRegistrationId, kScope.GetOrigin()));
EXPECT_TRUE(context_->GetLiveVersion(kRegistrationId));
// Should no longer be found.
EXPECT_EQ(SERVICE_WORKER_ERROR_NOT_FOUND,
FindRegistrationForId(
kRegistrationId, kScope.GetOrigin(), &found_registration));
EXPECT_FALSE(found_registration);
// Deleting an unstored registration should succeed.
EXPECT_EQ(SERVICE_WORKER_OK,
DeleteRegistration(kRegistrationId + 1, kScope.GetOrigin()));
}
TEST_F(ServiceWorkerStorageTest, InstallingRegistrationsAreFindable) {
const GURL kScope("http://www.test.not/scope/*");
const GURL kScript("http://www.test.not/script.js");
const GURL kDocumentUrl("http://www.test.not/scope/document.html");
const int64 kRegistrationId = 0;
const int64 kVersionId = 0;
scoped_refptr<ServiceWorkerRegistration> found_registration;
// Create an unstored registration.
scoped_refptr<ServiceWorkerRegistration> live_registration =
new ServiceWorkerRegistration(
kScope, kScript, kRegistrationId, context_ptr_);
scoped_refptr<ServiceWorkerVersion> live_version =
new ServiceWorkerVersion(
live_registration, kVersionId, context_ptr_);
live_version->SetStatus(ServiceWorkerVersion::INSTALLING);
live_registration->set_waiting_version(live_version);
// Should not be findable, including by GetAllRegistrations.
EXPECT_EQ(SERVICE_WORKER_ERROR_NOT_FOUND,
FindRegistrationForId(
kRegistrationId, kScope.GetOrigin(), &found_registration));
EXPECT_FALSE(found_registration);
EXPECT_EQ(SERVICE_WORKER_ERROR_NOT_FOUND,
FindRegistrationForDocument(kDocumentUrl, &found_registration));
EXPECT_FALSE(found_registration);
EXPECT_EQ(SERVICE_WORKER_ERROR_NOT_FOUND,
FindRegistrationForPattern(kScope, &found_registration));
EXPECT_FALSE(found_registration);
std::vector<ServiceWorkerRegistrationInfo> all_registrations;
GetAllRegistrations(&all_registrations);
EXPECT_TRUE(all_registrations.empty());
// Notify storage of it being installed.
storage()->NotifyInstallingRegistration(live_registration);
// Now should be findable.
EXPECT_EQ(SERVICE_WORKER_OK,
FindRegistrationForId(
kRegistrationId, kScope.GetOrigin(), &found_registration));
EXPECT_EQ(live_registration, found_registration);
found_registration = NULL;
EXPECT_EQ(SERVICE_WORKER_OK,
FindRegistrationForDocument(kDocumentUrl, &found_registration));
EXPECT_EQ(live_registration, found_registration);
found_registration = NULL;
EXPECT_EQ(SERVICE_WORKER_OK,
FindRegistrationForPattern(kScope, &found_registration));
EXPECT_EQ(live_registration, found_registration);
found_registration = NULL;
GetAllRegistrations(&all_registrations);
EXPECT_EQ(1u, all_registrations.size());
all_registrations.clear();
// Notify storage of installation no longer happening.
storage()->NotifyDoneInstallingRegistration(
live_registration, NULL, SERVICE_WORKER_OK);
// Once again, should not be findable.
EXPECT_EQ(SERVICE_WORKER_ERROR_NOT_FOUND,
FindRegistrationForId(
kRegistrationId, kScope.GetOrigin(), &found_registration));
EXPECT_FALSE(found_registration);
EXPECT_EQ(SERVICE_WORKER_ERROR_NOT_FOUND,
FindRegistrationForDocument(kDocumentUrl, &found_registration));
EXPECT_FALSE(found_registration);
EXPECT_EQ(SERVICE_WORKER_ERROR_NOT_FOUND,
FindRegistrationForPattern(kScope, &found_registration));
EXPECT_FALSE(found_registration);
GetAllRegistrations(&all_registrations);
EXPECT_TRUE(all_registrations.empty());
}
TEST_F(ServiceWorkerStorageTest, ResourceIdsAreStoredAndPurged) {
storage()->LazyInitialize(base::Bind(&base::DoNothing));
base::RunLoop().RunUntilIdle();
const GURL kScope("http://www.test.not/scope/*");
const GURL kScript("http://www.test.not/script.js");
const GURL kImport("http://www.test.not/import.js");
const GURL kDocumentUrl("http://www.test.not/scope/document.html");
const int64 kRegistrationId = storage()->NewRegistrationId();
const int64 kVersionId = storage()->NewVersionId();
const int64 kResourceId1 = storage()->NewResourceId();
const int64 kResourceId2 = storage()->NewResourceId();
// Cons up a new registration+version with two script resources.
RegistrationData data;
data.registration_id = kRegistrationId;
data.scope = kScope;
data.script = kScript;
data.version_id = kVersionId;
data.is_active = false;
std::vector<ResourceRecord> resources;
resources.push_back(ResourceRecord(kResourceId1, kScript));
resources.push_back(ResourceRecord(kResourceId2, kImport));
scoped_refptr<ServiceWorkerRegistration> registration =
storage()->GetOrCreateRegistration(data, resources);
registration->waiting_version()->SetStatus(ServiceWorkerVersion::NEW);
// Add the resources ids to the uncommitted list.
std::set<int64> resource_ids;
resource_ids.insert(kResourceId1);
resource_ids.insert(kResourceId2);
EXPECT_EQ(ServiceWorkerDatabase::STATUS_OK,
storage()->database_->WriteUncommittedResourceIds(resource_ids));
// And dump something in the disk cache for them.
WriteBasicResponse(storage(), kResourceId1);
WriteBasicResponse(storage(), kResourceId2);
EXPECT_TRUE(VerifyBasicResponse(storage(), kResourceId1, true));
EXPECT_TRUE(VerifyBasicResponse(storage(), kResourceId2, true));
// Storing the registration/version should take the resources ids out
// of the uncommitted list.
EXPECT_EQ(SERVICE_WORKER_OK,
StoreRegistration(registration, registration->waiting_version()));
std::set<int64> verify_ids;
EXPECT_EQ(ServiceWorkerDatabase::STATUS_OK,
storage()->database_->GetUncommittedResourceIds(&verify_ids));
EXPECT_TRUE(verify_ids.empty());
// Deleting it should result in the resources being added to the
// purgeable list and then doomed in the disk cache and removed from
// that list.
bool was_called = false;
ServiceWorkerStatusCode result = SERVICE_WORKER_ERROR_FAILED;
verify_ids.clear();
storage()->DeleteRegistration(
registration->id(), kScope.GetOrigin(),
base::Bind(&VerifyPurgeableListStatusCallback,
base::Unretained(storage()->database_.get()),
&verify_ids, &was_called, &result));
base::RunLoop().RunUntilIdle();
ASSERT_TRUE(was_called);
EXPECT_EQ(SERVICE_WORKER_OK, result);
EXPECT_EQ(2u, verify_ids.size());
verify_ids.clear();
EXPECT_EQ(ServiceWorkerDatabase::STATUS_OK,
storage()->database_->GetPurgeableResourceIds(&verify_ids));
EXPECT_TRUE(verify_ids.empty());
EXPECT_FALSE(VerifyBasicResponse(storage(), kResourceId1, false));
EXPECT_FALSE(VerifyBasicResponse(storage(), kResourceId2, false));
}
TEST_F(ServiceWorkerStorageTest, FindRegistration_LongestScopeMatch) {
const GURL kDocumentUrl("http://www.example.com/scope/foo");
scoped_refptr<ServiceWorkerRegistration> found_registration;
// Registration for "/scope/*".
const GURL kScope1("http://www.example.com/scope/*");
const GURL kScript1("http://www.example.com/script1.js");
const int64 kRegistrationId1 = 1;
const int64 kVersionId1 = 1;
scoped_refptr<ServiceWorkerRegistration> live_registration1 =
new ServiceWorkerRegistration(
kScope1, kScript1, kRegistrationId1, context_ptr_);
scoped_refptr<ServiceWorkerVersion> live_version1 =
new ServiceWorkerVersion(
live_registration1, kVersionId1, context_ptr_);
live_version1->SetStatus(ServiceWorkerVersion::INSTALLED);
live_registration1->set_waiting_version(live_version1);
// Registration for "/scope/foo*".
const GURL kScope2("http://www.example.com/scope/foo*");
const GURL kScript2("http://www.example.com/script2.js");
const int64 kRegistrationId2 = 2;
const int64 kVersionId2 = 2;
scoped_refptr<ServiceWorkerRegistration> live_registration2 =
new ServiceWorkerRegistration(
kScope2, kScript2, kRegistrationId2, context_ptr_);
scoped_refptr<ServiceWorkerVersion> live_version2 =
new ServiceWorkerVersion(
live_registration2, kVersionId2, context_ptr_);
live_version2->SetStatus(ServiceWorkerVersion::INSTALLED);
live_registration2->set_waiting_version(live_version2);
// Registration for "/scope/foo".
const GURL kScope3("http://www.example.com/scope/foo");
const GURL kScript3("http://www.example.com/script3.js");
const int64 kRegistrationId3 = 3;
const int64 kVersionId3 = 3;
scoped_refptr<ServiceWorkerRegistration> live_registration3 =
new ServiceWorkerRegistration(
kScope3, kScript3, kRegistrationId3, context_ptr_);
scoped_refptr<ServiceWorkerVersion> live_version3 =
new ServiceWorkerVersion(
live_registration3, kVersionId3, context_ptr_);
live_version3->SetStatus(ServiceWorkerVersion::INSTALLED);
live_registration3->set_waiting_version(live_version3);
// Notify storage of they being installed.
storage()->NotifyInstallingRegistration(live_registration1);
storage()->NotifyInstallingRegistration(live_registration2);
storage()->NotifyInstallingRegistration(live_registration3);
// Find a registration among installing ones.
EXPECT_EQ(SERVICE_WORKER_OK,
FindRegistrationForDocument(kDocumentUrl, &found_registration));
EXPECT_EQ(live_registration2, found_registration);
found_registration = NULL;
// Store registrations.
EXPECT_EQ(SERVICE_WORKER_OK,
StoreRegistration(live_registration1, live_version1));
EXPECT_EQ(SERVICE_WORKER_OK,
StoreRegistration(live_registration2, live_version2));
EXPECT_EQ(SERVICE_WORKER_OK,
StoreRegistration(live_registration3, live_version3));
// Notify storage of installations no longer happening.
storage()->NotifyDoneInstallingRegistration(
live_registration1, NULL, SERVICE_WORKER_OK);
storage()->NotifyDoneInstallingRegistration(
live_registration2, NULL, SERVICE_WORKER_OK);
storage()->NotifyDoneInstallingRegistration(
live_registration3, NULL, SERVICE_WORKER_OK);
// Find a registration among installed ones.
EXPECT_EQ(SERVICE_WORKER_OK,
FindRegistrationForDocument(kDocumentUrl, &found_registration));
EXPECT_EQ(live_registration2, found_registration);
}
} // namespace content