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

randombit / botan / 5696981105

28 Jul 2023 12:55PM UTC coverage: 91.69% (+0.005%) from 91.685%
5696981105

push

github

randombit
Merge GH #3649 Remove some macros from loadstor.h

78267 of 85360 relevant lines covered (91.69%)

12322548.38 hits per line

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

89.22
/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/rng.h>
13
#include <botan/symkey.h>
14
#include <botan/types.h>
15
#include <functional>
16
#include <iosfwd>
17
#include <map>
18
#include <memory>
19
#include <optional>
20
#include <set>
21
#include <sstream>
22
#include <string>
23
#include <typeindex>
24
#include <unordered_map>
25
#include <variant>
26
#include <vector>
27

28
namespace Botan {
29

30
#if defined(BOTAN_HAS_BIGINT)
31
class BigInt;
32
#endif
33

34
#if defined(BOTAN_HAS_EC_CURVE_GFP)
35
class EC_Point;
36
#endif
37

38
}  // namespace Botan
39

40
namespace Botan_Tests {
41

42
#if defined(BOTAN_HAS_BIGINT)
43
using Botan::BigInt;
44
#endif
45

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

50
      Botan::ErrorType error_type() const noexcept override { return Botan::ErrorType::Unknown; }
×
51
};
52

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

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

62
      Test_Options(const std::vector<std::string>& requested_tests,
1✔
63
                   const std::vector<std::string>& skip_tests,
64
                   const std::string& data_dir,
65
                   const std::string& pkcs11_lib,
66
                   const std::string& provider,
67
                   const std::string& drbg_seed,
68
                   const std::string& xml_results_dir,
69
                   const std::vector<std::string>& report_properties,
70
                   size_t test_runs,
71
                   size_t test_threads,
72
                   bool verbose,
73
                   bool log_success,
74
                   bool run_online_tests,
75
                   bool run_long_tests,
76
                   bool run_memory_intensive_tests,
77
                   bool abort_on_first_fail) :
1✔
78
            m_requested_tests(requested_tests),
1✔
79
            m_skip_tests(skip_tests.begin(), skip_tests.end()),
1✔
80
            m_data_dir(data_dir),
1✔
81
            m_pkcs11_lib(pkcs11_lib),
1✔
82
            m_provider(provider),
1✔
83
            m_drbg_seed(drbg_seed),
1✔
84
            m_xml_results_dir(xml_results_dir),
1✔
85
            m_report_properties(report_properties),
1✔
86
            m_test_runs(test_runs),
1✔
87
            m_test_threads(test_threads),
1✔
88
            m_verbose(verbose),
1✔
89
            m_log_success(log_success),
1✔
90
            m_run_online_tests(run_online_tests),
1✔
91
            m_run_long_tests(run_long_tests),
1✔
92
            m_run_memory_intensive_tests(run_memory_intensive_tests),
1✔
93
            m_abort_on_first_fail(abort_on_first_fail) {}
1✔
94

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

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

99
      const std::string& data_dir() const { return m_data_dir; }
100

101
      const std::string& pkcs11_lib() const { return m_pkcs11_lib; }
3✔
102

103
      const std::string& provider() const { return m_provider; }
2✔
104

105
      const std::string& drbg_seed() const { return m_drbg_seed; }
1✔
106

107
      const std::string& xml_results_dir() const { return m_xml_results_dir; }
1✔
108

109
      std::map<std::string, std::string> report_properties() const;
110

111
      size_t test_runs() const { return m_test_runs; }
4✔
112

113
      size_t test_threads() const { return m_test_threads; }
3✔
114

115
      bool log_success() const { return m_log_success; }
2,986,044✔
116

117
      bool run_online_tests() const { return m_run_online_tests; }
2✔
118

119
      bool run_long_tests() const { return m_run_long_tests; }
2,340✔
120

121
      bool run_memory_intensive_tests() const { return m_run_memory_intensive_tests; }
1✔
122

123
      bool abort_on_first_fail() const { return m_abort_on_first_fail; }
25✔
124

125
      bool verbose() const { return m_verbose; }
1,705✔
126

127
   private:
128
      std::vector<std::string> m_requested_tests;
129
      std::set<std::string> m_skip_tests;
