summaryrefslogtreecommitdiff
path: root/brillo/streams/fake_stream_unittest.cc
diff options
context:
space:
mode:
Diffstat (limited to 'brillo/streams/fake_stream_unittest.cc')
-rw-r--r--brillo/streams/fake_stream_unittest.cc510
1 files changed, 510 insertions, 0 deletions
diff --git a/brillo/streams/fake_stream_unittest.cc b/brillo/streams/fake_stream_unittest.cc
new file mode 100644
index 0000000..2aa18fd
--- /dev/null
+++ b/brillo/streams/fake_stream_unittest.cc
@@ -0,0 +1,510 @@
+// 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 <vector>
+
+#include <base/callback.h>
+#include <base/test/simple_test_clock.h>
+#include <brillo/bind_lambda.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 = [&call_count](Stream::AccessMode mode) {
+ call_count++;
+ EXPECT_EQ(Stream::AccessMode::READ, mode);
+ };
+
+ EXPECT_TRUE(stream_->WaitForData(Stream::AccessMode::READ,
+ base::Bind(callback), nullptr));
+ mock_loop_.Run();
+ EXPECT_EQ(1, call_count);
+
+ stream_->AddReadPacketString({}, "foobar");
+ EXPECT_TRUE(stream_->WaitForData(Stream::AccessMode::READ,
+ base::Bind(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,
+ base::Bind(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 = [&success_count] { success_count++; };
+ auto on_failure = [&error_count](const Error* error) { error_count++; };
+
+ EXPECT_TRUE(stream_->ReadAllAsync(buffer.data(), buffer.size(),
+ base::Bind(on_success),
+ base::Bind(on_failure),
+ 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 = [&call_count](Stream::AccessMode mode) {
+ call_count++;
+ EXPECT_EQ(Stream::AccessMode::WRITE, mode);
+ };
+
+ EXPECT_TRUE(stream_->WaitForData(Stream::AccessMode::WRITE,
+ base::Bind(callback), nullptr));
+ mock_loop_.Run();
+ EXPECT_EQ(1, call_count);
+
+ stream_->ExpectWritePacketString({}, "foobar");
+ EXPECT_TRUE(stream_->WaitForData(Stream::AccessMode::WRITE,
+ base::Bind(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,
+ base::Bind(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 = [&success_count] { success_count++; };
+ auto on_failure = [&error_count](const Error* error) { error_count++; };
+
+ EXPECT_TRUE(stream_->WriteAllAsync(output_data.data(), output_data.size(),
+ base::Bind(on_success),
+ base::Bind(on_failure),
+ 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 = [&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,
+ 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,
+ 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,
+ 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,
+ Stream::AccessMode::READ_WRITE),
+ nullptr));
+ mock_loop_.Run();
+ EXPECT_EQ(4, call_count);
+}
+
+} // namespace brillo