summaryrefslogtreecommitdiff
path: root/Tests/LibCompress/TestDeflate.cpp
blob: b325ffa3db6bbf7d07508b74e294285ac6862e32 (plain)
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
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
/*
 * Copyright (c) 2020-2021, the SerenityOS developers.
 *
 * SPDX-License-Identifier: BSD-2-Clause
 */

#include <LibTest/TestCase.h>

#include <AK/Array.h>
#include <AK/MemoryStream.h>
#include <AK/Random.h>
#include <LibCompress/Deflate.h>
#include <cstring>

TEST_CASE(canonical_code_simple)
{
    const Array<u8, 32> code {
        0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,
        0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,
        0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05
    };
    const Array<u8, 6> input {
        0x00, 0x42, 0x84, 0xa9, 0xb0, 0x15
    };
    const Array<u32, 9> output {
        0x00, 0x01, 0x01, 0x02, 0x03, 0x05, 0x08, 0x0d, 0x15
    };

    const auto huffman = Compress::CanonicalCode::from_bytes(code).value();
    auto memory_stream = InputMemoryStream { input };
    auto bit_stream = InputBitStream { memory_stream };

    for (size_t idx = 0; idx < 9; ++idx)
        EXPECT_EQ(huffman.read_symbol(bit_stream), output[idx]);
}

TEST_CASE(canonical_code_complex)
{
    const Array<u8, 6> code {
        0x03, 0x02, 0x03, 0x03, 0x02, 0x03
    };
    const Array<u8, 4> input {
        0xa1, 0xf3, 0xa1, 0xf3
    };
    const Array<u32, 12> output {
        0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05
    };

    const auto huffman = Compress::CanonicalCode::from_bytes(code).value();
    auto memory_stream = InputMemoryStream { input };
    auto bit_stream = InputBitStream { memory_stream };

    for (size_t idx = 0; idx < 12; ++idx)
        EXPECT_EQ(huffman.read_symbol(bit_stream), output[idx]);
}

TEST_CASE(deflate_decompress_compressed_block)
{
    const Array<u8, 28> compressed {
        0x0B, 0xC9, 0xC8, 0x2C, 0x56, 0x00, 0xA2, 0x44, 0x85, 0xE2, 0xCC, 0xDC,
        0x82, 0x9C, 0x54, 0x85, 0x92, 0xD4, 0x8A, 0x12, 0x85, 0xB4, 0x4C, 0x20,
        0xCB, 0x4A, 0x13, 0x00
    };

    const u8 uncompressed[] = "This is a simple text file :)";

    const auto decompressed = Compress::DeflateDecompressor::decompress_all(compressed);
    EXPECT(decompressed.value().bytes() == ReadonlyBytes({ uncompressed, sizeof(uncompressed) - 1 }));
}

TEST_CASE(deflate_decompress_uncompressed_block)
{
    const Array<u8, 18> compressed {
        0x01, 0x0d, 0x00, 0xf2, 0xff, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x2c, 0x20,
        0x57, 0x6f, 0x72, 0x6c, 0x64, 0x21
    };

    const u8 uncompressed[] = "Hello, World!";

    const auto decompressed = Compress::DeflateDecompressor::decompress_all(compressed);
    EXPECT(decompressed.value().bytes() == (ReadonlyBytes { uncompressed, sizeof(uncompressed) - 1 }));
}

