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

randombit / botan / 11214575493

07 Oct 2024 11:30AM UTC coverage: 91.002% (-0.3%) from 91.28%
11214575493

push

github

web-flow
Merge pull request #4337 from Rohde-Schwarz/feature/tpm2_support

Feature: TPM2 Support

89410 of 98251 relevant lines covered (91.0%)

8897349.05 hits per line

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

89.92
/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 <functional>
17
#include <iosfwd>
18
#include <map>
19
#include <memory>
20
#include <optional>
21
#include <ranges>
22
#include <set>
23
#include <sstream>
24
#include <string>
25
#include <typeindex>
26
#include <unordered_map>
27
#include <variant>
28
#include <vector>
29

30
namespace Botan {
31

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

36
#if defined(BOTAN_HAS_EC_CURVE_GFP)
37
class EC_Point;
38
#endif
39

40
}  // namespace Botan
41

42
namespace Botan_Tests {
43

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

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

52
      Botan::ErrorType error_type() const noexcept override { return Botan::ErrorType::Unknown; }
×
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.begin(), skip_tests.end()),
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::set<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); }
126

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

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

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

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

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

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

143
      bool log_success() const { return m_log_success; }
3,144,014✔
144

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

147
      bool run_long_tests() const { return m_run_long_tests; }
2,451✔
148

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

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

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

155
      bool verbose() const { return m_verbose; }
2,107✔
156

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

182
namespace detail {
183

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

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

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

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

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

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

210
}  // namespace detail
211

212
/**
213
 * A code location consisting of the source file path and a line
214
 */
215
struct CodeLocation {
243,378✔
216
      std::string path;
217
      unsigned int line;
218
};
219

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

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

242
            size_t tests_passed() const { return m_tests_passed; }
10,417✔
243

244
            size_t tests_failed() const { return m_fail_log.size(); }
64,364✔
245

246
            size_t tests_run() const { return tests_passed() + tests_failed(); }
10,417✔
247

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

250
            const std::string& who() const { return m_who; }
214,866✔
251

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

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

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

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

266
            std::string result_string() const;
267

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

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

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

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

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

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

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

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

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

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

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

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

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

321
            bool confirm(const std::string& what, bool expr, bool expected = true) {
146,445✔
322
               return test_eq(what, expr, expected);
146,180✔
323
            }
324

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

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

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

345
               if(produced == expected) {
583,695✔
346
                  out << " produced expected result";
583,702✔
347
                  return test_success(out.str());
583,702✔
348
               } else {
349
                  out << " produced unexpected result '" << to_string(produced) << "' expected '" << to_string(expected)
350
                      << "'";
7✔
351
                  return test_failure(out.str());
2✔
352
               }
353
            }
583,704✔
354

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

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

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

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

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

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

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

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

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

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

398
            bool test_lt(const std::string& what, size_t produced, size_t expected);
399
            bool test_lte(const std::string& what, size_t produced, size_t expected);
400
            bool test_gt(const std::string& what, size_t produced, size_t expected);
401
            bool test_gte(const std::string& what, size_t produced, size_t expected);
402

403
            template <typename T>
404
            bool test_rc_ok(const std::string& func, T rc) {
1,139✔
405
               static_assert(std::is_integral<T>::value, "Integer required.");
406

407
               if(rc != 0) {
1,139✔
408
                  std::ostringstream err;
1✔
409
                  err << m_who;
1✔
410
                  err << " " << func;
1✔
411
                  err << " unexpectedly failed with error code " << rc;
1✔
412
                  return test_failure(err.str());
1✔
413
               }
1✔
414

415
               return test_success();
1,138✔
416
            }
417

418
            template <typename T>
419
            bool test_rc_fail(const std::string& func, const std::string& why, T rc) {
23✔
420
               static_assert(std::is_integral<T>::value, "Integer required.");
421

422
               if(rc == 0) {
23✔
423
                  std::ostringstream err;
1✔
424
                  err << m_who;
1✔
425
                  err << " call to " << func << " unexpectedly succeeded";
1✔
426
                  err << " expecting failure because " << why;
1✔
427
                  return test_failure(err.str());
1✔
428
               }
1✔
429

430
               return test_success();
22✔
431
            }
432

433
            bool test_rc(const std::string& func, int expected, int rc);
434

435
            bool test_rc_init(const std::string& func, int rc);
436

