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

randombit / botan / 21989216571

13 Feb 2026 01:49PM UTC coverage: 90.07% (+0.004%) from 90.066%
21989216571

Pull #5327

github

web-flow
Merge 0e74fc899 into 2640927f7
Pull Request #5327: Various cleanups for tests.h

102221 of 113490 relevant lines covered (90.07%)

11420666.65 hits per line

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

87.31
/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/assert.h>
18
#include <botan/types.h>
19
#include <functional>
20
#include <iosfwd>
21
#include <memory>
22
#include <optional>
23
#include <span>
24
#include <sstream>
25
#include <stdexcept>
26
#include <string>
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 std::runtime_error {
51
   public:
52
      explicit Test_Error(const std::string& what) : std::runtime_error(what) {}
1✔
53
};
54

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

139
      bool log_success() const { return m_log_success; }
3,620,258✔
140

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

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

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

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

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

151
      bool verbose() const { return m_verbose; }
2,541✔
152

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

178
namespace detail {
179

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

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

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

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

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

201
}  // namespace detail
202

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

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

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

233
            size_t tests_passed() const { return m_tests_passed; }
12,585✔
234

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

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

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

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

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

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

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

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

258
            std::string result_string() const;
259

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

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

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

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

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

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

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

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

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

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

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

290
            bool confirm(const std::string& what, bool expr, bool expected = true) {
174,255✔
291
               return test_eq(what, expr, expected);
173,926✔
292
            }
293

294
            /**
295
             * Require a condition, throw Test_Aborted otherwise
296
             * Note: works best when combined with CHECK scopes!
297
             */
298
            void require(const std::string& what, bool expr, bool expected = true) {
329✔
299
               if(!confirm(what, expr, expected)) {
329✔
300
                  throw Test_Aborted("test aborted, because required condition was not met: " + what);
×
301
               }
302
            }
329✔
303

304
            template <typename T>
305
            bool test_is_eq(const T& produced, const T& expected) {
120✔
306
               return test_is_eq("comparison", produced, expected);
51✔
307
            }
308

309
            template <typename T>
310
            bool test_is_eq(std::string_view what, const T& produced, const T& expected) {
906,720✔
311
               std::ostringstream out;
906,720✔
312
               out << m_who << " " << what;
906,720✔
313

314
               if(produced == expected) {
906,711✔
315
                  out << " produced expected result";
906,718✔
316
                  return test_success(out.str());
906,718✔
317
               } else {
318
                  out << " produced unexpected result '" << to_string(produced) << "' expected '" << to_string(expected)
319
                      << "'";
7✔
320
                  return test_failure(out.str());
2✔
321
               }
322
            }
906,720✔
323

324
            template <typename T>
325
            bool test_not_null(const std::string& what, const T& ptr) {
1,735✔
326
               if(ptr == nullptr) {
1,735✔
327
                  return test_failure(what + " was null");
×
328
               } else {
329
                  return test_success(what + " was not null");
1,735✔
330
               }
331
            }
332

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

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

344
            bool test_is_nonempty(const std::string& what_is_it, const std::string& to_examine);
345

346
            bool test_eq(const std::string& what, const std::string& produced, const std::string& expected);
347

348
            bool test_eq(const std::string& what, bool produced, bool expected);
349

350
            bool test_eq(const std::string& what, size_t produced, size_t expected);
351
            bool test_eq_sz(const std::string& what, size_t produced, size_t expected);
352

353
            template <typename I1, typename I2>
354
            bool test_int_eq(I1 x, I2 y, const char* what) {
59✔
355
               return test_eq(what, static_cast<size_t>(x), static_cast<size_t>(y));
59✔
356
            }
357

358
            template <typename I1, typename I2>
359
            bool test_int_eq(const std::string& what, I1 x, I2 y) {
5,190✔
360
               return test_eq(what, static_cast<size_t>(x), static_cast<size_t>(y));
5,190✔
361
            }
362

363
            template <typename T>
364
            bool test_eq(const std::string& what, const std::optional<T>& a, const std::optional<T>& b) {
1,215✔
365
               if(a.has_value() != b.has_value()) {
1,215✔
366
                  std::ostringstream err;
×
367
                  err << m_who << " " << what << " only one of a/b was nullopt";
×
368
                  return test_failure(err.str());
×
369
               } else if(a.has_value() && b.has_value()) {
1,215✔
370
                  return test_is_eq(what, a.value(), b.value());
10✔
371
               } else {
372
                  // both nullopt
373
                  return test_success();
1,205✔
374
               }
375
            }
