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