437
            bool test_ne(const std::string& what, size_t produced, size_t expected);
438

439
            bool test_ne(const std::string& what, const std::string& str1, const std::string& str2);
440

441
#if defined(BOTAN_HAS_BIGINT)
442
            bool test_eq(const std::string& what, const BigInt& produced, const BigInt& expected);
443
            bool test_ne(const std::string& what, const BigInt& produced, const BigInt& expected);
444
#endif
445

446
#if defined(BOTAN_HAS_EC_CURVE_GFP)
447
            bool test_eq(const std::string& what, const Botan::EC_Point& a, const Botan::EC_Point& b);
448
#endif
449

450
            bool test_eq(const char* producer,
451
                         const std::string& what,
452
                         const uint8_t produced[],
453
                         size_t produced_len,
454
                         const uint8_t expected[],
455
                         size_t expected_len);
456

457
            bool test_ne(const std::string& what,
458
                         const uint8_t produced[],
459
                         size_t produced_len,
460
                         const uint8_t expected[],
461
                         size_t expected_len);
462

463
            template <typename Alloc1, typename Alloc2>
464
            bool test_eq(const std::string& what,
51,500✔
465
                         const std::vector<uint8_t, Alloc1>& produced,
466
                         const std::vector<uint8_t, Alloc2>& expected) {
467
               return test_eq(nullptr, what, produced.data(), produced.size(), expected.data(), expected.size());
51,500✔
468
            }
469

470
            template <typename Alloc1, typename Alloc2>
471
            bool test_eq(const std::string& producer,
104,194✔
472
                         const std::string& what,
473
                         const std::vector<uint8_t, Alloc1>& produced,
474
                         const std::vector<uint8_t, Alloc2>& expected) {
475
               return test_eq(
104,194✔
476
                  producer.c_str(), what, produced.data(), produced.size(), expected.data(), expected.size());
104,194✔
477
            }
478

479
            template <typename Alloc>
480
            bool test_eq(const std::string& what,
401✔
481
                         const std::vector<uint8_t, Alloc>& produced,
482
                         const char* expected_hex) {
483
               const std::vector<uint8_t> expected = Botan::hex_decode(expected_hex);
401✔
484
               return test_eq(nullptr, what, produced.data(), produced.size(), expected.data(), expected.size());
401✔
485
            }
401✔
486

487
            template <typename Alloc1, typename Alloc2>
488
            bool test_ne(const std::string& what,
2,031✔
489
                         const std::vector<uint8_t, Alloc1>& produced,
490
                         const std::vector<uint8_t, Alloc2>& expected) {
491
               return test_ne(what, produced.data(), produced.size(), expected.data(), expected.size());
2,031✔
492
            }
493

494
         private:
495
            class ThrowExpectations {
496
               public:
497
                  ThrowExpectations(std::function<void()> fn) :
47,814✔
498
                        m_fn(std::move(fn)), m_expect_success(false), m_consumed(false) {}
95,628✔
499

500
                  ThrowExpectations(const ThrowExpectations&) = delete;
501
                  ThrowExpectations& operator=(const ThrowExpectations&) = delete;
502
                  ThrowExpectations(ThrowExpectations&&) = default;
503
                  ThrowExpectations& operator=(ThrowExpectations&&) = default;
504

505
                  ~ThrowExpectations() { BOTAN_ASSERT_NOMSG(m_consumed); }
47,997✔
506

507
                  ThrowExpectations& expect_success() {
1,639✔
508
                     BOTAN_ASSERT_NOMSG(!m_expected_message && !m_expected_exception_type);
1,639✔
509
                     m_expect_success = true;
1,639✔
510
                     return *this;
1,639✔
511
                  }
512

513
                  ThrowExpectations& expect_message(const std::string& message) {
183✔
514
                     BOTAN_ASSERT_NOMSG(!m_expect_success);
183✔
515
                     m_expected_message = message;
183✔
516
                     return *this;
183✔
517
                  }
518

519
                  template <typename ExT>
520
                  ThrowExpectations& expect_exception_type() {
164✔
521
                     BOTAN_ASSERT_NOMSG(!m_expect_success);
164✔
522
                     m_expected_exception_type = typeid(ExT);
164✔
523
                     return *this;
164✔
524
                  }
525

526
                  bool check(const std::string& test_name, Test::Result& result);
527

528
               private:
529
                  std::function<void()> m_fn;
530
                  bool m_expect_success;
531
                  std::optional<std::string> m_expected_message;
532
                  std::optional<std::type_index> m_expected_exception_type;
533
                  bool m_consumed;
534
            };
