summaryrefslogtreecommitdiff
path: root/Tests/LibCompress/TestDeflate.cpp
blob: d2bc4e82c663a0a0518608fe7066e63a82311186 (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)
{
    Array<u8, 32> const 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
    };
    Array<u8, 6> const input {
        0x00, 0x42, 0x84, 0xa9, 0xb0, 0x15
    };
    Array<u32, 9> const output {
        0x00, 0x01, 0x01, 0x02, 0x03, 0x05, 0x08, 0x0d, 0x15
    };

    auto const 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)
{
    Array<u8, 6> const code {
        0x03, 0x02, 0x03, 0x03, 0x02, 0x03
    };
    Array<u8, 4> const input {
        0xa1, 0xf3, 0xa1, 0xf3
    };
    Array<u32, 12> const output {
        0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05
    };

    auto const 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)
{
    Array<u8, 28> const 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 :)";

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

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

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

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

TEST_CASE(deflate_decompress_multiple_blocks)
{
    Array<u8, 84> const 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.";

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

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

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

    auto const 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());
}