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