535

536
         public:
537
            bool test_throws(const std::string& what, const std::function<void()>& fn);
538

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

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

543
            template <typename ExceptionT>
544
            bool test_throws(const std::string& what, const std::function<void()>& fn) {
149✔
545
               return ThrowExpectations(fn).expect_exception_type<ExceptionT>().check(what, *this);
298✔
546
            }
547

548
            template <typename ExceptionT>
549
            bool test_throws(const std::string& what, const std::string& expected, const std::function<void()>& fn) {
15✔
550
               return ThrowExpectations(fn).expect_exception_type<ExceptionT>().expect_message(expected).check(what,
30✔
551
                                                                                                               *this);
15✔
552
            }
553

554
            void set_ns_consumed(uint64_t ns) { m_ns_taken = ns; }
45,789✔
555

556
            void start_timer();
557
            void end_timer();
558

559
            void set_code_location(CodeLocation where) { m_where = std::move(where); }
47,663✔
560

561
            const std::optional<CodeLocation>& code_location() const { return m_where; }
49,744✔
562

563
         private:
564
            template <typename T>
565
            std::string to_string(const T& v) {
2✔
566
               if constexpr(detail::is_optional_v<T>) {
567
                  return (v.has_value()) ? to_string(v.value()) : std::string("std::nullopt");
×
568
               } else if constexpr(detail::has_Botan_to_string<T>) {
569
                  return Botan::to_string(v);
570
               } else if constexpr(detail::has_ostream_operator<T>) {
571
                  std::ostringstream oss;
2✔
572
                  oss << v;
2✔
573
                  return oss.str();
2✔
574
               } else if constexpr(detail::has_std_to_string<T>) {
2✔
575
                  return std::to_string(v);
2✔
576
               } else {
577
                  return "<?>";
×
578
               }
579
            }
580

581
         private:
582
            std::string m_who;
583
            std::optional<CodeLocation> m_where;
584
            std::chrono::system_clock::time_point m_timestamp;
585
            uint64_t m_started = 0;
586
            uint64_t m_ns_taken = 0;
587
            size_t m_tests_passed = 0;
588
            std::vector<std::string> m_fail_log;
589
            std::vector<std::string> m_log;
590
      };
591

592
      virtual ~Test() = default;
855✔
593
      virtual std::vector<Test::Result> run() = 0;
594

595
      virtual std::vector<std::string> possible_providers(const std::string&);
596

597
      void initialize(std::string test_name, CodeLocation location);
598

599
      const std::string& test_name() const { return m_test_name; }
18✔
600

601
      Botan::RandomNumberGenerator& rng() const;
602

603
      const std::optional<CodeLocation>& registration_location() const { return m_registration_location; }
47,663✔
604

605
      /// @p smoke_test are run first in an unfiltered test run
606
      static void register_test(const std::string& category,
607
                                const std::string& name,
608
                                bool smoke_test,
609
                                bool needs_serialization,
610
                                std::function<std::unique_ptr<Test>()> maker_fn);
611

612
      static std::set<std::string> registered_tests();
613
      static std::set<std::string> registered_test_categories();
614
      static std::vector<std::string> filter_registered_tests(const std::vector<std::string>& requested,
615
                                                              const std::set<std::string>& to_be_skipped);
616

617
      static std::unique_ptr<Test> get_test(const std::string& test_name);
618
      static bool test_needs_serialization(const std::string& test_name);
619

620
      static std::string data_dir(const std::string& subdir);
621
      static std::vector<std::string> files_in_data_dir(const std::string& subdir);
622
      static std::string data_file(const std::string& what);
623
      static std::string data_file_as_temporary_copy(const std::string& what);
624

625
      static std::string format_time(uint64_t nanoseconds);
626

627
      static std::string format_time(const std::chrono::nanoseconds nanoseconds) {
1✔
628
         return format_time(nanoseconds.count());
1✔
629
      }
630

631
      template <typename Alloc>
