You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
525 lines
16 KiB
525 lines
16 KiB
// Copyright 2015 The Chromium OS 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 <brillo/streams/fake_stream.h>
|
|
|
|
#include <memory>
|
|
#include <vector>
|
|
|
|
#include <base/bind.h>
|
|
#include <base/callback.h>
|
|
#include <base/test/simple_test_clock.h>
|
|
#include <brillo/message_loops/mock_message_loop.h>
|
|
#include <gmock/gmock.h>
|
|
#include <gtest/gtest.h>
|
|
|
|
using testing::AnyNumber;
|
|
using testing::InSequence;
|
|
using testing::_;
|
|
|
|
namespace brillo {
|
|
|
|
class FakeStreamTest : public testing::Test {
|
|
public:
|
|
void SetUp() override {
|
|
mock_loop_.SetAsCurrent();
|
|
// Ignore calls to RunOnce().
|
|
EXPECT_CALL(mock_loop_, RunOnce(true)).Times(AnyNumber());
|
|
}
|
|
|
|
void CreateStream(Stream::AccessMode mode) {
|
|
stream_.reset(new FakeStream{mode, &clock_});
|
|
}
|
|
|
|
// Performs non-blocking read on the stream and returns the read data
|
|
// as a string in |out_buffer|. Returns true if the read was successful or
|
|
// false when an error occurs. |*eos| is set to true when end of stream is
|
|
// reached.
|
|
bool ReadString(size_t size_to_read, std::string* out_buffer, bool* eos) {
|
|
std::vector<char> data;
|
|
data.resize(size_to_read);
|
|
size_t size_read = 0;
|
|
bool ok = stream_->ReadNonBlocking(data.data(), data.size(), &size_read,
|
|
eos, nullptr);
|
|
if (ok) {
|
|
out_buffer->assign(data.data(), data.data() + size_read);
|
|
} else {
|
|
out_buffer->clear();
|
|
}
|
|
return ok;
|
|
}
|
|
|
|
// Writes a string to a stream. Returns the number of bytes written or -1
|
|
// in case an error occurred.
|
|
int WriteString(const std::string& data) {
|
|
size_t written = 0;
|
|
if (!stream_->WriteNonBlocking(data.data(), data.size(), &written, nullptr))
|
|
return -1;
|
|
return static_cast<int>(written);
|
|
}
|
|
|
|
protected:
|
|
base::SimpleTestClock clock_;
|
|
MockMessageLoop mock_loop_{&clock_};
|
|
std::unique_ptr<FakeStream> stream_;
|
|
const base::TimeDelta zero_delay;
|
|
};
|
|
|
|
TEST_F(FakeStreamTest, InitReadOnly) {
|
|
CreateStream(Stream::AccessMode::READ);
|
|
EXPECT_TRUE(stream_->IsOpen());
|
|
EXPECT_TRUE(stream_->CanRead());
|
|
EXPECT_FALSE(stream_->CanWrite());
|
|
EXPECT_FALSE(stream_->CanSeek());
|
|
EXPECT_FALSE(stream_->CanGetSize());
|
|
EXPECT_EQ(0, stream_->GetSize());
|
|
EXPECT_EQ(0, stream_->GetRemainingSize());
|
|
EXPECT_EQ(0, stream_->GetPosition());
|
|
}
|
|
|
|
TEST_F(FakeStreamTest, InitWriteOnly) {
|
|
CreateStream(Stream::AccessMode::WRITE);
|
|
EXPECT_TRUE(stream_->IsOpen());
|
|
EXPECT_FALSE(stream_->CanRead());
|
|
EXPECT_TRUE(stream_->CanWrite());
|
|
EXPECT_FALSE(stream_->CanSeek());
|
|
EXPECT_FALSE(stream_->CanGetSize());
|
|
EXPECT_EQ(0, stream_->GetSize());
|
|
EXPECT_EQ(0, stream_->GetRemainingSize());
|
|
EXPECT_EQ(0, stream_->GetPosition());
|
|
}
|
|
|
|
TEST_F(FakeStreamTest, InitReadWrite) {
|
|
CreateStream(Stream::AccessMode::READ_WRITE);
|
|
EXPECT_TRUE(stream_->IsOpen());
|
|
EXPECT_TRUE(stream_->CanRead());
|
|
EXPECT_TRUE(stream_->CanWrite());
|
|
EXPECT_FALSE(stream_->CanSeek());
|
|
EXPECT_FALSE(stream_->CanGetSize());
|
|
EXPECT_EQ(0, stream_->GetSize());
|
|
EXPECT_EQ(0, stream_->GetRemainingSize());
|
|
EXPECT_EQ(0, stream_->GetPosition());
|
|
}
|
|
|
|
TEST_F(FakeStreamTest, ReadEmpty) {
|
|
CreateStream(Stream::AccessMode::READ);
|
|
std::string data;
|
|
bool eos = false;
|
|
EXPECT_TRUE(ReadString(100, &data, &eos));
|
|
EXPECT_TRUE(eos);
|
|
EXPECT_TRUE(data.empty());
|
|
}
|
|
|
|
TEST_F(FakeStreamTest, ReadFullPacket) {
|
|
CreateStream(Stream::AccessMode::READ);
|
|
stream_->AddReadPacketString({}, "foo");
|
|
std::string data;
|
|
bool eos = false;
|
|
EXPECT_TRUE(ReadString(100, &data, &eos));
|
|
EXPECT_FALSE(eos);
|
|
EXPECT_EQ("foo", data);
|
|
|
|
EXPECT_TRUE(ReadString(100, &data, &eos));
|
|
EXPECT_TRUE(eos);
|
|
EXPECT_TRUE(data.empty());
|
|
}
|
|
|
|
TEST_F(FakeStreamTest, ReadPartialPacket) {
|
|
CreateStream(Stream::AccessMode::READ);
|
|
stream_->AddReadPacketString({}, "foobar");
|
|
std::string data;
|
|
bool eos = false;
|
|
EXPECT_TRUE(ReadString(3, &data, &eos));
|
|
EXPECT_FALSE(eos);
|
|
EXPECT_EQ("foo", data);
|
|
|
|
EXPECT_TRUE(ReadString(100, &data, &eos));
|
|
EXPECT_FALSE(eos);
|
|
EXPECT_EQ("bar", data);
|
|
|
|
EXPECT_TRUE(ReadString(100, &data, &eos));
|
|
EXPECT_TRUE(eos);
|
|
EXPECT_TRUE(data.empty());
|
|
}
|
|
|
|
TEST_F(FakeStreamTest, ReadMultiplePackets) {
|
|
CreateStream(Stream::AccessMode::READ);
|
|
stream_->AddReadPacketString({}, "foobar");
|
|
stream_->AddReadPacketString({}, "baz");
|
|
stream_->AddReadPacketString({}, "quux");
|
|
std::string data;
|
|
bool eos = false;
|
|
EXPECT_TRUE(ReadString(100, &data, &eos));
|
|
EXPECT_FALSE(eos);
|
|
EXPECT_EQ("foobar", data);
|
|
|
|
EXPECT_TRUE(ReadString(2, &data, &eos));
|
|
EXPECT_FALSE(eos);
|
|
EXPECT_EQ("ba", data);
|
|
|
|
EXPECT_TRUE(ReadString(100, &data, &eos));
|
|
EXPECT_FALSE(eos);
|
|
EXPECT_EQ("z", data);
|
|
|
|
EXPECT_TRUE(ReadString(100, &data, &eos));
|
|
EXPECT_FALSE(eos);
|
|
EXPECT_EQ("quux", data);
|
|
|
|
EXPECT_TRUE(ReadString(100, &data, &eos));
|
|
EXPECT_TRUE(eos);
|
|
EXPECT_TRUE(data.empty());
|
|
|
|
stream_->AddReadPacketString({}, "foo-bar");
|
|
EXPECT_TRUE(ReadString(100, &data, &eos));
|
|
EXPECT_FALSE(eos);
|
|
EXPECT_EQ("foo-bar", data);
|
|
}
|
|
|
|
TEST_F(FakeStreamTest, ReadPacketsWithDelay) {
|
|
CreateStream(Stream::AccessMode::READ);
|
|
stream_->AddReadPacketString({}, "foobar");
|
|
stream_->AddReadPacketString(base::TimeDelta::FromSeconds(1), "baz");
|
|
std::string data;
|
|
bool eos = false;
|
|
EXPECT_TRUE(ReadString(100, &data, &eos));
|
|
EXPECT_FALSE(eos);
|
|
EXPECT_EQ("foobar", data);
|
|
|
|
EXPECT_TRUE(ReadString(100, &data, &eos));
|
|
EXPECT_FALSE(eos);
|
|
EXPECT_TRUE(data.empty());
|
|
|
|
EXPECT_TRUE(ReadString(100, &data, &eos));
|
|
EXPECT_FALSE(eos);
|
|
EXPECT_TRUE(data.empty());
|
|
|
|
clock_.Advance(base::TimeDelta::FromSeconds(1));
|
|
|
|
EXPECT_TRUE(ReadString(100, &data, &eos));
|
|
EXPECT_FALSE(eos);
|
|
EXPECT_EQ("baz", data);
|
|
}
|
|
|
|
TEST_F(FakeStreamTest, ReadPacketsWithError) {
|
|
CreateStream(Stream::AccessMode::READ);
|
|
stream_->AddReadPacketString({}, "foobar");
|
|
stream_->QueueReadErrorWithMessage(base::TimeDelta::FromSeconds(1),
|
|
"Dummy error");
|
|
stream_->AddReadPacketString({}, "baz");
|
|
|
|
std::string data;
|
|
bool eos = false;
|
|
EXPECT_TRUE(ReadString(100, &data, &eos));
|
|
EXPECT_FALSE(eos);
|
|
EXPECT_EQ("foobar", data);
|
|
|
|
EXPECT_TRUE(ReadString(100, &data, &eos));
|
|
EXPECT_FALSE(eos);
|
|
EXPECT_TRUE(data.empty());
|
|
|
|
EXPECT_TRUE(ReadString(100, &data, &eos));
|
|
EXPECT_FALSE(eos);
|
|
EXPECT_TRUE(data.empty());
|
|
|
|
clock_.Advance(base::TimeDelta::FromSeconds(1));
|
|
|
|
EXPECT_FALSE(ReadString(100, &data, &eos));
|
|
|
|
EXPECT_TRUE(ReadString(100, &data, &eos));
|
|
EXPECT_FALSE(eos);
|
|
EXPECT_EQ("baz", data);
|
|
}
|
|
|
|
TEST_F(FakeStreamTest, WaitForDataRead) {
|
|
CreateStream(Stream::AccessMode::READ);
|
|
|
|
EXPECT_CALL(mock_loop_, PostDelayedTask(_, _, zero_delay)).Times(2);
|
|
|
|
int call_count = 0;
|
|
auto callback = base::Bind([](int* call_count, Stream::AccessMode mode) {
|
|
(*call_count)++;
|
|
EXPECT_EQ(Stream::AccessMode::READ, mode);
|
|
}, &call_count);
|
|
|
|
EXPECT_TRUE(stream_->WaitForData(Stream::AccessMode::READ,
|
|
callback, nullptr));
|
|
mock_loop_.Run();
|
|
EXPECT_EQ(1, call_count);
|
|
|
|
stream_->AddReadPacketString({}, "foobar");
|
|
EXPECT_TRUE(stream_->WaitForData(Stream::AccessMode::READ,
|
|
callback, nullptr));
|
|
mock_loop_.Run();
|
|
EXPECT_EQ(2, call_count);
|
|
|
|
stream_->ClearReadQueue();
|
|
|
|
auto one_sec_delay = base::TimeDelta::FromSeconds(1);
|
|
stream_->AddReadPacketString(one_sec_delay, "baz");
|
|
EXPECT_CALL(mock_loop_, PostDelayedTask(_, _, one_sec_delay)).Times(1);
|
|
EXPECT_TRUE(stream_->WaitForData(Stream::AccessMode::READ,
|
|
callback, nullptr));
|
|
mock_loop_.Run();
|
|
EXPECT_EQ(3, call_count);
|
|
}
|
|
|
|
TEST_F(FakeStreamTest, ReadAsync) {
|
|
CreateStream(Stream::AccessMode::READ);
|
|
std::string input_data = "foobar-baz";
|
|
size_t split_pos = input_data.find('-');
|
|
|
|
auto one_sec_delay = base::TimeDelta::FromSeconds(1);
|
|
stream_->AddReadPacketString({}, input_data.substr(0, split_pos));
|
|
stream_->AddReadPacketString(one_sec_delay, input_data.substr(split_pos));
|
|
|
|
{
|
|
InSequence seq;
|
|
EXPECT_CALL(mock_loop_, PostDelayedTask(_, _, zero_delay)).Times(1);
|
|
EXPECT_CALL(mock_loop_, PostDelayedTask(_, _, one_sec_delay)).Times(1);
|
|
}
|
|
|
|
std::vector<char> buffer;
|
|
buffer.resize(input_data.size());
|
|
|
|
int success_count = 0;
|
|
int error_count = 0;
|
|
auto on_success = [](int* success_count) { (*success_count)++; };
|
|
auto on_failure = [](int* error_count, const Error* /* error */) {
|
|
(*error_count)++;
|
|
};
|
|
|
|
EXPECT_TRUE(stream_->ReadAllAsync(
|
|
buffer.data(),
|
|
buffer.size(),
|
|
base::Bind(on_success, base::Unretained(&success_count)),
|
|
base::Bind(on_failure, base::Unretained(&error_count)),
|
|
nullptr));
|
|
mock_loop_.Run();
|
|
EXPECT_EQ(1, success_count);
|
|
EXPECT_EQ(0, error_count);
|
|
EXPECT_EQ(input_data, (std::string{buffer.begin(), buffer.end()}));
|
|
}
|
|
|
|
TEST_F(FakeStreamTest, WriteEmpty) {
|
|
CreateStream(Stream::AccessMode::WRITE);
|
|
EXPECT_EQ(-1, WriteString("foo"));
|
|
}
|
|
|
|
TEST_F(FakeStreamTest, WritePartial) {
|
|
CreateStream(Stream::AccessMode::WRITE);
|
|
stream_->ExpectWritePacketSize({}, 6);
|
|
EXPECT_EQ(3, WriteString("foo"));
|
|
EXPECT_EQ(3, WriteString("bar"));
|
|
EXPECT_EQ(-1, WriteString("baz"));
|
|
|
|
EXPECT_EQ("foobar", stream_->GetFlushedOutputDataAsString());
|
|
}
|
|
|
|
TEST_F(FakeStreamTest, WriteFullPackets) {
|
|
CreateStream(Stream::AccessMode::WRITE);
|
|
|
|
stream_->ExpectWritePacketSize({}, 3);
|
|
EXPECT_EQ(3, WriteString("foo"));
|
|
EXPECT_EQ(-1, WriteString("bar"));
|
|
|
|
stream_->ExpectWritePacketSize({}, 3);
|
|
EXPECT_EQ(3, WriteString("bar"));
|
|
|
|
stream_->ExpectWritePacketSize({}, 3);
|
|
EXPECT_EQ(3, WriteString("quux"));
|
|
|
|
EXPECT_EQ("foobarquu", stream_->GetFlushedOutputDataAsString());
|
|
}
|
|
|
|
TEST_F(FakeStreamTest, WriteAndVerifyData) {
|
|
CreateStream(Stream::AccessMode::WRITE);
|
|
|
|
stream_->ExpectWritePacketString({}, "foo");
|
|
stream_->ExpectWritePacketString({}, "bar");
|
|
EXPECT_EQ(3, WriteString("foobar"));
|
|
EXPECT_EQ(3, WriteString("bar"));
|
|
|
|
stream_->ExpectWritePacketString({}, "foo");
|
|
stream_->ExpectWritePacketString({}, "baz");
|
|
EXPECT_EQ(3, WriteString("foobar"));
|
|
EXPECT_EQ(-1, WriteString("bar"));
|
|
|
|
stream_->ExpectWritePacketString({}, "foobar");
|
|
EXPECT_EQ(3, WriteString("foo"));
|
|
EXPECT_EQ(2, WriteString("ba"));
|
|
EXPECT_EQ(-1, WriteString("z"));
|
|
}
|
|
|
|
TEST_F(FakeStreamTest, WriteWithDelay) {
|
|
CreateStream(Stream::AccessMode::WRITE);
|
|
|
|
const auto delay = base::TimeDelta::FromMilliseconds(500);
|
|
|
|
stream_->ExpectWritePacketSize({}, 3);
|
|
stream_->ExpectWritePacketSize(delay, 3);
|
|
EXPECT_EQ(3, WriteString("foobar"));
|
|
|
|
EXPECT_EQ(0, WriteString("bar"));
|
|
EXPECT_EQ(0, WriteString("bar"));
|
|
clock_.Advance(delay);
|
|
EXPECT_EQ(3, WriteString("bar"));
|
|
|
|
EXPECT_EQ("foobar", stream_->GetFlushedOutputDataAsString());
|
|
}
|
|
|
|
TEST_F(FakeStreamTest, WriteWithError) {
|
|
CreateStream(Stream::AccessMode::WRITE);
|
|
|
|
const auto delay = base::TimeDelta::FromMilliseconds(500);
|
|
|
|
stream_->ExpectWritePacketSize({}, 3);
|
|
stream_->QueueWriteError({});
|
|
stream_->ExpectWritePacketSize({}, 3);
|
|
stream_->QueueWriteErrorWithMessage(delay, "Dummy message");
|
|
stream_->ExpectWritePacketString({}, "foobar");
|
|
|
|
const std::string data = "foobarbaz";
|
|
EXPECT_EQ(3, WriteString(data));
|
|
EXPECT_EQ(-1, WriteString(data)); // Simulated error #1.
|
|
EXPECT_EQ(3, WriteString(data));
|
|
EXPECT_EQ(0, WriteString(data)); // Waiting for data...
|
|
clock_.Advance(delay);
|
|
EXPECT_EQ(-1, WriteString(data)); // Simulated error #2.
|
|
EXPECT_EQ(6, WriteString(data));
|
|
EXPECT_EQ(-1, WriteString(data)); // No more data expected.
|
|
}
|
|
|
|
TEST_F(FakeStreamTest, WaitForDataWrite) {
|
|
CreateStream(Stream::AccessMode::WRITE);
|
|
|
|
EXPECT_CALL(mock_loop_, PostDelayedTask(_, _, zero_delay)).Times(2);
|
|
|
|
int call_count = 0;
|
|
auto callback = base::Bind([](int* call_count, Stream::AccessMode mode) {
|
|
(*call_count)++;
|
|
EXPECT_EQ(Stream::AccessMode::WRITE, mode);
|
|
}, &call_count);
|
|
|
|
EXPECT_TRUE(stream_->WaitForData(Stream::AccessMode::WRITE,
|
|
callback, nullptr));
|
|
mock_loop_.Run();
|
|
EXPECT_EQ(1, call_count);
|
|
|
|
stream_->ExpectWritePacketString({}, "foobar");
|
|
EXPECT_TRUE(stream_->WaitForData(Stream::AccessMode::WRITE,
|
|
callback, nullptr));
|
|
mock_loop_.Run();
|
|
EXPECT_EQ(2, call_count);
|
|
|
|
stream_->ClearWriteQueue();
|
|
|
|
auto one_sec_delay = base::TimeDelta::FromSeconds(1);
|
|
stream_->ExpectWritePacketString(one_sec_delay, "baz");
|
|
EXPECT_CALL(mock_loop_, PostDelayedTask(_, _, one_sec_delay)).Times(1);
|
|
EXPECT_TRUE(stream_->WaitForData(Stream::AccessMode::WRITE,
|
|
callback, nullptr));
|
|
mock_loop_.Run();
|
|
EXPECT_EQ(3, call_count);
|
|
}
|
|
|
|
TEST_F(FakeStreamTest, WriteAsync) {
|
|
CreateStream(Stream::AccessMode::WRITE);
|
|
std::string output_data = "foobar-baz";
|
|
size_t split_pos = output_data.find('-');
|
|
|
|
auto one_sec_delay = base::TimeDelta::FromSeconds(1);
|
|
stream_->ExpectWritePacketString({}, output_data.substr(0, split_pos));
|
|
stream_->ExpectWritePacketString(one_sec_delay,
|
|
output_data.substr(split_pos));
|
|
|
|
{
|
|
InSequence seq;
|
|
EXPECT_CALL(mock_loop_, PostDelayedTask(_, _, zero_delay)).Times(1);
|
|
EXPECT_CALL(mock_loop_, PostDelayedTask(_, _, one_sec_delay)).Times(1);
|
|
}
|
|
|
|
int success_count = 0;
|
|
int error_count = 0;
|
|
auto on_success = [](int* success_count) { (*success_count)++; };
|
|
auto on_failure = [](int* error_count, const Error* /* error */) {
|
|
(*error_count)++;
|
|
};
|
|
|
|
EXPECT_TRUE(stream_->WriteAllAsync(
|
|
output_data.data(),
|
|
output_data.size(),
|
|
base::Bind(on_success, base::Unretained(&success_count)),
|
|
base::Bind(on_failure, base::Unretained(&error_count)),
|
|
nullptr));
|
|
mock_loop_.Run();
|
|
EXPECT_EQ(1, success_count);
|
|
EXPECT_EQ(0, error_count);
|
|
EXPECT_EQ(output_data, stream_->GetFlushedOutputDataAsString());
|
|
}
|
|
|
|
TEST_F(FakeStreamTest, WaitForDataReadWrite) {
|
|
CreateStream(Stream::AccessMode::READ_WRITE);
|
|
auto one_sec_delay = base::TimeDelta::FromSeconds(1);
|
|
auto two_sec_delay = base::TimeDelta::FromSeconds(2);
|
|
|
|
int call_count = 0;
|
|
auto callback = [](int* call_count,
|
|
Stream::AccessMode mode,
|
|
Stream::AccessMode expected_mode) {
|
|
(*call_count)++;
|
|
EXPECT_EQ(static_cast<int>(expected_mode), static_cast<int>(mode));
|
|
};
|
|
|
|
stream_->AddReadPacketString(one_sec_delay, "foo");
|
|
stream_->ExpectWritePacketString(two_sec_delay, "bar");
|
|
|
|
EXPECT_CALL(mock_loop_, PostDelayedTask(_, _, one_sec_delay)).Times(1);
|
|
EXPECT_TRUE(stream_->WaitForData(Stream::AccessMode::READ_WRITE,
|
|
base::Bind(callback,
|
|
base::Unretained(&call_count),
|
|
Stream::AccessMode::READ),
|
|
nullptr));
|
|
mock_loop_.Run();
|
|
EXPECT_EQ(1, call_count);
|
|
|
|
// The above step has adjusted the clock by 1 second already.
|
|
stream_->ClearReadQueue();
|
|
stream_->AddReadPacketString(two_sec_delay, "foo");
|
|
EXPECT_CALL(mock_loop_, PostDelayedTask(_, _, one_sec_delay)).Times(1);
|
|
EXPECT_TRUE(stream_->WaitForData(Stream::AccessMode::READ_WRITE,
|
|
base::Bind(callback,
|
|
base::Unretained(&call_count),
|
|
Stream::AccessMode::WRITE),
|
|
nullptr));
|
|
mock_loop_.Run();
|
|
EXPECT_EQ(2, call_count);
|
|
|
|
clock_.Advance(one_sec_delay);
|
|
|
|
EXPECT_CALL(mock_loop_, PostDelayedTask(_, _, zero_delay)).Times(1);
|
|
EXPECT_TRUE(stream_->WaitForData(Stream::AccessMode::READ_WRITE,
|
|
base::Bind(callback,
|
|
base::Unretained(&call_count),
|
|
Stream::AccessMode::READ_WRITE),
|
|
nullptr));
|
|
mock_loop_.Run();
|
|
EXPECT_EQ(3, call_count);
|
|
|
|
stream_->ClearReadQueue();
|
|
stream_->ClearWriteQueue();
|
|
stream_->AddReadPacketString(one_sec_delay, "foo");
|
|
stream_->ExpectWritePacketString(one_sec_delay, "bar");
|
|
|
|
EXPECT_CALL(mock_loop_, PostDelayedTask(_, _, one_sec_delay)).Times(1);
|
|
EXPECT_TRUE(stream_->WaitForData(Stream::AccessMode::READ_WRITE,
|
|
base::Bind(callback,
|
|
base::Unretained(&call_count),
|
|
Stream::AccessMode::READ_WRITE),
|
|
nullptr));
|
|
mock_loop_.Run();
|
|
EXPECT_EQ(4, call_count);
|
|
}
|
|
|
|
} // namespace brillo
|