• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

randombit / botan / 24053024312

06 Apr 2026 09:51PM UTC coverage: 91.831% (+2.4%) from 89.454%
24053024312

Pull #5521

github

web-flow
Merge 89fecf072 into 417709dd7
Pull Request #5521: Rollup of small fixes

108699 of 118368 relevant lines covered (91.83%)

11266117.12 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

87.73
/src/tests/test_compression.cpp
1
/*
2
* (C) 2015 Jack Lloyd
3
*
4
* Botan is released under the Simplified BSD License (see license.txt)
5
*/
6

7
#include "tests.h"
8

9
#if defined(BOTAN_HAS_COMPRESSION)
10
   #include <botan/compression.h>
11
   #include <botan/rng.h>
12
   #include <cstring>
13
#endif
14

15
namespace Botan_Tests {
16

17
#if defined(BOTAN_HAS_COMPRESSION)
18

19
namespace {
20

21
const char* const COMPRESSION_TEST_TEXT =
22
   "'Twas brillig, and the slithy toves"
23
   "Did gyre and gimble in the wabe:"
24
   "All mimsy were the borogoves,"
25
   "And the mome raths outgrabe."
26

27
   "'Beware the Jabberwock, my son!"
28
   "The jaws that bite, the claws that catch!"
29
   "Beware the Jubjub bird, and shun"
30
   "The frumious Bandersnatch!'"
31

32
   "He took his vorpal sword in hand;"
33
   "Long time the manxome foe he sought-"
34
   "So rested he by the Tumtum tree"
35
   "And stood awhile in thought."
36

37
   "And, as in uffish thought he stood,"
38
   "The Jabberwock, with eyes of flame,"
39
   "Came whiffling through the tulgey wood,"
40
   "And burbled as it came!"
41

42
   "One, two! One, two! And through and through"
43
   "The vorpal blade went snicker-snack!"
44
   "He left it dead, and with its head"
45
   "He went galumphing back."
46

47
   "'And hast thou slain the Jabberwock?"
48
   "Come to my arms, my beamish boy!"
49
   "O frabjous day! Callooh! Callay!'"
50
   "He chortled in his joy."
51

52
   "'Twas brillig, and the slithy toves"
53
   "Did gyre and gimble in the wabe:"
54
   "All mimsy were the borogoves,"
55
   "And the mome raths outgrabe.";
56

57
class Compression_Tests final : public Test {
1✔
58
   public:
59
      std::vector<Test::Result> run() override {
1✔
60
         std::vector<Test::Result> results;
1✔
61
         const size_t text_len = std::strlen(COMPRESSION_TEST_TEXT);
1✔
62

63
         for(const std::string algo : {"zlib", "deflate", "gzip", "bz2", "lzma"}) {
6✔
64
            try {
5✔
65
               Test::Result result(algo + " compression");
5✔
66

67
               result.start_timer();
5✔
68

69
               auto c = Botan::Compression_Algorithm::create(algo);
5✔
70
               auto d = Botan::Decompression_Algorithm::create(algo);
5✔
71

72
               if(!c || !d) {
5✔
73
                  result.note_missing(algo);
×
74
                  continue;
×
75
               }
76

77
               result.test_str_ne("Not the same name", c->name(), d->name());
5✔
78

79
               const Botan::secure_vector<uint8_t> empty;
5✔
80
               const Botan::secure_vector<uint8_t> all_zeros(text_len, 0);
5✔
81
               const Botan::secure_vector<uint8_t> random_binary = this->rng().random_vec(text_len);
5✔
82
               const Botan::secure_vector<uint8_t> short_text = {'f', 'o', 'o', '\n'};
5✔
83

84
               const uint8_t* textb = reinterpret_cast<const uint8_t*>(COMPRESSION_TEST_TEXT);
5✔
85
               const Botan::secure_vector<uint8_t> text(textb, textb + text_len);
5✔
86

87
               const size_t c1_e = run_compression(result, 1, *c, *d, empty);
5✔
88
               const size_t c9_e = run_compression(result, 9, *c, *d, empty);
5✔
89
               const size_t c1_z = run_compression(result, 1, *c, *d, all_zeros);
5✔
90
               const size_t c9_z = run_compression(result, 9, *c, *d, all_zeros);
5✔
91
               const size_t c1_r = run_compression(result, 1, *c, *d, random_binary);
5✔
92
               const size_t c9_r = run_compression(result, 9, *c, *d, random_binary);
5✔
93
               const size_t c1_t = run_compression(result, 1, *c, *d, text);
5✔
94
               const size_t c9_t = run_compression(result, 9, *c, *d, text);
5✔
95
               const size_t c1_s = run_compression(result, 1, *c, *d, short_text);
5✔
96
               const size_t c9_s = run_compression(result, 9, *c, *d, short_text);
5✔
97

98
               result.test_sz_gte("Empty input L1 compresses to non-empty output", c1_e, 1);
5✔
99
               result.test_sz_gte("Empty input L9 compresses to non-empty output", c9_e, 1);
5✔
100

101
               // We assume that Level 9 is better than Level 1, but this is not
102
               // guaranteed (see GitHub #3896). Hence, we assert that level 9
103
               // it is at most 10% worse than level 1.
104
               result.test_sz_gte("Level 9 compresses empty at least as well as level 1", c1_e + (c1_e / 10), c9_e);
5✔
105
               result.test_sz_gte("Level 9 compresses zeros at least as well as level 1", c1_z + (c1_z / 10), c9_z);
5✔
106
               result.test_sz_gte("Level 9 compresses random at least as well as level 1", c1_r + (c1_r / 10), c9_r);
5✔
107
               result.test_sz_gte("Level 9 compresses text at least as well as level 1", c1_t + (c1_t / 10), c9_t);
5✔
108
               result.test_sz_gte(
10✔
109
                  "Level 9 compresses short text at least as well as level 1", c1_s + (c1_s / 10), c9_s);
5✔
110

111
               result.test_sz_lt("Zeros compresses much better than text", c1_z / 8, c1_t);
5✔
112
               result.test_sz_lt("Text compresses much better than random", c1_t / 2, c1_r);
5✔
113

114
               result.end_timer();
5✔
115

116
               results.emplace_back(result);
5✔
117
            } catch(std::exception& e) {
30✔
118
               results.emplace_back(Test::Result::Failure("testing " + algo, e.what()));
×
119
            }
×
120
         }
5✔
121

122
         return results;
1✔
123
      }
5✔
124

125
   private:
126
      // Returns # of bytes of compressed message
127
      size_t run_compression(Test::Result& result,
50✔
128
                             size_t level,
129
                             Botan::Compression_Algorithm& c,
130
                             Botan::Decompression_Algorithm& d,
131
                             const Botan::secure_vector<uint8_t>& msg) {
132
         Botan::secure_vector<uint8_t> compressed(2 * msg.size());
50✔
133

134
         for(const bool with_flush : {true, false}) {
150✔
135
            try {
100✔
136
               compressed = msg;
100✔
137

138
               c.start(level);
100✔
139
               c.update(compressed, 0, false);
100✔
140

141
               if(with_flush) {
100✔
142
                  Botan::secure_vector<uint8_t> flush_bits;
50✔
143
                  c.update(flush_bits, 0, true);
50✔
144
                  compressed += flush_bits;
50✔
145
               }
50✔
146

147
               Botan::secure_vector<uint8_t> final_bits;
100✔
148
               c.finish(final_bits);
100✔
149
               compressed += final_bits;
100✔
150

151
               Botan::secure_vector<uint8_t> decompressed = compressed;
100✔
152
               d.start();
100✔
153
               d.update(decompressed);
100✔
154

155
               Botan::secure_vector<uint8_t> final_outputs;
100✔
156
               d.finish(final_outputs);
100✔
157

158
               decompressed += final_outputs;
100✔
159

160
               result.test_bin_eq("compression round tripped", msg, decompressed);
100✔
161
            } catch(Botan::Exception& e) {
300✔
162
               result.test_failure(e.what());
×
163
            }
×
164
         }
165

166
         return compressed.size();
50✔
167
      }
50✔
168
};
169

170
BOTAN_REGISTER_TEST("compression", "compression_tests", Compression_Tests);
171

172
class Concatenated_Compression_Tests final : public Test {
1✔
173
   public:
174
      std::vector<Test::Result> run() override {
1✔
175
         std::vector<Test::Result> results;
1✔
176

177
         for(const std::string algo : {"zlib", "deflate", "gzip", "bz2", "lzma"}) {
6✔
178
            try {
5✔
179
               Test::Result result(algo + " concatenated decompression");
5✔
180

181
               auto c = Botan::Compression_Algorithm::create(algo);
5✔
182
               auto d = Botan::Decompression_Algorithm::create(algo);
5✔
183

184
               if(!c || !d) {
5✔
185
                  result.note_missing(algo);
×
186
                  continue;
×
187
               }
188

189
               const Botan::secure_vector<uint8_t> msg1 = {'H', 'e', 'l', 'l', 'o'};
5✔
190
               const Botan::secure_vector<uint8_t> msg2 = {'W', 'o', 'r', 'l', 'd'};
5✔
191

192
               // Compress two messages independently
193
               auto compress = [&](const Botan::secure_vector<uint8_t>& msg) {
15✔
194
                  Botan::secure_vector<uint8_t> buf = msg;
10✔
195
                  c->start(6);
10✔
196
                  c->update(buf, 0, false);
10✔
197
                  Botan::secure_vector<uint8_t> final_bits;
10✔
198
                  c->finish(final_bits);
10✔
199
                  buf += final_bits;
10✔
200
                  return buf;
10✔
201
               };
10✔
202

203
               const auto c1 = compress(msg1);
5✔
204
               const auto c2 = compress(msg2);
5✔
205

206
               // Concatenate the two compressed streams
207
               Botan::secure_vector<uint8_t> concatenated = c1;
5✔
208
               concatenated += c2;
5✔
209

210
               // Decompress in a single update() call
211
               Botan::secure_vector<uint8_t> decompressed = concatenated;
5✔
212
               d->start();
5✔
213
               d->update(decompressed);
5✔
214
               Botan::secure_vector<uint8_t> final_outputs;
5✔
215
               d->finish(final_outputs);
5✔
216
               decompressed += final_outputs;
5✔
217

218
               Botan::secure_vector<uint8_t> expected = msg1;
5✔
219
               expected += msg2;
5✔
220
               result.test_bin_eq("concatenated streams decompressed correctly", expected, decompressed);
5✔
221

222
               // Decompress feeding one byte at a time
223
               decompressed.clear();
5✔
224
               d->start();
5✔
225
               for(const auto byte : concatenated) {
310✔
226
                  Botan::secure_vector<uint8_t> buf = {byte};
305✔
227
                  d->update(buf);
305✔
228
                  decompressed += buf;
305✔
229
               }
305✔
230
               final_outputs.clear();
5✔
231
               d->finish(final_outputs);
5✔
232
               decompressed += final_outputs;
5✔
233

234
               result.test_bin_eq("byte-at-a-time concatenated streams", expected, decompressed);
5✔
235

236
               results.emplace_back(result);
5✔
237
            } catch(std::exception& e) {
45✔
238
               results.emplace_back(Test::Result::Failure("testing " + algo, e.what()));
×
239
            }
×
240
         }
5✔
241

242
         return results;
1✔
243
      }
×
244
};
245

246
BOTAN_REGISTER_TEST("compression", "concat_compression_tests", Concatenated_Compression_Tests);
247

248
class CompressionCreate_Tests final : public Test {
1✔
249
   public:
250
      std::vector<Test::Result> run() override {
1✔
251
         std::vector<Test::Result> results;
1✔
252

253
         for(const std::string algo : {"zlib", "deflate", "gzip", "bz2", "lzma"}) {
6✔
254
            try {
5✔
255
               Test::Result result(algo + " create compression");
5✔
256

257
               auto c1 = Botan::Compression_Algorithm::create(algo);
5✔
258
               auto d1 = Botan::Decompression_Algorithm::create(algo);
5✔
259

260
               if(!c1 || !d1) {
5✔
261
                  result.note_missing(algo);
×
262
                  continue;
×
263
               }
264
               result.test_str_ne("Not the same name after create", c1->name(), d1->name());
5✔
265

266
               auto c2 = Botan::Compression_Algorithm::create_or_throw(algo);
5✔
267
               auto d2 = Botan::Decompression_Algorithm::create_or_throw(algo);
5✔
268

269
               if(!c2 || !d2) {
5✔
270
                  result.note_missing(algo);
×
271
                  continue;
×
272
               }
273
               result.test_str_ne("Not the same name after create_or_throw", c2->name(), d2->name());
5✔
274

275
               results.emplace_back(result);
5✔
276
            } catch(std::exception& e) {
20✔
277
               results.emplace_back(Test::Result::Failure("testing " + algo, e.what()));
×
278
            }
×
279
         }
5✔
280

281
         {
1✔
282
            Test::Result result("create invalid compression");
1✔
283
            result.test_throws("lookup error", "Unavailable Compression bogocompress", [&]() {
1✔
284
               Botan::Compression_Algorithm::create_or_throw("bogocompress");
1✔
285
            });
×
286
            result.test_throws("lookup error", "Unavailable Decompression bogocompress", [&]() {
1✔
287
               Botan::Decompression_Algorithm::create_or_throw("bogocompress");
1✔
288
            });
×
289
            results.emplace_back(result);
1✔
290
         }
1✔
291

292
         return results;
1✔
293
      }
×
294
};
295

296
BOTAN_REGISTER_TEST("compression", "create_compression", CompressionCreate_Tests);
297

298
}  // namespace
299

300
#endif
301

302
}  // namespace Botan_Tests
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc