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

randombit / botan / 22090616258

17 Feb 2026 08:07AM UTC coverage: 90.026% (-0.002%) from 90.028%
22090616258

Pull #5352

github

web-flow
Merge 27083c141 into 7b1bee298
Pull Request #5352: Change VarMap to avoid copies internally

102334 of 113671 relevant lines covered (90.03%)

11388428.89 hits per line

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

86.58
/src/tests/tests.h
1
/*
2
* (C) 2014,2015,2026 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 <memory>
20
#include <optional>
21
#include <span>
22
#include <stdexcept>
23
#include <string>
24
#include <string_view>
25
#include <variant>
26
#include <vector>
27

28
namespace Botan {
29

30
class RandomNumberGenerator;
31

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

36
}  // namespace Botan
37

38
namespace Botan_Tests {
39

40
#if defined(BOTAN_HAS_BIGINT)
41
using Botan::BigInt;
42
#endif
43

44
class Test_Error : public std::runtime_error {
45
   public:
46
      explicit Test_Error(std::string_view what);
47
};
48

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

54
class Test_Options {
55
   public:
56
      Test_Options() = default;
57

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

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

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

107
      const std::string& data_dir() const { return m_data_dir; }
108

109
      const std::string& pkcs11_lib() const { return m_pkcs11_lib; }
3✔
110

111
      const std::string& provider() const { return m_provider; }
2✔
112

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

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

117
      uint32_t tpm2_persistent_rsa_handle() const { return static_cast<uint32_t>(m_tpm2_persistent_rsa_handle); }
2✔
118

119
      uint32_t tpm2_persistent_ecc_handle() const { return static_cast<uint32_t>(m_tpm2_persistent_ecc_handle); }
2✔
120

121
      const std::string& tpm2_persistent_auth_value() const { return m_tpm2_persistent_auth_value; }
122

123
      const std::string& drbg_seed() const { return m_drbg_seed; }
1✔
124

125
      const std::string& xml_results_dir() const { return m_xml_results_dir; }
1✔
126

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

129
      size_t test_runs() const { return m_test_runs; }
4✔
130

131
      size_t test_threads() const { return m_test_threads; }
3✔
132

133
      bool log_success() const { return m_log_success; }
3,620,844✔
134

135
      bool run_online_tests() const { return m_run_online_tests; }
2✔
136

137
      bool run_long_tests() const { return m_run_long_tests; }
2,871✔
138

139
      bool run_memory_intensive_tests() const { return m_run_memory_intensive_tests; }
1✔
140

141
      bool abort_on_first_fail() const { return m_abort_on_first_fail; }
24✔
142

143
      bool no_stdout() const { return m_no_stdout; }
1✔
144

145
      bool verbose() const { return m_verbose; }
2,557✔
146

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

172
/**
173
 * A code location consisting of the source file path and a line
174
 */
175
struct CodeLocation {
176
      const char* path;
177
      unsigned int line;
178
};
179

