Update To 11.40.268.0
[platform/framework/web/crosswalk.git] / src / content / browser / service_worker / service_worker_cache_unittest.cc
index 0f3c5ea..0d1c8fa 100644 (file)
@@ -10,6 +10,7 @@
 #include "base/run_loop.h"
 #include "content/browser/fileapi/chrome_blob_storage_context.h"
 #include "content/browser/fileapi/mock_url_request_delegate.h"
+#include "content/browser/quota/mock_quota_manager_proxy.h"
 #include "content/common/service_worker/service_worker_types.h"
 #include "content/public/browser/browser_thread.h"
 #include "content/public/test/test_browser_context.h"
@@ -20,6 +21,7 @@
 #include "storage/browser/blob/blob_data_handle.h"
 #include "storage/browser/blob/blob_storage_context.h"
 #include "storage/browser/blob/blob_url_request_job_factory.h"
+#include "storage/browser/quota/quota_manager_proxy.h"
 #include "storage/common/blob/blob_data.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
@@ -40,19 +42,63 @@ storage::BlobProtocolHandler* CreateMockBlobProtocolHandler(
 
 }  // namespace
 
+// A ServiceWorkerCache that can optionally pause during backend creation.
+class TestServiceWorkerCache : public ServiceWorkerCache {
+ public:
+  TestServiceWorkerCache(
+      const GURL& origin,
+      const base::FilePath& path,
+      net::URLRequestContext* request_context,
+      const scoped_refptr<storage::QuotaManagerProxy>& quota_manager_proxy,
+      base::WeakPtr<storage::BlobStorageContext> blob_context)
+      : ServiceWorkerCache(origin,
+                           path,
+                           request_context,
+                           quota_manager_proxy,
+                           blob_context),
+        pause_backend_creation_(false) {}
+
+  virtual void CreateBackend(const ErrorCallback& callback) override {
+    backend_creation_callback_ = callback;
+    if (pause_backend_creation_)
+      return;
+    ContinueCreateBackend();
+  }
+
+  void ContinueCreateBackend() {
+    ServiceWorkerCache::CreateBackend(backend_creation_callback_);
+  }
+
+  void set_pause_backend_creation(bool pause) {
+    pause_backend_creation_ = pause;
+  }
+
+ private:
+  virtual ~TestServiceWorkerCache() override {}
+
+  bool pause_backend_creation_;
+  ErrorCallback backend_creation_callback_;
+
+  DISALLOW_COPY_AND_ASSIGN(TestServiceWorkerCache);
+};
+
 class ServiceWorkerCacheTest : public testing::Test {
  public:
   ServiceWorkerCacheTest()
       : browser_thread_bundle_(TestBrowserThreadBundle::IO_MAINLOOP),
-        callback_error_(ServiceWorkerCache::ErrorTypeOK) {}
+        callback_error_(ServiceWorkerCache::ErrorTypeOK),
+        callback_closed_(false) {}
 
-  virtual void SetUp() OVERRIDE {
+  void SetUp() override {
     ChromeBlobStorageContext* blob_storage_context =
         ChromeBlobStorageContext::GetFor(&browser_context_);
     // Wait for chrome_blob_storage_context to finish initializing.
     base::RunLoop().RunUntilIdle();
     blob_storage_context_ = blob_storage_context->context();
 
+    quota_manager_proxy_ = new MockQuotaManagerProxy(
+        nullptr, base::MessageLoopProxy::current().get());
+
     url_request_job_factory_.reset(new net::URLRequestJobFactoryImpl);
     url_request_job_factory_->SetProtocolHandler(
         "blob", CreateMockBlobProtocolHandler(blob_storage_context->context()));
@@ -64,20 +110,17 @@ class ServiceWorkerCacheTest : public testing::Test {
 
     CreateRequests(blob_storage_context);
 
-    if (MemoryOnly()) {
-      cache_ = ServiceWorkerCache::CreateMemoryCache(
-          url_request_context,
-          blob_storage_context->context()->AsWeakPtr());
-    } else {
+    if (!MemoryOnly())
       ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
-      cache_ = ServiceWorkerCache::CreatePersistentCache(
-          temp_dir_.path(),
-          url_request_context,
-          blob_storage_context->context()->AsWeakPtr());
-    }
+    base::FilePath path = MemoryOnly() ? base::FilePath() : temp_dir_.path();
+
+    cache_ = make_scoped_refptr(new TestServiceWorkerCache(
+        GURL("http://example.com"), path, url_request_context,
+        quota_manager_proxy_, blob_storage_context->context()->AsWeakPtr()));
   }
 
-  virtual void TearDown() OVERRIDE {
+  void TearDown() override {
+    quota_manager_proxy_->SimulateQuotaManagerDestroyed();
     base::RunLoop().RunUntilIdle();
   }
 
@@ -105,14 +148,23 @@ class ServiceWorkerCacheTest : public testing::Test {
     blob_handle_ =
         blob_storage_context->context()->AddFinishedBlob(blob_data.get());
 
-    body_response_ = ServiceWorkerResponse(GURL("http://example.com/body.html"),
-                                           200,
-                                           "OK",
-                                           headers,
-                                           blob_handle_->uuid());
-
-    no_body_response_ = ServiceWorkerResponse(
-        GURL("http://example.com/no_body.html"), 200, "OK", headers, "");
+    body_response_ =
+        ServiceWorkerResponse(GURL("http://example.com/body.html"),
+                              200,
+                              "OK",
+                              blink::WebServiceWorkerResponseTypeDefault,
+                              headers,
+                              blob_handle_->uuid(),
+                              expected_blob_data_.size());
+
+    no_body_response_ =
+        ServiceWorkerResponse(GURL("http://example.com/no_body.html"),
+                              200,
+                              "OK",
+                              blink::WebServiceWorkerResponseTypeDefault,
+                              headers,
+                              "",
+                              0);
   }
 
   scoped_ptr<ServiceWorkerFetchRequest> CopyFetchRequest(
@@ -126,11 +178,15 @@ class ServiceWorkerCacheTest : public testing::Test {
 
   scoped_ptr<ServiceWorkerResponse> CopyFetchResponse(
       const ServiceWorkerResponse& response) {
-    return make_scoped_ptr(new ServiceWorkerResponse(response.url,
-                                                     response.status_code,
-                                                     response.status_text,
-                                                     response.headers,
-                                                     response.blob_uuid));
+    scoped_ptr<ServiceWorkerResponse> sw_response(
+        new ServiceWorkerResponse(response.url,
+                                  response.status_code,
+                                  response.status_text,
+                                  response.response_type,
+                                  response.headers,
+                                  response.blob_uuid,
+                                  response.blob_size));
+    return sw_response.Pass();
   }
 
   bool Put(const ServiceWorkerFetchRequest& request,
@@ -139,7 +195,7 @@ class ServiceWorkerCacheTest : public testing::Test {
 
     cache_->Put(CopyFetchRequest(request),
                 CopyFetchResponse(response),
-                base::Bind(&ServiceWorkerCacheTest::ErrorTypeCallback,
+                base::Bind(&ServiceWorkerCacheTest::ResponseAndErrorCallback,
                            base::Unretained(this),
                            base::Unretained(loop.get())));
     // TODO(jkarlin): These functions should use base::RunLoop().RunUntilIdle()
@@ -185,20 +241,34 @@ class ServiceWorkerCacheTest : public testing::Test {
     return callback_error_ == ServiceWorkerCache::ErrorTypeOK;
   }
 
+  bool Close() {
+    scoped_ptr<base::RunLoop> loop(new base::RunLoop());
+
+    cache_->Close(base::Bind(&ServiceWorkerCacheTest::CloseCallback,
+                             base::Unretained(this),
+                             base::Unretained(loop.get())));
+    loop->Run();
+    return callback_closed_;
+  }
+
   void RequestsCallback(base::RunLoop* run_loop,
                         ServiceWorkerCache::ErrorType error,
                         scoped_ptr<ServiceWorkerCache::Requests> requests) {
     callback_error_ = error;
     callback_strings_.clear();
-    for (size_t i = 0u; i < requests->size(); ++i)
-      callback_strings_.push_back(requests->at(i).url.spec());
-    run_loop->Quit();
+    if (requests) {
+      for (size_t i = 0u; i < requests->size(); ++i)
+        callback_strings_.push_back(requests->at(i).url.spec());
+    }
+    if (run_loop)
+      run_loop->Quit();
   }
 
   void ErrorTypeCallback(base::RunLoop* run_loop,
                          ServiceWorkerCache::ErrorType error) {
     callback_error_ = error;
-    run_loop->Quit();
+    if (run_loop)
+      run_loop->Quit();
   }
 
   void ResponseAndErrorCallback(
@@ -208,13 +278,21 @@ class ServiceWorkerCacheTest : public testing::Test {
       scoped_ptr<storage::BlobDataHandle> body_handle) {
     callback_error_ = error;
     callback_response_ = response.Pass();
-
+    callback_response_data_.reset();
     if (error == ServiceWorkerCache::ErrorTypeOK &&
         !callback_response_->blob_uuid.empty()) {
       callback_response_data_ = body_handle.Pass();
     }
 
-    run_loop->Quit();
+    if (run_loop)
+      run_loop->Quit();
+  }
+
+  void CloseCallback(base::RunLoop* run_loop) {
+    EXPECT_FALSE(callback_closed_);
+    callback_closed_ = true;
+    if (run_loop)
+      run_loop->Quit();
   }
 
   void CopyBody(storage::BlobDataHandle* blob_handle, std::string* output) {
@@ -239,16 +317,32 @@ class ServiceWorkerCacheTest : public testing::Test {
     return true;
   }
 
+  bool TestResponseType(blink::WebServiceWorkerResponseType response_type) {
+    body_response_.response_type = response_type;
+    EXPECT_TRUE(Put(body_request_, body_response_));
+    EXPECT_TRUE(Match(body_request_));
+    EXPECT_TRUE(Delete(body_request_));
+    return response_type == callback_response_->response_type;
+  }
+
+  void VerifyAllOpsFail() {
+    EXPECT_FALSE(Put(no_body_request_, no_body_response_));
+    EXPECT_FALSE(Match(no_body_request_));
+    EXPECT_FALSE(Delete(body_request_));
+    EXPECT_FALSE(Keys());
+  }
+
   virtual bool MemoryOnly() { return false; }
 
  protected:
   TestBrowserContext browser_context_;
   TestBrowserThreadBundle browser_thread_bundle_;
   scoped_ptr<net::URLRequestJobFactoryImpl> url_request_job_factory_;
+  scoped_refptr<MockQuotaManagerProxy> quota_manager_proxy_;
   storage::BlobStorageContext* blob_storage_context_;
 
   base::ScopedTempDir temp_dir_;
-  scoped_refptr<ServiceWorkerCache> cache_;
+  scoped_refptr<TestServiceWorkerCache> cache_;
 
   ServiceWorkerFetchRequest body_request_;
   ServiceWorkerResponse body_response_;
@@ -261,26 +355,67 @@ class ServiceWorkerCacheTest : public testing::Test {
   scoped_ptr<ServiceWorkerResponse> callback_response_;
   scoped_ptr<storage::BlobDataHandle> callback_response_data_;
   std::vector<std::string> callback_strings_;
+  bool callback_closed_;
 };
 
 class ServiceWorkerCacheTestP : public ServiceWorkerCacheTest,
                                 public testing::WithParamInterface<bool> {
-  virtual bool MemoryOnly() OVERRIDE { return !GetParam(); }
+  bool MemoryOnly() override { return !GetParam(); }
+};
+
+class ServiceWorkerCacheMemoryOnlyTest
+    : public ServiceWorkerCacheTest,
+      public testing::WithParamInterface<bool> {
+  bool MemoryOnly() override { return true; }
 };
 
 TEST_P(ServiceWorkerCacheTestP, PutNoBody) {
   EXPECT_TRUE(Put(no_body_request_, no_body_response_));
+  EXPECT_TRUE(callback_response_);
+  EXPECT_STREQ(no_body_response_.url.spec().c_str(),
+               callback_response_->url.spec().c_str());
+  EXPECT_FALSE(callback_response_data_);
+  EXPECT_STREQ("", callback_response_->blob_uuid.c_str());
+  EXPECT_EQ(0u, callback_response_->blob_size);
 }
 
 TEST_P(ServiceWorkerCacheTestP, PutBody) {
   EXPECT_TRUE(Put(body_request_, body_response_));
+  EXPECT_TRUE(callback_response_);
+  EXPECT_STREQ(body_response_.url.spec().c_str(),
+               callback_response_->url.spec().c_str());
+  EXPECT_TRUE(callback_response_data_);
+  EXPECT_STRNE("", callback_response_->blob_uuid.c_str());
+  EXPECT_EQ(expected_blob_data_.size(), callback_response_->blob_size);
+
+  std::string response_body;
+  CopyBody(callback_response_data_.get(), &response_body);
+  EXPECT_STREQ(expected_blob_data_.c_str(), response_body.c_str());
+}
+
+TEST_P(ServiceWorkerCacheTestP, ResponseURLDiffersFromRequestURL) {
+  no_body_response_.url = GURL("http://example.com/foobar");
+  EXPECT_STRNE("http://example.com/foobar",
+               no_body_request_.url.spec().c_str());
+  EXPECT_TRUE(Put(no_body_request_, no_body_response_));
+  EXPECT_TRUE(Match(no_body_request_));
+  EXPECT_STREQ("http://example.com/foobar",
+               callback_response_->url.spec().c_str());
+}
+
+TEST_P(ServiceWorkerCacheTestP, ResponseURLEmpty) {
+  no_body_response_.url = GURL();
+  EXPECT_STRNE("", no_body_request_.url.spec().c_str());
+  EXPECT_TRUE(Put(no_body_request_, no_body_response_));
+  EXPECT_TRUE(Match(no_body_request_));
+  EXPECT_STREQ("", callback_response_->url.spec().c_str());
 }
 
 TEST_F(ServiceWorkerCacheTest, PutBodyDropBlobRef) {
   scoped_ptr<base::RunLoop> loop(new base::RunLoop());
   cache_->Put(CopyFetchRequest(body_request_),
               CopyFetchResponse(body_response_),
-              base::Bind(&ServiceWorkerCacheTestP::ErrorTypeCallback,
+              base::Bind(&ServiceWorkerCacheTestP::ResponseAndErrorCallback,
                          base::Unretained(this),
                          base::Unretained(loop.get())));
   // The handle should be held by the cache now so the deref here should be
@@ -291,6 +426,20 @@ TEST_F(ServiceWorkerCacheTest, PutBodyDropBlobRef) {
   EXPECT_EQ(ServiceWorkerCache::ErrorTypeOK, callback_error_);
 }
 
+TEST_P(ServiceWorkerCacheTestP, PutReplace) {
+  EXPECT_TRUE(Put(body_request_, no_body_response_));
+  EXPECT_TRUE(Match(body_request_));
+  EXPECT_FALSE(callback_response_data_);
+
+  EXPECT_TRUE(Put(body_request_, body_response_));
+  EXPECT_TRUE(Match(body_request_));
+  EXPECT_TRUE(callback_response_data_);
+
+  EXPECT_TRUE(Put(body_request_, no_body_response_));
+  EXPECT_TRUE(Match(body_request_));
+  EXPECT_FALSE(callback_response_data_);
+}
+
 TEST_P(ServiceWorkerCacheTestP, MatchNoBody) {
   EXPECT_TRUE(Put(no_body_request_, no_body_response_));
   EXPECT_TRUE(Match(no_body_request_));
@@ -298,6 +447,8 @@ TEST_P(ServiceWorkerCacheTestP, MatchNoBody) {
   EXPECT_STREQ("OK", callback_response_->status_text.c_str());
   EXPECT_STREQ("http://example.com/no_body.html",
                callback_response_->url.spec().c_str());
+  EXPECT_STREQ("", callback_response_->blob_uuid.c_str());
+  EXPECT_EQ(0u, callback_response_->blob_size);
 }
 
 TEST_P(ServiceWorkerCacheTestP, MatchBody) {
@@ -307,6 +458,9 @@ TEST_P(ServiceWorkerCacheTestP, MatchBody) {
   EXPECT_STREQ("OK", callback_response_->status_text.c_str());
   EXPECT_STREQ("http://example.com/body.html",
                callback_response_->url.spec().c_str());
+  EXPECT_STRNE("", callback_response_->blob_uuid.c_str());
+  EXPECT_EQ(expected_blob_data_.size(), callback_response_->blob_size);
+
   std::string response_body;
   CopyBody(callback_response_data_.get(), &response_body);
   EXPECT_STREQ(expected_blob_data_.c_str(), response_body.c_str());
@@ -406,10 +560,6 @@ TEST_P(ServiceWorkerCacheTestP, TwoKeysThenOne) {
   EXPECT_TRUE(VerifyKeys(expected_key));
 }
 
-// TODO(jkarlin): Once SimpleCache is working bug-free on Windows reenable these
-// tests. In the meanwhile we know that Windows operations will be a little
-// flaky (though not crashy). See https://crbug.com/409109
-#ifndef OS_WIN
 TEST_P(ServiceWorkerCacheTestP, DeleteNoBody) {
   EXPECT_TRUE(Put(no_body_request_, no_body_response_));
   EXPECT_TRUE(Match(no_body_request_));
@@ -449,13 +599,25 @@ TEST_P(ServiceWorkerCacheTestP, QuickStressBody) {
     ASSERT_TRUE(Delete(body_request_));
   }
 }
-#endif  // OS_WIN
+
+TEST_P(ServiceWorkerCacheTestP, PutResponseType) {
+  EXPECT_TRUE(TestResponseType(blink::WebServiceWorkerResponseTypeBasic));
+  EXPECT_TRUE(TestResponseType(blink::WebServiceWorkerResponseTypeCORS));
+  EXPECT_TRUE(TestResponseType(blink::WebServiceWorkerResponseTypeDefault));
+  EXPECT_TRUE(TestResponseType(blink::WebServiceWorkerResponseTypeError));
+  EXPECT_TRUE(TestResponseType(blink::WebServiceWorkerResponseTypeOpaque));
+}
 
 TEST_F(ServiceWorkerCacheTest, CaselessServiceWorkerResponseHeaders) {
   // ServiceWorkerCache depends on ServiceWorkerResponse having caseless
   // headers so that it can quickly lookup vary headers.
-  ServiceWorkerResponse response(
-      GURL("http://www.example.com"), 200, "OK", ServiceWorkerHeaderMap(), "");
+  ServiceWorkerResponse response(GURL("http://www.example.com"),
+                                 200,
+                                 "OK",
+                                 blink::WebServiceWorkerResponseTypeDefault,
+                                 ServiceWorkerHeaderMap(),
+                                 "",
+                                 0);
   response.headers["content-type"] = "foo";
   response.headers["Content-Type"] = "bar";
   EXPECT_EQ("bar", response.headers["content-type"]);
@@ -474,6 +636,159 @@ TEST_F(ServiceWorkerCacheTest, CaselessServiceWorkerFetchRequestHeaders) {
   EXPECT_EQ("bar", request.headers["content-type"]);
 }
 
+TEST_P(ServiceWorkerCacheTestP, QuotaManagerModified) {
+  EXPECT_EQ(0, quota_manager_proxy_->notify_storage_modified_count());
+
+  EXPECT_TRUE(Put(no_body_request_, no_body_response_));
+  EXPECT_EQ(1, quota_manager_proxy_->notify_storage_modified_count());
+  EXPECT_LT(0, quota_manager_proxy_->last_notified_delta());
+  int64 sum_delta = quota_manager_proxy_->last_notified_delta();
+
+  EXPECT_TRUE(Put(body_request_, body_response_));
+  EXPECT_EQ(2, quota_manager_proxy_->notify_storage_modified_count());
+  EXPECT_LT(sum_delta, quota_manager_proxy_->last_notified_delta());
+  sum_delta += quota_manager_proxy_->last_notified_delta();
+
+  EXPECT_TRUE(Delete(body_request_));
+  EXPECT_EQ(3, quota_manager_proxy_->notify_storage_modified_count());
+  sum_delta += quota_manager_proxy_->last_notified_delta();
+
+  EXPECT_TRUE(Delete(no_body_request_));
+  EXPECT_EQ(4, quota_manager_proxy_->notify_storage_modified_count());
+  sum_delta += quota_manager_proxy_->last_notified_delta();
+
+  EXPECT_EQ(0, sum_delta);
+}
+
+TEST_F(ServiceWorkerCacheMemoryOnlyTest, MemoryBackedSize) {
+  EXPECT_EQ(0, cache_->MemoryBackedSize());
+  EXPECT_TRUE(Put(no_body_request_, no_body_response_));
+  EXPECT_LT(0, cache_->MemoryBackedSize());
+  int64 no_body_size = cache_->MemoryBackedSize();
+
+  EXPECT_TRUE(Delete(no_body_request_));
+  EXPECT_EQ(0, cache_->MemoryBackedSize());
+
+  EXPECT_TRUE(Put(body_request_, body_response_));
+  EXPECT_LT(no_body_size, cache_->MemoryBackedSize());
+
+  EXPECT_TRUE(Delete(body_request_));
+  EXPECT_EQ(0, cache_->MemoryBackedSize());
+}
+
+TEST_F(ServiceWorkerCacheTest, MemoryBackedSizePersistent) {
+  EXPECT_EQ(0, cache_->MemoryBackedSize());
+  EXPECT_TRUE(Put(no_body_request_, no_body_response_));
+  EXPECT_EQ(0, cache_->MemoryBackedSize());
+}
+
+TEST_P(ServiceWorkerCacheTestP, OpsFailOnClosedBackendNeverCreated) {
+  cache_->set_pause_backend_creation(
+      true);  // Will hang the test if a backend is created.
+  EXPECT_TRUE(Close());
+  VerifyAllOpsFail();
+}
+
+TEST_P(ServiceWorkerCacheTestP, OpsFailOnClosedBackend) {
+  // Create the backend and put something in it.
+  EXPECT_TRUE(Put(body_request_, body_response_));
+  EXPECT_TRUE(Close());
+  VerifyAllOpsFail();
+}
+
+TEST_F(ServiceWorkerCacheTest, ClosedDuringPut) {
+  // Even though Close is called in the middle of a Put operation (during
+  // backend creation), the put operation should still finish.
+  cache_->set_pause_backend_creation(true);
+  scoped_ptr<base::RunLoop> close_loop(new base::RunLoop());
+  cache_->Put(CopyFetchRequest(body_request_),
+              CopyFetchResponse(body_response_),
+              base::Bind(&ServiceWorkerCacheTest::ResponseAndErrorCallback,
+                         base::Unretained(this), nullptr));
+  cache_->Close(base::Bind(&ServiceWorkerCacheTest::CloseCallback,
+                           base::Unretained(this), close_loop.get()));
+  base::RunLoop().RunUntilIdle();
+  EXPECT_FALSE(callback_response_);
+  EXPECT_FALSE(callback_closed_);
+
+  cache_->ContinueCreateBackend();
+
+  close_loop->Run();
+  EXPECT_TRUE(callback_response_);
+  EXPECT_TRUE(callback_closed_);
+
+  VerifyAllOpsFail();
+}
+
+TEST_F(ServiceWorkerCacheTest, ClosedDuringMatch) {
+  // Even though Close is called in the middle of a Match operation (during
+  // backend creation), the match operation should still finish.
+  cache_->set_pause_backend_creation(true);
+  scoped_ptr<base::RunLoop> close_loop(new base::RunLoop());
+  cache_->Match(CopyFetchRequest(body_request_),
+                base::Bind(&ServiceWorkerCacheTest::ResponseAndErrorCallback,
+                           base::Unretained(this), nullptr));
+  cache_->Close(base::Bind(&ServiceWorkerCacheTest::CloseCallback,
+                           base::Unretained(this), close_loop.get()));
+  base::RunLoop().RunUntilIdle();
+  EXPECT_EQ(ServiceWorkerCache::ErrorTypeOK, callback_error_);
+  EXPECT_FALSE(callback_closed_);
+
+  cache_->ContinueCreateBackend();
+
+  close_loop->Run();
+  EXPECT_EQ(ServiceWorkerCache::ErrorTypeNotFound, callback_error_);
+  EXPECT_TRUE(callback_closed_);
+
+  VerifyAllOpsFail();
+}
+
+TEST_F(ServiceWorkerCacheTest, ClosedDuringDelete) {
+  // Even though Close is called in the middle of a Delete operation (during
+  // backend creation), the delete operation should still finish.
+  cache_->set_pause_backend_creation(true);
+  scoped_ptr<base::RunLoop> close_loop(new base::RunLoop());
+  cache_->Delete(CopyFetchRequest(body_request_),
+                 base::Bind(&ServiceWorkerCacheTest::ErrorTypeCallback,
+                            base::Unretained(this), nullptr));
+  cache_->Close(base::Bind(&ServiceWorkerCacheTest::CloseCallback,
+                           base::Unretained(this), close_loop.get()));
+  base::RunLoop().RunUntilIdle();
+  EXPECT_EQ(ServiceWorkerCache::ErrorTypeOK, callback_error_);
+  EXPECT_FALSE(callback_closed_);
+
+  cache_->ContinueCreateBackend();
+
+  close_loop->Run();
+  EXPECT_EQ(ServiceWorkerCache::ErrorTypeNotFound, callback_error_);
+  EXPECT_TRUE(callback_closed_);
+
+  VerifyAllOpsFail();
+}
+
+TEST_F(ServiceWorkerCacheTest, ClosedDuringKeys) {
+  // Even though Close is called in the middle of a Keys operation (during
+  // backend creation), the keys operation should still finish.
+  cache_->set_pause_backend_creation(true);
+  scoped_ptr<base::RunLoop> close_loop(new base::RunLoop());
+  callback_error_ = ServiceWorkerCache::ErrorTypeNotFound;
+  cache_->Keys(base::Bind(&ServiceWorkerCacheTest::RequestsCallback,
+                          base::Unretained(this), nullptr));
+  cache_->Close(base::Bind(&ServiceWorkerCacheTest::CloseCallback,
+                           base::Unretained(this), close_loop.get()));
+  base::RunLoop().RunUntilIdle();
+  EXPECT_EQ(ServiceWorkerCache::ErrorTypeNotFound, callback_error_);
+  EXPECT_FALSE(callback_closed_);
+
+  cache_->ContinueCreateBackend();
+
+  close_loop->Run();
+  EXPECT_EQ(ServiceWorkerCache::ErrorTypeOK, callback_error_);
+  EXPECT_TRUE(callback_closed_);
+
+  VerifyAllOpsFail();
+}
+
 INSTANTIATE_TEST_CASE_P(ServiceWorkerCacheTest,
                         ServiceWorkerCacheTestP,
                         ::testing::Values(false, true));