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