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

randombit / botan / 15725004337

18 Jun 2025 05:52AM UTC coverage: 90.539% (-0.02%) from 90.558%
15725004337

Pull #4890

github

web-flow
Merge 0636720b8 into cfc7bc1c6
Pull Request #4890: Improve RFC 3779 extension api

98455 of 108743 relevant lines covered (90.54%)

12317237.8 hits per line

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

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

8
#ifndef BOTAN_TESTS_H_
9
#define BOTAN_TESTS_H_
10

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

31
namespace Botan {
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 Botan::Exception {
50
   public:
51
      explicit Test_Error(const std::string& what) : Exception("Test error", what) {}
1✔
52

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

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

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

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

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

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

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

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

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

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

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

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

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

128
      std::vector<uint8_t> tpm2_persistent_auth_value() const {
2✔
129
         std::span<const uint8_t> auth_value(Botan::cast_char_ptr_to_uint8(m_tpm2_persistent_auth_value.data()),
2✔
130
                                             m_tpm2_persistent_auth_value.size());
2✔
131
         return std::vector<uint8_t>(auth_value.begin(), auth_value.end());
2✔
132
      }
133

134
      const std::string& drbg_seed() const { return m_drbg_seed; }
1✔
135

136
      const std::string& xml_results_dir() const { return m_xml_results_dir; }
1✔
137

138
      std::map<std::string, std::string> report_properties() const;
139

140
      size_t test_runs() const { return m_test_runs; }
4✔
141

142
      size_t test_threads() const { return m_test_threads; }
3✔
143

144
      bool log_success() const { return m_log_success; }
3,433,455✔
145

146
      bool run_online_tests() const { return m_run_online_tests; }
2✔
147

148
      bool run_long_tests() const { return m_run_long_tests; }
2,870✔
149

150
      bool run_memory_intensive_tests() const { return m_run_memory_intensive_tests; }
1✔
151

152
      bool abort_on_first_fail() const { return m_abort_on_first_fail; }
25✔
153

154
      bool no_stdout() const { return m_no_stdout; }
1✔
155

156
      bool verbose() const { return m_verbose; }
2,485✔
157

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

183
namespace detail {
184

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

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

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

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

205
template <typename T>
206
struct is_optional<std::optional<T>> : std::true_type {};
207

208
template <typename T>
209
constexpr bool is_optional_v = is_optional<T>::value;
210

211
}  // namespace detail
212

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

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

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

243
            size_t tests_passed() const { return m_tests_passed; }
12,305✔
244

245
            size_t tests_failed() const { return m_fail_log.size(); }
67,877✔
246

247
            size_t tests_run() const { return tests_passed() + tests_failed(); }
12,305✔
248

249
            bool any_results() const { return tests_run() > 0; }
250

251
            const std::string& who() const { return m_who; }
225,555✔
252

253
            const std::vector<std::string>& failures() const { return m_fail_log; }
2,459✔
254

255
            const std::vector<std::string>& notes() const { return m_log; }
2,459✔
256

257
            std::optional<std::chrono::nanoseconds> elapsed_time() const {
2,459✔
258
               if(m_ns_taken == 0) {
2,459✔
259
                  return std::nullopt;
260
               } else {
261
                  return std::chrono::nanoseconds(m_ns_taken);
1,439✔
262
               }
263
            }
264

265
            const std::chrono::system_clock::time_point& timestamp() const { return m_timestamp; }
2,459✔
266

267
            std::string result_string() const;
268

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

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

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

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

297
            void merge(const Result& other, bool ignore_test_name = false);
298

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

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

307
            void note_missing(const std::string& thing);
308

309
            bool test_success(const std::string& note = "");
310

311
            bool test_failure(const std::string& err);
312

313
            bool test_failure(const std::string& what, const std::string& error);
314

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

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

322
            bool confirm(const std::string& what, bool expr, bool expected = true) {
160,982✔
323
               return test_eq(what, expr, expected);
160,681✔
324
            }
325

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

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

341
            template <typename T>
342
            bool test_is_eq(const std::string& what, const T& produced, const T& expected) {
785,644✔
343
               std::ostringstream out;
785,644✔
344
               out << m_who << " " << what;
785,644✔
345

346
               if(produced == expected) {
785,635✔
347
                  out << " produced expected result";
785,642✔
348
                  return test_success(out.str());
785,642✔
349
               } else {
350
                  out << " produced unexpected result '" << to_string(produced) << "' expected '" << to_string(expected)
351
                      << "'";
7✔
352
                  return test_failure(out.str());
2✔
353
               }
354
            }
785,644✔
355

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

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

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

376
            bool test_is_nonempty(const std::string& what_is_it, const std::string& to_examine);
377

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

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

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

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

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

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

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

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

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

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

430
               return test_success();
2,310✔
431
            }
432

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

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

445
               return test_success();
24✔
446
            }
