| // 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 <chromeos/streams/memory_stream.h> |
| |
| #include <algorithm> |
| #include <limits> |
| #include <numeric> |
| #include <string> |
| #include <vector> |
| |
| #include <chromeos/streams/stream_errors.h> |
| #include <gmock/gmock.h> |
| #include <gtest/gtest.h> |
| |
| using testing::DoAll; |
| using testing::Return; |
| using testing::SetArgPointee; |
| using testing::_; |
| |
| namespace chromeos { |
| |
| namespace { |
| |
| int ReadByte(Stream* stream, chromeos::ErrorPtr* error) { |
| uint8_t byte = 0; |
| return stream->ReadAllBlocking(&byte, sizeof(byte), error) ? byte : -1; |
| } |
| |
| class MockMemoryContainer : public data_container::DataContainerInterface { |
| public: |
| MockMemoryContainer() = default; |
| |
| MOCK_METHOD5(Read, bool(void*, size_t, size_t, size_t*, ErrorPtr*)); |
| MOCK_METHOD5(Write, bool(const void*, size_t, size_t, size_t*, ErrorPtr*)); |
| MOCK_METHOD2(Resize, bool(size_t, ErrorPtr*)); |
| MOCK_CONST_METHOD0(GetSize, size_t()); |
| MOCK_CONST_METHOD0(IsReadOnly, bool()); |
| |
| private: |
| DISALLOW_COPY_AND_ASSIGN(MockMemoryContainer); |
| }; |
| |
| } // anonymous namespace |
| |
| class MemoryStreamTest : public testing::Test { |
| public: |
| void SetUp() override { |
| std::unique_ptr<MockMemoryContainer> container{new MockMemoryContainer{}}; |
| stream_.reset(new MemoryStream{std::move(container), 0}); |
| } |
| |
| MockMemoryContainer& container_mock() { |
| return *static_cast<MockMemoryContainer*>(stream_->container_.get()); |
| } |
| |
| inline static void* IntToPtr(int addr) { |
| return reinterpret_cast<void*>(addr); |
| } |
| |
| inline static const void* IntToConstPtr(int addr) { |
| return reinterpret_cast<const void*>(addr); |
| } |
| |
| std::unique_ptr<MemoryStream> stream_; |
| // Dummy buffer pointer values to make sure that input pointer values |
| // are delegated to the stream interface without a change. |
| void* const test_read_buffer_ = IntToPtr(12345); |
| const void* const test_write_buffer_ = IntToConstPtr(67890); |
| // We limit the size of memory streams to be the maximum size of either of |
| // size_t (on 32 bit platforms) or the size of signed 64 bit integer. |
| const size_t kSizeMax = |
| std::min<uint64_t>(std::numeric_limits<size_t>::max(), |
| std::numeric_limits<int64_t>::max()); |
| }; |
| |
| TEST_F(MemoryStreamTest, CanRead) { |
| EXPECT_TRUE(stream_->CanRead()); |
| } |
| |
| TEST_F(MemoryStreamTest, CanWrite) { |
| EXPECT_CALL(container_mock(), IsReadOnly()) |
| .WillOnce(Return(true)) |
| .WillOnce(Return(false)); |
| |
| EXPECT_FALSE(stream_->CanWrite()); |
| EXPECT_TRUE(stream_->CanWrite()); |
| } |
| |
| TEST_F(MemoryStreamTest, CanSeek) { |
| EXPECT_TRUE(stream_->CanSeek()); |
| } |
| |
| TEST_F(MemoryStreamTest, GetSize) { |
| EXPECT_CALL(container_mock(), GetSize()) |
| .WillOnce(Return(0)) |
| .WillOnce(Return(1234)) |
| .WillOnce(Return(kSizeMax)); |
| |
| EXPECT_EQ(0, stream_->GetSize()); |
| EXPECT_EQ(1234, stream_->GetSize()); |
| EXPECT_EQ(kSizeMax, stream_->GetSize()); |
| } |
| |
| TEST_F(MemoryStreamTest, SetSizeBlocking) { |
| EXPECT_CALL(container_mock(), Resize(0, _)).WillOnce(Return(true)); |
| |
| ErrorPtr error; |
| EXPECT_TRUE(stream_->SetSizeBlocking(0, &error)); |
| EXPECT_EQ(nullptr, error.get()); |
| |
| EXPECT_CALL(container_mock(), Resize(kSizeMax, nullptr)) |
| .WillOnce(Return(true)); |
| |
| EXPECT_TRUE(stream_->SetSizeBlocking(kSizeMax, nullptr)); |
| } |
| |
| TEST_F(MemoryStreamTest, SeekAndGetPosition) { |
| EXPECT_EQ(0, stream_->GetPosition()); |
| |
| EXPECT_CALL(container_mock(), GetSize()).WillRepeatedly(Return(200)); |
| |
| ErrorPtr error; |
| uint64_t new_pos = 0; |
| EXPECT_TRUE(stream_->Seek(2, Stream::Whence::FROM_BEGIN, &new_pos, &error)); |
| EXPECT_EQ(nullptr, error.get()); |
| EXPECT_EQ(2, new_pos); |
| EXPECT_EQ(2, stream_->GetPosition()); |
| EXPECT_TRUE(stream_->Seek(2, Stream::Whence::FROM_CURRENT, &new_pos, &error)); |
| EXPECT_EQ(nullptr, error.get()); |
| EXPECT_EQ(4, new_pos); |
| EXPECT_EQ(4, stream_->GetPosition()); |
| |
| EXPECT_TRUE(stream_->Seek(-2, Stream::Whence::FROM_END, nullptr, nullptr)); |
| EXPECT_EQ(198, stream_->GetPosition()); |
| |
| EXPECT_CALL(container_mock(), GetSize()).WillOnce(Return(kSizeMax)); |
| EXPECT_TRUE(stream_->Seek(0, Stream::Whence::FROM_END, nullptr, nullptr)); |
| EXPECT_EQ(kSizeMax, stream_->GetPosition()); |
| } |
| |
| TEST_F(MemoryStreamTest, ReadNonBlocking) { |
| size_t read = 0; |
| bool eos = false; |
| |
| EXPECT_CALL(container_mock(), Read(test_read_buffer_, 10, 0, _, nullptr)) |
| .WillOnce(DoAll(SetArgPointee<3>(5), Return(true))); |
| |
| EXPECT_TRUE(stream_->ReadNonBlocking(test_read_buffer_, 10, &read, &eos, |
| nullptr)); |
| EXPECT_EQ(5, read); |
| EXPECT_EQ(5, stream_->GetPosition()); |
| EXPECT_FALSE(eos); |
| |
| EXPECT_CALL(container_mock(), Read(test_read_buffer_, 100, 5, _, nullptr)) |
| .WillOnce(DoAll(SetArgPointee<3>(100), Return(true))); |
| |
| EXPECT_TRUE(stream_->ReadNonBlocking(test_read_buffer_, 100, &read, &eos, |
| nullptr)); |
| EXPECT_EQ(100, read); |
| EXPECT_EQ(105, stream_->GetPosition()); |
| EXPECT_FALSE(eos); |
| |
| EXPECT_CALL(container_mock(), Read(test_read_buffer_, 10, 105, _, nullptr)) |
| .WillOnce(DoAll(SetArgPointee<3>(0), Return(true))); |
| |
| EXPECT_TRUE(stream_->ReadNonBlocking(test_read_buffer_, 10, &read, &eos, |
| nullptr)); |
| EXPECT_EQ(0, read); |
| EXPECT_EQ(105, stream_->GetPosition()); |
| EXPECT_TRUE(eos); |
| } |
| |
| TEST_F(MemoryStreamTest, WriteNonBlocking) { |
| size_t written = 0; |
| |
| EXPECT_CALL(container_mock(), Write(test_write_buffer_, 10, 0, _, nullptr)) |
| .WillOnce(DoAll(SetArgPointee<3>(5), Return(true))); |
| |
| EXPECT_TRUE(stream_->WriteNonBlocking(test_write_buffer_, 10, &written, |
| nullptr)); |
| EXPECT_EQ(5, written); |
| EXPECT_EQ(5, stream_->GetPosition()); |
| |
| EXPECT_CALL(container_mock(), Write(test_write_buffer_, 100, 5, _, nullptr)) |
| .WillOnce(DoAll(SetArgPointee<3>(100), Return(true))); |
| |
| EXPECT_TRUE(stream_->WriteNonBlocking(test_write_buffer_, 100, &written, |
| nullptr)); |
| EXPECT_EQ(100, written); |
| EXPECT_EQ(105, stream_->GetPosition()); |
| |
| EXPECT_CALL(container_mock(), Write(test_write_buffer_, 10, 105, _, nullptr)) |
| .WillOnce(DoAll(SetArgPointee<3>(10), Return(true))); |
| |
| EXPECT_TRUE(stream_->WriteNonBlocking(test_write_buffer_, 10, &written, |
| nullptr)); |
| EXPECT_EQ(115, stream_->GetPosition()); |
| } |
| |
| ////////////////////////////////////////////////////////////////////////////// |
| // Factory method tests. |
| TEST(MemoryStream, OpenBinary) { |
| char buffer[] = {1, 2, 3}; |
| StreamPtr stream = MemoryStream::OpenRef(buffer, sizeof(buffer), nullptr); |
| buffer[0] = 5; |
| EXPECT_EQ(3, stream->GetSize()); |
| EXPECT_EQ(5, ReadByte(stream.get(), nullptr)); |
| EXPECT_EQ(2, ReadByte(stream.get(), nullptr)); |
| EXPECT_EQ(3, ReadByte(stream.get(), nullptr)); |
| chromeos::ErrorPtr error; |
| EXPECT_EQ(-1, ReadByte(stream.get(), &error)); |
| EXPECT_EQ(errors::stream::kPartialData, error->GetCode()); |
| EXPECT_EQ("Reading past the end of stream", error->GetMessage()); |
| } |
| |
| TEST(MemoryStream, OpenBinaryCopy) { |
| char buffer[] = {1, 2, 3}; |
| StreamPtr stream = MemoryStream::OpenCopyOf(buffer, sizeof(buffer), nullptr); |
| buffer[0] = 5; |
| EXPECT_EQ(3, stream->GetSize()); |
| EXPECT_EQ(1, ReadByte(stream.get(), nullptr)); |
| EXPECT_EQ(2, ReadByte(stream.get(), nullptr)); |
| EXPECT_EQ(3, ReadByte(stream.get(), nullptr)); |
| chromeos::ErrorPtr error; |
| EXPECT_EQ(-1, ReadByte(stream.get(), &error)); |
| EXPECT_EQ(errors::stream::kPartialData, error->GetCode()); |
| EXPECT_EQ("Reading past the end of stream", error->GetMessage()); |
| } |
| |
| TEST(MemoryStream, OpenString) { |
| std::string str("abcd"); |
| StreamPtr stream = MemoryStream::OpenRef(str, nullptr); |
| str[0] = 'A'; |
| EXPECT_EQ(4, stream->GetSize()); |
| EXPECT_EQ('A', ReadByte(stream.get(), nullptr)); |
| EXPECT_EQ('b', ReadByte(stream.get(), nullptr)); |
| EXPECT_EQ('c', ReadByte(stream.get(), nullptr)); |
| EXPECT_EQ('d', ReadByte(stream.get(), nullptr)); |
| EXPECT_EQ(-1, ReadByte(stream.get(), nullptr)); |
| } |
| |
| TEST(MemoryStream, OpenStringCopy) { |
| std::string str("abcd"); |
| StreamPtr stream = MemoryStream::OpenCopyOf(str, nullptr); |
| str[0] = 'A'; |
| EXPECT_EQ(4, stream->GetSize()); |
| EXPECT_EQ('a', ReadByte(stream.get(), nullptr)); |
| EXPECT_EQ('b', ReadByte(stream.get(), nullptr)); |
| EXPECT_EQ('c', ReadByte(stream.get(), nullptr)); |
| EXPECT_EQ('d', ReadByte(stream.get(), nullptr)); |
| EXPECT_EQ(-1, ReadByte(stream.get(), nullptr)); |
| } |
| |
| TEST(MemoryStream, OpenCharBuf) { |
| char str[] = "abcd"; |
| StreamPtr stream = MemoryStream::OpenRef(str, nullptr); |
| str[0] = 'A'; |
| EXPECT_EQ(4, stream->GetSize()); |
| EXPECT_EQ('A', ReadByte(stream.get(), nullptr)); |
| EXPECT_EQ('b', ReadByte(stream.get(), nullptr)); |
| EXPECT_EQ('c', ReadByte(stream.get(), nullptr)); |
| EXPECT_EQ('d', ReadByte(stream.get(), nullptr)); |
| EXPECT_EQ(-1, ReadByte(stream.get(), nullptr)); |
| } |
| |
| TEST(MemoryStream, OpenCharBufCopy) { |
| char str[] = "abcd"; |
| StreamPtr stream = MemoryStream::OpenCopyOf(str, nullptr); |
| str[0] = 'A'; |
| EXPECT_EQ(4, stream->GetSize()); |
| EXPECT_EQ('a', ReadByte(stream.get(), nullptr)); |
| EXPECT_EQ('b', ReadByte(stream.get(), nullptr)); |
| EXPECT_EQ('c', ReadByte(stream.get(), nullptr)); |
| EXPECT_EQ('d', ReadByte(stream.get(), nullptr)); |
| EXPECT_EQ(-1, ReadByte(stream.get(), nullptr)); |
| } |
| |
| TEST(MemoryStream, OpenVector) { |
| std::vector<char> data = {'a', 'b', 'c', 'd'}; |
| StreamPtr stream = MemoryStream::OpenRef(data, nullptr); |
| data[0] = 'A'; |
| EXPECT_EQ(4, stream->GetSize()); |
| EXPECT_EQ(0, stream->GetPosition()); |
| EXPECT_EQ(4, stream->GetRemainingSize()); |
| EXPECT_EQ('A', ReadByte(stream.get(), nullptr)); |
| EXPECT_EQ('b', ReadByte(stream.get(), nullptr)); |
| EXPECT_EQ('c', ReadByte(stream.get(), nullptr)); |
| EXPECT_EQ('d', ReadByte(stream.get(), nullptr)); |
| EXPECT_EQ(4, stream->GetPosition()); |
| EXPECT_EQ(4, stream->GetSize()); |
| EXPECT_EQ(0, stream->GetRemainingSize()); |
| } |
| |
| TEST(MemoryStream, OpenVectorCopy) { |
| std::vector<uint8_t> data = {'a', 'b', 'c', 'd'}; |
| StreamPtr stream = MemoryStream::OpenCopyOf(data, nullptr); |
| data[0] = 'A'; |
| EXPECT_EQ(4, stream->GetSize()); |
| EXPECT_EQ(0, stream->GetPosition()); |
| EXPECT_EQ(4, stream->GetRemainingSize()); |
| EXPECT_EQ('a', ReadByte(stream.get(), nullptr)); |
| EXPECT_EQ('b', ReadByte(stream.get(), nullptr)); |
| EXPECT_EQ('c', ReadByte(stream.get(), nullptr)); |
| EXPECT_EQ('d', ReadByte(stream.get(), nullptr)); |
| EXPECT_EQ(4, stream->GetPosition()); |
| EXPECT_EQ(4, stream->GetSize()); |
| EXPECT_EQ(0, stream->GetRemainingSize()); |
| } |
| |
| TEST(MemoryStream, CreateVector) { |
| std::vector<uint8_t> buffer; |
| StreamPtr stream = MemoryStream::CreateRef(&buffer, nullptr); |
| EXPECT_TRUE(buffer.empty()); |
| EXPECT_EQ(0, stream->GetPosition()); |
| EXPECT_EQ(0, stream->GetSize()); |
| EXPECT_TRUE(stream->CloseBlocking(nullptr)); |
| |
| buffer.resize(5); |
| std::iota(buffer.begin(), buffer.end(), 0); |
| stream = MemoryStream::CreateRef(&buffer, nullptr); |
| EXPECT_FALSE(buffer.empty()); |
| EXPECT_EQ(0, stream->GetPosition()); |
| EXPECT_EQ(5, stream->GetSize()); |
| EXPECT_TRUE(stream->CloseBlocking(nullptr)); |
| |
| stream = MemoryStream::CreateRefForAppend(&buffer, nullptr); |
| EXPECT_FALSE(buffer.empty()); |
| EXPECT_EQ(5, stream->GetPosition()); |
| EXPECT_EQ(5, stream->GetSize()); |
| EXPECT_TRUE(stream->WriteAllBlocking("abcde", 5, nullptr)); |
| EXPECT_FALSE(buffer.empty()); |
| EXPECT_EQ(10, stream->GetPosition()); |
| EXPECT_EQ(10, stream->GetSize()); |
| EXPECT_TRUE(stream->SetPosition(0, nullptr)); |
| EXPECT_EQ(0, stream->GetPosition()); |
| EXPECT_EQ(10, stream->GetSize()); |
| EXPECT_TRUE(stream->CloseBlocking(nullptr)); |
| |
| EXPECT_EQ(10, buffer.size()); |
| EXPECT_EQ((std::vector<uint8_t>{0, 1, 2, 3, 4, 'a', 'b', 'c', 'd', 'e'}), |
| buffer); |
| |
| stream = MemoryStream::OpenRef(buffer, nullptr); |
| EXPECT_FALSE(buffer.empty()); |
| EXPECT_EQ(0, stream->GetPosition()); |
| EXPECT_EQ(10, stream->GetSize()); |
| } |
| |
| TEST(MemoryStream, CreateString) { |
| std::string buffer; |
| StreamPtr stream = MemoryStream::CreateRef(&buffer, nullptr); |
| EXPECT_TRUE(buffer.empty()); |
| EXPECT_EQ(0, stream->GetPosition()); |
| EXPECT_EQ(0, stream->GetSize()); |
| EXPECT_TRUE(stream->CloseBlocking(nullptr)); |
| |
| buffer = "abc"; |
| stream = MemoryStream::CreateRef(&buffer, nullptr); |
| EXPECT_FALSE(buffer.empty()); |
| EXPECT_EQ(0, stream->GetPosition()); |
| EXPECT_EQ(3, stream->GetSize()); |
| EXPECT_TRUE(stream->CloseBlocking(nullptr)); |
| |
| stream = MemoryStream::CreateRefForAppend(&buffer, nullptr); |
| EXPECT_FALSE(buffer.empty()); |
| EXPECT_EQ(3, stream->GetPosition()); |
| EXPECT_EQ(3, stream->GetSize()); |
| EXPECT_TRUE(stream->WriteAllBlocking("d_1234", 6, nullptr)); |
| EXPECT_FALSE(buffer.empty()); |
| EXPECT_EQ(9, stream->GetPosition()); |
| EXPECT_EQ(9, stream->GetSize()); |
| EXPECT_TRUE(stream->SetPosition(0, nullptr)); |
| EXPECT_EQ(0, stream->GetPosition()); |
| EXPECT_EQ(9, stream->GetSize()); |
| EXPECT_TRUE(stream->CloseBlocking(nullptr)); |
| EXPECT_EQ(9, buffer.size()); |
| EXPECT_EQ("abcd_1234", buffer); |
| } |
| |
| } // namespace chromeos |