Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(525)

Unified Diff: content/browser/blob_storage/blob_dispatcher_host_unittest.cc

Issue 1234813004: [BlobAsync] Asynchronous Blob Construction Final Patch (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@blob-protocol-change
Patch Set: comments/fixes Created 4 years, 11 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: content/browser/blob_storage/blob_dispatcher_host_unittest.cc
diff --git a/content/browser/blob_storage/blob_dispatcher_host_unittest.cc b/content/browser/blob_storage/blob_dispatcher_host_unittest.cc
new file mode 100644
index 0000000000000000000000000000000000000000..648bb18a7ec707e624bc05cbe62c2cee2692556f
--- /dev/null
+++ b/content/browser/blob_storage/blob_dispatcher_host_unittest.cc
@@ -0,0 +1,472 @@
+// 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/blob_storage/blob_dispatcher_host.h"
+
+#include <vector>
+
+#include "base/command_line.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/run_loop.h"
+#include "base/tuple.h"
+#include "content/browser/fileapi/chrome_blob_storage_context.h"
+#include "content/common/fileapi/webblob_messages.h"
+#include "content/public/common/content_switches.h"
+#include "content/public/test/test_browser_context.h"
+#include "content/public/test/test_browser_thread_bundle.h"
+#include "ipc/ipc_sender.h"
+#include "ipc/ipc_test_sink.h"
+#include "ipc/message_filter.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/common/blob_storage/blob_item_bytes_request.h"
+#include "storage/common/blob_storage/blob_item_bytes_response.h"
+#include "storage/common/data_element.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using storage::BlobDataBuilder;
+using storage::BlobDataHandle;
+using storage::BlobItemBytesRequest;
+using storage::BlobItemBytesResponse;
+using storage::BlobStorageContext;
+using storage::BlobTransportResult;
+using storage::DataElement;
+using RequestMemoryCallback =
+ storage::BlobAsyncBuilderHost::RequestMemoryCallback;
+
+namespace content {
+namespace {
+
+const char kContentType[] = "text/plain";
+const char kContentDisposition[] = "content_disposition";
+const char kData[] = "data!!";
+const size_t kDataSize = 6;
+
+template <typename T>
+void SetPointerValue(T* pointer, T value) {
+ *pointer = value;
+}
+
+class TestableBlobDispatcherHost : public BlobDispatcherHost {
+ public:
+ TestableBlobDispatcherHost(ChromeBlobStorageContext* blob_storage_context,
+ IPC::TestSink* sink)
+ : BlobDispatcherHost(blob_storage_context), sink_(sink) {}
+
+ bool Send(IPC::Message* message) override { return sink_->Send(message); }
+
+ void ShutdownForBadMessage() override { shutdown_for_bad_message_ = true; }
+
+ bool shutdown_for_bad_message_ = false;
+ IPC::TestSink* sink_;
+
+ protected:
+ ~TestableBlobDispatcherHost() override {}
+
+ private:
+ friend class base::RefCountedThreadSafe<TestableBlobDispatcherHost>;
+};
+
+} // namespace
+
+class BlobDispatcherHostTest : public testing::Test {
+ protected:
+ BlobDispatcherHostTest()
+ : browser_thread_bundle_(),
+ browser_context_(),
+ chrome_blob_storage_context_(
+ ChromeBlobStorageContext::GetFor(&browser_context_)) {
+ host_ =
+ new TestableBlobDispatcherHost(chrome_blob_storage_context_, &sink_);
+ }
+ ~BlobDispatcherHostTest() override {}
+
+ void SetUp() override {
+ base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
+ if (!command_line->HasSwitch(switches::kDisableKillAfterBadIPC)) {
+ command_line->AppendSwitch(switches::kDisableKillAfterBadIPC);
+ }
+ // We run the run loop to initialize the chrome blob storage context.
+ base::RunLoop().RunUntilIdle();
+ context_ = chrome_blob_storage_context_->context();
+ DCHECK(context_);
+ }
+
+ void ExpectBlobNotExist(const std::string& id) {
+ EXPECT_FALSE(context_->registry().HasEntry(id));
+ EXPECT_FALSE(host_->IsInUseInHost(id));
+ EXPECT_FALSE(host_->IsBeingBuiltInHost(id));
+ }
+
+ void AsyncShortcutBlobTransfer(const std::string& id) {
+ sink_.ClearMessages();
+ ExpectBlobNotExist(id);
+ host_->OnRegisterBlobUUID(id, std::string(kContentType),
+ std::string(kContentDisposition));
+ EXPECT_FALSE(host_->shutdown_for_bad_message_);
+ DataElement element;
+ element.SetToBytes(kData, kDataSize);
+ std::vector<DataElement> elements = {element};
+ host_->OnStartBuildingBlob(id, elements);
+ EXPECT_FALSE(host_->shutdown_for_bad_message_);
+ ExpectDone(id);
+ sink_.ClearMessages();
+ }
+
+ void AsyncBlobTransfer(const std::string& id) {
+ sink_.ClearMessages();
+ ExpectBlobNotExist(id);
+ host_->OnRegisterBlobUUID(id, std::string(kContentType),
+ std::string(kContentDisposition));
+ EXPECT_FALSE(host_->shutdown_for_bad_message_);
+ DataElement element;
+ element.SetToBytesDescription(kDataSize);
+ std::vector<DataElement> elements = {element};
+ host_->OnStartBuildingBlob(id, elements);
+ EXPECT_FALSE(host_->shutdown_for_bad_message_);
+
+ // Expect our request.
+ std::vector<BlobItemBytesRequest> expected_requests = {
+ BlobItemBytesRequest::CreateIPCRequest(0, 0, 0, kDataSize)};
+ ExpectRequest(id, expected_requests);
+ sink_.ClearMessages();
+
+ // Send results;
+ BlobItemBytesResponse response(0);
+ std::memcpy(response.allocate_mutable_data(kDataSize), kData, kDataSize);
+ std::vector<BlobItemBytesResponse> responses = {response};
+ host_->OnMemoryItemResponse(id, responses);
+ ExpectDone(id);
+ sink_.ClearMessages();
+ }
+
+ void ExpectAndResetBadMessage() {
+ EXPECT_TRUE(host_->shutdown_for_bad_message_);
+ host_->shutdown_for_bad_message_ = false;
+ }
+
+ void ExpectHandleEqualsData(BlobDataHandle* handle,
+ const std::vector<DataElement>& data) {
+ scoped_ptr<storage::BlobDataSnapshot> snapshot = handle->CreateSnapshot();
+ EXPECT_FALSE(handle->IsBeingBuilt());
+ for (size_t i = 0; i < data.size(); i++) {
+ const DataElement& expected = data[i];
+ const DataElement& actual = snapshot->items()[i]->data_element();
+ EXPECT_EQ(expected, actual);
+ }
+ EXPECT_EQ(data.size(), snapshot->items().size());
+ }
+
+ void ExpectRequest(
+ const std::string& expected_uuid,
+ const std::vector<BlobItemBytesRequest>& expected_requests) {
+ EXPECT_FALSE(
+ sink_.GetFirstMessageMatching(BlobStorageMsg_DoneBuildingBlob::ID));
+ EXPECT_FALSE(
+ sink_.GetFirstMessageMatching(BlobStorageMsg_CancelBuildingBlob::ID));
+ const IPC::Message* message =
+ sink_.GetUniqueMessageMatching(BlobStorageMsg_RequestMemoryItem::ID);
+ ASSERT_TRUE(message);
+ base::Tuple<std::string, std::vector<storage::BlobItemBytesRequest>,
+ std::vector<base::SharedMemoryHandle>,
+ std::vector<IPC::PlatformFileForTransit>>
+ args;
+ BlobStorageMsg_RequestMemoryItem::Read(message, &args);
+ EXPECT_EQ(expected_uuid, base::get<0>(args));
+ std::vector<BlobItemBytesRequest> requests = base::get<1>(args);
+ ASSERT_EQ(requests.size(), expected_requests.size());
+ for (size_t i = 0; i < expected_requests.size(); ++i) {
+ EXPECT_EQ(expected_requests[i], requests[i]);
+ }
+ }
+
+ void ExpectCancel(const std::string& expected_uuid,
+ storage::IPCBlobCreationCancelCode code) {
+ EXPECT_FALSE(
+ sink_.GetFirstMessageMatching(BlobStorageMsg_RequestMemoryItem::ID));
+ EXPECT_FALSE(
+ sink_.GetFirstMessageMatching(BlobStorageMsg_DoneBuildingBlob::ID));
+ const IPC::Message* message =
+ sink_.GetUniqueMessageMatching(BlobStorageMsg_CancelBuildingBlob::ID);
+ ASSERT_TRUE(message);
+ base::Tuple<std::string, storage::IPCBlobCreationCancelCode> args;
+ BlobStorageMsg_CancelBuildingBlob::Read(message, &args);
+ EXPECT_EQ(expected_uuid, base::get<0>(args));
+ EXPECT_EQ(code, base::get<1>(args));
+ }
+
+ void ExpectDone(const std::string& expected_uuid) {
+ EXPECT_FALSE(
+ sink_.GetFirstMessageMatching(BlobStorageMsg_RequestMemoryItem::ID));
+ EXPECT_FALSE(
+ sink_.GetFirstMessageMatching(BlobStorageMsg_CancelBuildingBlob::ID));
+ const IPC::Message* message =
+ sink_.GetUniqueMessageMatching(BlobStorageMsg_DoneBuildingBlob::ID);
+ base::Tuple<std::string> args;
+ BlobStorageMsg_DoneBuildingBlob::Read(message, &args);
+ EXPECT_EQ(expected_uuid, base::get<0>(args));
+ }
+
+ IPC::TestSink sink_;
+ TestBrowserThreadBundle browser_thread_bundle_;
+ TestBrowserContext browser_context_;
+ ChromeBlobStorageContext* chrome_blob_storage_context_;
+ BlobStorageContext* context_ = nullptr;
+ scoped_refptr<TestableBlobDispatcherHost> host_;
+};
+
+TEST_F(BlobDispatcherHostTest, EmptyUUIDs) {
+ host_->OnRegisterBlobUUID("", "", "");
+ ExpectAndResetBadMessage();
+ host_->OnStartBuildingBlob("", std::vector<DataElement>());
+ ExpectAndResetBadMessage();
+ host_->OnMemoryItemResponse("", std::vector<BlobItemBytesResponse>());
+ ExpectAndResetBadMessage();
+ host_->OnCancelBuildingBlob("", storage::IPCBlobCreationCancelCode::UNKNOWN);
+ ExpectAndResetBadMessage();
+}
+
+TEST_F(BlobDispatcherHostTest, Shortcut) {
+ const std::string kId = "uuid1";
+ AsyncShortcutBlobTransfer(kId);
+ EXPECT_TRUE(context_->registry().HasEntry(kId));
+ scoped_ptr<BlobDataHandle> handle = context_->GetBlobDataFromUUID(kId);
+ EXPECT_TRUE(handle);
+
+ DataElement expected;
+ expected.SetToBytes(kData, kDataSize);
+ std::vector<DataElement> elements = {expected};
+ ExpectHandleEqualsData(handle.get(), elements);
+}
+
+TEST_F(BlobDispatcherHostTest, RegularTransfer) {
+ const std::string kId = "uuid1";
+ AsyncBlobTransfer(kId);
+ EXPECT_TRUE(context_->registry().HasEntry(kId));
+ scoped_ptr<BlobDataHandle> handle = context_->GetBlobDataFromUUID(kId);
+ EXPECT_TRUE(handle);
+
+ DataElement expected;
+ expected.SetToBytes(kData, kDataSize);
+ std::vector<DataElement> elements = {expected};
+ ExpectHandleEqualsData(handle.get(), elements);
+}
+
+TEST_F(BlobDispatcherHostTest, OnCancelBuildingBlob) {
+ const std::string kId("id");
+ host_->OnCancelBuildingBlob(kId, storage::IPCBlobCreationCancelCode::UNKNOWN);
+ ExpectAndResetBadMessage();
+
+ // Start building blob.
+ host_->OnRegisterBlobUUID(kId, std::string(kContentType),
+ std::string(kContentDisposition));
+ EXPECT_FALSE(host_->shutdown_for_bad_message_);
+ DataElement element;
+ element.SetToBytesDescription(kDataSize);
+ std::vector<DataElement> elements = {element};
+ host_->OnStartBuildingBlob(kId, elements);
+ // It should have requested memory here.
+ EXPECT_FALSE(host_->shutdown_for_bad_message_);
+ sink_.ClearMessages();
+
+ // Cancel in middle of construction.
+ host_->OnCancelBuildingBlob(kId, storage::IPCBlobCreationCancelCode::UNKNOWN);
+ EXPECT_TRUE(context_->registry().HasEntry(kId));
+ EXPECT_TRUE(host_->IsInUseInHost(kId));
+ EXPECT_FALSE(host_->IsBeingBuiltInHost(kId));
+ // Check that's it's broken.
+ scoped_ptr<BlobDataHandle> handle = context_->GetBlobDataFromUUID(kId);
+ EXPECT_TRUE(handle->IsBroken());
+ handle.reset();
+ base::RunLoop().RunUntilIdle();
+
+ // Get rid of it in the host.
+ host_->OnDecrementBlobRefCount(kId);
+ ExpectBlobNotExist(kId);
+
+ // Create blob again to verify we don't have any old construction state lying
+ // around.
+ AsyncBlobTransfer(kId);
+
+ // Check data.
+ handle = context_->GetBlobDataFromUUID(kId);
+ EXPECT_TRUE(handle);
+ DataElement expected;
+ expected.SetToBytes(kData, kDataSize);
+ std::vector<DataElement> expecteds = {expected};
+ ExpectHandleEqualsData(handle.get(), expecteds);
+
+ // Verify we can't cancel after the fact.
+ host_->OnCancelBuildingBlob(kId, storage::IPCBlobCreationCancelCode::UNKNOWN);
+ ExpectAndResetBadMessage();
+}
+
+TEST_F(BlobDispatcherHostTest, BlobDataWithHostDeletion) {
+ // Build up a basic blob.
+ const std::string kId("id");
+ AsyncShortcutBlobTransfer(kId);
+ scoped_ptr<BlobDataHandle> handle = context_->GetBlobDataFromUUID(kId);
+ EXPECT_TRUE(handle);
+
+ // Kill the host.
+ host_ = nullptr;
+ base::RunLoop().RunUntilIdle();
+ // Should still be there due to the handle.
+ scoped_ptr<BlobDataHandle> another_handle =
+ context_->GetBlobDataFromUUID(kId);
+ EXPECT_TRUE(another_handle);
+
+ // Should disappear after dropping both handles.
+ handle.reset();
+ another_handle.reset();
+ base::RunLoop().RunUntilIdle();
+
+ handle = context_->GetBlobDataFromUUID(kId);
+ EXPECT_FALSE(handle);
+}
+
+TEST_F(BlobDispatcherHostTest, BlobReferenceWhileConstructing) {
+ const std::string kId("id");
+
+ // Start building blob.
+ host_->OnRegisterBlobUUID(kId, std::string(kContentType),
+ std::string(kContentDisposition));
+ EXPECT_FALSE(host_->shutdown_for_bad_message_);
+
+ // Grab the handle.
+ scoped_ptr<BlobDataHandle> blob_data_handle =
+ context_->GetBlobDataFromUUID(kId);
+ EXPECT_TRUE(blob_data_handle);
+ EXPECT_TRUE(blob_data_handle->IsBeingBuilt());
+ bool built = false;
+ blob_data_handle->RunOnConstructionComplete(
+ base::Bind(&SetPointerValue<bool>, &built));
+
+ // Continue building.
+ DataElement element;
+ element.SetToBytesDescription(kDataSize);
+ std::vector<DataElement> elements = {element};
+ host_->OnStartBuildingBlob(kId, elements);
+ EXPECT_FALSE(host_->shutdown_for_bad_message_);
+ sink_.ClearMessages();
+
+ // Send data.
+ BlobItemBytesResponse response(0);
+ std::memcpy(response.allocate_mutable_data(kDataSize), kData, kDataSize);
+ std::vector<BlobItemBytesResponse> responses = {response};
+ sink_.ClearMessages();
+ host_->OnMemoryItemResponse(kId, responses);
+
+ ExpectDone(kId);
+ base::RunLoop().RunUntilIdle();
+ EXPECT_TRUE(built);
+}
+
+TEST_F(BlobDispatcherHostTest, BlobReferenceWhileShortcutConstructing) {
+ const std::string kId("id");
+
+ // Start building blob.
+ host_->OnRegisterBlobUUID(kId, std::string(kContentType),
+ std::string(kContentDisposition));
+ EXPECT_FALSE(host_->shutdown_for_bad_message_);
+
+ // Grab the handle.
+ scoped_ptr<BlobDataHandle> blob_data_handle =
+ context_->GetBlobDataFromUUID(kId);
+ EXPECT_TRUE(blob_data_handle);
+ EXPECT_TRUE(blob_data_handle->IsBeingBuilt());
+ bool built = false;
+ blob_data_handle->RunOnConstructionComplete(
+ base::Bind(&SetPointerValue<bool>, &built));
+
+ // Continue building.
+ DataElement element;
+ element.SetToBytes(kData, kDataSize);
+ std::vector<DataElement> elements = {element};
+ host_->OnStartBuildingBlob(kId, elements);
+ EXPECT_FALSE(host_->shutdown_for_bad_message_);
+ ExpectDone(kId);
+ base::RunLoop().RunUntilIdle();
+ EXPECT_TRUE(built);
+}
+
+TEST_F(BlobDispatcherHostTest, BlobReferenceWhileConstructingCancelled) {
+ const std::string kId("id");
+
+ // Start building blob.
+ host_->OnRegisterBlobUUID(kId, std::string(kContentType),
+ std::string(kContentDisposition));
+ EXPECT_FALSE(host_->shutdown_for_bad_message_);
+
+ // Grab the handle.
+ scoped_ptr<BlobDataHandle> blob_data_handle =
+ context_->GetBlobDataFromUUID(kId);
+ EXPECT_TRUE(blob_data_handle);
+ EXPECT_TRUE(blob_data_handle->IsBeingBuilt());
+ bool built = true;
+ blob_data_handle->RunOnConstructionComplete(
+ base::Bind(&SetPointerValue<bool>, &built));
+
+ // Cancel in middle of construction.
+ host_->OnCancelBuildingBlob(kId, storage::IPCBlobCreationCancelCode::UNKNOWN);
+ base::RunLoop().RunUntilIdle();
+ EXPECT_TRUE(context_->registry().HasEntry(kId));
+ EXPECT_FALSE(host_->shutdown_for_bad_message_);
+ EXPECT_TRUE(host_->IsInUseInHost(kId));
+ EXPECT_FALSE(host_->IsBeingBuiltInHost(kId));
+ EXPECT_TRUE(blob_data_handle->IsBroken());
+ EXPECT_FALSE(built);
+ built = true;
+ blob_data_handle->RunOnConstructionComplete(
+ base::Bind(&SetPointerValue<bool>, &built));
+ EXPECT_FALSE(built);
+
+ // Remove it.
+ blob_data_handle.reset();
+ base::RunLoop().RunUntilIdle();
+ EXPECT_TRUE(context_->registry().HasEntry(kId));
+ host_->OnDecrementBlobRefCount(kId);
+ ExpectBlobNotExist(kId);
+}
+
+TEST_F(BlobDispatcherHostTest, BlobDestructionEdgeCases) {
+ const std::string kId("id");
+
+ // Start building blob.
+ host_->OnRegisterBlobUUID(kId, std::string(kContentType),
+ std::string(kContentDisposition));
+ EXPECT_FALSE(host_->shutdown_for_bad_message_);
+
+ // Grab the handle.
+ scoped_ptr<BlobDataHandle> blob_data_handle =
+ context_->GetBlobDataFromUUID(kId);
+ EXPECT_TRUE(blob_data_handle->IsBeingBuilt());
+ bool built = true;
+ blob_data_handle->RunOnConstructionComplete(
+ base::Bind(&SetPointerValue<bool>, &built));
+
+ // Get rid of host, which was doing the constructing.
+ host_ = nullptr;
+
+ EXPECT_FALSE(blob_data_handle->IsBeingBuilt());
+ base::RunLoop().RunUntilIdle();
+
+ EXPECT_FALSE(built);
+
+ // Should still be there due to the handle.
+ scoped_ptr<BlobDataHandle> another_handle =
+ context_->GetBlobDataFromUUID(kId);
+ EXPECT_TRUE(another_handle);
+
+ // Should disappear after dropping both handles.
+ blob_data_handle.reset();
+ another_handle.reset();
+ base::RunLoop().RunUntilIdle();
+
+ EXPECT_FALSE(context_->registry().HasEntry(kId));
+}
+
+} // namespace contet

Powered by Google App Engine
This is Rietveld 408576698