Index: content/browser/blob_storage/blob_transport_host_unittest.cc |
diff --git a/content/browser/blob_storage/blob_transport_host_unittest.cc b/content/browser/blob_storage/blob_transport_host_unittest.cc |
new file mode 100644 |
index 0000000000000000000000000000000000000000..55877d97fdd90fbdd24f4390fb2573384b4b566e |
--- /dev/null |
+++ b/content/browser/blob_storage/blob_transport_host_unittest.cc |
@@ -0,0 +1,512 @@ |
+// 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 <stddef.h> |
+#include <stdint.h> |
+#include <string.h> |
+ |
+#include "base/bind.h" |
+#include "base/logging.h" |
+#include "base/memory/shared_memory.h" |
+#include "base/run_loop.h" |
+#include "content/public/test/test_browser_thread_bundle.h" |
+#include "storage/browser/blob/blob_data_builder.h" |
+#include "storage/browser/blob/blob_data_handle.h" |
+#include "storage/browser/blob/blob_storage_context.h" |
+#include "storage/browser/blob/blob_transport_host.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 kContentType = "content_type"; |
+const std::string kContentDisposition = "content_disposition"; |
+const std::string kCompletedBlobUUID = "completedBlob"; |
+const std::string kCompletedBlobData = "completedBlobData"; |
+ |
+const size_t kTestBlobStorageIPCThresholdBytes = 5; |
+const size_t kTestBlobStorageMaxSharedMemoryBytes = 20; |
+ |
+const size_t kTestBlobStorageMaxBlobMemorySize = 400; |
+const uint64_t kTestBlobStorageMaxDiskSpace = 4000; |
+const uint64_t kTestBlobStorageMinFileSizeBytes = 10; |
+const uint64_t kTestBlobStorageMaxFileSizeBytes = 100; |
+ |
+void PopulateBytes(char* bytes, size_t length) { |
+ for (size_t i = 0; i < length; i++) { |
+ bytes[i] = static_cast<char>(i); |
+ } |
+} |
+ |
+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(kCompletedBlobUUID); |
+ out->push_back(blob); |
+} |
+} // namespace |
+ |
+class BlobTransportHostTest : public testing::Test { |
+ public: |
+ BlobTransportHostTest() |
+ : status_code_(BlobStatus::ERR_INVALID_CONSTRUCTION_ARGUMENTS), |
+ request_called_(false) {} |
+ ~BlobTransportHostTest() override {} |
+ |
+ void SetUp() override { |
+ status_code_ = BlobStatus::ERR_INVALID_CONSTRUCTION_ARGUMENTS; |
+ request_called_ = false; |
+ requests_.clear(); |
+ memory_handles_.clear(); |
+ storage::BlobStorageLimits limits; |
+ limits.max_ipc_memory_size = kTestBlobStorageIPCThresholdBytes; |
+ limits.max_shared_memory_size = kTestBlobStorageMaxSharedMemoryBytes; |
+ limits.max_blob_in_memory_space = kTestBlobStorageMaxBlobMemorySize; |
+ limits.max_blob_disk_space = kTestBlobStorageMaxDiskSpace; |
+ limits.min_page_file_size = kTestBlobStorageMinFileSizeBytes; |
+ limits.max_file_size = kTestBlobStorageMaxFileSizeBytes; |
+ context_.mutable_memory_controller()->set_limits_for_testing(limits); |
+ BlobDataBuilder builder(kCompletedBlobUUID); |
+ builder.AppendData(kCompletedBlobData); |
+ completed_blob_handle_ = context_.AddFinishedBlob(builder); |
+ EXPECT_EQ(BlobStatus::DONE, completed_blob_handle_->GetBlobStatus()); |
+ } |
+ |
+ void StatusCallback(BlobStatus status) { |
+ status_called_ = true; |
+ status_code_ = status; |
+ } |
+ |
+ void RequestMemoryCallback( |
+ std::vector<storage::BlobItemBytesRequest> requests, |
+ std::vector<base::SharedMemoryHandle> shared_memory_handles, |
+ std::vector<base::File> files) { |
+ requests_ = std::move(requests); |
+ memory_handles_ = std::move(shared_memory_handles); |
+ request_called_ = true; |
+ } |
+ |
+ BlobStatus BuildBlobAsync(const std::string& uuid, |
+ const std::vector<DataElement>& descriptions, |
+ std::unique_ptr<BlobDataHandle>* storage) { |
+ EXPECT_NE(storage, nullptr); |
+ request_called_ = false; |
+ status_called_ = false; |
+ *storage = host_.StartBuildingBlob( |
+ uuid, kContentType, kContentDisposition, descriptions, &context_, |
+ base::Bind(&BlobTransportHostTest::RequestMemoryCallback, |
+ base::Unretained(this)), |
+ base::Bind(&BlobTransportHostTest::StatusCallback, |
+ base::Unretained(this))); |
+ if (status_called_) |
+ return status_code_; |
+ else |
+ return context_.GetBlobStatus(uuid); |
+ } |
+ |
+ BlobStatus GetBlobStatus(const std::string& uuid) const { |
+ return context_.GetBlobStatus(uuid); |
+ } |
+ |
+ bool IsBeingBuiltInContext(const std::string& uuid) const { |
+ return BlobStatusIsPending(context_.GetBlobStatus(uuid)); |
+ } |
+ |
+ content::TestBrowserThreadBundle browser_thread_bundle_; |
+ BlobStorageContext context_; |
+ BlobTransportHost host_; |
+ bool status_called_; |
+ BlobStatus status_code_; |
+ |
+ bool request_called_; |
+ std::vector<storage::BlobItemBytesRequest> requests_; |
+ std::vector<base::SharedMemoryHandle> memory_handles_; |
+ std::unique_ptr<BlobDataHandle> completed_blob_handle_; |
+}; |
+ |
+// The 'shortcut' method is when the data is included in the initial IPCs and |
+// the browser uses that instead of requesting the memory. |
+TEST_F(BlobTransportHostTest, TestShortcut) { |
+ std::vector<DataElement> descriptions; |
+ |
+ AddShortcutMemoryItem(10, &descriptions); |
+ AddBlobItem(&descriptions); |
+ AddShortcutMemoryItem(300, &descriptions); |
+ |
+ BlobDataBuilder expected(kBlobUUID); |
+ expected.set_content_type(kContentType); |
+ expected.set_content_disposition(kContentDisposition); |
+ AddShortcutMemoryItem(10, &expected); |
+ expected.AppendData(kCompletedBlobData); |
+ AddShortcutMemoryItem(300, &expected); |
+ |
+ std::unique_ptr<BlobDataHandle> handle; |
+ EXPECT_EQ(BlobStatus::DONE, BuildBlobAsync(kBlobUUID, descriptions, &handle)); |
+ |
+ EXPECT_FALSE(request_called_); |
+ EXPECT_EQ(0u, host_.blob_building_count()); |
+ EXPECT_FALSE(handle->IsBeingBuilt()); |
+ ASSERT_FALSE(handle->IsBroken()); |
+ std::unique_ptr<BlobDataSnapshot> data = handle->CreateSnapshot(); |
+ EXPECT_EQ(expected, *data); |
+ data.reset(); |
+ handle.reset(); |
+ base::RunLoop().RunUntilIdle(); |
+}; |
+ |
+TEST_F(BlobTransportHostTest, TestShortcutNoRoom) { |
+ std::vector<DataElement> descriptions; |
+ |
+ AddShortcutMemoryItem(10, &descriptions); |
+ AddBlobItem(&descriptions); |
+ AddShortcutMemoryItem(5000, &descriptions); |
+ |
+ std::unique_ptr<BlobDataHandle> handle; |
+ EXPECT_EQ(BlobStatus::ERR_OUT_OF_MEMORY, |
+ BuildBlobAsync(kBlobUUID, descriptions, &handle)); |
+ |
+ EXPECT_FALSE(request_called_); |
+ EXPECT_EQ(0u, host_.blob_building_count()); |
+}; |
+ |
+TEST_F(BlobTransportHostTest, TestSingleSharedMemRequest) { |
+ std::vector<DataElement> descriptions; |
+ const size_t kSize = kTestBlobStorageIPCThresholdBytes + 1; |
+ AddMemoryItem(kSize, &descriptions); |
+ |
+ std::unique_ptr<BlobDataHandle> handle; |
+ EXPECT_EQ(BlobStatus::PENDING_TRANSPORT, |
+ BuildBlobAsync(kBlobUUID, descriptions, &handle)); |
+ EXPECT_TRUE(handle); |
+ EXPECT_EQ(BlobStatus::PENDING_TRANSPORT, handle->GetBlobStatus()); |
+ |
+ 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(BlobTransportHostTest, 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(kContentType); |
+ expected.set_content_disposition(kContentDisposition); |
+ char data[kSize]; |
+ memset(data, kFirstBlockByte, kTestBlobStorageMaxSharedMemoryBytes); |
+ expected.AppendData(data, kTestBlobStorageMaxSharedMemoryBytes); |
+ expected.AppendData(&kSecondBlockByte, 1); |
+ |
+ std::unique_ptr<BlobDataHandle> handle; |
+ EXPECT_EQ(BlobStatus::PENDING_TRANSPORT, |
+ BuildBlobAsync(kBlobUUID, descriptions, &handle)); |
+ |
+ 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 shared_mem_handle = |
+ base::SharedMemory::DuplicateHandle(memory_handles_.at(0)); |
+ EXPECT_TRUE(base::SharedMemory::IsHandleValid(shared_mem_handle)); |
+ base::SharedMemory shared_memory(shared_mem_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, &context_); |
+ EXPECT_EQ(BlobStatus::PENDING_TRANSPORT, GetBlobStatus(kBlobUUID)); |
+ ASSERT_TRUE(handle); |
+ EXPECT_EQ(BlobStatus::PENDING_TRANSPORT, handle->GetBlobStatus()); |
+ |
+ 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, &context_); |
+ EXPECT_TRUE(handle); |
+ EXPECT_EQ(BlobStatus::DONE, handle->GetBlobStatus()); |
+ EXPECT_FALSE(request_called_); |
+ EXPECT_EQ(0u, host_.blob_building_count()); |
+ std::unique_ptr<BlobDataHandle> blob_handle = |
+ context_.GetBlobDataFromUUID(kBlobUUID); |
+ EXPECT_FALSE(blob_handle->IsBeingBuilt()); |
+ EXPECT_FALSE(blob_handle->IsBroken()); |
+ std::unique_ptr<BlobDataSnapshot> blob_data = blob_handle->CreateSnapshot(); |
+ EXPECT_EQ(expected, *blob_data); |
+}; |
+ |
+TEST_F(BlobTransportHostTest, TestBasicIPCAndStopBuilding) { |
+ std::vector<DataElement> descriptions; |
+ |
+ AddMemoryItem(2, &descriptions); |
+ AddBlobItem(&descriptions); |
+ AddMemoryItem(2, &descriptions); |
+ |
+ BlobDataBuilder expected(kBlobUUID); |
+ expected.set_content_type(kContentType); |
+ expected.set_content_disposition(kContentDisposition); |
+ AddShortcutMemoryItem(2, &expected); |
+ expected.AppendData(kCompletedBlobData); |
+ AddShortcutMemoryItem(2, &expected); |
+ |
+ std::unique_ptr<BlobDataHandle> handle1; |
+ EXPECT_EQ(BlobStatus::PENDING_TRANSPORT, |
+ BuildBlobAsync(kBlobUUID, descriptions, &handle1)); |
+ EXPECT_TRUE(handle1); |
+ host_.CancelBuildingBlob(kBlobUUID, BlobStatus::ERR_OUT_OF_MEMORY, &context_); |
+ |
+ // Check that we're broken, and then remove the blob. |
+ EXPECT_FALSE(handle1->IsBeingBuilt()); |
+ EXPECT_TRUE(handle1->IsBroken()); |
+ handle1.reset(); |
+ base::RunLoop().RunUntilIdle(); |
+ handle1 = context_.GetBlobDataFromUUID(kBlobUUID); |
+ EXPECT_FALSE(handle1.get()); |
+ |
+ // This should succeed because we've removed all references to the blob. |
+ std::unique_ptr<BlobDataHandle> handle2; |
+ EXPECT_EQ(BlobStatus::PENDING_TRANSPORT, |
+ BuildBlobAsync(kBlobUUID, descriptions, &handle2)); |
+ |
+ EXPECT_TRUE(request_called_); |
+ EXPECT_EQ(1u, host_.blob_building_count()); |
+ request_called_ = false; |
+ |
+ BlobItemBytesResponse response1(0); |
+ PopulateBytes(response1.allocate_mutable_data(2), 2); |
+ BlobItemBytesResponse response2(1); |
+ PopulateBytes(response2.allocate_mutable_data(2), 2); |
+ std::vector<BlobItemBytesResponse> responses = {response1, response2}; |
+ |
+ host_.OnMemoryResponses(kBlobUUID, responses, &context_); |
+ EXPECT_EQ(BlobStatus::DONE, handle2->GetBlobStatus()); |
+ EXPECT_FALSE(request_called_); |
+ EXPECT_EQ(0u, host_.blob_building_count()); |
+ EXPECT_FALSE(handle2->IsBeingBuilt()); |
+ EXPECT_FALSE(handle2->IsBroken()); |
+ std::unique_ptr<BlobDataSnapshot> blob_data = handle2->CreateSnapshot(); |
+ EXPECT_EQ(expected, *blob_data); |
+}; |
+ |
+TEST_F(BlobTransportHostTest, TestBreakingAllBuilding) { |
+ const std::string& kBlob1 = "blob1"; |
+ const std::string& kBlob2 = "blob2"; |
+ const std::string& kBlob3 = "blob3"; |
+ |
+ std::vector<DataElement> descriptions; |
+ AddMemoryItem(2, &descriptions); |
+ |
+ // Register blobs. |
+ std::unique_ptr<BlobDataHandle> handle1; |
+ std::unique_ptr<BlobDataHandle> handle2; |
+ std::unique_ptr<BlobDataHandle> handle3; |
+ EXPECT_EQ(BlobStatus::PENDING_TRANSPORT, |
+ BuildBlobAsync(kBlob1, descriptions, &handle1)); |
+ EXPECT_EQ(BlobStatus::PENDING_TRANSPORT, |
+ BuildBlobAsync(kBlob2, descriptions, &handle2)); |
+ EXPECT_EQ(BlobStatus::PENDING_TRANSPORT, |
+ BuildBlobAsync(kBlob3, descriptions, &handle3)); |
+ |
+ EXPECT_TRUE(request_called_); |
+ EXPECT_TRUE(handle1->IsBeingBuilt() && handle2->IsBeingBuilt() && |
+ handle3->IsBeingBuilt()); |
+ EXPECT_FALSE(handle1->IsBroken() || handle2->IsBroken() || |
+ handle3->IsBroken()); |
+ |
+ EXPECT_TRUE(IsBeingBuiltInContext(kBlob1) && IsBeingBuiltInContext(kBlob2) && |
+ IsBeingBuiltInContext(kBlob3)); |
+ |
+ // This shouldn't call the transport complete callbacks, so our handles should |
+ // still be false. |
+ host_.CancelAll(&context_); |
+ |
+ EXPECT_FALSE(handle1->IsBeingBuilt() || handle2->IsBeingBuilt() || |
+ handle3->IsBeingBuilt()); |
+ EXPECT_TRUE(handle1->IsBroken() && handle2->IsBroken() && |
+ handle3->IsBroken()); |
+ |
+ base::RunLoop().RunUntilIdle(); |
+}; |
+ |
+TEST_F(BlobTransportHostTest, TestBadIPCs) { |
+ std::vector<DataElement> descriptions; |
+ |
+ // Test reusing same blob uuid. |
+ AddMemoryItem(10, &descriptions); |
+ AddBlobItem(&descriptions); |
+ AddMemoryItem(300, &descriptions); |
+ std::unique_ptr<BlobDataHandle> handle1; |
+ EXPECT_EQ(BlobStatus::PENDING_TRANSPORT, |
+ BuildBlobAsync(kBlobUUID, descriptions, &handle1)); |
+ EXPECT_TRUE(host_.IsBeingBuilt(kBlobUUID)); |
+ EXPECT_TRUE(request_called_); |
+ host_.CancelBuildingBlob(kBlobUUID, BlobStatus::ERR_REFERENCED_BLOB_BROKEN, |
+ &context_); |
+ handle1.reset(); |
+ EXPECT_FALSE(context_.GetBlobDataFromUUID(kBlobUUID).get()); |
+ |
+ // Test empty responses. |
+ EXPECT_EQ(BlobStatus::PENDING_TRANSPORT, |
+ BuildBlobAsync(kBlobUUID, descriptions, &handle1)); |
+ std::vector<BlobItemBytesResponse> responses; |
+ host_.OnMemoryResponses(kBlobUUID, responses, &context_); |
+ EXPECT_EQ(BlobStatus::ERR_INVALID_CONSTRUCTION_ARGUMENTS, |
+ handle1->GetBlobStatus()); |
+ handle1.reset(); |
+ |
+ // Test response problems below here. |
+ descriptions.clear(); |
+ AddMemoryItem(2, &descriptions); |
+ AddBlobItem(&descriptions); |
+ AddMemoryItem(2, &descriptions); |
+ EXPECT_EQ(BlobStatus::PENDING_TRANSPORT, |
+ BuildBlobAsync(kBlobUUID, descriptions, &handle1)); |
+ |
+ // Invalid request number. |
+ BlobItemBytesResponse response1(3); |
+ PopulateBytes(response1.allocate_mutable_data(2), 2); |
+ responses = {response1}; |
+ host_.OnMemoryResponses(kBlobUUID, responses, &context_); |
+ EXPECT_EQ(BlobStatus::ERR_INVALID_CONSTRUCTION_ARGUMENTS, |
+ handle1->GetBlobStatus()); |
+ EXPECT_TRUE(context_.GetBlobDataFromUUID(kBlobUUID)->IsBroken()); |
+ handle1.reset(); |
+ base::RunLoop().RunUntilIdle(); |
+ |
+ // Duplicate request number responses. |
+ EXPECT_EQ(BlobStatus::PENDING_TRANSPORT, |
+ BuildBlobAsync(kBlobUUID, descriptions, &handle1)); |
+ response1.request_number = 0; |
+ BlobItemBytesResponse response2(0); |
+ PopulateBytes(response2.allocate_mutable_data(2), 2); |
+ responses = {response1, response2}; |
+ host_.OnMemoryResponses(kBlobUUID, responses, &context_); |
+ EXPECT_EQ(BlobStatus::ERR_INVALID_CONSTRUCTION_ARGUMENTS, |
+ handle1->GetBlobStatus()); |
+ EXPECT_TRUE(context_.GetBlobDataFromUUID(kBlobUUID)->IsBroken()); |
+ handle1.reset(); |
+ base::RunLoop().RunUntilIdle(); |
+}; |
+ |
+TEST_F(BlobTransportHostTest, WaitOnReferencedBlob) { |
+ const std::string& kBlob1 = "blob1"; |
+ const std::string& kBlob2 = "blob2"; |
+ const std::string& kBlob3 = "blob3"; |
+ |
+ std::vector<DataElement> descriptions; |
+ AddMemoryItem(2, &descriptions); |
+ |
+ // Register blobs. |
+ std::unique_ptr<BlobDataHandle> handle1; |
+ |
+ std::unique_ptr<BlobDataHandle> handle2; |
+ std::unique_ptr<BlobDataHandle> handle3; |
+ EXPECT_EQ(BlobStatus::PENDING_TRANSPORT, |
+ BuildBlobAsync(kBlob1, descriptions, &handle1)); |
+ EXPECT_EQ(BlobStatus::PENDING_TRANSPORT, |
+ BuildBlobAsync(kBlob2, descriptions, &handle2)); |
+ EXPECT_TRUE(request_called_); |
+ request_called_ = false; |
+ |
+ // Finish the third one, with a reference to the first and second blob. |
+ DataElement element; |
+ element.SetToBlob(kBlob1); |
+ descriptions.push_back(element); |
+ element.SetToBlob(kBlob2); |
+ descriptions.push_back(element); |
+ |
+ EXPECT_EQ(BlobStatus::PENDING_TRANSPORT, |
+ BuildBlobAsync(kBlob3, descriptions, &handle3)); |
+ EXPECT_TRUE(request_called_); |
+ request_called_ = false; |
+ |
+ // Finish the third, but we should still be 'building' it. |
+ BlobItemBytesResponse response1(0); |
+ PopulateBytes(response1.allocate_mutable_data(2), 2); |
+ std::vector<BlobItemBytesResponse> responses = {response1}; |
+ host_.OnMemoryResponses(kBlob3, responses, &context_); |
+ EXPECT_EQ(BlobStatus::PENDING_INTERNALS, handle3->GetBlobStatus()); |
+ EXPECT_FALSE(request_called_); |
+ EXPECT_FALSE(host_.IsBeingBuilt(kBlob3)); |
+ EXPECT_TRUE(IsBeingBuiltInContext(kBlob3)); |
+ |
+ // Finish the first. |
+ descriptions.clear(); |
+ AddShortcutMemoryItem(2, &descriptions); |
+ host_.OnMemoryResponses(kBlob1, responses, &context_); |
+ EXPECT_EQ(BlobStatus::DONE, handle1->GetBlobStatus()); |
+ EXPECT_FALSE(request_called_); |
+ EXPECT_FALSE(host_.IsBeingBuilt(kBlob1)); |
+ EXPECT_FALSE(IsBeingBuiltInContext(kBlob1)); |
+ EXPECT_TRUE(context_.GetBlobDataFromUUID(kBlob1)); |
+ |
+ // Run the message loop so we propogate the construction complete callbacks. |
+ base::RunLoop().RunUntilIdle(); |
+ // Verify we're not done. |
+ EXPECT_TRUE(IsBeingBuiltInContext(kBlob3)); |
+ |
+ // Finish the second. |
+ host_.OnMemoryResponses(kBlob2, responses, &context_); |
+ EXPECT_EQ(BlobStatus::DONE, handle2->GetBlobStatus()); |
+ EXPECT_FALSE(request_called_); |
+ EXPECT_FALSE(host_.IsBeingBuilt(kBlob2)); |
+ EXPECT_FALSE(IsBeingBuiltInContext(kBlob2)); |
+ EXPECT_TRUE(context_.GetBlobDataFromUUID(kBlob2)); |
+ |
+ // Run the message loop so we propogate the construction complete callbacks. |
+ base::RunLoop().RunUntilIdle(); |
+ // Finally, we should be finished with third blob. |
+ EXPECT_FALSE(host_.IsBeingBuilt(kBlob3)); |
+ EXPECT_FALSE(IsBeingBuiltInContext(kBlob3)); |
+ EXPECT_TRUE(context_.GetBlobDataFromUUID(kBlob3)); |
+}; |
+ |
+} // namespace storage |