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.
390 lines
13 KiB
390 lines
13 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/memory_stream.h>
|
|
|
|
#include <algorithm>
|
|
#include <limits>
|
|
#include <numeric>
|
|
#include <string>
|
|
#include <utility>
|
|
#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_METHOD(bool,
|
|
Read,
|
|
(void*, size_t, size_t, size_t*, ErrorPtr*),
|
|
(override));
|
|
MOCK_METHOD(bool,
|
|
Write,
|
|
(const void*, size_t, size_t, size_t*, ErrorPtr*),
|
|
(override));
|
|
MOCK_METHOD(bool, Resize, (size_t, ErrorPtr*), (override));
|
|
MOCK_METHOD(size_t, GetSize, (), (const, override));
|
|
MOCK_METHOD(bool, IsReadOnly, (), (const, override));
|
|
|
|
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
|