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.
Loading...
Searching...
No Matches
unit_test.h
Go to the documentation of this file.
1
4#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
18namespace xtd {
20 namespace tunit {
22 template <typename test_class_t>
23 class test_class_attribute;
25
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().repeat_test() || xtd::tunit::settings::default_settings().repeat_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().repeat_test();}
99
100 bool repeat_tests() const noexcept {return xtd::tunit::settings::default_settings().repeat_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}
virtual const xtd::ustring & get_file_name() const
Gets the file name that contains the code that is executing. This information is typically extracted ...
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...
static stack_frame empty() noexcept
Return an empty stack frame.
The base class for assert.
Definition: base_assert.h:25
Represent the event listener class. Unit test call theses events when unit tests are processing.
Definition: event_listener.h:23
Represents the registered test class.
Definition: registered_test_class.h:19
std::string output_xml_path() const noexcept
Gets output xml path.
Definition: settings.h:94
int exit_status() const noexcept
Gets exit status.
Definition: settings.h:46
std::chrono::time_point< std::chrono::system_clock > end_time() const noexcept
Gets unit test end time.
Definition: settings.h:140
bool also_run_ignored_tests() const noexcept
Gets also run ignored test.
Definition: settings.h:36
bool shuffle_test() const noexcept
Gets shuffle tests.
Definition: settings.h:103
bool output_xml() const noexcept
Gets output xml.
Definition: settings.h:86
bool show_duration() const noexcept
Gets if show duration for each test.
Definition: settings.h:132
static xtd::tunit::settings & default_settings() noexcept
Get default settings instance.
bool output_color() const noexcept
Gets output color.
Definition: settings.h:78
void repeat_tests(int repeat_tests) noexcept
Sets repeat tests count.
Definition: settings.h:128
const std::string & filter_tests() const noexcept
Gets filter tests.
Definition: settings.h:57
int random_seed() const noexcept
Gets random seed value.
Definition: settings.h:113
bool list_tests() const noexcept
Gets list tests.
Definition: settings.h:70
int repeat_test() const noexcept
Gets repeat tests count.
Definition: settings.h:123
Definition: test_class_attribute.h:14
Definition: test_class.h:25
Definition: test.h:24
tunit_event_args is the base class for classes containing event data.
Definition: tunit_event_args.h:20
The template class.
Definition: unit_test.h:32
int run()
Runs all tests in this UnitTest object and prints the result.
Definition: unit_test.h:44
Contains xtd::tunit::event_listener class.
#define tunit_export_
Define shared library export.
Definition: tunit_export.h:13
std::string to_string(const value_t &value, const std::string &fmt, const std::locale &loc)
Convert a specified value into a string with specified format and locale.
Definition: to_string.h:37
@ add
The Add key.
@ end
The END key.
@ g
The G key.
The xtd namespace contains all fundamental classes to access Hardware, Os, System,...
Definition: system_report.h:17
Contains xtd::tunit::registered_test_class class.
Contains xtd::tunit::settings class.