• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.
TestRead(StreamInterface * stream,const Buffer & data)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 
TestWriteBoundary(StreamInterface * stream)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 
TestWrite(StreamInterface * write_stream,StreamInterface * read_stream)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|.
TestSeek(StreamInterface * stream,bool seek_end_is_fine)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 
TestClose(StreamInterface * stream)157   void TestClose(StreamInterface* stream) { ASSERT_TRUE(stream->Close()); }
158 };
159 
TEST_F(StreamTest,MemoryStreamTest)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 
TEST_F(StreamTest,FileStreamTest)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 
TEST_F(StreamTest,PuffinStreamTest)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 
TEST_F(StreamTest,ExtentStreamTest)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