180
/*
181
* A generic test which returns a set of results when run.
182
* The tests may not all have the same type (for example test
183
* "block" returns results for "AES-128" and "AES-256").
184
*
185
* For most test cases you want Text_Based_Test derived below
186
*/
187
class Test {
188
   public:
189
      /*
190
      * Some number of test results, all associated with who()
191
      */
192
      class Result final {
193
         public:
194
            explicit Result(std::string_view who);
195

196
            /**
197
             * This 'consolidation constructor' creates a single test result from
198
             * a vector of downstream test result objects.
199
             */
200
            Result(std::string_view who, const std::vector<Result>& downstream_results);
201

202
            size_t tests_passed() const { return m_tests_passed; }
12,668✔
203

204
            size_t tests_failed() const { return m_fail_log.size(); }
69,004✔
205

206
            size_t tests_run() const { return tests_passed() + tests_failed(); }
12,668✔
207

208
            bool any_results() const { return tests_run() > 0; }
209

210
            const std::string& who() const { return m_who; }
236,130✔
211

212
            const std::vector<std::string>& failures() const { return m_fail_log; }
2,532✔
213

214
            const std::vector<std::string>& notes() const { return m_log; }
2,532✔
215

216
            std::optional<uint64_t> elapsed_time() const {
2,532✔
217
               if(m_ns_taken == 0) {
2,532✔
218
                  return std::nullopt;
219
               } else {
220
                  return m_ns_taken;
1,473✔
221
               }
222
            }
223

224
            // Nanoseconds since epoch
225
            uint64_t timestamp() const { return m_timestamp; }
2,532✔
226

227
            std::string result_string() const;
228

229
            static Result Failure(std::string_view who, std::string_view what) {
1✔
230
               Result r(who);
1✔
231
               r.test_failure(what);
1✔
232
               return r;
1✔
233
            }
×
234

235
            static Result Note(std::string_view who, std::string_view what) {
×
236
               Result r(who);
×
237
               r.test_note(what);
×
238
               return r;
×
239
            }
×
240

241
            void merge(const Result& other, bool ignore_test_name = false);
242

243
            /* Test reporting functions */
244

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

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

249
            void note_missing(std::string_view whatever);
250

251
            bool test_success(std::string_view note = "");
252

253
            bool test_failure(std::string err);
254

255
            bool test_failure(std::string_view err);
256

257
            bool test_failure(const char* err);
258

259
            bool test_failure(std::string_view what, std::string_view error);
260

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

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

265
            /**
266
             * Require a condition, throw Test_Aborted otherwise
267
             * Note: works best when combined with CHECK scopes!
268
             */
269
            void require(std::string_view what, bool expr);
270

271
            /* Generic arbitrary equality check */
272

273
            template <typename E>
274
               requires std::is_enum_v<E>
275
            bool test_enum_eq(std::string_view what, const E& produced, const E& expected) {
61✔
276
               auto produced_v = static_cast<std::underlying_type_t<E>>(produced);
61✔
277
               auto expected_v = static_cast<std::underlying_type_t<E>>(expected);
61✔
278
               return test_u64_eq(what, produced_v, expected_v);
59✔
279
            }
280

281
            template <typename T>
282
            bool test_not_null(std::string_view what, const T& ptr) {
2,873✔
283
               if(ptr == nullptr) {
2,873✔
284
                  return test_failure(what, "was null");
×
285
               } else {
286
                  return test_success("not null");
2,873✔
287
               }
288
            }
289

290
            /* String comparison predicates */
291
            bool test_str_not_empty(std::string_view what, std::string_view produced);
292

293
            bool test_str_eq(std::string_view what, std::string_view produced, std::string_view expected);
294

295
            bool test_str_ne(std::string_view what, std::string_view produced, std::string_view expected);
296

297
            /* Test predicates on bool */
298
            bool test_bool_eq(std::string_view what, bool produced, bool expected);
299

300
            bool test_is_false(std::string_view what, bool produced);
301

302
            bool test_is_true(std::string_view what, bool produced);
303

304
            /* Test predicates on size_t */
305
            bool test_sz_eq(std::string_view what, size_t produced, size_t expected);
306
            bool test_sz_ne(std::string_view what, size_t produced, size_t expected);
307
            bool test_sz_lt(std::string_view what, size_t produced, size_t expected);
308
            bool test_sz_lte(std::string_view what, size_t produced, size_t expected);
309
            bool test_sz_gt(std::string_view what, size_t produced, size_t expected);
310
            bool test_sz_gte(std::string_view what, size_t produced, size_t expected);
311

312
            /* Type-hinted unsigned integer equality predicates */
313
            bool test_u8_eq(std::string_view what, uint8_t produced, uint8_t expected);
314
            bool test_u16_eq(std::string_view what, uint16_t produced, uint16_t expected);
315
            bool test_u32_eq(std::string_view what, uint32_t produced, uint32_t expected);
316
            bool test_u64_eq(std::string_view what, uint64_t produced, uint64_t expected);
317
            bool test_i16_eq(std::string_view what, int16_t produced, int16_t expected);
318
            bool test_i32_eq(std::string_view what, int32_t produced, int32_t expected);
319

320
            /* Prefer the versions that take a descriptor string above */
321
            bool test_u8_eq(uint8_t produced, uint8_t expected);
322
            bool test_u16_eq(uint16_t produced, uint16_t expected);
323
            bool test_u32_eq(uint32_t produced, uint32_t expected);
324
            bool test_u64_eq(uint64_t produced, uint64_t expected);
325

326
            /* Test predicates on integer return codes */
327
            bool test_rc_ok(std::string_view func, int rc);
328
            bool test_rc_fail(std::string_view func, std::string_view why, int rc);
329
            bool test_rc(std::string_view func, int rc, int expected);
330
            bool test_rc_init(std::string_view func, int rc);
331

332
            /* Test predicates on optional values */
333

334
            template <typename T>
335
            bool test_opt_not_null(std::string_view what, const std::optional<T>& val) {
10✔
336
               if(val == std::nullopt) {
10✔
337
                  return test_failure(what, "was nullopt");
×
338
               } else {
339
                  return test_success("not nullopt");
10✔
340
               }
341
            }
342

343
            template <typename T>
344
            bool test_opt_is_null(std::string_view what, const std::optional<T>& val) {
5✔
345
               if(val == std::nullopt) {
5✔
346
                  return test_success("was nullopt");
5✔
347
               } else {
348
                  return test_failure(what, "not nullopt");
×
349
               }
350
            }
351

352
            bool test_opt_u8_eq(std::string_view what,
353
                                std::optional<uint8_t> produced,
354
                                std::optional<uint8_t> expected);
355

356
            bool test_opt_u64_eq(std::string_view what,
357
                                 std::optional<uint64_t> produced,
358
                                 std::optional<uint64_t> expected);
359

360
#if defined(BOTAN_HAS_BIGINT)
361
            /* Test predicates for BigInt */
362
            bool test_bn_eq(std::string_view what, const BigInt& produced, const BigInt& expected);
363
            bool test_bn_ne(std::string_view what, const BigInt& produced, const BigInt& expected);
364
#endif
365

366
            /* Test predicates for binary strings */
367
            bool test_bin_ne(std::string_view what,
368
                             std::span<const uint8_t> produced,
369
                             std::span<const uint8_t> expected);
370

371
            bool test_bin_eq(std::string_view what,
372
                             std::span<const uint8_t> produced,
373
                             std::span<const uint8_t> expected);
374

375
            bool test_bin_eq(std::string_view what, std::span<const uint8_t> produced, std::string_view expected_hex);
376

377
            template <std::size_t N>
378
            bool test_bin_eq(std::string_view what,
1,212✔
379
                             const std::array<uint8_t, N>& produced,
380
                             const std::array<uint8_t, N>& expected) {
381
               return test_bin_eq(what, std::span{produced}, std::span{expected});
1,186✔
382
            }
383

384
         private:
385
            class ThrowExpectations {
386
               public:
387
                  explicit ThrowExpectations(std::function<void()> fn) : m_fn(std::move(fn)) {}
73,518✔
388

389
                  ThrowExpectations(const ThrowExpectations&) = delete;
390
                  ThrowExpectations& operator=(const ThrowExpectations&) = delete;
391
                  ThrowExpectations(ThrowExpectations&&) = default;
392
                  ThrowExpectations& operator=(ThrowExpectations&&) = default;
393

394
                  ~ThrowExpectations();
395

396
                  ThrowExpectations& expect_success();
397

398
                  ThrowExpectations& expect_message(std::string_view message);
399

400
                  template <typename ExT>
401
                  ThrowExpectations& expect_exception_type() {
62,048✔
402
                     assert_that_success_is_not_expected();
62,048✔
403

404
                     m_expected_exception_check_fn = [](const std::exception_ptr& e) {
124,096✔
405
                        try {
406
                           if(e) {
62,048✔
407
                              std::rethrow_exception(e);
124,096✔
408
                           }
409
                        } catch(const ExT&) {
62,048✔
410
                           return true;
411
                        } catch(...) {
2✔
412
                           return false;
413
                        }
414
                        return false;
415
                     };
416
                     return *this;
417
                  }
418

419
                  bool check(std::string_view test_name, Test::Result& result);
420

421
               private:
422
                  void assert_that_success_is_not_expected() const;
423

424
                  std::function<void()> m_fn;
425
                  std::optional<std::string> m_expected_message;
426
                  std::function<bool(std::exception_ptr)> m_expected_exception_check_fn;
427
                  bool m_expect_success = false;
428
                  bool m_consumed = false;
429
            };
430

431
         public:
432
            bool test_throws(std::string_view what, std::function<void()> fn);
433

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

436
            bool test_no_throw(std::string_view what, std::function<void()> fn);
437

438
            template <typename ExceptionT>
439
            bool test_throws(std::string_view what, std::function<void()> fn) {
62,033✔
440
               return ThrowExpectations(std::move(fn)).expect_exception_type<ExceptionT>().check(what, *this);
186,099✔
441
            }
442

443
            template <typename ExceptionT>
444
            bool test_throws(std::string_view what, std::string_view expected, std::function<void()> fn) {
15✔
445
               // clang-format off
446
               return ThrowExpectations(std::move(fn)).expect_exception_type<ExceptionT>().expect_message(expected).check(what, *this);
45✔
447
               // clang-format on
448
            }
449

450
            void set_ns_consumed(uint64_t ns) { m_ns_taken = ns; }
48,267✔
451

452
            void start_timer();
453
            void end_timer();
454

455
            void set_code_location(CodeLocation where) { m_where = where; }
50,724✔
456

457
            const std::optional<CodeLocation>& code_location() const { return m_where; }
53,256✔
458

459
         private:
460
            std::string m_who;
461
            std::optional<CodeLocation> m_where;
462
            uint64_t m_timestamp;
463
            uint64_t m_started = 0;
464
            uint64_t m_ns_taken = 0;
465
            size_t m_tests_passed = 0;
466
            std::vector<std::string> m_fail_log;
467
            std::vector<std::string> m_log;
468
      };
469

470
      virtual ~Test();
471

472
      Test();
473
      Test(const Test& other) = delete;
474
      Test(Test&& other) = default;
475
      Test& operator=(const Test& other) = delete;
476
      Test& operator=(Test&& other) = delete;
477

478
      virtual std::vector<Test::Result> run() = 0;
479

480
      virtual std::vector<std::string> possible_providers(const std::string& alg);
481

482
      void initialize(std::string test_name, CodeLocation location);
483

484
      const std::string& test_name() const { return m_test_name; }
23✔
485

486
      Botan::RandomNumberGenerator& rng() const;
487

488
      /**
489
       * Use this if a test needs some supported EC group but it is not relevant
490
       * which one exactly. This tries to find a commonly used group that is
491
       * both supported in this build and as small as possible (for test speed).
492
       *
493
       * If @p preferred_groups is non-empty, a group from that list is chosen
494
       *
495
       * @returns the name of a supported EC group, or std::nullopt if no
496
       *          supported EC group could be found for this build
497
       */
498
      static std::optional<std::string> supported_ec_group_name(std::vector<std::string> preferred_groups = {});
499

500
      const std::optional<CodeLocation>& registration_location() const { return m_registration_location; }
50,724✔
501

502
      /// @p smoke_test are run first in an unfiltered test run
503
      static void register_test(const std::string& category,
504
                                const std::string& name,
505
                                bool smoke_test,
506
                                bool needs_serialization,
507
                                std::function<std::unique_ptr<Test>()> maker_fn);
508

509
      static std::vector<std::string> registered_tests();
510
      static std::vector<std::string> registered_test_categories();
511

512
      static std::vector<std::string> filter_registered_tests(const std::vector<std::string>& requested,
513
                                                              const std::vector<std::string>& to_be_skipped);
514

515
      static std::unique_ptr<Test> get_test(const std::string& test_name);
516
      static bool test_needs_serialization(const std::string& test_name);
517

518
      static std::string data_dir(const std::string& subdir);
519
      static std::vector<std::string> files_in_data_dir(const std::string& subdir);
520
      static std::string data_file(const std::string& file);
521
      static std::string data_file_as_temporary_copy(const std::string& what);
522

523
      static std::string format_time(uint64_t nanoseconds);
524

525
      static std::vector<uint8_t> mutate_vec(const std::vector<uint8_t>& v,
526
                                             Botan::RandomNumberGenerator& rng,
527
                                             bool maybe_resize = false,
528
                                             size_t min_offset = 0);
529

530
      static void set_test_options(const Test_Options& opts);
531

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

534
      static const Test_Options& options() { return m_opts; }
535

536
      static bool run_long_tests() { return options().run_long_tests(); }
2,871✔
537

538
      static bool run_memory_intensive_tests() { return options().run_memory_intensive_tests(); }
1✔
539

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

542
      static std::string temp_file_name(const std::string& basename);
543
      static bool copy_file(const std::string& from, const std::string& to);
544

545
      static std::vector<std::string> provider_filter(const std::vector<std::string>& providers);
546

547
      static std::string read_data_file(const std::string& path);
548
      static std::vector<uint8_t> read_binary_data_file(const std::string& path);
549

550
      static std::unique_ptr<Botan::RandomNumberGenerator> new_rng(std::string_view test_name);
551
      static std::shared_ptr<Botan::RandomNumberGenerator> new_shared_rng(std::string_view test_name);
552

553
      static std::string random_password(Botan::RandomNumberGenerator& rng);
554
      static size_t random_index(Botan::RandomNumberGenerator& rng, size_t max);
555
      static uint64_t timestamp();  // nanoseconds arbitrary epoch
556

557
      static std::vector<Test::Result> flatten_result_lists(std::vector<std::vector<Test::Result>> result_lists);
558

559
   private:
560
      static Test_Options m_opts;
561
      static std::string m_test_rng_seed;
562

563
      /// The string ID that was used to register this test
564
      std::string m_test_name;
565
      /// The source file location where the test was registered
566
      std::optional<CodeLocation> m_registration_location;
567
      /// The test-specific RNG state
568
      mutable std::unique_ptr<Botan::RandomNumberGenerator> m_test_rng;
569
};
570

