xref: /aosp_15_r20/external/perfetto/src/trace_processor/util/zip_reader_unittest.cc (revision 6dbdd20afdafa5e3ca9b8809fa73465d530080dc)
1 /*
2  * Copyright (C) 2022 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #include "src/trace_processor/util/zip_reader.h"
18 
19 #include <cstdint>
20 #include <cstring>
21 #include <ctime>
22 #include <string>
23 #include <vector>
24 
25 #include "perfetto/base/build_config.h"
26 #include "perfetto/base/status.h"
27 #include "perfetto/ext/base/string_view.h"
28 #include "perfetto/trace_processor/trace_blob.h"
29 #include "perfetto/trace_processor/trace_blob_view.h"
30 #include "src/base/test/status_matchers.h"
31 #include "test/gtest_and_gmock.h"
32 
33 namespace perfetto::trace_processor::util {
34 namespace {
35 using base::gtest_matchers::IsError;
36 
37 // This zip file contains the following:
38 // Zip file size: 386 bytes, number of entries: 2
39 // -rw-r--r--  3.0 unx        4 tx stor 22-Jul-25 16:43 stored_file
40 // -rw-r--r--  3.0 unx       89 tx defN 22-Jul-25 18:34 dir/deflated_file
41 // 2 files, 92 bytes uncompressed, 52 bytes compressed:  43.5%
42 //
43 // /stored_file      content: "foo"
44 // dir/deflated_file content: 2x "The quick brown fox jumps over the lazy dog\n"
45 const uint8_t kTestZip[] = {
46     0x50, 0x4b, 0x03, 0x04, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6a, 0x85,
47     0xf9, 0x54, 0xa8, 0x65, 0x32, 0x7e, 0x04, 0x00, 0x00, 0x00, 0x04, 0x00,
48     0x00, 0x00, 0x0b, 0x00, 0x1c, 0x00, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x64,
49     0x5f, 0x66, 0x69, 0x6c, 0x65, 0x55, 0x54, 0x09, 0x00, 0x03, 0x17, 0xba,
50     0xde, 0x62, 0x44, 0xba, 0xde, 0x62, 0x75, 0x78, 0x0b, 0x00, 0x01, 0x04,
51     0xce, 0x69, 0x02, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x66, 0x6f, 0x6f,
52     0x0a, 0x50, 0x4b, 0x03, 0x04, 0x14, 0x00, 0x00, 0x00, 0x08, 0x00, 0x47,
53     0x94, 0xf9, 0x54, 0xf2, 0x03, 0x92, 0x3c, 0x34, 0x00, 0x00, 0x00, 0x59,
54     0x00, 0x00, 0x00, 0x11, 0x00, 0x1c, 0x00, 0x64, 0x69, 0x72, 0x2f, 0x64,
55     0x65, 0x66, 0x6c, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x66, 0x69, 0x6c, 0x65,
56     0x55, 0x54, 0x09, 0x00, 0x03, 0x15, 0xd4, 0xde, 0x62, 0xf4, 0xba, 0xde,
57     0x62, 0x75, 0x78, 0x0b, 0x00, 0x01, 0x04, 0xce, 0x69, 0x02, 0x00, 0x04,
58     0x00, 0x00, 0x00, 0x00, 0x0b, 0xc9, 0x48, 0x55, 0x28, 0x2c, 0xcd, 0x4c,
59     0xce, 0x56, 0x48, 0x2a, 0xca, 0x2f, 0xcf, 0x53, 0x48, 0xcb, 0xaf, 0x50,
60     0xc8, 0x2a, 0xcd, 0x2d, 0x28, 0x56, 0xc8, 0x2f, 0x4b, 0x2d, 0x52, 0x28,
61     0x01, 0x4a, 0xe7, 0x24, 0x56, 0x55, 0x2a, 0xa4, 0xe4, 0xa7, 0x73, 0x85,
62     0x10, 0xa9, 0x36, 0xad, 0x08, 0xa8, 0x18, 0x00, 0x50, 0x4b, 0x01, 0x02,
63     0x1e, 0x03, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6a, 0x85, 0xf9, 0x54,
64     0xa8, 0x65, 0x32, 0x7e, 0x04, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
65     0x0b, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
66     0xa4, 0x81, 0x00, 0x00, 0x00, 0x00, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x64,
67     0x5f, 0x66, 0x69, 0x6c, 0x65, 0x55, 0x54, 0x05, 0x00, 0x03, 0x17, 0xba,
68     0xde, 0x62, 0x75, 0x78, 0x0b, 0x00, 0x01, 0x04, 0xce, 0x69, 0x02, 0x00,
69     0x04, 0x00, 0x00, 0x00, 0x00, 0x50, 0x4b, 0x01, 0x02, 0x1e, 0x03, 0x14,
70     0x00, 0x00, 0x00, 0x08, 0x00, 0x47, 0x94, 0xf9, 0x54, 0xf2, 0x03, 0x92,
71     0x3c, 0x34, 0x00, 0x00, 0x00, 0x59, 0x00, 0x00, 0x00, 0x11, 0x00, 0x18,
72     0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0xa4, 0x81, 0x49,
73     0x00, 0x00, 0x00, 0x64, 0x69, 0x72, 0x2f, 0x64, 0x65, 0x66, 0x6c, 0x61,
74     0x74, 0x65, 0x64, 0x5f, 0x66, 0x69, 0x6c, 0x65, 0x55, 0x54, 0x05, 0x00,
75     0x03, 0x15, 0xd4, 0xde, 0x62, 0x75, 0x78, 0x0b, 0x00, 0x01, 0x04, 0xce,
76     0x69, 0x02, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x50, 0x4b, 0x05, 0x06,
77     0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x02, 0x00, 0xa8, 0x00, 0x00, 0x00,
78     0xc8, 0x00, 0x00, 0x00, 0x00, 0x00};
79 
vec2str(const std::vector<uint8_t> & vec)80 std::string vec2str(const std::vector<uint8_t>& vec) {
81   return {reinterpret_cast<const char*>(vec.data()), vec.size()};
82 }
83 
ValidateTestZip(ZipReader & zr)84 void ValidateTestZip(ZipReader& zr) {
85   ASSERT_EQ(zr.files().size(), 2u);
86 
87   std::vector<uint8_t> dec;
88   ASSERT_EQ(zr.files()[0].name(), "stored_file");
89   ASSERT_EQ(zr.files()[0].GetDatetimeStr(), "2022-07-25 16:43:20");
90 
91   ASSERT_EQ(zr.files()[1].name(), "dir/deflated_file");
92   ASSERT_EQ(zr.files()[1].GetDatetimeStr(), "2022-07-25 18:34:14");
93 
94   // This file is STORE-d and doesn't require any decompression.
95   auto res = zr.files()[0].Decompress(&dec);
96   ASSERT_TRUE(res.ok()) << res.message();
97   ASSERT_EQ(dec.size(), 4u);
98   ASSERT_EQ(vec2str(dec), "foo\n");
99 
100   // This file is DEFLATE-d and requires zlib.
101 #if PERFETTO_BUILDFLAG(PERFETTO_ZLIB)
102   res = zr.files()[1].Decompress(&dec);
103   ASSERT_TRUE(res.ok()) << res.message();
104   ASSERT_EQ(dec.size(), 89u);
105   ASSERT_EQ(vec2str(dec),
106             "The quick brown fox jumps over the lazy dog\n"
107             "The quick brown fox jumps over the lazy frog\n");
108 #endif
109 }
110 
TEST(ZipReaderTest,ValidZip_OneShotParse)111 TEST(ZipReaderTest, ValidZip_OneShotParse) {
112   ZipReader zr;
113   ASSERT_OK(
114       zr.Parse(TraceBlobView(TraceBlob::CopyFrom(kTestZip, sizeof(kTestZip)))));
115   ValidateTestZip(zr);
116 }
117 
TEST(ZipReaderTest,ValidZip_OneByteChunks)118 TEST(ZipReaderTest, ValidZip_OneByteChunks) {
119   ZipReader zr;
120   for (auto i : kTestZip) {
121     ASSERT_OK(zr.Parse(TraceBlobView(TraceBlob::CopyFrom(&i, 1))));
122   }
123   ValidateTestZip(zr);
124 }
125 
TEST(ZipReaderTest,MalformedZip_InvalidSignature)126 TEST(ZipReaderTest, MalformedZip_InvalidSignature) {
127   ZipReader zr;
128   uint8_t content[sizeof(kTestZip)];
129   memcpy(content, kTestZip, sizeof(kTestZip));
130   content[0] = 0xff;  // Invalid signature
131   ASSERT_THAT(
132       zr.Parse(TraceBlobView(TraceBlob::CopyFrom(content, sizeof(kTestZip)))),
133       IsError());
134   ASSERT_EQ(zr.files().size(), 0u);
135 }
136 
TEST(ZipReaderTest,MalformedZip_VersionTooHigh)137 TEST(ZipReaderTest, MalformedZip_VersionTooHigh) {
138   ZipReader zr;
139   uint8_t content[sizeof(kTestZip)];
140   memcpy(content, kTestZip, sizeof(kTestZip));
141   content[5] = 9;  // Version: 9.0
142   ASSERT_THAT(
143       zr.Parse(TraceBlobView(TraceBlob::CopyFrom(content, sizeof(kTestZip)))),
144       IsError());
145   ASSERT_EQ(zr.files().size(), 0u);
146 }
147 
TEST(ZipReaderTest,TruncatedZip)148 TEST(ZipReaderTest, TruncatedZip) {
149   ZipReader zr;
150   ASSERT_OK(zr.Parse(TraceBlobView(TraceBlob::CopyFrom(kTestZip, 40))));
151   ASSERT_EQ(zr.files().size(), 0u);
152 }
153 
TEST(ZipReaderTest,Find)154 TEST(ZipReaderTest, Find) {
155   ZipReader zr;
156   ASSERT_OK(
157       zr.Parse(TraceBlobView(TraceBlob::CopyFrom(kTestZip, sizeof(kTestZip)))));
158   ASSERT_EQ(zr.Find("stored_file")->name(), "stored_file");
159   ASSERT_EQ(zr.Find("dir/deflated_file")->name(), "dir/deflated_file");
160   ASSERT_EQ(nullptr, zr.Find("stored_f"));
161   ASSERT_EQ(nullptr, zr.Find("_file*"));
162   ASSERT_EQ(nullptr, zr.Find("dirz/deflated_file"));
163 }
164 
165 // All the tests below require zlib.
166 #if PERFETTO_BUILDFLAG(PERFETTO_ZLIB)
167 
TEST(ZipReaderTest,ValidZip_DecompressLines)168 TEST(ZipReaderTest, ValidZip_DecompressLines) {
169   ZipReader zr;
170   ASSERT_OK(
171       zr.Parse(TraceBlobView(TraceBlob::CopyFrom(kTestZip, sizeof(kTestZip)))));
172   ValidateTestZip(zr);
173   int num_callbacks = 0;
174   zr.files()[1].DecompressLines(
175       [&](const std::vector<base::StringView>& lines) {
176         ASSERT_EQ(num_callbacks++, 0);
177         ASSERT_TRUE(lines.size() == 2);
178         ASSERT_EQ(lines[0].ToStdString(),
179                   "The quick brown fox jumps over the lazy dog");
180         ASSERT_EQ(lines[1].ToStdString(),
181                   "The quick brown fox jumps over the lazy frog");
182       });
183 
184   ASSERT_EQ(num_callbacks, 1);
185 }
186 
TEST(ZipReaderTest,MalformedZip_DecomprError)187 TEST(ZipReaderTest, MalformedZip_DecomprError) {
188   ZipReader zr;
189   uint8_t content[sizeof(kTestZip)];
190   memcpy(content, kTestZip, sizeof(kTestZip));
191 
192   // The 2nd file header starts at 103, the payload at 30 (header) + 17 (fname)
193   // bytes later. We start clobbering at offset=150, so the header is intanct
194   // but decompression fails.
195   memset(&content[150], 0, 40);
196   ASSERT_OK(
197       zr.Parse(TraceBlobView(TraceBlob::CopyFrom(content, sizeof(kTestZip)))));
198   ASSERT_EQ(zr.files().size(), 2u);
199   std::vector<uint8_t> ignored;
200   ASSERT_OK(zr.files()[0].Decompress(&ignored));
201   ASSERT_THAT(zr.files()[1].Decompress(&ignored), IsError());
202 }
203 
204 #endif  // PERFETTO_BUILDFLAG(PERFETTO_ZLIB)
205 
206 }  // namespace
207 }  // namespace perfetto::trace_processor::util
208