Index: extensions/browser/api/socket/tcp_socket_unittest.cc |
diff --git a/extensions/browser/api/socket/tcp_socket_unittest.cc b/extensions/browser/api/socket/tcp_socket_unittest.cc |
new file mode 100644 |
index 0000000000000000000000000000000000000000..31a6c1fe3fea3a53a5ec18f1b7c79bc1cb6adf1a |
--- /dev/null |
+++ b/extensions/browser/api/socket/tcp_socket_unittest.cc |
@@ -0,0 +1,150 @@ |
+// 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 <algorithm> |
+#include <queue> |
+#include <vector> |
+ |
+#include "base/memory/scoped_ptr.h" |
+#include "extensions/browser/api/socket/tcp_socket.h" |
+#include "extensions/browser/api_unittest.h" |
+#include "net/base/io_buffer.h" |
+#include "net/base/net_errors.h" |
+#include "testing/gmock/include/gmock/gmock.h" |
+#include "testing/gtest/include/gtest/gtest.h" |
+ |
+namespace extensions { |
+ |
+class UnderlyingSource { |
+ public: |
+ UnderlyingSource() {} |
+ |
+ int Read(net::IOBuffer* dest, int sz, const net::CompletionCallback& cb) { |
+ CHECK(!response_queue_.empty()); |
+ CHECK(waiting_cb_.is_null()); |
+ const bool immediate = response_queue_.front().immediate_return; |
+ std::string& front_text = response_queue_.front().text; |
+ int amount_to_return = std::min<int>(sz, front_text.length()); |
+ memcpy(dest->data(), front_text.c_str(), amount_to_return); |
+ // This may make the string empty. We use that as a consumption |
+ // state-var. |
+ front_text.erase(0, amount_to_return); |
+ |
+ if (immediate) { |
+ if (front_text.empty()) { |
+ response_queue_.pop(); |
+ } |
+ return amount_to_return; |
+ } else { |
+ waiting_cb_ = cb; |
+ waiting_cb_arg_ = amount_to_return; |
+ return net::ERR_IO_PENDING; |
+ } |
+ } |
+ |
+ void QueueResponse(const std::string& data, bool immediate) { |
+ response_queue_.push(response(data, immediate)); |
+ } |
+ |
+ void SendResponse() { |
+ int arg = waiting_cb_arg_; |
+ net::CompletionCallback cb = waiting_cb_; |
+ waiting_cb_.Reset(); |
+ waiting_cb_arg_ = 0; |
+ if (response_queue_.front().text.empty()) { |
+ response_queue_.pop(); |
+ } |
+ cb.Run(arg); |
+ } |
+ |
+ size_t remaining_responses() { return response_queue_.size(); } |
+ |
+ struct response { |
+ std::string text; |
+ const bool immediate_return; |
+ response(const std::string& s, bool ret) : text(s), immediate_return(ret) {} |
+ }; |
+ |
+ // The top of this queue is either a fresh, ready-to-use response, or it's |
+ // mid-response, and waiting_cb_ is non-null. |
+ std::queue<response> response_queue_; |
+ // If the last caller to Read() had a non-immediate return, then this is |
+ // the callback that's called when SendResponse() is invoked. |
+ net::CompletionCallback waiting_cb_; |
+ int waiting_cb_arg_; |
+}; |
+ |
+class SocketPauseBufferUnitTest : public testing::Test { |
+ public: |
+ SocketPauseBufferUnitTest() |
+ : complete_callback_(base::Bind(&SocketPauseBufferUnitTest::IOComplete, |
+ base::Unretained(this))) {} |
+ void SetUp() override { |
+ testing::Test::SetUp(); |
+ last_size_ = 0; |
+ source_.reset(new UnderlyingSource); |
+ pause_buffer_.reset(new SocketPauseBuffer( |
+ base::Bind(&UnderlyingSource::Read, base::Unretained(source_.get())))); |
+ } |
+ |
+ void IOComplete(int size) { last_size_ = size; } |
+ |
+ protected: |
+ SocketPauseBuffer* pause_buffer() { return pause_buffer_.get(); } |
+ |
+ UnderlyingSource* source() { return source_.get(); } |
+ |
+ net::CompletionCallback& complete_callback() { return complete_callback_; } |
+ |
+ int last_size() { return last_size_; } |
+ void reset_size() { last_size_ = 0; } |
+ |
+ scoped_ptr<SocketPauseBuffer> pause_buffer_; |
+ scoped_ptr<UnderlyingSource> source_; |
+ int last_size_; |
+ net::CompletionCallback complete_callback_; |
+}; |
+ |
+TEST_F(SocketPauseBufferUnitTest, ReadNormally) { |
+ source()->QueueResponse("Foo", true); |
+ scoped_refptr<net::IOBuffer> iob(new net::IOBuffer(512)); |
+ int ret = pause_buffer()->Read(iob.get(), 3, complete_callback()); |
+ |
+ CHECK_EQ(ret, 3); |
+ CHECK_EQ(last_size_, 0); |
+ CHECK_EQ(source()->remaining_responses(), 0); |
+ CHECK_EQ(memcmp(iob->data(), "Foo", 3), 0); |
+ source()->QueueResponse("BarBaz", false); |
+ ret = pause_buffer()->Read(iob.get(), 3, complete_callback()); |
+ CHECK_LT(ret, 0); |
+ source()->SendResponse(); |
+ CHECK_EQ(source()->remaining_responses(), 1); |
+ CHECK_EQ(last_size(), 3); |
+ CHECK_EQ(memcmp(iob->data(), "Bar", 3), 0); |
+} |
+ |
+TEST_F(SocketPauseBufferUnitTest, PauseAndRead) { |
+ source()->QueueResponse("FirstSecond", false); |
+ scoped_refptr<net::IOBuffer> iob(new net::IOBuffer(512)); |
+ int read_count = pause_buffer()->Read(iob.get(), 5, complete_callback()); |
+ CHECK_LT(read_count, 0); |
+ bool pause_ret = pause_buffer()->Pause(); |
+ CHECK_EQ(pause_ret, true); |
+ // Verify that we got a ERR_ABORTED from the Read() upon Pause(). |
+ CHECK_EQ(last_size(), net::ERR_ABORTED); |
+ reset_size(); |
+ source()->SendResponse(); |
+ // Now the PauseBuffer has data. 5 bytes. |
+ read_count = pause_buffer()->Read(iob.get(), 10, complete_callback()); |
+ CHECK_EQ(read_count, 5); // that's all that was requested originally. |
+ read_count = pause_buffer()->Read(iob.get(), 10, complete_callback()); |
+ |
+ // This isn't from the buffer, so it'll invoke the callback. |
+ CHECK_LT(read_count, 0); |
+ CHECK_EQ(last_size(), 0); // no callback invoked yet... |
+ source()->SendResponse(); |
+ CHECK_EQ(last_size(), 6); |
+} |
+ |
+} // namespace extensions |