571
/*
572
* Register the test with the runner
573
*/
574
template <typename Test_Class>
575
class TestClassRegistration {
576
   public:
577
      TestClassRegistration(const std::string& category,
386✔
578
                            const std::string& name,
579
                            bool smoke_test,
580
                            bool needs_serialization,
581
                            const CodeLocation& registration_location) {
582
         Test::register_test(category, name, smoke_test, needs_serialization, [=] {
1,884✔
583
            auto test = std::make_unique<Test_Class>();
386✔
584
            test->initialize(name, registration_location);
772✔
585
            return test;
386✔
586
         });
×
587
      }
386✔
588
};
589

590
// NOLINTBEGIN(*-macro-usage)
591

592
#define BOTAN_REGISTER_TEST(category, name, Test_Class) \
593
   /* NOLINTNEXTLINE(cert-err58-cpp) */                 \
594
   const TestClassRegistration<Test_Class> reg_##Test_Class##_tests(category, name, false, false, {__FILE__, __LINE__})
595
#define BOTAN_REGISTER_SERIALIZED_TEST(category, name, Test_Class) \
596
   /* NOLINTNEXTLINE(cert-err58-cpp) */                            \
597
   const TestClassRegistration<Test_Class> reg_##Test_Class##_tests(category, name, false, true, {__FILE__, __LINE__})
