Home | History | Annotate | Download | only in src
      1 // Copyright 2017 The Chromium OS Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #include <numeric>
      6 
      7 #include "gtest/gtest.h"
      8 
      9 #include "puffin/src/include/puffin/huffer.h"
     10 #include "puffin/src/include/puffin/puffer.h"
     11 #include "puffin/src/extent_stream.h"
     12 #include "puffin/src/file_stream.h"
     13 #include "puffin/src/memory_stream.h"
     14 #include "puffin/src/puffin_stream.h"
     15 #include "puffin/src/unittest_common.h"
     16 
     17 namespace puffin {
     18 
     19 using std::string;
     20 using std::shared_ptr;
     21 using std::vector;
     22 
     23 class StreamTest : public ::testing::Test {
     24  public:
     25   // |data| is the content of stream as a buffer.
     26   void TestRead(StreamInterface* stream, const Buffer& data) {
     27     // Test read
     28     Buffer buf(data.size(), 0);
     29 
     30     ASSERT_TRUE(stream->Seek(0));
     31     ASSERT_TRUE(stream->Read(buf.data(), buf.size()));
     32     for (size_t idx = 0; idx < buf.size(); idx++) {
     33       ASSERT_EQ(buf[idx], data[idx]);
     34     }
     35 
     36     // No reading out of data boundary.
     37     Buffer tmp(100);
     38     uint64_t size;
     39     ASSERT_TRUE(stream->GetSize(&size));
     40     ASSERT_TRUE(stream->Seek(size));
     41     ASSERT_TRUE(stream->Read(tmp.data(), 0));
     42     ASSERT_FALSE(stream->Read(tmp.data(), 1));
     43     ASSERT_FALSE(stream->Read(tmp.data(), 2));
     44     ASSERT_FALSE(stream->Read(tmp.data(), 3));
     45     ASSERT_FALSE(stream->Read(tmp.data(), 100));
     46 
     47     ASSERT_TRUE(stream->Seek(size - 1));
     48     ASSERT_TRUE(stream->Read(tmp.data(), 0));
     49     ASSERT_TRUE(stream->Read(tmp.data(), 1));
     50 
     51     ASSERT_TRUE(stream->Seek(size - 1));
     52     ASSERT_FALSE(stream->Read(tmp.data(), 2));
     53     ASSERT_FALSE(stream->Read(tmp.data(), 3));
     54     ASSERT_FALSE(stream->Read(tmp.data(), 100));
     55 
     56     // Read the entire buffer one byte at a time.
     57     ASSERT_TRUE(stream->Seek(0));
     58     for (size_t idx = 0; idx < size; idx++) {
     59       uint8_t u;
     60       ASSERT_TRUE(stream->Read(&u, 1));
     61       ASSERT_EQ(u, buf[idx]);
     62     }
     63 
     64     // Read the entire buffer one byte at a time and set offset for each read.
     65     for (size_t idx = 0; idx < size; idx++) {
     66       uint8_t u;
     67       ASSERT_TRUE(stream->Seek(idx));
     68       ASSERT_TRUE(stream->Read(&u, 1));
     69       ASSERT_EQ(u, buf[idx]);
     70     }
     71 
     72     // Read random lengths from random offsets.
     73     tmp.resize(buf.size());
     74     srand(time(nullptr));
     75     uint32_t rand_seed;
     76     for (size_t idx = 0; idx < 10000; idx++) {
     77       // zero to full size available.
     78       size_t size = rand_r(&rand_seed) % (buf.size() + 1);
     79       uint64_t max_start = buf.size() - size;
     80       uint64_t start = rand_r(&rand_seed) % (max_start + 1);
     81       ASSERT_TRUE(stream->Seek(start));
     82       ASSERT_TRUE(stream->Read(tmp.data(), size));
     83       for (size_t idx = 0; idx < size; idx++) {
     84         ASSERT_EQ(tmp[idx], buf[start + idx]);
     85       }
     86     }
     87   }
     88 
     89   void TestWriteBoundary(StreamInterface* stream) {
     90     Buffer buf(10);
     91     // Writing out of boundary is fine.
     92     uint64_t size;
     93     ASSERT_TRUE(stream->GetSize(&size));
     94     ASSERT_TRUE(stream->Seek(size));
     95     ASSERT_TRUE(stream->Write(buf.data(), 0));
     96     ASSERT_TRUE(stream->Write(buf.data(), 1));
     97     ASSERT_TRUE(stream->Write(buf.data(), 2));
     98     ASSERT_TRUE(stream->Write(buf.data(), 3));
     99     ASSERT_TRUE(stream->Write(buf.data(), 10));
    100 
    101     ASSERT_TRUE(stream->GetSize(&size));
    102     ASSERT_TRUE(stream->Seek(size - 1));
    103     ASSERT_TRUE(stream->Write(buf.data(), 0));
    104     ASSERT_TRUE(stream->Write(buf.data(), 1));
    105 
    106     ASSERT_TRUE(stream->GetSize(&size));
    107     ASSERT_TRUE(stream->Seek(size - 1));
    108     ASSERT_TRUE(stream->Write(buf.data(), 2));
    109     ASSERT_TRUE(stream->Write(buf.data(), 3));
    110     ASSERT_TRUE(stream->Write(buf.data(), 10));
    111   }
    112 
    113   void TestWrite(StreamInterface* write_stream, StreamInterface* read_stream) {
    114     uint64_t size;
    115     ASSERT_TRUE(read_stream->GetSize(&size));
    116     Buffer buf1(size);
    117     Buffer buf2(size);
    118     std::iota(buf1.begin(), buf1.end(), 0);
    119 
    120     // Make sure the write works.
    121     ASSERT_TRUE(write_stream->Seek(0));
    122     ASSERT_TRUE(write_stream->Write(buf1.data(), buf1.size()));
    123     ASSERT_TRUE(read_stream->Seek(0));
    124     ASSERT_TRUE(read_stream->Read(buf2.data(), buf2.size()));
    125     ASSERT_EQ(buf1, buf2);
    126 
    127     std::fill(buf2.begin(), buf2.end(), 0);
    128 
    129     // Write entire buffer one byte at a time. (all zeros).
    130     ASSERT_TRUE(write_stream->Seek(0));
    131     for (size_t idx = 0; idx < buf2.size(); idx++) {
    132       ASSERT_TRUE(write_stream->Write(&buf2[idx], 1));
    133     }
    134 
    135     ASSERT_TRUE(read_stream->Seek(0));
    136     ASSERT_TRUE(read_stream->Read(buf1.data(), buf1.size()));
    137     ASSERT_EQ(buf1, buf2);
    138   }
    139 
    140   // Call this at the end before |TestClose|.
    141   void TestSeek(StreamInterface* stream, bool seek_end_is_fine) {
    142     uint64_t size, offset;
    143     ASSERT_TRUE(stream->GetSize(&size));
    144     ASSERT_TRUE(stream->Seek(size));
    145     ASSERT_TRUE(stream->GetOffset(&offset));
    146     ASSERT_EQ(offset, size);
    147     ASSERT_TRUE(stream->Seek(10));
    148     ASSERT_TRUE(stream->GetOffset(&offset));
    149     ASSERT_EQ(offset, 10);
    150     ASSERT_TRUE(stream->Seek(0));
    151     ASSERT_TRUE(stream->GetOffset(&offset));
    152     ASSERT_EQ(offset, 0);
    153     // Test end of stream offset.
    154     ASSERT_EQ(stream->Seek(size + 1), seek_end_is_fine);
    155   }
    156 
    157   void TestClose(StreamInterface* stream) { ASSERT_TRUE(stream->Close()); }
    158 };
    159 
    160 TEST_F(StreamTest, MemoryStreamTest) {
    161   Buffer buf(105);
    162   std::iota(buf.begin(), buf.end(), 0);
    163 
    164   auto read_stream = MemoryStream::CreateForRead(buf);
    165   TestRead(read_stream.get(), buf);
    166   TestSeek(read_stream.get(), false);
    167 
    168   auto write_stream = MemoryStream::CreateForWrite(&buf);
    169   TestWrite(write_stream.get(), read_stream.get());
    170   TestWriteBoundary(write_stream.get());
    171   TestSeek(write_stream.get(), false);
    172 
    173   TestClose(read_stream.get());
    174   TestClose(write_stream.get());
    175 }
    176 
    177 TEST_F(StreamTest, FileStreamTest) {
    178   string filepath("/tmp/test_filepath");
    179   ScopedPathUnlinker scoped_unlinker(filepath);
    180   ASSERT_FALSE(FileStream::Open(filepath, false, false));
    181 
    182   auto stream = FileStream::Open(filepath, true, true);
    183   ASSERT_TRUE(stream.get() != nullptr);
    184   // Doesn't matter if it is not initialized. I will be overridden.
    185   Buffer buf(105);
    186   std::iota(buf.begin(), buf.end(), 0);
    187 
    188   ASSERT_TRUE(stream->Write(buf.data(), buf.size()));
    189 
    190   TestRead(stream.get(), buf);
    191   TestWrite(stream.get(), stream.get());
    192   TestWriteBoundary(stream.get());
    193   TestSeek(stream.get(), true);
    194   TestClose(stream.get());
    195 }
    196 
    197 TEST_F(StreamTest, PuffinStreamTest) {
    198   shared_ptr<Puffer> puffer(new Puffer());
    199   auto read_stream = PuffinStream::CreateForPuff(
    200       MemoryStream::CreateForRead(kDeflates8), puffer, kPuffs8.size(),
    201       kSubblockDeflateExtents8, kPuffExtents8);
    202   TestRead(read_stream.get(), kPuffs8);
    203   TestSeek(read_stream.get(), false);
    204   TestClose(read_stream.get());
    205 
    206   // Test the stream with puff cache.
    207   read_stream = PuffinStream::CreateForPuff(
    208       MemoryStream::CreateForRead(kDeflates8), puffer, kPuffs8.size(),
    209       kSubblockDeflateExtents8, kPuffExtents8, 8 /* max_cache_size */);
    210   TestRead(read_stream.get(), kPuffs8);
    211   TestSeek(read_stream.get(), false);
    212   TestClose(read_stream.get());
    213 
    214   Buffer buf(kDeflates8.size());
    215   shared_ptr<Huffer> huffer(new Huffer());
    216   auto write_stream = PuffinStream::CreateForHuff(
    217       MemoryStream::CreateForWrite(&buf), huffer, kPuffs8.size(),
    218       kSubblockDeflateExtents8, kPuffExtents8);
    219 
    220   ASSERT_TRUE(write_stream->Seek(0));
    221   for (size_t idx = 0; idx < kPuffs8.size(); idx++) {
    222     ASSERT_TRUE(write_stream->Write(&kPuffs8[idx], 1));
    223   }
    224   // Make sure the write works
    225   ASSERT_EQ(buf, kDeflates8);
    226 
    227   std::fill(buf.begin(), buf.end(), 0);
    228   ASSERT_TRUE(write_stream->Seek(0));
    229   ASSERT_TRUE(write_stream->Write(kPuffs8.data(), kPuffs8.size()));
    230   // Check its correctness.
    231   ASSERT_EQ(buf, kDeflates8);
    232 
    233   // Write entire buffer one byte at a time. (all zeros).
    234   std::fill(buf.begin(), buf.end(), 0);
    235   ASSERT_TRUE(write_stream->Seek(0));
    236   for (const auto& byte : kPuffs8) {
    237     ASSERT_TRUE(write_stream->Write(&byte, 1));
    238   }
    239   // Check its correctness.
    240   ASSERT_EQ(buf, kDeflates8);
    241 
    242   // No TestSeek is needed as PuffinStream is not supposed to seek to anywhere
    243   // except 0.
    244   TestClose(write_stream.get());
    245 }
    246 
    247 TEST_F(StreamTest, ExtentStreamTest) {
    248   Buffer buf(100);
    249   std::iota(buf.begin(), buf.end(), 0);
    250 
    251   vector<ByteExtent> extents = {{10, 10}, {25, 0}, {30, 10}};
    252   Buffer data = {10, 11, 12, 13, 14, 15, 16, 17, 18, 19,
    253                  30, 31, 32, 33, 34, 35, 36, 37, 38, 39};
    254 
    255   auto read_stream =
    256       ExtentStream::CreateForRead(MemoryStream::CreateForRead(buf), extents);
    257   TestSeek(read_stream.get(), false);
    258   TestRead(read_stream.get(), data);
    259   TestClose(read_stream.get());
    260 
    261   auto buf2 = buf;
    262   std::fill(data.begin(), data.end(), 3);
    263   for (const auto& extent : extents) {
    264     std::fill(buf.begin() + extent.offset,
    265               buf.begin() + (extent.offset + extent.length), 3);
    266   }
    267   auto write_stream = ExtentStream::CreateForWrite(
    268       MemoryStream::CreateForWrite(&buf2), extents);
    269   ASSERT_TRUE(write_stream->Seek(0));
    270   ASSERT_TRUE(write_stream->Write(data.data(), data.size()));
    271   EXPECT_EQ(buf2, buf);
    272 
    273   TestSeek(write_stream.get(), false);
    274   TestClose(write_stream.get());
    275 }
    276 
    277 }  // namespace puffin
    278