summaryrefslogtreecommitdiff
path: root/chromium/extensions/browser/mojo/stash_backend_unittest.cc
diff options
context:
space:
mode:
Diffstat (limited to 'chromium/extensions/browser/mojo/stash_backend_unittest.cc')
-rw-r--r--chromium/extensions/browser/mojo/stash_backend_unittest.cc301
1 files changed, 301 insertions, 0 deletions
diff --git a/chromium/extensions/browser/mojo/stash_backend_unittest.cc b/chromium/extensions/browser/mojo/stash_backend_unittest.cc
new file mode 100644
index 00000000000..cf69f98c43b
--- /dev/null
+++ b/chromium/extensions/browser/mojo/stash_backend_unittest.cc
@@ -0,0 +1,301 @@
+// Copyright 2014 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 "extensions/browser/mojo/stash_backend.h"
+
+#include <stdint.h>
+
+#include <utility>
+
+#include "base/bind.h"
+#include "base/macros.h"
+#include "base/message_loop/message_loop.h"
+#include "base/run_loop.h"
+#include "mojo/shell/public/interfaces/interface_provider.mojom.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace extensions {
+namespace {
+
+// Create a data pipe, write some data to the producer handle and return the
+// consumer handle.
+mojo::ScopedHandle CreateReadableHandle() {
+ mojo::ScopedDataPipeConsumerHandle consumer_handle;
+ mojo::ScopedDataPipeProducerHandle producer_handle;
+ MojoCreateDataPipeOptions options = {
+ sizeof(options), MOJO_CREATE_DATA_PIPE_OPTIONS_FLAG_NONE, 1, 1,
+ };
+ MojoResult result =
+ mojo::CreateDataPipe(&options, &producer_handle, &consumer_handle);
+ EXPECT_EQ(MOJO_RESULT_OK, result);
+ uint32_t num_bytes = 1;
+ result = mojo::WriteDataRaw(producer_handle.get(), "a", &num_bytes,
+ MOJO_WRITE_DATA_FLAG_NONE);
+ EXPECT_EQ(MOJO_RESULT_OK, result);
+ EXPECT_EQ(1u, num_bytes);
+ return mojo::ScopedHandle::From(std::move(consumer_handle));
+}
+
+} // namespace
+
+class StashServiceTest : public testing::Test {
+ public:
+ enum Event {
+ EVENT_NONE,
+ EVENT_STASH_RETRIEVED,
+ EVENT_HANDLE_READY,
+ };
+
+ StashServiceTest() {}
+
+ void SetUp() override {
+ expecting_error_ = false;
+ expected_event_ = EVENT_NONE;
+ stash_backend_.reset(new StashBackend(base::Bind(
+ &StashServiceTest::OnHandleReadyToRead, base::Unretained(this))));
+ stash_backend_->BindToRequest(mojo::GetProxy(&stash_service_));
+ stash_service_.set_connection_error_handler(base::Bind(&OnConnectionError));
+ handles_ready_ = 0;
+ }
+
+ static void OnConnectionError() { FAIL() << "Unexpected connection error"; }
+
+ mojo::Array<StashedObjectPtr> RetrieveStash() {
+ mojo::Array<StashedObjectPtr> stash;
+ stash_service_->RetrieveStash(base::Bind(
+ &StashServiceTest::StashRetrieved, base::Unretained(this), &stash));
+ WaitForEvent(EVENT_STASH_RETRIEVED);
+ return stash;
+ }
+
+ void StashRetrieved(mojo::Array<StashedObjectPtr>* output,
+ mojo::Array<StashedObjectPtr> stash) {
+ *output = std::move(stash);
+ EventReceived(EVENT_STASH_RETRIEVED);
+ }
+
+ void WaitForEvent(Event event) {
+ expected_event_ = event;
+ base::RunLoop run_loop;
+ stop_run_loop_ = run_loop.QuitClosure();
+ run_loop.Run();
+ }
+
+ void EventReceived(Event event) {
+ if (event == expected_event_ && !stop_run_loop_.is_null())
+ stop_run_loop_.Run();
+ }
+
+ void OnHandleReadyToRead() {
+ handles_ready_++;
+ EventReceived(EVENT_HANDLE_READY);
+ }
+
+ protected:
+ base::MessageLoop message_loop_;
+ base::Closure stop_run_loop_;
+ scoped_ptr<StashBackend> stash_backend_;
+ Event expected_event_;
+ bool expecting_error_;
+ mojo::InterfacePtr<StashService> stash_service_;
+ int handles_ready_;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(StashServiceTest);
+};
+
+// Test that adding stashed objects in multiple calls can all be retrieved by a
+// Retrieve call.
+TEST_F(StashServiceTest, AddTwiceAndRetrieve) {
+ mojo::Array<StashedObjectPtr> stashed_objects;
+ StashedObjectPtr stashed_object(StashedObject::New());
+ stashed_object->id = "test type";
+ stashed_object->data.push_back(1);
+ stashed_object->stashed_handles = mojo::Array<mojo::ScopedHandle>();
+ stashed_objects.push_back(std::move(stashed_object));
+ stash_service_->AddToStash(std::move(stashed_objects));
+ stashed_object = StashedObject::New();
+ stashed_object->id = "test type2";
+ stashed_object->data.push_back(2);
+ stashed_object->data.push_back(3);
+ stashed_object->stashed_handles = mojo::Array<mojo::ScopedHandle>();
+ stashed_objects.push_back(std::move(stashed_object));
+ stash_service_->AddToStash(std::move(stashed_objects));
+ stashed_objects = RetrieveStash();
+ ASSERT_EQ(2u, stashed_objects.size());
+ EXPECT_EQ("test type", stashed_objects[0]->id);
+ EXPECT_EQ(0u, stashed_objects[0]->stashed_handles.size());
+ EXPECT_EQ(1u, stashed_objects[0]->data.size());
+ EXPECT_EQ(1, stashed_objects[0]->data[0]);
+ EXPECT_EQ("test type2", stashed_objects[1]->id);
+ EXPECT_EQ(0u, stashed_objects[1]->stashed_handles.size());
+ EXPECT_EQ(2u, stashed_objects[1]->data.size());
+ EXPECT_EQ(2, stashed_objects[1]->data[0]);
+ EXPECT_EQ(3, stashed_objects[1]->data[1]);
+}
+
+// Test that handles survive a round-trip through the stash.
+TEST_F(StashServiceTest, StashAndRetrieveHandles) {
+ mojo::Array<StashedObjectPtr> stashed_objects;
+ StashedObjectPtr stashed_object(StashedObject::New());
+ stashed_object->id = "test type";
+ stashed_object->data.push_back(1);
+
+ mojo::ScopedDataPipeConsumerHandle consumer;
+ mojo::ScopedDataPipeProducerHandle producer;
+ MojoCreateDataPipeOptions options = {
+ sizeof(options), MOJO_CREATE_DATA_PIPE_OPTIONS_FLAG_NONE, 1, 1,
+ };
+ mojo::CreateDataPipe(&options, &producer, &consumer);
+ uint32_t num_bytes = 1;
+ MojoResult result = mojo::WriteDataRaw(
+ producer.get(), "1", &num_bytes, MOJO_WRITE_DATA_FLAG_ALL_OR_NONE);
+ ASSERT_EQ(MOJO_RESULT_OK, result);
+ ASSERT_EQ(1u, num_bytes);
+
+ stashed_object->stashed_handles.push_back(
+ mojo::ScopedHandle::From(std::move(producer)));
+ stashed_object->stashed_handles.push_back(
+ mojo::ScopedHandle::From(std::move(consumer)));
+ stashed_objects.push_back(std::move(stashed_object));
+ stash_service_->AddToStash(std::move(stashed_objects));
+ stashed_objects = RetrieveStash();
+ ASSERT_EQ(1u, stashed_objects.size());
+ EXPECT_EQ("test type", stashed_objects[0]->id);
+ ASSERT_EQ(2u, stashed_objects[0]->stashed_handles.size());
+
+ consumer = mojo::ScopedDataPipeConsumerHandle::From(
+ std::move(stashed_objects[0]->stashed_handles[1]));
+ result = mojo::Wait(
+ consumer.get(), MOJO_HANDLE_SIGNAL_READABLE, MOJO_DEADLINE_INDEFINITE,
+ nullptr);
+ ASSERT_EQ(MOJO_RESULT_OK, result);
+ char data = '\0';
+ result = mojo::ReadDataRaw(
+ consumer.get(), &data, &num_bytes, MOJO_READ_DATA_FLAG_ALL_OR_NONE);
+ ASSERT_EQ(MOJO_RESULT_OK, result);
+ ASSERT_EQ(1u, num_bytes);
+ EXPECT_EQ('1', data);
+}
+
+TEST_F(StashServiceTest, RetrieveWithoutStashing) {
+ mojo::Array<StashedObjectPtr> stashed_objects = RetrieveStash();
+ ASSERT_TRUE(!stashed_objects.is_null());
+ EXPECT_EQ(0u, stashed_objects.size());
+}
+
+TEST_F(StashServiceTest, NotifyOnReadableHandle) {
+ mojo::Array<StashedObjectPtr> stash_entries;
+ StashedObjectPtr stashed_object(StashedObject::New());
+ stashed_object->id = "test type";
+ stashed_object->data.push_back(0);
+ stashed_object->monitor_handles = true;
+ mojo::shell::mojom::InterfaceProviderPtr service_provider;
+
+ // Stash the ServiceProvider request. When we make a call on
+ // |service_provider|, the stashed handle will become readable.
+ stashed_object->stashed_handles.push_back(mojo::ScopedHandle::From(
+ mojo::GetProxy(&service_provider).PassMessagePipe()));
+
+ stash_entries.push_back(std::move(stashed_object));
+ stash_service_->AddToStash(std::move(stash_entries));
+
+ mojo::MessagePipe pipe;
+ service_provider->GetInterface("", std::move(pipe.handle0));
+
+ WaitForEvent(EVENT_HANDLE_READY);
+ EXPECT_EQ(1, handles_ready_);
+}
+
+TEST_F(StashServiceTest, NotifyOnReadableDataPipeHandle) {
+ mojo::Array<StashedObjectPtr> stash_entries;
+ StashedObjectPtr stashed_object(StashedObject::New());
+ stashed_object->id = "test type";
+ stashed_object->monitor_handles = true;
+
+ MojoCreateDataPipeOptions options = {
+ sizeof(options), MOJO_CREATE_DATA_PIPE_OPTIONS_FLAG_NONE, 1, 1,
+ };
+ mojo::ScopedDataPipeConsumerHandle consumer_handle;
+ mojo::ScopedDataPipeProducerHandle producer_handle;
+ uint32_t num_bytes = 1;
+ MojoResult result =
+ mojo::CreateDataPipe(&options, &producer_handle, &consumer_handle);
+ ASSERT_EQ(MOJO_RESULT_OK, result);
+ result = mojo::WriteDataRaw(producer_handle.get(), "a", &num_bytes,
+ MOJO_WRITE_DATA_FLAG_NONE);
+ ASSERT_EQ(MOJO_RESULT_OK, result);
+ ASSERT_EQ(1u, num_bytes);
+ stashed_object->stashed_handles.push_back(
+ mojo::ScopedHandle::From(std::move(producer_handle)));
+ stashed_object->stashed_handles.push_back(
+ mojo::ScopedHandle::From(std::move(consumer_handle)));
+ stashed_object->data.push_back(1);
+
+ stash_entries.push_back(std::move(stashed_object));
+ stash_service_->AddToStash(std::move(stash_entries));
+ WaitForEvent(EVENT_HANDLE_READY);
+ EXPECT_EQ(1, handles_ready_);
+}
+
+TEST_F(StashServiceTest, NotifyOncePerStashOnReadableHandles) {
+ mojo::Array<StashedObjectPtr> stash_entries;
+ StashedObjectPtr stashed_object(StashedObject::New());
+ stashed_object->id = "test type";
+ stashed_object->data.push_back(1);
+ stashed_object->monitor_handles = true;
+ stashed_object->stashed_handles.push_back(CreateReadableHandle());
+ stashed_object->stashed_handles.push_back(CreateReadableHandle());
+ stash_entries.push_back(std::move(stashed_object));
+ stashed_object = StashedObject::New();
+ stashed_object->id = "another test type";
+ stashed_object->data.push_back(2);
+ stashed_object->monitor_handles = true;
+ stashed_object->stashed_handles.push_back(CreateReadableHandle());
+ stashed_object->stashed_handles.push_back(CreateReadableHandle());
+ stash_entries.push_back(std::move(stashed_object));
+ stash_service_->AddToStash(std::move(stash_entries));
+ WaitForEvent(EVENT_HANDLE_READY);
+ EXPECT_EQ(1, handles_ready_);
+
+ stashed_object = StashedObject::New();
+ stashed_object->id = "yet another test type";
+ stashed_object->data.push_back(3);
+ stashed_object->monitor_handles = true;
+ stashed_object->stashed_handles.push_back(CreateReadableHandle());
+ stashed_object->stashed_handles.push_back(CreateReadableHandle());
+ stash_entries.push_back(std::move(stashed_object));
+ stash_service_->AddToStash(std::move(stash_entries));
+
+ stash_service_->AddToStash(RetrieveStash());
+ WaitForEvent(EVENT_HANDLE_READY);
+ EXPECT_EQ(2, handles_ready_);
+}
+
+// Test that a stash service discards stashed objects when the backend no longer
+// exists.
+TEST_F(StashServiceTest, ServiceWithDeletedBackend) {
+ stash_backend_.reset();
+ stash_service_.set_connection_error_handler(base::Bind(&OnConnectionError));
+
+ mojo::Array<StashedObjectPtr> stashed_objects;
+ StashedObjectPtr stashed_object(StashedObject::New());
+ stashed_object->id = "test type";
+ stashed_object->data.push_back(1);
+ mojo::MessagePipe message_pipe;
+ stashed_object->stashed_handles.push_back(
+ mojo::ScopedHandle::From(std::move(message_pipe.handle0)));
+ stashed_objects.push_back(std::move(stashed_object));
+ stash_service_->AddToStash(std::move(stashed_objects));
+ stashed_objects = RetrieveStash();
+ ASSERT_EQ(0u, stashed_objects.size());
+ // Check that the stashed handle has been closed.
+ MojoResult result =
+ mojo::Wait(message_pipe.handle1.get(),
+ MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_READABLE,
+ MOJO_DEADLINE_INDEFINITE, nullptr);
+ EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION, result);
+}
+
+} // namespace extensions