632
      static std::vector<uint8_t, Alloc> mutate_vec(const std::vector<uint8_t, Alloc>& v,
74,762✔
633
                                                    Botan::RandomNumberGenerator& rng,
634
                                                    bool maybe_resize = false,
635
                                                    size_t min_offset = 0) {
636
         std::vector<uint8_t, Alloc> r = v;
74,762✔
637

638
         if(maybe_resize && (r.empty() || rng.next_byte() < 32)) {
84,444✔
639
            // TODO: occasionally truncate, insert at random index
640
            const size_t add = 1 + (rng.next_byte() % 16);
1,678✔
641
            r.resize(r.size() + add);
1,678✔
642
            rng.randomize(&r[r.size() - add], add);
1,678✔
643
         }
644

645
         if(r.size() > min_offset) {
74,762✔
646
            const size_t offset = std::max<size_t>(min_offset, rng.next_byte() % r.size());
73,362✔
647
            const uint8_t perturb = rng.next_nonzero_byte();
73,362✔
648
            r[offset] ^= perturb;
73,362✔
649
         }
650

651
         return r;
74,762✔
652
      }
×
653

654
      static void set_test_options(const Test_Options& opts);
655

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

658
      static const Test_Options& options() { return m_opts; }
1✔
659

660
      static bool run_long_tests() { return options().run_long_tests(); }
2,451✔
661

662
      static bool run_memory_intensive_tests() { return options().run_memory_intensive_tests(); }
1✔
663

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

666
      static std::string temp_file_name(const std::string& basename);
667
      static bool copy_file(const std::string& from, const std::string& to);
668

669
      static std::vector<std::string> provider_filter(const std::vector<std::string>& providers);
670

671
      static std::string read_data_file(const std::string& path);
672
      static std::vector<uint8_t> read_binary_data_file(const std::string& path);
673

674
      static std::unique_ptr<Botan::RandomNumberGenerator> new_rng(std::string_view test_name);
675
      static std::shared_ptr<Botan::RandomNumberGenerator> new_shared_rng(std::string_view test_name);
676

677
      static std::string random_password(Botan::RandomNumberGenerator& rng);
678
      static uint64_t timestamp();  // nanoseconds arbitrary epoch
679

680
      static std::vector<Test::Result> flatten_result_lists(std::vector<std::vector<Test::Result>> result_lists);
681

682
   private:
683
      static Test_Options m_opts;
684
      static std::string m_test_rng_seed;
685

686
      /// The string ID that was used to register this test
687
      std::string m_test_name;
688
      /// The source file location where the test was registered
689
      std::optional<CodeLocation> m_registration_location;
690
      /// The test-specific RNG state
691
      mutable std::unique_ptr<Botan::RandomNumberGenerator> m_test_rng;
692
};
693

694
/*
695
* Register the test with the runner
696
*/
697
template <typename Test_Class>
698
class TestClassRegistration {
699
   public:
700
      TestClassRegistration(const std::string& category,
335✔
701
                            const std::string& name,
702
                            bool smoke_test,
703
                            bool needs_serialization,
704
                            CodeLocation registration_location) {
705
         Test::register_test(category, name, smoke_test, needs_serialization, [=] {
1,296✔
706
            auto test = std::make_unique<Test_Class>();
335✔
707
            test->initialize(name, registration_location);
1,005✔
708
            return test;
335✔
709
         });
×
710
      }
335✔
711
};
712

713
#define BOTAN_REGISTER_TEST(category, name, Test_Class) \
714
   const TestClassRegistration<Test_Class> reg_##Test_Class##_tests(category, name, false, false, {__FILE__, __LINE__})
715
#define BOTAN_REGISTER_SERIALIZED_TEST(category, name, Test_Class) \
716
   const TestClassRegistration<Test_Class> reg_##Test_Class##_tests(category, name, false, true, {__FILE__, __LINE__})
717
#define BOTAN_REGISTER_SMOKE_TEST(category, name, Test_Class) \
718
   const TestClassRegistration<Test_Class> reg_##Test_Class##_tests(category, name, true, false, {__FILE__, __LINE__})
719
#define BOTAN_REGISTER_SERIALIZED_SMOKE_TEST(category, name, Test_Class) \
720
   const TestClassRegistration<Test_Class> reg_##Test_Class##_tests(category, name, true, true, {__FILE__, __LINE__})
721

722
typedef Test::Result (*test_fn)();
723
typedef std::vector<Test::Result> (*test_fn_vec)();
724