376

377
            bool test_lt(const std::string& what, size_t produced, size_t expected);
378
            bool test_lte(const std::string& what, size_t produced, size_t expected);
379
            bool test_gt(const std::string& what, size_t produced, size_t expected);
380
            bool test_gte(const std::string& what, size_t produced, size_t expected);
381

382
            /* Test predicates on integer return codes */
383
            bool test_rc_ok(const std::string& func, int rc);
384
            bool test_rc_fail(const std::string& func, const std::string& why, int rc);
385
            bool test_rc(const std::string& func, int expected, int rc);
386
            bool test_rc_init(const std::string& func, int rc);
387

388
            bool test_ne(const std::string& what, size_t produced, size_t expected);
389

390
            bool test_ne(const std::string& what, const std::string& str1, const std::string& str2);
391

392
#if defined(BOTAN_HAS_BIGINT)
393
            bool test_eq(const std::string& what, const BigInt& produced, const BigInt& expected);
394
            bool test_ne(const std::string& what, const BigInt& produced, const BigInt& expected);
395
#endif
396

397
#if defined(BOTAN_HAS_LEGACY_EC_POINT)
398
            bool test_eq(const std::string& what, const Botan::EC_Point& a, const Botan::EC_Point& b);
399
#endif
400

401
            bool test_eq(const char* producer,
402
                         const std::string& what,
403
                         const uint8_t produced[],
404
                         size_t produced_size,
405
                         const uint8_t expected[],
406
                         size_t expected_size);
407

408
            bool test_ne(const std::string& what,
409
                         const uint8_t produced[],
410
                         size_t produced_len,
411
                         const uint8_t expected[],
412
                         size_t expected_len);
413

414
            bool test_eq(const std::string& what,
131,413✔
415
                         std::span<const uint8_t> produced,
416
                         std::span<const uint8_t> expected) {
417
               return test_eq(nullptr, what, produced.data(), produced.size(), expected.data(), expected.size());
131,413✔
418
            }
419

420
            bool test_eq(const std::string& producer,
115,192✔
421
                         const std::string& what,
422
                         std::span<const uint8_t> produced,
423
                         std::span<const uint8_t> expected) {
424
               return test_eq(
115,192✔
425
                  producer.c_str(), what, produced.data(), produced.size(), expected.data(), expected.size());
115,192✔
426
            }
427

428
            bool test_eq(const std::string& what, std::span<const uint8_t> produced, const char* expected_hex);
429

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

437
            bool test_ne(const std::string& what,
3,405✔
438
                         std::span<const uint8_t> produced,
439
                         std::span<const uint8_t> expected) {
440
               return test_ne(what, produced.data(), produced.size(), expected.data(), expected.size());
3,405✔
441
            }
442

443
         private:
444
            class ThrowExpectations {
445
               public:
446
                  explicit ThrowExpectations(std::function<void()> fn) : m_fn(std::move(fn)) {}
135,566✔
447

448
                  ThrowExpectations(const ThrowExpectations&) = delete;
449
                  ThrowExpectations& operator=(const ThrowExpectations&) = delete;
450
                  ThrowExpectations(ThrowExpectations&&) = default;
451
                  ThrowExpectations& operator=(ThrowExpectations&&) = default;
452

453
                  ~ThrowExpectations() { BOTAN_ASSERT_NOMSG(m_consumed); }
130,020✔
454

455
                  ThrowExpectations& expect_success() {
1,645✔
456
                     BOTAN_ASSERT_NOMSG(!m_expected_message && !m_expected_exception_check_fn);
1,645✔
457
                     m_expect_success = true;
1,645✔
458
                     return *this;
1,645✔
459
                  }
460

461
                  ThrowExpectations& expect_message(const std::string& message) {
189✔
462
                     BOTAN_ASSERT_NOMSG(!m_expect_success);
189✔
463
                     m_expected_message = message;
189✔
464
                     return *this;
189✔
465
                  }
466

467
                  template <typename ExT>
468
                  ThrowExpectations& expect_exception_type() {
62,048✔
469
                     BOTAN_ASSERT_NOMSG(!m_expect_success);
62,048✔
470
                     m_expected_exception_check_fn = [](const std::exception_ptr& e) {
124,096✔
471
                        try {
472
                           if(e) {
62,048✔
473
                              std::rethrow_exception(e);
124,096✔
474
                           }
475
                        } catch(const ExT&) {
62,048✔
476
                           return true;
477
                        } catch(...) {
2✔
478
                           return false;
479
                        }
480
                        return false;
481
                     };
482
                     return *this;
62,048✔
483
                  }
484

485
                  bool check(const std::string& test_name, Test::Result& result);
486

487
               private:
488
                  std::function<void()> m_fn;
489
                  std::optional<std::string> m_expected_message;
490
                  std::function<bool(std::exception_ptr)> m_expected_exception_check_fn;
491
                  bool m_expect_success = false;
492
                  bool m_consumed = false;
493
            };