598
#define BOTAN_REGISTER_SMOKE_TEST(category, name, Test_Class) \
599
   /* NOLINTNEXTLINE(cert-err58-cpp) */                       \
600
   const TestClassRegistration<Test_Class> reg_##Test_Class##_tests(category, name, true, false, {__FILE__, __LINE__})
601
#define BOTAN_REGISTER_SERIALIZED_SMOKE_TEST(category, name, Test_Class) \
602
   /* NOLINTNEXTLINE(cert-err58-cpp) */                                  \
603
   const TestClassRegistration<Test_Class> reg_##Test_Class##_tests(category, name, true, true, {__FILE__, __LINE__})
604

605
// NOLINTEND(*-macro-usage)
606

607
typedef Test::Result (*test_fn)();
608
typedef std::vector<Test::Result> (*test_fn_vec)();
609

610
class FnTest : public Test {
611
   private:
612
      using TestFnVariant = std::variant<test_fn, test_fn_vec>;
613

614
      template <typename TestFn>
615
      std::vector<TestFnVariant> make_variant_vector(TestFn fn) {
34✔
616
         using T = std::decay_t<decltype(fn)>;
617
         static_assert(std::is_same_v<T, test_fn> || std::is_same_v<T, test_fn_vec>,
618
                       "functions passed to BOTAN_REGISTER_TEST_FN must either return a "
619
                       "single Test::Result or a std::vector of Test::Result");
620
         return {fn};
34✔
621
      }
622

623
      template <typename TestFn, typename... TestFns>
624
      std::vector<TestFnVariant> make_variant_vector(const TestFn& fn, const TestFns&... fns) {
31✔
625
         auto functions = make_variant_vector(fns...);
31✔
626
         functions.emplace_back(fn);
31✔
627
         return functions;
31✔
628
      }
×
629

630
   public:
631
      template <typename... TestFns>
632
      explicit FnTest(TestFns... fns) : m_fns(make_variant_vector(fns...)) {}
34✔
633

634
      std::vector<Test::Result> run() override {
34✔
635
         std::vector<Test::Result> result;
34✔
636

637
         // TODO(Botan4) use std::ranges::reverse_view here once available (need newer Clang)
638
         // NOLINTNEXTLINE(modernize-loop-convert)
639
         for(auto fn_variant = m_fns.crbegin(); fn_variant != m_fns.crend(); ++fn_variant) {
99✔
640
            std::visit(
65✔
641
               [&](auto&& fn) {
130✔
642
                  using T = std::decay_t<decltype(fn)>;
643
                  if constexpr(std::is_same_v<T, test_fn>) {
644
                     result.emplace_back(fn());
7✔
645
                  } else {
646
                     const auto results = fn();
58✔
647
                     result.insert(result.end(), results.begin(), results.end());
58✔
648
                  }
58✔
649
               },
65✔
650
               *fn_variant);
65✔
651
         }
652

653
         return result;
34✔
654
      }
×
655

656
   private:
657
      std::vector<TestFnVariant> m_fns;
658
};
659

