1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
|
#include <gtest/gtest.h>
#include "Compression.hpp"
#include "LogEntry.hpp"
#include <vector>
#include <string>
#include <algorithm>
class CompressionTest : public ::testing::Test
{
protected:
void SetUp() override
{
// Create a few sample log entries for testing
entry1 = LogEntry(LogEntry::ActionType::CREATE, "/data/records/1", "controller123", "processor123", "subject456");
entry2 = LogEntry(LogEntry::ActionType::READ, "/data/records/2", "controller789", "processor789", "subject456");
entry3 = LogEntry(LogEntry::ActionType::UPDATE, "/data/records/3", "controller123", "processor123", "subject789");
entry4 = LogEntry(LogEntry::ActionType::DELETE, "/data/records/4", "controller789", "processor789", "subject123");
}
LogEntry entry1, entry2, entry3, entry4;
};
// Helper function to compare two LogEntry objects
bool LogEntriesEqual(const LogEntry &a, const LogEntry &b)
{
// Compare serialized representations to check equality
auto serializedA = a.serialize();
auto serializedB = b.serialize();
return serializedA == serializedB;
}
// Test compressing and decompressing a batch of log entries
TEST_F(CompressionTest, CompressDecompressBatch)
{
std::vector<LogEntry> batch = {entry1, entry2, entry3, entry4};
std::vector<uint8_t> serializedBatch = LogEntry::serializeBatch(std::move(batch));
std::vector<uint8_t> compressed = Compression::compress(std::move(serializedBatch));
// Make sure compression produced data
ASSERT_GT(compressed.size(), 0);
std::vector<uint8_t> decompressed = Compression::decompress(std::move(compressed));
std::vector<LogEntry> recoveredBatch = LogEntry::deserializeBatch(std::move(decompressed));
// Verify we got back the same number of entries
ASSERT_EQ(batch.size(), recoveredBatch.size());
// Verify each entry matches
for (size_t i = 0; i < batch.size(); i++)
{
EXPECT_TRUE(LogEntriesEqual(batch[i], recoveredBatch[i]))
<< "Entries at index " << i << " don't match";
}
}
// Test with an empty batch
TEST_F(CompressionTest, EmptyBatch)
{
// Create an empty batch
std::vector<LogEntry> emptyBatch;
std::vector<uint8_t> serializedBatch = LogEntry::serializeBatch(std::move(emptyBatch));
std::vector<uint8_t> compressed = Compression::compress(std::move(serializedBatch));
std::vector<uint8_t> decompressed = Compression::decompress(std::move(compressed));
std::vector<LogEntry> recoveredBatch = LogEntry::deserializeBatch(std::move(decompressed));
// Verify we still have an empty vector
EXPECT_TRUE(recoveredBatch.empty());
}
// Test with invalid compressed data
TEST_F(CompressionTest, InvalidCompressedData)
{
// Create some invalid compressed data
std::vector<uint8_t> invalidData = {0x01, 0x02, 0x03, 0x04};
// Verify that decompression failed
EXPECT_THROW(
Compression::decompress(std::move(invalidData)),
std::runtime_error);
}
// Test batch compression ratio
TEST_F(CompressionTest, BatchCompressionRatio)
{
// Create a batch of log entries with repetitive data which should compress well
const int batchSize = 50;
std::string repetitiveData(1000, 'X');
LogEntry repetitiveEntry(LogEntry::ActionType::CREATE, repetitiveData, repetitiveData, repetitiveData, repetitiveData);
std::vector<LogEntry> repetitiveBatch(batchSize, repetitiveEntry);
std::vector<uint8_t> serializedBatch = LogEntry::serializeBatch(std::move(repetitiveBatch));
std::vector<uint8_t> compressed = Compression::compress(std::move(serializedBatch));
// Check that batch compression significantly reduced the size
double compressionRatio = static_cast<double>(compressed.size()) / static_cast<double>(serializedBatch.size());
EXPECT_LT(compressionRatio, 0.05); // Expect at least 95% compression for batch
std::vector<uint8_t> decompressed = Compression::decompress(std::move(compressed));
std::vector<LogEntry> recoveredBatch = LogEntry::deserializeBatch(std::move(decompressed));
// Verify the correct number of entries and their content
ASSERT_EQ(repetitiveBatch.size(), recoveredBatch.size());
for (size_t i = 0; i < repetitiveBatch.size(); i++)
{
EXPECT_TRUE(LogEntriesEqual(repetitiveBatch[i], recoveredBatch[i]));
}
}
// Test with a large batch of entries
TEST_F(CompressionTest, LargeBatch)
{
std::vector<LogEntry> largeBatch(100, entry1);
std::vector<uint8_t> serializedBatch = LogEntry::serializeBatch(std::move(largeBatch));
std::vector<uint8_t> compressed = Compression::compress(std::move(serializedBatch));
std::vector<uint8_t> decompressed = Compression::decompress(std::move(compressed));
std::vector<LogEntry> recoveredBatch = LogEntry::deserializeBatch(std::move(decompressed));
// Verify the correct number of entries
ASSERT_EQ(largeBatch.size(), recoveredBatch.size());
// Verify the entries match
for (size_t i = 0; i < largeBatch.size(); i++)
{
EXPECT_TRUE(LogEntriesEqual(largeBatch[i], recoveredBatch[i]));
}
}
int main(int argc, char **argv)
{
::testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}
|