494

495
         public:
496
            bool test_throws(const std::string& what, const std::function<void()>& fn);
497

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

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

502
            template <typename ExceptionT>
503
            bool test_throws(const std::string& what, const std::function<void()>& fn) {
62,033✔
504
               return ThrowExpectations(fn).expect_exception_type<ExceptionT>().check(what, *this);
124,066✔
505
            }
506

507
            template <typename ExceptionT>
508
            bool test_throws(const std::string& what, const std::string& expected, const std::function<void()>& fn) {
15✔
509
               return ThrowExpectations(fn).expect_exception_type<ExceptionT>().expect_message(expected).check(what,
30✔
510
                                                                                                               *this);
15✔
511
            }
512

513
            void set_ns_consumed(uint64_t ns) { m_ns_taken = ns; }
48,251✔
514

515
            void start_timer();
516
            void end_timer();
517

518
            void set_code_location(CodeLocation where) { m_where = where; }
50,691✔
519

520
            const std::optional<CodeLocation>& code_location() const { return m_where; }
53,206✔
521

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

540
         private:
541
            std::string m_who;
542
            std::optional<CodeLocation> m_where;
543
            uint64_t m_timestamp;
544
            uint64_t m_started = 0;
545
            uint64_t m_ns_taken = 0;
546
            size_t m_tests_passed = 0;
547
            std::vector<std::string> m_fail_log;
548
            std::vector<std::string> m_log;
549
      };
550

551
      virtual ~Test();
552

553
      Test();
554
      Test(const Test& other) = delete;
555
      Test(Test&& other) = default;
556
      Test& operator=(const Test& other) = delete;
557
      Test& operator=(Test&& other) = delete;
558

559
      virtual std::vector<Test::Result> run() = 0;
560

561
      virtual std::vector<std::string> possible_providers(const std::string& alg);
562

563
      void initialize(std::string test_name, CodeLocation location);
564

565
      const std::string& test_name() const { return m_test_name; }
23✔
566

567
      Botan::RandomNumberGenerator& rng() const;
568

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

581
      const std::optional<CodeLocation>& registration_location() const { return m_registration_location; }
50,691✔
582

583
      /// @p smoke_test are run first in an unfiltered test run
584
      static void register_test(const std::string& category,
585
                                const std::string& name,
586
                                bool smoke_test,
587
                                bool needs_serialization,
588
                                std::function<std::unique_ptr<Test>()> maker_fn);
589

590
      static std::vector<std::string> registered_tests();
591
      static std::vector<std::string> registered_test_categories();
592

593
      static std::vector<std::string> filter_registered_tests(const std::vector<std::string>& requested,
594
                                                              const std::vector<std::string>& to_be_skipped);
595

596
      static std::unique_ptr<Test> get_test(const std::string& test_name);
597
      static bool test_needs_serialization(const std::string& test_name);
598

599
      static std::string data_dir(const std::string& subdir);
600
      static std::vector<std::string> files_in_data_dir(const std::string& subdir);
601
      static std::string data_file(const std::string& file);
602
      static std::string data_file_as_temporary_copy(const std::string& what);
603

604
      static std::string format_time(uint64_t nanoseconds);
605

606
      static std::vector<uint8_t> mutate_vec(const std::vector<uint8_t>& v,
607
                                             Botan::RandomNumberGenerator& rng,
608
                                             bool maybe_resize = false,
609
                                             size_t min_offset = 0);
610

611
      static void set_test_options(const Test_Options& opts);
612

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

615
      static const Test_Options& options() { return m_opts; }
616

617
      static bool run_long_tests() { return options().run_long_tests(); }
2,871✔
618

619
      static bool run_memory_intensive_tests() { return options().run_memory_intensive_tests(); }
1✔
620

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

623
      static std::string temp_file_name(const std::string& basename);
624
      static bool copy_file(const std::string& from, const std::string& to);
625

626
      static std::vector<std::string> provider_filter(const std::vector<std::string>& providers);
627

628
      static std::string read_data_file(const std::string& path);