130
      std::string m_data_dir;
131
      std::string m_pkcs11_lib;
132
      std::string m_provider;
133
      std::string m_drbg_seed;
134
      std::string m_xml_results_dir;
135
      std::vector<std::string> m_report_properties;
136
      size_t m_test_runs;
137
      size_t m_test_threads;
138
      bool m_verbose;
139
      bool m_log_success;
140
      bool m_run_online_tests;
141
      bool m_run_long_tests;
142
      bool m_run_memory_intensive_tests;
143
      bool m_abort_on_first_fail;
144
};
145

146
namespace detail {
147

148
template <typename, typename = void>
149
constexpr bool has_Botan_to_string = false;
150
template <typename T>
151
constexpr bool has_Botan_to_string<T, std::void_t<decltype(Botan::to_string(std::declval<T>()))>> = true;
152

153
template <typename, typename = void>
154
constexpr bool has_std_to_string = false;
155
template <typename T>
156
constexpr bool has_std_to_string<T, std::void_t<decltype(std::to_string(std::declval<T>()))>> = true;
157

158
template <typename, typename = void>
159
constexpr bool has_ostream_operator = false;
160
template <typename T>
161
constexpr bool
162
   has_ostream_operator<T, std::void_t<decltype(operator<<(std::declval<std::ostringstream&>(), std::declval<T>()))>> =
163
      true;
164

165
template <typename T>
166
struct is_optional : std::false_type {};
167

168
template <typename T>
169
struct is_optional<std::optional<T>> : std::true_type {};
170

171
template <typename T>
172
constexpr bool is_optional_v = is_optional<T>::value;
173

174
}  // namespace detail
175

176
/**
177
 * A code location consisting of the source file path and a line
178
 */
179
struct CodeLocation {
470,938✔
180
      std::string path;
181
      unsigned int line;
182
};
183

