| // 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 |