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

randombit / botan / 22022566023

14 Feb 2026 06:55PM UTC coverage: 90.067%. Remained the same
22022566023

push

github

web-flow
Merge pull request #5334 from randombit/jack/test-h-remove-confirm

Remove confirm test helper, use test_is_true or test_is_false instead

102260 of 113538 relevant lines covered (90.07%)

11510296.11 hits per line

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

85.8
/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
/*
12
Warning: be very careful about adding any new includes here
13

14
Each include is parsed for every test file which can get quite expensive
15
*/
16

17
#include <botan/types.h>
18
#include <functional>
19
#include <iosfwd>
20
#include <memory>
21
#include <optional>
22
#include <span>
23
#include <sstream>
24
#include <stdexcept>
25
#include <string>
26
#include <variant>
27
#include <vector>
28

29
namespace Botan {
30

31
class RandomNumberGenerator;
32

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

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

41
}  // namespace Botan
42

43
namespace Botan_Tests {
44

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

49
class Test_Error : public std::runtime_error {
50
   public:
51
      explicit Test_Error(std::string_view what);
52
};
53

54
class Test_Aborted final : public Test_Error {
55
   public:
56
      explicit Test_Aborted(std::string_view what) : Test_Error(what) {}
×
57
};
58

59
class Test_Options {
60
   public:
61
      Test_Options() = default;
62

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

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

110
      const std::vector<std::string>& skip_tests() const { return m_skip_tests; }
1✔
111

112
      const std::string& data_dir() const { return m_data_dir; }
113

114
      const std::string& pkcs11_lib() const { return m_pkcs11_lib; }
3✔
115

116
      const std::string& provider() const { return m_provider; }
2✔
117

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

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

122
      uint32_t tpm2_persistent_rsa_handle() const { return static_cast<uint32_t>(m_tpm2_persistent_rsa_handle); }
2✔
123

124
      uint32_t tpm2_persistent_ecc_handle() const { return static_cast<uint32_t>(m_tpm2_persistent_ecc_handle); }
2✔
125

126
      const std::string& tpm2_persistent_auth_value() const { return m_tpm2_persistent_auth_value; }
127

128
      const std::string& drbg_seed() const { return m_drbg_seed; }
1✔
129

130
      const std::string& xml_results_dir() const { return m_xml_results_dir; }
1✔
131

132
      const std::vector<std::string>& report_properties() const { return m_report_properties; }
1✔
133

134
      size_t test_runs() const { return m_test_runs; }
4✔
135

136
      size_t test_threads() const { return m_test_threads; }
3✔
137

138
      bool log_success() const { return m_log_success; }
3,620,301✔
139

140
      bool run_online_tests() const { return m_run_online_tests; }
2✔
141

142
      bool run_long_tests() const { return m_run_long_tests; }
2,871✔
143

144
      bool run_memory_intensive_tests() const { return m_run_memory_intensive_tests; }
1✔
145

146
      bool abort_on_first_fail() const { return m_abort_on_first_fail; }
25✔
147

148
      bool no_stdout() const { return m_no_stdout; }
1✔
149

150
      bool verbose() const { return m_verbose; }
2,542✔
151

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

177
namespace detail {
178

179
template <typename, typename = void>
180
constexpr bool has_std_to_string = false;
181
template <typename T>
182
constexpr bool has_std_to_string<T, std::void_t<decltype(std::to_string(std::declval<T>()))>> = true;
183

184
template <typename, typename = void>
185
constexpr bool has_ostream_operator = false;
186
template <typename T>
187
constexpr bool
188
   has_ostream_operator<T, std::void_t<decltype(operator<<(std::declval<std::ostringstream&>(), std::declval<T>()))>> =
189
      true;
190

191
template <typename T>
192
struct is_optional : std::false_type {};
193

194
template <typename T>
195
struct is_optional<std::optional<T>> : std::true_type {};
196

197
template <typename T>
198
constexpr bool is_optional_v = is_optional<T>::value;
199

200
}  // namespace detail
201

202
/**
203
 * A code location consisting of the source file path and a line
204
 */
205
struct CodeLocation {
206
      const char* path;
207
      unsigned int line;
208
};
209

210
/*
211
* A generic test which returns a set of results when run.
212
* The tests may not all have the same type (for example test
213
* "block" returns results for "AES-128" and "AES-256").
214
*
215
* For most test cases you want Text_Based_Test derived below
216
*/
217
class Test {
218
   public:
219
      /*
220
      * Some number of test results, all associated with who()
221
      */
222
      class Result final {
223
         public:
224
            explicit Result(std::string_view who);
225

226
            /**
227
             * This 'consolidation constructor' creates a single test result from
228
             * a vector of downstream test result objects.
229
             */
230
            Result(std::string_view who, const std::vector<Result>& downstream_results);
231

232
            size_t tests_passed() const { return m_tests_passed; }
12,590✔
233

234
            size_t tests_failed() const { return m_fail_log.size(); }
68,935✔
235

236
            size_t tests_run() const { return tests_passed() + tests_failed(); }
12,590✔
237

238
            bool any_results() const { return tests_run() > 0; }
239

240
            const std::string& who() const { return m_who; }
235,960✔
241

242
            const std::vector<std::string>& failures() const { return m_fail_log; }
2,516✔
243

244
            const std::vector<std::string>& notes() const { return m_log; }
2,516✔
245

246
            std::optional<uint64_t> elapsed_time() const {
2,516✔
247
               if(m_ns_taken == 0) {
2,516✔
248
                  return std::nullopt;
249
               } else {
250
                  return m_ns_taken;
1,473✔
251
               }
252
            }
253

254
            // Nanoseconds since epoch
255
            uint64_t timestamp() const { return m_timestamp; }
2,516✔
256

257
            std::string result_string() const;
258

259
            static Result Failure(std::string_view who, std::string_view what) {
1✔
260
               Result r(who);
1✔
261
               r.test_failure(what);
1✔
262
               return r;
1✔
263
            }
×
264

265
            static Result Note(std::string_view who, std::string_view what) {
×
266
               Result r(who);
×
267
               r.test_note(what);
×
268
               return r;
×
269
            }
×
270

271
            void merge(const Result& other, bool ignore_test_name = false);
272

273
            void test_note(std::string_view note, const char* extra = nullptr);
274

275
            void test_note(std::string_view note, std::span<const uint8_t> context);
276

277
            void note_missing(std::string_view whatever);
278

279
            bool test_success(std::string_view note = "");
280

281
            bool test_failure(std::string_view err);
282

283
            bool test_failure(std::string_view what, std::string_view error);
284

285
            void test_failure(std::string_view what, const uint8_t buf[], size_t buf_len);
286

287
            void test_failure(std::string_view what, std::span<const uint8_t> context);
288

289
            /**
290
             * Require a condition, throw Test_Aborted otherwise
291
             * Note: works best when combined with CHECK scopes!
292
             */
293
            void require(std::string_view what, bool expr, bool expected = true);
294

295
            template <typename T>
296
            bool test_is_eq(const T& produced, const T& expected) {
42✔
297
               return test_is_eq("comparison", produced, expected);
39✔
298
            }
299

300
            template <typename T>
301
            bool test_is_eq(std::string_view what, const T& produced, const T& expected) {
334,186✔
302
               std::ostringstream out;
334,186✔
303
               out << m_who << " " << what;
334,186✔
304

305
               if(produced == expected) {
257,394✔
306
                  out << " produced expected result";
334,185✔
307
                  return test_success(out.str());
334,185✔
308
               } else {
309
                  out << " produced unexpected result '" << to_string(produced) << "' expected '" << to_string(expected)
310
                      << "'";
4✔
311
                  return test_failure(out.str());
1✔
312
               }
313
            }
334,186✔
314

315
            template <typename T>
316
            bool test_not_null(std::string_view what, const T& ptr) {
1,736✔
317
               if(ptr == nullptr) {
1,736✔
318
                  return test_failure(what, "was null");
×
319
               } else {
320
                  return test_success("not null");
1,736✔
321
               }
322
            }
323

324
            template <typename T>
325
            bool test_not_nullopt(std::string_view what, const std::optional<T>& val) {
10✔
326
               if(val == std::nullopt) {
10✔
327
                  return test_failure(what, "was nullopt");
×
328
               } else {
329
                  return test_success("not nullopt");
10✔
330
               }
331
            }
332

333
            bool test_eq(std::string_view what, const char* produced, const char* expected);
334

335
            bool test_is_nonempty(std::string_view what_is_it, std::string_view to_examine);
336

337
            bool test_eq(std::string_view what, std::string_view produced, std::string_view expected);
338

339
            /* Test predicates on bool */
340
            bool test_bool_eq(std::string_view what, bool produced, bool expected);
341

342
            bool test_is_false(std::string_view what, bool produced);
343

344
            bool test_is_true(std::string_view what, bool produced);
345

346
            /* Test predicates on size_t */
347
            bool test_sz_eq(std::string_view what, size_t produced, size_t expected);
348
            bool test_sz_ne(std::string_view what, size_t produced, size_t expected);
349
            bool test_sz_lt(std::string_view what, size_t produced, size_t expected);
350
            bool test_sz_lte(std::string_view what, size_t produced, size_t expected);
351
            bool test_sz_gt(std::string_view what, size_t produced, size_t expected);
352
            bool test_sz_gte(std::string_view what, size_t produced, size_t expected);
353

354
            /* Type-hinted unsigned integer equality predicates */
355
            bool test_u8_eq(uint8_t produced, uint8_t expected);
356
            bool test_u8_eq(std::string_view what, uint8_t produced, uint8_t expected);
357

358
            bool test_u16_eq(uint16_t produced, uint16_t expected);
359
            bool test_u16_eq(std::string_view what, uint16_t produced, uint16_t expected);
360

361
            bool test_u32_eq(uint32_t produced, uint32_t expected);
362
            bool test_u32_eq(std::string_view what, uint32_t produced, uint32_t expected);
363

364
            bool test_u64_eq(uint64_t produced, uint64_t expected);
365
            bool test_u64_eq(std::string_view what, uint64_t produced, uint64_t expected);
366

367
            /* Test predicates on integer return codes */
368
            bool test_rc_ok(std::string_view func, int rc);
369
            bool test_rc_fail(std::string_view func, std::string_view why, int rc);
370
            bool test_rc(std::string_view func, int expected, int rc);
371
            bool test_rc_init(std::string_view func, int rc);
372

373
            bool test_ne(std::string_view what, std::string_view str1, std::string_view str2);
374

375
            template <typename T>
376
            bool test_eq(std::string_view what, const std::optional<T>& a, const std::optional<T>& b) {
1,215✔
377
               if(a.has_value() != b.has_value()) {
1,215✔
378
                  std::ostringstream err;
×
379
                  err << m_who << " " << what << " only one of a/b was nullopt";
×
380
                  return test_failure(err.str());
×
381
               } else if(a.has_value() && b.has_value()) {
1,215✔
382
                  return test_is_eq(what, a.value(), b.value());
10✔
383
               } else {
384
                  // both nullopt
385
                  return test_success();
1,205✔
386
               }
387
            }
388

389
#if defined(BOTAN_HAS_BIGINT)
390
            bool test_eq(std::string_view what, const BigInt& produced, const BigInt& expected);
391
            bool test_ne(std::string_view what, const BigInt& produced, const BigInt& expected);
392
#endif
393

394
#if defined(BOTAN_HAS_LEGACY_EC_POINT)
395
            bool test_eq(std::string_view what, const Botan::EC_Point& a, const Botan::EC_Point& b);
396
#endif
397

398
            bool test_eq(const char* producer,
399
                         std::string_view what,
400
                         const uint8_t produced[],
401
                         size_t produced_size,
402
                         const uint8_t expected[],
403
                         size_t expected_size);
404

405
            bool test_ne(std::string_view what,
406
                         const uint8_t produced[],
407
                         size_t produced_len,
408
                         const uint8_t expected[],
409
                         size_t expected_len);
410

411
            bool test_eq(std::string_view what, std::span<const uint8_t> produced, std::span<const uint8_t> expected) {
131,395✔
412
               return test_eq(nullptr, what, produced.data(), produced.size(), expected.data(), expected.size());
131,264✔
413
            }
414

415
            bool test_eq(std::string_view producer,
115,192✔
416
                         std::string_view what,
417
                         std::span<const uint8_t> produced,
418
                         std::span<const uint8_t> expected) {
419
               return test_eq(std::string(producer).c_str(),
115,192✔
420
                              what,
421
                              produced.data(),
422
                              produced.size(),
423
                              expected.data(),
424
                              expected.size());
115,192✔
425
            }
230,384✔
426

427
            bool test_eq(std::string_view what, std::span<const uint8_t> produced, const char* expected_hex);
428

429
            template <std::size_t N>
430
            bool test_eq(std::string_view what,
2,440✔
431
                         const std::array<uint8_t, N>& produced,
432
                         const std::array<uint8_t, N>& expected) {
433
               return test_eq(nullptr, what, produced.data(), produced.size(), expected.data(), expected.size());
1,163✔
434
            }
435

436
            bool test_ne(std::string_view what, std::span<const uint8_t> produced, std::span<const uint8_t> expected) {
3,404✔
437
               return test_ne(what, produced.data(), produced.size(), expected.data(), expected.size());
3,404✔
438
            }
439

440
         private:
441
            class ThrowExpectations {
442
               public:
443
                  explicit ThrowExpectations(std::function<void()> fn) : m_fn(std::move(fn)) {}
73,518✔
444

445
                  ThrowExpectations(const ThrowExpectations&) = delete;
446
                  ThrowExpectations& operator=(const ThrowExpectations&) = delete;
447
                  ThrowExpectations(ThrowExpectations&&) = default;
448
                  ThrowExpectations& operator=(ThrowExpectations&&) = default;
449

450
                  ~ThrowExpectations();
451

452
                  ThrowExpectations& expect_success();
453

454
                  ThrowExpectations& expect_message(std::string_view message);
455

456
                  template <typename ExT>
457
                  ThrowExpectations& expect_exception_type() {
62,048✔
458
                     assert_that_success_is_not_expected();
62,048✔
459

460
                     m_expected_exception_check_fn = [](const std::exception_ptr& e) {
124,096✔
461
                        try {
462
                           if(e) {
62,048✔
463
                              std::rethrow_exception(e);
124,096✔
464
                           }
465
                        } catch(const ExT&) {
62,048✔
466
                           return true;
467
                        } catch(...) {
2✔
468
                           return false;
469
                        }
470
                        return false;
471
                     };
472
                     return *this;
473
                  }
474

475
                  bool check(std::string_view test_name, Test::Result& result);
476

477
               private:
478
                  void assert_that_success_is_not_expected() const;
479

480
                  std::function<void()> m_fn;
481
                  std::optional<std::string> m_expected_message;
482
                  std::function<bool(std::exception_ptr)> m_expected_exception_check_fn;
483
                  bool m_expect_success = false;
484
                  bool m_consumed = false;
485
            };
486

487
         public:
488
            bool test_throws(std::string_view what, std::function<void()> fn);
489

490
            bool test_throws(std::string_view what, std::string_view expected, std::function<void()> fn);
491

492
            bool test_no_throw(std::string_view what, std::function<void()> fn);
493

494
            template <typename ExceptionT>
495
            bool test_throws(std::string_view what, std::function<void()> fn) {
62,033✔
496
               return ThrowExpectations(std::move(fn)).expect_exception_type<ExceptionT>().check(what, *this);
186,099✔
497
            }
498

499
            template <typename ExceptionT>
500
            bool test_throws(std::string_view what, std::string_view expected, std::function<void()> fn) {
15✔
501
               // clang-format off
502
               return ThrowExpectations(std::move(fn)).expect_exception_type<ExceptionT>().expect_message(expected).check(what, *this);
45✔
503
               // clang-format on
504
            }
505

506
            void set_ns_consumed(uint64_t ns) { m_ns_taken = ns; }
48,251✔
507

508
            void start_timer();
509
            void end_timer();
510

511
            void set_code_location(CodeLocation where) { m_where = where; }
50,692✔
512

513
            const std::optional<CodeLocation>& code_location() const { return m_where; }
53,208✔
514

515
         private:
516
            template <typename T>
517
            std::string to_string(const T& v) {
2✔
518
               if constexpr(detail::is_optional_v<T>) {
519
                  return (v.has_value()) ? to_string(v.value()) : std::string("std::nullopt");
×
520
               } else if constexpr(detail::has_ostream_operator<T>) {
521
                  std::ostringstream oss;
2✔
522
                  oss << v;
2✔
523
                  return oss.str();
2✔
524
               } else if constexpr(detail::has_std_to_string<T>) {
2✔
525
                  //static_assert(false, "no std::to_string for you");
526
                  return std::to_string(v);
×
527
               } else {
528
                  //static_assert(false, "unknown type");
529
                  return "<?>";
×
530
               }
531
            }
532

533
         private:
534
            std::string m_who;
535
            std::optional<CodeLocation> m_where;
536
            uint64_t m_timestamp;
537
            uint64_t m_started = 0;
538
            uint64_t m_ns_taken = 0;
539
            size_t m_tests_passed = 0;
540
            std::vector<std::string> m_fail_log;
541
            std::vector<std::string> m_log;
542
      };
543

544
      virtual ~Test();
545

546
      Test();
547
      Test(const Test& other) = delete;
548
      Test(Test&& other) = default;
549
      Test& operator=(const Test& other) = delete;
550
      Test& operator=(Test&& other) = delete;
551

552
      virtual std::vector<Test::Result> run() = 0;
553

554
      virtual std::vector<std::string> possible_providers(const std::string& alg);
555

556
      void initialize(std::string test_name, CodeLocation location);
557

558
      const std::string& test_name() const { return m_test_name; }
23✔
559

560
      Botan::RandomNumberGenerator& rng() const;
561

562
      /**
563
       * Use this if a test needs some supported EC group but it is not relevant
564
       * which one exactly. This tries to find a commonly used group that is
565
       * both supported in this build and as small as possible (for test speed).
566
       *
567
       * If @p preferred_groups is non-empty, a group from that list is chosen
568
       *
569
       * @returns the name of a supported EC group, or std::nullopt if no
570
       *          supported EC group could be found for this build
571
       */
572
      static std::optional<std::string> supported_ec_group_name(std::vector<std::string> preferred_groups = {});
573

574
      const std::optional<CodeLocation>& registration_location() const { return m_registration_location; }
50,692✔
575

576
      /// @p smoke_test are run first in an unfiltered test run
577
      static void register_test(const std::string& category,
578
                                const std::string& name,
579
                                bool smoke_test,
580
                                bool needs_serialization,
581
                                std::function<std::unique_ptr<Test>()> maker_fn);
582

583
      static std::vector<std::string> registered_tests();
584
      static std::vector<std::string> registered_test_categories();
585

586
      static std::vector<std::string> filter_registered_tests(const std::vector<std::string>& requested,
587
                                                              const std::vector<std::string>& to_be_skipped);
588

589
      static std::unique_ptr<Test> get_test(const std::string& test_name);
590
      static bool test_needs_serialization(const std::string& test_name);
591

592
      static std::string data_dir(const std::string& subdir);
593
      static std::vector<std::string> files_in_data_dir(const std::string& subdir);
594
      static std::string data_file(const std::string& file);
595
      static std::string data_file_as_temporary_copy(const std::string& what);
596

597
      static std::string format_time(uint64_t nanoseconds);
598

599
      static std::vector<uint8_t> mutate_vec(const std::vector<uint8_t>& v,
600
                                             Botan::RandomNumberGenerator& rng,
601
                                             bool maybe_resize = false,
602
                                             size_t min_offset = 0);
603

604
      static void set_test_options(const Test_Options& opts);
605

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

608
      static const Test_Options& options() { return m_opts; }
609

610
      static bool run_long_tests() { return options().run_long_tests(); }
2,871✔
611

612
      static bool run_memory_intensive_tests() { return options().run_memory_intensive_tests(); }
1✔
613

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

616
      static std::string temp_file_name(const std::string& basename);
617
      static bool copy_file(const std::string& from, const std::string& to);
618

619
      static std::vector<std::string> provider_filter(const std::vector<std::string>& providers);
620

621
      static std::string read_data_file(const std::string& path);
622
      static std::vector<uint8_t> read_binary_data_file(const std::string& path);
623

624
      static std::unique_ptr<Botan::RandomNumberGenerator> new_rng(std::string_view test_name);
625
      static std::shared_ptr<Botan::RandomNumberGenerator> new_shared_rng(std::string_view test_name);
626

627
      static std::string random_password(Botan::RandomNumberGenerator& rng);
628
      static size_t random_index(Botan::RandomNumberGenerator& rng, size_t max);
629
      static uint64_t timestamp();  // nanoseconds arbitrary epoch
630

631
      static std::vector<Test::Result> flatten_result_lists(std::vector<std::vector<Test::Result>> result_lists);
632

633
   private:
634
      static Test_Options m_opts;
635
      static std::string m_test_rng_seed;
636

637
      /// The string ID that was used to register this test
638
      std::string m_test_name;
639
      /// The source file location where the test was registered
640
      std::optional<CodeLocation> m_registration_location;
641
      /// The test-specific RNG state
642
      mutable std::unique_ptr<Botan::RandomNumberGenerator> m_test_rng;
643
};
644

645
/*
646
* Register the test with the runner
647
*/
648
template <typename Test_Class>
649
class TestClassRegistration {
650
   public:
651
      TestClassRegistration(const std::string& category,
385✔
652
                            const std::string& name,
653
                            bool smoke_test,
654
                            bool needs_serialization,
655
                            const CodeLocation& registration_location) {
656
         Test::register_test(category, name, smoke_test, needs_serialization, [=] {
1,878✔
657
            auto test = std::make_unique<Test_Class>();
385✔
658
            test->initialize(name, registration_location);
770✔
659
            return test;
385✔
660
         });
×
661
      }
385✔
662
};
663

664
// NOLINTBEGIN(*-macro-usage)
665

666
#define BOTAN_REGISTER_TEST(category, name, Test_Class) \
667
   /* NOLINTNEXTLINE(cert-err58-cpp) */                 \
668
   const TestClassRegistration<Test_Class> reg_##Test_Class##_tests(category, name, false, false, {__FILE__, __LINE__})
669
#define BOTAN_REGISTER_SERIALIZED_TEST(category, name, Test_Class) \
670
   /* NOLINTNEXTLINE(cert-err58-cpp) */                            \
671
   const TestClassRegistration<Test_Class> reg_##Test_Class##_tests(category, name, false, true, {__FILE__, __LINE__})
672
#define BOTAN_REGISTER_SMOKE_TEST(category, name, Test_Class) \
673
   /* NOLINTNEXTLINE(cert-err58-cpp) */                       \
674
   const TestClassRegistration<Test_Class> reg_##Test_Class##_tests(category, name, true, false, {__FILE__, __LINE__})
675
#define BOTAN_REGISTER_SERIALIZED_SMOKE_TEST(category, name, Test_Class) \
676
   /* NOLINTNEXTLINE(cert-err58-cpp) */                                  \
677
   const TestClassRegistration<Test_Class> reg_##Test_Class##_tests(category, name, true, true, {__FILE__, __LINE__})
678

679
// NOLINTEND(*-macro-usage)
680

681
typedef Test::Result (*test_fn)();
682
typedef std::vector<Test::Result> (*test_fn_vec)();
683

684
class FnTest : public Test {
685
   private:
686
      using TestFnVariant = std::variant<test_fn, test_fn_vec>;
687

688
      template <typename TestFn>
689
      std::vector<TestFnVariant> make_variant_vector(TestFn fn) {
34✔
690
         using T = std::decay_t<decltype(fn)>;
691
         static_assert(std::is_same_v<T, test_fn> || std::is_same_v<T, test_fn_vec>,
692
                       "functions passed to BOTAN_REGISTER_TEST_FN must either return a "
693
                       "single Test::Result or a std::vector of Test::Result");
694
         return {fn};
34✔
695
      }
696

697
      template <typename TestFn, typename... TestFns>
698
      std::vector<TestFnVariant> make_variant_vector(const TestFn& fn, const TestFns&... fns) {
31✔
699
         auto functions = make_variant_vector(fns...);
31✔
700
         functions.emplace_back(fn);
31✔
701
         return functions;
31✔
702
      }
×
703

704
   public:
705
      template <typename... TestFns>
706
      explicit FnTest(TestFns... fns) : m_fns(make_variant_vector(fns...)) {}
34✔
707

708
      std::vector<Test::Result> run() override {
34✔
709
         std::vector<Test::Result> result;
34✔
710

711
         // TODO(Botan4) use std::ranges::reverse_view here once available (need newer Clang)
712
         // NOLINTNEXTLINE(modernize-loop-convert)
713
         for(auto fn_variant = m_fns.crbegin(); fn_variant != m_fns.crend(); ++fn_variant) {
99✔
714
            std::visit(
65✔
715
               [&](auto&& fn) {
130✔
716
                  using T = std::decay_t<decltype(fn)>;
717
                  if constexpr(std::is_same_v<T, test_fn>) {
718
                     result.emplace_back(fn());
7✔
719
                  } else {
720
                     const auto results = fn();
58✔
721
                     result.insert(result.end(), results.begin(), results.end());
58✔
722
                  }
58✔
723
               },
65✔
724
               *fn_variant);
65✔
725
         }
726

727
         return result;
34✔
728
      }
×
729

730
   private:
731
      std::vector<TestFnVariant> m_fns;
732
};
733

734
class TestFnRegistration {
735
   public:
736
      template <typename... TestFns>
737
      TestFnRegistration(const std::string& category,
34✔
738
                         const std::string& name,
739
                         bool smoke_test,
740
                         bool needs_serialization,
741
                         const CodeLocation& registration_location,
742
                         TestFns... fn) {
743
         Test::register_test(category, name, smoke_test, needs_serialization, [=] {
125✔
744
            auto test = std::make_unique<FnTest>(fn...);
34✔
745
            test->initialize(name, registration_location);
68✔
746
            return test;
34✔
747
         });
×
748
      }
34✔
749
};
750

751
// NOLINTBEGIN(*-macro-usage)
752

753
#define BOTAN_REGISTER_TEST_FN_IMPL(category, name, smoke_test, needs_serialization, fn0, ...) \
754
   /* NOLINTNEXTLINE(cert-err58-cpp) */                                                        \
755
   static const TestFnRegistration register_##fn0(                                             \
756
      category, name, smoke_test, needs_serialization, {__FILE__, __LINE__}, fn0 __VA_OPT__(, ) __VA_ARGS__)
757

758
#define BOTAN_REGISTER_TEST_FN(category, name, ...) \
759
   BOTAN_REGISTER_TEST_FN_IMPL(category, name, false, false, __VA_ARGS__)
760
#define BOTAN_REGISTER_SMOKE_TEST_FN(category, name, ...) \
761
   BOTAN_REGISTER_TEST_FN_IMPL(category, name, true, false, __VA_ARGS__)
762
#define BOTAN_REGISTER_SERIALIZED_TEST_FN(category, name, ...) \
763
   BOTAN_REGISTER_TEST_FN_IMPL(category, name, false, true, __VA_ARGS__)
764
#define BOTAN_REGISTER_SERIALIZED_SMOKE_TEST_FN(category, name, ...) \
765
   BOTAN_REGISTER_TEST_FN_IMPL(category, name, true, true, __VA_ARGS__)
766

767
// NOLINTEND(*-macro-usage)
768

769
class VarMap {
181✔
770
   public:
771
      bool has_key(const std::string& key) const;
772

773
      bool get_req_bool(const std::string& key) const;
774

775
      std::vector<uint8_t> get_req_bin(const std::string& key) const;
776
      std::vector<uint8_t> get_opt_bin(const std::string& key) const;
777

778
      std::vector<std::vector<uint8_t>> get_req_bin_list(const std::string& key) const;
779

780
#if defined(BOTAN_HAS_BIGINT)
781
      Botan::BigInt get_req_bn(const std::string& key) const;
782
      Botan::BigInt get_opt_bn(const std::string& key, const Botan::BigInt& def_value) const;
783
#endif
784

785
      std::string get_req_str(const std::string& key) const;
786
      std::string get_opt_str(const std::string& key, const std::string& def_value) const;
787

788
      size_t get_req_sz(const std::string& key) const;
789

790
      uint8_t get_req_u8(const std::string& key) const;
791
      uint32_t get_req_u32(const std::string& key) const;
792
      uint64_t get_req_u64(const std::string& key) const;
793

794
      size_t get_opt_sz(const std::string& key, size_t def_value) const;
795

796
      uint64_t get_opt_u64(const std::string& key, uint64_t def_value) const;
797

798
      void clear();
799

800
      void add(const std::string& key, const std::string& value);
801

802
   private:
803
      std::optional<std::string> get_var(const std::string& key) const;
804

805
      std::vector<std::pair<std::string, std::string>> m_vars;
806
};
807

808
/*
809
* A test based on reading an input file which contains key/value pairs
810
* Special note: the last value in required_key (there must be at least
811
* one), is the output key. This triggers the callback.
812
*
813
* Calls run_one_test with the variables set. If an ini-style [header]
814
* is used in the file, then header will be set to that value. This allows
815
* splitting up tests between [valid] and [invalid] tests, or different
816
* related algorithms tested in the same file. Use the get_XXX functions
817
* on VarMap to retrieve formatted values.
818
*
819
* If most of your tests are text-based but you find yourself with a few
820
* odds-and-ends tests that you want to do, override run_final_tests which
821
* can test whatever it likes and returns a vector of Results.
822
*/
823
class Text_Based_Test : public Test {
824
   public:
825
      Text_Based_Test(const std::string& data_src,
826
                      const std::string& required_keys_str,
827
                      const std::string& optional_keys_str = "");
828

829
      Text_Based_Test(const Text_Based_Test& other) = delete;
830
      Text_Based_Test(Text_Based_Test&& other) = default;
831
      Text_Based_Test& operator=(const Text_Based_Test& other) = delete;
832
      Text_Based_Test& operator=(Text_Based_Test&& other) = delete;
833

834
      ~Text_Based_Test() override;
835

836
      virtual bool clear_between_callbacks() const { return true; }
33,476✔
837

838
      std::vector<Test::Result> run() override;
839

840
   private:
841
      virtual Test::Result run_one_test(const std::string& header, const VarMap& vars) = 0;
842
      // Called before run_one_test
843
      virtual bool skip_this_test(const std::string& header, const VarMap& vars);
844

845
      virtual std::vector<Test::Result> run_final_tests() { return std::vector<Test::Result>(); }
172✔
846

847
   private:
848
      class Text_Based_Test_Data;
849
      std::unique_ptr<Text_Based_Test_Data> m_data;
850
};
851

852
/**
853
 * This is a convenience wrapper to write small self-contained and in particular
854
 * exception-safe unit tests. If some (unexpected) exception is thrown in one of
855
 * the CHECK-scopes, it will fail the particular test gracefully with a human-
856
 * understandable failure output.
857
 *
858
 * Example Usage:
859
 *
860
 * ```
861
 * std::vector<Test::Result> test_something()
862
 *    {
863
 *    return
864
 *       {
865
 *       CHECK("some unit test name", [](Test::Result& r)
866
 *          {
867
 *          r.test_is_true("some observation", 1+1 == 2);
868
 *          }),
869
 *       CHECK("some other unit test name", [](Test::Result& r)
870
 *          {
871
 *          // ...
872
 *          })
873
 *       };
874
 *    }
875
 *
876
 * BOTAN_REGISTER_TEST_FN("some_category", "some_test_name", test_something);
877
 * ```
878
 */
879
template <typename FunT>
880
Test::Result CHECK(const char* name, FunT check_fun) {
508✔
881
   Botan_Tests::Test::Result r(name);
508✔
882

883
   try {
884
      check_fun(r);
508✔
885
   } catch(const Botan_Tests::Test_Aborted&) {
×
886
      // pass, failure was already noted in the responsible `require`
887
   } catch(const std::exception& ex) {
×
888
      r.test_failure("failed unexpectedly", ex.what());
×
889
   } catch(...) {
×
890
      r.test_failure("failed with unknown exception");
×
891
   }
892

893
   return r;
507✔
894
}
×
895

896
}  // namespace Botan_Tests
897

898
#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