TestRunner.hpp
Go to the documentation of this file.
1 // @formatter:off
2 //
3 // Balau core C++ library
4 //
5 // Copyright (C) 2008 Bora Software (contact@borasoftware.com)
6 //
7 // Licensed under the Boost Software License - Version 1.0 - August 17th, 2003.
8 // See the LICENSE file for the full license text.
9 //
10 
16 
17 #ifndef COM_BORA_SOFTWARE__BALAU_TESTING__TEST_RUNNER
18 #define COM_BORA_SOFTWARE__BALAU_TESTING__TEST_RUNNER
19 
21 #include <Balau/Dev/Assert.hpp>
28 #include <Balau/Testing/Impl/SingleThreadedTestRunnerExecutor.hpp>
29 #include <Balau/Testing/Impl/ProcessPerTestTestRunnerExecutor.hpp>
30 #include <Balau/Testing/Impl/TestGroup.hpp>
31 #include <Balau/Testing/Impl/TestMethodBase.hpp>
32 #include <Balau/Testing/Impl/WorkerProcessesTestRunnerExecutor.hpp>
33 #include <Balau/Testing/Impl/WorkerThreadsTestRunnerExecutor.hpp>
34 #include <Balau/Testing/Reporters/SurefireTestReportGenerator.hpp>
36 #include <Balau/Util/DateTime.hpp>
37 
38 #include <termios.h>
39 #include <thread>
40 
41 #include <unistd.h>
42 #include <boost/predef.h>
43 
44 namespace Balau::Testing {
45 
46 enum class TestRunnerOption {
48  , Namespaces
49  , PauseAtExit
50  , ConcurrencyLevel
51  , ReportFolder
52  , Help
53 };
54 
55 inline std::string toString(const TestRunnerOption & option) {
56  switch (option) {
57  case TestRunnerOption::ExecutionModel: return "ExecutionModel";
58  case TestRunnerOption::Namespaces: return "Namespaces";
59  case TestRunnerOption::PauseAtExit: return "PauseAtExit";
60  case TestRunnerOption::ConcurrencyLevel: return "ConcurrencyLevel";
61  case TestRunnerOption::ReportFolder: return "ReportFolder";
62  case TestRunnerOption::Help: return "Help";
63 
64  default: {
66  Exception::IllegalArgumentException
67  , ::toString("Unknown test option supplied to toString function: ", Util::Enums::toUnderlying(option))
68  );
69  }
70  }
71 }
72 
83 class TestRunner : public Impl::TestRunnerBase {
97  public: static int run(int argc,
98  char * argv[],
99  bool ignoreFirst = true,
100  std::shared_ptr<Impl::TestReportGenerator> reportGenerator = std::shared_ptr<Impl::TestReportGenerator>(new Reporters::SurefireTestReportGenerator)) {
101  CommandLine<TestRunnerOption> cl = getCommandLineParser();
102 
103  cl.parse(argc, argv, ignoreFirst);
104 
105  if (cl.hasOption(TestRunnerOption::Help)) {
106  std::cout << cl.getHelpText(4, 80) << "\n";
107  return 0;
108  }
109 
110  if (reportGenerator && cl.hasOption(TestRunnerOption::ReportFolder)) {
111  const std::string reportFolderString = cl.getOption(TestRunnerOption::ReportFolder);
112  setReportOutputFolder(Resource::File(reportFolderString), reportGenerator);
113  }
114 
115  const auto executionModel = parseExecutionModel(cl);
116  auto & r = runner();
117 
118  r.testList = createTestList(cl);
119  r.writer = Impl::CompositeWriter(StdOutTestWriter());
120  r.reportGenerator = std::move(reportGenerator);
121  r.concurrencyLevel = r.getConcurrencyLevel(cl);
122  r.executionModel = r.checkOutOfProcessCapability(executionModel);
123  r.setUseNamespaces(cl.hasOption(TestRunnerOption::Namespaces));
124  r.pauseAtExit = cl.hasOption(TestRunnerOption::PauseAtExit);
125  return r.performTestRun();
126  }
127 
144  public: template <typename ... WriterItemT>
145  static int run(int argc,
146  char * argv[],
147  bool ignoreFirst,
148  std::shared_ptr<Impl::TestReportGenerator> reportGenerator,
149  const WriterItemT & ... writerItems) {
150  CommandLine<TestRunnerOption> cl = getCommandLineParser();
151 
152  cl.parse(argc, argv, ignoreFirst);
153 
154  if (cl.hasOption(TestRunnerOption::Help)) {
155  std::cout << cl.getHelpText(4, 80) << "\n";
156  return 0;
157  }
158 
159  if (reportGenerator && cl.hasOption(TestRunnerOption::ReportFolder)) {
160  const std::string reportFolderString = cl.getOption(TestRunnerOption::ReportFolder);
161  setReportOutputFolder(Resource::File(reportFolderString), reportGenerator);
162  }
163 
164  const auto executionModel = parseExecutionModel(cl);
165  auto & r = runner();
166 
167  r.testList = createTestList(cl);
168  r.writer = Impl::CompositeWriter(writerItems ...);
169  r.reportGenerator = std::move(reportGenerator);
170  r.concurrencyLevel = r.getConcurrencyLevel(cl);
171  r.executionModel = r.checkOutOfProcessCapability(executionModel);
172  r.setUseNamespaces(cl.hasOption(TestRunnerOption::Namespaces));
173  r.pauseAtExit = cl.hasOption(TestRunnerOption::PauseAtExit);
174  return r.performTestRun();
175  }
176 
190  public: static int run(ExecutionModel executionModel,
191  const Resource::File & reportOutputFolder = Resource::File(),
192  std::shared_ptr<Impl::TestReportGenerator> reportGenerator = std::shared_ptr<Impl::TestReportGenerator>(new Reporters::SurefireTestReportGenerator)) {
193  const bool empty = reportOutputFolder.toRawString().empty();
194 
195  if (reportGenerator && !empty) {
196  setReportOutputFolder(reportOutputFolder, reportGenerator);
197  }
198 
199  auto & r = runner();
200 
201  r.writer = Impl::CompositeWriter(StdOutTestWriter());
202 
203  if (!empty) {
204  r.reportGenerator = std::move(reportGenerator);
205  }
206 
207  r.concurrencyLevel = r.getConcurrencyLevel();
208  r.executionModel = r.checkOutOfProcessCapability(executionModel);
209  r.setUseNamespaces(false);
210  r.pauseAtExit = false;
211  return r.performTestRun();
212  }
213 
229  public: template <typename ... WriterItemT>
230  static int run(ExecutionModel executionModel,
231  const Resource::File & reportOutputFolder,
232  std::shared_ptr<Impl::TestReportGenerator> reportGenerator,
233  const WriterItemT & ... writerItems) {
234  const bool empty = reportOutputFolder.toRawString().empty();
235 
236  if (reportGenerator && !empty) {
237  setReportOutputFolder(reportOutputFolder, reportGenerator);
238  }
239 
240  auto & r = runner();
241 
242  r.writer = Impl::CompositeWriter(writerItems ...);
243 
244  if (!empty) {
245  r.reportGenerator = std::move(reportGenerator);
246  }
247 
248  r.concurrencyLevel = r.getConcurrencyLevel();
249  r.executionModel = r.checkOutOfProcessCapability(executionModel);
250  r.setUseNamespaces(false);
251  r.pauseAtExit = false;
252  return r.performTestRun();
253  }
254 
256 
257  //
258  // bool pauseAtExit = false
259  // unsigned int concurrencyLevel = number of logical cores
260  // bool generateReports = false
261  // bool reportGenerator = SurefireTestReportGenerator
262  // bool reportOutputDirectory = no default
263  //
264  // const WriterItemT & ... writerItems
265  // std::shared_ptr<Impl::TestReportGenerator> reportGenerator = SurefireTestReportGenerator
266  //
267  private: static CommandLine<TestRunnerOption> getCommandLineParser() {
269  .withOption(TestRunnerOption::ExecutionModel, "e", "execution-model", true, "The execution model (default = SingleThreaded).")
270  .withOption(TestRunnerOption::Namespaces, "n", "namespaces", false, "Use namespaces in test group names (default is not to use).")
271  .withOption(TestRunnerOption::PauseAtExit, "p", "pause", false, "Pause at exit (default is not to pause).")
272  .withOption(TestRunnerOption::ConcurrencyLevel, "c", "concurrency", true, "The number of threads or processes to use to run the tests (default = detect).")
273  .withOption(TestRunnerOption::ReportFolder, "r", "report-folder", true, "Generate test reports in the specified folder.")
274  .withOption(TestRunnerOption::Help, "h", "help", false, "Displays this help message.")
276  }
277 
278  private: static void setReportOutputFolder(const Resource::File & reportOutputFolder,
279  std::shared_ptr<Impl::TestReportGenerator> & reportGenerator) {
280  if (!reportOutputFolder.toRawString().empty()) {
281  auto path = Resource::File(boost::filesystem::system_complete(reportOutputFolder.getEntry()));
282 
283  if (path.isRegularFile()) {
286  , ::toString("The specified report output folder is an existing regular file: ", path.toRawString())
287  );
288  }
289 
290  if (!path.isRegularDirectory()) {
291  try {
292  path.createDirectories();
293  } catch (const boost::filesystem::filesystem_error & e) {
296  , ::toString("The specified report output folder could not be created: ", path.toRawString())
297  );
298  }
299  }
300 
301  reportGenerator->setOutputFolder(path);
302  }
303  }
304 
305  // The test runner singleton.
306  private: static TestRunner & runner() {
307  static TestRunner r;
308  return r;
309  }
310 
311  private: TestRunner()
312  : writer(Impl::CompositeWriter(StdOutTestWriter()))
313  , reportGenerator()
314  , concurrencyLevel(getConcurrencyLevel())
315  , executionModel(checkOutOfProcessCapability(WorkerProcesses))
316  , useNamespaces(false)
317  , pauseAtExit(false) {}
318 
319  // Called by the test group base class in order to get a unique index.
320  private: unsigned int getGroupIndex() override {
321  std::lock_guard<std::mutex> lock(runner().mutex);
322  return runner().currentGroupIndex++;
323  }
324 
325  // Parse the execution model string.
326  private: static ExecutionModel parseExecutionModel(CommandLine<TestRunnerOption> & cl) {
327  if (!cl.hasOption(TestRunnerOption::ExecutionModel)) {
329  }
330 
331  const auto m = Util::Strings::toLower(Util::Strings::trim(cl.getOption(TestRunnerOption::ExecutionModel)));
333 
334  if (isExecutionModel(m)) {
335  fromString(model, m);
336  std::cout << "\nRunning tests for command line specified execution model " << toString(model) << "\n" << std::endl;
337  } else {
338  model = Testing::SingleThreaded;
339  std::cout << "\nUnknown execution model specified. Using execution model SingleThreaded\n" << std::endl;
340  }
341 
342  return model;
343  }
344 
345  // Called from the run methods after configuring the test runner.
346  private: int performTestRun() {
347  System::ThreadName::setName("TestMain");
348 
349  writer << "\n------------------------- STARTING TESTS -------------------------\n\n";
350 
351  auto startTime = System::SystemClock().nanotime();
352  std::unique_ptr<Impl::TestRunnerExecutor> executor;
353 
354  switch (executionModel) {
355  case SingleThreaded: {
356  writer << "Run type = single process, single threaded\n";
357 
358  executor = std::unique_ptr<Impl::TestRunnerExecutor>(
359  new Impl::SingleThreadedTestRunnerExecutor(
360  writer, reportGenerator, useNamespaces, testCasesByGroup, testList
361  )
362  );
363 
364  break;
365  }
366 
367  case WorkerThreads: {
368  writer << "Run type = single process, multi-threaded "
369  << "(" << concurrencyLevel << " thread" << (concurrencyLevel > 1 ? "s" : "") << ")\n";
370 
371  executor = std::unique_ptr<Impl::TestRunnerExecutor>(
372  new Impl::WorkerThreadsTestRunnerExecutor(
373  writer, reportGenerator, useNamespaces, testCasesByGroup, testList, concurrencyLevel
374  )
375  );
376 
377  break;
378  }
379 
380  case WorkerProcesses: {
381  writer << "Run type = worker processes "
382  << "(" << concurrencyLevel << " worker process" << (concurrencyLevel > 1 ? "es" : "") << ")\n";
383 
384  executor = std::unique_ptr<Impl::TestRunnerExecutor>(
385  new Impl::WorkerProcessesTestRunnerExecutor(
386  writer, reportGenerator, useNamespaces, testCasesByGroup, testList, concurrencyLevel
387  )
388  );
389 
390  break;
391  }
392 
393  case ProcessPerTest: {
394  writer << "Run type = process per test "
395  << "(" << concurrencyLevel << " simultaneous process" << (concurrencyLevel > 1 ? "es" : "") << ")\n";
396 
397  executor = std::unique_ptr<Impl::TestRunnerExecutor>(
398  new Impl::ProcessPerTestTestRunnerExecutor(
399  writer, reportGenerator, useNamespaces, testCasesByGroup, testList, concurrencyLevel
400  )
401  );
402 
403  break;
404  }
405 
406  default: ThrowBalauException(Exception::BugException, "Unhandled ExecutionModel in switch statement.");
407  }
408 
409  executor->run();
410 
411  const std::chrono::nanoseconds duration = System::SystemClock().nanotime() - startTime;
412  const bool success = report(*executor, duration);
413 
414  if (executionModel == SingleThreaded || executionModel == WorkerThreads) {
415  writer << ("\nTest application process with pid " + ::toString(getpid()) + " finished execution.\n");
416  } else {
417  writer << ("\nTest application parent process with pid " + ::toString(getpid()) + " finished execution.");
418  }
419 
420  if (pauseAtExit) {
421  writer << "Press a key to exit..\n";
422  struct termios oldattr = {};
423  tcgetattr(STDIN_FILENO, &oldattr);
424  struct termios newattr = oldattr;
425  newattr.c_lflag &= ~(ICANON | ECHO); // NOLINT
426  tcsetattr(STDIN_FILENO, TCSANOW, &newattr);
427  getchar();
428  tcsetattr(STDIN_FILENO, TCSANOW, &oldattr);
429  }
430 
431  return success ? 0 : 1;
432  }
433 
434  // Concatenates final values into a single string, forming the test list.
435  private: static std::string createTestList(CommandLine<TestRunnerOption> & cl) {
436  const size_t count = cl.getFinalValueCount();
437 
438  if (count == 0) {
439  return "";
440  }
441 
442  std::string tl;
443  std::string prefix;
444 
445  for (size_t m = 0; m < count; m++) {
446  tl += prefix;
447  tl += cl.getFinalValue(m);
448  prefix = " ";
449  }
450 
451  return tl;
452  }
453 
454  // Determine the concurrency level, either from the supplied requested concurrency level or from the hardware capabilities.
455  private: unsigned int getConcurrencyLevel(CommandLine<TestRunnerOption> & cl) {
456  const size_t requestedConcurrency = cl.getOptionAsUnsignedIntOrDefault(TestRunnerOption::ConcurrencyLevel, 0);
457  return getConcurrencyLevel(requestedConcurrency);
458  }
459 
460  // Determine the concurrency level, either from the supplied requested concurrency level or from the hardware capabilities.
461  private: unsigned int getConcurrencyLevel(size_t requestedConcurrency = 0) {
462  const unsigned int coreCount = std::thread::hardware_concurrency();
463  const unsigned int maxConcurrency = Impl::TestRunnerLimits::MaxConcurrency;
464 
465  if (requestedConcurrency != 0) {
466  if (requestedConcurrency > maxConcurrency) {
467  writer << "Requested concurrency level exceeds the maximum supported concurrency level of "
468  << maxConcurrency << ". Setting concurrency level to " << maxConcurrency << "\n";
469  return maxConcurrency;
470  }
471 
472  return requestedConcurrency;
473  } else if (coreCount == 0) {
476  , "Could not determine the core count of the machine "
477  "(std::thread::hardware_concurrency() returned 0). "
478  "Please specify the concurrency level manually via "
479  "a test runner constructor argument."
480  );
481  } else if (coreCount > maxConcurrency) {
482  writer << "Core count exceeds the maximum supported concurrency level of "
483  << maxConcurrency << ". Setting concurrency level to " << maxConcurrency << "\n";
484 
485  return maxConcurrency;
486  }
487 
488  return coreCount;
489  }
490 
491  private: ExecutionModel checkOutOfProcessCapability(ExecutionModel desiredExecutionModel) {
493  return desiredExecutionModel;
494  }
495 
496  if (desiredExecutionModel != WorkerProcesses) {
497  writer << "Warning: running WorkerThreads execution model (this platform does not support out of process test runs).\n";
498  }
499 
500  return WorkerProcesses;
501  }
502 
503  private: bool report(const Impl::TestRunnerExecutor & runner, std::chrono::nanoseconds duration) {
504  const std::vector<Impl::TestResult> & testResults = runner.getTestResults();
505  const std::vector<Impl::FlattenedTestCase> & tests = runner.getTests();
506 
507  const auto failureCount = (size_t) std::count_if(
508  testResults.begin()
509  , testResults.end()
510  , [] (const Impl::TestResult & message) { return message.result == Impl::TestResult::Result::Failure; }
511  );
512 
513  const auto errorCount = (size_t) std::count_if(
514  testResults.begin()
515  , testResults.end()
516  , [] (const Impl::TestResult & message) { return message.result == Impl::TestResult::Result::Error; }
517  );
518 
519  const auto ignoredCount = (size_t) std::count_if(
520  testResults.begin()
521  , testResults.end()
522  , [] (const Impl::TestResult & message) { return message.result == Impl::TestResult::Result::Ignored; }
523  );
524 
525  const size_t successCount = testResults.size() - ignoredCount - failureCount - errorCount;
526  const std::string totalCoreDuration = Impl::TestRunnerExecutor::durationStr(runner.getTotalCoreTime());
527  const std::string totalClockDuration = Impl::TestRunnerExecutor::durationStr(duration);
528  const std::string testAverageDuration = runner.getTestResults().empty() ? "n/a" : Impl::TestRunnerExecutor::durationStr(runner.getTotalCoreTime() / runner.getTestResults().size());
529 
530  writer << "\n------------------------- COMPLETED TESTS ------------------------\n"
531  << "\nTotal duration (test run clock time) = " << totalCoreDuration
532  << "\nAverage duration (test run clock time) = " << testAverageDuration
533  << "\nTotal duration (application clock time) = " << totalClockDuration << "\n";
534 
535  if (failureCount == 0 && errorCount == 0) {
536  writer << "\nALL TESTS PASSED"
537  << "\n tests passed: " << Util::Strings::padLeft(::toString(successCount), 6)
538  << "\n tests ignored: " << Util::Strings::padLeft(::toString(ignoredCount), 6)
539  << "\n";
540  } else {
541  writer << "\n***** THERE WERE TEST FAILURES. *****\n"
542  << "\nTotal tests run: " << (successCount + ignoredCount + failureCount + errorCount) << "\n"
543  << "\n tests passed: " << Util::Strings::padLeft(::toString(successCount), 6)
544  << "\n tests ignored: " << Util::Strings::padLeft(::toString(ignoredCount), 6)
545  << "\n tests failed: " << Util::Strings::padLeft(::toString(failureCount), 6)
546  << "\n tests errored: " << Util::Strings::padLeft(::toString(errorCount), 6)
547  << "\n\n"
548  << "Failed/errored tests:\n";
549 
550  std::for_each(
551  testResults.begin()
552  , testResults.end()
553  , [&tests, this] (const Impl::TestResult & message) {
554  if (message.result == Impl::TestResult::Result::Failure || message.result == Impl::TestResult::Result::Error) {
555  writer << " " << tests[message.testIndex].testName << "\n";
556  }
557  }
558  );
559 
560  writer << "\n";
561  }
562 
563  return failureCount == 0 && errorCount == 0;
564  }
565 
566  private: void registerTest(Impl::TestGroupBase * group,
567  const std::string & testGroupName,
568  const std::shared_ptr<Impl::TestMethodBase> & method,
569  const std::string & name) {
570  std::lock_guard<std::mutex> lock(mutex);
571  auto & testCases = testCasesByGroup[testGroupName];
572  testCases.emplace_back(Impl::TestCase(group, method, testGroupName + "::" + name));
573  }
574 
575  private: void setUseNamespaces(bool useNamespaces_) {
576  std::lock_guard<std::mutex> lock(mutex);
577 
578  useNamespaces = useNamespaces_;
579 
580  for (auto & testCaseGroup : testCasesByGroup) {
581  auto & testCaseVector = testCaseGroup.second;
582 
583  if (!testCaseVector.empty()) {
584  testCaseVector[0].group->setGroupName(
585  Impl::TestRunnerExecutor::extractTypeName(
586  testCaseVector[0].group->getGroupName().c_str(), useNamespaces
587  )
588  );
589  }
590  }
591  }
592 
593  private: void log(const std::string & string) {
594  writer << string;
595  }
596 
597  private: void logLine(const std::string & string) {
598  writer << string << "\n";
599  }
600 
601  private: void logLine() {
602  writer << "\n";
603  }
604 
605  template <typename T> friend class TestGroup;
606 
607  private: std::mutex mutex;
608  private: std::string testList;
609  private: std::unordered_map<std::string, std::vector<Impl::TestCase>> testCasesByGroup;
610  private: Impl::CompositeWriter writer;
611  private: std::shared_ptr<Impl::TestReportGenerator> reportGenerator;
612  private: unsigned int concurrencyLevel {};
613  private: ExecutionModel executionModel;
614  private: bool useNamespaces;
615  private: bool pauseAtExit;
616  private: unsigned int currentGroupIndex = 0;
617 };
618 
620 
621 template <typename TestClassT>
622 inline void TestGroup<TestClassT>::ignore() {
623  ignoreCurrent();
624 }
625 
626 template <typename TestClassT>
627 inline void TestGroup<TestClassT>::log(const std::string & string) {
628  return Testing::TestRunner::runner().log(string);
629 }
630 
631 template <typename TestClassT>
632 template <typename S, typename ... SR>
633 inline void TestGroup<TestClassT>::log(const S & p, const SR & ... pRest) {
634  using ::toString;
635  log(toString(p, pRest ...));
636 }
637 
638 template <typename TestClassT>
639 inline void TestGroup<TestClassT>::logLine(const std::string & string) {
640  return Testing::TestRunner::runner().logLine(string);
641 }
642 
643 template <typename TestClassT>
644 template <typename S, typename ... SR>
645 inline void TestGroup<TestClassT>::logLine(const S & p, const SR & ... pRest) {
646  using ::toString;
647  logLine(toString(p, pRest ...));
648 }
649 
650 template <typename TestClassT>
651 inline TestGroup<TestClassT>::TestGroup()
652  : TestGroupBase(TestRunner::runner())
654  , testGroupName(Impl::TestRunnerExecutor::extractTypeName(typeid(*this).name(), false)) {
655  (void) &instance; // force instantiation
656 }
657 
658 template <typename TestClassT>
659 inline TestGroup<TestClassT>::TestGroup(unsigned int executionModels_)
660  : TestGroupBase(TestRunner::runner())
661  , executionModels(executionModels_)
662  , testGroupName(Impl::TestRunnerExecutor::extractTypeName(typeid(*this).name(), false)) {
663  (void) &instance; // force instantiation
664 }
665 
666 template <typename TestClassT>
667 inline void TestGroup<TestClassT>::registerTest(Method method, const std::string & testName) {
668  auto m = std::shared_ptr<Impl::TestMethodBase>(new TestMethod(*static_cast<TestClassT *>(this), method));
669  auto & runner = Testing::TestRunner::runner();
670  runner.registerTest(this, testGroupName, m, testName);
671 }
672 
673 } // namespace Balau::Testing
674 
675 #endif // COM_BORA_SOFTWARE__BALAU_TESTING__TEST_RUNNER
static std::string padLeft(const std::string_view &str, unsigned int width, char c=' ')
Left pad the supplied UTF-8 string up to the specified width in code points.
Definition: Strings.hpp:603
Pre-defined test writer classes for stdout, stderr, and files.
A test writer that writes to stdout.
Definition: StdWriters.hpp:34
static int run(int argc, char *argv[], bool ignoreFirst, std::shared_ptr< Impl::TestReportGenerator > reportGenerator, const WriterItemT &... writerItems)
Run the test runner after parsing the supplied input arguments and using the supplied writers...
Definition: TestRunner.hpp:145
Test runner execution model enum.
The default implementation of the clock API.
Definition: SystemClock.hpp:30
static bool forkSupported()
Determine whether forking is support on this platform.
Definition: Fork.hpp:35
Run all tests in a single thread.
Definition: ExecutionModel.hpp:41
#define ThrowBalauException(ExceptionClass,...)
Throw a Balau style exception, with implicit file and line number, and optional stacktrace.
Definition: BalauException.hpp:45
bool hasOption(KeyT key) const
Does the command line option set have the specified option?
Definition: CommandLine.hpp:248
A write only standard file on a file system which is written as bytes.
A compact command line argument parser.
static int run(ExecutionModel executionModel, const Resource::File &reportOutputFolder, std::shared_ptr< Impl::TestReportGenerator > reportGenerator, const WriterItemT &... writerItems)
Run the test runner after parsing the supplied input arguments and using the supplied writers...
Definition: TestRunner.hpp:230
Thrown by the test runner when there is a non test related error.
Definition: TestExceptions.hpp:35
boost::beast::http::error Error
Boost error codes returned from HTTP algorithms and operations.
Definition: NetworkTypes.hpp:313
The main test runner class.
Definition: TestRunner.hpp:83
Run tests in a set of worker processes.
Definition: ExecutionModel.hpp:69
void parse(int argc, char *argv[], bool ignoreFirst)
Parse the input arguments.
Definition: CommandLine.hpp:174
Balau exceptions for the test framework.
boost::beast::http::verb Method
The HTTP method (GET, HEAD, POST).
Definition: NetworkTypes.hpp:298
std::string getFinalValue(size_t index=0) const
Get the final value with the specified index as a string or throw if no final value is available...
Definition: CommandLine.hpp:481
CommandLine & withOptionalFinalValue()
Specify that the command line arguments include one (SEV style) or one or more (SSV style) final stan...
Definition: CommandLine.hpp:156
Balau exceptions for system utilities.
Thrown when a bug is encountered (mainly unimplemented switch cases).
Definition: BalauException.hpp:178
Switches that have a leading dash &#39;-&#39; character.
static constexpr auto toUnderlying(E e) noexcept -> typename std::underlying_type< E >::type
Convert the strongly typed enum to its underlying integer.
Definition: Enums.hpp:31
std::string getHelpText(size_t indent, size_t totalWidth, CommandLineStyle styleOverride=CommandLineStyle::Detect) const
Get the help text.
Definition: CommandLine.hpp:569
Thrown when an illegal argument is passed to a function or method.
Definition: BalauException.hpp:138
boost::filesystem::directory_entry getEntry() const
Get the underlying directory entry for this file URI.
Definition: File.hpp:400
static std::string_view trim(const std::string_view &input)
Trim whitespace from the beginning and end of the supplied UTF-8 string.
Definition: Strings.hpp:706
Run all tests in a single process.
Definition: ExecutionModel.hpp:55
size_t getFinalValueCount() const
Get the number of final values available.
Definition: CommandLine.hpp:258
bool isExecutionModel(std::string_view value)
Returns true if the supplied string represents a valid execution model (the case is ignored)...
Definition: ExecutionModel.hpp:140
std::string getOption(KeyT key) const
Get the specified option value as a UTF-8 string.
Definition: CommandLine.hpp:269
CommandLine & withOption(KeyT key, const std::string &shortSwitch, const std::string &longSwitch, bool hasValue, const std::string &documentation)
Specify an option that has a short style switch and a long style switch.
Definition: CommandLine.hpp:100
A file on the local file system.
Definition: File.hpp:35
A compact command line argument parser.
Definition: CommandLine.hpp:60
A thread-local name (used by the logging system).
unsigned int getOptionAsUnsignedIntOrDefault(KeyT key, unsigned int defaultValue) const
Get the specified option value as an unsigned int, or the specified default if there is no such optio...
Definition: CommandLine.hpp:399
static std::string toLower(const std::string_view &s)
Convert the supplied UTF-8 string to lowercase.
Definition: Strings.hpp:457
virtual std::chrono::nanoseconds nanotime() const =0
Get the current time in nanoseconds since the unix epoch.
Balau exceptions for resources.
Assertion utilities for development purposes.
static int run(ExecutionModel executionModel, const Resource::File &reportOutputFolder=Resource::File(), std::shared_ptr< Impl::TestReportGenerator > reportGenerator=std::shared_ptr< Impl::TestReportGenerator >(new Reporters::SurefireTestReportGenerator))
Run all the tests in the test runner.
Definition: TestRunner.hpp:190
The test runner and test assertion functions.
Definition: Assertions.hpp:23
ExecutionModel
The type of execution model to be used by the test runner.
Definition: ExecutionModel.hpp:32
Date and time utilities.
std::string toRawString() const override
Get a string representing the raw URI.
Definition: File.hpp:572
static void setName(const std::string &name)
Set the name of the calling thread.
static int run(int argc, char *argv[], bool ignoreFirst=true, std::shared_ptr< Impl::TestReportGenerator > reportGenerator=std::shared_ptr< Impl::TestReportGenerator >(new Reporters::SurefireTestReportGenerator))
Run the test runner after parsing the supplied input arguments.
Definition: TestRunner.hpp:97
void fromString(ExecutionModel &model, const std::string &value)
Obtain the execution model from a string.
Definition: ExecutionModel.hpp:123
Balau::U8String< AllocatorT > toString(ExecutionModel model)
Print the execution model value as a UTF-8 string.
Definition: ExecutionModel.hpp:92
Test group report generator that generates XML reports with the Maven Surefire plugin schema...
Definition: SurefireTestReportGenerator.hpp:21
Run each test in a separate worker process.
Definition: ExecutionModel.hpp:83