| Index: content/browser/service_worker/service_worker_disk_cache_migrator_unittest.cc
 | 
| diff --git a/content/browser/service_worker/service_worker_disk_cache_migrator_unittest.cc b/content/browser/service_worker/service_worker_disk_cache_migrator_unittest.cc
 | 
| new file mode 100644
 | 
| index 0000000000000000000000000000000000000000..7f3f030a3430331453efd75734da7ff18e4ee052
 | 
| --- /dev/null
 | 
| +++ b/content/browser/service_worker/service_worker_disk_cache_migrator_unittest.cc
 | 
| @@ -0,0 +1,241 @@
 | 
| +// Copyright 2015 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/service_worker/service_worker_disk_cache_migrator.h"
 | 
| +
 | 
| +#include "base/files/scoped_temp_dir.h"
 | 
| +#include "base/run_loop.h"
 | 
| +#include "base/thread_task_runner_handle.h"
 | 
| +#include "content/public/test/test_browser_thread_bundle.h"
 | 
| +#include "net/base/io_buffer.h"
 | 
| +#include "net/base/net_errors.h"
 | 
| +#include "net/base/test_completion_callback.h"
 | 
| +#include "net/http/http_response_headers.h"
 | 
| +#include "testing/gtest/include/gtest/gtest.h"
 | 
| +
 | 
