QuickCppLib 0.10
Eliminate all the tedious hassle when making state-of-the-art C++ 14 - 23 libraries!
Loading...
Searching...
No Matches
quickcpplib::_xxx::unit_test Namespace Reference

Classes

struct  requirement_failed
 
struct  test_case
 
struct  test_case_registration
 
struct  test_suite
 
struct  test_suite_registration
 

Functions

jmp_buf & test_case_failed ()
 
std::vector< test_suite > & test_suites ()
 
test_suite *& current_test_suite ()
 
test_case *& current_test_case ()
 
int run (int argc, const char *const argv[])
 

Function Documentation

◆ test_case_failed()

jmp_buf & quickcpplib::_xxx::unit_test::test_case_failed ( )
externinline
113 {
114 static jmp_buf b;
115 return b;
116 }

◆ test_suites()

std::vector< test_suite > & quickcpplib::_xxx::unit_test::test_suites ( )
externinline
164 {
165 static std::vector<test_suite> v;
166 return v;
167 }

◆ current_test_suite()

test_suite *& quickcpplib::_xxx::unit_test::current_test_suite ( )
externinline
169 {
170 static test_suite *v;
171 return v;
172 }
Definition unit_test.hpp:155

◆ current_test_case()

test_case *& quickcpplib::_xxx::unit_test::current_test_case ( )
externinline
174 {
175 static test_case default_test_case("unnamed", "Default test case for unit test which don't declare test cases",
176 nullptr);
177 static test_case *v = &default_test_case;
178 return v;
179 }
Definition unit_test.hpp:119

◆ run()

