xtd - Reference Guide  0.1.0
Modern c++17/20 framework to create console, GUI and unit test applications on Windows, macOS, Linux, iOS and android.
unit_test.h
Go to the documentation of this file.
1 #pragma once
5 
6 #include "../tunit_export.h"
7 #include "event_listener.h"
9 #include "settings.h"
10 #include <chrono>
11 #include <iomanip>
12 #include <fstream>
13 #include <memory>
14 #include <random>
15 #include <string>
16 
18 namespace xtd {
20  namespace tunit {
22  template <typename test_class_t>
23  class test_class_attribute;
25 
32  class tunit_export_ unit_test {
33  public:
34  explicit unit_test(std::unique_ptr<xtd::tunit::event_listener> event_listener) noexcept;
35 
36  unit_test(std::unique_ptr<xtd::tunit::event_listener> event_listener, int argc, char* argv[]) noexcept : arguments(argv + 1, argv + argc), name_(get_filename(argv[0])), event_listener_(std::move(event_listener)) {}
37 
39  virtual ~unit_test() {}
41 
44  int run() {
45  if (parse_arguments(arguments))
47 
48  if (xtd::tunit::settings::default_settings().list_tests()) {
49  std::vector<std::string> tests;
50  for (auto test_class : test_classes())
51  for(auto test : test_class.test()->tests())
52  tests.push_back(test_class.test()->name() + '.' + test.name());
53  return list_tests(tests);
54  }
55 
56  if (xtd::tunit::settings::default_settings().shuffle_test()) {
57  std::random_device rd;
58  std::mt19937 g = xtd::tunit::settings::default_settings().random_seed() == 0 ? std::mt19937(rd()) : std::mt19937(xtd::tunit::settings::default_settings().random_seed());
59  std::shuffle(test_classes().begin(), test_classes().end(), g);
60  }
61 
62  for (repeat_iteration_ = 1; repeat_iteration_ <= xtd::tunit::settings::default_settings().repeaat_test() || xtd::tunit::settings::default_settings().repeaat_test() < 0; ++repeat_iteration_) {
63  try {
64  event_listener_->on_unit_test_start(xtd::tunit::tunit_event_args(*this));
65 
66  event_listener_->on_unit_test_initialize_start(xtd::tunit::tunit_event_args(*this));
67  unit_test_initialize();
68  event_listener_->on_unit_test_initialize_end(xtd::tunit::tunit_event_args(*this));
69 
70  start_time_point_ = std::chrono::high_resolution_clock::now();
71  for (auto& test_class : test_classes())
72  if (test_class.test()->test_count())
73  test_class.test()->run(*this);
74  end_time_point_ = std::chrono::high_resolution_clock::now();
75 
76  event_listener_->on_unit_test_cleanup_start(xtd::tunit::tunit_event_args(*this));
77  unit_test_cleanup();
78  event_listener_->on_unit_test_cleanup_end(xtd::tunit::tunit_event_args(*this));
79 
80  event_listener_->on_unit_test_end(xtd::tunit::tunit_event_args(*this));
81  } catch(const std::exception&) {
83  // do error...
84  } catch(...) {
86  // do error...
87  }
88  }
89 
90  xtd::tunit::settings::default_settings().end_time(std::chrono::system_clock::now());
91  write_xml();
92 
94  }
95 
96  int repeat_iteration() const noexcept {return repeat_iteration_;}
97 
98  int repeat_iteration_count() const noexcept {return xtd::tunit::settings::default_settings().repeaat_test();}
99 
100  bool repeat_tests() const noexcept {return xtd::tunit::settings::default_settings().repeaat_test() != 1;}
101 
102  size_t test_cases_count() const noexcept {
103  size_t count = 0;
104  for (auto test_class : test_classes())
105  if (test_class.test()->test_count())
106  count ++;
107  return count;
108  }
109 
110  size_t test_count() const noexcept {
111  size_t count = 0;
112  for (auto test_class : test_classes())
113  count += test_class.test()->test_count();
114  return count;
115  }
116 
117  size_t aborted_test_count() const noexcept {
118  size_t count = 0;
119  for (auto& test_class : test_classes())
120  for (auto& test : test_class.test()->tests())
121  if (settings::default_settings().is_match_test_name(test_class.test()->name(), test.name()) && test.aborted()) count++;
122  return count;
123  }
124 
125  std::vector<std::string> aborted_test_names() const noexcept {
126  std::vector<std::string> names;
127  for (auto& test_class : test_classes())
128  for (auto& test : test_class.test()->tests())
129  if (settings::default_settings().is_match_test_name(test_class.test()->name(), test.name()) && test.aborted()) names.push_back(test_class.test()->name() + "." + test.name());
130  return names;
131  }
132 
133  std::chrono::milliseconds elapsed_time() const noexcept {
134  using namespace std::chrono_literals;
135  if (start_time_point_.time_since_epoch() == 0ms && end_time_point_.time_since_epoch() == 0ms) return 0ms;
136  if (end_time_point_.time_since_epoch() == 0ms) return std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::high_resolution_clock::now() - start_time_point_);
137  return std::chrono::duration_cast<std::chrono::milliseconds>(end_time_point_ - start_time_point_);
138  }
139 
140  size_t ignored_test_count() const noexcept {
141  size_t count = 0;
142  for (auto test_class : test_classes())
143  count += test_class.test()->ignored_test_count();
144  return count;
145  }
146 
147  std::vector<std::string> ignored_test_names() const noexcept {
148  std::vector<std::string> names;
149  for (auto& test_class : test_classes())
150  for (auto& test : test_class.test()->tests())
151  if (settings::default_settings().is_match_test_name(test_class.test()->name(), test.name()) && test.ignored()) names.push_back(test_class.test()->name() + "." + test.name());
152  return names;
153  }
154 
155  size_t failed_test_count() const noexcept {
156  size_t count = 0;
157  for (auto& test_class : test_classes())
158  for (auto& test : test_class.test()->tests())
159  if (settings::default_settings().is_match_test_name(test_class.test()->name(), test.name()) && test.failed()) count++;
160  return count;
161  }
162 
163  std::vector<std::string> failed_test_names() const noexcept {
164  std::vector<std::string> names;
165  for (auto& test_class : test_classes())
166  for (auto& test : test_class.test()->tests())
167  if (settings::default_settings().is_match_test_name(test_class.test()->name(), test.name()) && test.failed()) names.push_back(test_class.test()->name() + "." + test.name());
168  return names;
169  }
170 
171  size_t succeed_test_count() const noexcept {
172  size_t count = 0;
173  for (auto& test_class : test_classes())
174  for (auto& test : test_class.test()->tests())
175  if (settings::default_settings().is_match_test_name(test_class.test()->name(), test.name()) && test.succeed()) count++;
176  return count;
177  }
178 
179  std::vector<std::string> succeed_test_names() const noexcept {
180  std::vector<std::string> names;
181  for (auto& test_class : test_classes())
182  for (auto& test : test_class.test()->tests())
183  if (settings::default_settings().is_match_test_name(test_class.test()->name(), test.name()) && test.succeed()) names.push_back(test_class.test()->name() + "." + test.name());
184  return names;
185  }
186 
187  protected:
188  virtual int list_tests(const std::vector<std::string>& tests) {
190  }
191 
192  virtual bool parse_arguments(const std::vector<std::string>& args) {
193  for (auto arg : args) {
194  if (arg == "--also_run_ignored_tests") xtd::tunit::settings::default_settings().also_run_ignored_tests(true);
195  else if (arg.find("--filter_tests=") == 0) xtd::tunit::settings::default_settings().filter_tests(arg.substr(15));
196  else if (arg == "--list_tests") xtd::tunit::settings::default_settings().list_tests(true);
197  else if (arg == "--output_color=true") xtd::tunit::settings::default_settings().output_color(true);
198  else if (arg == "--output_color=false") xtd::tunit::settings::default_settings().output_color(false);
199  else if (arg.find("--output_xml") == 0) { xtd::tunit::settings::default_settings().output_xml(true);
200  if (arg[12] == '=') xtd::tunit::settings::default_settings().output_xml_path(arg.substr(13));
201  } else if (arg.find("--random_seed=") == 0) xtd::tunit::settings::default_settings().random_seed(std::stoi(arg.substr(14)));
202  else if (arg.find("--repeat_tests=") == 0) xtd::tunit::settings::default_settings().repeat_tests(std::stoi(arg.substr(15)));
203  else if (arg == "--show_duration=true") xtd::tunit::settings::default_settings().show_duration(true);
204  else if (arg == "--show_duration=false") xtd::tunit::settings::default_settings().show_duration(false);
205  else if (arg == "--shuffle_tests") xtd::tunit::settings::default_settings().shuffle_test(true);
206  }
207  return false;
208  }
209 
210  private:
211  template <typename test_class_t>
213  friend class xtd::tunit::test_class;
214  friend class xtd::tunit::test;
215  friend class xtd::tunit::base_assert;
216 
217  static void add(const xtd::tunit::registered_test_class& test_class) {test_classes().push_back(test_class);}
218 
219  void unit_test_cleanup() {
220  }
221 
222  void unit_test_initialize() {
223  }
224 
225  static std::vector<xtd::tunit::registered_test_class>& test_classes() {
226  static std::vector<xtd::tunit::registered_test_class> test_classes;
227  return test_classes;
228  }
229 
230  std::string get_filename(const std::string& path) {
231  std::string filename = path;
232  const size_t last_slash_idx = filename.find_last_of("\\/");
233  if (std::string::npos != last_slash_idx)
234  filename.erase(0, last_slash_idx + 1);
235 
236  const size_t period_idx = filename.rfind('.');
237  if (std::string::npos != period_idx)
238  filename.erase(period_idx);
239  return filename;
240  }
241 
242  std::string to_string(const std::chrono::milliseconds& ms) {
243  std::stringstream ss;
244  if (ms.count() == 0)
245  ss << 0;
246  else
247  ss << ms.count() / 1000 << "." << std::setfill('0') << std::setw(3) << ms.count() % 1000;
248  return ss.str();
249  }
250 
251  std::string to_string(const std::chrono::time_point<std::chrono::system_clock>& time) {
252  std::time_t time_t = std::chrono::system_clock::to_time_t(time);
253  std::tm tm = *std::localtime(&time_t);
254  std::stringstream ss;
255  ss << tm.tm_year + 1900 << "-" << std::setfill('0') << std::setw(2) << tm.tm_mon << "-" << std::setfill('0') << std::setw(2) << tm.tm_mday;
256  ss << "T" << std::setfill('0') << std::setw(2) << tm.tm_hour << ":" << std::setfill('0') << std::setw(2) << tm.tm_min << ":" << std::setfill('0') << std::setw(2) << tm.tm_sec;
257  return ss.str();
258  }
259 
260  std::string status_to_string(const xtd::tunit::test& test) {
261  std::stringstream ss;
262  if (test.not_started() || test.ignored()) ss << "notrun";
263  else ss << "run";
264  return ss.str();
265  }
266 
267  std::string message_to_string(const xtd::tunit::test& test) {
268  std::stringstream ss;
269  if (test.stack_frame() != xtd::diagnostics::stack_frame::empty())
270  ss << test.stack_frame().get_file_name() << ":" << test.stack_frame().get_file_line_number() << "&#0x0A;";
271  ss << "Expected: " << test.expect() << "&#0x0A;";
272  ss << "But was : " << test.actual();
273  return ss.str();
274  }
275 
276  std::string cdata_message_to_string(const xtd::tunit::test& test) {
277  std::stringstream ss;
278  if (test.stack_frame() != xtd::diagnostics::stack_frame::empty())
279  ss << test.stack_frame().get_file_name() << ":" << test.stack_frame().get_file_line_number() << std::endl;
280  ss << "Expected: " << test.expect() << std::endl;
281  ss << "But was : " << test.actual();
282  return ss.str();
283  }
284 
285  void write_xml() {
286  if (xtd::tunit::settings::default_settings().output_xml()) {
287  std::fstream file(xtd::tunit::settings::default_settings().output_xml_path(), std::ios::out | std::ios::trunc);
288  file << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" << std::endl;
289  file << "<testsuites tests=\"" << test_count() << "\" failures=\"" << failed_test_count() << "\" disabled=\"" << ignored_test_count() << "\" errors=\"" << 0 << "\" timestamp=\"" << to_string(xtd::tunit::settings::default_settings().start_time()) << "\" time=\"" << to_string(elapsed_time()) << "\" name=\"" << name_ << "\">" << std::endl;
290  for (auto& test_class : test_classes()) {
291  file << " <testsuite name=\"" << test_class.test()->name() << "\" tests=\"" << test_class.test()->test_count() << "\" failures=\"" << test_class.test()->failed_test_count() << "\" disabled=\"" << test_class.test()->ignored_test_count() << "\" error=\"" << test_class.test()->failed_test_count() << "\" time=\"" << to_string(test_class.test()->elapsed_time()) << "\">" << std::endl;
292  for (auto& test : test_class.test()->tests()) {
293  file << " <testcase name=\"" << test.name() << "\" status=\"" << status_to_string(test) << "\" time=\"" << to_string(test.elapsed_time()) << "\" classname=\"" << test_class.test()->name() << "\"";
294  if (!test.failed())
295  file << " />" << std::endl;
296  else {
297  file << ">" << std::endl;
298  file << " <failure message=\"" << message_to_string(test) << "\" type= \"" << "\">" << "<![CDATA[" << cdata_message_to_string(test) << "]]></failure>" << std::endl;
299  file << " </testcase>" << std::endl;
300  }
301  }
302  file << " </testsuite>" << std::endl;
303  }
304  file << "</testsuites>" << std::endl;
305  file.close();
306  }
307  }
308 
309  std::vector<std::string> arguments;
310  std::string name_ = "AllTests";
311  std::unique_ptr<xtd::tunit::event_listener> event_listener_;
312  std::chrono::high_resolution_clock::time_point end_time_point_;
313  int repeat_iteration_ = 0;
314  std::chrono::high_resolution_clock::time_point start_time_point_;
315  };
316  }
317 }
Thebase class for assert.
Definition: base_assert.h:25
Contains xtd::tunit::registered_test_class class.
Definition: test_class_attribute.h:14
bool output_xml() const noexcept
Gets output xml.
Definition: settings.h:86
bool shuffle_test() const noexcept
Gets shuffle tests.
Definition: settings.h:103
virtual const xtd::ustring & get_file_name() const
Gets the file name that contains the code that is executing. This information is typically extracted ...
Definition: test.h:24
std::string output_xml_path() const noexcept
Gets output xml path.
Definition: settings.h:94
static xtd::tunit::settings & default_settings() noexcept
Get default settings intance.
The xtd namespace contains all fundamental classes to access Hardware, Os, System, and more.
Definition: system_report.h:17
Contains xtd::tunit::settings class.
std::chrono::time_point< std::chrono::system_clock > end_time() const noexcept
Gets unit test end time.
Definition: settings.h:140
Represent the event listener class. Unit test call theses events when unit tests are processing...
Definition: event_listener.h:23
virtual uint32_t get_file_line_number() const
Gets the line number in the file that contains the code that is executing. This information is typica...
int run()
Runs all tests in this UnitTest object and prints the result.
Definition: unit_test.h:44
int random_seed() const noexcept
Gets random seed value.
Definition: settings.h:113
Definition: test_class.h:25
void repeat_tests(int repeat_tests) noexcept
Sets repeat tests count.
Definition: settings.h:128
bool output_color() const noexcept
Gets output color.
Definition: settings.h:78
int exit_status() const noexcept
Gets exit status.
Definition: settings.h:46
int repeaat_test() const noexcept
Gets repeat tests count.
Definition: settings.h:123
bool is_match_test_name(const std::string &test_class_name, const std::string &test_name) const noexcept
Return true if a specified test class name and specified test name match with the current filter test...
Definition: settings.h:66
static stack_frame empty() noexcept
Return an empty stack frame.
bool list_tests() const noexcept
Gets list tests.
Definition: settings.h:70
Contains xtd::tunit::event_listener class.
Represents the registered test class.
Definition: registered_test_class.h:19
const std::string & filter_tests() const noexcept
Gets filter tests.
Definition: settings.h:57
bool also_run_ignored_tests() const noexcept
Gets also run ignored test.
Definition: settings.h:36
The template class.
Definition: unit_test.h:32
bool show_duration() const noexcept
Gets if show duration for each test.
Definition: settings.h:132
tunit_event_args is the base class for classes containing event data.
Definition: tunit_event_args.h:20