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

randombit / botan / 5133556677

31 May 2023 02:11PM UTC coverage: 91.735% (-0.3%) from 92.012%
5133556677

Pull #3568

github

web-flow
Merge de48a2eb6 into 1cbeffafb
Pull Request #3568: Change clang-format AllowShortBlocksOnASingleLine from true to Empty

76059 of 82912 relevant lines covered (91.73%)

12004312.75 hits per line

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

77.5
/src/tests/runner/test_runner.cpp
1
/*
2
* (C) 2017 Jack Lloyd
3
* (C) 2022 René Meusel, Rohde & Schwarz Cybersecurity
4
*
5
* Botan is released under the Simplified BSD License (see license.txt)
6
*/
7

8
#include "../tests.h"
9

10
#include "test_runner.h"
11
#include "test_stdout_reporter.h"
12
#include "test_xml_reporter.h"
13

14
#include <botan/version.h>
15
#include <botan/internal/cpuid.h>
16
#include <botan/internal/loadstor.h>
17

18
#if defined(BOTAN_HAS_THREAD_UTILS)
19
   #include <botan/internal/rwlock.h>
20
   #include <botan/internal/thread_pool.h>
21
#endif
22

23
#include <shared_mutex>
24

25
namespace Botan_Tests {
26

27
Test_Runner::Test_Runner(std::ostream& out) : m_output(out) {}
1✔
28

29
Test_Runner::~Test_Runner() = default;
1✔
30

31
namespace {
32

33
/*
34
* This is a fast, simple, deterministic PRNG that's used for running
35
* the tests. It is not intended to be cryptographically secure.
36
*/
37
class Testsuite_RNG final : public Botan::RandomNumberGenerator {
1✔
38
   public:
39
      std::string name() const override { return "Testsuite_RNG"; }
×
40

41
      void clear() override { m_x = 0; }
×
42

43
      bool accepts_input() const override { return true; }
×
44

45
      bool is_seeded() const override { return true; }
23,367✔
46

47
      void fill_bytes_with_input(std::span<uint8_t> output, std::span<const uint8_t> input) override {
1,570,963✔
48
         for(const auto byte : input) {
1,570,971✔
49
            mix(byte);
8✔
50
         }
51

52
         for(auto& byte : output) {
9,750,675✔
53
            byte = mix();
8,179,712✔
54
         }
55
      }
1,570,963✔
56

57
      Testsuite_RNG(const std::vector<uint8_t>& seed, uint64_t test_counter) {
1✔
58
         m_x = ~test_counter;
1✔
59
         add_entropy(seed.data(), seed.size());
1✔
60
      }
1✔
61

62
   private:
63
      uint8_t mix(uint8_t input = 0) {
8,179,720✔
64
         m_x ^= input;
8,179,720✔
65
         m_x *= 0xF2E16957;
8,179,720✔
66
         m_x += 0xE50B590F;
8,179,720✔
67
         return static_cast<uint8_t>(m_x >> 27);
8,179,720✔
68
      }
69

70
      uint64_t m_x;
71
};
72

73
}  // namespace
74

75
bool Test_Runner::run(const Test_Options& opts) {
1✔
76
   m_reporters.emplace_back(std::make_unique<StdoutReporter>(opts, output()));
1✔
77
   if(!opts.xml_results_dir().empty()) {
1✔
78
#if defined(BOTAN_TARGET_OS_HAS_FILESYSTEM)
79
      m_reporters.emplace_back(std::make_unique<XmlReporter>(opts, opts.xml_results_dir()));
2✔
80
#else
81
      output() << "Generating test report files is not supported on this platform\n";
82
#endif
83
   }
84

85
   auto req = Botan_Tests::Test::filter_registered_tests(opts.requested_tests(), opts.skip_tests());
1✔
86

87
   // TODO: Test runner should not be aware of certain test's environmental requirements.
88
   if(opts.pkcs11_lib().empty()) {
1✔
89
      // do not run pkcs11 tests by default unless pkcs11-lib set
90
      for(auto iter = req.begin(); iter != req.end();) {
×
91
         if((*iter).find("pkcs11") != std::string::npos) {
×
92
            iter = req.erase(iter);
×
93
         } else {
94
            ++iter;
×
95
         }
96
      }
97
   }
98

99
   if(req.empty()) {
1✔
100
      throw Test_Error("No tests to run");
×
101
   }
102

103
   std::vector<uint8_t> seed = Botan::hex_decode(opts.drbg_seed());
1✔
104
   if(seed.empty()) {
1✔
105
      const uint64_t ts = Botan_Tests::Test::timestamp();
1✔
106
      seed.resize(8);
1✔
107
      Botan::store_be(ts, seed.data());
1✔
108
   }
109

110
   for(auto& reporter : m_reporters) {
3✔
111
      const std::string cpuid = Botan::CPUID::to_string();
2✔
112
      if(!cpuid.empty()) {
2✔
113
         reporter->set_property("CPU flags", cpuid);
4✔
114
      }
115

116
      if(!opts.pkcs11_lib().empty()) {
2✔
117
         reporter->set_property("pkcs11 library", opts.pkcs11_lib());
4✔
118
      }
119

120
      if(!opts.provider().empty()) {
2✔
121
         reporter->set_property("provider", opts.provider());
×
122
      }
123

124
      reporter->set_property("drbg_seed", Botan::hex_encode(seed));
6✔
125
   }
2✔
126

127
   Botan_Tests::Test::set_test_options(opts);
1✔
128

129
   for(size_t i = 0; i != opts.test_runs(); ++i) {
2✔
130
      Botan_Tests::Test::set_test_rng(std::make_shared<Testsuite_RNG>(seed, i));
2✔
131

132
      for(const auto& reporter : m_reporters) {
3✔
133
         reporter->next_test_run();
2✔
134
      }
135

136
      const bool passed =
1✔
137
         (opts.test_threads() == 1) ? run_tests(req) : run_tests_multithreaded(req, opts.test_threads());
1✔
138

139
      for(const auto& reporter : m_reporters) {
3✔
140
         reporter->render();
2✔
141
      }
142

143
      if(!passed) {
1✔
144
         return false;
×
145
      }
146
   }
147

148
   return true;
1✔
149
}
1✔
150

151
namespace {
152

153
std::vector<Test::Result> run_a_test(const std::string& test_name) {
312✔
154
   std::vector<Test::Result> results;
312✔
155

156
   try {
312✔
157
      if(test_name == "simd_32" && Botan::CPUID::has_simd_32() == false) {
312✔
158
         results.push_back(Test::Result::Note(test_name, "SIMD not available on this platform"));
×
159
      } else if(std::unique_ptr<Test> test = Test::get_test(test_name)) {
312✔
160
         std::vector<Test::Result> test_results = test->run();
312✔
161
         for(auto& result : test_results) {
45,484✔
162
            if(!result.code_location() && test->registration_location()) {
45,172✔
163
               // If a test result has no specific code location associated to it,
164
               // we fall back to the test case's registration location.
165
               result.set_code_location(test->registration_location().value());
45,172✔
166
            }
167
         }
168
         results.insert(results.end(), test_results.begin(), test_results.end());
312✔
169
      } else {
312✔
170
         results.push_back(Test::Result::Note(test_name, "Test missing or unavailable"));
×
171
      }
312✔
172
   } catch(std::exception& e) {
×
173
      results.push_back(Test::Result::Failure(test_name, e.what()));
×
174
   } catch(...) {
×
175
      results.push_back(Test::Result::Failure(test_name, "unknown exception"));
×
176
   }
×
177

178
   return results;
312✔
179
}
×
180

181
bool all_passed(const std::vector<Test::Result>& results) {
312✔
182
   return std::all_of(results.begin(), results.end(), [](const auto& r) { return r.tests_failed() == 0; });
11,599✔
183
}
184

185
}  // namespace
186

187
bool Test_Runner::run_tests_multithreaded(const std::vector<std::string>& tests_to_run, size_t test_threads) {
1✔
188
   // If 0 then we let thread pool select the count
189
   BOTAN_ASSERT_NOMSG(test_threads != 1);
1✔
190

191
#if !defined(BOTAN_HAS_THREAD_UTILS)
192
   output() << "Running tests in multiple threads not enabled in this build\n";
193
   return run_tests(tests_to_run);
194

195
#else
196
   Botan::Thread_Pool pool(test_threads);
1✔
197
   Botan::RWLock rwlock;
1✔
198

199
   std::vector<std::future<std::vector<Test::Result>>> m_fut_results;
1✔
200

201
   auto run_test_exclusive = [&](const std::string& test_name) {
18✔
202
      std::unique_lock lk(rwlock);
17✔
203
      return run_a_test(test_name);
17✔
204
   };
17✔
205

206
   auto run_test_shared = [&](const std::string& test_name) {
296✔
207
      std::shared_lock lk(rwlock);
295✔
208
      return run_a_test(test_name);
295✔
209
   };
295✔
210

211
   for(const auto& test_name : tests_to_run) {
313✔
212
      if(Test::test_needs_serialization(test_name)) {
312✔
213
         m_fut_results.push_back(pool.run(run_test_exclusive, test_name));
34✔
214
      } else {
215
         m_fut_results.push_back(pool.run(run_test_shared, test_name));
590✔
216
      }
217
   }
218

219
   bool passed = true;
220
   for(size_t i = 0; i != m_fut_results.size(); ++i) {
313✔
221
      const auto results = m_fut_results[i].get();
312✔
222
      for(auto& reporter : m_reporters) {
936✔
223
         reporter->record(tests_to_run[i], results);
624✔
224
      }
225
      passed &= all_passed(results);
312✔
226
   }
312✔
227

228
   pool.shutdown();
1✔
229

230
   return passed;
1✔
231
#endif
232
}
2✔
233

234
bool Test_Runner::run_tests(const std::vector<std::string>& tests_to_run) {
×
235
   bool passed = true;
×
236
   for(const auto& test_name : tests_to_run) {
×
237
      const auto results = run_a_test(test_name);
×
238

239
      for(auto& reporter : m_reporters) {
×
240
         reporter->record(test_name, results);
×
241
      }
242
      passed &= all_passed(results);
×
243
   }
×
244

245
   return passed;
×
246
}
247

248
}  // namespace Botan_Tests
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

© 2025 Coveralls, Inc