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

randombit / botan / 20283898778

16 Dec 2025 09:52PM UTC coverage: 90.52% (+0.2%) from 90.36%
20283898778

Pull #5167

github

web-flow
Merge 795a38954 into 3d96b675e
Pull Request #5167: Changes to reduce unnecessary inclusions

101154 of 111748 relevant lines covered (90.52%)

12682929.61 hits per line

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

88.7
/src/tests/tests.h
1
/*
2
* (C) 2014,2015 Jack Lloyd
3
* (C) 2015 Simon Warta (Kullo GmbH)
4
*
5
* Botan is released under the Simplified BSD License (see license.txt)
6
*/
7

8
#ifndef BOTAN_TESTS_H_
9
#define BOTAN_TESTS_H_
10

11
#include <botan/assert.h>
12
#include <botan/exceptn.h>
13
#include <botan/hex.h>
14
#include <botan/symkey.h>
15
#include <botan/types.h>
16
#include <deque>
17
#include <functional>
18
#include <iosfwd>
19
#include <map>
20
#include <memory>
21
#include <optional>
22
#include <set>
23
#include <span>
24
#include <sstream>
25
#include <string>
26
#include <unordered_map>
27
#include <variant>
28
#include <vector>
29

30
namespace Botan {
31

32
class RandomNumberGenerator;
33

34
#if defined(BOTAN_HAS_BIGINT)
35
class BigInt;
36
#endif
37

38
#if defined(BOTAN_HAS_LEGACY_EC_POINT)
39
class EC_Point;
40
#endif
41

42
}  // namespace Botan
43

