xref: /aosp_15_r20/external/perfetto/src/trace_processor/util/gzip_utils_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 "test/gtest_and_gmock.h"
18 
19 #include "src/trace_processor/util/gzip_utils.h"
20 
21 #include <zlib.h>
22 #include <fstream>
23 #include <iostream>
24 #include "perfetto/base/logging.h"
25 
26 using std::string;
27 
28 namespace perfetto {
29 namespace trace_processor {
30 namespace util {
31 
TrivialGzipCompress(const std::string & input)32 static std::string TrivialGzipCompress(const std::string& input) {
33   constexpr auto buffer_len = 10000;
34   std::unique_ptr<char[]> output_ptr(new char[buffer_len]);
35   char* output = output_ptr.get();
36   z_stream defstream;
37   defstream.zalloc = Z_NULL;
38   defstream.zfree = Z_NULL;
39   defstream.opaque = Z_NULL;
40   defstream.avail_in = uint32_t(input.size());
41   defstream.next_in =
42       const_cast<Bytef*>(reinterpret_cast<const Bytef*>(input.data()));
43   defstream.avail_out = buffer_len;
44   defstream.next_out = reinterpret_cast<Bytef*>(output);
45   deflateInit(&defstream, Z_BEST_COMPRESSION);  // GZip decompress
46   deflate(&defstream, Z_FINISH);
47   deflateEnd(&defstream);
48   PERFETTO_CHECK(defstream.avail_out > 0);
49   return std::string(output, buffer_len - defstream.avail_out);
50 }
51 
52 // Trivially decompress using ZlibOnlineDecompress.
53 // It's called 'trivial' because we are feeding the entire input in one shot.
TrivialDecompress(const std::string & input)54 static std::string TrivialDecompress(const std::string& input) {
55   string output;
56   GzipDecompressor decompressor;
57   decompressor.FeedAndExtract(
58       reinterpret_cast<const uint8_t*>(input.data()), uint32_t(input.size()),
59       [&](const uint8_t* data, size_t len) {
60         output.append(reinterpret_cast<const char*>(data), len);
61       });
62   return output;
63 }
64 
65 // Decompress a large GZip file using a in-memory buffer of 4KB, and write the
66 // decompressed output in another file.
DecompressGzipFileInFileOut(const std::string & input_file,const std::string & output_file)67 static void DecompressGzipFileInFileOut(const std::string& input_file,
68                                         const std::string& output_file) {
69   std::ofstream output(output_file.c_str(), std::ios::out | std::ios::binary);
70   std::ifstream input(input_file.c_str(), std::ios::binary);
71   GzipDecompressor decompressor;
72   constexpr uint32_t buffer_sizeof = 4096;
73   char buffer[buffer_sizeof];
74   while (!input.eof()) {
75     input.read(buffer, buffer_sizeof);
76     decompressor.FeedAndExtract(
77         reinterpret_cast<const uint8_t*>(buffer), size_t(input.gcount()),
78         [&](const uint8_t* data, size_t len) {
79           output.write(reinterpret_cast<const char*>(data),
80                        std::streamsize(len));
81         });
82   }
83   EXPECT_FALSE(input.bad());
84 }
85 
TEST(GzipDecompressor,Basic)86 TEST(GzipDecompressor, Basic) {
87   string input = "Abc..Def..Ghi";
88   string compressed = TrivialGzipCompress(input);
89   EXPECT_EQ(21u, compressed.size());
90   string decompressed = TrivialDecompress(compressed);
91   EXPECT_EQ(input, decompressed);
92 }
93 
TEST(GzipDecompressor,Streaming)94 TEST(GzipDecompressor, Streaming) {
95   string input = "Abc..Def..Ghi";
96   string compressed = TrivialGzipCompress(input);
97   string decompressed;
98   auto consumer = [&](const uint8_t* data, size_t len) {
99     decompressed.append(reinterpret_cast<const char*>(data), len);
100   };
101   GzipDecompressor decompressor;
102   auto compressed_u8 = reinterpret_cast<const uint8_t*>(compressed.data());
103   ASSERT_GT(compressed.size(), 17u);
104   decompressor.FeedAndExtract(compressed_u8, 7, consumer);
105   decompressor.FeedAndExtract(compressed_u8 + 7, 10, consumer);
106   decompressor.FeedAndExtract(compressed_u8 + 17, compressed.size() - 17,
107                               consumer);
108 
109   EXPECT_EQ(input, decompressed);
110 }
111 
ReadFile(const std::string & file_name)112 static std::string ReadFile(const std::string& file_name) {
113   std::ifstream fd(file_name, std::ios::binary);
114   std::stringstream buffer;
115   buffer << fd.rdbuf();
116   fd.close();
117   return buffer.str();
118 }
119 
WriteFile(const std::string & file_name,const std::string & content)120 static void WriteFile(const std::string& file_name,
121                       const std::string& content) {
122   std::ofstream fd(file_name, std::ios::out | std::ios::binary);
123   fd.write(content.data(), std::streamsize(content.size()));
124   fd.close();
125 }
126 
TEST(GzipDecompressor,DISABLED_FileInFileOut)127 TEST(GzipDecompressor, DISABLED_FileInFileOut) {
128   auto big_string = []() {
129     std::string output;
130     for (int i = 0; i < 1000; i++) {
131       output += "Abc..Def..Ghi.";  // len = 14
132     }
133     return output;
134   }();
135   constexpr auto gz_file = "/tmp/abc.gz";
136   constexpr auto txt_file = "/tmp/abc.txt";
137   EXPECT_EQ(size_t(1000 * 14), big_string.size());
138   WriteFile(gz_file, TrivialGzipCompress(big_string));
139   DecompressGzipFileInFileOut(gz_file, txt_file);
140   EXPECT_TRUE(ReadFile(txt_file) == big_string);
141 }
142 
143 }  // namespace util
144 }  // namespace trace_processor
145 }  // namespace perfetto
146