TEST_CASE(deflate_decompress_multiple_blocks)
{
    const Array<u8, 84> compressed {
        0x00, 0x1f, 0x00, 0xe0, 0xff, 0x54, 0x68, 0x65, 0x20, 0x66, 0x69, 0x72,
        0x73, 0x74, 0x20, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x20, 0x69, 0x73, 0x20,
        0x75, 0x6e, 0x63, 0x6f, 0x6d, 0x70, 0x72, 0x65, 0x73, 0x73, 0x65, 0x64,
        0x53, 0x48, 0xcc, 0x4b, 0x51, 0x28, 0xc9, 0x48, 0x55, 0x28, 0x4e, 0x4d,
        0xce, 0x07, 0x32, 0x93, 0x72, 0xf2, 0x93, 0xb3, 0x15, 0x32, 0x8b, 0x15,
        0x92, 0xf3, 0x73, 0x0b, 0x8a, 0x52, 0x8b, 0x8b, 0x53, 0x53, 0xf4, 0x00
    };

    const u8 uncompressed[] = "The first block is uncompressed and the second block is compressed.";

    const auto decompressed = Compress::DeflateDecompressor::decompress_all(compressed);
    EXPECT(decompressed.value().bytes() == (ReadonlyBytes { uncompressed, sizeof(uncompressed) - 1 }));
}

TEST_CASE(deflate_decompress_zeroes)
{
    const Array<u8, 20> compressed {
        0xed, 0xc1, 0x01, 0x0d, 0x00, 0x00, 0x00, 0xc2, 0xa0, 0xf7, 0x4f, 0x6d,
        0x0f, 0x07, 0x14, 0x00, 0x00, 0x00, 0xf0, 0x6e
    };

    const Array<u8, 4096> uncompressed { 0 };

    const auto decompressed = Compress::DeflateDecompressor::decompress_all(compressed);
    EXPECT(uncompressed == decompressed.value().bytes());
}

TEST_CASE(deflate_round_trip_store)
{
    auto original = ByteBuffer::create_uninitialized(1024).release_value();
    fill_with_random(original.data(), 1024);
    auto compressed = Compress::DeflateCompressor::compress_all(original, Compress::DeflateCompressor::CompressionLevel::STORE);
    EXPECT(compressed.has_value());
    auto uncompressed = Compress::DeflateDecompressor::decompress_all(compressed.value());
    EXPECT(uncompressed.has_value());
    EXPECT(uncompressed.value() == original);
}

TEST_CASE(deflate_round_trip_compress)
{
    auto original = ByteBuffer::create_zeroed(2048).release_value();
    fill_with_random(original.data(), 1024); // we pre-filled the second half with 0s to make sure we test back references as well
    // Since the different levels just change how much time is spent looking for better matches, just use fast here to reduce test time
    auto compressed = Compress::DeflateCompressor::compress_all(original, Compress::DeflateCompressor::CompressionLevel::FAST);
    EXPECT(compressed.has_value());
    auto uncompressed = Compress::DeflateDecompressor::decompress_all(compressed.value());
    EXPECT(uncompressed.has_value());
    EXPECT(uncompressed.value() == original);
}

TEST_CASE(deflate_round_trip_compress_large)
{
    auto size = Compress::DeflateCompressor::block_size * 2;
    auto original = ByteBuffer::create_uninitialized(size).release_value(); // Compress a buffer larger than the maximum block size to test the sliding window mechanism
    fill_with_random(original.data(), size);
    // Since the different levels just change how much time is spent looking for better matches, just use fast here to reduce test time
    auto compressed = Compress::DeflateCompressor::compress_all(original, Compress::DeflateCompressor::CompressionLevel::FAST);
    EXPECT(compressed.has_value());
    auto uncompressed = Compress::DeflateDecompressor::decompress_all(compressed.value());
    EXPECT(uncompressed.has_value());
    EXPECT(uncompressed.value() == original);
}

TEST_CASE(deflate_compress_literals)
{
    // This byte array is known to not produce any back references with our lz77 implementation even at the highest compression settings
    Array<u8, 0x13> test { 0, 0, 0, 0, 0x72, 0, 0, 0xee, 0, 0, 0, 0x26, 0, 0, 0, 0x28, 0, 0, 0x72 };
    auto compressed = Compress::DeflateCompressor::compress_all(test, Compress::DeflateCompressor::CompressionLevel::GOOD);
    EXPECT(compressed.has_value());
}