blob: ce557be015fe8a712b2886f92cbeccf2f94712eb [file] [log] [blame]
// Copyright 2015 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <array>
#include <memory>
#include <string>
#include "base/compiler_specific.h"
#include "base/functional/bind.h"
#include "base/memory/raw_ptr.h"
#include "base/memory/ref_counted.h"
#include "base/run_loop.h"
#include "net/base/completion_once_callback.h"
#include "net/base/io_buffer.h"
#include "net/base/test_completion_callback.h"
#include "net/log/net_log_with_source.h"
#include "net/socket/socket_test_util.h"
#include "net/test/gtest_util.h"
#include "net/test/test_with_task_environment.h"
#include "net/traffic_annotation/network_traffic_annotation_test_helper.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest-spi.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "testing/platform_test.h"
using net::test::IsError;
using net::test::IsOk;
//-----------------------------------------------------------------------------
namespace net {
namespace {
const char kMsg1[] = "\0hello!\xff";
const int kLen1 = std::size(kMsg1);
const auto kMsg1Span = base::byte_span_with_nul_from_cstring(kMsg1);
const char kMsg2[] = "\0a2345678\0";
const int kLen2 = std::size(kMsg2);
const auto kMsg2Span = base::byte_span_with_nul_from_cstring(kMsg2);
const char kMsg3[] = "bye!";
const int kLen3 = std::size(kMsg3);
const auto kMsg3Span = base::byte_span_with_nul_from_cstring(kMsg3);
const char kMsg4[] = "supercalifragilisticexpialidocious";
const int kLen4 = std::size(kMsg4);
const auto kMsg4Span = base::byte_span_with_nul_from_cstring(kMsg4);
// Helper class for starting the next operation operation reentrantly after the
// previous operation completed asynchronously. When OnIOComplete is called,
// it will first verify that the previous operation behaved as expected. This is
// specified by either SetExpectedRead or SetExpectedWrite. It will then invoke
// a read or write operation specified by SetInvokeRead or SetInvokeWrite.
class ReentrantHelper {
public:
explicit ReentrantHelper(StreamSocket* socket) : socket_(socket) {}
ReentrantHelper(const ReentrantHelper&) = delete;
ReentrantHelper& operator=(const ReentrantHelper&) = delete;
// Expect that the previous operation will return `first_read.size()` and will
// fill `first_read_buf_` with `first_read`.
void SetExpectedRead(base::span<const uint8_t> first_read) {
verify_read_ = true;
first_read_buf_ = base::MakeRefCounted<IOBufferWithSize>(first_read.size());
first_read_ = first_read;
first_len_ = first_read.size();
}
// Expect that the previous operation will return |first_len|.
void SetExpectedWrite(int first_len) {
verify_read_ = false;
first_len_ = first_len;
}
// After verifying expectations, invoke a read of |read_len| bytes into
// |read_buf|, notifying |callback| when complete.
void SetInvokeRead(scoped_refptr<IOBuffer> read_buf,
int read_len,
int second_rv,
CompletionOnceCallback callback) {
second_read_ = true;
second_read_buf_ = read_buf;
second_rv_ = second_rv;
second_callback_ = std::move(callback);
second_len_ = read_len;
}
// After verifying expectations, invoke a write of `write.size()` bytes from
// `write`, notifying `callback` when complete.
void SetInvokeWrite(base::span<const uint8_t> write,
int second_rv,
CompletionOnceCallback callback) {
second_read_ = false;
second_rv_ = second_rv;
second_write_ = write;
second_callback_ = std::move(callback);
second_len_ = write.size();
}
// Returns the OnIOComplete callback for this helper.
CompletionOnceCallback callback() {
return base::BindOnce(&ReentrantHelper::OnIOComplete,
base::Unretained(this));
}
// Retuns the buffer where data is expected to have been written,
// when checked by SetExpectRead()
scoped_refptr<IOBuffer> read_buf() { return first_read_buf_; }
private:
void OnIOComplete(int rv) {
CHECK_NE(-1, first_len_) << "Expectation not set.";
CHECK_NE(-1, second_len_) << "Invocation not set.";
ASSERT_EQ(first_len_, rv);
if (verify_read_) {
ASSERT_EQ(first_read_, first_read_buf_->first(rv));
}
if (second_read_) {
ASSERT_EQ(second_rv_, socket_->Read(second_read_buf_.get(), second_len_,
std::move(second_callback_)));
} else {
auto write_buf = base::MakeRefCounted<IOBufferWithSize>(second_len_);
write_buf->span().copy_from_nonoverlapping(second_write_);
ASSERT_EQ(second_rv_, socket_->Write(write_buf.get(), second_len_,
std::move(second_callback_),
TRAFFIC_ANNOTATION_FOR_TESTS));
}
}
raw_ptr<StreamSocket> socket_;
bool verify_read_ = false;
scoped_refptr<IOBuffer> first_read_buf_;
int first_len_ = -1;
base::raw_span<const uint8_t> first_read_;
CompletionOnceCallback second_callback_;
bool second_read_ = false;
int second_rv_;
scoped_refptr<IOBuffer> second_read_buf_;
int second_len_ = -1;
base::raw_span<const uint8_t> second_write_;
};
class SequencedSocketDataTest : public TestWithTaskEnvironment {
public:
SequencedSocketDataTest();
~SequencedSocketDataTest() override;
// This method is used as the completion callback for an async read
// operation and when invoked, it verifies that the correct data was read,
// then reads from the socket and verifies that that it returns the correct
// value.
void ReentrantReadCallback(base::span<const uint8_t> data,
int len2,
int expected_rv2,
int rv);
// This method is used at the completion callback for an async operation.
// When executed, verifies that |rv| equals |expected_rv| and then
// attempts an aync read from the socket into |read_buf_| (initialized
// to |read_buf_len|) using |callback|.
void ReentrantAsyncReadCallback(int len1, int len2, int rv);
// This method is used as the completion callback for an async write
// operation and when invoked, it verifies that the write returned correctly,
// then
// attempts to write to the socket and verifies that that it returns the
// correct value.
void ReentrantWriteCallback(int expected_rv1,
base::span<const uint8_t> data,
int expected_rv2,
int rv);
// This method is used at the completion callback for an async operation.
// When executed, verifies that |rv| equals |expected_rv| and then
// attempts an aync write of |data| with |callback|
void ReentrantAsyncWriteCallback(base::span<const uint8_t> data,
CompletionOnceCallback callback,
int expected_rv,
int rv);
// Callback which adds a failure if it's ever called.
void FailingCompletionCallback(int rv);
protected:
void Initialize(base::span<const MockRead> reads,
base::span<const MockWrite> writes);
void AssertSyncReadEquals(base::span<const uint8_t> data);
void AssertAsyncReadEquals(base::span<const uint8_t> data);
void AssertReadReturns(int len, int rv);
void AssertReadBufferEquals(base::span<const uint8_t> data);
void AssertSyncWriteEquals(base::span<const uint8_t> data);
void AssertAsyncWriteEquals(base::span<const uint8_t> data);
void AssertWriteReturns(base::span<const uint8_t> data, int rv);
bool IsPaused() const;
void Resume();
void RunUntilPaused();
// When a given test completes, data_.at_eof() is expected to
// match the value specified here. Most test should consume all
// reads and writes, but some tests verify error handling behavior
// do not consume all data.
void set_expect_eof(bool expect_eof) { expect_eof_ = expect_eof; }
CompletionOnceCallback failing_callback() {
return base::BindOnce(&SequencedSocketDataTest::FailingCompletionCallback,
base::Unretained(this));
}
TestCompletionCallback read_callback_;
scoped_refptr<IOBuffer> read_buf_;
TestCompletionCallback write_callback_;
std::unique_ptr<SequencedSocketData> data_;
MockClientSocketFactory socket_factory_;
bool expect_eof_ = true;
std::unique_ptr<StreamSocket> sock_;
};
SequencedSocketDataTest::SequencedSocketDataTest() = default;
SequencedSocketDataTest::~SequencedSocketDataTest() {
// Make sure no unexpected pending tasks will cause a failure.
base::RunLoop().RunUntilIdle();
if (expect_eof_) {
EXPECT_EQ(expect_eof_, data_->AllReadDataConsumed());
EXPECT_EQ(expect_eof_, data_->AllWriteDataConsumed());
}
}
void SequencedSocketDataTest::Initialize(base::span<const MockRead> reads,
base::span<const MockWrite> writes) {
data_ = std::make_unique<SequencedSocketData>(MockConnect(SYNCHRONOUS, OK),
reads, writes);
socket_factory_.AddSocketDataProvider(data_.get());
sock_ = socket_factory_.CreateTransportClientSocket(
AddressList(IPEndPoint(IPAddress::IPv4Localhost(), 443)),
nullptr /* socket_performance_watcher */,
nullptr /* network_quality_estimator */, nullptr /* net_log */,
NetLogSource());
TestCompletionCallback callback;
EXPECT_EQ(OK, sock_->Connect(callback.callback()));
}
void SequencedSocketDataTest::AssertSyncReadEquals(
base::span<const uint8_t> data) {
// Issue the read, which will complete immediately.
AssertReadReturns(data.size(), data.size());
AssertReadBufferEquals(data);
}
void SequencedSocketDataTest::AssertAsyncReadEquals(
base::span<const uint8_t> data) {
// Issue the read, which will be completed asynchronously.
AssertReadReturns(data.size(), ERR_IO_PENDING);
EXPECT_TRUE(sock_->IsConnected());
// Now the read should complete.
ASSERT_EQ(data.size(), read_callback_.WaitForResult());
AssertReadBufferEquals(data);
}
void SequencedSocketDataTest::AssertReadReturns(int len, int rv) {
read_buf_ = base::MakeRefCounted<IOBufferWithSize>(len);
if (rv == ERR_IO_PENDING) {
ASSERT_EQ(rv, sock_->Read(read_buf_.get(), len, read_callback_.callback()));
ASSERT_FALSE(read_callback_.have_result());
} else {
ASSERT_EQ(rv, sock_->Read(read_buf_.get(), len, failing_callback()));
}
}
void SequencedSocketDataTest::AssertReadBufferEquals(
base::span<const uint8_t> data) {
ASSERT_EQ(data, read_buf_->first(data.size()));
}
void SequencedSocketDataTest::AssertSyncWriteEquals(
base::span<const uint8_t> data) {
// Issue the write, which should be complete immediately.
AssertWriteReturns(data, data.size());
ASSERT_FALSE(write_callback_.have_result());
}
void SequencedSocketDataTest::AssertAsyncWriteEquals(
base::span<const uint8_t> data) {
// Issue the read, which should be completed asynchronously.
AssertWriteReturns(data, ERR_IO_PENDING);
EXPECT_FALSE(read_callback_.have_result());
EXPECT_TRUE(sock_->IsConnected());
ASSERT_EQ(data.size(), write_callback_.WaitForResult());
}
bool SequencedSocketDataTest::IsPaused() const {
return data_->IsPaused();
}
void SequencedSocketDataTest::Resume() {
data_->Resume();
}
void SequencedSocketDataTest::RunUntilPaused() {
data_->RunUntilPaused();
}
void SequencedSocketDataTest::AssertWriteReturns(base::span<const uint8_t> data,
int rv) {
auto buf = base::MakeRefCounted<IOBufferWithSize>(data.size());
buf->span().copy_from_nonoverlapping(data);
if (rv == ERR_IO_PENDING) {
ASSERT_EQ(rv,
sock_->Write(buf.get(), data.size(), write_callback_.callback(),
TRAFFIC_ANNOTATION_FOR_TESTS));
ASSERT_FALSE(write_callback_.have_result());
} else {
ASSERT_EQ(rv, sock_->Write(buf.get(), data.size(), failing_callback(),
TRAFFIC_ANNOTATION_FOR_TESTS));
}
}
void SequencedSocketDataTest::ReentrantReadCallback(
base::span<const uint8_t> data,
int len2,
int expected_rv2,
int rv) {
ASSERT_EQ(data.size(), rv);
AssertReadBufferEquals(data);
AssertReadReturns(len2, expected_rv2);
}
void SequencedSocketDataTest::ReentrantAsyncReadCallback(int expected_rv,
int len,
int rv) {
ASSERT_EQ(expected_rv, rv);
AssertReadReturns(len, ERR_IO_PENDING);
}
void SequencedSocketDataTest::ReentrantWriteCallback(
int expected_rv1,
base::span<const uint8_t> data,
int expected_rv2,
int rv) {
ASSERT_EQ(expected_rv1, rv);
AssertWriteReturns(data, expected_rv2);
}
void SequencedSocketDataTest::ReentrantAsyncWriteCallback(
base::span<const uint8_t> data,
CompletionOnceCallback callback,
int expected_rv,
int rv) {
EXPECT_EQ(expected_rv, rv);
auto write_buf = base::MakeRefCounted<IOBufferWithSize>(data.size());
write_buf->span().copy_from_nonoverlapping(data);
EXPECT_THAT(sock_->Write(write_buf.get(), data.size(), std::move(callback),
TRAFFIC_ANNOTATION_FOR_TESTS),
IsError(ERR_IO_PENDING));
}
void SequencedSocketDataTest::FailingCompletionCallback(int rv) {
ADD_FAILURE() << "Callback should not have been invoked";
}
// ----------- Read
TEST_F(SequencedSocketDataTest, SingleSyncRead) {
MockRead reads[] = {
MockRead(SYNCHRONOUS, 0, kMsg1Span),
};
Initialize(reads, base::span<MockWrite>());
AssertSyncReadEquals(kMsg1Span);
}
TEST_F(SequencedSocketDataTest, MultipleSyncReads) {
MockRead reads[] = {
MockRead(SYNCHRONOUS, 0, kMsg1Span), MockRead(SYNCHRONOUS, 1, kMsg2Span),
MockRead(SYNCHRONOUS, 2, kMsg3Span), MockRead(SYNCHRONOUS, 3, kMsg3Span),
MockRead(SYNCHRONOUS, 4, kMsg2Span), MockRead(SYNCHRONOUS, 5, kMsg3Span),
MockRead(SYNCHRONOUS, 6, kMsg1Span),
};
Initialize(reads, base::span<MockWrite>());
AssertSyncReadEquals(kMsg1Span);
AssertSyncReadEquals(kMsg2Span);
AssertSyncReadEquals(kMsg3Span);
AssertSyncReadEquals(kMsg3Span);
AssertSyncReadEquals(kMsg2Span);
AssertSyncReadEquals(kMsg3Span);
AssertSyncReadEquals(kMsg1Span);
}
TEST_F(SequencedSocketDataTest, SingleAsyncRead) {
MockRead reads[] = {
MockRead(ASYNC, 0, kMsg1Span),
};
Initialize(reads, base::span<MockWrite>());
AssertAsyncReadEquals(kMsg1Span);
}
TEST_F(SequencedSocketDataTest, MultipleAsyncReads) {
MockRead reads[] = {
MockRead(ASYNC, 0, kMsg1Span), MockRead(ASYNC, 1, kMsg2Span),
MockRead(ASYNC, 2, kMsg3Span), MockRead(ASYNC, 3, kMsg3Span),
MockRead(ASYNC, 4, kMsg2Span), MockRead(ASYNC, 5, kMsg3Span),
MockRead(ASYNC, 6, kMsg1Span),
};
Initialize(reads, base::span<MockWrite>());
AssertAsyncReadEquals(kMsg1Span);
AssertAsyncReadEquals(kMsg2Span);
AssertAsyncReadEquals(kMsg3Span);
AssertAsyncReadEquals(kMsg3Span);
AssertAsyncReadEquals(kMsg2Span);
AssertAsyncReadEquals(kMsg3Span);
AssertAsyncReadEquals(kMsg1Span);
}
TEST_F(SequencedSocketDataTest, MixedReads) {
MockRead reads[] = {
MockRead(SYNCHRONOUS, 0, kMsg1Span), MockRead(ASYNC, 1, kMsg2Span),
MockRead(SYNCHRONOUS, 2, kMsg3Span), MockRead(ASYNC, 3, kMsg3Span),
MockRead(SYNCHRONOUS, 4, kMsg2Span), MockRead(ASYNC, 5, kMsg3Span),
MockRead(SYNCHRONOUS, 6, kMsg1Span),
};
Initialize(reads, base::span<MockWrite>());
AssertSyncReadEquals(kMsg1Span);
AssertAsyncReadEquals(kMsg2Span);
AssertSyncReadEquals(kMsg3Span);
AssertAsyncReadEquals(kMsg3Span);
AssertSyncReadEquals(kMsg2Span);
AssertAsyncReadEquals(kMsg3Span);
AssertSyncReadEquals(kMsg1Span);
}
TEST_F(SequencedSocketDataTest, SyncReadFromCompletionCallback) {
MockRead reads[] = {
MockRead(ASYNC, 0, kMsg1Span),
MockRead(SYNCHRONOUS, 1, kMsg2Span),
};
Initialize(reads, base::span<MockWrite>());
read_buf_ = base::MakeRefCounted<IOBufferWithSize>(kLen1);
ASSERT_EQ(ERR_IO_PENDING,
sock_->Read(read_buf_.get(), kLen1,
base::BindOnce(
&SequencedSocketDataTest::ReentrantReadCallback,
base::Unretained(this), kMsg1Span, kLen2, kLen2)));
base::RunLoop().RunUntilIdle();
AssertReadBufferEquals(kMsg2Span);
}
TEST_F(SequencedSocketDataTest, ManyReentrantReads) {
MockRead reads[] = {
MockRead(ASYNC, 0, kMsg1Span),
MockRead(ASYNC, 1, kMsg2Span),
MockRead(ASYNC, 2, kMsg3Span),
MockRead(ASYNC, 3, kMsg4Span),
};
Initialize(reads, base::span<MockWrite>());
read_buf_ = base::MakeRefCounted<IOBufferWithSize>(kLen4);
ReentrantHelper helper3(sock_.get());
helper3.SetExpectedRead(kMsg3Span);
helper3.SetInvokeRead(read_buf_, kLen4, ERR_IO_PENDING,
read_callback_.callback());
ReentrantHelper helper2(sock_.get());
helper2.SetExpectedRead(kMsg2Span);
helper2.SetInvokeRead(helper3.read_buf(), kLen3, ERR_IO_PENDING,
helper3.callback());
ReentrantHelper helper(sock_.get());
helper.SetExpectedRead(kMsg1Span);
helper.SetInvokeRead(helper2.read_buf(), kLen2, ERR_IO_PENDING,
helper2.callback());
sock_->Read(helper.read_buf().get(), kLen1, helper.callback());
ASSERT_EQ(kLen4, read_callback_.WaitForResult());
AssertReadBufferEquals(kMsg4Span);
}
TEST_F(SequencedSocketDataTest, AsyncReadFromCompletionCallback) {
MockRead reads[] = {
MockRead(ASYNC, 0, kMsg1Span),
MockRead(ASYNC, 1, kMsg2Span),
};
Initialize(reads, base::span<MockWrite>());
read_buf_ = base::MakeRefCounted<IOBufferWithSize>(kLen1);
ASSERT_EQ(ERR_IO_PENDING,
sock_->Read(
read_buf_.get(), kLen1,
base::BindOnce(&SequencedSocketDataTest::ReentrantReadCallback,
base::Unretained(this), kMsg1Span, kLen2,
ERR_IO_PENDING)));
ASSERT_FALSE(read_callback_.have_result());
ASSERT_EQ(kLen2, read_callback_.WaitForResult());
AssertReadBufferEquals(kMsg2Span);
}
TEST_F(SequencedSocketDataTest, SingleSyncReadTooEarly) {
MockRead reads[] = {
MockRead(SYNCHRONOUS, 1, kMsg1Span),
};
MockWrite writes[] = {MockWrite(SYNCHRONOUS, 0, 0)};
Initialize(reads, writes);
EXPECT_NONFATAL_FAILURE(AssertReadReturns(kLen1, ERR_UNEXPECTED),
"Unable to perform synchronous IO while stopped");
set_expect_eof(false);
}
TEST_F(SequencedSocketDataTest, SingleSyncReadSmallBuffer) {
MockRead reads[] = {
MockRead(SYNCHRONOUS, 0, kMsg1Span),
};
Initialize(reads, base::span<MockWrite>());
auto [chunk_1, chunk_2] = kMsg1Span.split_at(kMsg1Span.size() - 1);
EXPECT_EQ(chunk_2.size(), 1u);
// Read the first chunk.
AssertReadReturns(chunk_1.size(), chunk_1.size());
AssertReadBufferEquals(chunk_1);
// Then read the second chunk.
AssertReadReturns(1, 1);
AssertReadBufferEquals(chunk_2);
}
TEST_F(SequencedSocketDataTest, SingleSyncReadLargeBuffer) {
MockRead reads[] = {
MockRead(SYNCHRONOUS, 0, kMsg1Span),
};
Initialize(reads, base::span<MockWrite>());
auto read_buf = base::MakeRefCounted<IOBufferWithSize>(2 * kLen1);
ASSERT_EQ(kLen1, sock_->Read(read_buf.get(), 2 * kLen1, failing_callback()));
ASSERT_EQ(std::string(kMsg1, kLen1), std::string(read_buf->data(), kLen1));
}
TEST_F(SequencedSocketDataTest, SingleAsyncReadLargeBuffer) {
MockRead reads[] = {
MockRead(ASYNC, 0, kMsg1Span),
};
Initialize(reads, base::span<MockWrite>());
auto read_buf = base::MakeRefCounted<IOBufferWithSize>(2 * kLen1);
ASSERT_EQ(ERR_IO_PENDING,
sock_->Read(read_buf.get(), 2 * kLen1, read_callback_.callback()));
ASSERT_EQ(kLen1, read_callback_.WaitForResult());
ASSERT_EQ(std::string(kMsg1, kLen1), std::string(read_buf->data(), kLen1));
}
TEST_F(SequencedSocketDataTest, HangingRead) {
MockRead reads[] = {
MockRead(SYNCHRONOUS, ERR_IO_PENDING, 0),
};
Initialize(reads, base::span<MockWrite>());
auto read_buf = base::MakeRefCounted<IOBufferWithSize>(1);
ASSERT_EQ(ERR_IO_PENDING,
sock_->Read(read_buf.get(), 1, read_callback_.callback()));
ASSERT_FALSE(read_callback_.have_result());
// Even though the read is scheduled to complete at sequence number 0,
// verify that the read callback in never called.
base::RunLoop().RunUntilIdle();
ASSERT_FALSE(read_callback_.have_result());
}
// ----------- Write
TEST_F(SequencedSocketDataTest, SingleSyncWriteTooEarly) {
MockWrite writes[] = {
MockWrite(SYNCHRONOUS, 1, kMsg1Span),
};
MockRead reads[] = {MockRead(SYNCHRONOUS, 0, 0)};
Initialize(reads, writes);
EXPECT_NONFATAL_FAILURE(AssertWriteReturns(kMsg1Span, ERR_UNEXPECTED),
"Unable to perform synchronous IO while stopped");
set_expect_eof(false);
}
TEST_F(SequencedSocketDataTest, SingleSyncWriteTooSmall) {
MockWrite writes[] = {
MockWrite(SYNCHRONOUS, 0, kMsg1Span),
};
Initialize(base::span<MockRead>(), writes);
// Expecting too small of a write triggers multiple expectation failures.
//
// The gtest infrastructure does not have a macro similar to
// EXPECT_NONFATAL_FAILURE which works when there is more than one
// failure.
//
// However, tests can gather the TestPartResultArray and directly
// validate the test failures. That's what the rest of this test does.
::testing::TestPartResultArray gtest_failures;
{
::testing::ScopedFakeTestPartResultReporter gtest_reporter(
::testing::ScopedFakeTestPartResultReporter::
INTERCEPT_ONLY_CURRENT_THREAD,
&gtest_failures);
AssertSyncWriteEquals(kMsg1Span.first(kMsg1Span.size() - 1));
}
static auto kExpectedFailures =
std::to_array<const char*>({"Value of: actual_data == expected_data\n "
"Actual: false\nExpected: true",
"Expected equality of these values:\n rv"});
ASSERT_EQ(std::size(kExpectedFailures),
static_cast<size_t>(gtest_failures.size()));
for (int i = 0; i < gtest_failures.size(); ++i) {
const ::testing::TestPartResult& result =
gtest_failures.GetTestPartResult(i);
EXPECT_NE(std::string(result.message()).find(kExpectedFailures[i]),
std::string::npos);
}
set_expect_eof(false);
}
TEST_F(SequencedSocketDataTest, SingleSyncPartialWrite) {
size_t split = base::checked_cast<size_t>(kLen1 - 1);
MockWrite writes[] = {
MockWrite(SYNCHRONOUS, 0, kMsg1Span.first(split)),
MockWrite(SYNCHRONOUS, 1, kMsg1Span.subspan(split)),
};
Initialize(base::span<MockRead>(), writes);
// Attempt to write all of the message, but only some will be written.
AssertSyncWriteEquals(kMsg1Span.first(split));
// Write the rest of the message.
AssertSyncWriteEquals(kMsg1Span.subspan(split));
}
TEST_F(SequencedSocketDataTest, SingleSyncWrite) {
MockWrite writes[] = {
MockWrite(SYNCHRONOUS, 0, kMsg1Span),
};
Initialize(base::span<MockRead>(), writes);
AssertSyncWriteEquals(kMsg1Span);
}
TEST_F(SequencedSocketDataTest, MultipleSyncWrites) {
MockWrite writes[] = {
MockWrite(SYNCHRONOUS, 0, kMsg1Span),
MockWrite(SYNCHRONOUS, 1, kMsg2Span),
MockWrite(SYNCHRONOUS, 2, kMsg3Span),
MockWrite(SYNCHRONOUS, 3, kMsg3Span),
MockWrite(SYNCHRONOUS, 4, kMsg2Span),
MockWrite(SYNCHRONOUS, 5, kMsg3Span),
MockWrite(SYNCHRONOUS, 6, kMsg1Span),
};
Initialize(base::span<MockRead>(), writes);
AssertSyncWriteEquals(kMsg1Span);
AssertSyncWriteEquals(kMsg2Span);
AssertSyncWriteEquals(kMsg3Span);
AssertSyncWriteEquals(kMsg3Span);
AssertSyncWriteEquals(kMsg2Span);
AssertSyncWriteEquals(kMsg3Span);
AssertSyncWriteEquals(kMsg1Span);
}
TEST_F(SequencedSocketDataTest, SingleAsyncWrite) {
MockWrite writes[] = {
MockWrite(ASYNC, 0, kMsg1Span),
};
Initialize(base::span<MockRead>(), writes);
AssertAsyncWriteEquals(kMsg1Span);
}
TEST_F(SequencedSocketDataTest, MultipleAsyncWrites) {
MockWrite writes[] = {
MockWrite(ASYNC, 0, kMsg1Span), MockWrite(ASYNC, 1, kMsg2Span),
MockWrite(ASYNC, 2, kMsg3Span), MockWrite(ASYNC, 3, kMsg3Span),
MockWrite(ASYNC, 4, kMsg2Span), MockWrite(ASYNC, 5, kMsg3Span),
MockWrite(ASYNC, 6, kMsg1Span),
};
Initialize(base::span<MockRead>(), writes);
AssertAsyncWriteEquals(kMsg1Span);
AssertAsyncWriteEquals(kMsg2Span);
AssertAsyncWriteEquals(kMsg3Span);
AssertAsyncWriteEquals(kMsg3Span);
AssertAsyncWriteEquals(kMsg2Span);
AssertAsyncWriteEquals(kMsg3Span);
AssertAsyncWriteEquals(kMsg1Span);
}
TEST_F(SequencedSocketDataTest, MixedWrites) {
MockWrite writes[] = {
MockWrite(SYNCHRONOUS, 0, kMsg1Span), MockWrite(ASYNC, 1, kMsg2Span),
MockWrite(SYNCHRONOUS, 2, kMsg3Span), MockWrite(ASYNC, 3, kMsg3Span),
MockWrite(SYNCHRONOUS, 4, kMsg2Span), MockWrite(ASYNC, 5, kMsg3Span),
MockWrite(SYNCHRONOUS, 6, kMsg1Span),
};
Initialize(base::span<MockRead>(), writes);
AssertSyncWriteEquals(kMsg1Span);
AssertAsyncWriteEquals(kMsg2Span);
AssertSyncWriteEquals(kMsg3Span);
AssertAsyncWriteEquals(kMsg3Span);
AssertSyncWriteEquals(kMsg2Span);
AssertAsyncWriteEquals(kMsg3Span);
AssertSyncWriteEquals(kMsg1Span);
}
TEST_F(SequencedSocketDataTest, SyncWriteFromCompletionCallback) {
MockWrite writes[] = {
MockWrite(ASYNC, 0, kMsg1Span),
MockWrite(SYNCHRONOUS, 1, kMsg2Span),
};
Initialize(base::span<MockRead>(), writes);
auto write_buf = base::MakeRefCounted<IOBufferWithSize>(kLen1);
write_buf->span().copy_from_nonoverlapping(kMsg1Span);
ASSERT_EQ(ERR_IO_PENDING,
sock_->Write(
write_buf.get(), kLen1,
base::BindOnce(&SequencedSocketDataTest::ReentrantWriteCallback,
base::Unretained(this), kLen1, kMsg2Span, kLen2),
TRAFFIC_ANNOTATION_FOR_TESTS));
base::RunLoop().RunUntilIdle();
}
TEST_F(SequencedSocketDataTest, AsyncWriteFromCompletionCallback) {
MockWrite writes[] = {
MockWrite(ASYNC, 0, kMsg1Span),
MockWrite(ASYNC, 1, kMsg2Span),
};
Initialize(base::span<MockRead>(), writes);
auto write_buf = base::MakeRefCounted<IOBufferWithSize>(kLen1);
write_buf->span().copy_from_nonoverlapping(kMsg1Span);
ASSERT_EQ(ERR_IO_PENDING,
sock_->Write(
write_buf.get(), kLen1,
base::BindOnce(&SequencedSocketDataTest::ReentrantWriteCallback,
base::Unretained(this), kLen1, kMsg2Span,
ERR_IO_PENDING),
TRAFFIC_ANNOTATION_FOR_TESTS));
ASSERT_FALSE(write_callback_.have_result());
ASSERT_EQ(kLen2, write_callback_.WaitForResult());
}
TEST_F(SequencedSocketDataTest, ManyReentrantWrites) {
MockWrite writes[] = {
MockWrite(ASYNC, 0, kMsg1Span),
MockWrite(ASYNC, 1, kMsg2Span),
MockWrite(ASYNC, 2, kMsg3Span),
MockWrite(ASYNC, 3, kMsg4Span),
};
Initialize(base::span<MockRead>(), writes);
ReentrantHelper helper3(sock_.get());
helper3.SetExpectedWrite(kLen3);
helper3.SetInvokeWrite(kMsg4Span, ERR_IO_PENDING, write_callback_.callback());
ReentrantHelper helper2(sock_.get());
helper2.SetExpectedWrite(kLen2);
helper2.SetInvokeWrite(kMsg3Span, ERR_IO_PENDING, helper3.callback());
ReentrantHelper helper(sock_.get());
helper.SetExpectedWrite(kLen1);
helper.SetInvokeWrite(kMsg2Span, ERR_IO_PENDING, helper2.callback());
auto write_buf = base::MakeRefCounted<IOBufferWithSize>(kLen1);
write_buf->span().copy_from_nonoverlapping(kMsg1Span);
sock_->Write(write_buf.get(), kLen1, helper.callback(),
TRAFFIC_ANNOTATION_FOR_TESTS);
ASSERT_EQ(kLen4, write_callback_.WaitForResult());
}
// ----------- Mixed Reads and Writes
TEST_F(SequencedSocketDataTest, MixedSyncOperations) {
MockRead reads[] = {
MockRead(SYNCHRONOUS, 0, kMsg1Span),
MockRead(SYNCHRONOUS, 3, kMsg2Span),
};
MockWrite writes[] = {
MockWrite(SYNCHRONOUS, 1, kMsg2Span),
MockWrite(SYNCHRONOUS, 2, kMsg3Span),
};
Initialize(reads, writes);
AssertSyncReadEquals(kMsg1Span);
AssertSyncWriteEquals(kMsg2Span);
AssertSyncWriteEquals(kMsg3Span);
AssertSyncReadEquals(kMsg2Span);
}
TEST_F(SequencedSocketDataTest, MixedAsyncOperations) {
MockRead reads[] = {
MockRead(ASYNC, 0, kMsg1Span),
MockRead(ASYNC, 3, kMsg2Span),
};
MockWrite writes[] = {
MockWrite(ASYNC, 1, kMsg2Span),
MockWrite(ASYNC, 2, kMsg3Span),
};
Initialize(reads, writes);
AssertAsyncReadEquals(kMsg1Span);
AssertAsyncWriteEquals(kMsg2Span);
AssertAsyncWriteEquals(kMsg3Span);
AssertAsyncReadEquals(kMsg2Span);
}
TEST_F(SequencedSocketDataTest, InterleavedAsyncOperations) {
// Order of completion is read, write, write, read.
MockRead reads[] = {
MockRead(ASYNC, 0, kMsg1Span),
MockRead(ASYNC, 3, kMsg2Span),
};
MockWrite writes[] = {
MockWrite(ASYNC, 1, kMsg2Span),
MockWrite(ASYNC, 2, kMsg3Span),
};
Initialize(reads, writes);
// Issue the write, which will block until the read completes.
AssertWriteReturns(kMsg2Span, ERR_IO_PENDING);
// Issue the read which will return first.
AssertReadReturns(kLen1, ERR_IO_PENDING);
ASSERT_EQ(kLen1, read_callback_.WaitForResult());
AssertReadBufferEquals(kMsg1Span);
// Run posted OnWriteComplete().
base::RunLoop().RunUntilIdle();
ASSERT_TRUE(write_callback_.have_result());
ASSERT_EQ(kLen2, write_callback_.WaitForResult());
// Issue the read, which will block until the write completes.
AssertReadReturns(kLen2, ERR_IO_PENDING);
// Issue the writes which will return first.
AssertWriteReturns(kMsg3Span, ERR_IO_PENDING);
ASSERT_EQ(kLen3, write_callback_.WaitForResult());
ASSERT_EQ(kLen2, read_callback_.WaitForResult());
AssertReadBufferEquals(kMsg2Span);
}
TEST_F(SequencedSocketDataTest, InterleavedMixedOperations) {
// Order of completion is read, write, write, read.
MockRead reads[] = {
MockRead(SYNCHRONOUS, 0, kMsg1Span),
MockRead(ASYNC, 3, kMsg2Span),
MockRead(ASYNC, 5, kMsg3Span),
};
MockWrite writes[] = {
MockWrite(ASYNC, 1, kMsg2Span),
MockWrite(SYNCHRONOUS, 2, kMsg3Span),
MockWrite(SYNCHRONOUS, 4, kMsg1Span),
};
Initialize(reads, writes);
// Issue the write, which will block until the read completes.
AssertWriteReturns(kMsg2Span, ERR_IO_PENDING);
// Issue the writes which will complete immediately.
AssertSyncReadEquals(kMsg1Span);
ASSERT_FALSE(write_callback_.have_result());
ASSERT_EQ(kLen2, write_callback_.WaitForResult());
// Issue the read, which will block until the write completes.
AssertReadReturns(kLen2, ERR_IO_PENDING);
// Issue the writes which will complete immediately.
AssertSyncWriteEquals(kMsg3Span);
ASSERT_FALSE(read_callback_.have_result());
ASSERT_EQ(kLen2, read_callback_.WaitForResult());
AssertReadBufferEquals(kMsg2Span);
// Issue the read, which will block until the write completes.
AssertReadReturns(kLen2, ERR_IO_PENDING);
// Issue the writes which will complete immediately.
AssertSyncWriteEquals(kMsg1Span);
ASSERT_FALSE(read_callback_.have_result());
ASSERT_EQ(kLen3, read_callback_.WaitForResult());
AssertReadBufferEquals(kMsg3Span);
}
TEST_F(SequencedSocketDataTest, AsyncReadFromWriteCompletionCallback) {
MockWrite writes[] = {
MockWrite(ASYNC, 0, kMsg1Span),
};
MockRead reads[] = {
MockRead(ASYNC, 1, kMsg2Span),
};
Initialize(reads, writes);
auto write_buf = base::MakeRefCounted<IOBufferWithSize>(kLen1);
write_buf->span().copy_from_nonoverlapping(kMsg1Span);
ASSERT_EQ(
ERR_IO_PENDING,
sock_->Write(
write_buf.get(), kLen1,
base::BindOnce(&SequencedSocketDataTest::ReentrantAsyncReadCallback,
base::Unretained(this), kLen1, kLen2),
TRAFFIC_ANNOTATION_FOR_TESTS));
ASSERT_FALSE(read_callback_.have_result());
ASSERT_EQ(kLen2, read_callback_.WaitForResult());
AssertReadBufferEquals(kMsg2Span);
}
TEST_F(SequencedSocketDataTest, AsyncWriteFromReadCompletionCallback) {
MockWrite writes[] = {
MockWrite(ASYNC, 1, kMsg2Span),
};
MockRead reads[] = {
MockRead(ASYNC, 0, kMsg1Span),
};
Initialize(reads, writes);
auto read_buf = base::MakeRefCounted<IOBufferWithSize>(kLen1);
ASSERT_EQ(
ERR_IO_PENDING,
sock_->Read(
read_buf.get(), kLen1,
base::BindOnce(&SequencedSocketDataTest::ReentrantAsyncWriteCallback,
base::Unretained(this), kMsg2Span,
write_callback_.callback(), kLen1)));
ASSERT_FALSE(write_callback_.have_result());
ASSERT_EQ(kLen2, write_callback_.WaitForResult());
}
TEST_F(SequencedSocketDataTest, MixedReentrantOperations) {
MockWrite writes[] = {
MockWrite(ASYNC, 0, kMsg1Span),
MockWrite(ASYNC, 2, kMsg3Span),
};
MockRead reads[] = {
MockRead(ASYNC, 1, kMsg2Span),
MockRead(ASYNC, 3, kMsg4Span),
};
Initialize(reads, writes);
read_buf_ = base::MakeRefCounted<IOBufferWithSize>(kLen4);
ReentrantHelper helper3(sock_.get());
helper3.SetExpectedWrite(kLen3);
helper3.SetInvokeRead(read_buf_, kLen4, ERR_IO_PENDING,
read_callback_.callback());
ReentrantHelper helper2(sock_.get());
helper2.SetExpectedRead(kMsg2Span);
helper2.SetInvokeWrite(kMsg3Span, ERR_IO_PENDING, helper3.callback());
ReentrantHelper helper(sock_.get());
helper.SetExpectedWrite(kLen1);
helper.SetInvokeRead(helper2.read_buf(), kLen2, ERR_IO_PENDING,
helper2.callback());
auto write_buf = base::MakeRefCounted<IOBufferWithSize>(kLen1);
write_buf->span().copy_from_nonoverlapping(kMsg1Span);
sock_->Write(write_buf.get(), kLen1, helper.callback(),
TRAFFIC_ANNOTATION_FOR_TESTS);
ASSERT_EQ(kLen4, read_callback_.WaitForResult());
}
TEST_F(SequencedSocketDataTest, MixedReentrantOperationsThenSynchronousRead) {
MockWrite writes[] = {
MockWrite(ASYNC, 0, kMsg1Span),
MockWrite(ASYNC, 2, kMsg3Span),
};
MockRead reads[] = {
MockRead(ASYNC, 1, kMsg2Span),
MockRead(SYNCHRONOUS, 3, kMsg4Span),
};
Initialize(reads, writes);
read_buf_ = base::MakeRefCounted<IOBufferWithSize>(kLen4);
ReentrantHelper helper3(sock_.get());
helper3.SetExpectedWrite(kLen3);
helper3.SetInvokeRead(read_buf_, kLen4, kLen4, failing_callback());
ReentrantHelper helper2(sock_.get());
helper2.SetExpectedRead(kMsg2Span);
helper2.SetInvokeWrite(kMsg3Span, ERR_IO_PENDING, helper3.callback());
ReentrantHelper helper(sock_.get());
helper.SetExpectedWrite(kLen1);
helper.SetInvokeRead(helper2.read_buf(), kLen2, ERR_IO_PENDING,
helper2.callback());
auto write_buf = base::MakeRefCounted<IOBufferWithSize>(kLen1);
write_buf->span().copy_from_nonoverlapping(kMsg1Span);
ASSERT_EQ(ERR_IO_PENDING,
sock_->Write(write_buf.get(), kLen1, helper.callback(),
TRAFFIC_ANNOTATION_FOR_TESTS));
base::RunLoop().RunUntilIdle();
AssertReadBufferEquals(kMsg4Span);
}
TEST_F(SequencedSocketDataTest, MixedReentrantOperationsThenSynchronousWrite) {
MockWrite writes[] = {
MockWrite(ASYNC, 1, kMsg2Span),
MockWrite(SYNCHRONOUS, 3, kMsg4Span),
};
MockRead reads[] = {
MockRead(ASYNC, 0, kMsg1Span),
MockRead(ASYNC, 2, kMsg3Span),
};
Initialize(reads, writes);
read_buf_ = base::MakeRefCounted<IOBufferWithSize>(kLen4);
ReentrantHelper helper3(sock_.get());
helper3.SetExpectedRead(kMsg3Span);
helper3.SetInvokeWrite(kMsg4Span, kLen4, failing_callback());
ReentrantHelper helper2(sock_.get());
helper2.SetExpectedWrite(kLen2);
helper2.SetInvokeRead(helper3.read_buf(), kLen3, ERR_IO_PENDING,
helper3.callback());
ReentrantHelper helper(sock_.get());
helper.SetExpectedRead(kMsg1Span);
helper.SetInvokeWrite(kMsg2Span, ERR_IO_PENDING, helper2.callback());
ASSERT_EQ(ERR_IO_PENDING,
sock_->Read(helper.read_buf().get(), kLen1, helper.callback()));
base::RunLoop().RunUntilIdle();
}
// Test the basic case where a read is paused.
TEST_F(SequencedSocketDataTest, PauseAndResume_PauseRead) {
MockRead reads[] = {
MockRead(ASYNC, ERR_IO_PENDING, 0),
MockRead(ASYNC, 1, kMsg1Span),
};
Initialize(reads, base::span<MockWrite>());
AssertReadReturns(kLen1, ERR_IO_PENDING);
ASSERT_FALSE(read_callback_.have_result());
RunUntilPaused();
ASSERT_TRUE(IsPaused());
// Spinning the message loop should do nothing.
base::RunLoop().RunUntilIdle();
ASSERT_FALSE(read_callback_.have_result());
ASSERT_TRUE(IsPaused());
Resume();
ASSERT_FALSE(IsPaused());
ASSERT_TRUE(read_callback_.have_result());
ASSERT_EQ(kLen1, read_callback_.WaitForResult());
AssertReadBufferEquals(kMsg1Span);
}
// Test the case where a read that will be paused is started before write that
// completes before the pause.
TEST_F(SequencedSocketDataTest, PauseAndResume_WritePauseRead) {
MockWrite writes[] = {
MockWrite(SYNCHRONOUS, 0, kMsg1Span),
};
MockRead reads[] = {
MockRead(ASYNC, ERR_IO_PENDING, 1),
MockRead(ASYNC, 2, kMsg2Span),
};
Initialize(reads, writes);
AssertReadReturns(kLen2, ERR_IO_PENDING);
ASSERT_FALSE(read_callback_.have_result());
// Nothing should happen until the write starts.
base::RunLoop().RunUntilIdle();
ASSERT_FALSE(read_callback_.have_result());
ASSERT_FALSE(IsPaused());
AssertSyncWriteEquals(kMsg1Span);
RunUntilPaused();
ASSERT_FALSE(read_callback_.have_result());
ASSERT_TRUE(IsPaused());
// Spinning the message loop should do nothing.
base::RunLoop().RunUntilIdle();
ASSERT_FALSE(read_callback_.have_result());
ASSERT_TRUE(IsPaused());
Resume();
ASSERT_FALSE(IsPaused());
ASSERT_TRUE(read_callback_.have_result());
ASSERT_EQ(kLen2, read_callback_.WaitForResult());
AssertReadBufferEquals(kMsg2Span);
}
// Test the basic case where a write is paused.
TEST_F(SequencedSocketDataTest, PauseAndResume_PauseWrite) {
MockWrite writes[] = {
MockWrite(ASYNC, ERR_IO_PENDING, 0),
MockWrite(ASYNC, 1, kMsg1Span),
};
Initialize(base::span<MockRead>(), writes);
AssertWriteReturns(kMsg1Span, ERR_IO_PENDING);
ASSERT_FALSE(write_callback_.have_result());
RunUntilPaused();
ASSERT_TRUE(IsPaused());
// Spinning the message loop should do nothing.
base::RunLoop().RunUntilIdle();
ASSERT_FALSE(write_callback_.have_result());
ASSERT_TRUE(IsPaused());
Resume();
ASSERT_FALSE(IsPaused());
ASSERT_TRUE(write_callback_.have_result());
ASSERT_EQ(kLen1, write_callback_.WaitForResult());
}
// Test the case where a write that will be paused is started before read that
// completes before the pause.
TEST_F(SequencedSocketDataTest, PauseAndResume_ReadPauseWrite) {
MockWrite writes[] = {
MockWrite(ASYNC, ERR_IO_PENDING, 1),
MockWrite(ASYNC, 2, kMsg2Span),
};
MockRead reads[] = {
MockRead(SYNCHRONOUS, 0, kMsg1Span),
};
Initialize(reads, writes);
AssertWriteReturns(kMsg2Span, ERR_IO_PENDING);
ASSERT_FALSE(write_callback_.have_result());
// Nothing should happen until the write starts.
base::RunLoop().RunUntilIdle();
ASSERT_FALSE(write_callback_.have_result());
ASSERT_FALSE(IsPaused());
AssertSyncReadEquals(kMsg1Span);
RunUntilPaused();
ASSERT_FALSE(write_callback_.have_result());
ASSERT_TRUE(IsPaused());
// Spinning the message loop should do nothing.
base::RunLoop().RunUntilIdle();
ASSERT_FALSE(write_callback_.have_result());
ASSERT_TRUE(IsPaused());
Resume();
ASSERT_FALSE(IsPaused());
ASSERT_TRUE(write_callback_.have_result());
ASSERT_EQ(kLen2, write_callback_.WaitForResult());
}
} // namespace
} // namespace net