629
      static std::vector<uint8_t> read_binary_data_file(const std::string& path);
630

631
      static std::unique_ptr<Botan::RandomNumberGenerator> new_rng(std::string_view test_name);
632
      static std::shared_ptr<Botan::RandomNumberGenerator> new_shared_rng(std::string_view test_name);
633

634
      static std::string random_password(Botan::RandomNumberGenerator& rng);
635
      static size_t random_index(Botan::RandomNumberGenerator& rng, size_t max);
636
      static uint64_t timestamp();  // nanoseconds arbitrary epoch
637

638
      static std::vector<Test::Result> flatten_result_lists(std::vector<std::vector<Test::Result>> result_lists);
639

640
   private:
641
      static Test_Options m_opts;
642
      static std::string m_test_rng_seed;
643

644
      /// The string ID that was used to register this test
645
      std::string m_test_name;
646
      /// The source file location where the test was registered
647
      std::optional<CodeLocation> m_registration_location;
648
      /// The test-specific RNG state
649
      mutable std::unique_ptr<Botan::RandomNumberGenerator> m_test_rng;
650
};
651

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

671
// NOLINTBEGIN(*-macro-usage)
672

673
#define BOTAN_REGISTER_TEST(category, name, Test_Class) \
674
   /* NOLINTNEXTLINE(cert-err58-cpp) */                 \
675
   const TestClassRegistration<Test_Class> reg_##Test_Class##_tests(category, name, false, false, {__FILE__, __LINE__})
676
#define BOTAN_REGISTER_SERIALIZED_TEST(category, name, Test_Class) \
677
   /* NOLINTNEXTLINE(cert-err58-cpp) */                            \
678
   const TestClassRegistration<Test_Class> reg_##Test_Class##_tests(category, name, false, true, {__FILE__, __LINE__})
679
#define BOTAN_REGISTER_SMOKE_TEST(category, name, Test_Class) \
680
   /* NOLINTNEXTLINE(cert-err58-cpp) */                       \
681
   const TestClassRegistration<Test_Class> reg_##Test_Class##_tests(category, name, true, false, {__FILE__, __LINE__})
682
#define BOTAN_REGISTER_SERIALIZED_SMOKE_TEST(category, name, Test_Class) \
683
   /* NOLINTNEXTLINE(cert-err58-cpp) */                                  \
684
   const TestClassRegistration<Test_Class> reg_##Test_Class##_tests(category, name, true, true, {__FILE__, __LINE__})
685

686
// NOLINTEND(*-macro-usage)
687

688
typedef Test::Result (*test_fn)();
689
typedef std::vector<Test::Result> (*test_fn_vec)();
690

691
class FnTest : public Test {
692
   private:
693
      using TestFnVariant = std::variant<test_fn, test_fn_vec>;
694

695
      template <typename TestFn>
696
      std::vector<TestFnVariant> make_variant_vector(TestFn fn) {
34✔
697
         using T = std::decay_t<decltype(fn)>;
698
         static_assert(std::is_same_v<T, test_fn> || std::is_same_v<T, test_fn_vec>,
699
                       "functions passed to BOTAN_REGISTER_TEST_FN must either return a "
700
                       "single Test::Result or a std::vector of Test::Result");
701
         return {fn};
34✔
702
      }
703

704
      template <typename TestFn, typename... TestFns>
705
      std::vector<TestFnVariant> make_variant_vector(const TestFn& fn, const TestFns&... fns) {
31✔
706
         auto functions = make_variant_vector(fns...);
31✔
707
         functions.emplace_back(fn);
31✔
708
         return functions;
31✔
709
      }
×
710

711
   public:
712
      template <typename... TestFns>
713
      explicit FnTest(TestFns... fns) : m_fns(make_variant_vector(fns...)) {}
34✔
714

715
      std::vector<Test::Result> run() override {
34✔
716
         std::vector<Test::Result> result;
34✔
717

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

734
         return result;
34✔
735
      }
×
736

737
   private:
738
      std::vector<TestFnVariant> m_fns;
739
};
740

741
class TestFnRegistration {
742
   public:
743
      template <typename... TestFns>
744
      TestFnRegistration(const std::string& category,
34✔
745
                         const std::string& name,
746
                         bool smoke_test,
747
                         bool needs_serialization,
748
                         const CodeLocation& registration_location,
749
                         TestFns... fn) {
750
         Test::register_test(category, name, smoke_test, needs_serialization, [=] {
125✔
751
            auto test = std::make_unique<FnTest>(fn...);
34✔
752
            test->initialize(name, registration_location);
68✔
753
            return test;
34✔
754
         });
×
755
      }
34✔
756
};
757