725
class FnTest : public Test {
726
   private:
727
      using TestFnVariant = std::variant<test_fn, test_fn_vec>;
728

729
      template <typename TestFn>
730
      std::vector<TestFnVariant> make_variant_vector(TestFn fn) {
31✔
731
         using T = std::decay_t<decltype(fn)>;
732
         static_assert(std::is_same_v<T, test_fn> || std::is_same_v<T, test_fn_vec>,
733
                       "functions passed to BOTAN_REGISTER_TEST_FN must either return a "
734
                       "single Test::Result or a std::vector of Test::Result");
735
         return {fn};
31✔
736
      }
737

738
      template <typename TestFn, typename... TestFns>
739
      std::vector<TestFnVariant> make_variant_vector(const TestFn& fn, const TestFns&... fns) {
30✔
740
         auto functions = make_variant_vector(fns...);
30✔
741
         functions.emplace_back(fn);
30✔
742
         return functions;
30✔
743
      }
×
744

745
   public:
746
      template <typename... TestFns>
747
      FnTest(TestFns... fns) : m_fns(make_variant_vector(fns...)) {}
31✔
748

749
      std::vector<Test::Result> run() override {
31✔
750
         std::vector<Test::Result> result;
31✔
751

752
         for(auto fn_variant = m_fns.crbegin(); fn_variant != m_fns.crend(); ++fn_variant) {
92✔
753
            std::visit(
61✔
754
               [&](auto&& fn) {
122✔
755
                  using T = std::decay_t<decltype(fn)>;
756
                  if constexpr(std::is_same_v<T, test_fn>) {
757
                     result.emplace_back(fn());
7✔
758
                  } else {
759
                     const auto results = fn();
54✔
760
                     result.insert(result.end(), results.begin(), results.end());
54✔
761
                  }
54✔
762
               },
61✔
763
               *fn_variant);
61✔
764
         }
765

766
         return result;
31✔
767
      }
×
768

769
   private:
770
      std::vector<TestFnVariant> m_fns;
771
};
772

773
class TestFnRegistration {
774
   public:
775
      template <typename... TestFns>
776
      TestFnRegistration(const std::string& category,
31✔
777
                         const std::string& name,
778
                         bool smoke_test,
779
                         bool needs_serialization,
780
                         CodeLocation registration_location,
781
                         TestFns... fn) {
782
         Test::register_test(category, name, smoke_test, needs_serialization, [=] {
93✔
783
            auto test = std::make_unique<FnTest>(fn...);
31✔
784
            test->initialize(name, registration_location);
93✔
785
            return test;
31✔
786
         });
×
787
      }
31✔
788
};
789

790
#define BOTAN_REGISTER_TEST_FN_IMPL(category, name, smoke_test, needs_serialization, fn0, ...) \
791
   static const TestFnRegistration register_##fn0(                                             \
792
      category, name, smoke_test, needs_serialization, {__FILE__, __LINE__}, fn0 __VA_OPT__(, ) __VA_ARGS__)
793

794
#define BOTAN_REGISTER_TEST_FN(category, name, ...) \
795
   BOTAN_REGISTER_TEST_FN_IMPL(category, name, false, false, __VA_ARGS__)
796
#define BOTAN_REGISTER_SMOKE_TEST_FN(category, name, ...) \
797
   BOTAN_REGISTER_TEST_FN_IMPL(category, name, true, false, __VA_ARGS__)
798
#define BOTAN_REGISTER_SERIALIZED_TEST_FN(category, name, ...) \
799
   BOTAN_REGISTER_TEST_FN_IMPL(category, name, false, true, __VA_ARGS__)
800
#define BOTAN_REGISTER_SERIALIZED_SMOKE_TEST_FN(category, name, ...) \
801
   BOTAN_REGISTER_TEST_FN_IMPL(category, name, true, true, __VA_ARGS__)
802