447

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

450
            bool test_rc_init(const std::string& func, int rc);
451

452
            bool test_ne(const std::string& what, size_t produced, size_t expected);
453

454
            bool test_ne(const std::string& what, const std::string& str1, const std::string& str2);
455

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

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

465
            bool test_eq(const char* producer,
466
                         const std::string& what,
467
                         const uint8_t produced[],
468
                         size_t produced_len,
469
                         const uint8_t expected[],
470
                         size_t expected_len);
471

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

478
            bool test_eq(const std::string& what,
118,349✔
479
                         std::span<const uint8_t> produced,
480
                         std::span<const uint8_t> expected) {
481
               return test_eq(nullptr, what, produced.data(), produced.size(), expected.data(), expected.size());
118,349✔
482
            }
483

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

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

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

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

510
         private:
511
            class ThrowExpectations {
512
               public:
513
                  ThrowExpectations(std::function<void()> fn) :
47,999✔
514
                        m_fn(std::move(fn)), m_expect_success(false), m_consumed(false) {}
95,998✔
515

516
                  ThrowExpectations(const ThrowExpectations&) = delete;
517
                  ThrowExpectations& operator=(const ThrowExpectations&) = delete;
518
                  ThrowExpectations(ThrowExpectations&&) = default;
519
                  ThrowExpectations& operator=(ThrowExpectations&&) = default;
520

521
                  ~ThrowExpectations() { BOTAN_ASSERT_NOMSG(m_consumed); }
48,188✔
522

523
                  ThrowExpectations& expect_success() {
1,644✔
524
                     BOTAN_ASSERT_NOMSG(!m_expected_message && !m_expected_exception_type);
1,644✔
525
                     m_expect_success = true;
1,644✔
526
                     return *this;
1,644✔
527
                  }
528

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

535
                  template <typename ExT>
536
                  ThrowExpectations& expect_exception_type() {
270✔
537
                     BOTAN_ASSERT_NOMSG(!m_expect_success);
270✔
538
                     m_expected_exception_type = typeid(ExT);
270✔
539
                     return *this;
270✔
540
                  }
541

542
                  bool check(const std::string& test_name, Test::Result& result);
543

544
               private:
545
                  std::function<void()> m_fn;
546
                  bool m_expect_success;
547
                  std::optional<std::string> m_expected_message;
548
                  std::optional<std::type_index> m_expected_exception_type;
549
                  bool m_consumed;
550
            };
551

552
         public:
553
            bool test_throws(const std::string& what, const std::function<void()>& fn);
554

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

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

559
            template <typename ExceptionT>
560
            bool test_throws(const std::string& what, const std::function<void()>& fn) {
255✔
561
               return ThrowExpectations(fn).expect_exception_type<ExceptionT>().check(what, *this);
510✔
562
            }
563

564
            template <typename ExceptionT>
565
            bool test_throws(const std::string& what, const std::string& expected, const std::function<void()>& fn) {
15✔
566
               return ThrowExpectations(fn).expect_exception_type<ExceptionT>().expect_message(expected).check(what,
30✔
567
                                                                                                               *this);
15✔
568
            }
569

570
            void set_ns_consumed(uint64_t ns) { m_ns_taken = ns; }
47,608✔
571

572
            void start_timer();
573
            void end_timer();
574

575
            void set_code_location(CodeLocation where) { m_where = where; }
49,743✔
576

577
            const std::optional<CodeLocation>& code_location() const { return m_where; }
52,202✔
578

579
         private:
580
            template <typename T>
581
            std::string to_string(const T& v) {
2✔
582
               if constexpr(detail::is_optional_v<T>) {
583
                  return (v.has_value()) ? to_string(v.value()) : std::string("std::nullopt");
×
584
               } else if constexpr(detail::has_Botan_to_string<T>) {
585
                  return Botan::to_string(v);
586
               } else if constexpr(detail::has_ostream_operator<T>) {
587
                  std::ostringstream oss;
2✔
588
                  oss << v;
2✔
589
                  return oss.str();
2✔
590
               } else if constexpr(detail::has_std_to_string<T>) {
2✔
591
                  return std::to_string(v);
2✔
592
               } else {
593
                  return "<?>";
×
594
               }
595
            }
596

597
         private:
598
            std::string m_who;
599
            std::optional<CodeLocation> m_where;
600
            std::chrono::system_clock::time_point m_timestamp;
