blob: a58332e1c4d0d87bc84c8a463bdbecf7de6becd0 [file] [log] [blame]
/*
* Copyright (C) 2022 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "test/gtest_and_gmock.h"
#include "src/trace_processor/util/gzip_utils.h"
#include <zlib.h>
#include <fstream>
#include <iostream>
#include "perfetto/base/logging.h"
using std::string;
namespace perfetto {
namespace trace_processor {
namespace util {
static std::string TrivialGzipCompress(const std::string& input) {
constexpr auto buffer_len = 10000;
std::unique_ptr<char[]> output_ptr(new char[buffer_len]);
char* output = output_ptr.get();
z_stream defstream;
defstream.zalloc = Z_NULL;
defstream.zfree = Z_NULL;
defstream.opaque = Z_NULL;
defstream.avail_in = uint32_t(input.size());
defstream.next_in =
const_cast<Bytef*>(reinterpret_cast<const Bytef*>(input.data()));
defstream.avail_out = buffer_len;
defstream.next_out = reinterpret_cast<Bytef*>(output);
deflateInit(&defstream, Z_BEST_COMPRESSION); // GZip decompress
deflate(&defstream, Z_FINISH);
deflateEnd(&defstream);
PERFETTO_CHECK(defstream.avail_out > 0);
return std::string(output, buffer_len - defstream.avail_out);
}
// Trivially decompress using ZlibOnlineDecompress.
// It's called 'trivial' because we are feeding the entire input in one shot.
static std::string TrivialDecompress(const std::string& input) {
string output;
GzipDecompressor decompressor;
decompressor.FeedAndExtract(
reinterpret_cast<const uint8_t*>(input.data()), uint32_t(input.size()),
[&](const uint8_t* data, size_t len) {
output.append(reinterpret_cast<const char*>(data), len);
});
return output;
}
// Decompress a large GZip file using a in-memory buffer of 4KB, and write the
// decompressed output in another file.
static void DecompressGzipFileInFileOut(const std::string& input_file,
const std::string& output_file) {
std::ofstream output(output_file.c_str(), std::ios::out | std::ios::binary);
std::ifstream input(input_file.c_str(), std::ios::binary);
GzipDecompressor decompressor;
constexpr uint32_t buffer_sizeof = 4096;
char buffer[buffer_sizeof];
while (!input.eof()) {
input.read(buffer, buffer_sizeof);
decompressor.FeedAndExtract(
reinterpret_cast<const uint8_t*>(buffer), size_t(input.gcount()),
[&](const uint8_t* data, size_t len) {
output.write(reinterpret_cast<const char*>(data),
std::streamsize(len));
});
}
EXPECT_FALSE(input.bad());
}
TEST(GzipDecompressor, Basic) {
string input = "Abc..Def..Ghi";
string compressed = TrivialGzipCompress(input);
EXPECT_EQ(21u, compressed.size());
string decompressed = TrivialDecompress(compressed);
EXPECT_EQ(input, decompressed);
}
TEST(GzipDecompressor, Streaming) {
string input = "Abc..Def..Ghi";
string compressed = TrivialGzipCompress(input);
string decompressed;
auto consumer = [&](const uint8_t* data, size_t len) {
decompressed.append(reinterpret_cast<const char*>(data), len);
};
GzipDecompressor decompressor;
auto compressed_u8 = reinterpret_cast<const uint8_t*>(compressed.data());
ASSERT_GT(compressed.size(), 17u);
decompressor.FeedAndExtract(compressed_u8, 7, consumer);
decompressor.FeedAndExtract(compressed_u8 + 7, 10, consumer);
decompressor.FeedAndExtract(compressed_u8 + 17, compressed.size() - 17,
consumer);
EXPECT_EQ(input, decompressed);
}
static std::string ReadFile(const std::string& file_name) {
std::ifstream fd(file_name, std::ios::binary);
std::stringstream buffer;
buffer << fd.rdbuf();
fd.close();
return buffer.str();
}
static void WriteFile(const std::string& file_name,
const std::string& content) {
std::ofstream fd(file_name, std::ios::out | std::ios::binary);
fd.write(content.data(), std::streamsize(content.size()));
fd.close();
}
TEST(GzipDecompressor, DISABLED_FileInFileOut) {
auto big_string = []() {
std::string output;
for (int i = 0; i < 1000; i++) {
output += "Abc..Def..Ghi."; // len = 14
}
return output;
}();
constexpr auto gz_file = "/tmp/abc.gz";
constexpr auto txt_file = "/tmp/abc.txt";
EXPECT_EQ(size_t(1000 * 14), big_string.size());
WriteFile(gz_file, TrivialGzipCompress(big_string));
DecompressGzipFileInFileOut(gz_file, txt_file);
EXPECT_TRUE(ReadFile(txt_file) == big_string);
}
} // namespace util
} // namespace trace_processor
} // namespace perfetto