xref: /openbmc/bmcweb/test/http/zstd_compressor_test.cpp (revision f485bd44a22c27a2346a69d740764ea98b333bd1)
1 // SPDX-License-Identifier: Apache-2.0
2 // SPDX-FileCopyrightText: Copyright OpenBMC Authors
3 #include <boost/asio/buffer.hpp>
4 
5 #include <algorithm>
6 #include <climits>
7 #include <cstdint>
8 #include <functional>
9 #include <vector>
10 #ifdef HAVE_ZSTD
11 #include "zstd_compressor.hpp"
12 #include "zstd_decompressor.hpp"
13 #include "zstd_test_arrays.hpp"
14 
15 #include <cstddef>
16 #include <optional>
17 #include <random>
18 #include <span>
19 
20 #include <gmock/gmock.h>
21 #include <gtest/gtest.h>
22 
23 using ::testing::ElementsAreArray;
24 
25 namespace bmcweb
26 {
27 namespace
28 {
29 
TEST(ZstdCompressor,EmptyFile)30 TEST(ZstdCompressor, EmptyFile)
31 {
32     ZstdCompressor comp;
33     ASSERT_TRUE(comp.init(0U));
34     std::vector<uint8_t> out;
35     bool more = false;
36     std::vector<uint8_t> empty;
37     std::optional<std::span<const uint8_t>> segmentOut =
38         comp.compress(empty, more);
39     ASSERT_TRUE(segmentOut);
40     if (!segmentOut)
41     {
42         return;
43     }
44 
45     EXPECT_THAT(*segmentOut, ElementsAreArray(zstd::empty));
46 }
47 
TEST(ZstdCompressor,AllZeros)48 TEST(ZstdCompressor, AllZeros)
49 {
50     for (size_t chunkSize : {1U, 2U, 4U, 8U, 1024U, 1048576U})
51     {
52         ZstdCompressor comp;
53         constexpr size_t fileSize = 1048576U;
54         ASSERT_TRUE(comp.init(fileSize));
55         std::vector<uint8_t> out;
56 
57         std::vector<uint8_t> zeros(chunkSize, 0x00);
58         for (size_t i = 0; i < fileSize; i += chunkSize)
59         {
60             bool more = i != fileSize - chunkSize;
61             std::optional<std::span<const uint8_t>> segmentOut =
62                 comp.compress(zeros, more);
63             ASSERT_TRUE(segmentOut);
64             if (!segmentOut)
65             {
66                 return;
67             }
68             out.insert(out.end(), segmentOut->begin(), segmentOut->end());
69         }
70         EXPECT_THAT(out, ElementsAreArray(zstd::zeros));
71     }
72 }
73 
TEST(ZstdCompressor,AllOnes)74 TEST(ZstdCompressor, AllOnes)
75 {
76     ZstdCompressor comp;
77     ASSERT_TRUE(comp.init(1048576U));
78     std::vector<uint8_t> out;
79     std::vector<uint8_t> zeros(1024U, 0xFF);
80     for (size_t i = 0; i < 1024U; i++)
81     {
82         bool more = i < 1023U;
83         std::optional<std::span<const uint8_t>> segmentOut =
84             comp.compress(zeros, more);
85         ASSERT_TRUE(segmentOut);
86         if (!segmentOut)
87         {
88             return;
89         }
90         out.insert(out.end(), segmentOut->begin(), segmentOut->end());
91     }
92 
93     EXPECT_THAT(out, ElementsAreArray(zstd::ones));
94 }
95 
TEST(Zstd,RoundTrip)96 TEST(Zstd, RoundTrip)
97 {
98     using random_bytes_engine =
99         std::independent_bits_engine<std::default_random_engine, CHAR_BIT,
100                                      unsigned char>;
101 
102     // This is a unit test, we WANT reproducible tests
103     // NOLINTNEXTLINE(cert-msc51-cpp, cert-msc32-c)
104     random_bytes_engine rbe;
105     std::vector<unsigned char> data(1048576U);
106     std::ranges::generate(data, std::ref(rbe));
107 
108     for (size_t chunkSize : {1U, 2U, 4U, 8U, 1024U, 1048576U})
109     {
110         ZstdCompressor comp;
111         std::vector<uint8_t> compressed;
112         ASSERT_TRUE(comp.init(data.size()));
113         for (size_t i = 0; i < data.size(); i += chunkSize)
114         {
115             bool more = i != data.size() - chunkSize;
116             std::optional<std::span<const uint8_t>> segmentOut =
117                 comp.compress(std::span(data).subspan(i, chunkSize), more);
118             ASSERT_TRUE(segmentOut);
119             if (!segmentOut)
120             {
121                 return;
122             }
123             compressed.insert(compressed.end(), segmentOut->begin(),
124                               segmentOut->end());
125         }
126         ZstdDecompressor decomp;
127 
128         std::optional<boost::asio::const_buffer> segmentOut =
129             decomp.decompress(boost::asio::buffer(compressed));
130         ASSERT_TRUE(segmentOut);
131         if (!segmentOut)
132         {
133             continue;
134         }
135         std::span<const uint8_t> decompressedSpan = std::span<const uint8_t>(
136             static_cast<const uint8_t*>(segmentOut->data()),
137             segmentOut->size());
138 
139         EXPECT_THAT(decompressedSpan, ElementsAreArray(data));
140     }
141 }
142 
143 } // namespace
144 } // namespace bmcweb
145 #endif
146