601
            uint64_t m_started = 0;
602
            uint64_t m_ns_taken = 0;
603
            size_t m_tests_passed = 0;
604
            std::vector<std::string> m_fail_log;
605
            std::vector<std::string> m_log;
606
      };
607

608
      virtual ~Test() = default;
552✔
609

610
      Test() = default;
406✔
611
      Test(const Test& other) = delete;
612
      Test(Test&& other) = default;
613
      Test& operator=(const Test& other) = delete;
614
      Test& operator=(Test&& other) = delete;
615

616
      virtual std::vector<Test::Result> run() = 0;
617

618
      virtual std::vector<std::string> possible_providers(const std::string&);
619

620
      void initialize(std::string test_name, CodeLocation location);
621

622
      const std::string& test_name() const { return m_test_name; }
19✔
623

624
      Botan::RandomNumberGenerator& rng() const;
625

626
      const std::optional<CodeLocation>& registration_location() const { return m_registration_location; }
49,743✔
627

628
      /// @p smoke_test are run first in an unfiltered test run
629
      static void register_test(const std::string& category,
630
                                const std::string& name,
631
                                bool smoke_test,
632
                                bool needs_serialization,
633
                                std::function<std::unique_ptr<Test>()> maker_fn);
634

635
      static std::set<std::string> registered_tests();
636
      static std::set<std::string> registered_test_categories();
637
      static std::vector<std::string> filter_registered_tests(const std::vector<std::string>& requested,
638
                                                              const std::set<std::string>& to_be_skipped);
639

640
      static std::unique_ptr<Test> get_test(const std::string& test_name);
641
      static bool test_needs_serialization(const std::string& test_name);
642

643
      static std::string data_dir(const std::string& subdir);
644
      static std::vector<std::string> files_in_data_dir(const std::string& subdir);
645
      static std::string data_file(const std::string& what);
646
      static std::string data_file_as_temporary_copy(const std::string& what);
647

648
      static std::string format_time(uint64_t nanoseconds);
649

650
      static std::string format_time(const std::chrono::nanoseconds nanoseconds) {
1✔
651
         return format_time(nanoseconds.count());
1✔
652
      }
653

654
      template <typename Alloc>
655
      static std::vector<uint8_t, Alloc> mutate_vec(const std::vector<uint8_t, Alloc>& v,
82,878✔
656
                                                    Botan::RandomNumberGenerator& rng,
657
                                                    bool maybe_resize = false,
658
                                                    size_t min_offset = 0) {
659
         std::vector<uint8_t, Alloc> r = v;
82,878✔
660

661
         if(maybe_resize && (r.empty() || rng.next_byte() < 32)) {
92,577✔
662
            // TODO: occasionally truncate, insert at random index
663
            const size_t add = 1 + (rng.next_byte() % 16);
1,577✔
664
            r.resize(r.size() + add);
1,577✔
665
            rng.randomize(&r[r.size() - add], add);
1,577✔
666
         }
667

668
         if(r.size() > min_offset) {
82,878✔
669
            const size_t offset = std::max<size_t>(min_offset, rng.next_byte() % r.size());
81,478✔
670
            const uint8_t perturb = rng.next_nonzero_byte();
81,478✔
671
            r[offset] ^= perturb;
81,478✔
672
         }
673

674
         return r;
82,878✔
675
      }
×
676

677
      static void set_test_options(const Test_Options& opts);
678

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

681
      static const Test_Options& options() { return m_opts; }
2✔
682

683
      static bool run_long_tests() { return options().run_long_tests(); }
2,870✔
684

685
      static bool run_memory_intensive_tests() { return options().run_memory_intensive_tests(); }
1✔
686

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

689
      static std::string temp_file_name(const std::string& basename);
690
      static bool copy_file(const std::string& from, const std::string& to);
691

692
      static std::vector<std::string> provider_filter(const std::vector<std::string>& providers);
693

694
      static std::string read_data_file(const std::string& path);
695
      static std::vector<uint8_t> read_binary_data_file(const std::string& path);
696

697
      static std::unique_ptr<Botan::RandomNumberGenerator> new_rng(std::string_view test_name);
698
      static std::shared_ptr<Botan::RandomNumberGenerator> new_shared_rng(std::string_view test_name);
699

700
      static std::string random_password(Botan::RandomNumberGenerator& rng);
701
      static size_t random_index(Botan::RandomNumberGenerator& rng, size_t max);
702
      static uint64_t timestamp();  // nanoseconds arbitrary epoch
703

704
      static std::vector<Test::Result> flatten_result_lists(std::vector<std::vector<Test::Result>> result_lists);
705

706
   private:
707
      static Test_Options m_opts;
708
      static std::string m_test_rng_seed;
709

710
      /// The string ID that was used to register this test
711
      std::string m_test_name;
712
      /// The source file location where the test was registered
713
      std::optional<CodeLocation> m_registration_location;
714
      /// The test-specific RNG state
715
      mutable std::unique_ptr<Botan::RandomNumberGenerator> m_test_rng;
716
};
717

718
/*
719
* Register the test with the runner
720
*/
721
template <typename Test_Class>
722
class TestClassRegistration {
723
   public:
724
      TestClassRegistration(const std::string& category,
374✔
725
                            const std::string& name,
726
                            bool smoke_test,
727
                            bool needs_serialization,
728
                            const CodeLocation& registration_location) {
729
         Test::register_test(category, name, smoke_test, needs_serialization, [=] {
1,824✔
730
            auto test = std::make_unique<Test_Class>();
374✔
731
            test->initialize(name, registration_location);
748✔
732
            return test;
374✔
733
         });
×
734
      }
374✔
735
};
736

737
// NOLINTBEGIN(*-macro-usage)
738

739
#define BOTAN_REGISTER_TEST(category, name, Test_Class) \
740
   const TestClassRegistration<Test_Class> reg_##Test_Class##_tests(category, name, false, false, {__FILE__, __LINE__})
741
#define BOTAN_REGISTER_SERIALIZED_TEST(category, name, Test_Class) \
742
   const TestClassRegistration<Test_Class> reg_##Test_Class##_tests(category, name, false, true, {__FILE__, __LINE__})
743
#define BOTAN_REGISTER_SMOKE_TEST(category, name, Test_Class) \
744
   const TestClassRegistration<Test_Class> reg_##Test_Class##_tests(category, name, true, false, {__FILE__, __LINE__})
745
#define BOTAN_REGISTER_SERIALIZED_SMOKE_TEST(category, name, Test_Class) \
746
   const TestClassRegistration<Test_Class> reg_##Test_Class##_tests(category, name, true, true, {__FILE__, __LINE__})
747

748
// NOLINTEND(*-macro-usage)
749

750
typedef Test::Result (*test_fn)();
751
typedef std::vector<Test::Result> (*test_fn_vec)();
752

753
class FnTest : public Test {
754
   private:
755
      using TestFnVariant = std::variant<test_fn, test_fn_vec>;
756

757
      template <typename TestFn>
758
      std::vector<TestFnVariant> make_variant_vector(TestFn fn) {
34✔
759
         using T = std::decay_t<decltype(fn)>;
760
         static_assert(std::is_same_v<T, test_fn> || std::is_same_v<T, test_fn_vec>,
761
                       "functions passed to BOTAN_REGISTER_TEST_FN must either return a "
762
                       "single Test::Result or a std::vector of Test::Result");
763
         return {fn};
34✔
764
      }
765

766
      template <typename TestFn, typename... TestFns>
767
      std::vector<TestFnVariant> make_variant_vector(const TestFn& fn, const TestFns&... fns) {
31✔
768
         auto functions = make_variant_vector(fns...);
31✔
769
         functions.emplace_back(fn);
31✔
770
         return functions;
31✔
771
      }
×
772

773
   public:
774
      template <typename... TestFns>
775
      FnTest(TestFns... fns) : m_fns(make_variant_vector(fns...)) {}
34✔
776

777
      std::vector<Test::Result> run() override {
34✔
778
         std::vector<Test::Result> result;
34✔
779

780
         for(auto fn_variant = m_fns.crbegin(); fn_variant != m_fns.crend(); ++fn_variant) {
99✔
781
            std::visit(
65✔
782
               [&](auto&& fn) {
130✔
783
                  using T = std::decay_t<decltype(fn)>;
784
                  if constexpr(std::is_same_v<T, test_fn>) {
785
                     result.emplace_back(fn());
7✔
786
                  } else {
787
                     const auto results = fn();
58✔
788
                     result.insert(result.end(), results.begin(), results.end());
58✔
789
                  }
58✔
790
               },
65✔
791
               *fn_variant);
65✔
792
         }
793

794
         return result;
34✔
795
      }
×
796

797
   private:
798
      std::vector<TestFnVariant> m_fns;
799
};
800

801
class TestFnRegistration {
802
   public:
803
      template <typename... TestFns>
804
      TestFnRegistration(const std::string& category,
34✔
805
                         const std::string& name,
806
                         bool smoke_test,
807
                         bool needs_serialization,
808
                         const CodeLocation& registration_location,
809
                         TestFns... fn) {
810
         Test::register_test(category, name, smoke_test, needs_serialization, [=] {
125✔
811
            auto test = std::make_unique<FnTest>(fn...);
34✔
812
            test->initialize(name, registration_location);
68✔
813
            return test;
34✔
814
         });
×
815
      }
34✔
816
};
817