44
namespace Botan_Tests {
45

46
#if defined(BOTAN_HAS_BIGINT)
47
using Botan::BigInt;
48
#endif
49

50
class Test_Error : public Botan::Exception {
51
   public:
52
      explicit Test_Error(const std::string& what) : Exception("Test error", what) {}
1✔
53

54
      Botan::ErrorType error_type() const noexcept override { return Botan::ErrorType::Unknown; }
×
55
};
56

57
class Test_Aborted final : public Test_Error {
58
   public:
59
      explicit Test_Aborted(const std::string& what) : Test_Error(what) {}
×
60
};
61

62
class Test_Options {
63
   public:
64
      Test_Options() = default;
1✔
65

66
      Test_Options(const std::vector<std::string>& requested_tests,
1✔
67
                   const std::vector<std::string>& skip_tests,
68
                   const std::string& data_dir,
69
                   const std::string& pkcs11_lib,
70
                   const std::string& provider,
71
                   const std::string& tpm2_tcti_name,
72
                   const std::string& tpm2_tcti_conf,
73
                   size_t tpm2_persistent_rsa_handle,
74
                   size_t tpm2_persistent_ecc_handle,
75
                   const std::string& tpm2_persistent_auth_value,
76
                   const std::string& drbg_seed,
77
                   const std::string& xml_results_dir,
78
                   const std::vector<std::string>& report_properties,
79
                   size_t test_runs,
80
                   size_t test_threads,
81
                   bool verbose,
82
                   bool log_success,
83
                   bool run_online_tests,
84
                   bool run_long_tests,
85
                   bool run_memory_intensive_tests,
86
                   bool abort_on_first_fail,
87
                   bool no_stdout) :
1✔
88
            m_requested_tests(requested_tests),
1✔
89
            m_skip_tests(skip_tests.begin(), skip_tests.end()),
1✔
90
            m_data_dir(data_dir),
1✔
91
            m_pkcs11_lib(pkcs11_lib),
1✔
92
            m_provider(provider),
1✔
93
            m_tpm2_tcti_name(tpm2_tcti_name),
1✔
94
            m_tpm2_tcti_conf(tpm2_tcti_conf),
1✔
95
            m_tpm2_persistent_rsa_handle(tpm2_persistent_rsa_handle),
1✔
96
            m_tpm2_persistent_ecc_handle(tpm2_persistent_ecc_handle),
1✔
97
            m_tpm2_persistent_auth_value(tpm2_persistent_auth_value),
1✔
98
            m_drbg_seed(drbg_seed),
1✔
99
            m_xml_results_dir(xml_results_dir),
1✔
100
            m_report_properties(report_properties),
1✔
101
            m_test_runs(test_runs),
1✔
102
            m_test_threads(test_threads),
1✔
103
            m_verbose(verbose),
1✔
104
            m_log_success(log_success),
1✔
105
            m_run_online_tests(run_online_tests),
1✔
106
            m_run_long_tests(run_long_tests),
1✔
107
            m_run_memory_intensive_tests(run_memory_intensive_tests),
1✔
108
            m_abort_on_first_fail(abort_on_first_fail),
1✔
109
            m_no_stdout(no_stdout) {}
1✔
110

111
      const std::vector<std::string>& requested_tests() const { return m_requested_tests; }
1✔
112

113
      const std::set<std::string>& skip_tests() const { return m_skip_tests; }
1✔
114

115
      const std::string& data_dir() const { return m_data_dir; }
116

117
      const std::string& pkcs11_lib() const { return m_pkcs11_lib; }
3✔
118

119
      const std::string& provider() const { return m_provider; }
2✔
120

121
      const std::optional<std::string>& tpm2_tcti_name() const { return m_tpm2_tcti_name; }
122

123
      const std::optional<std::string>& tpm2_tcti_conf() const { return m_tpm2_tcti_conf; }
124

125
      uint32_t tpm2_persistent_rsa_handle() const { return static_cast<uint32_t>(m_tpm2_persistent_rsa_handle); }
2✔
126

127
      uint32_t tpm2_persistent_ecc_handle() const { return static_cast<uint32_t>(m_tpm2_persistent_ecc_handle); }
2✔
128

129
      const std::string& tpm2_persistent_auth_value() const { return m_tpm2_persistent_auth_value; }
130

131
      const std::string& drbg_seed() const { return m_drbg_seed; }
1✔
132

133
      const std::string& xml_results_dir() const { return m_xml_results_dir; }
1✔
134

135
      std::map<std::string, std::string> report_properties() const;
136

137
      size_t test_runs() const { return m_test_runs; }
4✔
138

139
      size_t test_threads() const { return m_test_threads; }
3✔
140

141
      bool log_success() const { return m_log_success; }
3,519,963✔
142

143
      bool run_online_tests() const { return m_run_online_tests; }
2✔
144

145
      bool run_long_tests() const { return m_run_long_tests; }
2,871✔
146

147
      bool run_memory_intensive_tests() const { return m_run_memory_intensive_tests; }
1✔
148

149
      bool abort_on_first_fail() const { return m_abort_on_first_fail; }
25✔
150

151
      bool no_stdout() const { return m_no_stdout; }
1✔
152

153
      bool verbose() const { return m_verbose; }
2,534✔
154

155
   private:
156
      std::vector<std::string> m_requested_tests;
157
      std::set<std::string> m_skip_tests;
158
      std::string m_data_dir;
159
      std::string m_pkcs11_lib;
160
      std::string m_provider;
161
      std::optional<std::string> m_tpm2_tcti_name;
162
      std::optional<std::string> m_tpm2_tcti_conf;
163
      size_t m_tpm2_persistent_rsa_handle = 0;
164
      size_t m_tpm2_persistent_ecc_handle = 0;
165
      std::string m_tpm2_persistent_auth_value;
166
      std::string m_drbg_seed;
167
      std::string m_xml_results_dir;
168
      std::vector<std::string> m_report_properties;
169
      size_t m_test_runs = 0;
170
      size_t m_test_threads = 0;
171
      bool m_verbose = false;
172
      bool m_log_success = false;
173
      bool m_run_online_tests = false;
174
      bool m_run_long_tests = false;
175
      bool m_run_memory_intensive_tests = false;
176
      bool m_abort_on_first_fail = false;
177
      bool m_no_stdout = false;
178
};
179

180
namespace detail {
181

182
template <typename, typename = void>
183
constexpr bool has_Botan_to_string = false;
184
template <typename T>
185
constexpr bool has_Botan_to_string<T, std::void_t<decltype(Botan::to_string(std::declval<T>()))>> = true;
186

187
template <typename, typename = void>
188
constexpr bool has_std_to_string = false;
189
template <typename T>
190
constexpr bool has_std_to_string<T, std::void_t<decltype(std::to_string(std::declval<T>()))>> = true;
191

192
template <typename, typename = void>
193
constexpr bool has_ostream_operator = false;
194
template <typename T>
195
constexpr bool
196
   has_ostream_operator<T, std::void_t<decltype(operator<<(std::declval<std::ostringstream&>(), std::declval<T>()))>> =
197
      true;
198

199
template <typename T>
200
struct is_optional : std::false_type {};
201

202
template <typename T>
203
struct is_optional<std::optional<T>> : std::true_type {};
204

205
template <typename T>
206
constexpr bool is_optional_v = is_optional<T>::value;
207

208
}  // namespace detail
209

210
/**
211
 * A code location consisting of the source file path and a line
212
 */
213
struct CodeLocation {
214
      const char* path;
215
      unsigned int line;
216
};
217

218
/*
219
* A generic test which returns a set of results when run.
220
* The tests may not all have the same type (for example test
221
* "block" returns results for "AES-128" and "AES-256").
222
*
223
* For most test cases you want Text_Based_Test derived below
224
*/
225
class Test {
226
   public:
227
      /*
228
      * Some number of test results, all associated with who()
229
      */
230
      class Result final {
231
         public:
232
            explicit Result(std::string who);
233

234
            /**
235
             * This 'consolidation constructor' creates a single test result from
236
             * a vector of downstream test result objects.
237
             */
238
            Result(std::string who, const std::vector<Result>& downstream_results);
239

240
            size_t tests_passed() const { return m_tests_passed; }
12,550✔
241

242
            size_t tests_failed() const { return m_fail_log.size(); }
68,546✔
243

244
            size_t tests_run() const { return tests_passed() + tests_failed(); }
12,550✔
245

246
            bool any_results() const { return tests_run() > 0; }
247

248
            const std::string& who() const { return m_who; }
228,528✔
249

250
            const std::vector<std::string>& failures() const { return m_fail_log; }
2,508✔
251

252
            const std::vector<std::string>& notes() const { return m_log; }
2,508✔
253

254
            std::optional<uint64_t> elapsed_time() const {
2,508✔
255
               if(m_ns_taken == 0) {
2,508✔
256
                  return std::nullopt;
257
               } else {
258
                  return m_ns_taken;
1,468✔
259
               }
260
            }
261

262
            // Nanoseconds since epoch
263
            uint64_t timestamp() const { return m_timestamp; }
2,508✔
264

265
            std::string result_string() const;
266

267
            static Result Failure(const std::string& who, const std::string& what) {
1✔
268
               Result r(who);
1✔
269
               r.test_failure(what);
1✔
270
               return r;
1✔
271
            }
×
272

273
            static Result Note(const std::string& who, const std::string& what) {
×
274
               Result r(who);
×
275
               r.test_note(what);
×
276
               return r;
×
277
            }
×
278

279
            static Result OfExpectedFailure(bool expecting_failure, const Test::Result& result) {
280
               if(!expecting_failure) {
281
                  return result;
282
               }
283

284
               if(result.tests_failed() == 0) {
285
                  Result r = result;
286
                  r.test_failure("Expected this test to fail, but it did not");
287
                  return r;
288
               } else {
289
                  Result r(result.who());
290
                  r.test_note("Got expected failure");
291
                  return r;
292
               }
293
            }
294

295
            void merge(const Result& other, bool ignore_test_name = false);
296

297
            void test_note(const std::string& note, const char* extra = nullptr);
298

299
            template <typename Alloc>
300
            void test_note(const std::string& who, const std::vector<uint8_t, Alloc>& vec) {
12✔
301
               const std::string hex = Botan::hex_encode(vec);
12✔
302
               return test_note(who, hex.c_str());
12✔
303
            }
12✔
304

305
            void note_missing(const std::string& whatever);
306

307
            bool test_success(const std::string& note = "");
308

309
            bool test_failure(const std::string& err);
310

311
            bool test_failure(const std::string& what, const std::string& error);
312

313
            void test_failure(const std::string& what, const uint8_t buf[], size_t buf_len);
314

315
            template <typename Alloc>
316
            void test_failure(const std::string& what, const std::vector<uint8_t, Alloc>& buf) {
1✔
317
               test_failure(what, buf.data(), buf.size());
1✔
318
            }
1✔
319

320
            bool confirm(const std::string& what, bool expr, bool expected = true) {
164,537✔
321
               return test_eq(what, expr, expected);
164,208✔
322
            }
323

324
            /**
325
             * Require a condition, throw Test_Aborted otherwise
326
             * Note: works best when combined with CHECK scopes!
327
             */
328
            void require(const std::string& what, bool expr, bool expected = true) {
329✔
329
               if(!confirm(what, expr, expected)) {
329✔
330
                  throw Test_Aborted("test aborted, because required condition was not met: " + what);
×
331
               }
332
            }
329✔
333

334
            template <typename T>
335
            bool test_is_eq(const T& produced, const T& expected) {
369✔
336
               return test_is_eq("comparison", produced, expected);
369✔
337
            }
338

339
            template <typename T>
340
            bool test_is_eq(const std::string& what, const T& produced, const T& expected) {
863,135✔
341
               std::ostringstream out;
863,135✔
342
               out << m_who << " " << what;
863,135✔
343

344
               if(produced == expected) {
863,126✔
345
                  out << " produced expected result";
863,133✔
346
                  return test_success(out.str());
863,133✔
347
               } else {
348
                  out << " produced unexpected result '" << to_string(produced) << "' expected '" << to_string(expected)
349
                      << "'";
7✔
350
                  return test_failure(out.str());
2✔
351
               }
352
            }
863,135✔
353

354
            template <typename T>
355
            bool test_not_null(const std::string& what, const T& ptr) {
1,735✔
356
               if(ptr == nullptr) {
1,735✔
357
                  return test_failure(what + " was null");
×
358
               } else {
359
                  return test_success(what + " was not null");
1,735✔
360
               }
361
            }
362

363
            template <typename T>
364
            bool test_not_nullopt(const std::string& what, const std::optional<T>& val) {
10✔
365
               if(val == std::nullopt) {
10✔
366
                  return test_failure(what + " was nullopt");
×
367
               } else {
368
                  return test_success(what + " was not nullopt");
10✔
369
               }
370
            }
371

372
            bool test_eq(const std::string& what, const char* produced, const char* expected);
373

374
            bool test_is_nonempty(const std::string& what_is_it, const std::string& to_examine);
375

376
            bool test_eq(const std::string& what, const std::string& produced, const std::string& expected);
377

378
            bool test_eq(const std::string& what, bool produced, bool expected);
379

380
            bool test_eq(const std::string& what, size_t produced, size_t expected);
381
            bool test_eq_sz(const std::string& what, size_t produced, size_t expected);
382

383
            bool test_eq(const std::string& what,
384
                         const Botan::OctetString& produced,
385
                         const Botan::OctetString& expected);
386

387
            template <typename I1, typename I2>
388
            bool test_int_eq(I1 x, I2 y, const char* what) {
59✔
389
               return test_eq(what, static_cast<size_t>(x), static_cast<size_t>(y));
59✔
390
            }
391

392
            template <typename I1, typename I2>
393
            bool test_int_eq(const std::string& what, I1 x, I2 y) {
3,975✔
394
               return test_eq(what, static_cast<size_t>(x), static_cast<size_t>(y));
3,975✔
395
            }
396

397
            template <typename T>
398
            bool test_eq(const std::string& what, const std::optional<T>& a, const std::optional<T>& b) {
1,215✔
399
               if(a.has_value() != b.has_value()) {
1,215✔
400
                  std::ostringstream err;
×
401
                  err << m_who << " " << what << " only one of a/b was nullopt";
×
402
                  return test_failure(err.str());
×
403
               } else if(a.has_value() && b.has_value()) {
1,215✔
404
                  return test_is_eq(what, a.value(), b.value());
10✔
405
               } else {
406
                  // both nullopt
407
                  return test_success();
1,205✔
408
               }
409
            }
410

411
            bool test_lt(const std::string& what, size_t produced, size_t expected);
412
            bool test_lte(const std::string& what, size_t produced, size_t expected);
413
            bool test_gt(const std::string& what, size_t produced, size_t expected);
414
            bool test_gte(const std::string& what, size_t produced, size_t expected);
415

416
            template <typename T>
417
            bool test_rc_ok(const std::string& func, T rc) {
2,354✔
418
               static_assert(std::is_integral_v<T>, "Integer required.");
419

420
               if(rc != 0) {
2,354✔
421
                  std::ostringstream err;
1✔
422
                  err << m_who;
1✔
423
                  err << " " << func;
1✔
424
                  err << " unexpectedly failed with error code " << rc;
1✔
425
                  return test_failure(err.str());
1✔
426
               }
1✔
427

428
               return test_success();
2,353✔
429
            }
430

431
            template <typename T>
432
            bool test_rc_fail(const std::string& func, const std::string& why, T rc) {
25✔
433
               static_assert(std::is_integral_v<T>, "Integer required.");
434

435
               if(rc == 0) {
25✔
436
                  std::ostringstream err;
1✔
437
                  err << m_who;
1✔
438
                  err << " call to " << func << " unexpectedly succeeded";
1✔
439
                  err << " expecting failure because " << why;
1✔
440
                  return test_failure(err.str());
1✔
441
               }
1✔
442

443
               return test_success();
24✔
444
            }
445

446
            bool test_rc(const std::string& func, int expected, int rc);
447

448
            bool test_rc_init(const std::string& func, int rc);
449

450
            bool test_ne(const std::string& what, size_t produced, size_t expected);
451

452
            bool test_ne(const std::string& what, const std::string& str1, const std::string& str2);
453

454
#if defined(BOTAN_HAS_BIGINT)
455
            bool test_eq(const std::string& what, const BigInt& produced, const BigInt& expected);
456
            bool test_ne(const std::string& what, const BigInt& produced, const BigInt& expected);
457
#endif
458

459
#if defined(BOTAN_HAS_LEGACY_EC_POINT)
460
            bool test_eq(const std::string& what, const Botan::EC_Point& a, const Botan::EC_Point& b);
461
#endif
462

463
            bool test_eq(const char* producer,
464
                         const std::string& what,
465
                         const uint8_t produced[],
466
                         size_t produced_size,
467
                         const uint8_t expected[],
468
                         size_t expected_size);
469

470
            bool test_ne(const std::string& what,
471
                         const uint8_t produced[],
472
                         size_t produced_len,
473
                         const uint8_t expected[],
474
                         size_t expected_len);
475

476
            bool test_eq(const std::string& what,
120,356✔
477
                         std::span<const uint8_t> produced,
478
                         std::span<const uint8_t> expected) {
479
               return test_eq(nullptr, what, produced.data(), produced.size(), expected.data(), expected.size());
120,356✔
480
            }
481

482
            bool test_eq(const std::string& producer,
108,966✔
483
                         const std::string& what,
484
                         std::span<const uint8_t> produced,
485
                         std::span<const uint8_t> expected) {
486
               return test_eq(
108,966✔
487
                  producer.c_str(), what, produced.data(), produced.size(), expected.data(), expected.size());
108,966✔
488
            }
489

490
            bool test_eq(const std::string& what, std::span<const uint8_t> produced, const char* expected_hex) {
421✔
491
               const std::vector<uint8_t> expected = Botan::hex_decode(expected_hex);
421✔
492
               return test_eq(nullptr, what, produced.data(), produced.size(), expected.data(), expected.size());
421✔
493
            }
421✔
494

495
            template <std::size_t N>
496
            bool test_eq(const std::string& what,
2,440✔
497
                         const std::array<uint8_t, N>& produced,
498
                         const std::array<uint8_t, N>& expected) {
499
               return test_eq(nullptr, what, produced.data(), produced.size(), expected.data(), expected.size());
2,440✔
500
            }
501

502
            bool test_ne(const std::string& what,
3,402✔
503
                         std::span<const uint8_t> produced,
504
                         std::span<const uint8_t> expected) {
505
               return test_ne(what, produced.data(), produced.size(), expected.data(), expected.size());
3,402✔
506
            }
507

508
         private:
509
            class ThrowExpectations {
510
               public:
511
                  explicit ThrowExpectations(std::function<void()> fn) : m_fn(std::move(fn)) {}
98,420✔
512

513
                  ThrowExpectations(const ThrowExpectations&) = delete;
514
                  ThrowExpectations& operator=(const ThrowExpectations&) = delete;
515
                  ThrowExpectations(ThrowExpectations&&) = default;
516
                  ThrowExpectations& operator=(ThrowExpectations&&) = default;
517

518
                  ~ThrowExpectations() { BOTAN_ASSERT_NOMSG(m_consumed); }
92,871✔
519

520
                  ThrowExpectations& expect_success() {
1,645✔
521
                     BOTAN_ASSERT_NOMSG(!m_expected_message && !m_expected_exception_check_fn);
1,645✔
522
                     m_expect_success = true;
1,645✔
523
                     return *this;
1,645✔
524
                  }
525

526
                  ThrowExpectations& expect_message(const std::string& message) {
189✔
527
                     BOTAN_ASSERT_NOMSG(!m_expect_success);
189✔
528
                     m_expected_message = message;
189✔
529
                     return *this;
189✔
530
                  }
531

532
                  template <typename ExT>
533
                  ThrowExpectations& expect_exception_type() {
43,472✔
534
                     BOTAN_ASSERT_NOMSG(!m_expect_success);
43,472✔
535
                     m_expected_exception_check_fn = [](const std::exception_ptr& e) {
86,944✔
536
                        try {
537
                           if(e) {
43,472✔
538
                              std::rethrow_exception(e);
86,944✔
539
                           }
540
                        } catch(const ExT&) {
43,472✔
541
                           return true;
542
                        } catch(...) {
2✔
543
                           return false;
544
                        }
545
                        return false;
546
                     };
547
                     return *this;
43,472✔
548
                  }
549

550
                  bool check(const std::string& test_name, Test::Result& result);
551

552
               private:
553
                  std::function<void()> m_fn;
554
                  std::optional<std::string> m_expected_message;
555
                  std::function<bool(std::exception_ptr)> m_expected_exception_check_fn;
556
                  bool m_expect_success = false;
557
                  bool m_consumed = false;
558
            };
559

560
         public:
561
            bool test_throws(const std::string& what, const std::function<void()>& fn);
562

563
            bool test_throws(const std::string& what, const std::string& expected, const std::function<void()>& fn);
564

565
            bool test_no_throw(const std::string& what, const std::function<void()>& fn);
566

567
            template <typename ExceptionT>
568
            bool test_throws(const std::string& what, const std::function<void()>& fn) {
43,457✔
569
               return ThrowExpectations(fn).expect_exception_type<ExceptionT>().check(what, *this);
86,914✔
570
            }
571

572
            template <typename ExceptionT>
573
            bool test_throws(const std::string& what, const std::string& expected, const std::function<void()>& fn) {
15✔
574
               return ThrowExpectations(fn).expect_exception_type<ExceptionT>().expect_message(expected).check(what,
30✔
575
                                                                                                               *this);
15✔
576
            }
577

578
            void set_ns_consumed(uint64_t ns) { m_ns_taken = ns; }
47,967✔
579

580
            void start_timer();
581
            void end_timer();
582

583
            void set_code_location(CodeLocation where) { m_where = where; }
50,401✔
584

585
            const std::optional<CodeLocation>& code_location() const { return m_where; }
52,909✔
586

587
         private:
588
            template <typename T>
589
            std::string to_string(const T& v) {
2✔
590
               if constexpr(detail::is_optional_v<T>) {
591
                  return (v.has_value()) ? to_string(v.value()) : std::string("std::nullopt");
×
592
               } else if constexpr(detail::has_Botan_to_string<T>) {
593
                  return Botan::to_string(v);
594
               } else if constexpr(detail::has_ostream_operator<T>) {
595
                  std::ostringstream oss;
2✔
596
                  oss << v;
2✔
597
                  return oss.str();
2✔
598
               } else if constexpr(detail::has_std_to_string<T>) {
2✔
599
                  return std::to_string(v);
2✔
600
               } else {
601
                  return "<?>";
×
602
               }
603
            }
604

605
         private:
606
            std::string m_who;
607
            std::optional<CodeLocation> m_where;
608
            uint64_t m_timestamp;
609
            uint64_t m_started = 0;
610
            uint64_t m_ns_taken = 0;
611
            size_t m_tests_passed = 0;
612
            std::vector<std::string> m_fail_log;
613
            std::vector<std::string> m_log;
614
      };
615

616
      virtual ~Test();
617

618
      Test();
619
      Test(const Test& other) = delete;
620
      Test(Test&& other) = default;
621
      Test& operator=(const Test& other) = delete;
622
      Test& operator=(Test&& other) = delete;
623

624
      virtual std::vector<Test::Result> run() = 0;
625

626
      virtual std::vector<std::string> possible_providers(const std::string& alg);
627

628
      void initialize(std::string test_name, CodeLocation location);
629

630
      const std::string& test_name() const { return m_test_name; }
19✔
631

632
      Botan::RandomNumberGenerator& rng() const;
633

634
      const std::optional<CodeLocation>& registration_location() const { return m_registration_location; }
50,401✔
635

636
      /// @p smoke_test are run first in an unfiltered test run
637
      static void register_test(const std::string& category,
638
                                const std::string& name,
639
                                bool smoke_test,
640
                                bool needs_serialization,
641
                                std::function<std::unique_ptr<Test>()> maker_fn);
642

643
      static std::set<std::string> registered_tests();
644
      static std::set<std::string> registered_test_categories();
645
      static std::vector<std::string> filter_registered_tests(const std::vector<std::string>& requested,
646
                                                              const std::set<std::string>& to_be_skipped);
647

648
      static std::unique_ptr<Test> get_test(const std::string& test_name);
649
      static bool test_needs_serialization(const std::string& test_name);
650

651
      static std::string data_dir(const std::string& subdir);
652
      static std::vector<std::string> files_in_data_dir(const std::string& subdir);
653
      static std::string data_file(const std::string& file);
654
      static std::string data_file_as_temporary_copy(const std::string& what);
655

656
      static std::string format_time(uint64_t nanoseconds);
657

658
      static std::vector<uint8_t> mutate_vec(const std::vector<uint8_t>& v,
659
                                             Botan::RandomNumberGenerator& rng,
660
                                             bool maybe_resize = false,
661
                                             size_t min_offset = 0);
662

663
      static void set_test_options(const Test_Options& opts);
664

665
      static void set_test_rng_seed(std::span<const uint8_t> seed, size_t epoch = 0);
666

667
      static const Test_Options& options() { return m_opts; }
668

669
      static bool run_long_tests() { return options().run_long_tests(); }
2,871✔
670

671
      static bool run_memory_intensive_tests() { return options().run_memory_intensive_tests(); }
1✔
672

673
      static const std::string& pkcs11_lib() { return options().pkcs11_lib(); }
50✔
674

675
      static std::string temp_file_name(const std::string& basename);
676
      static bool copy_file(const std::string& from, const std::string& to);
677

678
      static std::vector<std::string> provider_filter(const std::vector<std::string>& providers);
679

680
      static std::string read_data_file(const std::string& path);
681
      static std::vector<uint8_t> read_binary_data_file(const std::string& path);
682

683
      static std::unique_ptr<Botan::RandomNumberGenerator> new_rng(std::string_view test_name);
684
      static std::shared_ptr<Botan::RandomNumberGenerator> new_shared_rng(std::string_view test_name);
685

686
      static std::string random_password(Botan::RandomNumberGenerator& rng);
687
      static size_t random_index(Botan::RandomNumberGenerator& rng, size_t max);
688
      static uint64_t timestamp();  // nanoseconds arbitrary epoch
689

690
      static std::vector<Test::Result> flatten_result_lists(std::vector<std::vector<Test::Result>> result_lists);
691

692
   private:
693
      static Test_Options m_opts;
694
      static std::string m_test_rng_seed;
695

696
      /// The string ID that was used to register this test
697
      std::string m_test_name;
698
      /// The source file location where the test was registered
699
      std::optional<CodeLocation> m_registration_location;
700
      /// The test-specific RNG state
701
      mutable std::unique_ptr<Botan::RandomNumberGenerator> m_test_rng;
702
};
703

704
/*
705
* Register the test with the runner
706
*/
707
template <typename Test_Class>
708
class TestClassRegistration {
709
   public:
710
      TestClassRegistration(const std::string& category,
378✔
711
                            const std::string& name,
712
                            bool smoke_test,
713
                            bool needs_serialization,
714
                            const CodeLocation& registration_location) {
715
         Test::register_test(category, name, smoke_test, needs_serialization, [=] {
1,843✔
716
            auto test = std::make_unique<Test_Class>();
378✔
717
            test->initialize(name, registration_location);
756✔
718
            return test;
378✔
719
         });
×
720
      }
378✔
721
};
722

723
// NOLINTBEGIN(*-macro-usage)
724

725
#define BOTAN_REGISTER_TEST(category, name, Test_Class) \
726
   /* NOLINTNEXTLINE(cert-err58-cpp) */                 \
727
   const TestClassRegistration<Test_Class> reg_##Test_Class##_tests(category, name, false, false, {__FILE__, __LINE__})
728
#define BOTAN_REGISTER_SERIALIZED_TEST(category, name, Test_Class) \
729
   /* NOLINTNEXTLINE(cert-err58-cpp) */                            \
730
   const TestClassRegistration<Test_Class> reg_##Test_Class##_tests(category, name, false, true, {__FILE__, __LINE__})
731
#define BOTAN_REGISTER_SMOKE_TEST(category, name, Test_Class) \
732
   /* NOLINTNEXTLINE(cert-err58-cpp) */                       \
733
   const TestClassRegistration<Test_Class> reg_##Test_Class##_tests(category, name, true, false, {__FILE__, __LINE__})
734
#define BOTAN_REGISTER_SERIALIZED_SMOKE_TEST(category, name, Test_Class) \
735
   /* NOLINTNEXTLINE(cert-err58-cpp) */                                  \
736
   const TestClassRegistration<Test_Class> reg_##Test_Class##_tests(category, name, true, true, {__FILE__, __LINE__})
737

738
// NOLINTEND(*-macro-usage)
739

740
typedef Test::Result (*test_fn)();
741
typedef std::vector<Test::Result> (*test_fn_vec)();
742

743
class FnTest : public Test {
744
   private:
745
      using TestFnVariant = std::variant<test_fn, test_fn_vec>;
746

747
      template <typename TestFn>
748
      std::vector<TestFnVariant> make_variant_vector(TestFn fn) {
34✔
749
         using T = std::decay_t<decltype(fn)>;
750
         static_assert(std::is_same_v<T, test_fn> || std::is_same_v<T, test_fn_vec>,
751
                       "functions passed to BOTAN_REGISTER_TEST_FN must either return a "
752
                       "single Test::Result or a std::vector of Test::Result");
753
         return {fn};
34✔
754
      }
755

756
      template <typename TestFn, typename... TestFns>
757
      std::vector<TestFnVariant> make_variant_vector(const TestFn& fn, const TestFns&... fns) {
31✔
758
         auto functions = make_variant_vector(fns...);
31✔
759
         functions.emplace_back(fn);
31✔
760
         return functions;
31✔
761
      }
×
762

763
   public:
764
      template <typename... TestFns>
765
      explicit FnTest(TestFns... fns) : m_fns(make_variant_vector(fns...)) {}
34✔
766

767
      std::vector<Test::Result> run() override {
34✔
768
         std::vector<Test::Result> result;
34✔
769

770
         // TODO(Botan4) use std::ranges::reverse_view here once available (need newer Clang)
771
         // NOLINTNEXTLINE(modernize-loop-convert)
772
         for(auto fn_variant = m_fns.crbegin(); fn_variant != m_fns.crend(); ++fn_variant) {
99✔
773
            std::visit(
65✔
774
               [&](auto&& fn) {
130✔
775
                  using T = std::decay_t<decltype(fn)>;
776
                  if constexpr(std::is_same_v<T, test_fn>) {
777
                     result.emplace_back(fn());
7✔
778
                  } else {
779
                     const auto results = fn();
58✔
780
                     result.insert(result.end(), results.begin(), results.end());
58✔
781
                  }
58✔
782
               },
65✔
783
               *fn_variant);
65✔
784
         }
785

786
         return result;
34✔
787
      }
×
788

789
   private:
790
      std::vector<TestFnVariant> m_fns;
791
};
792

793
class TestFnRegistration {
794
   public:
795
      template <typename... TestFns>
796
      TestFnRegistration(const std::string& category,
34✔
797
                         const std::string& name,
798
                         bool smoke_test,
799
                         bool needs_serialization,
800
                         const CodeLocation& registration_location,
801
                         TestFns... fn) {
802
         Test::register_test(category, name, smoke_test, needs_serialization, [=] {
125✔
803
            auto test = std::make_unique<FnTest>(fn...);
34✔
804
            test->initialize(name, registration_location);
68✔
805
            return test;
34✔
806
         });
×
807
      }
34✔
808
};
809

810
// NOLINTBEGIN(*-macro-usage)
811

812
#define BOTAN_REGISTER_TEST_FN_IMPL(category, name, smoke_test, needs_serialization, fn0, ...) \
813
   /* NOLINTNEXTLINE(cert-err58-cpp) */                                                        \
814
   static const TestFnRegistration register_##fn0(                                             \
815
      category, name, smoke_test, needs_serialization, {__FILE__, __LINE__}, fn0 __VA_OPT__(, ) __VA_ARGS__)
