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 <algorithm> |
| 6 #include <queue> |
| 7 #include <vector> |
| 8 |
| 9 #include "base/memory/scoped_ptr.h" |
| 10 #include "extensions/browser/api/socket/tcp_socket.h" |
| 11 #include "extensions/browser/api_unittest.h" |
| 12 #include "net/base/io_buffer.h" |
| 13 #include "net/base/net_errors.h" |
| 14 #include "testing/gmock/include/gmock/gmock.h" |
| 15 #include "testing/gtest/include/gtest/gtest.h" |
| 16 |
| 17 namespace extensions { |
| 18 |
| 19 class UnderlyingSource { |
| 20 public: |
| 21 UnderlyingSource() {} |
| 22 |
| 23 int Read(net::IOBuffer* dest, int sz, const net::CompletionCallback& cb) { |
| 24 CHECK(!response_queue_.empty()); |
| 25 CHECK(waiting_cb_.is_null()); |
| 26 const bool immediate = response_queue_.front().immediate_return; |
| 27 std::string& front_text = response_queue_.front().text; |
| 28 int amount_to_return = std::min<int>(sz, front_text.length()); |
| 29 memcpy(dest->data(), front_text.c_str(), amount_to_return); |
| 30 // This may make the string empty. We use that as a consumption |
| 31 // state-var. |
| 32 front_text.erase(0, amount_to_return); |
| 33 |
| 34 if (immediate) { |
| 35 if (front_text.empty()) { |
| 36 response_queue_.pop(); |
| 37 } |
| 38 return amount_to_return; |
| 39 } else { |
| 40 waiting_cb_ = cb; |
| 41 waiting_cb_arg_ = amount_to_return; |
| 42 return net::ERR_IO_PENDING; |
| 43 } |
| 44 } |
| 45 |
| 46 void QueueResponse(const std::string& data, bool immediate) { |
| 47 response_queue_.push(response(data, immediate)); |
| 48 } |
| 49 |
| 50 void SendResponse() { |
| 51 int arg = waiting_cb_arg_; |
| 52 net::CompletionCallback cb = waiting_cb_; |
| 53 waiting_cb_.Reset(); |
| 54 waiting_cb_arg_ = 0; |
| 55 if (response_queue_.front().text.empty()) { |
| 56 response_queue_.pop(); |
| 57 } |
| 58 cb.Run(arg); |
| 59 } |
| 60 |
| 61 size_t remaining_responses() { return response_queue_.size(); } |
| 62 |
| 63 struct response { |
| 64 std::string text; |
| 65 const bool immediate_return; |
| 66 response(const std::string& s, bool ret) : text(s), immediate_return(ret) {} |
| 67 }; |
| 68 |
| 69 // The top of this queue is either a fresh, ready-to-use response, or it's |
| 70 // mid-response, and waiting_cb_ is non-null. |
| 71 std::queue<response> response_queue_; |
| 72 // If the last caller to Read() had a non-immediate return, then this is |
| 73 // the callback that's called when SendResponse() is invoked. |
| 74 net::CompletionCallback waiting_cb_; |
| 75 int waiting_cb_arg_; |
| 76 }; |
| 77 |
| 78 class SocketPauseBufferUnitTest : public testing::Test { |
| 79 public: |
| 80 SocketPauseBufferUnitTest() |
| 81 : complete_callback_(base::Bind(&SocketPauseBufferUnitTest::IOComplete, |
| 82 base::Unretained(this))) {} |
| 83 void SetUp() override { |
| 84 testing::Test::SetUp(); |
| 85 last_size_ = 0; |
| 86 source_.reset(new UnderlyingSource); |
| 87 pause_buffer_.reset(new SocketPauseBuffer( |
| 88 base::Bind(&UnderlyingSource::Read, base::Unretained(source_.get())))); |
| 89 } |
| 90 |
| 91 void IOComplete(int size) { last_size_ = size; } |
| 92 |
| 93 protected: |
| 94 SocketPauseBuffer* pause_buffer() { return pause_buffer_.get(); } |
| 95 |
| 96 UnderlyingSource* source() { return source_.get(); } |
| 97 |
| 98 net::CompletionCallback& complete_callback() { return complete_callback_; } |
| 99 |
| 100 int last_size() { return last_size_; } |
| 101 void reset_size() { last_size_ = 0; } |
| 102 |
| 103 scoped_ptr<SocketPauseBuffer> pause_buffer_; |
| 104 scoped_ptr<UnderlyingSource> source_; |
| 105 int last_size_; |
| 106 net::CompletionCallback complete_callback_; |
| 107 }; |
| 108 |
| 109 TEST_F(SocketPauseBufferUnitTest, ReadNormally) { |
| 110 source()->QueueResponse("Foo", true); |
| 111 scoped_refptr<net::IOBuffer> iob(new net::IOBuffer(512)); |
| 112 int ret = pause_buffer()->Read(iob.get(), 3, complete_callback()); |
| 113 |
| 114 CHECK_EQ(ret, 3); |
| 115 CHECK_EQ(last_size_, 0); |
| 116 CHECK_EQ(source()->remaining_responses(), 0); |
| 117 CHECK_EQ(memcmp(iob->data(), "Foo", 3), 0); |
| 118 source()->QueueResponse("BarBaz", false); |
| 119 ret = pause_buffer()->Read(iob.get(), 3, complete_callback()); |
| 120 CHECK_LT(ret, 0); |
| 121 source()->SendResponse(); |
| 122 CHECK_EQ(source()->remaining_responses(), 1); |
| 123 CHECK_EQ(last_size(), 3); |
| 124 CHECK_EQ(memcmp(iob->data(), "Bar", 3), 0); |
| 125 } |
| 126 |
| 127 TEST_F(SocketPauseBufferUnitTest, PauseAndRead) { |
| 128 source()->QueueResponse("FirstSecond", false); |
| 129 scoped_refptr<net::IOBuffer> iob(new net::IOBuffer(512)); |
| 130 int read_count = pause_buffer()->Read(iob.get(), 5, complete_callback()); |
| 131 CHECK_LT(read_count, 0); |
| 132 bool pause_ret = pause_buffer()->Pause(); |
| 133 CHECK_EQ(pause_ret, true); |
| 134 // Verify that we got a ERR_ABORTED from the Read() upon Pause(). |
| 135 CHECK_EQ(last_size(), net::ERR_ABORTED); |
| 136 reset_size(); |
| 137 source()->SendResponse(); |
| 138 // Now the PauseBuffer has data. 5 bytes. |
| 139 read_count = pause_buffer()->Read(iob.get(), 10, complete_callback()); |
| 140 CHECK_EQ(read_count, 5); // that's all that was requested originally. |
| 141 read_count = pause_buffer()->Read(iob.get(), 10, complete_callback()); |
| 142 |
| 143 // This isn't from the buffer, so it'll invoke the callback. |
| 144 CHECK_LT(read_count, 0); |
| 145 CHECK_EQ(last_size(), 0); // no callback invoked yet... |
| 146 source()->SendResponse(); |
| 147 CHECK_EQ(last_size(), 6); |
| 148 } |
| 149 |
| 150 } // namespace extensions |
OLD | NEW |