803
class VarMap {
320✔
804
   public:
805
      void clear() { m_vars.clear(); }
31,954✔
806

807
      void add(const std::string& key, const std::string& value) { m_vars[key] = value; }
144,684✔
808

809
      bool has_key(const std::string& key) const { return m_vars.count(key) == 1; }
403,041✔
810

811
      bool get_req_bool(const std::string& key) const;
812

813
      std::vector<uint8_t> get_req_bin(const std::string& key) const;
814
      std::vector<uint8_t> get_opt_bin(const std::string& key) const;
815

816
      std::vector<std::vector<uint8_t>> get_req_bin_list(const std::string& key) const;
817

818
#if defined(BOTAN_HAS_BIGINT)
819
      Botan::BigInt get_req_bn(const std::string& key) const;
820
      Botan::BigInt get_opt_bn(const std::string& key, const Botan::BigInt& def_value) const;
821
#endif
822

823
      std::string get_req_str(const std::string& key) const;
824
      std::string get_opt_str(const std::string& key, const std::string& def_value) const;
825

826
      size_t get_req_sz(const std::string& key) const;
827

828
      uint8_t get_req_u8(const std::string& key) const;
829
      uint32_t get_req_u32(const std::string& key) const;
830
      uint64_t get_req_u64(const std::string& key) const;
831

832
      size_t get_opt_sz(const std::string& key, size_t def_value) const;
833

834
      uint64_t get_opt_u64(const std::string& key, uint64_t def_value) const;
835

836
   private:
837
      std::unordered_map<std::string, std::string> m_vars;
838
};
839

840
/*
841
* A test based on reading an input file which contains key/value pairs
842
* Special note: the last value in required_key (there must be at least
843
* one), is the output key. This triggers the callback.
844
*
845
* Calls run_one_test with the variables set. If an ini-style [header]
846
* is used in the file, then header will be set to that value. This allows
847
* splitting up tests between [valid] and [invalid] tests, or different
848
* related algorithms tested in the same file. Use the get_XXX functions
849
* on VarMap to retrieve formatted values.
850
*
851
* If most of your tests are text-based but you find yourself with a few
852
* odds-and-ends tests that you want to do, override run_final_tests which
853
* can test whatever it likes and returns a vector of Results.
854
*/
855
class Text_Based_Test : public Test {
856
   public:
857
      Text_Based_Test(const std::string& input_file,
858
                      const std::string& required_keys,
859
                      const std::string& optional_keys = "");
860

861
      virtual bool clear_between_callbacks() const { return true; }
31,280✔
862

863
      std::vector<Test::Result> run() override;
864

865
   protected:
866
      std::string get_next_line();
867

868
      virtual Test::Result run_one_test(const std::string& header, const VarMap& vars) = 0;
869
      // Called before run_one_test
870
      virtual bool skip_this_test(const std::string& header, const VarMap& vars);
871

872
      virtual std::vector<Test::Result> run_final_tests() { return std::vector<Test::Result>(); }
151✔
873

874
   private:
875
      std::string m_data_src;
876
      std::set<std::string> m_required_keys;
877
      std::set<std::string> m_optional_keys;
878
      std::string m_output_key;
879

880
      bool m_first = true;
881
      std::unique_ptr<std::istream> m_cur;
882
      std::string m_cur_src_name;
883
      std::deque<std::string> m_srcs;
884
      std::vector<uint64_t> m_cpu_flags;
885
};
886

887
/**
888
 * This is a convenience wrapper to write small self-contained and in particular
889
 * exception-safe unit tests. If some (unexpected) exception is thrown in one of
890
 * the CHECK-scopes, it will fail the particular test gracefully with a human-
891
 * understandable failure output.
892
 *
893
 * Example Usage:
894
 *
895
 * ```
896
 * std::vector<Test::Result> test_something()
897
 *    {
898
 *    return
899
 *       {
900
 *       CHECK("some unit test name", [](Test::Result& r)
901
 *          {
902
 *          r.confirm("some observation", 1+1 == 2);
903
 *          }),
904
 *       CHECK("some other unit test name", [](Test::Result& r)
905
 *          {
906
 *          // ...
907
 *          })
908
 *       };
909
 *    }
910
 *
911
 * BOTAN_REGISTER_TEST_FN("some_category", "some_test_name", test_something);
912
 * ```
913
 */
914
template <typename FunT>
915
Test::Result CHECK(const char* name, FunT check_fun) {
433✔
916
   Botan_Tests::Test::Result r(name);
433✔
917

918
   try {
919
      check_fun(r);
433✔
920
   } catch(const Botan_Tests::Test_Aborted&) {
×
921
      // pass, failure was already noted in the responsible `require`
922
   } catch(const std::exception& ex) {
×
923
      r.test_failure("failed unexpectedly", ex.what());
×
924
   } catch(...) {
×
925
      r.test_failure("failed with unknown exception");
×
926
   }
927

928
   return r;
432✔
929
}
×
930

931
}  // namespace Botan_Tests
932

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