816

817
#define BOTAN_REGISTER_TEST_FN(category, name, ...) \
818
   BOTAN_REGISTER_TEST_FN_IMPL(category, name, false, false, __VA_ARGS__)
819
#define BOTAN_REGISTER_SMOKE_TEST_FN(category, name, ...) \
820
   BOTAN_REGISTER_TEST_FN_IMPL(category, name, true, false, __VA_ARGS__)
821
#define BOTAN_REGISTER_SERIALIZED_TEST_FN(category, name, ...) \
822
   BOTAN_REGISTER_TEST_FN_IMPL(category, name, false, true, __VA_ARGS__)
823
#define BOTAN_REGISTER_SERIALIZED_SMOKE_TEST_FN(category, name, ...) \
824
   BOTAN_REGISTER_TEST_FN_IMPL(category, name, true, true, __VA_ARGS__)
825

826
// NOLINTEND(*-macro-usage)
827

828
class VarMap {
360✔
829
   public:
830
      void clear() { m_vars.clear(); }
33,997✔
831

832
      void add(const std::string& key, const std::string& value) { m_vars[key] = value; }
154,995✔
833

834
      bool has_key(const std::string& key) const { return m_vars.count(key) == 1; }
425,465✔
835

836
      bool get_req_bool(const std::string& key) const;
837

838
      std::vector<uint8_t> get_req_bin(const std::string& key) const;
839
      std::vector<uint8_t> get_opt_bin(const std::string& key) const;
840

841
      std::vector<std::vector<uint8_t>> get_req_bin_list(const std::string& key) const;
842

843
#if defined(BOTAN_HAS_BIGINT)
844
      Botan::BigInt get_req_bn(const std::string& key) const;
845
      Botan::BigInt get_opt_bn(const std::string& key, const Botan::BigInt& def_value) const;
846
#endif
847

848
      std::string get_req_str(const std::string& key) const;
849
      std::string get_opt_str(const std::string& key, const std::string& def_value) const;
850

851
      size_t get_req_sz(const std::string& key) const;
852

853
      uint8_t get_req_u8(const std::string& key) const;
854
      uint32_t get_req_u32(const std::string& key) const;
855
      uint64_t get_req_u64(const std::string& key) const;
856

857
      size_t get_opt_sz(const std::string& key, size_t def_value) const;
858

859
      uint64_t get_opt_u64(const std::string& key, uint64_t def_value) const;
860

861
   private:
862
      std::unordered_map<std::string, std::string> m_vars;
863
};
864

