Chromium Code Reviews| Index: content/child/web_data_consumer_handle_impl_unittest.cc |
| diff --git a/content/child/web_data_consumer_handle_impl_unittest.cc b/content/child/web_data_consumer_handle_impl_unittest.cc |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..2276ceed9e6bb2425e0cade7856e28df15ccb4e5 |
| --- /dev/null |
| +++ b/content/child/web_data_consumer_handle_impl_unittest.cc |
| @@ -0,0 +1,280 @@ |
| +// 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 "content/child/web_data_consumer_handle_impl.h" |
| + |
| +#include <algorithm> |
| +#include <string> |
| +#include "base/bind.h" |
| +#include "base/memory/scoped_ptr.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::WebDataConsumerHandle; |
| + |
| +class ReadDataOperationBase { |
| + public: |
| + virtual ~ReadDataOperationBase() {} |
| + virtual void ReadMore() = 0; |
| + |
| + static const WebDataConsumerHandle::Flags kNone = |
| + WebDataConsumerHandle::FlagNone; |
| + static const WebDataConsumerHandle::Result kOk = WebDataConsumerHandle::Ok; |
| + static const WebDataConsumerHandle::Result kDone = |
| + WebDataConsumerHandle::Done; |
| + static const WebDataConsumerHandle::Result kShouldWait = |
| + WebDataConsumerHandle::ShouldWait; |
| +}; |
| + |
| +class ClientImpl final : public WebDataConsumerHandle::Client { |
| + public: |
| + explicit ClientImpl(ReadDataOperationBase* operation) |
| + : operation_(operation) {} |
| + |
| + virtual void didGetReadable() override { |
| + base::MessageLoop::current()->PostTask( |
| + FROM_HERE, |
| + base::Bind(&ReadDataOperationBase::ReadMore, |
| + base::Unretained(operation_))); |
| + } |
| + |
| + private: |
| + ReadDataOperationBase* operation_; |
| +}; |
| + |
| +class ReadDataOperation : public ReadDataOperationBase { |
| + public: |
| + typedef WebDataConsumerHandle::Result Result; |
| + ReadDataOperation(mojo::ScopedDataPipeConsumerHandle handle, |
| + base::MessageLoop* main_message_loop, |
| + const base::Closure& stop) |
|
tyoshino (SeeGerritForStatus)
2014/10/23 09:38:51
how about on_stop or on_done?
yhirano
2014/10/23 12:01:05
Done.
|
| + : handle_(new WebDataConsumerHandleImpl(handle.Pass())), |
| + main_message_loop_(main_message_loop), stop_(stop) {} |
| + |
| + const std::string& result() const { return result_; } |
| + |
| + virtual void ReadMore() override { |
| + ReadData(); |
| + } |
| + |
| + void ReadData() { |
| + if (!client_) { |
| + client_.reset(new ClientImpl(this)); |
| + handle_->registerClient(client_.get()); |
| + } |
| + |
| + Result rv = kOk; |
| + size_t readSize = 0; |
| + |
| + while (rv == kOk) { |
| + char buffer[16]; |
| + result_.insert(result_.size(), &buffer[0], readSize); |
| + rv = handle_->read(&buffer, sizeof(buffer), kNone, &readSize); |
|
tyoshino (SeeGerritForStatus)
2014/10/23 09:38:50
how about checking rv here (if not kOk, break from
yhirano
2014/10/23 12:01:06
Done.
|
| + } |
| + |
| + if (rv == kShouldWait) { |
| + // Wait a while... |
| + return; |
| + } |
| + |
| + if (rv != kDone) { |
| + // Something is wrong. |
| + result_ = "error"; |
| + } |
| + |
| + // The operation is done. |
| + main_message_loop_->PostTask(FROM_HERE, stop_); |
| + } |
| + |
| + private: |
| + scoped_ptr<WebDataConsumerHandle> handle_; |
| + scoped_ptr<WebDataConsumerHandle::Client> client_; |
| + base::MessageLoop* main_message_loop_; |
| + base::Closure stop_; |
| + std::string result_; |
| +}; |
| + |
| + |
|
tyoshino (SeeGerritForStatus)
2014/10/23 09:38:50
1 blank line
yhirano
2014/10/23 12:01:06
Done.
|
| +class TwoPhaseReadDataOperation : public ReadDataOperationBase { |
| + public: |
| + typedef WebDataConsumerHandle::Result Result; |
| + TwoPhaseReadDataOperation(mojo::ScopedDataPipeConsumerHandle handle, |
| + base::MessageLoop* main_message_loop, |
| + const base::Closure& stop) |
|
tyoshino (SeeGerritForStatus)
2014/10/23 09:38:50
ditto
yhirano
2014/10/23 12:01:06
Done.
|
| + : handle_(new WebDataConsumerHandleImpl(handle.Pass())), |
| + main_message_loop_(main_message_loop), stop_(stop) {} |
| + |
| + const std::string& result() const { return result_; } |
| + |
| + virtual void ReadMore() override { |
| + ReadData(); |
| + } |
| + |
| + void ReadData() { |
| + if (!client_) { |
| + client_.reset(new ClientImpl(this)); |
| + handle_->registerClient(client_.get()); |
| + } |
| + |
| + Result rv; |
| + while (true) { |
| + const void* buffer = nullptr; |
| + size_t size; |
| + rv = handle_->beginRead(&buffer, kNone, &size); |
| + if (rv != kOk) |
| + break; |
| + // In order to verify endRead, we read at most one byte for each time. |
| + size_t read_size = std::max(static_cast<size_t>(1), size); |
|
tyoshino (SeeGerritForStatus)
2014/10/23 09:38:50
1U?
tyoshino (SeeGerritForStatus)
2014/10/23 11:18:18
Never mind
yhirano
2014/10/23 12:01:05
We still need static_cast<size_t> even if we use 1
|
| + result_.insert(result_.size(), static_cast<const char*>(buffer), |
| + read_size); |
| + rv = handle_->endRead(read_size); |
|
tyoshino (SeeGerritForStatus)
2014/10/23 09:38:50
check rv?
yhirano
2014/10/23 12:01:06
Done.
|
| + } |
| + |
| + if (rv == kShouldWait) { |
| + // Wait a while... |
| + return; |
| + } |
| + |
| + if (rv != kDone) { |
| + // Something is wrong. |
| + result_ = "error"; |
| + } |
| + |
| + // The operation is done. |
| + main_message_loop_->PostTask(FROM_HERE, stop_); |
| + } |
| + |
| + private: |
| + scoped_ptr<WebDataConsumerHandle> handle_; |
| + scoped_ptr<WebDataConsumerHandle::Client> client_; |
| + base::MessageLoop* main_message_loop_; |
| + base::Closure stop_; |
| + std::string result_; |
| +}; |
| + |
| +class WebDataConsumerHandleImplTest : public ::testing::Test { |
| + public: |
| + typedef WebDataConsumerHandle::Result Result; |
| + |
| + WebDataConsumerHandleImplTest(): done_(false) {} |
| + |
| + virtual 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); |
| + } |
| + |
| + void Loop() { |
| + if (!done_) { |
| + message_loop_.PostTask(FROM_HERE, |
| + base::Bind(&WebDataConsumerHandleImplTest::Loop, |
| + base::Unretained(this))); |
| + } |
| + } |
| + |
| + void StopLoop() { |
| + done_ = true; |
| + } |
| + |
| + // 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; |
| + } |
| + |
| + const char* p = expected.data(); |
| + size_t remaining = total_size; |
| + const MojoWriteDataFlags kNone = MOJO_WRITE_DATA_FLAG_NONE; |
| + MojoResult rv; |
| + while (remaining > 0) { |
| + uint32_t size = remaining; |
| + rv = mojo::WriteDataRaw(producer_.get(), p, &size, kNone); |
| + if (rv == MOJO_RESULT_OK) { |
| + remaining -= size; |
| + p += size; |
| + } else if (rv != MOJO_RESULT_SHOULD_WAIT) { |
| + // Something is wrong. |
| + EXPECT_TRUE(false) << "mojo::WriteDataRaw returns an invalid value."; |
| + return "error on writing"; |
| + } |
| + } |
| + return expected; |
| + } |
| + |
| + base::MessageLoop message_loop_; |
| + |
| + mojo::ScopedDataPipeProducerHandle producer_; |
| + mojo::ScopedDataPipeConsumerHandle consumer_; |
| + |
| + bool done_; |
| +}; |
| + |
| +TEST_F(WebDataConsumerHandleImplTest, ReadData) { |
| + auto operation = make_scoped_ptr(new ReadDataOperation( |
| + consumer_.Pass(), |
| + &message_loop_, |
| + base::Bind(&WebDataConsumerHandleImplTest::StopLoop, |
| + base::Unretained(this)))); |
| + |
| + base::Thread t("DataConsumerHandle test thread"); |
| + ASSERT_TRUE(t.Start()); |
| + |
| + t.message_loop()->PostTask( |
| + FROM_HERE, |
| + base::Bind(&ReadDataOperation::ReadData, |
| + base::Unretained(operation.get()))); |
| + |
| + std::string expected = ProduceData(24 * 1024); |
| + producer_.reset(); |
| + |
| + Loop(); |
|
tyoshino (SeeGerritForStatus)
2014/10/23 09:38:51
does this make the message loop busy?
tyoshino (SeeGerritForStatus)
2014/10/23 11:18:18
Use RunLoop::Run() and QuitClosure()?
yhirano
2014/10/23 12:01:05
Done.
|
| + message_loop_.RunUntilIdle(); |
| + t.Stop(); |
| + |
| + EXPECT_EQ(expected, operation->result()); |
| +} |
| + |
| +TEST_F(WebDataConsumerHandleImplTest, TwoPhaseReadData) { |
| + auto operation = make_scoped_ptr(new TwoPhaseReadDataOperation( |
| + consumer_.Pass(), |
| + &message_loop_, |
| + base::Bind(&WebDataConsumerHandleImplTest::StopLoop, |
| + base::Unretained(this)))); |
| + |
| + base::Thread t("DataConsumerHandle test thread"); |
| + ASSERT_TRUE(t.Start()); |
| + |
| + t.message_loop()->PostTask( |
| + FROM_HERE, |
| + base::Bind(&TwoPhaseReadDataOperation::ReadData, |
| + base::Unretained(operation.get()))); |
| + |
| + std::string expected = ProduceData(24 * 1024); |
| + producer_.reset(); |
| + |
| + Loop(); |
| + message_loop_.RunUntilIdle(); |
| + t.Stop(); |
| + |
| + EXPECT_EQ(expected, operation->result()); |
| +} |
| + |
| +} // namespace |
| + |
| +} // namespace content |