xref: /aosp_15_r20/external/puffin/src/utils_unittest.cc (revision 07fb1d065b7cfb4729786fadd42a612532d2f466)
1 // Copyright 2017 The ChromiumOS Authors
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 <unistd.h>
6 
7 #include <vector>
8 
9 #include "gtest/gtest.h"
10 
11 #include "puffin/file_stream.h"
12 #include "puffin/memory_stream.h"
13 #include "puffin/src/include/puffin/common.h"
14 #include "puffin/src/include/puffin/utils.h"
15 #include "puffin/src/unittest_common.h"
16 
17 using std::string;
18 using std::vector;
19 
20 namespace puffin {
21 
22 namespace {
23 const uint8_t kZipEntries[] = {
24     0x50, 0x4b, 0x03, 0x04, 0x14, 0x00, 0x02, 0x00, 0x08, 0x00, 0xfc, 0x88,
25     0x28, 0x4c, 0xcb, 0x86, 0xe1, 0x80, 0x06, 0x00, 0x00, 0x00, 0x09, 0x00,
26     0x00, 0x00, 0x01, 0x00, 0x1c, 0x00, 0x31, 0x55, 0x54, 0x09, 0x00, 0x03,
27     0xec, 0x15, 0x54, 0x5a, 0x49, 0x10, 0x54, 0x5a, 0x75, 0x78, 0x0b, 0x00,
28     0x01, 0x04, 0x8f, 0x66, 0x05, 0x00, 0x04, 0x88, 0x13, 0x00, 0x00, 0x33,
29     0x34, 0x84, 0x00, 0x2e, 0x00, 0x50, 0x4b, 0x03, 0x04, 0x14, 0x00, 0x02,
30     0x00, 0x08, 0x00, 0x01, 0x89, 0x28, 0x4c, 0xe0, 0xe8, 0x6f, 0x6d, 0x06,
31     0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x01, 0x00, 0x1c, 0x00, 0x32,
32     0x55, 0x54, 0x09, 0x00, 0x03, 0xf1, 0x15, 0x54, 0x5a, 0x38, 0x10, 0x54,
33     0x5a, 0x75, 0x78, 0x0b, 0x00, 0x01, 0x04, 0x8f, 0x66, 0x05, 0x00, 0x04,
34     0x88, 0x13, 0x00, 0x00, 0x33, 0x32, 0x82, 0x01, 0x2e, 0x00};
35 
36 // (echo "666666" > 2 && zip -fd test.zip 2 &&
37 //  cat test.zip | hexdump -v -e '10/1 "0x%02x, " "\n"')
38 const uint8_t kZipEntryWithDataDescriptor[] = {
39     0x50, 0x4b, 0x03, 0x04, 0x14, 0x00, 0x08, 0x00, 0x08, 0x00, 0x0b, 0x74,
40     0x2b, 0x4c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x00,
41     0x00, 0x00, 0x01, 0x00, 0x1c, 0x00, 0x32, 0x55, 0x54, 0x09, 0x00, 0x03,
42     0xf5, 0xe5, 0x57, 0x5a, 0xf2, 0xe5, 0x57, 0x5a, 0x75, 0x78, 0x0b, 0x00,
43     0x01, 0x04, 0x8f, 0x66, 0x05, 0x00, 0x04, 0x88, 0x13, 0x00, 0x00, 0x33,
44     0x33, 0x03, 0x01, 0x2e, 0x00, 0x50, 0x4b, 0x07, 0x08, 0xb4, 0xa0, 0xf2,
45     0x36, 0x06, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x50, 0x4b, 0x03,
46     0x04, 0x14, 0x00, 0x08, 0x00, 0x08, 0x00, 0x0b, 0x74, 0x2b, 0x4c, 0x00,
47     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x01,
48     0x00, 0x1c, 0x00, 0x32, 0x55, 0x54, 0x09, 0x00, 0x03, 0xf5, 0xe5, 0x57,
49     0x5a, 0xf2, 0xe5, 0x57, 0x5a, 0x75, 0x78, 0x0b, 0x00, 0x01, 0x04, 0x8f,
50     0x66, 0x05, 0x00, 0x04, 0x88, 0x13, 0x00, 0x00, 0x33, 0x33, 0x03, 0x01,
51     0x2e, 0x00, 0xb4, 0xa0, 0xf2, 0x36, 0x06, 0x00, 0x00, 0x00, 0x07, 0x00,
52     0x00, 0x00};
53 
54 // echo "0123456789" > test1.txt && echo "9876543210" > test2.txt &&
55 // gzip -kf test1.txt test2.txt && cat test1.txt.gz test2.txt.gz |
56 // hexdump -v -e '12/1 "0x%02x, " "\n"'
57 const uint8_t kGzipEntryWithMultipleMembers[] = {
58     0x1f, 0x8b, 0x08, 0x08, 0x77, 0xd5, 0x84, 0x5a, 0x00, 0x03, 0x74, 0x65,
59     0x73, 0x74, 0x31, 0x2e, 0x74, 0x78, 0x74, 0x00, 0x33, 0x30, 0x34, 0x32,
60     0x36, 0x31, 0x35, 0x33, 0xb7, 0xb0, 0xe4, 0x02, 0x00, 0xd1, 0xe5, 0x76,
61     0x40, 0x0b, 0x00, 0x00, 0x00, 0x1f, 0x8b, 0x08, 0x08, 0x77, 0xd5, 0x84,
62     0x5a, 0x00, 0x03, 0x74, 0x65, 0x73, 0x74, 0x32, 0x2e, 0x74, 0x78, 0x74,
63     0x00, 0xb3, 0xb4, 0x30, 0x37, 0x33, 0x35, 0x31, 0x36, 0x32, 0x34, 0xe0,
64     0x02, 0x00, 0x20, 0x9c, 0x5f, 0x89, 0x0b, 0x00, 0x00, 0x00};
65 
66 // echo "0123456789" > test1.txt && gzip -kf test1.txt && cat test1.txt.gz |
67 // hexdump -v -e '12/1 "0x%02x, " "\n"'
68 // And manually insert extra field with two byte length (10) followed by:
69 // echo "extrafield" | hexdump -v -e '12/1 "0x%02x, " "\n"'
70 // Then change the forth byte of array to -x0c to enable the extra field.
71 const uint8_t kGzipEntryWithExtraField[] = {
72     0x1f, 0x8b, 0x08, 0x0c, 0xcf, 0x0e, 0x86, 0x5a, 0x00, 0x03,
73     // Extra field begin
74     0x0A, 0x00, 0x65, 0x78, 0x74, 0x72, 0x61, 0x66, 0x69, 0x65, 0x6c, 0x64,
75     // Extra field end
76     0x74, 0x65, 0x73, 0x74, 0x31, 0x2e, 0x74, 0x78, 0x74, 0x00, 0x33, 0x30,
77     0x34, 0x32, 0x36, 0x31, 0x35, 0x33, 0xb7, 0xb0, 0xe4, 0x02, 0x00, 0xd1,
78     0xe5, 0x76, 0x40, 0x0b, 0x00, 0x00, 0x00};
79 
80 // echo "0123456789" | zlib-flate -compress |
81 // hexdump -v -e '12/1 "0x%02x, " "\n"'
82 const uint8_t kZlibEntry[] = {0x78, 0x9c, 0x33, 0x30, 0x34, 0x32, 0x36,
83                               0x31, 0x35, 0x33, 0xb7, 0xb0, 0xe4, 0x02,
84                               0x00, 0x0d, 0x17, 0x02, 0x18};
85 
FindDeflatesInZlibBlocks(const Buffer & src,const vector<ByteExtent> & zlibs,const vector<BitExtent> & deflates)86 void FindDeflatesInZlibBlocks(const Buffer& src,
87                               const vector<ByteExtent>& zlibs,
88                               const vector<BitExtent>& deflates) {
89   string tmp_file;
90   ASSERT_TRUE(MakeTempFile(&tmp_file, nullptr));
91   ScopedPathUnlinker unlinker(tmp_file);
92   auto src_stream = FileStream::Open(tmp_file, false, true);
93   ASSERT_TRUE(src_stream);
94   ASSERT_TRUE(src_stream->Write(src.data(), src.size()));
95   ASSERT_TRUE(src_stream->Close());
96 
97   vector<BitExtent> deflates_out;
98   ASSERT_TRUE(LocateDeflatesInZlibBlocks(tmp_file, zlibs, &deflates_out));
99   ASSERT_EQ(deflates, deflates_out);
100 }
101 
CheckFindPuffLocation(const Buffer & compressed,const vector<BitExtent> & deflates,const vector<ByteExtent> & expected_puffs,uint64_t expected_puff_size)102 void CheckFindPuffLocation(const Buffer& compressed,
103                            const vector<BitExtent>& deflates,
104                            const vector<ByteExtent>& expected_puffs,
105                            uint64_t expected_puff_size) {
106   auto src = MemoryStream::CreateForRead(compressed);
107   vector<ByteExtent> puffs;
108   uint64_t puff_size;
109   ASSERT_TRUE(FindPuffLocations(src, deflates, &puffs, &puff_size));
110   EXPECT_EQ(puffs, expected_puffs);
111   EXPECT_EQ(puff_size, expected_puff_size);
112 }
113 }  // namespace
114 
115 // Test Simple Puffing of the source.
TEST(UtilsTest,FindPuffLocations1Test)116 TEST(UtilsTest, FindPuffLocations1Test) {
117   CheckFindPuffLocation(kDeflatesSample1, kSubblockDeflateExtentsSample1,
118                         kPuffExtentsSample1, kPuffsSample1.size());
119 }
120 
TEST(UtilsTest,FindPuffLocations2Test)121 TEST(UtilsTest, FindPuffLocations2Test) {
122   CheckFindPuffLocation(kDeflatesSample2, kSubblockDeflateExtentsSample2,
123                         kPuffExtentsSample2, kPuffsSample2.size());
124 }
125 
TEST(UtilsTest,LocateDeflatesInZlib)126 TEST(UtilsTest, LocateDeflatesInZlib) {
127   Buffer zlib_data(kZlibEntry, std::end(kZlibEntry));
128   vector<BitExtent> deflates;
129   vector<BitExtent> expected_deflates = {{16, 98}};
130   EXPECT_TRUE(LocateDeflatesInZlib(zlib_data, &deflates));
131   EXPECT_EQ(deflates, expected_deflates);
132 }
133 
TEST(UtilsTest,LocateDeflatesInEmptyZlib)134 TEST(UtilsTest, LocateDeflatesInEmptyZlib) {
135   Buffer empty;
136   vector<ByteExtent> empty_zlibs;
137   vector<BitExtent> empty_deflates;
138   FindDeflatesInZlibBlocks(empty, empty_zlibs, empty_deflates);
139 }
140 
TEST(UtilsTest,LocateDeflatesInZlibWithInvalidFields)141 TEST(UtilsTest, LocateDeflatesInZlibWithInvalidFields) {
142   Buffer zlib_data(kZlibEntry, std::end(kZlibEntry));
143   auto cmf = zlib_data[0];
144   auto flag = zlib_data[1];
145 
146   vector<BitExtent> deflates;
147   zlib_data[0] = cmf & 0xF0;
148   EXPECT_FALSE(LocateDeflatesInZlib(zlib_data, &deflates));
149   zlib_data[0] = cmf | (8 << 4);
150   EXPECT_FALSE(LocateDeflatesInZlib(zlib_data, &deflates));
151   zlib_data[0] = cmf;  // Correct it.
152 
153   zlib_data[1] = flag & 0xF0;
154   EXPECT_FALSE(LocateDeflatesInZlib(zlib_data, &deflates));
155 }
156 
TEST(UtilsTest,LocateDeflatesInZipArchiveSmoke)157 TEST(UtilsTest, LocateDeflatesInZipArchiveSmoke) {
158   Buffer zip_entries(kZipEntries, std::end(kZipEntries));
159   vector<BitExtent> deflates;
160   vector<BitExtent> expected_deflates = {{472, 46}, {992, 46}};
161   EXPECT_TRUE(LocateDeflatesInZipArchive(zip_entries, &deflates));
162   EXPECT_EQ(deflates, expected_deflates);
163 }
164 
TEST(UtilsTest,LocateDeflatesInZipArchiveWithDataDescriptor)165 TEST(UtilsTest, LocateDeflatesInZipArchiveWithDataDescriptor) {
166   Buffer zip_entries(kZipEntryWithDataDescriptor,
167                      std::end(kZipEntryWithDataDescriptor));
168   vector<BitExtent> deflates;
169   vector<BitExtent> expected_deflates = {{472, 46}, {1120, 46}};
170   EXPECT_TRUE(LocateDeflatesInZipArchive(zip_entries, &deflates));
171   EXPECT_EQ(deflates, expected_deflates);
172 }
173 
TEST(UtilsTest,LocateDeflatesInZipArchiveErrorChecks)174 TEST(UtilsTest, LocateDeflatesInZipArchiveErrorChecks) {
175   Buffer zip_entries(kZipEntries, std::end(kZipEntries));
176   // Construct a invalid zip entry whose size overflows.
177   zip_entries[29] = 0xff;
178   vector<BitExtent> deflates_overflow;
179   vector<BitExtent> expected_deflates = {{992, 46}};
180   EXPECT_TRUE(LocateDeflatesInZipArchive(zip_entries, &deflates_overflow));
181   EXPECT_EQ(deflates_overflow, expected_deflates);
182 
183   zip_entries.resize(128);
184   vector<BitExtent> deflates_incomplete;
185   EXPECT_TRUE(LocateDeflatesInZipArchive(zip_entries, &deflates_incomplete));
186   EXPECT_TRUE(deflates_incomplete.empty());
187 }
188 
TEST(UtilsTest,LocateDeflatesInGzip)189 TEST(UtilsTest, LocateDeflatesInGzip) {
190   Buffer gzip_data(kGzipEntryWithMultipleMembers,
191                    std::end(kGzipEntryWithMultipleMembers));
192   vector<BitExtent> deflates;
193   vector<BitExtent> expected_deflates = {{160, 98}, {488, 98}};
194   EXPECT_TRUE(LocateDeflatesInGzip(gzip_data, &deflates));
195   EXPECT_EQ(deflates, expected_deflates);
196 }
197 
TEST(UtilsTest,LocateDeflatesInGzipFail)198 TEST(UtilsTest, LocateDeflatesInGzipFail) {
199   Buffer gzip_data(kGzipEntryWithMultipleMembers,
200                    std::end(kGzipEntryWithMultipleMembers));
201   gzip_data[0] ^= 1;
202   vector<BitExtent> deflates;
203   EXPECT_FALSE(LocateDeflatesInGzip(gzip_data, &deflates));
204 }
205 
TEST(UtilsTest,LocateDeflatesInGzipWithPadding)206 TEST(UtilsTest, LocateDeflatesInGzipWithPadding) {
207   Buffer gzip_data(kGzipEntryWithMultipleMembers,
208                    std::end(kGzipEntryWithMultipleMembers));
209   gzip_data.resize(gzip_data.size() + 100);
210   vector<BitExtent> deflates;
211   vector<BitExtent> expected_deflates = {{160, 98}, {488, 98}};
212   EXPECT_TRUE(LocateDeflatesInGzip(gzip_data, &deflates));
213   EXPECT_EQ(deflates, expected_deflates);
214 }
215 
TEST(UtilsTest,LocateDeflatesInGzipWithExtraField)216 TEST(UtilsTest, LocateDeflatesInGzipWithExtraField) {
217   Buffer gzip_data(kGzipEntryWithExtraField,
218                    std::end(kGzipEntryWithExtraField));
219   vector<BitExtent> deflates;
220   vector<BitExtent> expected_deflates = {{256, 98}};
221   EXPECT_TRUE(LocateDeflatesInGzip(gzip_data, &deflates));
222   EXPECT_EQ(deflates, expected_deflates);
223 }
224 
TEST(UtilsTest,RemoveEqualBitExtents)225 TEST(UtilsTest, RemoveEqualBitExtents) {
226   Buffer data1 = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
227   Buffer data2 = {1, 2, 3, 4, 5, 5, 6, 7, 8, 9};
228   vector<BitExtent> ext1 = {{0, 10}, {10, 14}, {25, 15}, {40, 8}, {50, 23}};
229   vector<BitExtent> ext2 = {{0, 10}, {17, 15}, {32, 8}, {40, 8}, {50, 23}};
230   RemoveEqualBitExtents(data1, data2, &ext1, &ext2);
231   vector<BitExtent> expected_ext1 = {{0, 10}, {10, 14}};
232   EXPECT_EQ(expected_ext1, ext1);
233   vector<BitExtent> expected_ext2 = {{0, 10}};
234   EXPECT_EQ(expected_ext2, ext2);
235   RemoveEqualBitExtents(data1, data2, &ext1, &ext1);
236   EXPECT_EQ(expected_ext1, ext1);
237   RemoveEqualBitExtents(data1, data1, &ext1, &ext1);
238   EXPECT_TRUE(ext1.empty());
239   expected_ext1 = ext1 = {{0, 0}, {1, 1}, {2, 7}};
240   RemoveEqualBitExtents(data1, data2, &ext1, &ext2);
241   EXPECT_EQ(expected_ext1, ext1);
242   EXPECT_EQ(expected_ext2, ext2);
243 }
244 
TEST(UtilsTest,RemoveDeflatesWithBadDistanceCaches)245 TEST(UtilsTest, RemoveDeflatesWithBadDistanceCaches) {
246   vector<BitExtent> deflates(kProblematicCacheDeflateExtents), empty;
247   EXPECT_TRUE(
248       RemoveDeflatesWithBadDistanceCaches(kProblematicCache, &deflates));
249   EXPECT_EQ(deflates, empty);
250 
251   // Just a sanity check to make sure this function is not removing anything
252   // else.
253   deflates = kSubblockDeflateExtentsSample1;
254   EXPECT_TRUE(RemoveDeflatesWithBadDistanceCaches(kDeflatesSample1, &deflates));
255   EXPECT_EQ(deflates, kSubblockDeflateExtentsSample1);
256 
257   // Now combine three deflates and make sure it is doing the right job.
258   Buffer data;
259   data.insert(data.end(), kDeflatesSample1.begin(), kDeflatesSample1.end());
260   data.insert(data.end(), kProblematicCache.begin(), kProblematicCache.end());
261   data.insert(data.end(), kDeflatesSample1.begin(), kDeflatesSample1.end());
262 
263   deflates = kSubblockDeflateExtentsSample1;
264   size_t offset = kDeflatesSample1.size() * 8;
265   for (const auto& deflate : kProblematicCacheDeflateExtents) {
266     deflates.emplace_back(deflate.offset + offset, deflate.length);
267   }
268   offset += kProblematicCache.size() * 8;
269   for (const auto& deflate : kSubblockDeflateExtentsSample1) {
270     deflates.emplace_back(deflate.offset + offset, deflate.length);
271   }
272 
273   auto expected_deflates(deflates);
274   expected_deflates.erase(expected_deflates.begin() +
275                           kSubblockDeflateExtentsSample1.size());
276 
277   EXPECT_TRUE(RemoveDeflatesWithBadDistanceCaches(data, &deflates));
278   EXPECT_EQ(deflates, expected_deflates);
279 }
280 
281 }  // namespace puffin
282