865
/*
866
* A test based on reading an input file which contains key/value pairs
867
* Special note: the last value in required_key (there must be at least
868
* one), is the output key. This triggers the callback.
869
*
870
* Calls run_one_test with the variables set. If an ini-style [header]
871
* is used in the file, then header will be set to that value. This allows
872
* splitting up tests between [valid] and [invalid] tests, or different
873
* related algorithms tested in the same file. Use the get_XXX functions
874
* on VarMap to retrieve formatted values.
875
*
876
* If most of your tests are text-based but you find yourself with a few
877
* odds-and-ends tests that you want to do, override run_final_tests which
878
* can test whatever it likes and returns a vector of Results.
879
*/
880
class Text_Based_Test : public Test {
881
   public:
882
      Text_Based_Test(const std::string& data_src,
883
                      const std::string& required_keys_str,
884
                      const std::string& optional_keys_str = "");
885

886
      virtual bool clear_between_callbacks() const { return true; }
33,193✔
887

888
      std::vector<Test::Result> run() override;
889

890
   protected:
891
      std::string get_next_line();
892

893
      virtual Test::Result run_one_test(const std::string& header, const VarMap& vars) = 0;
894
      // Called before run_one_test
895
      virtual bool skip_this_test(const std::string& header, const VarMap& vars);
896

897
      virtual std::vector<Test::Result> run_final_tests() { return std::vector<Test::Result>(); }
171✔
898

899
   private:
900
      std::string m_data_src;
901
      std::set<std::string> m_required_keys;
902
      std::set<std::string> m_optional_keys;
903
      std::string m_output_key;
904

905
      bool m_first = true;
906
      std::unique_ptr<std::istream> m_cur;
907
      std::string m_cur_src_name;
908
      std::deque<std::string> m_srcs;
909
      std::vector<std::string> m_cpu_flags;
910
};
911

