blob: 4d53b59e18340f7c99c55cc520832d3151119f87 [file] [log] [blame] [edit]
// Copyright 2021 The ChromiumOS Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "missive/compression/compression_module.h"
#include <optional>
#include <string>
#include <string_view>
#include <tuple>
#include <base/functional/bind.h>
#include <base/hash/hash.h>
#include <base/memory/scoped_refptr.h>
#include <base/rand_util.h>
#include <base/strings/strcat.h>
#include <base/synchronization/waitable_event.h>
#include <base/task/thread_pool.h>
#include <base/test/task_environment.h>
#include <base/time/time.h>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include <snappy.h>
#include "missive/proto/record.pb.h"
#include "missive/resources/resource_manager.h"
#include "missive/util/test_support_callbacks.h"
using ::testing::Eq;
using ::testing::StrEq;
namespace reporting {
namespace {
constexpr char kTestString[] = "AAAAAAAAAAAAAA1111111111111";
constexpr char kPoorlyCompressibleTestString[] = "AAAAA11111";
class CompressionModuleTest : public ::testing::Test {
protected:
CompressionModuleTest()
: memory_resource_(base::MakeRefCounted<ResourceManager>(
4u * 1024LLu * 1024LLu)) // 4 MiB
{}
void TearDown() override { ASSERT_THAT(memory_resource_->GetUsed(), Eq(0u)); }
std::string BenchmarkCompressRecordSnappy(std::string record_string) {
std::string output;
snappy::Compress(record_string.data(), record_string.size(), &output);
return output;
}
base::test::TaskEnvironment task_environment_;
scoped_refptr<ResourceManager> memory_resource_;
scoped_refptr<CompressionModule> compression_module_;
};
TEST_F(CompressionModuleTest, CompressRecordSnappy) {
scoped_refptr<CompressionModule> test_compression_module =
CompressionModule::Create(/*is_enabled=*/true, 0,
CompressionInformation::COMPRESSION_SNAPPY);
// Compress string directly with snappy as benchmark
const std::string expected_output =
BenchmarkCompressRecordSnappy(kTestString);
test::TestMultiEvent<std::string, std::optional<CompressionInformation>>
compressed_record_event;
// Compress string with CompressionModule
test_compression_module->CompressRecord(kTestString, memory_resource_,
compressed_record_event.cb());
const std::tuple<std::string, std::optional<CompressionInformation>>
compressed_record_tuple = compressed_record_event.result();
const std::string_view compressed_string_callback =
std::get<0>(compressed_record_tuple);
// Expect that benchmark compression is the same as compression module
EXPECT_THAT(compressed_string_callback, StrEq(expected_output));
const std::optional<CompressionInformation> compression_info =
std::get<1>(compressed_record_tuple);
EXPECT_TRUE(compression_info.has_value());
// Expect that compression information contains COMPRESSION_SNAPPY
EXPECT_THAT(compression_info.value().compression_algorithm(),
Eq(CompressionInformation::COMPRESSION_SNAPPY));
}
TEST_F(CompressionModuleTest, CompressPoorlyCompressibleRecordSnappy) {
scoped_refptr<CompressionModule> test_compression_module =
CompressionModule::Create(/*is_enabled=*/true, 0,
CompressionInformation::COMPRESSION_SNAPPY);
// Compress string directly with snappy as benchmark
const std::string expected_output =
BenchmarkCompressRecordSnappy(kPoorlyCompressibleTestString);
test::TestMultiEvent<std::string, std::optional<CompressionInformation>>
compressed_record_event;
// Compress string with CompressionModule
test_compression_module->CompressRecord(kPoorlyCompressibleTestString,
memory_resource_,
compressed_record_event.cb());
const std::tuple<std::string, std::optional<CompressionInformation>>
compressed_record_tuple = compressed_record_event.result();
const std::string_view compressed_string_callback =
std::get<0>(compressed_record_tuple);
// Expect that benchmark compression is the same as compression module
EXPECT_THAT(compressed_string_callback, StrEq(kPoorlyCompressibleTestString));
const std::optional<CompressionInformation> compression_info =
std::get<1>(compressed_record_tuple);
EXPECT_TRUE(compression_info.has_value());
// Expect that compression information contains COMPRESSION_NONE
EXPECT_THAT(compression_info.value().compression_algorithm(),
Eq(CompressionInformation::COMPRESSION_NONE));
}
TEST_F(CompressionModuleTest, CompressRecordBelowThreshold) {
scoped_refptr<CompressionModule> test_compression_module =
CompressionModule::Create(/*is_enabled=*/true, 512,
CompressionInformation::COMPRESSION_SNAPPY);
test::TestMultiEvent<std::string, std::optional<CompressionInformation>>
compressed_record_event;
// Compress string with CompressionModule
test_compression_module->CompressRecord(kTestString, memory_resource_,
compressed_record_event.cb());
const std::tuple<std::string, std::optional<CompressionInformation>>
compressed_record_tuple = compressed_record_event.result();
const std::string_view compressed_string_callback =
std::get<0>(compressed_record_tuple);
// Expect that record is not compressed since size is smaller than 512 bytes
EXPECT_THAT(compressed_string_callback, StrEq(kTestString));
const std::optional<CompressionInformation> compression_info =
std::get<1>(compressed_record_tuple);
EXPECT_TRUE(compression_info.has_value());
// Expect that compression information contains COMPRESSION_NONE since the
// record was below the compression threshold.
EXPECT_THAT(compression_info.value().compression_algorithm(),
Eq(CompressionInformation::COMPRESSION_NONE));
}
TEST_F(CompressionModuleTest, CompressRecordCompressionDisabled) {
scoped_refptr<CompressionModule> test_compression_module =
CompressionModule::Create(/*is_enabled=*/false, 0,
CompressionInformation::COMPRESSION_SNAPPY);
test::TestMultiEvent<std::string, std::optional<CompressionInformation>>
compressed_record_event;
// Compress string with CompressionModule
test_compression_module->CompressRecord(kTestString, memory_resource_,
compressed_record_event.cb());
const std::tuple<std::string, std::optional<CompressionInformation>>
compressed_record_tuple = compressed_record_event.result();
const std::string_view compressed_string_callback =
std::get<0>(compressed_record_tuple);
// Expect that record is not compressed since compression is not enabled
EXPECT_THAT(compressed_string_callback, StrEq(kTestString));
const std::optional<CompressionInformation> compression_info =
std::get<1>(compressed_record_tuple);
// Expect no compression information since compression has been disabled.
EXPECT_FALSE(compression_info.has_value());
}
TEST_F(CompressionModuleTest, CompressRecordCompressionNone) {
scoped_refptr<CompressionModule> test_compression_module =
CompressionModule::Create(/*is_enabled=*/true, 0,
CompressionInformation::COMPRESSION_NONE);
test::TestMultiEvent<std::string, std::optional<CompressionInformation>>
compressed_record_event;
// Compress string with CompressionModule
test_compression_module->CompressRecord(kTestString, memory_resource_,
compressed_record_event.cb());
const std::tuple<std::string, std::optional<CompressionInformation>>
compressed_record_tuple = compressed_record_event.result();
const std::string_view compressed_string_callback =
std::get<0>(compressed_record_tuple);
// Expect that record is not compressed since COMPRESSION_NONE was chosen as
// the compression_algorithm.
EXPECT_THAT(compressed_string_callback, StrEq(kTestString));
const std::optional<CompressionInformation> compression_info =
std::get<1>(compressed_record_tuple);
EXPECT_TRUE(compression_info.has_value());
// Expect that compression information contains COMPRESSION_NONE
EXPECT_THAT(compression_info.value().compression_algorithm(),
Eq(CompressionInformation::COMPRESSION_NONE));
}
TEST_F(CompressionModuleTest, DynamicEnableDisable) {
scoped_refptr<CompressionModule> test_compression_module =
CompressionModule::Create(/*is_enabled=*/true, 0,
CompressionInformation::COMPRESSION_NONE);
EXPECT_TRUE(test_compression_module->is_enabled());
test_compression_module->SetValue(/*is_enabled=*/false);
EXPECT_FALSE(test_compression_module->is_enabled());
test_compression_module->SetValue(/*is_enabled=*/true);
EXPECT_TRUE(test_compression_module->is_enabled());
}
} // namespace
} // namespace reporting