660
class TestFnRegistration {
661
   public:
662
      template <typename... TestFns>
663
      TestFnRegistration(const std::string& category,
34✔
664
                         const std::string& name,
665
                         bool smoke_test,
666
                         bool needs_serialization,
667
                         const CodeLocation& registration_location,
668
                         TestFns... fn) {
669
         Test::register_test(category, name, smoke_test, needs_serialization, [=] {
125✔
670
            auto test = std::make_unique<FnTest>(fn...);
34✔
671
            test->initialize(name, registration_location);
68✔
672
            return test;
34✔
673
         });
×
674
      }
34✔
675
};
676

677
// NOLINTBEGIN(*-macro-usage)
678

679
#define BOTAN_REGISTER_TEST_FN_IMPL(category, name, smoke_test, needs_serialization, fn0, ...) \
680
   /* NOLINTNEXTLINE(cert-err58-cpp) */                                                        \
681
   static const TestFnRegistration register_##fn0(                                             \
682
      category, name, smoke_test, needs_serialization, {__FILE__, __LINE__}, fn0 __VA_OPT__(, ) __VA_ARGS__)
683

684
#define BOTAN_REGISTER_TEST_FN(category, name, ...) \
685
   BOTAN_REGISTER_TEST_FN_IMPL(category, name, false, false, __VA_ARGS__)
