xtd - Reference Guide 0.2.0
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 <xtd/convert.h>
11 #include <xtd/date_time.h>
12 #include <xtd/random.h>
13 #include <xtd/system_exception.h>
14 #include <iomanip>
15 #include <fstream>
16 #include <memory>
17 #include <random>
18 #include <string>
19 
21 namespace xtd {
23  namespace tunit {
25  template <typename test_class_t>
26  class test_class_attribute;
28 
35  class tunit_export_ unit_test {
36  public:
40  explicit unit_test(std::unique_ptr<xtd::tunit::event_listener> event_listener) noexcept;
41 
46  unit_test(std::unique_ptr<xtd::tunit::event_listener> event_listener, int argc, char* argv[]) noexcept;
47 
49  virtual ~unit_test() {}
51 
54  int run() {
55  if (parse_arguments(arguments))
57 
58  if (xtd::tunit::settings::default_settings().list_tests()) {
59  std::vector<std::string> tests;
60  for (auto test_class : test_classes())
61  for (auto test : test_class.test()->tests())
62  tests.push_back(test_class.test()->name() + '.' + test.name());
63  return list_tests(tests);
64  }
65 
67 
69 
70  for (repeat_iteration_ = 1; repeat_iteration_ <= xtd::tunit::settings::default_settings().repeat_test() || xtd::tunit::settings::default_settings().repeat_test() < 0; ++repeat_iteration_) {
72  std::shuffle(test_classes().begin(), test_classes().end(), random.generator());
73 
74  try {
75  event_listener_->on_unit_test_start(xtd::tunit::tunit_event_args(*this));
76 
77  event_listener_->on_unit_test_initialize_start(xtd::tunit::tunit_event_args(*this));
78  unit_test_initialize();
79  event_listener_->on_unit_test_initialize_end(xtd::tunit::tunit_event_args(*this));
80 
81  start_time_point_ = xtd::date_time::now();
82  for (auto& test_class : test_classes())
83  if (test_class.test()->test_count())
84  test_class.test()->run(*this);
85  end_time_point_ = xtd::date_time::now();
86 
87  event_listener_->on_unit_test_cleanup_start(xtd::tunit::tunit_event_args(*this));
88  unit_test_cleanup();
89  event_listener_->on_unit_test_cleanup_end(xtd::tunit::tunit_event_args(*this));
90 
91  event_listener_->on_unit_test_end(xtd::tunit::tunit_event_args(*this));
92  } catch (const std::exception&) {
94  // do error...
95  } catch (...) {
97  // do error...
98  }
99  }
100 
102  write_xml();
103 
105  }
106 
107  int repeat_iteration() const noexcept {return repeat_iteration_;}
108 
109  int repeat_iteration_count() const noexcept {return xtd::tunit::settings::default_settings().repeat_test();}
110 
111  bool repeat_tests() const noexcept {return xtd::tunit::settings::default_settings().repeat_test() != 1;}
112 
113  size_t test_cases_count() const noexcept {
114  size_t count = 0;
115  for (auto test_class : test_classes())
116  if (test_class.test()->test_count())
117  count ++;
118  return count;
119  }
120 
121  size_t test_count() const noexcept {
122  size_t count = 0;
123  for (auto test_class : test_classes())
124  count += test_class.test()->test_count();
125  return count;
126  }
127 
128  size_t aborted_test_count() const noexcept {
129  size_t count = 0;
130  for (auto& test_class : test_classes())
131  for (auto& test : test_class.test()->tests())
132  if (settings::default_settings().is_match_test_name(test_class.test()->name(), test.name()) && test.aborted()) count++;
133  return count;
134  }
135 
136  std::vector<std::string> aborted_test_names() const noexcept {
137  std::vector<std::string> names;
138  for (auto& test_class : test_classes())
139  for (auto& test : test_class.test()->tests())
140  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());
141  return names;
142  }
143 
144  std::chrono::milliseconds elapsed_time() const noexcept {
145  using namespace std::chrono_literals;
146  if (start_time_point_.ticks() == 0ms && end_time_point_.ticks() == 0ms) return 0ms;
147  if (end_time_point_.ticks() == 0ms) return std::chrono::duration_cast<std::chrono::milliseconds>((xtd::date_time::now() - start_time_point_).ticks());
148  return std::chrono::duration_cast<std::chrono::milliseconds>((end_time_point_ - start_time_point_).ticks());
149  }
150 
151  size_t ignored_test_count() const noexcept {
152  size_t count = 0;
153  for (auto test_class : test_classes())
154  count += test_class.test()->ignored_test_count();
155  return count;
156  }
157 
158  std::vector<std::string> ignored_test_names() const noexcept {
159  std::vector<std::string> names;
160  for (auto& test_class : test_classes())
161  for (auto& test : test_class.test()->tests())
162  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());
163  return names;
164  }
165 
166  size_t failed_test_count() const noexcept {
167  size_t count = 0;
168  for (auto& test_class : test_classes())
169  for (auto& test : test_class.test()->tests())
170  if (settings::default_settings().is_match_test_name(test_class.test()->name(), test.name()) && test.failed()) count++;
171  return count;
172  }
173 
174  std::vector<std::string> failed_test_names() const noexcept {
175  std::vector<std::string> names;
176  for (auto& test_class : test_classes())
177  for (auto& test : test_class.test()->tests())
178  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());
179  return names;
180  }
181 
182  size_t succeed_test_count() const noexcept {
183  size_t count = 0;
184  for (auto& test_class : test_classes())
185  for (auto& test : test_class.test()->tests())
186  if (settings::default_settings().is_match_test_name(test_class.test()->name(), test.name()) && test.succeed()) count++;
187  return count;
188  }
189 
190  std::vector<std::string> succeed_test_names() const noexcept {
191  std::vector<std::string> names;
192  for (auto& test_class : test_classes())
193  for (auto& test : test_class.test()->tests())
194  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());
195  return names;
196  }
197 
198  protected:
199  virtual int list_tests(const std::vector<std::string>& tests) {
201  }
202 
203  virtual bool parse_arguments(const std::vector<std::string>& args) {
204  for (auto arg : args) {
205  if (arg == "--also_run_ignored_tests") xtd::tunit::settings::default_settings().also_run_ignored_tests(true);
206  else if (arg.find("--filter_tests=") == 0) xtd::tunit::settings::default_settings().filter_tests(arg.substr(15));
207  else if (arg == "--list_tests") xtd::tunit::settings::default_settings().list_tests(true);
208  else if (arg == "--output_color=true") xtd::tunit::settings::default_settings().output_color(true);
209  else if (arg == "--output_color=false") xtd::tunit::settings::default_settings().output_color(false);
210  else if (arg.find("--output_xml") == 0) {
212  if (arg[12] == '=') xtd::tunit::settings::default_settings().output_xml_path(arg.substr(13));
213  } else if (arg.find("--random_seed=") == 0) xtd::tunit::settings::default_settings().random_seed(convert::to_uint32(arg.substr(14)));
214  else if (arg == "--enable_stack_trace=true") xtd::tunit::settings::default_settings().enable_stack_trace(true);
215  else if (arg == "--enable_stack_trace=false") xtd::tunit::settings::default_settings().enable_stack_trace(false);
216  else if (arg.find("--repeat_tests=") == 0) xtd::tunit::settings::default_settings().repeat_tests(convert::to_int32(arg.substr(15)));
217  else if (arg == "--show_duration=true") xtd::tunit::settings::default_settings().show_duration(true);
218  else if (arg == "--show_duration=false") xtd::tunit::settings::default_settings().show_duration(false);
219  else if (arg == "--shuffle_tests") xtd::tunit::settings::default_settings().shuffle_test(true);
220  }
221  return false;
222  }
223 
224  private:
225  template <typename test_class_t>
227  friend class xtd::tunit::test_class;
228  friend class xtd::tunit::test;
229  friend class xtd::tunit::base_assert;
230 
231  static void add(const xtd::tunit::registered_test_class& test_class) {test_classes().push_back(test_class);}
232 
233  void unit_test_cleanup() {
234  }
235 
236  void unit_test_initialize() {
237  }
238 
239  static std::vector<xtd::tunit::registered_test_class>& test_classes() {
240  static std::vector<xtd::tunit::registered_test_class> test_classes;
241  return test_classes;
242  }
243 
244  std::string get_filename(const std::string& path) {
245  std::string filename = path;
246  const size_t last_slash_idx = filename.find_last_of("\\/");
247  if (std::string::npos != last_slash_idx)
248  filename.erase(0, last_slash_idx + 1);
249 
250  const size_t period_idx = filename.rfind('.');
251  if (std::string::npos != period_idx)
252  filename.erase(period_idx);
253  return filename;
254  }
255 
256  std::string to_string(const std::chrono::milliseconds& ms) {
257  std::stringstream ss;
258  if (ms.count() == 0)
259  ss << 0;
260  else
261  ss << ms.count() / 1000 << "." << std::setfill('0') << std::setw(3) << ms.count() % 1000;
262  return ss.str();
263  }
264 
265  std::string to_string(const std::chrono::time_point<std::chrono::system_clock>& time) {
266  std::time_t time_t = std::chrono::system_clock::to_time_t(time);
267  std::tm tm = *std::localtime(&time_t);
268  std::stringstream ss;
269  ss << tm.tm_year + 1900 << "-" << std::setfill('0') << std::setw(2) << tm.tm_mon << "-" << std::setfill('0') << std::setw(2) << tm.tm_mday;
270  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;
271  return ss.str();
272  }
273 
274  std::string status_to_string(const xtd::tunit::test& test) {
275  std::stringstream ss;
276  if (test.not_started() || test.ignored()) ss << "notrun";
277  else ss << "run";
278  return ss.str();
279  }
280 
281  std::string message_to_string(const xtd::tunit::test& test) {
282  std::stringstream ss;
283  if (test.stack_frame() != xtd::diagnostics::stack_frame::empty())
284  ss << test.stack_frame().get_file_name() << ":" << test.stack_frame().get_file_line_number() << "&#0x0A;";
285  ss << "Expected: " << test.expect() << "&#0x0A;";
286  ss << "But was : " << test.actual();
287  return ss.str();
288  }
289 
290  std::string cdata_message_to_string(const xtd::tunit::test& test) {
291  std::stringstream ss;
292  if (test.stack_frame() != xtd::diagnostics::stack_frame::empty())
293  ss << test.stack_frame().get_file_name() << ":" << test.stack_frame().get_file_line_number() << std::endl;
294  ss << "Expected: " << test.expect() << std::endl;
295  ss << "But was : " << test.actual();
296  return ss.str();
297  }
298 
299  void write_xml() {
300  if (xtd::tunit::settings::default_settings().output_xml()) {
301  std::fstream file(xtd::tunit::settings::default_settings().output_xml_path(), std::ios::out | std::ios::trunc);
302  file << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" << std::endl;
303  file << "<testsuites tests=\"" << test_count() << "\" failures=\"" << failed_test_count() << "\" disabled=\"" << ignored_test_count() << "\" errors=\"" << 0 << "\" timestamp=\"" << xtd::tunit::settings::default_settings().start_time().to_string() << "\" time=\"" << to_string(elapsed_time()) << "\" name=\"" << name_ << "\">" << std::endl;
304  for (auto& test_class : test_classes()) {
305  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;
306  for (auto& test : test_class.test()->tests()) {
307  file << " <testcase name=\"" << test.name() << "\" status=\"" << status_to_string(test) << "\" time=\"" << to_string(test.elapsed_time()) << "\" classname=\"" << test_class.test()->name() << "\"";
308  if (!test.failed())
309  file << " />" << std::endl;
310  else {
311  file << ">" << std::endl;
312  file << " <failure message=\"" << message_to_string(test) << "\" type= \"" << "\">" << "<![CDATA[" << cdata_message_to_string(test) << "]]></failure>" << std::endl;
313  file << " </testcase>" << std::endl;
314  }
315  }
316  file << " </testsuite>" << std::endl;
317  }
318  file << "</testsuites>" << std::endl;
319  file.close();
320  }
321  }
322 
323  std::vector<std::string> arguments;
324  std::string name_ = "AllTests";
325  std::unique_ptr<xtd::tunit::event_listener> event_listener_;
326  xtd::date_time end_time_point_;
327  int repeat_iteration_ = 0;
328  xtd::date_time start_time_point_;
329  };
330  }
331 }
The base 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
std::chrono::duration< long long, tick > ticks
Represents a tick duration.
Definition: ticks.h:17
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
unsigned int random_seed() const noexcept
Gets random seed value.
Definition: settings.h:113
int repeat_test() const noexcept
Gets repeat tests count.
Definition: settings.h:123
Contains xtd::system_exception exception.
std::string output_xml_path() const noexcept
Gets output xml path.
Definition: settings.h:94
static xtd::tunit::settings & default_settings() noexcept
Gets default settings instance.
The xtd namespace contains all fundamental classes to access Hardware, Os, System, and more.
Definition: system_report.h:17
Contains xtd::tunit::settings class.
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...
Represents a pseudo-random number generator, a device that produces a sequence of numbers that meet c...
Definition: random.h:37
const xtd::date_time & end_time() const noexcept
Gets unit test end time.
Definition: settings.h:140
std::default_random_engine generator() const
Gets the underlying generator.
Definition: random.h:65
std::string to_string(const date_time &value, const std::string &fmt, const std::locale &loc)
Convert a specified value into a string with specified format and locale.
Definition: date_time.h:1110
int run()
Runs all tests in this UnitTest object and prints the result.
Definition: unit_test.h:54
static bool enable_stack_trace()
Gets or sets if the generation of the stack trace is enabled.
Definition: system_exception.h:108
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
Contains xtd::date_time class.
const xtd::date_time & start_time() const noexcept
Gets unit test start time.
Definition: settings.h:144
Represents an instant in time, typically expressed as a date and time of day.
Definition: date_time.h:71
The xtd::uri::local_path data.
bool enable_stack_trace() const noexcept
Gets stack trace enabled.
Definition: settings.h:149
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.
xtd::ustring to_string() const noexcept override
Converts the value of the current xtd::date_time object to its equivalent string representation using...
Contains xtd::random 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
static int32_t to_int32(std::any value)
Convert std::any to int32.
Contains xtd::convert class.
static uint32_t to_uint32(std::any value)
Convert std::any to uint32.
The template class.
Definition: unit_test.h:35
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
static date_time now() noexcept
Gets a xtd::date_time object that is set to the current date and time on this computer, expressed as the local time.