758
// NOLINTBEGIN(*-macro-usage)
759

760
#define BOTAN_REGISTER_TEST_FN_IMPL(category, name, smoke_test, needs_serialization, fn0, ...) \
761
   /* NOLINTNEXTLINE(cert-err58-cpp) */                                                        \
762
   static const TestFnRegistration register_##fn0(                                             \
763
      category, name, smoke_test, needs_serialization, {__FILE__, __LINE__}, fn0 __VA_OPT__(, ) __VA_ARGS__)
764

765
#define BOTAN_REGISTER_TEST_FN(category, name, ...) \
766
   BOTAN_REGISTER_TEST_FN_IMPL(category, name, false, false, __VA_ARGS__)
767
#define BOTAN_REGISTER_SMOKE_TEST_FN(category, name, ...) \
768
   BOTAN_REGISTER_TEST_FN_IMPL(category, name, true, false, __VA_ARGS__)
769
#define BOTAN_REGISTER_SERIALIZED_TEST_FN(category, name, ...) \
770
   BOTAN_REGISTER_TEST_FN_IMPL(category, name, false, true, __VA_ARGS__)
771
#define BOTAN_REGISTER_SERIALIZED_SMOKE_TEST_FN(category, name, ...) \
772
   BOTAN_REGISTER_TEST_FN_IMPL(category, name, true, true, __VA_ARGS__)
773

774
// NOLINTEND(*-macro-usage)
775

776
class VarMap {
181✔
777
   public:
778
      bool has_key(const std::string& key) const;
779

780
      bool get_req_bool(const std::string& key) const;
781

782
      std::vector<uint8_t> get_req_bin(const std::string& key) const;
783
      std::vector<uint8_t> get_opt_bin(const std::string& key) const;
784

785
      std::vector<std::vector<uint8_t>> get_req_bin_list(const std::string& key) const;
786

787
#if defined(BOTAN_HAS_BIGINT)
788
      Botan::BigInt get_req_bn(const std::string& key) const;
789
      Botan::BigInt get_opt_bn(const std::string& key, const Botan::BigInt& def_value) const;
790
#endif
791

792
      std::string get_req_str(const std::string& key) const;
793
      std::string get_opt_str(const std::string& key, const std::string& def_value) const;
794

795
      size_t get_req_sz(const std::string& key) const;
796

797
      uint8_t get_req_u8(const std::string& key) const;
798
      uint32_t get_req_u32(const std::string& key) const;
799
      uint64_t get_req_u64(const std::string& key) const;
800

801
      size_t get_opt_sz(const std::string& key, size_t def_value) const;
802

803
      uint64_t get_opt_u64(const std::string& key, uint64_t def_value) const;
804

805
      void clear();
806

807
      void add(const std::string& key, const std::string& value);
808

809
   private:
810
      std::optional<std::string> get_var(const std::string& key) const;
811

812
      std::vector<std::pair<std::string, std::string>> m_vars;
813
};
814

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

836
      Text_Based_Test(const Text_Based_Test& other) = delete;
837
      Text_Based_Test(Text_Based_Test&& other) = default;
838
      Text_Based_Test& operator=(const Text_Based_Test& other) = delete;
839
      Text_Based_Test& operator=(Text_Based_Test&& other) = delete;
840

841
      ~Text_Based_Test() override;
842

843
      virtual bool clear_between_callbacks() const { return true; }
33,476✔
844

845
      std::vector<Test::Result> run() override;
846

847
   private:
848
      virtual Test::Result run_one_test(const std::string& header, const VarMap& vars) = 0;
849
      // Called before run_one_test
850
      virtual bool skip_this_test(const std::string& header, const VarMap& vars);
851

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

854
   private:
855
      class Text_Based_Test_Data;
856
      std::unique_ptr<Text_Based_Test_Data> m_data;
857
};
858

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

890
   try {
891
      check_fun(r);
507✔
892
   } catch(const Botan_Tests::Test_Aborted&) {
×
893
      // pass, failure was already noted in the responsible `require`
894
   } catch(const std::exception& ex) {
×
895
      r.test_failure("failed unexpectedly", ex.what());
×
896
   } catch(...) {
×
897
      r.test_failure("failed with unknown exception");
×
898
   }
899

900
   return r;
506✔
901
}
×
902

903
}  // namespace Botan_Tests
904

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