184
/*
185
* A generic test which returns a set of results when run.
186
* The tests may not all have the same type (for example test
187
* "block" returns results for "AES-128" and "AES-256").
188
*
189
* For most test cases you want Text_Based_Test derived below
190
*/
191
class Test {
319✔
192
   public:
193
      /*
194
      * Some number of test results, all associated with who()
195
      */
196
      class Result final {
197
         public:
198
            explicit Result(std::string who) : m_who(std::move(who)), m_timestamp(std::chrono::system_clock::now()) {}
126,172✔
199

200
            /**
201
             * This 'consolidation constructor' creates a single test result from
202
             * a vector of downstream test result objects.
203
             */
204
            Result(std::string who, const std::vector<Result>& downstream_results);
205

206
            size_t tests_passed() const { return m_tests_passed; }
8,419✔
207

208
            size_t tests_failed() const { return m_fail_log.size(); }
60,392✔
209

210
            size_t tests_run() const { return tests_passed() + tests_failed(); }
8,419✔
211

212
            bool any_results() const { return tests_run() > 0; }
213

214
            const std::string& who() const { return m_who; }
201,442✔
215

216
            const std::vector<std::string>& failures() const { return m_fail_log; }
1,679✔
217

218
            const std::vector<std::string>& notes() const { return m_log; }
1,679✔
219

220
            std::optional<std::chrono::nanoseconds> elapsed_time() const {
1,679✔
221
               if(m_ns_taken == 0) {
1,679✔
222
                  return std::nullopt;
223
               } else {
224
                  return std::chrono::nanoseconds(m_ns_taken);
996✔
225
               }
226
            }
227

228
            const std::chrono::system_clock::time_point& timestamp() const { return m_timestamp; }
1,679✔
229

230
            std::string result_string() const;
231

232
            static Result Failure(const std::string& who, const std::string& what) {
1✔
233
               Result r(who);
1✔
234
               r.test_failure(what);
1✔
235
               return r;
1✔
236
            }
×
237

238
            static Result Note(const std::string& who, const std::string& what) {
×
239
               Result r(who);
×
240
               r.test_note(what);
×
241
               return r;
×
242
            }
×
243

244
            static Result OfExpectedFailure(bool expecting_failure, const Test::Result& result) {
245
               if(!expecting_failure) {
246
                  return result;
247
               }
248

249
               if(result.tests_failed() == 0) {
250
                  Result r = result;
251
                  r.test_failure("Expected this test to fail, but it did not");
252
                  return r;
253
               } else {
254
                  Result r(result.who());
255
                  r.test_note("Got expected failure");
256
                  return r;
257
               }
258
            }
259

260
            void merge(const Result& other, bool ignore_test_name = false);
261

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

264
            template <typename Alloc>
265
            void test_note(const std::string& who, const std::vector<uint8_t, Alloc>& vec) {
12✔
266
               const std::string hex = Botan::hex_encode(vec);
12✔
267
               return test_note(who, hex.c_str());
12✔
268
            }
12✔
269

270
            void note_missing(const std::string& thing);
271

272
            bool test_success(const std::string& note = "");
273

274
            bool test_failure(const std::string& err);
275

276
            bool test_failure(const std::string& what, const std::string& error);
277

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

280
            template <typename Alloc>
281
            void test_failure(const std::string& what, const std::vector<uint8_t, Alloc>& buf) {
1✔
282
               test_failure(what, buf.data(), buf.size());
1✔
283
            }
1✔
284

285
            bool confirm(const std::string& what, bool expr, bool expected = true) {
125,493✔
286
               return test_eq(what, expr, expected);
125,341✔
287
            }
288

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

299
            template <typename T>
300
            bool test_is_eq(const T& produced, const T& expected) {
230✔
301
               return test_is_eq("comparison", produced, expected);
230✔
302
            }
303

304
            template <typename T>
305
            bool test_is_eq(const std::string& what, const T& produced, const T& expected) {
479,924✔
306
               std::ostringstream out;
479,924✔
307
               out << m_who << " " << what;
479,924✔
308

309
               if(produced == expected) {
479,915✔
310
                  out << " produced expected result";
479,922✔
311
                  return test_success(out.str());
959,844✔
312
               } else {
313
                  out << " produced unexpected result '" << to_string(produced) << "' expected '" << to_string(expected)
314
                      << "'";
7✔
315
                  return test_failure(out.str());
4✔
316
               }
317
            }
479,924✔
318

319
            template <typename T>
320
            bool test_not_null(const std::string& what, const T& ptr) {
25✔
321
               if(ptr == nullptr) {
25✔
322
                  return test_failure(what + " was null");
×
323
               } else {
324
                  return test_success(what + " was not null");
50✔
325
               }
326
            }
327

328
            template <typename T>
329
            bool test_not_nullopt(const std::string& what, std::optional<T> val) {
9✔
330
               if(val == std::nullopt) {
9✔
331
                  return test_failure(what + " was nullopt");
×
332
               } else {
333
                  return test_success(what + " was not nullopt");
18✔
334
               }
335
            }
336

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

339
            bool test_is_nonempty(const std::string& what_is_it, const std::string& to_examine);
340

341
            bool test_eq(const std::string& what, const std::string& produced, const std::string& expected);
342

343
            bool test_eq(const std::string& what, bool produced, bool expected);
344

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

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

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

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

362
            bool test_lt(const std::string& what, size_t produced, size_t expected);
363
            bool test_lte(const std::string& what, size_t produced, size_t expected);
364
            bool test_gt(const std::string& what, size_t produced, size_t expected);
365
            bool test_gte(const std::string& what, size_t produced, size_t expected);
366

367
            template <typename T>
368
            bool test_rc_ok(const std::string& func, T rc) {
898✔
369
               static_assert(std::is_integral<T>::value, "Integer required.");
370

371
               if(rc != 0) {
898✔
372
                  std::ostringstream err;
1✔
373
                  err << m_who;
1✔
374
                  err << " " << func;
1✔
375
                  err << " unexpectedly failed with error code " << rc;
1✔
376
                  return test_failure(err.str());
1✔
377
               }
1✔
378

379
               return test_success();
897✔
380
            }
381

382
            template <typename T>
383
            bool test_rc_fail(const std::string& func, const std::string& why, T rc) {
22✔
384
               static_assert(std::is_integral<T>::value, "Integer required.");
385

386
               if(rc == 0) {
22✔
387
                  std::ostringstream err;
1✔
388
                  err << m_who;
1✔
389
                  err << " call to " << func << " unexpectedly succeeded";
1✔
390
                  err << " expecting failure because " << why;
1✔
391
                  return test_failure(err.str());
1✔
392
               }
1✔
393

394
               return test_success();
21✔
395
            }
396

397
            bool test_rc(const std::string& func, int expected, int rc);
398

399
            bool test_rc_init(const std::string& func, int rc);
400

401
            bool test_ne(const std::string& what, size_t produced, size_t expected);
402

403
            bool test_ne(const std::string& what, const std::string& str1, const std::string& str2);
404

405
#if defined(BOTAN_HAS_BIGINT)
406
            bool test_eq(const std::string& what, const BigInt& produced, const BigInt& expected);
407
            bool test_ne(const std::string& what, const BigInt& produced, const BigInt& expected);
408
#endif
409

410
#if defined(BOTAN_HAS_EC_CURVE_GFP)
411
            bool test_eq(const std::string& what, const Botan::EC_Point& a, const Botan::EC_Point& b);
412
#endif
413

414
            bool test_eq(const char* producer,
415
                         const std::string& what,
416
                         const uint8_t produced[],
417
                         size_t produced_len,
418
                         const uint8_t expected[],
419
                         size_t expected_len);
420

421
            bool test_ne(const std::string& what,
422
                         const uint8_t produced[],
423
                         size_t produced_len,
424
                         const uint8_t expected[],
425
                         size_t expected_len);
426

427
            template <typename Alloc1, typename Alloc2>
428
            bool test_eq(const std::string& what,
42,405✔
429
                         const std::vector<uint8_t, Alloc1>& produced,
430
                         const std::vector<uint8_t, Alloc2>& expected) {
431
               return test_eq(nullptr, what, produced.data(), produced.size(), expected.data(), expected.size());
42,405✔
432
            }
433

434
            template <typename Alloc1, typename Alloc2>
435
            bool test_eq(const std::string& producer,
92,434✔
436
                         const std::string& what,
437
                         const std::vector<uint8_t, Alloc1>& produced,
438
                         const std::vector<uint8_t, Alloc2>& expected) {
439
               return test_eq(
92,434✔
440
                  producer.c_str(), what, produced.data(), produced.size(), expected.data(), expected.size());
92,434✔
441
            }
442

443
            template <typename Alloc>
444
            bool test_eq(const std::string& what,
405✔
445
                         const std::vector<uint8_t, Alloc>& produced,
446
                         const char* expected_hex) {
447
               const std::vector<uint8_t> expected = Botan::hex_decode(expected_hex);
405✔
448
               return test_eq(nullptr, what, produced.data(), produced.size(), expected.data(), expected.size());
405✔
449
            }
405✔
450

451
            template <typename Alloc1, typename Alloc2>
452
            bool test_ne(const std::string& what,
1,939✔
453
                         const std::vector<uint8_t, Alloc1>& produced,
454
                         const std::vector<uint8_t, Alloc2>& expected) {
455
               return test_ne(what, produced.data(), produced.size(), expected.data(), expected.size());
1,939✔
456
            }
457

458
         private:
459
            class ThrowExpectations {
460
               public:
461
                  ThrowExpectations(std::function<void()> fn) :
46,065✔
462
                        m_fn(std::move(fn)), m_expect_success(false), m_consumed(false) {}
92,087✔
463

464
                  ThrowExpectations(const ThrowExpectations&) = delete;
465
                  ThrowExpectations& operator=(const ThrowExpectations&) = delete;
466
                  ThrowExpectations(ThrowExpectations&&) = default;
467
                  ThrowExpectations& operator=(ThrowExpectations&&) = default;
468

469
                  ~ThrowExpectations() { BOTAN_ASSERT_NOMSG(m_consumed); }
46,065✔
470

471
                  ThrowExpectations& expect_success() {
61✔
472
                     BOTAN_ASSERT_NOMSG(!m_expected_message && !m_expected_exception_type);
61✔
473
                     m_expect_success = true;
61✔
474
                     return *this;
61✔
475
                  }
476

477
                  ThrowExpectations& expect_message(const std::string& message) {
169✔
478
                     BOTAN_ASSERT_NOMSG(!m_expect_success);
169✔
479
                     m_expected_message = message;
169✔
480
                     return *this;
169✔
481
                  }
482

483
                  template <typename ExT>
484
                  ThrowExpectations& expect_exception_type() {
43✔
485
                     BOTAN_ASSERT_NOMSG(!m_expect_success);
43✔
486
                     m_expected_exception_type = typeid(ExT);
43✔
487
                     return *this;
43✔
488
                  }
489

490
                  bool check(const std::string& test_name, Test::Result& result);
491

492
               private:
493
                  std::function<void()> m_fn;
494
                  bool m_expect_success;
495
                  std::optional<std::string> m_expected_message;
496
                  std::optional<std::type_index> m_expected_exception_type;
497
                  bool m_consumed;
498
            };
499

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

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

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

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

512
            template <typename ExceptionT>
513
            bool test_throws(const std::string& what, const std::string& expected, const std::function<void()>& fn) {
15✔
514
               return ThrowExpectations(fn).expect_exception_type<ExceptionT>().expect_message(expected).check(what,
30✔
515
                                                                                                               *this);
15✔
516
            }
517

518
            void set_ns_consumed(uint64_t ns) { m_ns_taken = ns; }
43,679✔
519

520
            void start_timer();
521
            void end_timer();
522

523
            void set_code_location(CodeLocation where) { m_where = std::move(where); }
45,228✔
524

525
            const std::optional<CodeLocation>& code_location() const { return m_where; }
46,907✔
526

527
         private:
528
            template <typename T>
529
            std::string to_string(const T& v) {
2✔
530
               if constexpr(detail::is_optional_v<T>) {
531
                  return (v.has_value()) ? to_string(v.value()) : std::string("std::nullopt");
×
532
               } else if constexpr(detail::has_Botan_to_string<T>) {
533
                  return Botan::to_string(v);
×
534
               } else if constexpr(detail::has_ostream_operator<T>) {
535
                  std::ostringstream oss;
2✔
536
                  oss << v;
2✔
537
                  return oss.str();
2✔
538
               } else if constexpr(detail::has_std_to_string<T>) {
2✔
539
                  return std::to_string(v);
2✔
540
               } else {
541
                  return "<?>";
×
542
               }
543
            }
544

545
         private:
546
            std::string m_who;
547
            std::optional<CodeLocation> m_where;
548
            std::chrono::system_clock::time_point m_timestamp;
549
            uint64_t m_started = 0;
550
            uint64_t m_ns_taken = 0;
551
            size_t m_tests_passed = 0;
552
            std::vector<std::string> m_fail_log;
553
            std::vector<std::string> m_log;
554
      };
555

556
      virtual ~Test() = default;
320✔
557
      virtual std::vector<Test::Result> run() = 0;
558

559
      virtual std::vector<std::string> possible_providers(const std::string&);
560

561
      void set_test_name(const std::string& name) { m_test_name = name; }
320✔
562

563
      const std::string& test_name() const { return m_test_name; }
16✔
564

565
      void set_registration_location(CodeLocation location) { m_registration_location = std::move(location); }
320✔
566

567
      const std::optional<CodeLocation>& registration_location() const { return m_registration_location; }
45,228✔
568

569
      /// @p smoke_test are run first in an unfiltered test run
570
      static void register_test(const std::string& category,
571
                                const std::string& name,
572
                                bool smoke_test,
573
                                bool needs_serialization,
574
                                std::function<std::unique_ptr<Test>()> maker_fn);
575

576
      static std::set<std::string> registered_tests();
577
      static std::set<std::string> registered_test_categories();
578
      static std::vector<std::string> filter_registered_tests(const std::vector<std::string>& requested,
579
                                                              const std::set<std::string>& to_be_skipped);
580

581
      static std::unique_ptr<Test> get_test(const std::string& test_name);
582
      static bool test_needs_serialization(const std::string& test_name);
583

584
      static std::string data_file(const std::string& what);
585
      static std::string data_file_as_temporary_copy(const std::string& what);
586

587
      static std::string format_time(uint64_t nanoseconds);
588

589
      static std::string format_time(const std::chrono::nanoseconds nanoseconds) {
1✔
590
         return format_time(nanoseconds.count());
1✔
591
      }
592

593
      template <typename Alloc>
594
      static std::vector<uint8_t, Alloc> mutate_vec(const std::vector<uint8_t, Alloc>& v,
67,193✔
595
                                                    bool maybe_resize = false,
596
                                                    size_t min_offset = 0) {
597
         auto& rng = Test::rng();
67,193✔
598

599
         std::vector<uint8_t, Alloc> r = v;
67,193✔
600

601
         if(maybe_resize && (r.empty() || rng.next_byte() < 32)) {
71,515✔
602
            // TODO: occasionally truncate, insert at random index
603
            const size_t add = 1 + (rng.next_byte() % 16);
879✔
604
            r.resize(r.size() + add);
879✔
605
            rng.randomize(&r[r.size() - add], add);
68,072✔
606
         }
607

608
         if(r.size() > min_offset) {
67,193✔
609
            const size_t offset = std::max<size_t>(min_offset, rng.next_byte() % r.size());
65,793✔
610
            const uint8_t perturb = rng.next_nonzero_byte();
65,793✔
611
            r[offset] ^= perturb;
65,793✔
612
         }
613

614
         return r;
67,193✔
615
      }
×
616

617
      static void set_test_options(const Test_Options& opts);
618

619
      static void set_test_rng(std::shared_ptr<Botan::RandomNumberGenerator> rng);
620

621
      static const Test_Options& options() { return m_opts; }
622

623
      static bool run_long_tests() { return options().run_long_tests(); }
2,340✔
624

625
      static bool run_memory_intensive_tests() { return options().run_memory_intensive_tests(); }
1✔
626

627
      static const std::string& data_dir() { return options().data_dir(); }
447✔
628

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

631
      static std::string temp_file_name(const std::string& basename);
632
      static bool copy_file(const std::string& from, const std::string& to);
633

634
      static std::vector<std::string> provider_filter(const std::vector<std::string>& providers);
635

636
      static std::string read_data_file(const std::string& path);
637
      static std::vector<uint8_t> read_binary_data_file(const std::string& path);
638

639
      static Botan::RandomNumberGenerator& rng();
640
      static std::shared_ptr<Botan::RandomNumberGenerator> rng_as_shared();
641
      static std::string random_password();
642
      static uint64_t timestamp();  // nanoseconds arbitrary epoch
643

644
      static std::vector<Test::Result> flatten_result_lists(std::vector<std::vector<Test::Result>> result_lists);
645

646
   private:
647
      static Test_Options m_opts;
648
      static std::shared_ptr<Botan::RandomNumberGenerator> m_test_rng;
649

650
      std::string m_test_name;                              // The string ID that was used to register this test
651
      std::optional<CodeLocation> m_registration_location;  /// The source file location where the test was registered
652
};
653

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