int quickcpplib::_xxx::unit_test::run ( int  argc,
const char *const  argv[] 
)
externinline
181 {
182 std::regex enabled(".*"), disabled;
183 bool list_tests = false;
184 std::string output_xml;
185 for(int n = 1; n < argc; n++)
186 {
187 // Double -- means it's a flag
188 if(argv[n][0] == '-' && argv[n][1] == '-')
189 {
190 if(strstr(argv[n] + 2, "help"))
191 {
192 std::cout << "\nQuickCppLib minimal unit test framework\n\n" //
193 << "Usage: " << argv[0]
194 << " [options] [<regex for tests to run, defaults to .*>] [-<regex for tests to not run>]\n\n" //
195 << " --help : Prints this help\n" //
196 << " --list-tests : List matching tests\n" //
197 << " --reporter <format> : Reporter to use, only format possible is junit\n" //
198 << " --out <filename> : Write JUnit XML test report to filename\n" //
199 << std::endl;
200 return 0;
201 }
202 else if(strstr(argv[n] + 2, "list-tests"))
203 {
204 list_tests = true;
205 }
206 else if(strstr(argv[n] + 2, "reporter"))
207 {
208 if(n + 1 >= argc || strcmp(argv[n + 1], "junit"))
209 {
210 std::cerr << "--reporter must be followed by 'junit'" << std::endl;
211 return 1;
212 }
213 n++;
214 }
215 else if(strstr(argv[n] + 2, "out"))
216 {
217 if(n + 1 >= argc)
218 {
219 std::cerr << "--out must be followed by the output filename" << std::endl;
220 return 1;
221 }
222 output_xml = argv[n + 1];
223 n++;
224 }
225 }
226 else
227 {
228 // -regex is disabled, otherwise it's an enabled
229 if(argv[n][0] == '-')
230 disabled.assign(argv[n] + 1);
231 else
232 enabled.assign(argv[n]);
233 }
234 }
235 if(list_tests)
236 {
237 size_t maxname = 0;
238 for(const auto &j : test_suites())
239 {
240 for(const auto &i : j.test_cases)
241 {
242 if(strlen(i.name) > maxname)
243 maxname = strlen(i.name);
244 }
245 }
246 for(const auto &j : test_suites())
247 {
248 std::cout << "\n" << j.name << ":\n";
249 for(const auto &i : j.test_cases)
250 {
251 if(std::regex_match(i.name, enabled) && !std::regex_match(i.name, disabled))
252 {
253 std::string padding(maxname - strlen(i.name), ' ');
254 std::cout << " " << i.name << padding << " (" << i.desc << ")\n";
255 }
256 }
257 }
258 std::cout << std::endl;
259 return 0;
260 }
261 for(auto &j : test_suites())
262 {
263 for(auto &i : j.test_cases)
264 {
265 if(std::regex_match(i.name, enabled) && !std::regex_match(i.name, disabled))
266 {
267 current_test_case() = &i;
268 std::cout << std::endl << bold << blue << i.name << white << " : " << i.desc << normal << std::endl;
269 std::chrono::steady_clock::time_point begin, end;
270#ifdef __cpp_exceptions
271 try
272 {
273#else
274 if(setjmp(test_case_failed()))
275 {
276 end = std::chrono::steady_clock::now();
277 i.requirement_failed = true;
278 }
279 else
280#endif
281 {
282 begin = std::chrono::steady_clock::now();
283 i.func();
284 end = std::chrono::steady_clock::now();
285 }
286#ifdef __cpp_exceptions
287 }
288 catch(const requirement_failed &)
289 {
290 end = std::chrono::steady_clock::now();
291 i.requirement_failed = true;
292 }
293 catch(const std::exception &e)
294 {
295 end = std::chrono::steady_clock::now();
296 ++i.fails;
297 std::cerr << red << "FAILURE: std::exception '" << e.what() << "' thrown out of test case" << normal
298 << std::endl;
299 }
300 catch(...)
301 {
302 end = std::chrono::steady_clock::now();
303 ++i.fails;
304 std::cerr << red << "FAILURE: Exception thrown out of test case" << normal << std::endl;
305 }
306#endif
307 i.duration = end - begin;
308 if(i.passes)
309 std::cout << green << i.passes << " checks passed ";
310 if(i.fails)
311 std::cout << red << i.fails << " checks failed ";
312 std::cout << normal << "duration "
313 << std::chrono::duration_cast<std::chrono::milliseconds>(i.duration).count() << " ms" << std::endl;
314 }
315 else
316 i.skipped = true;
317 }
318 }
319 current_test_case() = nullptr;
320 std::ofstream oh;
321 if(!output_xml.empty())
322 {
323 oh.open(output_xml);
324 oh << R"(<?xml version="1.0" encoding="UTF-8"?>
325<testsuites>
326)";
327 }
328 size_t totalpassed = 0, totalfailed = 0, totalskipped = 0;
329 double totaltime = 0;
330 for(const auto &j : test_suites())
331 {
332 size_t passed = 0, failed = 0, skipped = 0;
333 double time = 0;
334 for(const auto &i : j.test_cases)
335 {
336 if(i.skipped)
337 ++skipped;
338 else if(i.fails)
339 ++failed;
340 else
341 ++passed;
342 time += std::chrono::duration_cast<std::chrono::duration<double>>(i.duration).count();
343 }
344 if(!output_xml.empty())
345 {
346 oh << " <testsuite name=\"" << j.name << "\" errors=\"" << 0 << "\" failures=\"" << failed << "\" skipped=\""
347 << skipped << "\" tests=\"" << j.test_cases.size() << "\" hostname=\"\" time=\"" << time
348 << "\" timestamp=\"\">\n";
349 for(const auto &i : j.test_cases)
350 {
351 oh << " <testcase classname=\"\" name=\"" << i.name << "\" time=\""
352 << std::chrono::duration_cast<std::chrono::duration<double>>(i.duration).count() << "\">";
353 if(i.skipped)
354 oh << "<skipped/>";
355 else if(i.fails)
356 oh << "<failure/>";
357 oh << "</testcase>\n";
358 }
359 oh << " </testsuite>\n";
360 }
361 totalpassed += passed;
362 totalfailed += failed;
363 totalskipped += skipped;
364 totaltime += time;
365 }
366 if(!output_xml.empty())
367 {
368 oh << R"(</testsuites>
369)";
370 }
371 std::cout << bold << white << "\n\nTest case summary: " << green << totalpassed << " passed " << red << totalfailed
372 << " failed " << yellow << totalskipped << " skipped" << normal << "\nTotal duration: " << totaltime
constexpr const char bold[]
Definition console_colours.hpp:135
constexpr const char yellow[]
Definition console_colours.hpp:131
constexpr const char normal[]
Definition console_colours.hpp:136
constexpr const char blue[]
Definition console_colours.hpp:130
constexpr const char red[]
Definition console_colours.hpp:128
constexpr const char green[]
Definition console_colours.hpp:129
constexpr const char white[]
Definition console_colours.hpp:134
std::vector< test_suite > & test_suites()
Definition unit_test.hpp:163
jmp_buf & test_case_failed()
Definition unit_test.hpp:112
test_case *& current_test_case()
Definition unit_test.hpp:173