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