| // 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/stream.h> |
| |
| #include <limits> |
| |
| #include <base/callback.h> |
| #include <gmock/gmock.h> |
| #include <gtest/gtest.h> |
| |
| #include <brillo/bind_lambda.h> |
| #include <brillo/message_loops/fake_message_loop.h> |
| #include <brillo/streams/stream_errors.h> |
| |
| using testing::DoAll; |
| using testing::InSequence; |
| using testing::Return; |
| using testing::SaveArg; |
| using testing::SetArgPointee; |
| using testing::_; |
| |
| namespace brillo { |
| |
| using AccessMode = Stream::AccessMode; |
| using Whence = Stream::Whence; |
| |
| // To verify "non-trivial" methods implemented in Stream, mock out the |
| // "trivial" methods to make sure the ones we are interested in testing |
| // actually end up calling the expected methods with right parameters. |
| class MockStreamImpl : public Stream { |
| public: |
| MockStreamImpl() = default; |
| |
| MOCK_CONST_METHOD0(IsOpen, bool()); |
| MOCK_CONST_METHOD0(CanRead, bool()); |
| MOCK_CONST_METHOD0(CanWrite, bool()); |
| MOCK_CONST_METHOD0(CanSeek, bool()); |
| MOCK_CONST_METHOD0(CanGetSize, bool()); |
| |
| MOCK_CONST_METHOD0(GetSize, uint64_t()); |
| MOCK_METHOD2(SetSizeBlocking, bool(uint64_t, ErrorPtr*)); |
| MOCK_CONST_METHOD0(GetRemainingSize, uint64_t()); |
| |
| MOCK_CONST_METHOD0(GetPosition, uint64_t()); |
| MOCK_METHOD4(Seek, bool(int64_t, Whence, uint64_t*, ErrorPtr*)); |
| |
| // Omitted: ReadAsync |
| // Omitted: ReadAllAsync |
| MOCK_METHOD5(ReadNonBlocking, bool(void*, size_t, size_t*, bool*, ErrorPtr*)); |
| // Omitted: ReadBlocking |
| // Omitted: ReadAllBlocking |
| |
| // Omitted: WriteAsync |
| // Omitted: WriteAllAsync |
| MOCK_METHOD4(WriteNonBlocking, bool(const void*, size_t, size_t*, ErrorPtr*)); |
| // Omitted: WriteBlocking |
| // Omitted: WriteAllBlocking |
| |
| MOCK_METHOD1(FlushBlocking, bool(ErrorPtr*)); |
| MOCK_METHOD1(CloseBlocking, bool(ErrorPtr*)); |
| |
| MOCK_METHOD3(WaitForData, bool(AccessMode, |
| const base::Callback<void(AccessMode)>&, |
| ErrorPtr*)); |
| MOCK_METHOD4(WaitForDataBlocking, |
| bool(AccessMode, base::TimeDelta, AccessMode*, ErrorPtr*)); |
| |
| private: |
| DISALLOW_COPY_AND_ASSIGN(MockStreamImpl); |
| }; |
| |
| TEST(Stream, TruncateBlocking) { |
| MockStreamImpl stream_mock; |
| EXPECT_CALL(stream_mock, GetPosition()).WillOnce(Return(123)); |
| EXPECT_CALL(stream_mock, SetSizeBlocking(123, _)).WillOnce(Return(true)); |
| EXPECT_TRUE(stream_mock.TruncateBlocking(nullptr)); |
| } |
| |
| TEST(Stream, SetPosition) { |
| MockStreamImpl stream_mock; |
| EXPECT_CALL(stream_mock, Seek(12345, Whence::FROM_BEGIN, _, _)) |
| .WillOnce(Return(true)); |
| EXPECT_TRUE(stream_mock.SetPosition(12345, nullptr)); |
| |
| // Test too large an offset (that doesn't fit in signed 64 bit value). |
| ErrorPtr error; |
| uint64_t max_offset = std::numeric_limits<int64_t>::max(); |
| EXPECT_CALL(stream_mock, Seek(max_offset, _, _, _)) |
| .WillOnce(Return(true)); |
| EXPECT_TRUE(stream_mock.SetPosition(max_offset, nullptr)); |
| |
| EXPECT_FALSE(stream_mock.SetPosition(max_offset + 1, &error)); |
| EXPECT_EQ(errors::stream::kDomain, error->GetDomain()); |
| EXPECT_EQ(errors::stream::kInvalidParameter, error->GetCode()); |
| } |
| |
| TEST(Stream, ReadAsync) { |
| size_t read_size = 0; |
| bool succeeded = false; |
| bool failed = false; |
| auto success_callback = [&read_size, &succeeded](size_t size) { |
| read_size = size; |
| succeeded = true; |
| }; |
| auto error_callback = [&failed](const Error* error) { failed = true; }; |
| |
| MockStreamImpl stream_mock; |
| base::Callback<void(AccessMode)> data_callback; |
| char buf[10]; |
| |
| // This sets up an initial non blocking read that would block, so ReadAsync() |
| // should wait for more data. |
| EXPECT_CALL(stream_mock, ReadNonBlocking(buf, 10, _, _, _)) |
| .WillOnce( |
| DoAll(SetArgPointee<2>(0), SetArgPointee<3>(false), Return(true))); |
| EXPECT_CALL(stream_mock, WaitForData(AccessMode::READ, _, _)) |
| .WillOnce(DoAll(SaveArg<1>(&data_callback), Return(true))); |
| EXPECT_TRUE(stream_mock.ReadAsync(buf, sizeof(buf), |
| base::Bind(success_callback), |
| base::Bind(error_callback), nullptr)); |
| EXPECT_EQ(0u, read_size); |
| EXPECT_FALSE(succeeded); |
| EXPECT_FALSE(failed); |
| |
| // Since the previous call is waiting for the data to be available, we can't |
| // schedule another read. |
| ErrorPtr error; |
| EXPECT_FALSE(stream_mock.ReadAsync(buf, sizeof(buf), |
| base::Bind(success_callback), |
| base::Bind(error_callback), &error)); |
| EXPECT_EQ(errors::stream::kDomain, error->GetDomain()); |
| EXPECT_EQ(errors::stream::kOperationNotSupported, error->GetCode()); |
| EXPECT_EQ("Another asynchronous operation is still pending", |
| error->GetMessage()); |
| |
| // Making the data available via data_callback should not schedule the |
| // success callback from the main loop and run it directly instead. |
| EXPECT_CALL(stream_mock, ReadNonBlocking(buf, 10, _, _, _)) |
| .WillOnce(DoAll(SetArgPointee<2>(7), |
| SetArgPointee<3>(false), |
| Return(true))); |
| data_callback.Run(AccessMode::READ); |
| EXPECT_EQ(7u, read_size); |
| EXPECT_FALSE(failed); |
| } |
| |
| TEST(Stream, ReadAsync_DontWaitForData) { |
| bool succeeded = false; |
| bool failed = false; |
| auto success_callback = [&succeeded](size_t size) { succeeded = true; }; |
| auto error_callback = [&failed](const Error* error) { failed = true; }; |
| |
| MockStreamImpl stream_mock; |
| char buf[10]; |
| FakeMessageLoop fake_loop_{nullptr}; |
| fake_loop_.SetAsCurrent(); |
| |
| EXPECT_CALL(stream_mock, ReadNonBlocking(buf, 10, _, _, _)) |
| .WillOnce( |
| DoAll(SetArgPointee<2>(5), SetArgPointee<3>(false), Return(true))); |
| EXPECT_CALL(stream_mock, WaitForData(_, _, _)).Times(0); |
| EXPECT_TRUE(stream_mock.ReadAsync(buf, sizeof(buf), |
| base::Bind(success_callback), |
| base::Bind(error_callback), nullptr)); |
| // Even if ReadNonBlocking() returned some data without waiting, the |
| // |success_callback| should not run yet. |
| EXPECT_TRUE(fake_loop_.PendingTasks()); |
| EXPECT_FALSE(succeeded); |
| EXPECT_FALSE(failed); |
| |
| // Since the previous callback is still waiting in the main loop, we can't |
| // schedule another read yet. |
| ErrorPtr error; |
| EXPECT_FALSE(stream_mock.ReadAsync(buf, sizeof(buf), |
| base::Bind(success_callback), |
| base::Bind(error_callback), &error)); |
| EXPECT_EQ(errors::stream::kDomain, error->GetDomain()); |
| EXPECT_EQ(errors::stream::kOperationNotSupported, error->GetCode()); |
| EXPECT_EQ("Another asynchronous operation is still pending", |
| error->GetMessage()); |
| |
| fake_loop_.Run(); |
| EXPECT_TRUE(succeeded); |
| EXPECT_FALSE(failed); |
| } |
| |
| TEST(Stream, ReadAllAsync) { |
| bool succeeded = false; |
| bool failed = false; |
| auto success_callback = [&succeeded]() { succeeded = true; }; |
| auto error_callback = [&failed](const Error* error) { failed = true; }; |
| |
| MockStreamImpl stream_mock; |
| base::Callback<void(AccessMode)> data_callback; |
| char buf[10]; |
| |
| // This sets up an initial non blocking read that would block, so |
| // ReadAllAsync() should wait for more data. |
| EXPECT_CALL(stream_mock, ReadNonBlocking(buf, 10, _, _, _)) |
| .WillOnce( |
| DoAll(SetArgPointee<2>(0), SetArgPointee<3>(false), Return(true))); |
| EXPECT_CALL(stream_mock, WaitForData(AccessMode::READ, _, _)) |
| .WillOnce(DoAll(SaveArg<1>(&data_callback), Return(true))); |
| EXPECT_TRUE(stream_mock.ReadAllAsync(buf, sizeof(buf), |
| base::Bind(success_callback), |
| base::Bind(error_callback), |
| nullptr)); |
| EXPECT_FALSE(succeeded); |
| EXPECT_FALSE(failed); |
| testing::Mock::VerifyAndClearExpectations(&stream_mock); |
| |
| // ReadAllAsync() will try to read non blocking until the read would block |
| // before it waits for the data to be available again. |
| EXPECT_CALL(stream_mock, ReadNonBlocking(buf, 10, _, _, _)) |
| .WillOnce(DoAll(SetArgPointee<2>(7), |
| SetArgPointee<3>(false), |
| Return(true))); |
| EXPECT_CALL(stream_mock, ReadNonBlocking(buf + 7, 3, _, _, _)) |
| .WillOnce( |
| DoAll(SetArgPointee<2>(0), SetArgPointee<3>(false), Return(true))); |
| EXPECT_CALL(stream_mock, WaitForData(AccessMode::READ, _, _)) |
| .WillOnce(DoAll(SaveArg<1>(&data_callback), Return(true))); |
| data_callback.Run(AccessMode::READ); |
| EXPECT_FALSE(succeeded); |
| EXPECT_FALSE(failed); |
| testing::Mock::VerifyAndClearExpectations(&stream_mock); |
| |
| EXPECT_CALL(stream_mock, ReadNonBlocking(buf + 7, 3, _, _, _)) |
| .WillOnce(DoAll(SetArgPointee<2>(3), |
| SetArgPointee<3>(true), |
| Return(true))); |
| data_callback.Run(AccessMode::READ); |
| EXPECT_TRUE(succeeded); |
| EXPECT_FALSE(failed); |
| } |
| |
| TEST(Stream, ReadAllAsync_EOS) { |
| bool succeeded = false; |
| bool failed = false; |
| auto success_callback = [&succeeded]() { succeeded = true; }; |
| auto error_callback = [&failed](const Error* error) { |
| ASSERT_EQ(errors::stream::kDomain, error->GetDomain()); |
| ASSERT_EQ(errors::stream::kPartialData, error->GetCode()); |
| failed = true; |
| }; |
| |
| MockStreamImpl stream_mock; |
| base::Callback<void(AccessMode)> data_callback; |
| char buf[10]; |
| |
| EXPECT_CALL(stream_mock, ReadNonBlocking(buf, 10, _, _, _)) |
| .WillOnce( |
| DoAll(SetArgPointee<2>(0), SetArgPointee<3>(false), Return(true))); |
| EXPECT_CALL(stream_mock, WaitForData(AccessMode::READ, _, _)) |
| .WillOnce(DoAll(SaveArg<1>(&data_callback), Return(true))); |
| EXPECT_TRUE(stream_mock.ReadAllAsync(buf, sizeof(buf), |
| base::Bind(success_callback), |
| base::Bind(error_callback), |
| nullptr)); |
| |
| // ReadAsyncAll() should finish and fail once ReadNonBlocking() returns an |
| // end-of-stream condition. |
| EXPECT_CALL(stream_mock, ReadNonBlocking(buf, 10, _, _, _)) |
| .WillOnce(DoAll(SetArgPointee<2>(7), |
| SetArgPointee<3>(true), |
| Return(true))); |
| data_callback.Run(AccessMode::READ); |
| EXPECT_FALSE(succeeded); |
| EXPECT_TRUE(failed); |
| } |
| |
| TEST(Stream, ReadBlocking) { |
| MockStreamImpl stream_mock; |
| char buf[1024]; |
| size_t read = 0; |
| |
| EXPECT_CALL(stream_mock, ReadNonBlocking(buf, 1024, _, _, _)) |
| .WillOnce(DoAll(SetArgPointee<2>(24), |
| SetArgPointee<3>(false), |
| Return(true))); |
| EXPECT_TRUE(stream_mock.ReadBlocking(buf, sizeof(buf), &read, nullptr)); |
| EXPECT_EQ(24, read); |
| |
| EXPECT_CALL(stream_mock, ReadNonBlocking(buf, 1024, _, _, _)) |
| .WillOnce(DoAll(SetArgPointee<2>(0), |
| SetArgPointee<3>(true), |
| Return(true))); |
| EXPECT_TRUE(stream_mock.ReadBlocking(buf, sizeof(buf), &read, nullptr)); |
| EXPECT_EQ(0, read); |
| |
| { |
| InSequence seq; |
| EXPECT_CALL(stream_mock, ReadNonBlocking(buf, 1024, _, _, _)) |
| .WillOnce(DoAll(SetArgPointee<2>(0), |
| SetArgPointee<3>(false), |
| Return(true))); |
| EXPECT_CALL(stream_mock, WaitForDataBlocking(AccessMode::READ, _, _, _)) |
| .WillOnce(Return(true)); |
| EXPECT_CALL(stream_mock, ReadNonBlocking(buf, 1024, _, _, _)) |
| .WillOnce(DoAll(SetArgPointee<2>(0), |
| SetArgPointee<3>(false), |
| Return(true))); |
| EXPECT_CALL(stream_mock, WaitForDataBlocking(AccessMode::READ, _, _, _)) |
| .WillOnce(Return(true)); |
| EXPECT_CALL(stream_mock, ReadNonBlocking(buf, 1024, _, _, _)) |
| .WillOnce(DoAll(SetArgPointee<2>(124), |
| SetArgPointee<3>(false), |
| Return(true))); |
| } |
| EXPECT_TRUE(stream_mock.ReadBlocking(buf, sizeof(buf), &read, nullptr)); |
| EXPECT_EQ(124, read); |
| |
| { |
| InSequence seq; |
| EXPECT_CALL(stream_mock, ReadNonBlocking(buf, 1024, _, _, _)) |
| .WillOnce(DoAll(SetArgPointee<2>(0), |
| SetArgPointee<3>(false), |
| Return(true))); |
| EXPECT_CALL(stream_mock, WaitForDataBlocking(AccessMode::READ, _, _, _)) |
| .WillOnce(Return(false)); |
| } |
| EXPECT_FALSE(stream_mock.ReadBlocking(buf, sizeof(buf), &read, nullptr)); |
| } |
| |
| TEST(Stream, ReadAllBlocking) { |
| class MockReadBlocking : public MockStreamImpl { |
| public: |
| MOCK_METHOD4(ReadBlocking, bool(void*, size_t, size_t*, ErrorPtr*)); |
| } stream_mock; |
| |
| char buf[1024]; |
| |
| EXPECT_CALL(stream_mock, ReadBlocking(buf, 1024, _, _)) |
| .WillOnce(DoAll(SetArgPointee<2>(24), Return(true))); |
| EXPECT_CALL(stream_mock, ReadBlocking(buf + 24, 1000, _, _)) |
| .WillOnce(DoAll(SetArgPointee<2>(1000), Return(true))); |
| EXPECT_TRUE(stream_mock.ReadAllBlocking(buf, sizeof(buf), nullptr)); |
| |
| ErrorPtr error; |
| EXPECT_CALL(stream_mock, ReadBlocking(buf, 1024, _, _)) |
| .WillOnce(DoAll(SetArgPointee<2>(24), Return(true))); |
| EXPECT_CALL(stream_mock, ReadBlocking(buf + 24, 1000, _, _)) |
| .WillOnce(DoAll(SetArgPointee<2>(0), Return(true))); |
| EXPECT_FALSE(stream_mock.ReadAllBlocking(buf, sizeof(buf), &error)); |
| EXPECT_EQ(errors::stream::kDomain, error->GetDomain()); |
| EXPECT_EQ(errors::stream::kPartialData, error->GetCode()); |
| } |
| |
| TEST(Stream, WriteAsync) { |
| size_t write_size = 0; |
| bool failed = false; |
| auto success_callback = [&write_size](size_t size) { write_size = size; }; |
| auto error_callback = [&failed](const Error* error) { failed = true; }; |
| |
| MockStreamImpl stream_mock; |
| InSequence s; |
| base::Callback<void(AccessMode)> data_callback; |
| char buf[10] = {}; |
| |
| // WriteNonBlocking returns a blocking situation (size_written = 0) so the |
| // WaitForData() is run. |
| EXPECT_CALL(stream_mock, WriteNonBlocking(buf, 10, _, _)) |
| .WillOnce(DoAll(SetArgPointee<2>(0), Return(true))); |
| EXPECT_CALL(stream_mock, WaitForData(AccessMode::WRITE, _, _)) |
| .WillOnce(DoAll(SaveArg<1>(&data_callback), Return(true))); |
| EXPECT_TRUE(stream_mock.WriteAsync(buf, sizeof(buf), |
| base::Bind(success_callback), |
| base::Bind(error_callback), nullptr)); |
| EXPECT_EQ(0u, write_size); |
| EXPECT_FALSE(failed); |
| |
| ErrorPtr error; |
| EXPECT_FALSE(stream_mock.WriteAsync(buf, sizeof(buf), |
| base::Bind(success_callback), |
| base::Bind(error_callback), &error)); |
| EXPECT_EQ(errors::stream::kDomain, error->GetDomain()); |
| EXPECT_EQ(errors::stream::kOperationNotSupported, error->GetCode()); |
| EXPECT_EQ("Another asynchronous operation is still pending", |
| error->GetMessage()); |
| |
| EXPECT_CALL(stream_mock, WriteNonBlocking(buf, 10, _, _)) |
| .WillOnce(DoAll(SetArgPointee<2>(7), Return(true))); |
| data_callback.Run(AccessMode::WRITE); |
| EXPECT_EQ(7u, write_size); |
| EXPECT_FALSE(failed); |
| } |
| |
| TEST(Stream, WriteAllAsync) { |
| bool succeeded = false; |
| bool failed = false; |
| auto success_callback = [&succeeded]() { succeeded = true; }; |
| auto error_callback = [&failed](const Error* error) { failed = true; }; |
| |
| MockStreamImpl stream_mock; |
| base::Callback<void(AccessMode)> data_callback; |
| char buf[10] = {}; |
| |
| EXPECT_CALL(stream_mock, WriteNonBlocking(buf, 10, _, _)) |
| .WillOnce(DoAll(SetArgPointee<2>(0), Return(true))); |
| EXPECT_CALL(stream_mock, WaitForData(AccessMode::WRITE, _, _)) |
| .WillOnce(DoAll(SaveArg<1>(&data_callback), Return(true))); |
| EXPECT_TRUE(stream_mock.WriteAllAsync(buf, sizeof(buf), |
| base::Bind(success_callback), |
| base::Bind(error_callback), |
| nullptr)); |
| testing::Mock::VerifyAndClearExpectations(&stream_mock); |
| EXPECT_FALSE(succeeded); |
| EXPECT_FALSE(failed); |
| |
| EXPECT_CALL(stream_mock, WriteNonBlocking(buf, 10, _, _)) |
| .WillOnce(DoAll(SetArgPointee<2>(7), Return(true))); |
| EXPECT_CALL(stream_mock, WriteNonBlocking(buf + 7, 3, _, _)) |
| .WillOnce(DoAll(SetArgPointee<2>(0), Return(true))); |
| EXPECT_CALL(stream_mock, WaitForData(AccessMode::WRITE, _, _)) |
| .WillOnce(DoAll(SaveArg<1>(&data_callback), Return(true))); |
| data_callback.Run(AccessMode::WRITE); |
| testing::Mock::VerifyAndClearExpectations(&stream_mock); |
| EXPECT_FALSE(succeeded); |
| EXPECT_FALSE(failed); |
| |
| EXPECT_CALL(stream_mock, WriteNonBlocking(buf + 7, 3, _, _)) |
| .WillOnce(DoAll(SetArgPointee<2>(3), Return(true))); |
| data_callback.Run(AccessMode::WRITE); |
| EXPECT_TRUE(succeeded); |
| EXPECT_FALSE(failed); |
| } |
| |
| TEST(Stream, WriteBlocking) { |
| MockStreamImpl stream_mock; |
| char buf[1024]; |
| size_t written = 0; |
| |
| EXPECT_CALL(stream_mock, WriteNonBlocking(buf, 1024, _, _)) |
| .WillOnce(DoAll(SetArgPointee<2>(24), Return(true))); |
| EXPECT_TRUE(stream_mock.WriteBlocking(buf, sizeof(buf), &written, nullptr)); |
| EXPECT_EQ(24, written); |
| |
| { |
| InSequence seq; |
| EXPECT_CALL(stream_mock, WriteNonBlocking(buf, 1024, _, _)) |
| .WillOnce(DoAll(SetArgPointee<2>(0), Return(true))); |
| EXPECT_CALL(stream_mock, WaitForDataBlocking(AccessMode::WRITE, _, _, _)) |
| .WillOnce(Return(true)); |
| EXPECT_CALL(stream_mock, WriteNonBlocking(buf, 1024, _, _)) |
| .WillOnce(DoAll(SetArgPointee<2>(0), Return(true))); |
| EXPECT_CALL(stream_mock, WaitForDataBlocking(AccessMode::WRITE, _, _, _)) |
| .WillOnce(Return(true)); |
| EXPECT_CALL(stream_mock, WriteNonBlocking(buf, 1024, _, _)) |
| .WillOnce(DoAll(SetArgPointee<2>(124), Return(true))); |
| } |
| EXPECT_TRUE(stream_mock.WriteBlocking(buf, sizeof(buf), &written, nullptr)); |
| EXPECT_EQ(124, written); |
| |
| { |
| InSequence seq; |
| EXPECT_CALL(stream_mock, WriteNonBlocking(buf, 1024, _, _)) |
| .WillOnce(DoAll(SetArgPointee<2>(0), Return(true))); |
| EXPECT_CALL(stream_mock, WaitForDataBlocking(AccessMode::WRITE, _, _, _)) |
| .WillOnce(Return(false)); |
| } |
| EXPECT_FALSE(stream_mock.WriteBlocking(buf, sizeof(buf), &written, nullptr)); |
| } |
| |
| TEST(Stream, WriteAllBlocking) { |
| class MockWritelocking : public MockStreamImpl { |
| public: |
| MOCK_METHOD4(WriteBlocking, bool(const void*, size_t, size_t*, ErrorPtr*)); |
| } stream_mock; |
| |
| char buf[1024]; |
| |
| EXPECT_CALL(stream_mock, WriteBlocking(buf, 1024, _, _)) |
| .WillOnce(DoAll(SetArgPointee<2>(24), Return(true))); |
| EXPECT_CALL(stream_mock, WriteBlocking(buf + 24, 1000, _, _)) |
| .WillOnce(DoAll(SetArgPointee<2>(1000), Return(true))); |
| EXPECT_TRUE(stream_mock.WriteAllBlocking(buf, sizeof(buf), nullptr)); |
| } |
| |
| } // namespace brillo |