| 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
|
|
|