674
#define BOTAN_REGISTER_TEST(category, name, Test_Class) \
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
   const TestClassRegistration<Test_Class> reg_##Test_Class##_tests(category, name, false, true, {__FILE__, __LINE__})
678
#define BOTAN_REGISTER_SMOKE_TEST(category, name, Test_Class) \
679
   const TestClassRegistration<Test_Class> reg_##Test_Class##_tests(category, name, true, false, {__FILE__, __LINE__})
680
#define BOTAN_REGISTER_SERIALIZED_SMOKE_TEST(category, name, Test_Class) \
681
   const TestClassRegistration<Test_Class> reg_##Test_Class##_tests(category, name, true, true, {__FILE__, __LINE__})
682

683
typedef Test::Result (*test_fn)();
684
typedef std::vector<Test::Result> (*test_fn_vec)();
685

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

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

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

706
   public:
707
      template <typename... TestFns>
708
      FnTest(TestFns... fns) : m_fns(make_variant_vector(fns...)) {}
16✔
709

710
      std::vector<Test::Result> run() override {
16✔
711
         std::vector<Test::Result> result;
16✔
712

713
         for(auto fn_variant = m_fns.crbegin(); fn_variant != m_fns.crend(); ++fn_variant) {
55✔
714
            std::visit(
78✔
715
               [&](auto&& fn) {
39✔
716
                  using T = std::decay_t<decltype(fn)>;
717
                  if constexpr(std::is_same_v<T, test_fn>) {
718
                     result.emplace_back(fn());
39✔
719
                  } else {
720
                     const auto results = fn();
35✔
721
                     result.insert(result.end(), results.begin(), results.end());
35✔
722
                  }
35✔
723
               },
39✔
724
               *fn_variant);
39✔
725
         }
726

727
         return result;
16✔
728
      }
