| Index: content/browser/blob_storage/blob_async_builder_host_unittest.cc
 | 
| diff --git a/content/browser/blob_storage/blob_async_builder_host_unittest.cc b/content/browser/blob_storage/blob_async_builder_host_unittest.cc
 | 
| new file mode 100644
 | 
| index 0000000000000000000000000000000000000000..32f715e347bd5dce3369b2a37f504ab6edf98196
 | 
| --- /dev/null
 | 
| +++ b/content/browser/blob_storage/blob_async_builder_host_unittest.cc
 | 
| @@ -0,0 +1,240 @@
 | 
| +// 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 "storage/browser/blob/blob_async_builder_host.h"
 | 
| +
 | 
| +#include "base/bind.h"
 | 
| +#include "base/logging.h"
 | 
| +#include "base/memory/shared_memory.h"
 | 
| +#include "storage/common/blob_storage/blob_storage_constants.h"
 | 
| +#include "testing/gtest/include/gtest/gtest.h"
 | 
| +
 | 
| +namespace storage {
 | 
| +namespace {
 | 
| +const std::string kBlobUUID = "blobUUIDYAY";
 | 
| +const std::string kFakeBlobUUID = "fakeBlob";
 | 
| +const std::string kBlobType = "blobtypeYAY";
 | 
| +
 | 
| +const size_t kTestBlobStorageIPCThresholdBytes = 5;
 | 
| +const size_t kTestBlobStorageMaxSharedMemoryBytes = 20;
 | 
| +const uint64_t kTestBlobStorageMaxFileSizeBytes = 100;
 | 
| +
 | 
| +void PopulateBytes(char* bytes, size_t start_num, size_t length) {
 | 
| +  for (size_t i = 0; i < length; i++) {
 | 
| +    bytes[i] = static_cast<char>(start_num + i);
 | 
| +  }
 | 
| +}
 | 
| +
 | 
| +void PopulateBytes(char* bytes, size_t length) {
 | 
| +  PopulateBytes(bytes, 0, length);
 | 
| +}
 | 
| +
 | 
| +void AddMemoryItem(size_t length, std::vector<DataElement>* out) {
 | 
| +  DataElement bytes;
 | 
| +  bytes.SetToBytesDescription(length);
 | 
| +  out->push_back(bytes);
 | 
| +}
 | 
| +
 | 
| +void AddShortcutMemoryItem(size_t length, std::vector<DataElement>* out) {
 | 
| +  DataElement bytes;
 | 
| +  bytes.SetToAllocatedBytes(length);
 | 
| +  PopulateBytes(bytes.mutable_bytes(), length);
 | 
| +  out->push_back(bytes);
 | 
| +}
 | 
| +
 | 
| +void AddShortcutMemoryItem(size_t length, BlobDataBuilder* out) {
 | 
| +  DataElement bytes;
 | 
| +  bytes.SetToAllocatedBytes(length);
 | 
| +  PopulateBytes(bytes.mutable_bytes(), length);
 | 
| +  out->AppendData(bytes.bytes(), length);
 | 
| +}
 | 
| +
 | 
| +void AddBlobItem(std::vector<DataElement>* out) {
 | 
| +  DataElement blob;
 | 
| +  blob.SetToBlob(kFakeBlobUUID);
 | 
| +  out->push_back(blob);
 | 
| +}
 | 
| +
 | 
| +class BlobAsyncBuilderHostTest : public testing::Test {
 | 
| + protected:
 | 
| +  BlobAsyncBuilderHostTest()
 | 
| +      : matching_builder_(nullptr),
 | 
| +        done_called_(false),
 | 
| +        cancel_called_(false),
 | 
| +        cancel_code_(IPCBlobCreationCancelCode::UNKNOWN),
 | 
| +        request_called_(false) {}
 | 
| +  ~BlobAsyncBuilderHostTest() override {}
 | 
| +
 | 
| +  void SetUp() override {
 | 
| +    matching_builder_ = nullptr;
 | 
| +    done_called_ = false;
 | 
| +    cancel_called_ = false;
 | 
| +    cancel_code_ = IPCBlobCreationCancelCode::UNKNOWN;
 | 
| +    request_called_ = false;
 | 
| +    requests_.clear();
 | 
| +    memory_handles_.clear();
 | 
| +    file_handles_.clear();
 | 
| +    host_.SetMemoryConstantsForTesting(kTestBlobStorageIPCThresholdBytes,
 | 
| +                                       kTestBlobStorageMaxSharedMemoryBytes,
 | 
| +                                       kTestBlobStorageMaxFileSizeBytes);
 | 
| +  }
 | 
| +
 | 
| +  void SetMatchingBuilder(BlobDataBuilder* builder) {
 | 
| +    matching_builder_ = builder;
 | 
| +  }
 | 
| +
 | 
| +  void CancelCallback(IPCBlobCreationCancelCode code) {
 | 
| +    cancel_called_ = true;
 | 
| +    cancel_code_ = code;
 | 
| +  }
 | 
| +
 | 
| +  void DoneCallback(BlobDataBuilder* builder) {
 | 
| +    ASSERT_TRUE(builder);
 | 
| +    if (matching_builder_)
 | 
| +      EXPECT_EQ(*matching_builder_, *builder);
 | 
| +    done_called_ = true;
 | 
| +  }
 | 
| +
 | 
| +  void RequestMemoryCallback(
 | 
| +      const std::vector<storage::BlobItemBytesRequest>& requests,
 | 
| +      const std::vector<base::SharedMemoryHandle>& sms,
 | 
| +      const std::vector<uint64_t>& pfs) {
 | 
| +    this->requests_ = requests;
 | 
| +    memory_handles_ = sms;
 | 
| +    file_handles_ = pfs;
 | 
| +    request_called_ = true;
 | 
| +  }
 | 
| +
 | 
| +  void BuildBlobAsync(const std::vector<DataElement>& descriptions,
 | 
| +                      size_t memory_available) {
 | 
| +    host_.StartBuildingBlob(
 | 
| +        kBlobUUID, kBlobType, descriptions, memory_available,
 | 
| +        base::Bind(&BlobAsyncBuilderHostTest::RequestMemoryCallback,
 | 
| +                   base::Unretained(this)),
 | 
| +        base::Bind(&BlobAsyncBuilderHostTest::DoneCallback,
 | 
| +                   base::Unretained(this)),
 | 
| +        base::Bind(&BlobAsyncBuilderHostTest::CancelCallback,
 | 
| +                   base::Unretained(this)));
 | 
| +  }
 | 
| +
 | 
| +  BlobDataBuilder* matching_builder_;
 | 
| +  BlobAsyncBuilderHost host_;
 | 
| +  bool done_called_;
 | 
| +  bool cancel_called_;
 | 
| +  IPCBlobCreationCancelCode cancel_code_;
 | 
| +
 | 
| +  bool request_called_;
 | 
| +  std::vector<storage::BlobItemBytesRequest> requests_;
 | 
| +  std::vector<base::SharedMemoryHandle> memory_handles_;
 | 
| +  std::vector<uint64_t> file_handles_;
 | 
| +};
 | 
| +
 | 
| +TEST_F(BlobAsyncBuilderHostTest, TestShortcut) {
 | 
| +  std::vector<DataElement> descriptions;
 | 
| +
 | 
| +  AddShortcutMemoryItem(10, &descriptions);
 | 
| +  AddBlobItem(&descriptions);
 | 
| +  AddShortcutMemoryItem(5000, &descriptions);
 | 
| +
 | 
| +  BlobDataBuilder expected(kBlobUUID);
 | 
| +  expected.set_content_type(kBlobType);
 | 
| +  AddShortcutMemoryItem(10, &expected);
 | 
| +  expected.AppendBlob(kFakeBlobUUID);
 | 
| +  AddShortcutMemoryItem(5000, &expected);
 | 
| +  SetMatchingBuilder(&expected);
 | 
| +
 | 
| +  BuildBlobAsync(descriptions, 5010);
 | 
| +
 | 
| +  EXPECT_TRUE(done_called_);
 | 
| +  EXPECT_FALSE(cancel_called_);
 | 
| +  EXPECT_FALSE(request_called_);
 | 
| +  EXPECT_EQ(0u, host_.blob_building_count());
 | 
| +};
 | 
| +
 | 
| +TEST_F(BlobAsyncBuilderHostTest, TestSingleSharedMemRequest) {
 | 
| +  std::vector<DataElement> descriptions;
 | 
| +  const size_t kSize = kTestBlobStorageIPCThresholdBytes + 1;
 | 
| +  AddMemoryItem(kSize, &descriptions);
 | 
| +
 | 
| +  BuildBlobAsync(descriptions, kTestBlobStorageIPCThresholdBytes + 1);
 | 
| +
 | 
| +  EXPECT_FALSE(done_called_);
 | 
| +  EXPECT_FALSE(cancel_called_);
 | 
| +  EXPECT_TRUE(request_called_);
 | 
| +  EXPECT_EQ(1u, host_.blob_building_count());
 | 
| +  ASSERT_EQ(1u, requests_.size());
 | 
| +  request_called_ = false;
 | 
| +
 | 
| +  EXPECT_EQ(
 | 
| +      BlobItemBytesRequest::CreateSharedMemoryRequest(0, 0, 0, kSize, 0, 0),
 | 
| +      requests_.at(0));
 | 
| +};
 | 
| +
 | 
| +TEST_F(BlobAsyncBuilderHostTest, TestMultipleSharedMemRequests) {
 | 
| +  std::vector<DataElement> descriptions;
 | 
| +  const size_t kSize = kTestBlobStorageMaxSharedMemoryBytes + 1;
 | 
| +  const char kFirstBlockByte = 7;
 | 
| +  const char kSecondBlockByte = 19;
 | 
| +  AddMemoryItem(kSize, &descriptions);
 | 
| +
 | 
| +  BlobDataBuilder expected(kBlobUUID);
 | 
| +  expected.set_content_type(kBlobType);
 | 
| +  char data[kSize];
 | 
| +  memset(data, kFirstBlockByte, kTestBlobStorageMaxSharedMemoryBytes);
 | 
| +  expected.AppendData(data, kTestBlobStorageMaxSharedMemoryBytes);
 | 
| +  expected.AppendData(&kSecondBlockByte, 1);
 | 
| +  SetMatchingBuilder(&expected);
 | 
| +
 | 
| +  BuildBlobAsync(descriptions, kTestBlobStorageMaxSharedMemoryBytes + 1);
 | 
| +
 | 
| +  EXPECT_FALSE(done_called_);
 | 
| +  EXPECT_FALSE(cancel_called_);
 | 
| +  EXPECT_TRUE(request_called_);
 | 
| +  EXPECT_EQ(1u, host_.blob_building_count());
 | 
| +  ASSERT_EQ(1u, requests_.size());
 | 
| +  request_called_ = false;
 | 
| +
 | 
| +  // We need to grab a duplicate handle so we can have two blocks open at the
 | 
| +  // same time.
 | 
| +  base::SharedMemoryHandle handle =
 | 
| +      base::SharedMemory::DuplicateHandle(memory_handles_.at(0));
 | 
| +  EXPECT_TRUE(base::SharedMemory::IsHandleValid(handle));
 | 
| +  base::SharedMemory shared_memory(handle, false);
 | 
| +  EXPECT_TRUE(shared_memory.Map(kTestBlobStorageMaxSharedMemoryBytes));
 | 
| +
 | 
| +  EXPECT_EQ(BlobItemBytesRequest::CreateSharedMemoryRequest(
 | 
| +                0, 0, 0, kTestBlobStorageMaxSharedMemoryBytes, 0, 0),
 | 
| +            requests_.at(0));
 | 
| +
 | 
| +  memset(shared_memory.memory(), kFirstBlockByte,
 | 
| +         kTestBlobStorageMaxSharedMemoryBytes);
 | 
| +
 | 
| +  BlobItemBytesResponse response(0);
 | 
| +  std::vector<BlobItemBytesResponse> responses = {response};
 | 
| +  host_.OnMemoryResponses(kBlobUUID, responses);
 | 
| +
 | 
| +  EXPECT_FALSE(done_called_);
 | 
| +  EXPECT_FALSE(cancel_called_);
 | 
| +  EXPECT_TRUE(request_called_);
 | 
| +  EXPECT_EQ(1u, host_.blob_building_count());
 | 
| +  ASSERT_EQ(1u, requests_.size());
 | 
| +  request_called_ = false;
 | 
| +
 | 
| +  EXPECT_EQ(BlobItemBytesRequest::CreateSharedMemoryRequest(
 | 
| +                1, 0, kTestBlobStorageMaxSharedMemoryBytes, 1, 0, 0),
 | 
| +            requests_.at(0));
 | 
| +
 | 
| +  memset(shared_memory.memory(), kSecondBlockByte, 1);
 | 
| +
 | 
| +  response.request_number = 1;
 | 
| +  responses[0] = response;
 | 
| +  host_.OnMemoryResponses(kBlobUUID, responses);
 | 
| +  EXPECT_TRUE(done_called_);
 | 
| +  EXPECT_FALSE(cancel_called_);
 | 
| +  EXPECT_FALSE(request_called_);
 | 
| +  EXPECT_EQ(0u, host_.blob_building_count());
 | 
| +};
 | 
| +
 | 
| +}  // namespace
 | 
| +}  // namespace storage
 | 
| 
 |