/* * Copyright (C) 2016 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include <stdlib.h> #include <string.h> #include <gtest/gtest.h> #include <nvram/messages/io.h> namespace nvram { namespace { // A simple |InputStreamBuffer| implementation that sets up a sequence of // windows of |sizes| specified by the template parameters. Each byte read from // the buffer has a value corresponding to its position in the stream. template<size_t... sizes> class TestInputStreamBuffer : public InputStreamBuffer { public: TestInputStreamBuffer() { Advance(); } private: bool Advance() override { if (index_ >= (sizeof(kSizes) / sizeof(kSizes[0]))) { return false; } memset(buffer, 0xff, kMaxSize); const size_t size = kSizes[index_] < kMaxSize ? kSizes[index_] : kMaxSize; pos_ = buffer; end_ = buffer + size; for (uint8_t* p = buffer; p < end_; ++p) { *p = static_cast<uint8_t>(count_++ % 256); } ++index_; return true; } static constexpr size_t kMaxSize = 256; static constexpr size_t kSizes[] = { sizes... }; uint8_t buffer[kMaxSize]; size_t index_ = 0; size_t count_ = 0; }; template<size_t... sizes> constexpr size_t TestInputStreamBuffer<sizes...>::kSizes[]; // Tests whether a read of the given size returns the correct data, i.e. bytes // with consecutive values starting at |pos|. void CheckRead(InputStreamBuffer* buffer, size_t size, size_t pos) { uint8_t data[256]; ASSERT_LE(size, sizeof(data)); EXPECT_TRUE(buffer->Read(data, size)); for (uint8_t* p = data; p < data + size; ++p) { EXPECT_EQ(pos++ % 256, *p); } } } // namespace TEST(InputStreamBufferTest, Basic) { TestInputStreamBuffer<10> buf; EXPECT_FALSE(buf.Done()); uint8_t byte = 0; EXPECT_TRUE(buf.ReadByte(&byte)); EXPECT_EQ(0, byte); EXPECT_FALSE(buf.Done()); CheckRead(&buf, 6, 1); EXPECT_FALSE(buf.Done()); EXPECT_TRUE(buf.Skip(3)); EXPECT_TRUE(buf.Done()); } TEST(InputStreamBufferTest, Empty) { InputStreamBuffer buf(nullptr, nullptr); EXPECT_TRUE(buf.Done()); uint8_t byte = 0; EXPECT_FALSE(buf.ReadByte(&byte)); } TEST(InputStreamBufferTest, LargeRead) { TestInputStreamBuffer<10> buf; uint8_t read_buf[10]; EXPECT_FALSE(buf.Read(read_buf, SIZE_MAX)); } TEST(InputStreamBufferTest, LargeSkip) { TestInputStreamBuffer<10> buf; EXPECT_FALSE(buf.Skip(SIZE_MAX)); } TEST(InputStreamBufferTest, OverlappingReadByte) { TestInputStreamBuffer<1, 1> buf; uint8_t byte = 0; EXPECT_TRUE(buf.ReadByte(&byte)); EXPECT_EQ(0, byte); EXPECT_FALSE(buf.Done()); EXPECT_TRUE(buf.ReadByte(&byte)); EXPECT_EQ(1, byte); EXPECT_TRUE(buf.Done()); } TEST(InputStreamBufferTest, OverlappingRead) { TestInputStreamBuffer<10, 10, 10> buf; CheckRead(&buf, 15, 0); CheckRead(&buf, 10, 15); CheckRead(&buf, 5, 25); EXPECT_TRUE(buf.Done()); } TEST(InputStreamBufferTest, OverlappingSkip) { TestInputStreamBuffer<10, 10, 10> buf; EXPECT_TRUE(buf.Skip(15)); EXPECT_TRUE(buf.Skip(10)); EXPECT_TRUE(buf.Skip(5)); EXPECT_TRUE(buf.Done()); } TEST(NestedInputStreamBufferTest, Large) { TestInputStreamBuffer<10> buf; NestedInputStreamBuffer nested(&buf, SIZE_MAX); EXPECT_FALSE(nested.Skip(SIZE_MAX)); } TEST(NestedInputStreamBufferTest, Short) { TestInputStreamBuffer<10> buf; NestedInputStreamBuffer nested(&buf, 5); CheckRead(&nested, 5, 0); EXPECT_TRUE(nested.Done()); EXPECT_FALSE(nested.Skip(1)); } TEST(NestedInputStreamBufferTest, Matching) { TestInputStreamBuffer<10, 5> buf; NestedInputStreamBuffer nested(&buf, 10); CheckRead(&nested, 10, 0); EXPECT_TRUE(nested.Done()); EXPECT_FALSE(nested.Skip(1)); } TEST(NestedInputStreamBufferTest, Overlapping) { TestInputStreamBuffer<2, 3, 5, 8> buf; NestedInputStreamBuffer nested(&buf, 16); CheckRead(&nested, 8, 0); EXPECT_FALSE(nested.Done()); CheckRead(&nested, 8, 8); EXPECT_TRUE(nested.Done()); EXPECT_FALSE(nested.Skip(1)); } namespace { // An |OutputStreamBuffer| implementation backed by a sequence of buffer windows // of |sizes| specified as template parameters. The output is expected to be // sequential byte values starting at 0. template<size_t... sizes> class TestOutputStreamBuffer : public OutputStreamBuffer { public: TestOutputStreamBuffer() { Advance(); } ~TestOutputStreamBuffer() { EXPECT_TRUE(Verify()); } bool Verify() { for (; check_pos_ < pos_; check_pos_++, count_++) { data_matches_ &= *check_pos_ == (count_ % 256); } return data_matches_; } private: bool Advance() override { if (index_ >= (sizeof(kSizes) / sizeof(kSizes[0]))) { return false; } pos_ = end_; Verify(); memset(buffer, 0xff, kMaxSize); const size_t size = kSizes[index_] < kMaxSize ? kSizes[index_] : kMaxSize; pos_ = buffer; check_pos_ = buffer; end_ = buffer + size; ++index_; return true; } static constexpr size_t kMaxSize = 256; static constexpr size_t kSizes[] = { sizes... }; uint8_t buffer[kMaxSize]; size_t index_ = 0; // The pointer in buffer until which the data has been checked to match the // expectations. uint8_t* check_pos_ = nullptr; // The counter that determines the expected value for the buffer bytes. size_t count_ = 0; // Whether all bytes that have been checked so far had the expected value. bool data_matches_ = true; }; template<size_t... sizes> constexpr size_t TestOutputStreamBuffer<sizes...>::kSizes[]; // Writes a buffer of |size| to |buf|. The buffer contains consecutive byte // value starting at pos. void WriteBuf(OutputStreamBuffer* buffer, size_t size, size_t pos) { uint8_t data[1024]; ASSERT_LE(size, sizeof(data)); for (uint8_t* p = data; p < data + size; ++p) { *p = pos++ % 256; } EXPECT_TRUE(buffer->Write(data, size)); } } // namespace TEST(OutputStreamBufferTest, Basic) { TestOutputStreamBuffer<10> buf; EXPECT_FALSE(buf.Done()); EXPECT_TRUE(buf.WriteByte(0)); EXPECT_TRUE(buf.WriteByte(1)); EXPECT_FALSE(buf.Done()); EXPECT_TRUE(buf.Verify()); WriteBuf(&buf, 6, 2); EXPECT_FALSE(buf.Done()); EXPECT_TRUE(buf.Verify()); WriteBuf(&buf, 2, 8); EXPECT_TRUE(buf.Done()); } TEST(OutputStreamBufferTest, Empty) { OutputStreamBuffer buf(nullptr, nullptr); EXPECT_TRUE(buf.Done()); EXPECT_FALSE(buf.WriteByte(0)); } TEST(OutputStreamBufferTest, ShortWrite) { TestOutputStreamBuffer<10> buf; WriteBuf(&buf, 5, 0); } TEST(OutputStreamBufferTest, LargeWrite) { TestOutputStreamBuffer<5> buf; uint8_t data[10] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; EXPECT_FALSE(buf.Write(data, sizeof(data))); } TEST(OutputStreamBufferTest, OverlappingWriteByte) { TestOutputStreamBuffer<1, 1> buf; EXPECT_TRUE(buf.WriteByte(0)); EXPECT_FALSE(buf.Done()); EXPECT_TRUE(buf.WriteByte(1)); EXPECT_TRUE(buf.Done()); } TEST(OutputStreamBufferTest, OverlappingWrite) { TestOutputStreamBuffer<10, 10, 10> buf; WriteBuf(&buf, 15, 0); EXPECT_FALSE(buf.Done()); WriteBuf(&buf, 10, 15); EXPECT_FALSE(buf.Done()); WriteBuf(&buf, 5, 25); EXPECT_TRUE(buf.Done()); } TEST(CountingOutputStreamBuffer, Basic) { CountingOutputStreamBuffer buf; EXPECT_EQ(0U, buf.bytes_written()); EXPECT_FALSE(buf.Done()); WriteBuf(&buf, 15, 0); EXPECT_EQ(15U, buf.bytes_written()); EXPECT_FALSE(buf.Done()); EXPECT_TRUE(buf.WriteByte(0)); EXPECT_EQ(16U, buf.bytes_written()); EXPECT_FALSE(buf.Done()); WriteBuf(&buf, 1024, 0); EXPECT_EQ(1040U, buf.bytes_written()); EXPECT_FALSE(buf.Done()); } TEST(BlobOutputStreamBuffer, Basic) { Blob blob; ASSERT_TRUE(blob.Resize(1024 * 1024)); BlobOutputStreamBuffer buf(&blob); WriteBuf(&buf, 15, 0); EXPECT_FALSE(buf.Done()); EXPECT_TRUE(buf.WriteByte(15)); EXPECT_FALSE(buf.Done()); EXPECT_TRUE(buf.Truncate()); EXPECT_EQ(16U, blob.size()); for (size_t i = 0; i < blob.size(); ++i) { EXPECT_EQ(i % 256, blob.data()[i]); } WriteBuf(&buf, 1024, 16); EXPECT_FALSE(buf.Done()); EXPECT_TRUE(buf.Truncate()); EXPECT_EQ(1040U, blob.size()); for (size_t i = 0; i < blob.size(); ++i) { EXPECT_EQ(i % 256, blob.data()[i]); } } } // namespace nvram