| +namespace content {
 | 
| +
 | 
| +namespace {
 | 
| +
 | 
| +const int kMaxDiskCacheSize = 250 * 1024 * 1024;
 | 
| +
 | 
| +struct ResponseData {
 | 
| +  int64 resource_id;
 | 
| +  std::string headers;
 | 
| +  std::string body;
 | 
| +  std::string metadata;
 | 
| +
 | 
| +  ResponseData(int64 resource_id,
 | 
| +               const std::string& headers,
 | 
| +               const std::string& body,
 | 
| +               const std::string& metadata)
 | 
| +      : resource_id(resource_id),
 | 
| +        headers(headers),
 | 
| +        body(body),
 | 
| +        metadata(metadata) {}
 | 
| +};
 | 
| +
 | 
| +void OnDiskCacheMigrated(const base::Closure& callback,
 | 
| +                         ServiceWorkerStatusCode status) {
 | 
| +  EXPECT_EQ(SERVICE_WORKER_OK, status);
 | 
| +  callback.Run();
 | 
| +}
 | 
| +
 | 
| +}  // namespace
 | 
| +
 | 
| +class ServiceWorkerDiskCacheMigratorTest : public testing::Test {
 | 
| + public:
 | 
| +  ServiceWorkerDiskCacheMigratorTest()
 | 
| +      : browser_thread_bundle_(TestBrowserThreadBundle::IO_MAINLOOP) {}
 | 
| +
 | 
| +  void SetUp() override {
 | 
| +    ASSERT_TRUE(user_data_directory_.CreateUniqueTempDir());
 | 
| +    const base::FilePath kSrcDiskCachePath =
 | 
| +        user_data_directory_.path().AppendASCII("SrcCache");
 | 
| +    const base::FilePath kDestDiskCachePath =
 | 
| +        user_data_directory_.path().AppendASCII("DestCache");
 | 
| +
 | 
| +    // Initialize the src BlockFile diskcache.
 | 
| +    src_ = ServiceWorkerDiskCache::CreateWithBlockFileBackend();
 | 
| +    net::TestCompletionCallback cb1;
 | 
| +    src_->InitWithDiskBackend(
 | 
| +        kSrcDiskCachePath, kMaxDiskCacheSize, false /* force */,
 | 
| +        base::ThreadTaskRunnerHandle::Get(), cb1.callback());
 | 
| +    ASSERT_EQ(net::OK, cb1.WaitForResult());
 | 
| +
 | 
| +    // Initialize the dest Simple diskcache.
 | 
| +    dest_ = ServiceWorkerDiskCache::CreateWithSimpleBackend();
 | 
| +    net::TestCompletionCallback cb2;
 | 
| +    dest_->InitWithDiskBackend(
 | 
| +        kDestDiskCachePath, kMaxDiskCacheSize, false /* force */,
 | 
| +        base::ThreadTaskRunnerHandle::Get(), cb2.callback());
 | 
| +    ASSERT_EQ(net::OK, cb2.WaitForResult());
 | 
| +
 | 
| +    migrator_.reset(
 | 
| +        new ServiceWorkerDiskCacheMigrator(src_.get(), dest_.get()));
 | 
| +  }
 | 
| +
 | 
| +  bool WriteResponse(ServiceWorkerDiskCache* disk_cache,
 | 
| +                     const ResponseData& response) {
 | 
| +    scoped_ptr<ServiceWorkerResponseWriter> writer(
 | 
| +        new ServiceWorkerResponseWriter(response.resource_id, disk_cache));
 | 
| +
 | 
| +    // Write the response info.
 | 
| +    scoped_ptr<net::HttpResponseInfo> info(new net::HttpResponseInfo);
 | 
| +    info->request_time = base::Time() + base::TimeDelta::FromSeconds(10);
 | 
| +    info->response_time = info->request_time + base::TimeDelta::FromSeconds(10);
 | 
| +    info->was_cached = false;
 | 
| +    info->headers = new net::HttpResponseHeaders(response.headers);
 | 
| +    scoped_refptr<HttpResponseInfoIOBuffer> info_buffer =
 | 
| +        new HttpResponseInfoIOBuffer(info.release());
 | 
| +    net::TestCompletionCallback cb1;
 | 
| +    writer->WriteInfo(info_buffer.get(), cb1.callback());
 | 
| +    int rv = cb1.WaitForResult();
 | 
| +    EXPECT_LT(0, rv);
 | 
| +    if (rv < 0)
 | 
| +      return false;
 | 
| +
 | 
| +    // Write the response metadata.
 | 
| +    scoped_ptr<ServiceWorkerResponseMetadataWriter> metadata_writer(
 | 
| +        new ServiceWorkerResponseMetadataWriter(response.resource_id,
 | 
| +                                                disk_cache));
 | 
| +    scoped_refptr<net::IOBuffer> metadata_buffer(
 | 
| +        new net::WrappedIOBuffer(response.metadata.data()));
 | 
| +    const int metadata_length = response.metadata.length();
 | 
| +    net::TestCompletionCallback cb2;
 | 
| +    metadata_writer->WriteMetadata(metadata_buffer.get(), metadata_length,
 | 
| +                                   cb2.callback());
 | 
| +    rv = cb2.WaitForResult();
 | 
| +    EXPECT_EQ(metadata_length, rv);
 | 
| +    if (metadata_length != rv)
 | 
| +      return false;
 | 
| +
 | 
| +    // Write the response body.
 | 
| +    scoped_refptr<net::IOBuffer> body_buffer(
 | 
| +        new net::WrappedIOBuffer(response.body.data()));
 | 
| +    const int body_length = response.body.length();
 | 
| +    net::TestCompletionCallback cb3;
 | 
| +    writer->WriteData(body_buffer.get(), body_length, cb3.callback());
 | 
| +    rv = cb3.WaitForResult();
 | 
| +    EXPECT_EQ(body_length, rv);
 | 
| +    if (body_length != rv)
 | 
| +      return false;
 | 
| +
 | 
| +    return true;
 | 
| +  }
 | 
| +
 | 
| +  void VerifyResponse(ServiceWorkerDiskCache* disk_cache,
 | 
| +                      const ResponseData& expected) {
 | 
| +    scoped_ptr<ServiceWorkerResponseReader> reader(
 | 
| +        new ServiceWorkerResponseReader(expected.resource_id, disk_cache));
 | 
| +
 | 
| +    // Verify the response info.
 | 
| +    scoped_refptr<HttpResponseInfoIOBuffer> info_buffer =
 | 
| +        new HttpResponseInfoIOBuffer;
 | 
| +    net::TestCompletionCallback cb1;
 | 
| +    reader->ReadInfo(info_buffer.get(), cb1.callback());
 | 
| +    int rv = cb1.WaitForResult();
 | 
| +    EXPECT_LT(0, rv);
 | 
| +    EXPECT_EQ("OK", info_buffer->http_info->headers->GetStatusText());
 | 
| +
 | 
| +    // Verify the response metadata.
 | 
| +    if (!expected.metadata.empty()) {
 | 
| +      ASSERT_TRUE(info_buffer->http_info->metadata);
 | 
| +      const int data_size = info_buffer->http_info->metadata->size();
 | 
| +      ASSERT_EQ(static_cast<int>(expected.metadata.length()), data_size);
 | 
| +      EXPECT_EQ(0, memcmp(expected.metadata.data(),
 | 
| +                          info_buffer->http_info->metadata->data(),
 | 
| +                          expected.metadata.length()));
 | 
| +    }
 | 
| +
 | 
| +    // Verify the response body.
 | 
| +    const int kBigEnough = 512;
 | 
| +    scoped_refptr<net::IOBuffer> body_buffer = new net::IOBuffer(kBigEnough);
 | 
| +    net::TestCompletionCallback cb2;
 | 
| +    reader->ReadData(body_buffer.get(), kBigEnough, cb2.callback());
 | 
| +    rv = cb2.WaitForResult();
 | 
| +    ASSERT_EQ(static_cast<int>(expected.body.length()), rv);
 | 
| +    EXPECT_EQ(0, memcmp(expected.body.data(), body_buffer->data(), rv));
 | 
| +  }
 | 
| +
 | 
| +  void Migrate() {
 | 
| +    base::RunLoop run_loop;
 | 
| +    migrator_->Start(base::Bind(&OnDiskCacheMigrated, run_loop.QuitClosure()));
 | 
| +    run_loop.Run();
 | 
| +  }
 | 
| +
 | 
| +  int32 GetEntryCount(ServiceWorkerDiskCache* disk_cache) {
 | 
| +    return disk_cache->disk_cache()->GetEntryCount();
 | 
| +  }
 | 
| +
 | 
| +  void SetMaxNumberOfInflightTasks(size_t max_number) {
 | 
| +    migrator_->set_max_number_of_inflight_tasks(max_number);
 | 
| +  }
 | 
| +
 | 
| + protected:
 | 
| +  TestBrowserThreadBundle browser_thread_bundle_;
 | 
| +  base::ScopedTempDir user_data_directory_;
 | 
| +  scoped_ptr<ServiceWorkerDiskCache> src_;
 | 
| +  scoped_ptr<ServiceWorkerDiskCache> dest_;
 | 
| +  scoped_ptr<ServiceWorkerDiskCacheMigrator> migrator_;
 | 
| +};
 | 
| +
 | 
| +TEST_F(ServiceWorkerDiskCacheMigratorTest, Basic) {
 | 
| +  std::vector<ResponseData> responses;
 | 
| +  responses.push_back(ResponseData(1, "HTTP/1.1 200 OK\0\0", "Hello", ""));
 | 
| +  responses.push_back(ResponseData(2, "HTTP/1.1 200 OK\0\0", "Service", ""));
 | 
| +  responses.push_back(ResponseData(5, "HTTP/1.1 200 OK\0\0", "Worker", ""));
 | 
| +  responses.push_back(ResponseData(3, "HTTP/1.1 200 OK\0\0", "World", "meta"));
 | 
| +  responses.push_back(ResponseData(10, "HTTP/1.1 200 OK\0\0", "", "meta"));
 | 
| +  responses.push_back(ResponseData(11, "HTTP/1.1 200 OK\0\0", "body", ""));
 | 
| +  responses.push_back(ResponseData(12, "HTTP/1.1 200 OK\0\0", "", ""));
 | 
| +  responses.push_back(ResponseData(
 | 
| +      20, "HTTP/1.1 200 OK\0\0", std::string(256, 'a'), std::string(128, 'b')));
 | 
| +
 | 
| +  // Populate initial data in the src diskcache.
 | 
| +  for (const ResponseData& response : responses) {
 | 
| +    ASSERT_TRUE(WriteResponse(src_.get(), response));
 | 
| +    VerifyResponse(src_.get(), response);
 | 
| +  }
 | 
| +  ASSERT_EQ(static_cast<int>(responses.size()), GetEntryCount(src_.get()));
 | 
| +
 | 
| +  Migrate();
 | 
| +
 | 
| +  // Verify the migrated contents in the dest diskcache.
 | 
| +  for (const ResponseData& response : responses)
 | 
| +    VerifyResponse(dest_.get(), response);
 | 
| +  EXPECT_EQ(static_cast<int>(responses.size()), GetEntryCount(dest_.get()));
 | 
| +}
 | 
| +
 | 
| +TEST_F(ServiceWorkerDiskCacheMigratorTest, MigrateEmptyDiskCache) {
 | 
| +  ASSERT_EQ(0, GetEntryCount(src_.get()));
 | 
| +  Migrate();
 | 
| +  EXPECT_EQ(0, GetEntryCount(dest_.get()));
 | 
| +}
 | 
| +
 | 
| +TEST_F(ServiceWorkerDiskCacheMigratorTest, ThrottleInflightTasks) {
 | 
| +  std::vector<ResponseData> responses;
 | 
| +  for (int i = 0; i < 10; ++i)
 | 
| +    responses.push_back(ResponseData(i, "HTTP/1.1 200 OK\0\0", "foo", "bar"));
 | 
| +
 | 
| +  // Populate initial data in the src diskcache.
 | 
| +  for (const ResponseData& response : responses) {
 | 
| +    ASSERT_TRUE(WriteResponse(src_.get(), response));
 | 
| +    VerifyResponse(src_.get(), response);
 | 
| +  }
 | 
| +  ASSERT_EQ(static_cast<int>(responses.size()), GetEntryCount(src_.get()));
 | 
| +
 | 
| +  // Tighten the max number of inflight tasks.
 | 
| +  SetMaxNumberOfInflightTasks(2);
 | 
| +
 | 
| +  // Migration should hit the limit, but should successfully complete.
 | 
| +  Migrate();
 | 
| +
 | 
| +  // Verify the migrated contents in the dest diskcache.
 | 
| +  for (const ResponseData& response : responses)
 | 
| +    VerifyResponse(dest_.get(), response);
 | 
| +  EXPECT_EQ(static_cast<int>(responses.size()), GetEntryCount(dest_.get()));
 | 
| +}
 | 
| +
 | 
| +}  // namespace content
 | 
| 
 |