818
// NOLINTBEGIN(*-macro-usage)
819

820
#define BOTAN_REGISTER_TEST_FN_IMPL(category, name, smoke_test, needs_serialization, fn0, ...) \
821
   static const TestFnRegistration register_##fn0(                                             \
822
      category, name, smoke_test, needs_serialization, {__FILE__, __LINE__}, fn0 __VA_OPT__(, ) __VA_ARGS__)
823

824
#define BOTAN_REGISTER_TEST_FN(category, name, ...) \
825
   BOTAN_REGISTER_TEST_FN_IMPL(category, name, false, false, __VA_ARGS__)
826
#define BOTAN_REGISTER_SMOKE_TEST_FN(category, name, ...) \
827
   BOTAN_REGISTER_TEST_FN_IMPL(category, name, true, false, __VA_ARGS__)
828
#define BOTAN_REGISTER_SERIALIZED_TEST_FN(category, name, ...) \
829
   BOTAN_REGISTER_TEST_FN_IMPL(category, name, false, true, __VA_ARGS__)
830
#define BOTAN_REGISTER_SERIALIZED_SMOKE_TEST_FN(category, name, ...) \
831
   BOTAN_REGISTER_TEST_FN_IMPL(category, name, true, true, __VA_ARGS__)
832

833
// NOLINTEND(*-macro-usage)
834

835
class VarMap {
358✔
836
   public:
837
      void clear() { m_vars.clear(); }
33,644✔
838

839
      void add(const std::string& key, const std::string& value) { m_vars[key] = value; }
153,886✔
840

841
      bool has_key(const std::string& key) const { return m_vars.count(key) == 1; }
423,691✔
842

843
      bool get_req_bool(const std::string& key) const;
844

845
      std::vector<uint8_t> get_req_bin(const std::string& key) const;
846
      std::vector<uint8_t> get_opt_bin(const std::string& key) const;
847

848
      std::vector<std::vector<uint8_t>> get_req_bin_list(const std::string& key) const;
849

850
#if defined(BOTAN_HAS_BIGINT)
851
      Botan::BigInt get_req_bn(const std::string& key) const;
852
      Botan::BigInt get_opt_bn(const std::string& key, const Botan::BigInt& def_value) const;
853
#endif
854

855
      std::string get_req_str(const std::string& key) const;
856
      std::string get_opt_str(const std::string& key, const std::string& def_value) const;
857

858
      size_t get_req_sz(const std::string& key) const;
859

860
      uint8_t get_req_u8(const std::string& key) const;
861
      uint32_t get_req_u32(const std::string& key) const;
862
      uint64_t get_req_u64(const std::string& key) const;
863

864
      size_t get_opt_sz(const std::string& key, size_t def_value) const;
865

866
      uint64_t get_opt_u64(const std::string& key, uint64_t def_value) const;
867

868
   private:
869
      std::unordered_map<std::string, std::string> m_vars;
870
};
871

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

893
      virtual bool clear_between_callbacks() const { return true; }
32,852✔
894

895
      std::vector<Test::Result> run() override;
896

897
   protected:
898
      std::string get_next_line();
899

900
      virtual Test::Result run_one_test(const std::string& header, const VarMap& vars) = 0;
901
      // Called before run_one_test
902
      virtual bool skip_this_test(const std::string& header, const VarMap& vars);
903

904
      virtual std::vector<Test::Result> run_final_tests() { return std::vector<Test::Result>(); }
170✔
905

906
   private:
907
      std::string m_data_src;
908
      std::set<std::string> m_required_keys;
909
      std::set<std::string> m_optional_keys;
910
      std::string m_output_key;
911

912
      bool m_first = true;
913
      std::unique_ptr<std::istream> m_cur;
914
      std::string m_cur_src_name;
915
      std::deque<std::string> m_srcs;
916
      std::vector<std::string> m_cpu_flags;
917
};
918

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

950
   try {
951
      check_fun(r);
508✔
952
   } catch(const Botan_Tests::Test_Aborted&) {
×
953
      // pass, failure was already noted in the responsible `require`
954
   } catch(const std::exception& ex) {
×
955
      r.test_failure("failed unexpectedly", ex.what());
×
956
   } catch(...) {
×
957
      r.test_failure("failed with unknown exception");
×
958
   }
959

960
   return r;
507✔
961
}
×
962

963
}  // namespace Botan_Tests
964

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