| Index: content/child/web_data_producer_handle_impl_unittest.cc
|
| diff --git a/content/child/web_data_producer_handle_impl_unittest.cc b/content/child/web_data_producer_handle_impl_unittest.cc
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..6011ac9bf593f44646e28a74632a06de1b557f67
|
| --- /dev/null
|
| +++ b/content/child/web_data_producer_handle_impl_unittest.cc
|
| @@ -0,0 +1,276 @@
|
| +// 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/child/web_data_producer_handle_impl.h"
|
| +
|
| +#include <algorithm>
|
| +#include <deque>
|
| +#include <string>
|
| +#include <vector>
|
| +#include "base/bind.h"
|
| +#include "base/memory/scoped_ptr.h"
|
| +#include "base/message_loop/message_loop.h"
|
| +#include "base/run_loop.h"
|
| +#include "base/synchronization/waitable_event.h"
|
| +#include "base/threading/thread.h"
|
| +#include "mojo/public/cpp/system/data_pipe.h"
|
| +#include "testing/gtest/include/gtest/gtest.h"
|
| +
|
| +namespace content {
|
| +
|
| +namespace {
|
| +
|
| +using blink::WebDataProducerHandle;
|
| +
|
| +class WriteDataOperationBase {
|
| + public:
|
| + virtual ~WriteDataOperationBase() {}
|
| + virtual void WriteMore() = 0;
|
| +
|
| + static const WebDataProducerHandle::Flags kNone =
|
| + WebDataProducerHandle::FlagNone;
|
| + static const WebDataProducerHandle::Result kOk = WebDataProducerHandle::Ok;
|
| + // static const WebDataProducerHandle::Result kAlreadyClosed =
|
| + // WebDataProducerHandle::AlreadyClosed;
|
| + static const WebDataProducerHandle::Result kShouldWait =
|
| + WebDataProducerHandle::ShouldWait;
|
| +};
|
| +
|
| +class ClientImpl final : public WebDataProducerHandle::Client {
|
| + public:
|
| + explicit ClientImpl(WriteDataOperationBase* operation)
|
| + : operation_(operation) {}
|
| +
|
| + void didGetWritable() override {
|
| + base::MessageLoop::current()->PostTask(
|
| + FROM_HERE,
|
| + base::Bind(&WriteDataOperationBase::WriteMore,
|
| + base::Unretained(operation_)));
|
| + }
|
| +
|
| + private:
|
| + WriteDataOperationBase* operation_;
|
| +};
|
| +
|
| +class WriteDataOperation : public WriteDataOperationBase {
|
| + public:
|
| + typedef WebDataProducerHandle::Result Result;
|
| + WriteDataOperation(mojo::ScopedDataPipeProducerHandle handle,
|
| + const std::deque<char>& input)
|
| + : handle_(new WebDataProducerHandleImpl(handle.Pass())),
|
| + input_(input) {}
|
| +
|
| + Result final_result() const { return final_result_; }
|
| +
|
| + void WriteMore() override { WriteData(); }
|
| +
|
| + void WriteData() {
|
| + if (!client_) {
|
| + client_.reset(new ClientImpl(this));
|
| + handle_->registerClient(client_.get());
|
| + }
|
| +
|
| + Result rv = kOk;
|
| +
|
| + while (!input_.empty()) {
|
| + const size_t size = std::min(static_cast<size_t>(16), input_.size());
|
| + std::vector<char> buffer;
|
| + size_t written_size = 0;
|
| + buffer.insert(buffer.end(), input_.begin(), input_.begin() + size);
|
| + rv = handle_->write(&buffer[0], size, kNone, &written_size);
|
| + if (rv != kOk)
|
| + break;
|
| + input_.erase(input_.begin(), input_.begin() + written_size);
|
| + }
|
| +
|
| + if (rv == kShouldWait) {
|
| + // Wait a while...
|
| + return;
|
| + }
|
| +
|
| + final_result_ = rv;
|
| +
|
| + // The operation is done.
|
| + handle_ = nullptr;
|
| + }
|
| +
|
| + private:
|
| + scoped_ptr<WebDataProducerHandle> handle_;
|
| + scoped_ptr<WebDataProducerHandle::Client> client_;
|
| + std::deque<char> input_;
|
| + Result final_result_;
|
| +};
|
| +
|
| +class TwoPhaseWriteDataOperation : public WriteDataOperationBase {
|
| + public:
|
| + typedef WebDataProducerHandle::Result Result;
|
| + TwoPhaseWriteDataOperation(mojo::ScopedDataPipeProducerHandle handle,
|
| + const std::deque<char>& input)
|
| + : handle_(new WebDataProducerHandleImpl(handle.Pass())),
|
| + input_(input) {}
|
| +
|
| + Result final_result() const { return final_result_; }
|
| +
|
| + void WriteMore() override {
|
| + WriteData();
|
| + }
|
| +
|
| + void WriteData() {
|
| + if (!client_) {
|
| + client_.reset(new ClientImpl(this));
|
| + handle_->registerClient(client_.get());
|
| + }
|
| +
|
| + Result rv = kOk;
|
| + while (!input_.empty()) {
|
| + void* buffer = nullptr;
|
| + size_t size;
|
| + rv = handle_->beginWrite(&buffer, kNone, &size);
|
| + if (rv != kOk)
|
| + break;
|
| + // In order to verify endWrite, we write at most one byte for each time.
|
| + size_t written_size = std::max(static_cast<size_t>(1), size);
|
| + std::copy(input_.begin(),
|
| + input_.begin() + written_size,
|
| + static_cast<char*>(buffer));
|
| + rv = handle_->endWrite(written_size);
|
| + if (rv != kOk)
|
| + break;
|
| + input_.erase(input_.begin(), input_.begin() + written_size);
|
| + }
|
| +
|
| + if (rv == kShouldWait) {
|
| + // Wait a while...
|
| + return;
|
| + }
|
| +
|
| + final_result_ = rv;
|
| +
|
| + // The operation is done.
|
| + handle_ = nullptr;
|
| + }
|
| +
|
| + private:
|
| + scoped_ptr<WebDataProducerHandle> handle_;
|
| + scoped_ptr<WebDataProducerHandle::Client> client_;
|
| + std::deque<char> input_;
|
| + Result final_result_;
|
| +};
|
| +
|
| +class WebDataProducerHandleImplTest : public ::testing::Test {
|
| + public:
|
| + typedef WebDataProducerHandle::Result Result;
|
| +
|
| + void SetUp() override {
|
| + MojoCreateDataPipeOptions options;
|
| + options.struct_size = sizeof(MojoCreateDataPipeOptions);
|
| + options.flags = MOJO_CREATE_DATA_PIPE_OPTIONS_FLAG_NONE;
|
| + options.element_num_bytes = 1;
|
| + options.capacity_num_bytes = 4;
|
| +
|
| + MojoResult result = mojo::CreateDataPipe(&options, &producer_, &consumer_);
|
| + ASSERT_EQ(MOJO_RESULT_OK, result);
|
| + }
|
| +
|
| + // This function can be blocked if the associated consumer doesn't consume
|
| + // the data.
|
| + std::string ProduceData(size_t total_size) {
|
| + int index = 0;
|
| + std::string expected;
|
| + for (size_t i = 0; i < total_size; ++i) {
|
| + expected += static_cast<char>(index + 'a');
|
| + index = (37 * index + 11) % 26;
|
| + }
|
| + return expected;
|
| + }
|
| +
|
| + std::string Read() {
|
| + std::string result;
|
| + const MojoWriteDataFlags kNone = MOJO_WRITE_DATA_FLAG_NONE;
|
| + MojoResult rv;
|
| + while (true) {
|
| + char buffer[16];
|
| + uint32_t size = sizeof(buffer);
|
| + rv = mojo::ReadDataRaw(consumer_.get(), buffer, &size, kNone);
|
| + if (rv == MOJO_RESULT_OK) {
|
| + result.insert(result.end(), buffer, buffer + size);
|
| + } else if (rv == MOJO_RESULT_FAILED_PRECONDITION) {
|
| + break;
|
| + } else if (rv != MOJO_RESULT_SHOULD_WAIT) {
|
| + // Something is wrong.
|
| + EXPECT_TRUE(false) << "mojo::ReadDataRaw returns an invalid value.";
|
| + return "error on reading";
|
| + }
|
| + }
|
| + return result;
|
| + }
|
| +
|
| + mojo::ScopedDataPipeProducerHandle producer_;
|
| + mojo::ScopedDataPipeConsumerHandle consumer_;
|
| +};
|
| +
|
| +TEST_F(WebDataProducerHandleImplTest, WriteData) {
|
| + std::string expected = ProduceData(24 * 1024);
|
| + std::deque<char> input(expected.begin(), expected.end());
|
| +
|
| + auto operation =
|
| + make_scoped_ptr(new WriteDataOperation(producer_.Pass(), input));
|
| +
|
| + base::Thread t("DataProducerHandle test thread");
|
| + ASSERT_TRUE(t.Start());
|
| +
|
| + t.message_loop()->PostTask(
|
| + FROM_HERE,
|
| + base::Bind(&WriteDataOperation::WriteData,
|
| + base::Unretained(operation.get())));
|
| +
|
| + std::string actual = Read();
|
| + t.Stop();
|
| +
|
| + EXPECT_EQ(WebDataProducerHandle::Ok, operation->final_result());
|
| + EXPECT_EQ(expected, actual);
|
| +}
|
| +
|
| +TEST_F(WebDataProducerHandleImplTest, TwoPhaseWriteData) {
|
| + std::string expected = ProduceData(24 * 1024);
|
| + std::deque<char> input(expected.begin(), expected.end());
|
| +
|
| + auto operation =
|
| + make_scoped_ptr(new TwoPhaseWriteDataOperation(producer_.Pass(), input));
|
| +
|
| + base::Thread t("DataProducerHandle test thread");
|
| + ASSERT_TRUE(t.Start());
|
| +
|
| + t.message_loop()->PostTask(
|
| + FROM_HERE,
|
| + base::Bind(&TwoPhaseWriteDataOperation::WriteData,
|
| + base::Unretained(operation.get())));
|
| +
|
| + std::string actual = Read();
|
| +
|
| + t.Stop();
|
| +
|
| + EXPECT_EQ(WebDataProducerHandle::Ok, operation->final_result());
|
| + EXPECT_EQ(expected, actual);
|
| +}
|
| +
|
| +TEST_F(WebDataProducerHandleImplTest, WriteToClosedPipe) {
|
| + auto handle =
|
| + make_scoped_ptr(new WebDataProducerHandleImpl(producer_.Pass()));
|
| + const auto kNone = WebDataProducerHandleImpl::FlagNone;
|
| + const auto kAlreadyClosed = WebDataProducerHandleImpl::AlreadyClosed;
|
| +
|
| + size_t written_size = 0;
|
| + void* buffer = nullptr;
|
| + size_t available = 0;
|
| +
|
| + consumer_.reset();
|
| +
|
| + EXPECT_EQ(kAlreadyClosed, handle->write(nullptr, 0, kNone, &written_size));
|
| + EXPECT_EQ(kAlreadyClosed, handle->beginWrite(&buffer, kNone, &available));
|
| +}
|
| +
|
| +} // namespace
|
| +
|
| +} // namespace content
|
|
|