×
729

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

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

752
#define BOTAN_REGISTER_TEST_FN(category, name, ...) \
753
   static const TestFnRegistration reg_##fn_name(category, name, false, false, {__FILE__, __LINE__}, __VA_ARGS__)
754
#define BOTAN_REGISTER_SMOKE_TEST_FN(category, name, ...) \
755
   static const TestFnRegistration reg_##fn_name(category, name, true, false, {__FILE__, __LINE__}, __VA_ARGS__)
756
#define BOTAN_REGISTER_SERIALIZED_TEST_FN(category, name, ...) \
757
   static const TestFnRegistration reg_##fn_name(category, name, false, true {__FILE__, __LINE__}, __VA_ARGS__)
758
#define BOTAN_REGISTER_SERIALIZED_SMOKE_TEST_FN(category, name, ...) \
759
   static const TestFnRegistration reg_##fn_name(category, name, true, true {__FILE__, __LINE__}, __VA_ARGS__)
760

761
class VarMap {
300✔
762
   public:
763
      void clear() { m_vars.clear(); }
29,861✔
764

765
      void add(const std::string& key, const std::string& value) { m_vars[key] = value; }
139,839✔
766

767
      bool has_key(const std::string& key) const { return m_vars.count(key) == 1; }
392,887✔
768

769
      bool get_req_bool(const std::string& key) const;
770

771
      std::vector<uint8_t> get_req_bin(const std::string& key) const;
772
      std::vector<uint8_t> get_opt_bin(const std::string& key) const;
773

774
      std::vector<std::vector<uint8_t>> get_req_bin_list(const std::string& key) const;
775

776
#if defined(BOTAN_HAS_BIGINT)
777
      Botan::BigInt get_req_bn(const std::string& key) const;
778
      Botan::BigInt get_opt_bn(const std::string& key, const Botan::BigInt& def_value) const;
779
#endif
780

781
      std::string get_req_str(const std::string& key) const;
782
      std::string get_opt_str(const std::string& key, const std::string& def_value) const;
783

784
      size_t get_req_sz(const std::string& key) const;
785

786
      uint8_t get_req_u8(const std::string& key) const;
787
      uint32_t get_req_u32(const std::string& key) const;
788
      uint64_t get_req_u64(const std::string& key) const;
789

790
      size_t get_opt_sz(const std::string& key, size_t def_value) const;
791

792
      uint64_t get_opt_u64(const std::string& key, uint64_t def_value) const;
793

794
   private:
795
      std::unordered_map<std::string, std::string> m_vars;
796
};
797

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