912
/**
913
 * This is a convenience wrapper to write small self-contained and in particular
914
 * exception-safe unit tests. If some (unexpected) exception is thrown in one of
915
 * the CHECK-scopes, it will fail the particular test gracefully with a human-
916
 * understandable failure output.
917
 *
918
 * Example Usage:
919
 *
920
 * ```
921
 * std::vector<Test::Result> test_something()
922
 *    {
923
 *    return
924
 *       {
925
 *       CHECK("some unit test name", [](Test::Result& r)
926
 *          {
927
 *          r.confirm("some observation", 1+1 == 2);
928
 *          }),
929
 *       CHECK("some other unit test name", [](Test::Result& r)
930
 *          {
931
 *          // ...
932
 *          })
933
 *       };
934
 *    }
935
 *
936
 * BOTAN_REGISTER_TEST_FN("some_category", "some_test_name", test_something);
937
 * ```
938
 */
939
template <typename FunT>
940
Test::Result CHECK(const char* name, FunT check_fun) {
508✔
941
   Botan_Tests::Test::Result r(name);
585✔
942

943
   try {
944
      check_fun(r);
508✔
945
   } catch(const Botan_Tests::Test_Aborted&) {
×
946
      // pass, failure was already noted in the responsible `require`
947
   } catch(const std::exception& ex) {
×
948
      r.test_failure("failed unexpectedly", ex.what());
×
949
   } catch(...) {
×
950
      r.test_failure("failed with unknown exception");
×
951
   }
952

953
   return r;
508✔
954
}
×
955

956
}  // namespace Botan_Tests
957

958
#endif
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