| /* |
| * 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 |