First release of open-appsec source code

This commit is contained in:
roybarda
2022-10-26 19:33:19 +03:00
parent 3883109caf
commit a883352f79
1353 changed files with 276290 additions and 1 deletions

View File

@@ -0,0 +1,9 @@
include_directories(${ng_module_osrc_zlib_path}/include)
add_definitions(-DZLIB_CONST)
add_library(compression_utils SHARED compression_utils.cc)
add_subdirectory(compression_utils_ut)
install(TARGETS compression_utils DESTINATION lib)
install(TARGETS compression_utils DESTINATION http_transaction_handler_service/lib)

View File

@@ -0,0 +1,384 @@
// Copyright (C) 2022 Check Point Software Technologies Ltd. All rights reserved.
// 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 "compression_utils.h"
#include <iostream>
#include <sstream>
#include <array>
#include <vector>
#include <tuple>
#include <strings.h>
#include <string.h>
#include <zlib.h>
using namespace std;
using DebugFunction = void(*)(const char *);
static const int max_debug_level = static_cast<int>(CompressionUtilsDebugLevel::COMPRESSION_DBG_LEVEL_ASSERTION);
static void
defaultPrint(const char *debug_message)
{
cerr << debug_message;
};
class ZlibDebugStream
{
public:
ZlibDebugStream(const CompressionUtilsDebugLevel _debug_level) : debug_level(_debug_level) {}
~ZlibDebugStream()
{
ZlibDebugStream::debug_funcs[debug_level](debug_message.str().c_str());
if (debug_level == CompressionUtilsDebugLevel::COMPRESSION_DBG_LEVEL_ASSERTION) abort();
}
static void
resetDebugFunctions()
{
for (auto &func : debug_funcs) {
func = defaultPrint;
}
}
static void
setDebugFunction(const CompressionUtilsDebugLevel debug_level, DebugFunction function)
{
if (static_cast<int>(debug_level) > max_debug_level) return;
debug_funcs[static_cast<int>(debug_level)] = function;
}
template <typename T>
ZlibDebugStream & operator<<(const T &message) { debug_message << message; return *this; }
private:
ostringstream debug_message;
CompressionUtilsDebugLevel debug_level;
static array<DebugFunction, max_debug_level + 1> debug_funcs;
};
array<DebugFunction, max_debug_level + 1> ZlibDebugStream::debug_funcs = {
defaultPrint, // CompressionUtilsDebugLevel::COMPRESSION_DBG_LEVEL_TRACE
defaultPrint, // CompressionUtilsDebugLevel::COMPRESSION_DBG_LEVEL_DEBUG
defaultPrint, // CompressionUtilsDebugLevel::COMPRESSION_DBG_LEVEL_INFO
defaultPrint, // CompressionUtilsDebugLevel::COMPRESSION_DBG_LEVEL_WARNING
defaultPrint, // CompressionUtilsDebugLevel::COMPRESSION_DBG_LEVEL_ERROR
defaultPrint // CompressionUtilsDebugLevel::COMPRESSION_DBG_LEVEL_ASSERTION
};
#define zlibDbgError ZlibDebugStream(CompressionUtilsDebugLevel::COMPRESSION_DBG_LEVEL_ERROR)
#define zlibDbgAssertion ZlibDebugStream(CompressionUtilsDebugLevel::COMPRESSION_DBG_LEVEL_ASSERTION)
static const int default_num_window_bits = 15; // Default used by zlib.
static const int default_compression_level = Z_DEFAULT_COMPRESSION;
static const int default_compression_method = Z_DEFLATED;
static const int default_mem_level = 8; // Default recommended in zlib documentation.
static const int default_strategy = Z_DEFAULT_STRATEGY;
static const int zlib_ok_return_value = Z_OK;
static const int zlib_stream_done_return_value = Z_STREAM_END;
static const int zlib_bad_stream_state_error = Z_STREAM_ERROR;
static const int zlib_invalid_data_error = Z_DATA_ERROR;
static const int zlib_out_of_memory_error = Z_MEM_ERROR;
static const int zlib_version_mismatch_error = Z_VERSION_ERROR;
static const int zlib_buf_error = Z_BUF_ERROR;
static const int zlib_finish_flush = Z_FINISH;
static const int zlib_sync_flush = Z_SYNC_FLUSH;
static const int zlib_no_flush = Z_NO_FLUSH;
struct CompressionStream
{
CompressionStream() { bzero(&stream, sizeof(z_stream)); }
~CompressionStream() { fini(); }
tuple<basic_string<unsigned char>, bool>
decompress(const unsigned char *data, uint32_t size)
{
initInflate();
if (state != TYPE::DECOMPRESS) throw runtime_error("Could not start decompression");
stream.avail_in = size;
stream.next_in = data;
vector<unsigned char> work_space;
work_space.reserve(4096);
basic_string<unsigned char> res;
int retries = 0;
while (stream.avail_in != 0) {
stream.avail_out = work_space.capacity();
stream.next_out = work_space.data();
auto old_total_out = stream.total_out;
auto inflate_res = inflate(&stream, zlib_no_flush);
if (inflate_res != Z_OK && inflate_res != Z_STREAM_END) {
fini();
throw runtime_error("error in 'inflate': " + getZlibError(inflate_res));
}
if (stream.total_out != old_total_out) {
res.append(work_space.data(), stream.total_out - old_total_out);
} else {
++retries;
if (retries > 3) {
fini();
throw runtime_error("No results from inflate more than three times");
}
}
if (inflate_res == Z_STREAM_END) {
fini();
return make_tuple(res, true);
}
}
return make_tuple(res, false);
}
basic_string<unsigned char>
compress(CompressionType type, const unsigned char *data, uint32_t size, int is_last_chunk)
{
initDeflate(type);
if (state != TYPE::COMPRESS) throw runtime_error("Could not start compression");
stream.avail_in = size;
stream.next_in = data;
vector<unsigned char> work_space;
work_space.reserve(deflateBound(&stream, stream.avail_in));
basic_string<unsigned char> res;
int retries = 0;
while (stream.avail_in != 0) {
stream.avail_out = work_space.capacity();
stream.next_out = work_space.data();
auto old_total_out = stream.total_out;
int deflate_res = deflate(&stream, is_last_chunk ? zlib_finish_flush : zlib_sync_flush);
if (deflate_res != Z_OK && deflate_res != Z_STREAM_END) {
fini();
throw runtime_error("error in 'deflate': " + getZlibError(deflate_res));
}
if (stream.total_out != old_total_out) {
res.append(work_space.data(), stream.total_out - old_total_out);
} else {
++retries;
if (retries > 3) {
fini();
throw runtime_error("No results from deflate more than three times");
}
}
if (deflate_res == Z_STREAM_END) {
fini();
return res;
}
}
return res;
}
private:
void
initInflate()
{
if (state != TYPE::UNINITIALIZAED) return;
auto init_status = inflateInit2(&stream, default_num_window_bits + 32);
if (init_status != zlib_ok_return_value) {
throw runtime_error(
"Failed to initialize decompression stream. Error: " + getZlibError(init_status)
);
}
state = TYPE::DECOMPRESS;
}
void
initDeflate(CompressionType type)
{
if (state != TYPE::UNINITIALIZAED) return;
int num_history_window_bits;
switch (type) {
case CompressionType::GZIP: {
num_history_window_bits = default_num_window_bits + 16;
break;
}
case CompressionType::ZLIB: {
num_history_window_bits = default_num_window_bits;
break;
}
default: {
zlibDbgAssertion
<< "Invalid compression type value: "
<< static_cast<int>(type);
return;
}
}
int init_status = deflateInit2(
&stream,
default_compression_level,
default_compression_method,
num_history_window_bits,
default_mem_level,
default_strategy
);
if (init_status != zlib_ok_return_value) {
throw runtime_error(
"Failed to initialize compression stream. Error: " + getZlibError(init_status)
);
}
state = TYPE::COMPRESS;
}
void
fini()
{
int end_stream_res = zlib_ok_return_value;
if (state == TYPE::DECOMPRESS) end_stream_res = inflateEnd(&stream);
if (state == TYPE::COMPRESS) end_stream_res = deflateEnd(&stream);
if (end_stream_res != zlib_ok_return_value) {
zlibDbgError << "Failed to clean state: " << getZlibError(end_stream_res);
}
state = TYPE::UNINITIALIZAED;
}
string
getZlibError(int zlibErrorCode)
{
switch (zlibErrorCode) {
case zlib_buf_error:
return "No progress was possible (possibly no more input data or not enough output buffer space)";
case zlib_bad_stream_state_error:
return "Inconsistent compression stream state";
case zlib_invalid_data_error:
return "Invalid or corrupted stream data";
case zlib_out_of_memory_error:
return "Out of memory";
case zlib_version_mismatch_error:
return "zlib version mismatch";
default:
return "zlib error occurred. Error code: " + to_string(zlibErrorCode);
}
}
z_stream stream;
enum class TYPE { UNINITIALIZAED, COMPRESS, DECOMPRESS } state = TYPE::UNINITIALIZAED;
};
void
resetCompressionDebugFunctionsToStandardError()
{
ZlibDebugStream::resetDebugFunctions();
}
void
setCompressionDebugFunction(const CompressionUtilsDebugLevel debug_level, void (*debug_function)(const char *))
{
ZlibDebugStream::setDebugFunction(debug_level, debug_function);
}
CompressionStream *
initCompressionStream()
{
return new CompressionStream();
}
void
finiCompressionStream(CompressionStream *compression_stream)
{
delete compression_stream;
}
static unsigned char *
duplicateMemory(const basic_string<unsigned char> &str)
{
auto res = static_cast<unsigned char *>(malloc(str.size()));
if (res == nullptr) throw bad_alloc();
memcpy(res, str.data(), str.size());
return res;
}
CompressionResult
compressData(
CompressionStream *compression_stream,
const CompressionType compression_type,
const uint32_t data_size,
const unsigned char *uncompressed_data,
const int is_last_chunk
)
{
CompressionResult result;
try {
if (compression_stream == nullptr) throw invalid_argument("Compression stream is NULL");
if (uncompressed_data == nullptr) throw invalid_argument("Data pointer is NULL");
auto compress = compression_stream->compress(compression_type, uncompressed_data, data_size, is_last_chunk);
result.output = duplicateMemory(compress);
result.num_output_bytes = compress.size();
result.ok = 1;
} catch (const exception &e) {
zlibDbgError << "Compression failed " << e.what();
result.ok = 0;
}
return result;
}
DecompressionResult
decompressData(
CompressionStream *compression_stream,
const uint32_t compressed_data_size,
const unsigned char *compressed_data
)
{
DecompressionResult result;
try {
if (compression_stream == nullptr) throw invalid_argument("Compression stream is NULL");
if (compressed_data == nullptr) throw invalid_argument("Data pointer is NULL");
if (compressed_data_size == 0) throw invalid_argument("Data size is 0");
auto decompress = compression_stream->decompress(compressed_data, compressed_data_size);
result.output = duplicateMemory(get<0>(decompress));
result.num_output_bytes = get<0>(decompress).size();
result.is_last_chunk = get<1>(decompress);
result.ok = 1;
} catch (const exception &e) {
zlibDbgError << "Decompression failed " << e.what();
result.ok = 0;
}
return result;
}

