OTest2
A C++ testing framework
dfltenvironment.cpp
Go to the documentation of this file.
1 /*
2  * Copyright (C) 2019 Ondrej Starek
3  *
4  * This file is part of OTest2.
5  *
6  * OTest2 is free software: you can redistribute it and/or modify it under
7  * the terms of the GNU Lesser General Public License as published by
8  * the Free Software Foundation, either version 3 of the License,
9  * or (at your option) any later version.
10  *
11  * OTest2 is distributed in the hope that it will be useful, but WITHOUT
12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
13  * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
14  * License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public License
17  * along with OTest2. If not, see <http://www.gnu.org/licenses/>.
18  */
19 #include <dfltenvironment.h>
20 
21 #include <assert.h>
22 #include <cstdlib>
23 #include <getopt.h>
24 #include <iostream>
25 #include <memory>
26 #include <string>
27 #include <vector>
28 
29 #include <exccatcherordinary.h>
30 #include <registry.h>
31 #include <reporterconsole.h>
32 #include <reporterjunit.h>
33 #include <reportertee.h>
34 #include <runnerfilteruntagged.h>
35 #include <runnerfiltertags.h>
36 #include <runnerordinary.h>
37 #include <scenarioiterptr.h>
38 #include <testmarkfactory.h>
39 #include <testmarkstorage.h>
40 #include <timesourcesys.h>
41 #include <userdata.h>
42 #include <utils.h>
43 
44 namespace OTest2 {
45 
46 namespace {
47 
48 void printHelpMessage(
49  const char* binary_name_) {
50  std::cout << "Usage: " << binary_name_ << " [options]" << std::endl;
51  std::cout << std::endl;
52  std::cout << "A collection of automatic tests run by the OTest2 framework" << std::endl;
53  std::cout << std::endl;
54  std::cout << "Options (all options are optional):" << std::endl;
55  std::cout << " -h --help Print this message." << std::endl;
56  std::cout << " --disable-console Disable reporting into the console." << std::endl;
57  std::cout << " -v --verbose Make the console reporter verbose. This option" << std::endl;
58  std::cout << " turns on printing of results of passed tests." << std::endl;
59  std::cout << " -j file --junit=file Print report into a file in the JUnit XML format." << std::endl;
60  std::cout << " This option may be used several times. Several" << std::endl;
61  std::cout << " files will then be written." << std::endl;
62  std::cout << " -r glob --restrictive=glob Run just test object which match the tag glob." << std::endl;
63  std::cout << " The default value runs all untagged objects." << std::endl;
64  std::cout << " -m file --regression=file Path of the regression file. The default value" << std::endl;
65  std::cout << " is 'regression.ot2tm' (stored in the working" << std::endl;
66  std::cout << " directory)." << std::endl;
67  std::cout << " -t name --test=name Name of the test how it's reported. The default" << std::endl;
68  std::cout << " value is the name of the test's binary." << std::endl;
69  std::cout << std::endl;
70 }
71 
72 std::string createDefaultTestName(
73  const char* binname_) {
74  /* -- Default test name is derived from the test binary as the last part
75  * of the path. */
76  std::string test_name_(binname_);
77  auto last_slash_(test_name_.find_last_of('/'));
78  if(last_slash_ != std::string::npos)
79  test_name_.erase(0, last_slash_ + 1);
80  return test_name_;
81 }
82 
83 } /* -- namespace */
84 
85 struct DfltEnvironment::Impl {
86  /* -- the environment */
87  TimeSourceSys time_source;
88  ExcCatcherOrdinary exc_catcher_ordinary;
89  ExcCatcher* exc_catcher;
90  std::vector<std::unique_ptr<Reporter>> reporters;
91  ReporterTee reporter_root;
92  std::unique_ptr<RunnerFilter> filter;
93  TestMarkFactory test_mark_factory;
94  std::unique_ptr<TestMarkStorage> test_mark_storage;
95  UserData user_data;
96  std::unique_ptr<Runner> runner;
97 
98  /* -- builder state */
99  bool console_reporter;
100  bool console_verbose;
101  std::string regression_file;
102  std::string test_name;
103 
104  explicit Impl(
105  const std::string& test_name_);
106 };
107 
108 DfltEnvironment::Impl::Impl(
109  const std::string& test_name_) :
110  time_source(),
111  exc_catcher_ordinary(),
112  exc_catcher(nullptr),
113  reporters(),
114  reporter_root(),
115  filter(),
116  test_mark_factory(),
117  test_mark_storage(),
118  user_data(),
119  runner(),
120  console_reporter(true),
121  console_verbose(false),
122  regression_file("regression.ot2tm"),
123  test_name(test_name_) {
124 
125 }
126 
128  const std::string& testname_) :
129  pimpl(new Impl(testname_)) {
130 
131 }
132 
134  int argc_,
135  char* argv_[]) :
136  pimpl(new Impl(createDefaultTestName(argv_[0]))) {
137 
138  enum {
139  DISABLE_CONSOLE_REPORTER = 1000,
140  VERBOSE_CONSOLE,
141  JUNIT_REPORTER,
142  RESTRICTIVE_RUN,
143  REGRESSION_FILE,
144  TEST_NAME,
145  HELP,
146  };
147  struct option long_options_[] = {
148  {"disable-console", 0, nullptr, DISABLE_CONSOLE_REPORTER},
149  {"verbose", 0, nullptr, VERBOSE_CONSOLE},
150  {"junit", 1, nullptr, JUNIT_REPORTER},
151  {"restrictive", 1, nullptr, RESTRICTIVE_RUN},
152  {"regression", 1, nullptr, REGRESSION_FILE},
153  {"test", 1, nullptr, TEST_NAME},
154  {"help", 0, nullptr, HELP},
155  {nullptr, 0, nullptr, 0},
156  };
157  int opt_;
158  while((opt_ = getopt_long(argc_, argv_, "vj:r:m:t:h", long_options_, nullptr)) >= 0) {
159  switch(opt_) {
160  case DISABLE_CONSOLE_REPORTER:
161  pimpl->console_reporter = false;
162  break;
163  case 'v':
164  case VERBOSE_CONSOLE:
165  pimpl->console_verbose = true;
166  break;
167  case 'j':
168  case JUNIT_REPORTER:
169  pimpl->reporters.emplace_back(new ReporterJUnit(optarg, false));
170  pimpl->reporter_root.appendReporter(pimpl->reporters.back().get());
171  break;
172  case 'r':
173  case RESTRICTIVE_RUN:
174  try {
175  pimpl->filter = ::OTest2::make_unique<RunnerFilterTags>(optarg);
176  }
177  catch(const Exception& exc_) {
178  std::cout << "invalid tag expression: " << exc_.reason() << std::endl;
179  std::exit(2);
180  }
181  break;
182  case 'm':
183  case REGRESSION_FILE:
184  pimpl->regression_file = optarg;
185  break;
186  case 't':
187  case TEST_NAME:
188  pimpl->test_name = optarg;
189  break;
190  case 'h':
191  case HELP:
192  printHelpMessage(argv_[0]);
193  std::exit(0);
194  break;
195  default:
196  printHelpMessage(argv_[0]);
197  std::exit(1);
198  break;
199  }
200  }
201 
202 }
203 
205  odelete(pimpl);
206 }
207 
209  Reporter* reporter_) {
210  assert(reporter_ != nullptr);
211 
212  pimpl->reporter_root.appendReporter(reporter_);
213  pimpl->console_reporter = false;
214 }
215 
217  ExcCatcher* catcher_) {
218  assert(catcher_ != nullptr);
219 
220  pimpl->exc_catcher = catcher_;
221 }
222 
224  return pimpl->user_data;
225 }
226 
228  if(pimpl->runner == nullptr) {
229  /* -- set default exception catcher */
230  if(pimpl->exc_catcher == nullptr)
231  pimpl->exc_catcher = &pimpl->exc_catcher_ordinary;
232 
233  /* -- create the console reporter if it's not disabled */
234  if(pimpl->console_reporter) {
235  pimpl->reporters.emplace_back(new ReporterConsole(
236  &std::cout, pimpl->console_verbose, false));
237  pimpl->reporter_root.appendReporter(pimpl->reporters.back().get());
238  }
239 
240  /* -- create default runner filter - run all untagged tests */
241  if(pimpl->filter == nullptr)
242  pimpl->filter = ::OTest2::make_unique<RunnerFilterUntagged>();
243 
244  /* -- create the test mark storage */
245  pimpl->test_mark_storage.reset(
246  new TestMarkStorage(&pimpl->test_mark_factory, pimpl->regression_file));
247 
248  /* -- get the registry and set the test name */
249  const Registry& registry_(Registry::instance("default"));
250  ScenarioIterPtr scenario_(registry_.getTests(*pimpl->filter));
251 
252  /* -- finally, create the test runner */
253  pimpl->runner.reset(new RunnerOrdinary(
254  &pimpl->time_source,
255  pimpl->exc_catcher,
256  &pimpl->reporter_root,
257  &pimpl->test_mark_factory,
258  pimpl->test_mark_storage.get(),
259  &pimpl->user_data,
260  scenario_));
261  }
262  return *pimpl->runner;
263 }
264 
265 } /* namespace OTest2 */
reportertee.h
OTest2::Exception::reason
virtual std::string reason() const =0
Get exception reason.
scenarioiterptr.h
OTest2::DfltEnvironment::addReporter
void addReporter(Reporter *reporter_)
Append a test reporter.
Definition: dfltenvironment.cpp:208
userdata.h
OTest2::DfltEnvironment::getRunner
Runner & getRunner()
Get constructed runner.
Definition: dfltenvironment.cpp:227
OTest2::Reporter
Generic test reporter.
Definition: reporter.h:34
OTest2::RunnerOrdinary
Ordinary implementation of the Runner interface.
Definition: runnerordinary.h:42
OTest2::DfltEnvironment::~DfltEnvironment
~DfltEnvironment()
Dtor.
Definition: dfltenvironment.cpp:204
OTest2::DfltEnvironment::setExceptionCatcher
void setExceptionCatcher(ExcCatcher *catcher_)
Set exception catcher.
Definition: dfltenvironment.cpp:216
testmarkstorage.h
OTest2::UserData
An object keeping user data passed from user's custom main function.
Definition: userdata.h:120
timesourcesys.h
OTest2::DfltEnvironment::DfltEnvironment
DfltEnvironment(const DfltEnvironment &)=delete
utils.h
OTest2::DfltEnvironment::getUserData
UserData & getUserData() noexcept
Access the container of user data.
Definition: dfltenvironment.cpp:223
OTest2::Runner
Generic test runner.
Definition: runner.h:112
runnerfilteruntagged.h
OTest2
Definition: assertbean.h:25
runnerordinary.h
OTest2::TestMarkStorage
Storage of test marks.
Definition: testmarkstorage.h:34
dfltenvironment.h
OTest2::ScenarioIterPtr
std::shared_ptr< ScenarioIter > ScenarioIterPtr
Shared pointer of the scenario iter interface.
Definition: scenarioiterptr.h:27
registry.h
reporterconsole.h
OTest2::Exception
Generic OTest2 exception.
Definition: exc.h:31
testmarkfactory.h
runnerfiltertags.h
OTest2::Registry::instance
static Registry & instance(const std::string &domain_)
Access of the global instances.
Definition: registry.cpp:143
OTest2::ReporterJUnit
A reporter creating JUnit XML reports.
Definition: reporterjunit.h:32
OTest2::Registry
Test registry.
Definition: registry.h:38
OTest2::ExcCatcher
Generic exception catcher interface.
Definition: exccatcher.h:39
reporterjunit.h
exccatcherordinary.h
OTest2::odelete
void odelete(T_ *&object_)
Delete a pointer and set it invalid.
Definition: utils.h:34
OTest2::Registry::getTests
ScenarioIterPtr getTests(const RunnerFilter &filter_) const
Get iterator of test roots.
Definition: registry.cpp:132
OTest2::ReporterConsole
A reporter writing into a console or file.
Definition: reporterconsole.h:34