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