// 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/memory_stream.h>
#include <algorithm>
#include <limits>
#include <numeric>
#include <string>
#include <vector>
#include <brillo/streams/stream_errors.h>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
using testing::DoAll;
using testing::Return;
using testing::SetArgPointee;
using testing::_;
namespace brillo {
namespace {
int ReadByte(Stream* stream, brillo::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));
brillo::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));
brillo::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 brillo