OLD | NEW |
(Empty) | |
| 1 // Copyright 2015 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. |
| 4 |
| 5 #include "content/child/web_data_producer_handle_impl.h" |
| 6 |
| 7 #include <algorithm> |
| 8 #include <deque> |
| 9 #include <string> |
| 10 #include <vector> |
| 11 #include "base/bind.h" |
| 12 #include "base/memory/scoped_ptr.h" |
| 13 #include "base/message_loop/message_loop.h" |
| 14 #include "base/run_loop.h" |
| 15 #include "base/synchronization/waitable_event.h" |
| 16 #include "base/threading/thread.h" |
| 17 #include "mojo/public/cpp/system/data_pipe.h" |
| 18 #include "testing/gtest/include/gtest/gtest.h" |
| 19 |
| 20 namespace content { |
| 21 |
| 22 namespace { |
| 23 |
| 24 using blink::WebDataProducerHandle; |
| 25 |
| 26 class WriteDataOperationBase { |
| 27 public: |
| 28 virtual ~WriteDataOperationBase() {} |
| 29 virtual void WriteMore() = 0; |
| 30 |
| 31 static const WebDataProducerHandle::Flags kNone = |
| 32 WebDataProducerHandle::FlagNone; |
| 33 static const WebDataProducerHandle::Result kOk = WebDataProducerHandle::Ok; |
| 34 // static const WebDataProducerHandle::Result kAlreadyClosed = |
| 35 // WebDataProducerHandle::AlreadyClosed; |
| 36 static const WebDataProducerHandle::Result kShouldWait = |
| 37 WebDataProducerHandle::ShouldWait; |
| 38 }; |
| 39 |
| 40 class ClientImpl final : public WebDataProducerHandle::Client { |
| 41 public: |
| 42 explicit ClientImpl(WriteDataOperationBase* operation) |
| 43 : operation_(operation) {} |
| 44 |
| 45 void didGetWritable() override { |
| 46 base::MessageLoop::current()->PostTask( |
| 47 FROM_HERE, |
| 48 base::Bind(&WriteDataOperationBase::WriteMore, |
| 49 base::Unretained(operation_))); |
| 50 } |
| 51 |
| 52 private: |
| 53 WriteDataOperationBase* operation_; |
| 54 }; |
| 55 |
| 56 class WriteDataOperation : public WriteDataOperationBase { |
| 57 public: |
| 58 typedef WebDataProducerHandle::Result Result; |
| 59 WriteDataOperation(mojo::ScopedDataPipeProducerHandle handle, |
| 60 const std::deque<char>& input) |
| 61 : handle_(new WebDataProducerHandleImpl(handle.Pass())), |
| 62 input_(input) {} |
| 63 |
| 64 Result final_result() const { return final_result_; } |
| 65 |
| 66 void WriteMore() override { WriteData(); } |
| 67 |
| 68 void WriteData() { |
| 69 if (!client_) { |
| 70 client_.reset(new ClientImpl(this)); |
| 71 handle_->registerClient(client_.get()); |
| 72 } |
| 73 |
| 74 Result rv = kOk; |
| 75 |
| 76 while (!input_.empty()) { |
| 77 const size_t size = std::min(static_cast<size_t>(16), input_.size()); |
| 78 std::vector<char> buffer; |
| 79 size_t written_size = 0; |
| 80 buffer.insert(buffer.end(), input_.begin(), input_.begin() + size); |
| 81 rv = handle_->write(&buffer[0], size, kNone, &written_size); |
| 82 if (rv != kOk) |
| 83 break; |
| 84 input_.erase(input_.begin(), input_.begin() + written_size); |
| 85 } |
| 86 |
| 87 if (rv == kShouldWait) { |
| 88 // Wait a while... |
| 89 return; |
| 90 } |
| 91 |
| 92 final_result_ = rv; |
| 93 |
| 94 // The operation is done. |
| 95 handle_ = nullptr; |
| 96 } |
| 97 |
| 98 private: |
| 99 scoped_ptr<WebDataProducerHandle> handle_; |
| 100 scoped_ptr<WebDataProducerHandle::Client> client_; |
| 101 std::deque<char> input_; |
| 102 Result final_result_; |
| 103 }; |
| 104 |
| 105 class TwoPhaseWriteDataOperation : public WriteDataOperationBase { |
| 106 public: |
| 107 typedef WebDataProducerHandle::Result Result; |
| 108 TwoPhaseWriteDataOperation(mojo::ScopedDataPipeProducerHandle handle, |
| 109 const std::deque<char>& input) |
| 110 : handle_(new WebDataProducerHandleImpl(handle.Pass())), |
| 111 input_(input) {} |
| 112 |
| 113 Result final_result() const { return final_result_; } |
| 114 |
| 115 void WriteMore() override { |
| 116 WriteData(); |
| 117 } |
| 118 |
| 119 void WriteData() { |
| 120 if (!client_) { |
| 121 client_.reset(new ClientImpl(this)); |
| 122 handle_->registerClient(client_.get()); |
| 123 } |
| 124 |
| 125 Result rv = kOk; |
| 126 while (!input_.empty()) { |
| 127 void* buffer = nullptr; |
| 128 size_t size; |
| 129 rv = handle_->beginWrite(&buffer, kNone, &size); |
| 130 if (rv != kOk) |
| 131 break; |
| 132 // In order to verify endWrite, we write at most one byte for each time. |
| 133 size_t written_size = std::max(static_cast<size_t>(1), size); |
| 134 std::copy(input_.begin(), |
| 135 input_.begin() + written_size, |
| 136 static_cast<char*>(buffer)); |
| 137 rv = handle_->endWrite(written_size); |
| 138 if (rv != kOk) |
| 139 break; |
| 140 input_.erase(input_.begin(), input_.begin() + written_size); |
| 141 } |
| 142 |
| 143 if (rv == kShouldWait) { |
| 144 // Wait a while... |
| 145 return; |
| 146 } |
| 147 |
| 148 final_result_ = rv; |
| 149 |
| 150 // The operation is done. |
| 151 handle_ = nullptr; |
| 152 } |
| 153 |
| 154 private: |
| 155 scoped_ptr<WebDataProducerHandle> handle_; |
| 156 scoped_ptr<WebDataProducerHandle::Client> client_; |
| 157 std::deque<char> input_; |
| 158 Result final_result_; |
| 159 }; |
| 160 |
| 161 class WebDataProducerHandleImplTest : public ::testing::Test { |
| 162 public: |
| 163 typedef WebDataProducerHandle::Result Result; |
| 164 |
| 165 void SetUp() override { |
| 166 MojoCreateDataPipeOptions options; |
| 167 options.struct_size = sizeof(MojoCreateDataPipeOptions); |
| 168 options.flags = MOJO_CREATE_DATA_PIPE_OPTIONS_FLAG_NONE; |
| 169 options.element_num_bytes = 1; |
| 170 options.capacity_num_bytes = 4; |
| 171 |
| 172 MojoResult result = mojo::CreateDataPipe(&options, &producer_, &consumer_); |
| 173 ASSERT_EQ(MOJO_RESULT_OK, result); |
| 174 } |
| 175 |
| 176 // This function can be blocked if the associated consumer doesn't consume |
| 177 // the data. |
| 178 std::string ProduceData(size_t total_size) { |
| 179 int index = 0; |
| 180 std::string expected; |
| 181 for (size_t i = 0; i < total_size; ++i) { |
| 182 expected += static_cast<char>(index + 'a'); |
| 183 index = (37 * index + 11) % 26; |
| 184 } |
| 185 return expected; |
| 186 } |
| 187 |
| 188 std::string Read() { |
| 189 std::string result; |
| 190 const MojoWriteDataFlags kNone = MOJO_WRITE_DATA_FLAG_NONE; |
| 191 MojoResult rv; |
| 192 while (true) { |
| 193 char buffer[16]; |
| 194 uint32_t size = sizeof(buffer); |
| 195 rv = mojo::ReadDataRaw(consumer_.get(), buffer, &size, kNone); |
| 196 if (rv == MOJO_RESULT_OK) { |
| 197 result.insert(result.end(), buffer, buffer + size); |
| 198 } else if (rv == MOJO_RESULT_FAILED_PRECONDITION) { |
| 199 break; |
| 200 } else if (rv != MOJO_RESULT_SHOULD_WAIT) { |
| 201 // Something is wrong. |
| 202 EXPECT_TRUE(false) << "mojo::ReadDataRaw returns an invalid value."; |
| 203 return "error on reading"; |
| 204 } |
| 205 } |
| 206 return result; |
| 207 } |
| 208 |
| 209 mojo::ScopedDataPipeProducerHandle producer_; |
| 210 mojo::ScopedDataPipeConsumerHandle consumer_; |
| 211 }; |
| 212 |
| 213 TEST_F(WebDataProducerHandleImplTest, WriteData) { |
| 214 std::string expected = ProduceData(24 * 1024); |
| 215 std::deque<char> input(expected.begin(), expected.end()); |
| 216 |
| 217 auto operation = |
| 218 make_scoped_ptr(new WriteDataOperation(producer_.Pass(), input)); |
| 219 |
| 220 base::Thread t("DataProducerHandle test thread"); |
| 221 ASSERT_TRUE(t.Start()); |
| 222 |
| 223 t.message_loop()->PostTask( |
| 224 FROM_HERE, |
| 225 base::Bind(&WriteDataOperation::WriteData, |
| 226 base::Unretained(operation.get()))); |
| 227 |
| 228 std::string actual = Read(); |
| 229 t.Stop(); |
| 230 |
| 231 EXPECT_EQ(WebDataProducerHandle::Ok, operation->final_result()); |
| 232 EXPECT_EQ(expected, actual); |
| 233 } |
| 234 |
| 235 TEST_F(WebDataProducerHandleImplTest, TwoPhaseWriteData) { |
| 236 std::string expected = ProduceData(24 * 1024); |
| 237 std::deque<char> input(expected.begin(), expected.end()); |
| 238 |
| 239 auto operation = |
| 240 make_scoped_ptr(new TwoPhaseWriteDataOperation(producer_.Pass(), input)); |
| 241 |
| 242 base::Thread t("DataProducerHandle test thread"); |
| 243 ASSERT_TRUE(t.Start()); |
| 244 |
| 245 t.message_loop()->PostTask( |
| 246 FROM_HERE, |
| 247 base::Bind(&TwoPhaseWriteDataOperation::WriteData, |
| 248 base::Unretained(operation.get()))); |
| 249 |
| 250 std::string actual = Read(); |
| 251 |
| 252 t.Stop(); |
| 253 |
| 254 EXPECT_EQ(WebDataProducerHandle::Ok, operation->final_result()); |
| 255 EXPECT_EQ(expected, actual); |
| 256 } |
| 257 |
| 258 TEST_F(WebDataProducerHandleImplTest, WriteToClosedPipe) { |
| 259 auto handle = |
| 260 make_scoped_ptr(new WebDataProducerHandleImpl(producer_.Pass())); |
| 261 const auto kNone = WebDataProducerHandleImpl::FlagNone; |
| 262 const auto kAlreadyClosed = WebDataProducerHandleImpl::AlreadyClosed; |
| 263 |
| 264 size_t written_size = 0; |
| 265 void* buffer = nullptr; |
| 266 size_t available = 0; |
| 267 |
| 268 consumer_.reset(); |
| 269 |
| 270 EXPECT_EQ(kAlreadyClosed, handle->write(nullptr, 0, kNone, &written_size)); |
| 271 EXPECT_EQ(kAlreadyClosed, handle->beginWrite(&buffer, kNone, &available)); |
| 272 } |
| 273 |
| 274 } // namespace |
| 275 |
| 276 } // namespace content |
OLD | NEW |