View File

@@ -0,0 +1,5 @@
link_directories(${ng_module_osrc_zlib_path}/lib)
file(COPY test_files DESTINATION .)
add_unit_test(compression_utils_ut "compression_utils_ut.cc" "compression_utils;-lz")

View File

@@ -0,0 +1,459 @@
#include <fstream>
#include "cptest.h"
#include "compression_utils.h"
#include "buffer.h"
using namespace std;
using namespace testing;
using ErrorHook = function<void(const char *)>;
USE_DEBUG_FLAG(D_COMPRESSION);
class CompressionUtilsTest : public Test
{
public:
CompressionUtilsTest()
{
Debug::setUnitTestFlag(D_COMPRESSION, Debug::DebugLevel::ERROR);
Debug::setNewDefaultStdout(&capture_debug);
setCompressionDebugFunction(
CompressionUtilsDebugLevel::COMPRESSION_DBG_LEVEL_ERROR,
[](const char *debug_message) { dbgError(D_COMPRESSION) << debug_message; }
);
setCompressionDebugFunction(
CompressionUtilsDebugLevel::COMPRESSION_DBG_LEVEL_ASSERTION,
[](const char *assert_message) { dbgAssert(false) << assert_message; }
);
}
~CompressionUtilsTest()
{
resetOutputStream();
}
void
resetOutputStream()
{
capture_debug.str("");
Debug::setNewDefaultStdout(&cout);
resetCompressionDebugFunctionsToStandardError();
}
string
readTestFileContents(const string &file_name)
{
string file_path = cptestFnameInExeDir(test_files_dir_name + "/" + file_name);
ifstream test_string_file(file_path);
stringstream string_stream;
string_stream << test_string_file.rdbuf();
return string_stream.str();
}
Maybe<string>
compressString(
const CompressionType compression_type,
const string &uncompressed_string,
const bool last_chunk = true,
CompressionStream *compression_stream = nullptr
)
{
auto disposable_compression_stream = initCompressionStream();
CompressionStream *compression_stream_to_use =
compression_stream == nullptr ?
disposable_compression_stream :
compression_stream;
unsigned char *input_data = reinterpret_cast<unsigned char *>(const_cast<char *>(uncompressed_string.c_str()));
CompressionResult compress_data_result = compressData(
compression_stream_to_use,
compression_type,
uncompressed_string.size(),
input_data,
last_chunk ? 1 : 0
);
finiCompressionStream(disposable_compression_stream);
if (compress_data_result.ok == 0) return genError("compressString failed");
auto compressed_string = string(
reinterpret_cast<char *>(compress_data_result.output),
compress_data_result.num_output_bytes
);
free(compress_data_result.output);
return compressed_string;
}
Maybe<string>
chunkedCompressString(const CompressionType compression_type, const string &uncompressed_string)
{
vector<string> input_string_chunks = splitIntoChunks(
uncompressed_string,
uncompressed_string.size() / chunk_size + 1
);
stringstream compressed_data_ss;
auto compression_stream = initCompressionStream();
for (uint32_t curr_chunk_index = 0; curr_chunk_index < input_string_chunks.size() - 1; curr_chunk_index++) {
Maybe<string> compress_string_result = compressString(
compression_type,
input_string_chunks[curr_chunk_index],
false,
compression_stream
);
if (!compress_string_result.ok()) {
finiCompressionStream(compression_stream);
return genError("chunkedCompressString failed: " + compress_string_result.getErr());
}
compressed_data_ss << compress_string_result.unpack();
}
Maybe<string> compress_string_result = compressString(
compression_type,
input_string_chunks[input_string_chunks.size() - 1],
true,
compression_stream
);
finiCompressionStream(compression_stream);
if (!compress_string_result.ok()) {
return genError("chunkedCompressString failed: " + compress_string_result.getErr());
}
compressed_data_ss << compress_string_result.unpack();
return compressed_data_ss.str();
}
Maybe<string>
decompressString(
const string &compressed_string,
int *is_last_chunk = nullptr,
CompressionStream *compression_stream = nullptr
)
{
auto disposable_compression_stream = initCompressionStream();
CompressionStream *compression_stream_to_use =
compression_stream == nullptr ?
disposable_compression_stream :
compression_stream;
unsigned char *compressed_data = reinterpret_cast<unsigned char *>(
const_cast<char *>(
compressed_string.c_str()
)
);
int disposable_is_last_chunk_indicator = 0;
int *is_last_chunk_indicator_to_use =
is_last_chunk == nullptr ?
&disposable_is_last_chunk_indicator :
is_last_chunk;
DecompressionResult decompress_data_result = decompressData(
compression_stream_to_use,
compressed_string.size(),
compressed_data
);
*is_last_chunk_indicator_to_use = decompress_data_result.is_last_chunk;
finiCompressionStream(disposable_compression_stream);
if (decompress_data_result.ok == 0) return genError("decompressString failed");
auto decompressed_string = string(
reinterpret_cast<char *>(decompress_data_result.output),
decompress_data_result.num_output_bytes
);
free(decompress_data_result.output);
return decompressed_string;
}
Maybe<string>
chunkedDecompressString(const string &compressed_string)
{
auto compression_stream = initCompressionStream();
int is_last_chunk = 0;
stringstream decompressed_data_ss;
vector<string> input_string_chunks = splitIntoChunks(
compressed_string,
compressed_string.size() / chunk_size + 1
);
for (uint32_t curr_chunk_index = 0; curr_chunk_index < input_string_chunks.size(); curr_chunk_index++) {
Maybe<string> decompress_string_result = decompressString(
input_string_chunks[curr_chunk_index],
&is_last_chunk,
compression_stream
);
if (!decompress_string_result.ok()) {
finiCompressionStream(compression_stream);
return genError("chunkedDecompress failed: " + decompress_string_result.getErr());
}
decompressed_data_ss << decompress_string_result.unpack();
}
finiCompressionStream(compression_stream);
return decompressed_data_ss.str();
}
bool
performCompressionNullPointerTest()
{
static const vector<int> possible_last_chunk_values = { 0, 1 };
string compress_test_string = readTestFileContents(chunk_sized_string_file_name);
string decompress_test_string = readTestFileContents(chunk_sized_gzip_file_name);
for (CompressionType single_compression_type : compression_types) {
for (int single_possible_last_chunk_value : possible_last_chunk_values) {
CompressionResult result = compressData(
nullptr,
single_compression_type,
compress_test_string.size(),
reinterpret_cast<unsigned char *>(const_cast<char *>(compress_test_string.c_str())),
single_possible_last_chunk_value
);
if (result.ok) return false;
}
}
DecompressionResult result = decompressData(
nullptr,
decompress_test_string.size(),
reinterpret_cast<unsigned char *>(const_cast<char *>(decompress_test_string.c_str()))
);
if (result.ok) return false;
return true;
}
vector<string>
splitIntoChunks(const string &data, const uint32_t num_data_chunks)
{
vector<string> data_chunks;
uint32_t num_data_chunks_to_use = min(static_cast<uint32_t>(data.size()), num_data_chunks);
if (num_data_chunks_to_use == 1) return { data };
uint32_t chunk_size = data.size() / num_data_chunks;
for (uint32_t curr_chunk_index = 0; curr_chunk_index < num_data_chunks_to_use - 1; curr_chunk_index++) {
data_chunks.push_back(string(data.c_str() + curr_chunk_index * chunk_size, chunk_size));
}
uint32_t accumulated_chunks_size = (num_data_chunks_to_use - 1) * chunk_size;
data_chunks.push_back(string(data.c_str() + accumulated_chunks_size, data.size() - accumulated_chunks_size));
return data_chunks;
}
uint32_t
calcCompressedDataSizeBound(const uint32_t compressed_data_size)
{
return 2 * compressed_data_size;
}
ostringstream capture_debug;
const string simple_test_string = "Test data for compression utilities library";
const string chunk_sized_string_file_name = "chunk_sized_string";
const string chunk_sized_gzip_file_name = "chunk_sized_compressed_file.gz";
const string chunk_sized_zlib_file_name = "chunk_sized_compressed_file.zz";
const string multi_chunk_sized_string_file_name = "multiple_chunk_sized_string";
const string multi_chunk_sized_gzip_file_name = "multiple_chunk_sized_compressed_file.gz";
const string multi_chunk_sized_zlib_file_name = "multiple_chunk_sized_compressed_file.zz";
const vector<string> chunk_sized_compressed_files = { chunk_sized_gzip_file_name, chunk_sized_zlib_file_name };
const vector<string> multi_chunk_sized_compressed_files = {
multi_chunk_sized_gzip_file_name,
multi_chunk_sized_zlib_file_name
};
const vector<CompressionType> compression_types = { CompressionType::GZIP, CompressionType::ZLIB };
const uint32_t chunk_size = 32768;
private:
const string test_files_dir_name = "test_files";
};
TEST_F(CompressionUtilsTest, CompressAndDecompressSimpleString)
{
for (auto single_compression_type : compression_types) {
Maybe<string> compressed_string_maybe = compressString(
single_compression_type,
simple_test_string
);
EXPECT_TRUE(compressed_string_maybe.ok());
Maybe<string> decompressed_string_maybe = decompressString(compressed_string_maybe.unpack());
EXPECT_TRUE(decompressed_string_maybe.ok());
EXPECT_EQ(simple_test_string, decompressed_string_maybe.unpack());
}
}
TEST_F(CompressionUtilsTest, CompressAndDecompressChunkSizedString)
{
string test_string = readTestFileContents(chunk_sized_string_file_name);
for (auto single_compression_type : compression_types) {
Maybe<string> compressed_string_maybe = compressString(
single_compression_type,
test_string
);
EXPECT_TRUE(compressed_string_maybe.ok());
Maybe<string> decompressed_string_maybe = decompressString(compressed_string_maybe.unpack());
EXPECT_TRUE(decompressed_string_maybe.ok());
EXPECT_EQ(test_string, decompressed_string_maybe.unpack());
}
}
TEST_F(CompressionUtilsTest, CompressMultipleChunkSizedStringAndDecompress)
{
string test_string = readTestFileContents(multi_chunk_sized_string_file_name);
for (auto single_compression_type : compression_types) {
Maybe<string> chunked_compress_result = chunkedCompressString(single_compression_type, test_string);
EXPECT_TRUE(chunked_compress_result.ok());
Maybe<string> chunked_decompress_result = chunkedDecompressString(chunked_compress_result.unpack());
EXPECT_TRUE(chunked_decompress_result.ok());
EXPECT_EQ(chunked_decompress_result.unpack(), test_string);
}
}
TEST_F(CompressionUtilsTest, DecompressChunkSizedCompressedFile)
{
for (const auto &single_compressed_file_name : chunk_sized_compressed_files) {
string test_string = readTestFileContents(single_compressed_file_name);
string expected_decompressed_string = readTestFileContents(chunk_sized_string_file_name);
Maybe<string> decompressed_string_result = decompressString(test_string);
EXPECT_TRUE(decompressed_string_result.ok());
EXPECT_EQ(decompressed_string_result.unpack(), expected_decompressed_string);
}
}
TEST_F(CompressionUtilsTest, DecompressMultipleChunkSizedCompressedFile)
{
for (const auto &single_compressed_file_name : multi_chunk_sized_compressed_files) {
string test_string = readTestFileContents(single_compressed_file_name);
Maybe<string> chunked_decompress_result = chunkedDecompressString(test_string);
EXPECT_TRUE(chunked_decompress_result.ok());
string expected_decompressed_string = readTestFileContents(multi_chunk_sized_string_file_name);
EXPECT_EQ(chunked_decompress_result.unpack(), expected_decompressed_string);
}
}
TEST_F(CompressionUtilsTest, TestEmptyBuffer)
{
for (CompressionType compression_type : compression_types) {
auto compression_stream = initCompressionStream();
stringstream compressed_stream;
Maybe<string> compressed_string = compressString(
compression_type,
simple_test_string,
false,
compression_stream
);
EXPECT_TRUE(compressed_string.ok());
compressed_stream << compressed_string.unpack();
compressed_string = compressString(
compression_type,
"",
true,
compression_stream
);
finiCompressionStream(compression_stream);
EXPECT_TRUE(compressed_string.ok());
compressed_stream << compressed_string.unpack();
Buffer compressed_buffer(compressed_stream.str());
int is_last_chunk;
auto decompression_stream = initCompressionStream();
Maybe<string> decompressed_string = decompressString(
compressed_stream.str(),
&is_last_chunk,
decompression_stream
);
EXPECT_TRUE(decompressed_string.ok());
EXPECT_EQ(decompressed_string.unpack(), simple_test_string);
finiCompressionStream(decompression_stream);
}
}
TEST_F(CompressionUtilsTest, CompressionStreamNullPointer)
{
EXPECT_TRUE(performCompressionNullPointerTest());
EXPECT_THAT(
capture_debug.str(),
HasSubstr("Compression failed Compression stream is NULL")
);
resetOutputStream();
EXPECT_TRUE(performCompressionNullPointerTest());
EXPECT_EQ(capture_debug.str(), string());
}
TEST_F(CompressionUtilsTest, InputDataBufferNullPointer)
{
static const vector<int> possible_last_chunk_values = { 0, 1 };
string compress_test_string = readTestFileContents(chunk_sized_string_file_name);
auto compression_stream = initCompressionStream();
for (CompressionType single_compression_type : compression_types) {
for (int single_possible_last_chunk_value : possible_last_chunk_values) {
CompressionResult result = compressData(
compression_stream,
single_compression_type,
compress_test_string.size(),
nullptr,
single_possible_last_chunk_value
);
EXPECT_EQ(result.ok, 0);
}
}
string decompress_test_string = readTestFileContents(chunk_sized_gzip_file_name);
finiCompressionStream(compression_stream);
compression_stream = initCompressionStream();
DecompressionResult result = decompressData(
compression_stream,
decompress_test_string.size(),
nullptr
);
EXPECT_EQ(result.ok, 0);
EXPECT_THAT(
capture_debug.str(),
HasSubstr("Compression failed Data pointer is NULL")
);
finiCompressionStream(compression_stream);
}
TEST_F(CompressionUtilsTest, DecompressPlainText)
{
Maybe<string> decompress_string_result = decompressString(simple_test_string);
EXPECT_FALSE(decompress_string_result.ok());
EXPECT_THAT(
capture_debug.str(),
HasSubstr("error in 'inflate': Invalid or corrupted stream data")
);
}