686
#define BOTAN_REGISTER_SMOKE_TEST_FN(category, name, ...) \
687
   BOTAN_REGISTER_TEST_FN_IMPL(category, name, true, false, __VA_ARGS__)
688
#define BOTAN_REGISTER_SERIALIZED_TEST_FN(category, name, ...) \
689
   BOTAN_REGISTER_TEST_FN_IMPL(category, name, false, true, __VA_ARGS__)
690
#define BOTAN_REGISTER_SERIALIZED_SMOKE_TEST_FN(category, name, ...) \
691
   BOTAN_REGISTER_TEST_FN_IMPL(category, name, true, true, __VA_ARGS__)
692

693
// NOLINTEND(*-macro-usage)
694

695
class VarMap {
181✔
696
   public:
697
      bool has_key(std::string_view key) const;
698

699
      bool get_req_bool(std::string_view key) const;
700

701
      std::vector<uint8_t> get_req_bin(std::string_view key) const;
702
      std::vector<uint8_t> get_opt_bin(std::string_view key) const;
703

704
      std::vector<std::vector<uint8_t>> get_req_bin_list(std::string_view key) const;
705

706
#if defined(BOTAN_HAS_BIGINT)
707
      Botan::BigInt get_req_bn(std::string_view key) const;
708
      Botan::BigInt get_opt_bn(std::string_view key, const Botan::BigInt& def_value) const;
709
#endif
710

711
      std::string get_req_str(std::string_view key) const;
712
      std::string get_opt_str(std::string_view key, std::string_view def_value) const;
713

714
      size_t get_req_sz(std::string_view key) const;
715

716
      uint8_t get_req_u8(std::string_view key) const;
717
      uint32_t get_req_u32(std::string_view key) const;
718
      uint64_t get_req_u64(std::string_view key) const;
719

720
      size_t get_opt_sz(std::string_view key, size_t def_value) const;
721

722
      uint64_t get_opt_u64(std::string_view key, uint64_t def_value) const;
723

724
      void clear();
725

726
      void add(std::string_view key, std::string_view value);
727

728
   private:
729
      const std::string& get_req_var(std::string_view key) const;
730
      std::optional<std::string> get_opt_var(std::string_view key) const;
731

732
      std::vector<std::pair<std::string, std::string>> m_vars;
733
};
734