819
      virtual bool clear_between_callbacks() const { return true; }
29,271✔
820

821
      std::vector<Test::Result> run() override;
822

823
   protected:
824
      std::string get_next_line();
825

826
      virtual Test::Result run_one_test(const std::string& header, const VarMap& vars) = 0;
827
      // Called before run_one_test
828
      virtual bool skip_this_test(const std::string& header, const VarMap& vars);
829

830
      virtual std::vector<Test::Result> run_final_tests() { return std::vector<Test::Result>(); }
141✔
831

832
   private:
833
      std::string m_data_src;
834
      std::set<std::string> m_required_keys;
835
      std::set<std::string> m_optional_keys;
836
      std::string m_output_key;
837

838
      bool m_first = true;
839
      std::unique_ptr<std::istream> m_cur;
840
      std::string m_cur_src_name;
841
      std::deque<std::string> m_srcs;
842
      std::vector<uint64_t> m_cpu_flags;
843
};
844

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

876
   try {
877
      check_fun(r);
277✔
878
   } catch(const Botan_Tests::Test_Aborted&) {
×
879
      // pass, failure was already noted in the responsible `require`
880
   } catch(const std::exception& ex) {
×
881
      r.test_failure("failed unexpectedly", ex.what());
×
882
   } catch(...) {
×
883
      r.test_failure("failed with unknown exception");
×
884
   }
885

886
   return r;
277✔
887
}
×
888

889
}  // namespace Botan_Tests
890

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