735
/*
736
* A test based on reading an input file which contains key/value pairs
737
* Special note: the last value in required_key (there must be at least
738
* one), is the output key. This triggers the callback.
739
*
740
* Calls run_one_test with the variables set. If an ini-style [header]
741
* is used in the file, then header will be set to that value. This allows
742
* splitting up tests between [valid] and [invalid] tests, or different
743
* related algorithms tested in the same file. Use the get_XXX functions
744
* on VarMap to retrieve formatted values.
745
*
746
* If most of your tests are text-based but you find yourself with a few
747
* odds-and-ends tests that you want to do, override run_final_tests which
748
* can test whatever it likes and returns a vector of Results.
749
*/
750
class Text_Based_Test : public Test {
751
   public:
752
      Text_Based_Test(const std::string& data_src,
753
                      const std::string& required_keys_str,
754
                      const std::string& optional_keys_str = "");
755

756
      Text_Based_Test(const Text_Based_Test& other) = delete;
757
      Text_Based_Test(Text_Based_Test&& other) = default;
758
      Text_Based_Test& operator=(const Text_Based_Test& other) = delete;
759
      Text_Based_Test& operator=(Text_Based_Test&& other) = delete;
760

761
      ~Text_Based_Test() override;
762

763
      virtual bool clear_between_callbacks() const { return true; }
33,492✔
764

765
      std::vector<Test::Result> run() override;
766

767
   private:
768
      virtual Test::Result run_one_test(const std::string& header, const VarMap& vars) = 0;
769
      // Called before run_one_test
770
      virtual bool skip_this_test(const std::string& header, const VarMap& vars);
771

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

774
   private:
775
      class Text_Based_Test_Data;
776
      std::unique_ptr<Text_Based_Test_Data> m_data;
777
};
778

779
/**
780
 * This is a convenience wrapper to write small self-contained and in particular
781
 * exception-safe unit tests. If some (unexpected) exception is thrown in one of
782
 * the CHECK-scopes, it will fail the particular test gracefully with a human-
783
 * understandable failure output.
784
 *
785
 * Example Usage:
786
 *
787
 * ```
788
 * std::vector<Test::Result> test_something()
789
 *    {
790
 *    return
791
 *       {
792
 *       CHECK("some unit test name", [](Test::Result& r)
793
 *          {
794
 *          r.test_is_true("some observation", 1+1 == 2);
795
 *          }),
796
 *       CHECK("some other unit test name", [](Test::Result& r)
797
 *          {
798
 *          // ...
799
 *          })
800
 *       };
801
 *    }
802
 *
803
 * BOTAN_REGISTER_TEST_FN("some_category", "some_test_name", test_something);
804
 * ```
805
 */
806
template <typename FunT>
807
Test::Result CHECK(const char* name, FunT check_fun) {
508✔
808
   Botan_Tests::Test::Result r(name);
508✔
809

810
   try {
811
      check_fun(r);
508✔
812
   } catch(const Botan_Tests::Test_Aborted&) {
×
813
      // pass, failure was already noted in the responsible `require`
814
   } catch(const std::exception& ex) {
×
815
      r.test_failure("failed unexpectedly", ex.what());
×
816
   } catch(...) {
×
817
      r.test_failure("failed with unknown exception");
×
818
   }
819

820
   return r;
507✔
821
}
×
822

823
}  // namespace Botan_Tests
824

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