Marc Kupietz | 6663f11 | 2021-03-14 09:20:59 +0100 | [diff] [blame^] | 1 | /* |
| 2 | * Acutest -- Another C/C++ Unit Test facility |
| 3 | * <https://github.com/mity/acutest> |
| 4 | * |
| 5 | * Copyright 2013-2020 Martin Mitas |
| 6 | * Copyright 2019 Garrett D'Amore |
| 7 | * |
| 8 | * Permission is hereby granted, free of charge, to any person obtaining a |
| 9 | * copy of this software and associated documentation files (the "Software"), |
| 10 | * to deal in the Software without restriction, including without limitation |
| 11 | * the rights to use, copy, modify, merge, publish, distribute, sublicense, |
| 12 | * and/or sell copies of the Software, and to permit persons to whom the |
| 13 | * Software is furnished to do so, subject to the following conditions: |
| 14 | * |
| 15 | * The above copyright notice and this permission notice shall be included in |
| 16 | * all copies or substantial portions of the Software. |
| 17 | * |
| 18 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS |
| 19 | * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
| 20 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
| 21 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
| 22 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING |
| 23 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS |
| 24 | * IN THE SOFTWARE. |
| 25 | */ |
| 26 | |
| 27 | #ifndef ACUTEST_H |
| 28 | #define ACUTEST_H |
| 29 | |
| 30 | |
| 31 | /************************ |
| 32 | *** Public interface *** |
| 33 | ************************/ |
| 34 | |
| 35 | /* By default, "acutest.h" provides the main program entry point (function |
| 36 | * main()). However, if the test suite is composed of multiple source files |
| 37 | * which include "acutest.h", then this causes a problem of multiple main() |
| 38 | * definitions. To avoid this problem, #define macro TEST_NO_MAIN in all |
| 39 | * compilation units but one. |
| 40 | */ |
| 41 | |
| 42 | /* Macro to specify list of unit tests in the suite. |
| 43 | * The unit test implementation MUST provide list of unit tests it implements |
| 44 | * with this macro: |
| 45 | * |
| 46 | * TEST_LIST = { |
| 47 | * { "test1_name", test1_func_ptr }, |
| 48 | * { "test2_name", test2_func_ptr }, |
| 49 | * ... |
| 50 | * { NULL, NULL } // zeroed record marking the end of the list |
| 51 | * }; |
| 52 | * |
| 53 | * The list specifies names of each test (must be unique) and pointer to |
| 54 | * a function implementing it. The function does not take any arguments |
| 55 | * and has no return values, i.e. every test function has to be compatible |
| 56 | * with this prototype: |
| 57 | * |
| 58 | * void test_func(void); |
| 59 | * |
| 60 | * Note the list has to be ended with a zeroed record. |
| 61 | */ |
| 62 | #define TEST_LIST const struct acutest_test_ acutest_list_[] |
| 63 | |
| 64 | |
| 65 | /* Macros for testing whether an unit test succeeds or fails. These macros |
| 66 | * can be used arbitrarily in functions implementing the unit tests. |
| 67 | * |
| 68 | * If any condition fails throughout execution of a test, the test fails. |
| 69 | * |
| 70 | * TEST_CHECK takes only one argument (the condition), TEST_CHECK_ allows |
| 71 | * also to specify an error message to print out if the condition fails. |
| 72 | * (It expects printf-like format string and its parameters). The macros |
| 73 | * return non-zero (condition passes) or 0 (condition fails). |
| 74 | * |
| 75 | * That can be useful when more conditions should be checked only if some |
| 76 | * preceding condition passes, as illustrated in this code snippet: |
| 77 | * |
| 78 | * SomeStruct* ptr = allocate_some_struct(); |
| 79 | * if(TEST_CHECK(ptr != NULL)) { |
| 80 | * TEST_CHECK(ptr->member1 < 100); |
| 81 | * TEST_CHECK(ptr->member2 > 200); |
| 82 | * } |
| 83 | */ |
| 84 | #define TEST_CHECK_(cond,...) acutest_check_((cond), __FILE__, __LINE__, __VA_ARGS__) |
| 85 | #define TEST_CHECK(cond) acutest_check_((cond), __FILE__, __LINE__, "%s", #cond) |
| 86 | |
| 87 | |
| 88 | /* These macros are the same as TEST_CHECK_ and TEST_CHECK except that if the |
| 89 | * condition fails, the currently executed unit test is immediately aborted. |
| 90 | * |
| 91 | * That is done either by calling abort() if the unit test is executed as a |
| 92 | * child process; or via longjmp() if the unit test is executed within the |
| 93 | * main Acutest process. |
| 94 | * |
| 95 | * As a side effect of such abortion, your unit tests may cause memory leaks, |
| 96 | * unflushed file descriptors, and other phenomena caused by the abortion. |
| 97 | * |
| 98 | * Therefore you should not use these as a general replacement for TEST_CHECK. |
| 99 | * Use it with some caution, especially if your test causes some other side |
| 100 | * effects to the outside world (e.g. communicating with some server, inserting |
| 101 | * into a database etc.). |
| 102 | */ |
| 103 | #define TEST_ASSERT_(cond,...) \ |
| 104 | do { \ |
| 105 | if(!acutest_check_((cond), __FILE__, __LINE__, __VA_ARGS__)) \ |
| 106 | acutest_abort_(); \ |
| 107 | } while(0) |
| 108 | #define TEST_ASSERT(cond) \ |
| 109 | do { \ |
| 110 | if(!acutest_check_((cond), __FILE__, __LINE__, "%s", #cond)) \ |
| 111 | acutest_abort_(); \ |
| 112 | } while(0) |
| 113 | |
| 114 | |
| 115 | #ifdef __cplusplus |
| 116 | /* Macros to verify that the code (the 1st argument) throws exception of given |
| 117 | * type (the 2nd argument). (Note these macros are only available in C++.) |
| 118 | * |
| 119 | * TEST_EXCEPTION_ is like TEST_EXCEPTION but accepts custom printf-like |
| 120 | * message. |
| 121 | * |
| 122 | * For example: |
| 123 | * |
| 124 | * TEST_EXCEPTION(function_that_throw(), ExpectedExceptionType); |
| 125 | * |
| 126 | * If the function_that_throw() throws ExpectedExceptionType, the check passes. |
| 127 | * If the function throws anything incompatible with ExpectedExceptionType |
| 128 | * (or if it does not thrown an exception at all), the check fails. |
| 129 | */ |
| 130 | #define TEST_EXCEPTION(code, exctype) \ |
| 131 | do { \ |
| 132 | bool exc_ok_ = false; \ |
| 133 | const char *msg_ = NULL; \ |
| 134 | try { \ |
| 135 | code; \ |
| 136 | msg_ = "No exception thrown."; \ |
| 137 | } catch(exctype const&) { \ |
| 138 | exc_ok_= true; \ |
| 139 | } catch(...) { \ |
| 140 | msg_ = "Unexpected exception thrown."; \ |
| 141 | } \ |
| 142 | acutest_check_(exc_ok_, __FILE__, __LINE__, #code " throws " #exctype);\ |
| 143 | if(msg_ != NULL) \ |
| 144 | acutest_message_("%s", msg_); \ |
| 145 | } while(0) |
| 146 | #define TEST_EXCEPTION_(code, exctype, ...) \ |
| 147 | do { \ |
| 148 | bool exc_ok_ = false; \ |
| 149 | const char *msg_ = NULL; \ |
| 150 | try { \ |
| 151 | code; \ |
| 152 | msg_ = "No exception thrown."; \ |
| 153 | } catch(exctype const&) { \ |
| 154 | exc_ok_= true; \ |
| 155 | } catch(...) { \ |
| 156 | msg_ = "Unexpected exception thrown."; \ |
| 157 | } \ |
| 158 | acutest_check_(exc_ok_, __FILE__, __LINE__, __VA_ARGS__); \ |
| 159 | if(msg_ != NULL) \ |
| 160 | acutest_message_("%s", msg_); \ |
| 161 | } while(0) |
| 162 | #endif /* #ifdef __cplusplus */ |
| 163 | |
| 164 | |
| 165 | /* Sometimes it is useful to split execution of more complex unit tests to some |
| 166 | * smaller parts and associate those parts with some names. |
| 167 | * |
| 168 | * This is especially handy if the given unit test is implemented as a loop |
| 169 | * over some vector of multiple testing inputs. Using these macros allow to use |
| 170 | * sort of subtitle for each iteration of the loop (e.g. outputting the input |
| 171 | * itself or a name associated to it), so that if any TEST_CHECK condition |
| 172 | * fails in the loop, it can be easily seen which iteration triggers the |
| 173 | * failure, without the need to manually output the iteration-specific data in |
| 174 | * every single TEST_CHECK inside the loop body. |
| 175 | * |
| 176 | * TEST_CASE allows to specify only single string as the name of the case, |
| 177 | * TEST_CASE_ provides all the power of printf-like string formatting. |
| 178 | * |
| 179 | * Note that the test cases cannot be nested. Starting a new test case ends |
| 180 | * implicitly the previous one. To end the test case explicitly (e.g. to end |
| 181 | * the last test case after exiting the loop), you may use TEST_CASE(NULL). |
| 182 | */ |
| 183 | #define TEST_CASE_(...) acutest_case_(__VA_ARGS__) |
| 184 | #define TEST_CASE(name) acutest_case_("%s", name) |
| 185 | |
| 186 | |
| 187 | /* Maximal output per TEST_CASE call. Longer messages are cut. |
| 188 | * You may define another limit prior including "acutest.h" |
| 189 | */ |
| 190 | #ifndef TEST_CASE_MAXSIZE |
| 191 | #define TEST_CASE_MAXSIZE 64 |
| 192 | #endif |
| 193 | |
| 194 | |
| 195 | /* printf-like macro for outputting an extra information about a failure. |
| 196 | * |
| 197 | * Intended use is to output some computed output versus the expected value, |
| 198 | * e.g. like this: |
| 199 | * |
| 200 | * if(!TEST_CHECK(produced == expected)) { |
| 201 | * TEST_MSG("Expected: %d", expected); |
| 202 | * TEST_MSG("Produced: %d", produced); |
| 203 | * } |
| 204 | * |
| 205 | * Note the message is only written down if the most recent use of any checking |
| 206 | * macro (like e.g. TEST_CHECK or TEST_EXCEPTION) in the current test failed. |
| 207 | * This means the above is equivalent to just this: |
| 208 | * |
| 209 | * TEST_CHECK(produced == expected); |
| 210 | * TEST_MSG("Expected: %d", expected); |
| 211 | * TEST_MSG("Produced: %d", produced); |
| 212 | * |
| 213 | * The macro can deal with multi-line output fairly well. It also automatically |
| 214 | * adds a final new-line if there is none present. |
| 215 | */ |
| 216 | #define TEST_MSG(...) acutest_message_(__VA_ARGS__) |
| 217 | |
| 218 | |
| 219 | /* Maximal output per TEST_MSG call. Longer messages are cut. |
| 220 | * You may define another limit prior including "acutest.h" |
| 221 | */ |
| 222 | #ifndef TEST_MSG_MAXSIZE |
| 223 | #define TEST_MSG_MAXSIZE 1024 |
| 224 | #endif |
| 225 | |
| 226 | |
| 227 | /* Macro for dumping a block of memory. |
| 228 | * |
| 229 | * Its intended use is very similar to what TEST_MSG is for, but instead of |
| 230 | * generating any printf-like message, this is for dumping raw block of a |
| 231 | * memory in a hexadecimal form: |
| 232 | * |
| 233 | * TEST_CHECK(size_produced == size_expected && |
| 234 | * memcmp(addr_produced, addr_expected, size_produced) == 0); |
| 235 | * TEST_DUMP("Expected:", addr_expected, size_expected); |
| 236 | * TEST_DUMP("Produced:", addr_produced, size_produced); |
| 237 | */ |
| 238 | #define TEST_DUMP(title, addr, size) acutest_dump_(title, addr, size) |
| 239 | |
| 240 | /* Maximal output per TEST_DUMP call (in bytes to dump). Longer blocks are cut. |
| 241 | * You may define another limit prior including "acutest.h" |
| 242 | */ |
| 243 | #ifndef TEST_DUMP_MAXSIZE |
| 244 | #define TEST_DUMP_MAXSIZE 1024 |
| 245 | #endif |
| 246 | |
| 247 | |
| 248 | /* Common test initialiation/clean-up |
| 249 | * |
| 250 | * In some test suites, it may be needed to perform some sort of the same |
| 251 | * initialization and/or clean-up in all the tests. |
| 252 | * |
| 253 | * Such test suites may use macros TEST_INIT and/or TEST_FINI prior including |
| 254 | * this header. The expansion of the macro is then used as a body of helper |
| 255 | * function called just before executing every single (TEST_INIT) or just after |
| 256 | * it ends (TEST_FINI). |
| 257 | * |
| 258 | * Examples of various ways how to use the macro TEST_INIT: |
| 259 | * |
| 260 | * #define TEST_INIT my_init_func(); |
| 261 | * #define TEST_INIT my_init_func() // Works even without the semicolon |
| 262 | * #define TEST_INIT setlocale(LC_ALL, NULL); |
| 263 | * #define TEST_INIT { setlocale(LC_ALL, NULL); my_init_func(); } |
| 264 | * |
| 265 | * TEST_FINI is to be used in the same way. |
| 266 | */ |
| 267 | |
| 268 | |
| 269 | /********************** |
| 270 | *** Implementation *** |
| 271 | **********************/ |
| 272 | |
| 273 | /* The unit test files should not rely on anything below. */ |
| 274 | |
| 275 | #include <ctype.h> |
| 276 | #include <stdarg.h> |
| 277 | #include <stdio.h> |
| 278 | #include <stdlib.h> |
| 279 | #include <string.h> |
| 280 | #include <setjmp.h> |
| 281 | |
| 282 | #if defined(unix) || defined(__unix__) || defined(__unix) || defined(__APPLE__) |
| 283 | #define ACUTEST_UNIX_ 1 |
| 284 | #include <errno.h> |
| 285 | #include <libgen.h> |
| 286 | #include <unistd.h> |
| 287 | #include <sys/types.h> |
| 288 | #include <sys/wait.h> |
| 289 | #include <signal.h> |
| 290 | #include <time.h> |
| 291 | |
| 292 | #if defined CLOCK_PROCESS_CPUTIME_ID && defined CLOCK_MONOTONIC |
| 293 | #define ACUTEST_HAS_POSIX_TIMER_ 1 |
| 294 | #endif |
| 295 | #endif |
| 296 | |
| 297 | #if defined(_gnu_linux_) || defined(__linux__) |
| 298 | #define ACUTEST_LINUX_ 1 |
| 299 | #include <fcntl.h> |
| 300 | #include <sys/stat.h> |
| 301 | #endif |
| 302 | |
| 303 | #if defined(_WIN32) || defined(__WIN32__) || defined(__WINDOWS__) |
| 304 | #define ACUTEST_WIN_ 1 |
| 305 | #include <windows.h> |
| 306 | #include <io.h> |
| 307 | #endif |
| 308 | |
| 309 | #ifdef __cplusplus |
| 310 | #include <exception> |
| 311 | #endif |
| 312 | |
| 313 | #ifdef __has_include |
| 314 | #if __has_include(<valgrind.h>) |
| 315 | #include <valgrind.h> |
| 316 | #endif |
| 317 | #endif |
| 318 | |
| 319 | /* Enable the use of the non-standard keyword __attribute__ to silence warnings under some compilers */ |
| 320 | #if defined(__GNUC__) || defined(__clang__) |
| 321 | #define ACUTEST_ATTRIBUTE_(attr) __attribute__((attr)) |
| 322 | #else |
| 323 | #define ACUTEST_ATTRIBUTE_(attr) |
| 324 | #endif |
| 325 | |
| 326 | /* Note our global private identifiers end with '_' to mitigate risk of clash |
| 327 | * with the unit tests implementation. */ |
| 328 | |
| 329 | #ifdef __cplusplus |
| 330 | extern "C" { |
| 331 | #endif |
| 332 | |
| 333 | #ifdef _MSC_VER |
| 334 | /* In the multi-platform code like ours, we cannot use the non-standard |
| 335 | * "safe" functions from Microsoft C lib like e.g. sprintf_s() instead of |
| 336 | * standard sprintf(). Hence, lets disable the warning C4996. */ |
| 337 | #pragma warning(push) |
| 338 | #pragma warning(disable: 4996) |
| 339 | #endif |
| 340 | |
| 341 | |
| 342 | struct acutest_test_ { |
| 343 | const char* name; |
| 344 | void (*func)(void); |
| 345 | }; |
| 346 | |
| 347 | struct acutest_test_data_ { |
| 348 | unsigned char flags; |
| 349 | double duration; |
| 350 | }; |
| 351 | |
| 352 | enum { |
| 353 | ACUTEST_FLAG_RUN_ = 1 << 0, |
| 354 | ACUTEST_FLAG_SUCCESS_ = 1 << 1, |
| 355 | ACUTEST_FLAG_FAILURE_ = 1 << 2, |
| 356 | }; |
| 357 | |
| 358 | extern const struct acutest_test_ acutest_list_[]; |
| 359 | |
| 360 | int acutest_check_(int cond, const char* file, int line, const char* fmt, ...); |
| 361 | void acutest_case_(const char* fmt, ...); |
| 362 | void acutest_message_(const char* fmt, ...); |
| 363 | void acutest_dump_(const char* title, const void* addr, size_t size); |
| 364 | void acutest_abort_(void) ACUTEST_ATTRIBUTE_(noreturn); |
| 365 | |
| 366 | |
| 367 | #ifndef TEST_NO_MAIN |
| 368 | |
| 369 | static char* acutest_argv0_ = NULL; |
| 370 | static size_t acutest_list_size_ = 0; |
| 371 | static struct acutest_test_data_* acutest_test_data_ = NULL; |
| 372 | static size_t acutest_count_ = 0; |
| 373 | static int acutest_no_exec_ = -1; |
| 374 | static int acutest_no_summary_ = 0; |
| 375 | static int acutest_tap_ = 0; |
| 376 | static int acutest_skip_mode_ = 0; |
| 377 | static int acutest_worker_ = 0; |
| 378 | static int acutest_worker_index_ = 0; |
| 379 | static int acutest_cond_failed_ = 0; |
| 380 | static int acutest_was_aborted_ = 0; |
| 381 | static FILE *acutest_xml_output_ = NULL; |
| 382 | |
| 383 | static int acutest_stat_failed_units_ = 0; |
| 384 | static int acutest_stat_run_units_ = 0; |
| 385 | |
| 386 | static const struct acutest_test_* acutest_current_test_ = NULL; |
| 387 | static int acutest_current_index_ = 0; |
| 388 | static char acutest_case_name_[TEST_CASE_MAXSIZE] = ""; |
| 389 | static int acutest_test_already_logged_ = 0; |
| 390 | static int acutest_case_already_logged_ = 0; |
| 391 | static int acutest_verbose_level_ = 2; |
| 392 | static int acutest_test_failures_ = 0; |
| 393 | static int acutest_colorize_ = 0; |
| 394 | static int acutest_timer_ = 0; |
| 395 | |
| 396 | static int acutest_abort_has_jmp_buf_ = 0; |
| 397 | static jmp_buf acutest_abort_jmp_buf_; |
| 398 | |
| 399 | |
| 400 | static void |
| 401 | acutest_cleanup_(void) |
| 402 | { |
| 403 | free((void*) acutest_test_data_); |
| 404 | } |
| 405 | |
| 406 | static void ACUTEST_ATTRIBUTE_(noreturn) |
| 407 | acutest_exit_(int exit_code) |
| 408 | { |
| 409 | acutest_cleanup_(); |
| 410 | exit(exit_code); |
| 411 | } |
| 412 | |
| 413 | #if defined ACUTEST_WIN_ |
| 414 | typedef LARGE_INTEGER acutest_timer_type_; |
| 415 | static LARGE_INTEGER acutest_timer_freq_; |
| 416 | static acutest_timer_type_ acutest_timer_start_; |
| 417 | static acutest_timer_type_ acutest_timer_end_; |
| 418 | |
| 419 | static void |
| 420 | acutest_timer_init_(void) |
| 421 | { |
| 422 | QueryPerformanceFrequency(´st_timer_freq_); |
| 423 | } |
| 424 | |
| 425 | static void |
| 426 | acutest_timer_get_time_(LARGE_INTEGER* ts) |
| 427 | { |
| 428 | QueryPerformanceCounter(ts); |
| 429 | } |
| 430 | |
| 431 | static double |
| 432 | acutest_timer_diff_(LARGE_INTEGER start, LARGE_INTEGER end) |
| 433 | { |
| 434 | double duration = (double)(end.QuadPart - start.QuadPart); |
| 435 | duration /= (double)acutest_timer_freq_.QuadPart; |
| 436 | return duration; |
| 437 | } |
| 438 | |
| 439 | static void |
| 440 | acutest_timer_print_diff_(void) |
| 441 | { |
| 442 | printf("%.6lf secs", acutest_timer_diff_(acutest_timer_start_, acutest_timer_end_)); |
| 443 | } |
| 444 | #elif defined ACUTEST_HAS_POSIX_TIMER_ |
| 445 | static clockid_t acutest_timer_id_; |
| 446 | typedef struct timespec acutest_timer_type_; |
| 447 | static acutest_timer_type_ acutest_timer_start_; |
| 448 | static acutest_timer_type_ acutest_timer_end_; |
| 449 | |
| 450 | static void |
| 451 | acutest_timer_init_(void) |
| 452 | { |
| 453 | if(acutest_timer_ == 1) |
| 454 | acutest_timer_id_ = CLOCK_MONOTONIC; |
| 455 | else if(acutest_timer_ == 2) |
| 456 | acutest_timer_id_ = CLOCK_PROCESS_CPUTIME_ID; |
| 457 | } |
| 458 | |
| 459 | static void |
| 460 | acutest_timer_get_time_(struct timespec* ts) |
| 461 | { |
| 462 | clock_gettime(acutest_timer_id_, ts); |
| 463 | } |
| 464 | |
| 465 | static double |
| 466 | acutest_timer_diff_(struct timespec start, struct timespec end) |
| 467 | { |
| 468 | double endns; |
| 469 | double startns; |
| 470 | |
| 471 | endns = end.tv_sec; |
| 472 | endns *= 1e9; |
| 473 | endns += end.tv_nsec; |
| 474 | |
| 475 | startns = start.tv_sec; |
| 476 | startns *= 1e9; |
| 477 | startns += start.tv_nsec; |
| 478 | |
| 479 | return ((endns - startns)/ 1e9); |
| 480 | } |
| 481 | |
| 482 | static void |
| 483 | acutest_timer_print_diff_(void) |
| 484 | { |
| 485 | printf("%.6lf secs", |
| 486 | acutest_timer_diff_(acutest_timer_start_, acutest_timer_end_)); |
| 487 | } |
| 488 | #else |
| 489 | typedef int acutest_timer_type_; |
| 490 | static acutest_timer_type_ acutest_timer_start_; |
| 491 | static acutest_timer_type_ acutest_timer_end_; |
| 492 | |
| 493 | void |
| 494 | acutest_timer_init_(void) |
| 495 | {} |
| 496 | |
| 497 | static void |
| 498 | acutest_timer_get_time_(int* ts) |
| 499 | { |
| 500 | (void) ts; |
| 501 | } |
| 502 | |
| 503 | static double |
| 504 | acutest_timer_diff_(int start, int end) |
| 505 | { |
| 506 | (void) start; |
| 507 | (void) end; |
| 508 | return 0.0; |
| 509 | } |
| 510 | |
| 511 | static void |
| 512 | acutest_timer_print_diff_(void) |
| 513 | {} |
| 514 | #endif |
| 515 | |
| 516 | #define ACUTEST_COLOR_DEFAULT_ 0 |
| 517 | #define ACUTEST_COLOR_GREEN_ 1 |
| 518 | #define ACUTEST_COLOR_RED_ 2 |
| 519 | #define ACUTEST_COLOR_DEFAULT_INTENSIVE_ 3 |
| 520 | #define ACUTEST_COLOR_GREEN_INTENSIVE_ 4 |
| 521 | #define ACUTEST_COLOR_RED_INTENSIVE_ 5 |
| 522 | |
| 523 | static int ACUTEST_ATTRIBUTE_(format (printf, 2, 3)) |
| 524 | acutest_colored_printf_(int color, const char* fmt, ...) |
| 525 | { |
| 526 | va_list args; |
| 527 | char buffer[256]; |
| 528 | int n; |
| 529 | |
| 530 | va_start(args, fmt); |
| 531 | vsnprintf(buffer, sizeof(buffer), fmt, args); |
| 532 | va_end(args); |
| 533 | buffer[sizeof(buffer)-1] = '\0'; |
| 534 | |
| 535 | if(!acutest_colorize_) { |
| 536 | return printf("%s", buffer); |
| 537 | } |
| 538 | |
| 539 | #if defined ACUTEST_UNIX_ |
| 540 | { |
| 541 | const char* col_str; |
| 542 | switch(color) { |
| 543 | case ACUTEST_COLOR_GREEN_: col_str = "\033[0;32m"; break; |
| 544 | case ACUTEST_COLOR_RED_: col_str = "\033[0;31m"; break; |
| 545 | case ACUTEST_COLOR_GREEN_INTENSIVE_: col_str = "\033[1;32m"; break; |
| 546 | case ACUTEST_COLOR_RED_INTENSIVE_: col_str = "\033[1;31m"; break; |
| 547 | case ACUTEST_COLOR_DEFAULT_INTENSIVE_: col_str = "\033[1m"; break; |
| 548 | default: col_str = "\033[0m"; break; |
| 549 | } |
| 550 | printf("%s", col_str); |
| 551 | n = printf("%s", buffer); |
| 552 | printf("\033[0m"); |
| 553 | return n; |
| 554 | } |
| 555 | #elif defined ACUTEST_WIN_ |
| 556 | { |
| 557 | HANDLE h; |
| 558 | CONSOLE_SCREEN_BUFFER_INFO info; |
| 559 | WORD attr; |
| 560 | |
| 561 | h = GetStdHandle(STD_OUTPUT_HANDLE); |
| 562 | GetConsoleScreenBufferInfo(h, &info); |
| 563 | |
| 564 | switch(color) { |
| 565 | case ACUTEST_COLOR_GREEN_: attr = FOREGROUND_GREEN; break; |
| 566 | case ACUTEST_COLOR_RED_: attr = FOREGROUND_RED; break; |
| 567 | case ACUTEST_COLOR_GREEN_INTENSIVE_: attr = FOREGROUND_GREEN | FOREGROUND_INTENSITY; break; |
| 568 | case ACUTEST_COLOR_RED_INTENSIVE_: attr = FOREGROUND_RED | FOREGROUND_INTENSITY; break; |
| 569 | case ACUTEST_COLOR_DEFAULT_INTENSIVE_: attr = FOREGROUND_BLUE | FOREGROUND_GREEN | FOREGROUND_RED | FOREGROUND_INTENSITY; break; |
| 570 | default: attr = 0; break; |
| 571 | } |
| 572 | if(attr != 0) |
| 573 | SetConsoleTextAttribute(h, attr); |
| 574 | n = printf("%s", buffer); |
| 575 | SetConsoleTextAttribute(h, info.wAttributes); |
| 576 | return n; |
| 577 | } |
| 578 | #else |
| 579 | n = printf("%s", buffer); |
| 580 | return n; |
| 581 | #endif |
| 582 | } |
| 583 | |
| 584 | static void |
| 585 | acutest_begin_test_line_(const struct acutest_test_* test) |
| 586 | { |
| 587 | if(!acutest_tap_) { |
| 588 | if(acutest_verbose_level_ >= 3) { |
| 589 | acutest_colored_printf_(ACUTEST_COLOR_DEFAULT_INTENSIVE_, "Test %s:\n", test->name); |
| 590 | acutest_test_already_logged_++; |
| 591 | } else if(acutest_verbose_level_ >= 1) { |
| 592 | int n; |
| 593 | char spaces[48]; |
| 594 | |
| 595 | n = acutest_colored_printf_(ACUTEST_COLOR_DEFAULT_INTENSIVE_, "Test %s... ", test->name); |
| 596 | memset(spaces, ' ', sizeof(spaces)); |
| 597 | if(n < (int) sizeof(spaces)) |
| 598 | printf("%.*s", (int) sizeof(spaces) - n, spaces); |
| 599 | } else { |
| 600 | acutest_test_already_logged_ = 1; |
| 601 | } |
| 602 | } |
| 603 | } |
| 604 | |
| 605 | static void |
| 606 | acutest_finish_test_line_(int result) |
| 607 | { |
| 608 | if(acutest_tap_) { |
| 609 | const char* str = (result == 0) ? "ok" : "not ok"; |
| 610 | |
| 611 | printf("%s %d - %s\n", str, acutest_current_index_ + 1, acutest_current_test_->name); |
| 612 | |
| 613 | if(result == 0 && acutest_timer_) { |
| 614 | printf("# Duration: "); |
| 615 | acutest_timer_print_diff_(); |
| 616 | printf("\n"); |
| 617 | } |
| 618 | } else { |
| 619 | int color = (result == 0) ? ACUTEST_COLOR_GREEN_INTENSIVE_ : ACUTEST_COLOR_RED_INTENSIVE_; |
| 620 | const char* str = (result == 0) ? "OK" : "FAILED"; |
| 621 | printf("[ "); |
| 622 | acutest_colored_printf_(color, "%s", str); |
| 623 | printf(" ]"); |
| 624 | |
| 625 | if(result == 0 && acutest_timer_) { |
| 626 | printf(" "); |
| 627 | acutest_timer_print_diff_(); |
| 628 | } |
| 629 | |
| 630 | printf("\n"); |
| 631 | } |
| 632 | } |
| 633 | |
| 634 | static void |
| 635 | acutest_line_indent_(int level) |
| 636 | { |
| 637 | static const char spaces[] = " "; |
| 638 | int n = level * 2; |
| 639 | |
| 640 | if(acutest_tap_ && n > 0) { |
| 641 | n--; |
| 642 | printf("#"); |
| 643 | } |
| 644 | |
| 645 | while(n > 16) { |
| 646 | printf("%s", spaces); |
| 647 | n -= 16; |
| 648 | } |
| 649 | printf("%.*s", n, spaces); |
| 650 | } |
| 651 | |
| 652 | int ACUTEST_ATTRIBUTE_(format (printf, 4, 5)) |
| 653 | acutest_check_(int cond, const char* file, int line, const char* fmt, ...) |
| 654 | { |
| 655 | const char *result_str; |
| 656 | int result_color; |
| 657 | int verbose_level; |
| 658 | |
| 659 | if(cond) { |
| 660 | result_str = "ok"; |
| 661 | result_color = ACUTEST_COLOR_GREEN_; |
| 662 | verbose_level = 3; |
| 663 | } else { |
| 664 | if(!acutest_test_already_logged_ && acutest_current_test_ != NULL) |
| 665 | acutest_finish_test_line_(-1); |
| 666 | |
| 667 | result_str = "failed"; |
| 668 | result_color = ACUTEST_COLOR_RED_; |
| 669 | verbose_level = 2; |
| 670 | acutest_test_failures_++; |
| 671 | acutest_test_already_logged_++; |
| 672 | } |
| 673 | |
| 674 | if(acutest_verbose_level_ >= verbose_level) { |
| 675 | va_list args; |
| 676 | |
| 677 | if(!acutest_case_already_logged_ && acutest_case_name_[0]) { |
| 678 | acutest_line_indent_(1); |
| 679 | acutest_colored_printf_(ACUTEST_COLOR_DEFAULT_INTENSIVE_, "Case %s:\n", acutest_case_name_); |
| 680 | acutest_test_already_logged_++; |
| 681 | acutest_case_already_logged_++; |
| 682 | } |
| 683 | |
| 684 | acutest_line_indent_(acutest_case_name_[0] ? 2 : 1); |
| 685 | if(file != NULL) { |
| 686 | #ifdef ACUTEST_WIN_ |
| 687 | const char* lastsep1 = strrchr(file, '\\'); |
| 688 | const char* lastsep2 = strrchr(file, '/'); |
| 689 | if(lastsep1 == NULL) |
| 690 | lastsep1 = file-1; |
| 691 | if(lastsep2 == NULL) |
| 692 | lastsep2 = file-1; |
| 693 | file = (lastsep1 > lastsep2 ? lastsep1 : lastsep2) + 1; |
| 694 | #else |
| 695 | const char* lastsep = strrchr(file, '/'); |
| 696 | if(lastsep != NULL) |
| 697 | file = lastsep+1; |
| 698 | #endif |
| 699 | printf("%s:%d: Check ", file, line); |
| 700 | } |
| 701 | |
| 702 | va_start(args, fmt); |
| 703 | vprintf(fmt, args); |
| 704 | va_end(args); |
| 705 | |
| 706 | printf("... "); |
| 707 | acutest_colored_printf_(result_color, "%s", result_str); |
| 708 | printf("\n"); |
| 709 | acutest_test_already_logged_++; |
| 710 | } |
| 711 | |
| 712 | acutest_cond_failed_ = (cond == 0); |
| 713 | return !acutest_cond_failed_; |
| 714 | } |
| 715 | |
| 716 | void ACUTEST_ATTRIBUTE_(format (printf, 1, 2)) |
| 717 | acutest_case_(const char* fmt, ...) |
| 718 | { |
| 719 | va_list args; |
| 720 | |
| 721 | if(acutest_verbose_level_ < 2) |
| 722 | return; |
| 723 | |
| 724 | if(acutest_case_name_[0]) { |
| 725 | acutest_case_already_logged_ = 0; |
| 726 | acutest_case_name_[0] = '\0'; |
| 727 | } |
| 728 | |
| 729 | if(fmt == NULL) |
| 730 | return; |
| 731 | |
| 732 | va_start(args, fmt); |
| 733 | vsnprintf(acutest_case_name_, sizeof(acutest_case_name_) - 1, fmt, args); |
| 734 | va_end(args); |
| 735 | acutest_case_name_[sizeof(acutest_case_name_) - 1] = '\0'; |
| 736 | |
| 737 | if(acutest_verbose_level_ >= 3) { |
| 738 | acutest_line_indent_(1); |
| 739 | acutest_colored_printf_(ACUTEST_COLOR_DEFAULT_INTENSIVE_, "Case %s:\n", acutest_case_name_); |
| 740 | acutest_test_already_logged_++; |
| 741 | acutest_case_already_logged_++; |
| 742 | } |
| 743 | } |
| 744 | |
| 745 | void ACUTEST_ATTRIBUTE_(format (printf, 1, 2)) |
| 746 | acutest_message_(const char* fmt, ...) |
| 747 | { |
| 748 | char buffer[TEST_MSG_MAXSIZE]; |
| 749 | char* line_beg; |
| 750 | char* line_end; |
| 751 | va_list args; |
| 752 | |
| 753 | if(acutest_verbose_level_ < 2) |
| 754 | return; |
| 755 | |
| 756 | /* We allow extra message only when something is already wrong in the |
| 757 | * current test. */ |
| 758 | if(acutest_current_test_ == NULL || !acutest_cond_failed_) |
| 759 | return; |
| 760 | |
| 761 | va_start(args, fmt); |
| 762 | vsnprintf(buffer, TEST_MSG_MAXSIZE, fmt, args); |
| 763 | va_end(args); |
| 764 | buffer[TEST_MSG_MAXSIZE-1] = '\0'; |
| 765 | |
| 766 | line_beg = buffer; |
| 767 | while(1) { |
| 768 | line_end = strchr(line_beg, '\n'); |
| 769 | if(line_end == NULL) |
| 770 | break; |
| 771 | acutest_line_indent_(acutest_case_name_[0] ? 3 : 2); |
| 772 | printf("%.*s\n", (int)(line_end - line_beg), line_beg); |
| 773 | line_beg = line_end + 1; |
| 774 | } |
| 775 | if(line_beg[0] != '\0') { |
| 776 | acutest_line_indent_(acutest_case_name_[0] ? 3 : 2); |
| 777 | printf("%s\n", line_beg); |
| 778 | } |
| 779 | } |
| 780 | |
| 781 | void |
| 782 | acutest_dump_(const char* title, const void* addr, size_t size) |
| 783 | { |
| 784 | static const size_t BYTES_PER_LINE = 16; |
| 785 | size_t line_beg; |
| 786 | size_t truncate = 0; |
| 787 | |
| 788 | if(acutest_verbose_level_ < 2) |
| 789 | return; |
| 790 | |
| 791 | /* We allow extra message only when something is already wrong in the |
| 792 | * current test. */ |
| 793 | if(acutest_current_test_ == NULL || !acutest_cond_failed_) |
| 794 | return; |
| 795 | |
| 796 | if(size > TEST_DUMP_MAXSIZE) { |
| 797 | truncate = size - TEST_DUMP_MAXSIZE; |
| 798 | size = TEST_DUMP_MAXSIZE; |
| 799 | } |
| 800 | |
| 801 | acutest_line_indent_(acutest_case_name_[0] ? 3 : 2); |
| 802 | printf((title[strlen(title)-1] == ':') ? "%s\n" : "%s:\n", title); |
| 803 | |
| 804 | for(line_beg = 0; line_beg < size; line_beg += BYTES_PER_LINE) { |
| 805 | size_t line_end = line_beg + BYTES_PER_LINE; |
| 806 | size_t off; |
| 807 | |
| 808 | acutest_line_indent_(acutest_case_name_[0] ? 4 : 3); |
| 809 | printf("%08lx: ", (unsigned long)line_beg); |
| 810 | for(off = line_beg; off < line_end; off++) { |
| 811 | if(off < size) |
| 812 | printf(" %02x", ((const unsigned char*)addr)[off]); |
| 813 | else |
| 814 | printf(" "); |
| 815 | } |
| 816 | |
| 817 | printf(" "); |
| 818 | for(off = line_beg; off < line_end; off++) { |
| 819 | unsigned char byte = ((const unsigned char*)addr)[off]; |
| 820 | if(off < size) |
| 821 | printf("%c", (iscntrl(byte) ? '.' : byte)); |
| 822 | else |
| 823 | break; |
| 824 | } |
| 825 | |
| 826 | printf("\n"); |
| 827 | } |
| 828 | |
| 829 | if(truncate > 0) { |
| 830 | acutest_line_indent_(acutest_case_name_[0] ? 4 : 3); |
| 831 | printf(" ... (and more %u bytes)\n", (unsigned) truncate); |
| 832 | } |
| 833 | } |
| 834 | |
| 835 | /* This is called just before each test */ |
| 836 | static void |
| 837 | acutest_init_(const char *test_name) |
| 838 | { |
| 839 | #ifdef TEST_INIT |
| 840 | TEST_INIT |
| 841 | ; /* Allow for a single unterminated function call */ |
| 842 | #endif |
| 843 | |
| 844 | /* Suppress any warnings about unused variable. */ |
| 845 | (void) test_name; |
| 846 | } |
| 847 | |
| 848 | /* This is called after each test */ |
| 849 | static void |
| 850 | acutest_fini_(const char *test_name) |
| 851 | { |
| 852 | #ifdef TEST_FINI |
| 853 | TEST_FINI |
| 854 | ; /* Allow for a single unterminated function call */ |
| 855 | #endif |
| 856 | |
| 857 | /* Suppress any warnings about unused variable. */ |
| 858 | (void) test_name; |
| 859 | } |
| 860 | |
| 861 | void |
| 862 | acutest_abort_(void) |
| 863 | { |
| 864 | if(acutest_abort_has_jmp_buf_) { |
| 865 | longjmp(acutest_abort_jmp_buf_, 1); |
| 866 | } else { |
| 867 | if(acutest_current_test_ != NULL) |
| 868 | acutest_fini_(acutest_current_test_->name); |
| 869 | abort(); |
| 870 | } |
| 871 | } |
| 872 | |
| 873 | static void |
| 874 | acutest_list_names_(void) |
| 875 | { |
| 876 | const struct acutest_test_* test; |
| 877 | |
| 878 | printf("Unit tests:\n"); |
| 879 | for(test = ´st_list_[0]; test->func != NULL; test++) |
| 880 | printf(" %s\n", test->name); |
| 881 | } |
| 882 | |
| 883 | static void |
| 884 | acutest_remember_(int i) |
| 885 | { |
| 886 | if(acutest_test_data_[i].flags & ACUTEST_FLAG_RUN_) |
| 887 | return; |
| 888 | |
| 889 | acutest_test_data_[i].flags |= ACUTEST_FLAG_RUN_; |
| 890 | acutest_count_++; |
| 891 | } |
| 892 | |
| 893 | static void |
| 894 | acutest_set_success_(int i, int success) |
| 895 | { |
| 896 | acutest_test_data_[i].flags |= success ? ACUTEST_FLAG_SUCCESS_ : ACUTEST_FLAG_FAILURE_; |
| 897 | } |
| 898 | |
| 899 | static void |
| 900 | acutest_set_duration_(int i, double duration) |
| 901 | { |
| 902 | acutest_test_data_[i].duration = duration; |
| 903 | } |
| 904 | |
| 905 | static int |
| 906 | acutest_name_contains_word_(const char* name, const char* pattern) |
| 907 | { |
| 908 | static const char word_delim[] = " \t-_/.,:;"; |
| 909 | const char* substr; |
| 910 | size_t pattern_len; |
| 911 | |
| 912 | pattern_len = strlen(pattern); |
| 913 | |
| 914 | substr = strstr(name, pattern); |
| 915 | while(substr != NULL) { |
| 916 | int starts_on_word_boundary = (substr == name || strchr(word_delim, substr[-1]) != NULL); |
| 917 | int ends_on_word_boundary = (substr[pattern_len] == '\0' || strchr(word_delim, substr[pattern_len]) != NULL); |
| 918 | |
| 919 | if(starts_on_word_boundary && ends_on_word_boundary) |
| 920 | return 1; |
| 921 | |
| 922 | substr = strstr(substr+1, pattern); |
| 923 | } |
| 924 | |
| 925 | return 0; |
| 926 | } |
| 927 | |
| 928 | static int |
| 929 | acutest_lookup_(const char* pattern) |
| 930 | { |
| 931 | int i; |
| 932 | int n = 0; |
| 933 | |
| 934 | /* Try exact match. */ |
| 935 | for(i = 0; i < (int) acutest_list_size_; i++) { |
| 936 | if(strcmp(acutest_list_[i].name, pattern) == 0) { |
| 937 | acutest_remember_(i); |
| 938 | n++; |
| 939 | break; |
| 940 | } |
| 941 | } |
| 942 | if(n > 0) |
| 943 | return n; |
| 944 | |
| 945 | /* Try word match. */ |
| 946 | for(i = 0; i < (int) acutest_list_size_; i++) { |
| 947 | if(acutest_name_contains_word_(acutest_list_[i].name, pattern)) { |
| 948 | acutest_remember_(i); |
| 949 | n++; |
| 950 | } |
| 951 | } |
| 952 | if(n > 0) |
| 953 | return n; |
| 954 | |
| 955 | /* Try relaxed match. */ |
| 956 | for(i = 0; i < (int) acutest_list_size_; i++) { |
| 957 | if(strstr(acutest_list_[i].name, pattern) != NULL) { |
| 958 | acutest_remember_(i); |
| 959 | n++; |
| 960 | } |
| 961 | } |
| 962 | |
| 963 | return n; |
| 964 | } |
| 965 | |
| 966 | |
| 967 | /* Called if anything goes bad in Acutest, or if the unit test ends in other |
| 968 | * way then by normal returning from its function (e.g. exception or some |
| 969 | * abnormal child process termination). */ |
| 970 | static void ACUTEST_ATTRIBUTE_(format (printf, 1, 2)) |
| 971 | acutest_error_(const char* fmt, ...) |
| 972 | { |
| 973 | if(acutest_verbose_level_ == 0) |
| 974 | return; |
| 975 | |
| 976 | if(acutest_verbose_level_ >= 2) { |
| 977 | va_list args; |
| 978 | |
| 979 | acutest_line_indent_(1); |
| 980 | if(acutest_verbose_level_ >= 3) |
| 981 | acutest_colored_printf_(ACUTEST_COLOR_RED_INTENSIVE_, "ERROR: "); |
| 982 | va_start(args, fmt); |
| 983 | vprintf(fmt, args); |
| 984 | va_end(args); |
| 985 | printf("\n"); |
| 986 | } |
| 987 | |
| 988 | if(acutest_verbose_level_ >= 3) { |
| 989 | printf("\n"); |
| 990 | } |
| 991 | } |
| 992 | |
| 993 | /* Call directly the given test unit function. */ |
| 994 | static int |
| 995 | acutest_do_run_(const struct acutest_test_* test, int index) |
| 996 | { |
| 997 | int status = -1; |
| 998 | |
| 999 | acutest_was_aborted_ = 0; |
| 1000 | acutest_current_test_ = test; |
| 1001 | acutest_current_index_ = index; |
| 1002 | acutest_test_failures_ = 0; |
| 1003 | acutest_test_already_logged_ = 0; |
| 1004 | acutest_cond_failed_ = 0; |
| 1005 | |
| 1006 | #ifdef __cplusplus |
| 1007 | try { |
| 1008 | #endif |
| 1009 | acutest_init_(test->name); |
| 1010 | acutest_begin_test_line_(test); |
| 1011 | |
| 1012 | /* This is good to do in case the test unit crashes. */ |
| 1013 | fflush(stdout); |
| 1014 | fflush(stderr); |
| 1015 | |
| 1016 | if(!acutest_worker_) { |
| 1017 | acutest_abort_has_jmp_buf_ = 1; |
| 1018 | if(setjmp(acutest_abort_jmp_buf_) != 0) { |
| 1019 | acutest_was_aborted_ = 1; |
| 1020 | goto aborted; |
| 1021 | } |
| 1022 | } |
| 1023 | |
| 1024 | acutest_timer_get_time_(´st_timer_start_); |
| 1025 | test->func(); |
| 1026 | aborted: |
| 1027 | acutest_abort_has_jmp_buf_ = 0; |
| 1028 | acutest_timer_get_time_(´st_timer_end_); |
| 1029 | |
| 1030 | if(acutest_verbose_level_ >= 3) { |
| 1031 | acutest_line_indent_(1); |
| 1032 | if(acutest_test_failures_ == 0) { |
| 1033 | acutest_colored_printf_(ACUTEST_COLOR_GREEN_INTENSIVE_, "SUCCESS: "); |
| 1034 | printf("All conditions have passed.\n"); |
| 1035 | |
| 1036 | if(acutest_timer_) { |
| 1037 | acutest_line_indent_(1); |
| 1038 | printf("Duration: "); |
| 1039 | acutest_timer_print_diff_(); |
| 1040 | printf("\n"); |
| 1041 | } |
| 1042 | } else { |
| 1043 | acutest_colored_printf_(ACUTEST_COLOR_RED_INTENSIVE_, "FAILED: "); |
| 1044 | if(!acutest_was_aborted_) { |
| 1045 | printf("%d condition%s %s failed.\n", |
| 1046 | acutest_test_failures_, |
| 1047 | (acutest_test_failures_ == 1) ? "" : "s", |
| 1048 | (acutest_test_failures_ == 1) ? "has" : "have"); |
| 1049 | } else { |
| 1050 | printf("Aborted.\n"); |
| 1051 | } |
| 1052 | } |
| 1053 | printf("\n"); |
| 1054 | } else if(acutest_verbose_level_ >= 1 && acutest_test_failures_ == 0) { |
| 1055 | acutest_finish_test_line_(0); |
| 1056 | } |
| 1057 | |
| 1058 | status = (acutest_test_failures_ == 0) ? 0 : -1; |
| 1059 | |
| 1060 | #ifdef __cplusplus |
| 1061 | } catch(std::exception& e) { |
| 1062 | const char* what = e.what(); |
| 1063 | acutest_check_(0, NULL, 0, "Threw std::exception"); |
| 1064 | if(what != NULL) |
| 1065 | acutest_message_("std::exception::what(): %s", what); |
| 1066 | |
| 1067 | if(acutest_verbose_level_ >= 3) { |
| 1068 | acutest_line_indent_(1); |
| 1069 | acutest_colored_printf_(ACUTEST_COLOR_RED_INTENSIVE_, "FAILED: "); |
| 1070 | printf("C++ exception.\n\n"); |
| 1071 | } |
| 1072 | } catch(...) { |
| 1073 | acutest_check_(0, NULL, 0, "Threw an exception"); |
| 1074 | |
| 1075 | if(acutest_verbose_level_ >= 3) { |
| 1076 | acutest_line_indent_(1); |
| 1077 | acutest_colored_printf_(ACUTEST_COLOR_RED_INTENSIVE_, "FAILED: "); |
| 1078 | printf("C++ exception.\n\n"); |
| 1079 | } |
| 1080 | } |
| 1081 | #endif |
| 1082 | |
| 1083 | acutest_fini_(test->name); |
| 1084 | acutest_case_(NULL); |
| 1085 | acutest_current_test_ = NULL; |
| 1086 | |
| 1087 | return status; |
| 1088 | } |
| 1089 | |
| 1090 | /* Trigger the unit test. If possible (and not suppressed) it starts a child |
| 1091 | * process who calls acutest_do_run_(), otherwise it calls acutest_do_run_() |
| 1092 | * directly. */ |
| 1093 | static void |
| 1094 | acutest_run_(const struct acutest_test_* test, int index, int master_index) |
| 1095 | { |
| 1096 | int failed = 1; |
| 1097 | acutest_timer_type_ start, end; |
| 1098 | |
| 1099 | acutest_current_test_ = test; |
| 1100 | acutest_test_already_logged_ = 0; |
| 1101 | acutest_timer_get_time_(&start); |
| 1102 | |
| 1103 | if(!acutest_no_exec_) { |
| 1104 | |
| 1105 | #if defined(ACUTEST_UNIX_) |
| 1106 | |
| 1107 | pid_t pid; |
| 1108 | int exit_code; |
| 1109 | |
| 1110 | /* Make sure the child starts with empty I/O buffers. */ |
| 1111 | fflush(stdout); |
| 1112 | fflush(stderr); |
| 1113 | |
| 1114 | pid = fork(); |
| 1115 | if(pid == (pid_t)-1) { |
| 1116 | acutest_error_("Cannot fork. %s [%d]", strerror(errno), errno); |
| 1117 | failed = 1; |
| 1118 | } else if(pid == 0) { |
| 1119 | /* Child: Do the test. */ |
| 1120 | acutest_worker_ = 1; |
| 1121 | failed = (acutest_do_run_(test, index) != 0); |
| 1122 | acutest_exit_(failed ? 1 : 0); |
| 1123 | } else { |
| 1124 | /* Parent: Wait until child terminates and analyze its exit code. */ |
| 1125 | waitpid(pid, &exit_code, 0); |
| 1126 | if(WIFEXITED(exit_code)) { |
| 1127 | switch(WEXITSTATUS(exit_code)) { |
| 1128 | case 0: failed = 0; break; /* test has passed. */ |
| 1129 | case 1: /* noop */ break; /* "normal" failure. */ |
| 1130 | default: acutest_error_("Unexpected exit code [%d]", WEXITSTATUS(exit_code)); |
| 1131 | } |
| 1132 | } else if(WIFSIGNALED(exit_code)) { |
| 1133 | char tmp[32]; |
| 1134 | const char* signame; |
| 1135 | switch(WTERMSIG(exit_code)) { |
| 1136 | case SIGINT: signame = "SIGINT"; break; |
| 1137 | case SIGHUP: signame = "SIGHUP"; break; |
| 1138 | case SIGQUIT: signame = "SIGQUIT"; break; |
| 1139 | case SIGABRT: signame = "SIGABRT"; break; |
| 1140 | case SIGKILL: signame = "SIGKILL"; break; |
| 1141 | case SIGSEGV: signame = "SIGSEGV"; break; |
| 1142 | case SIGILL: signame = "SIGILL"; break; |
| 1143 | case SIGTERM: signame = "SIGTERM"; break; |
| 1144 | default: sprintf(tmp, "signal %d", WTERMSIG(exit_code)); signame = tmp; break; |
| 1145 | } |
| 1146 | acutest_error_("Test interrupted by %s.", signame); |
| 1147 | } else { |
| 1148 | acutest_error_("Test ended in an unexpected way [%d].", exit_code); |
| 1149 | } |
| 1150 | } |
| 1151 | |
| 1152 | #elif defined(ACUTEST_WIN_) |
| 1153 | |
| 1154 | char buffer[512] = {0}; |
| 1155 | STARTUPINFOA startupInfo; |
| 1156 | PROCESS_INFORMATION processInfo; |
| 1157 | DWORD exitCode; |
| 1158 | |
| 1159 | /* Windows has no fork(). So we propagate all info into the child |
| 1160 | * through a command line arguments. */ |
| 1161 | _snprintf(buffer, sizeof(buffer)-1, |
| 1162 | "%s --worker=%d %s --no-exec --no-summary %s --verbose=%d --color=%s -- \"%s\"", |
| 1163 | acutest_argv0_, index, acutest_timer_ ? "--time" : "", |
| 1164 | acutest_tap_ ? "--tap" : "", acutest_verbose_level_, |
| 1165 | acutest_colorize_ ? "always" : "never", |
| 1166 | test->name); |
| 1167 | memset(&startupInfo, 0, sizeof(startupInfo)); |
| 1168 | startupInfo.cb = sizeof(STARTUPINFO); |
| 1169 | if(CreateProcessA(NULL, buffer, NULL, NULL, FALSE, 0, NULL, NULL, &startupInfo, &processInfo)) { |
| 1170 | WaitForSingleObject(processInfo.hProcess, INFINITE); |
| 1171 | GetExitCodeProcess(processInfo.hProcess, &exitCode); |
| 1172 | CloseHandle(processInfo.hThread); |
| 1173 | CloseHandle(processInfo.hProcess); |
| 1174 | failed = (exitCode != 0); |
| 1175 | if(exitCode > 1) { |
| 1176 | switch(exitCode) { |
| 1177 | case 3: acutest_error_("Aborted."); break; |
| 1178 | case 0xC0000005: acutest_error_("Access violation."); break; |
| 1179 | default: acutest_error_("Test ended in an unexpected way [%lu].", exitCode); break; |
| 1180 | } |
| 1181 | } |
| 1182 | } else { |
| 1183 | acutest_error_("Cannot create unit test subprocess [%ld].", GetLastError()); |
| 1184 | failed = 1; |
| 1185 | } |
| 1186 | |
| 1187 | #else |
| 1188 | |
| 1189 | /* A platform where we don't know how to run child process. */ |
| 1190 | failed = (acutest_do_run_(test, index) != 0); |
| 1191 | |
| 1192 | #endif |
| 1193 | |
| 1194 | } else { |
| 1195 | /* Child processes suppressed through --no-exec. */ |
| 1196 | failed = (acutest_do_run_(test, index) != 0); |
| 1197 | } |
| 1198 | acutest_timer_get_time_(&end); |
| 1199 | |
| 1200 | acutest_current_test_ = NULL; |
| 1201 | |
| 1202 | acutest_stat_run_units_++; |
| 1203 | if(failed) |
| 1204 | acutest_stat_failed_units_++; |
| 1205 | |
| 1206 | acutest_set_success_(master_index, !failed); |
| 1207 | acutest_set_duration_(master_index, acutest_timer_diff_(start, end)); |
| 1208 | } |
| 1209 | |
| 1210 | #if defined(ACUTEST_WIN_) |
| 1211 | /* Callback for SEH events. */ |
| 1212 | static LONG CALLBACK |
| 1213 | acutest_seh_exception_filter_(EXCEPTION_POINTERS *ptrs) |
| 1214 | { |
| 1215 | acutest_check_(0, NULL, 0, "Unhandled SEH exception"); |
| 1216 | acutest_message_("Exception code: 0x%08lx", ptrs->ExceptionRecord->ExceptionCode); |
| 1217 | acutest_message_("Exception address: 0x%p", ptrs->ExceptionRecord->ExceptionAddress); |
| 1218 | |
| 1219 | fflush(stdout); |
| 1220 | fflush(stderr); |
| 1221 | |
| 1222 | return EXCEPTION_EXECUTE_HANDLER; |
| 1223 | } |
| 1224 | #endif |
| 1225 | |
| 1226 | |
| 1227 | #define ACUTEST_CMDLINE_OPTFLAG_OPTIONALARG_ 0x0001 |
| 1228 | #define ACUTEST_CMDLINE_OPTFLAG_REQUIREDARG_ 0x0002 |
| 1229 | |
| 1230 | #define ACUTEST_CMDLINE_OPTID_NONE_ 0 |
| 1231 | #define ACUTEST_CMDLINE_OPTID_UNKNOWN_ (-0x7fffffff + 0) |
| 1232 | #define ACUTEST_CMDLINE_OPTID_MISSINGARG_ (-0x7fffffff + 1) |
| 1233 | #define ACUTEST_CMDLINE_OPTID_BOGUSARG_ (-0x7fffffff + 2) |
| 1234 | |
| 1235 | typedef struct acutest_test_CMDLINE_OPTION_ { |
| 1236 | char shortname; |
| 1237 | const char* longname; |
| 1238 | int id; |
| 1239 | unsigned flags; |
| 1240 | } ACUTEST_CMDLINE_OPTION_; |
| 1241 | |
| 1242 | static int |
| 1243 | acutest_cmdline_handle_short_opt_group_(const ACUTEST_CMDLINE_OPTION_* options, |
| 1244 | const char* arggroup, |
| 1245 | int (*callback)(int /*optval*/, const char* /*arg*/)) |
| 1246 | { |
| 1247 | const ACUTEST_CMDLINE_OPTION_* opt; |
| 1248 | int i; |
| 1249 | int ret = 0; |
| 1250 | |
| 1251 | for(i = 0; arggroup[i] != '\0'; i++) { |
| 1252 | for(opt = options; opt->id != 0; opt++) { |
| 1253 | if(arggroup[i] == opt->shortname) |
| 1254 | break; |
| 1255 | } |
| 1256 | |
| 1257 | if(opt->id != 0 && !(opt->flags & ACUTEST_CMDLINE_OPTFLAG_REQUIREDARG_)) { |
| 1258 | ret = callback(opt->id, NULL); |
| 1259 | } else { |
| 1260 | /* Unknown option. */ |
| 1261 | char badoptname[3]; |
| 1262 | badoptname[0] = '-'; |
| 1263 | badoptname[1] = arggroup[i]; |
| 1264 | badoptname[2] = '\0'; |
| 1265 | ret = callback((opt->id != 0 ? ACUTEST_CMDLINE_OPTID_MISSINGARG_ : ACUTEST_CMDLINE_OPTID_UNKNOWN_), |
| 1266 | badoptname); |
| 1267 | } |
| 1268 | |
| 1269 | if(ret != 0) |
| 1270 | break; |
| 1271 | } |
| 1272 | |
| 1273 | return ret; |
| 1274 | } |
| 1275 | |
| 1276 | #define ACUTEST_CMDLINE_AUXBUF_SIZE_ 32 |
| 1277 | |
| 1278 | static int |
| 1279 | acutest_cmdline_read_(const ACUTEST_CMDLINE_OPTION_* options, int argc, char** argv, |
| 1280 | int (*callback)(int /*optval*/, const char* /*arg*/)) |
| 1281 | { |
| 1282 | |
| 1283 | const ACUTEST_CMDLINE_OPTION_* opt; |
| 1284 | char auxbuf[ACUTEST_CMDLINE_AUXBUF_SIZE_+1]; |
| 1285 | int after_doubledash = 0; |
| 1286 | int i = 1; |
| 1287 | int ret = 0; |
| 1288 | |
| 1289 | auxbuf[ACUTEST_CMDLINE_AUXBUF_SIZE_] = '\0'; |
| 1290 | |
| 1291 | while(i < argc) { |
| 1292 | if(after_doubledash || strcmp(argv[i], "-") == 0) { |
| 1293 | /* Non-option argument. */ |
| 1294 | ret = callback(ACUTEST_CMDLINE_OPTID_NONE_, argv[i]); |
| 1295 | } else if(strcmp(argv[i], "--") == 0) { |
| 1296 | /* End of options. All the remaining members are non-option arguments. */ |
| 1297 | after_doubledash = 1; |
| 1298 | } else if(argv[i][0] != '-') { |
| 1299 | /* Non-option argument. */ |
| 1300 | ret = callback(ACUTEST_CMDLINE_OPTID_NONE_, argv[i]); |
| 1301 | } else { |
| 1302 | for(opt = options; opt->id != 0; opt++) { |
| 1303 | if(opt->longname != NULL && strncmp(argv[i], "--", 2) == 0) { |
| 1304 | size_t len = strlen(opt->longname); |
| 1305 | if(strncmp(argv[i]+2, opt->longname, len) == 0) { |
| 1306 | /* Regular long option. */ |
| 1307 | if(argv[i][2+len] == '\0') { |
| 1308 | /* with no argument provided. */ |
| 1309 | if(!(opt->flags & ACUTEST_CMDLINE_OPTFLAG_REQUIREDARG_)) |
| 1310 | ret = callback(opt->id, NULL); |
| 1311 | else |
| 1312 | ret = callback(ACUTEST_CMDLINE_OPTID_MISSINGARG_, argv[i]); |
| 1313 | break; |
| 1314 | } else if(argv[i][2+len] == '=') { |
| 1315 | /* with an argument provided. */ |
| 1316 | if(opt->flags & (ACUTEST_CMDLINE_OPTFLAG_OPTIONALARG_ | ACUTEST_CMDLINE_OPTFLAG_REQUIREDARG_)) { |
| 1317 | ret = callback(opt->id, argv[i]+2+len+1); |
| 1318 | } else { |
| 1319 | sprintf(auxbuf, "--%s", opt->longname); |
| 1320 | ret = callback(ACUTEST_CMDLINE_OPTID_BOGUSARG_, auxbuf); |
| 1321 | } |
| 1322 | break; |
| 1323 | } else { |
| 1324 | continue; |
| 1325 | } |
| 1326 | } |
| 1327 | } else if(opt->shortname != '\0' && argv[i][0] == '-') { |
| 1328 | if(argv[i][1] == opt->shortname) { |
| 1329 | /* Regular short option. */ |
| 1330 | if(opt->flags & ACUTEST_CMDLINE_OPTFLAG_REQUIREDARG_) { |
| 1331 | if(argv[i][2] != '\0') |
| 1332 | ret = callback(opt->id, argv[i]+2); |
| 1333 | else if(i+1 < argc) |
| 1334 | ret = callback(opt->id, argv[++i]); |
| 1335 | else |
| 1336 | ret = callback(ACUTEST_CMDLINE_OPTID_MISSINGARG_, argv[i]); |
| 1337 | break; |
| 1338 | } else { |
| 1339 | ret = callback(opt->id, NULL); |
| 1340 | |
| 1341 | /* There might be more (argument-less) short options |
| 1342 | * grouped together. */ |
| 1343 | if(ret == 0 && argv[i][2] != '\0') |
| 1344 | ret = acutest_cmdline_handle_short_opt_group_(options, argv[i]+2, callback); |
| 1345 | break; |
| 1346 | } |
| 1347 | } |
| 1348 | } |
| 1349 | } |
| 1350 | |
| 1351 | if(opt->id == 0) { /* still not handled? */ |
| 1352 | if(argv[i][0] != '-') { |
| 1353 | /* Non-option argument. */ |
| 1354 | ret = callback(ACUTEST_CMDLINE_OPTID_NONE_, argv[i]); |
| 1355 | } else { |
| 1356 | /* Unknown option. */ |
| 1357 | char* badoptname = argv[i]; |
| 1358 | |
| 1359 | if(strncmp(badoptname, "--", 2) == 0) { |
| 1360 | /* Strip any argument from the long option. */ |
| 1361 | char* assignment = strchr(badoptname, '='); |
| 1362 | if(assignment != NULL) { |
| 1363 | size_t len = assignment - badoptname; |
| 1364 | if(len > ACUTEST_CMDLINE_AUXBUF_SIZE_) |
| 1365 | len = ACUTEST_CMDLINE_AUXBUF_SIZE_; |
| 1366 | strncpy(auxbuf, badoptname, len); |
| 1367 | auxbuf[len] = '\0'; |
| 1368 | badoptname = auxbuf; |
| 1369 | } |
| 1370 | } |
| 1371 | |
| 1372 | ret = callback(ACUTEST_CMDLINE_OPTID_UNKNOWN_, badoptname); |
| 1373 | } |
| 1374 | } |
| 1375 | } |
| 1376 | |
| 1377 | if(ret != 0) |
| 1378 | return ret; |
| 1379 | i++; |
| 1380 | } |
| 1381 | |
| 1382 | return ret; |
| 1383 | } |
| 1384 | |
| 1385 | static void |
| 1386 | acutest_help_(void) |
| 1387 | { |
| 1388 | printf("Usage: %s [options] [test...]\n", acutest_argv0_); |
| 1389 | printf("\n"); |
| 1390 | printf("Run the specified unit tests; or if the option '--skip' is used, run all\n"); |
| 1391 | printf("tests in the suite but those listed. By default, if no tests are specified\n"); |
| 1392 | printf("on the command line, all unit tests in the suite are run.\n"); |
| 1393 | printf("\n"); |
| 1394 | printf("Options:\n"); |
| 1395 | printf(" -s, --skip Execute all unit tests but the listed ones\n"); |
| 1396 | printf(" --exec[=WHEN] If supported, execute unit tests as child processes\n"); |
| 1397 | printf(" (WHEN is one of 'auto', 'always', 'never')\n"); |
| 1398 | printf(" -E, --no-exec Same as --exec=never\n"); |
| 1399 | #if defined ACUTEST_WIN_ |
| 1400 | printf(" -t, --time Measure test duration\n"); |
| 1401 | #elif defined ACUTEST_HAS_POSIX_TIMER_ |
| 1402 | printf(" -t, --time Measure test duration (real time)\n"); |
| 1403 | printf(" --time=TIMER Measure test duration, using given timer\n"); |
| 1404 | printf(" (TIMER is one of 'real', 'cpu')\n"); |
| 1405 | #endif |
| 1406 | printf(" --no-summary Suppress printing of test results summary\n"); |
| 1407 | printf(" --tap Produce TAP-compliant output\n"); |
| 1408 | printf(" (See https://testanything.org/)\n"); |
| 1409 | printf(" -x, --xml-output=FILE Enable XUnit output to the given file\n"); |
| 1410 | printf(" -l, --list List unit tests in the suite and exit\n"); |
| 1411 | printf(" -v, --verbose Make output more verbose\n"); |
| 1412 | printf(" --verbose=LEVEL Set verbose level to LEVEL:\n"); |
| 1413 | printf(" 0 ... Be silent\n"); |
| 1414 | printf(" 1 ... Output one line per test (and summary)\n"); |
| 1415 | printf(" 2 ... As 1 and failed conditions (this is default)\n"); |
| 1416 | printf(" 3 ... As 1 and all conditions (and extended summary)\n"); |
| 1417 | printf(" -q, --quiet Same as --verbose=0\n"); |
| 1418 | printf(" --color[=WHEN] Enable colorized output\n"); |
| 1419 | printf(" (WHEN is one of 'auto', 'always', 'never')\n"); |
| 1420 | printf(" --no-color Same as --color=never\n"); |
| 1421 | printf(" -h, --help Display this help and exit\n"); |
| 1422 | |
| 1423 | if(acutest_list_size_ < 16) { |
| 1424 | printf("\n"); |
| 1425 | acutest_list_names_(); |
| 1426 | } |
| 1427 | } |
| 1428 | |
| 1429 | static const ACUTEST_CMDLINE_OPTION_ acutest_cmdline_options_[] = { |
| 1430 | { 's', "skip", 's', 0 }, |
| 1431 | { 0, "exec", 'e', ACUTEST_CMDLINE_OPTFLAG_OPTIONALARG_ }, |
| 1432 | { 'E', "no-exec", 'E', 0 }, |
| 1433 | #if defined ACUTEST_WIN_ |
| 1434 | { 't', "time", 't', 0 }, |
| 1435 | { 0, "timer", 't', 0 }, /* kept for compatibility */ |
| 1436 | #elif defined ACUTEST_HAS_POSIX_TIMER_ |
| 1437 | { 't', "time", 't', ACUTEST_CMDLINE_OPTFLAG_OPTIONALARG_ }, |
| 1438 | { 0, "timer", 't', ACUTEST_CMDLINE_OPTFLAG_OPTIONALARG_ }, /* kept for compatibility */ |
| 1439 | #endif |
| 1440 | { 0, "no-summary", 'S', 0 }, |
| 1441 | { 0, "tap", 'T', 0 }, |
| 1442 | { 'l', "list", 'l', 0 }, |
| 1443 | { 'v', "verbose", 'v', ACUTEST_CMDLINE_OPTFLAG_OPTIONALARG_ }, |
| 1444 | { 'q', "quiet", 'q', 0 }, |
| 1445 | { 0, "color", 'c', ACUTEST_CMDLINE_OPTFLAG_OPTIONALARG_ }, |
| 1446 | { 0, "no-color", 'C', 0 }, |
| 1447 | { 'h', "help", 'h', 0 }, |
| 1448 | { 0, "worker", 'w', ACUTEST_CMDLINE_OPTFLAG_REQUIREDARG_ }, /* internal */ |
| 1449 | { 'x', "xml-output", 'x', ACUTEST_CMDLINE_OPTFLAG_REQUIREDARG_ }, |
| 1450 | { 0, NULL, 0, 0 } |
| 1451 | }; |
| 1452 | |
| 1453 | static int |
| 1454 | acutest_cmdline_callback_(int id, const char* arg) |
| 1455 | { |
| 1456 | switch(id) { |
| 1457 | case 's': |
| 1458 | acutest_skip_mode_ = 1; |
| 1459 | break; |
| 1460 | |
| 1461 | case 'e': |
| 1462 | if(arg == NULL || strcmp(arg, "always") == 0) { |
| 1463 | acutest_no_exec_ = 0; |
| 1464 | } else if(strcmp(arg, "never") == 0) { |
| 1465 | acutest_no_exec_ = 1; |
| 1466 | } else if(strcmp(arg, "auto") == 0) { |
| 1467 | /*noop*/ |
| 1468 | } else { |
| 1469 | fprintf(stderr, "%s: Unrecognized argument '%s' for option --exec.\n", acutest_argv0_, arg); |
| 1470 | fprintf(stderr, "Try '%s --help' for more information.\n", acutest_argv0_); |
| 1471 | acutest_exit_(2); |
| 1472 | } |
| 1473 | break; |
| 1474 | |
| 1475 | case 'E': |
| 1476 | acutest_no_exec_ = 1; |
| 1477 | break; |
| 1478 | |
| 1479 | case 't': |
| 1480 | #if defined ACUTEST_WIN_ || defined ACUTEST_HAS_POSIX_TIMER_ |
| 1481 | if(arg == NULL || strcmp(arg, "real") == 0) { |
| 1482 | acutest_timer_ = 1; |
| 1483 | #ifndef ACUTEST_WIN_ |
| 1484 | } else if(strcmp(arg, "cpu") == 0) { |
| 1485 | acutest_timer_ = 2; |
| 1486 | #endif |
| 1487 | } else { |
| 1488 | fprintf(stderr, "%s: Unrecognized argument '%s' for option --time.\n", acutest_argv0_, arg); |
| 1489 | fprintf(stderr, "Try '%s --help' for more information.\n", acutest_argv0_); |
| 1490 | acutest_exit_(2); |
| 1491 | } |
| 1492 | #endif |
| 1493 | break; |
| 1494 | |
| 1495 | case 'S': |
| 1496 | acutest_no_summary_ = 1; |
| 1497 | break; |
| 1498 | |
| 1499 | case 'T': |
| 1500 | acutest_tap_ = 1; |
| 1501 | break; |
| 1502 | |
| 1503 | case 'l': |
| 1504 | acutest_list_names_(); |
| 1505 | acutest_exit_(0); |
| 1506 | break; |
| 1507 | |
| 1508 | case 'v': |
| 1509 | acutest_verbose_level_ = (arg != NULL ? atoi(arg) : acutest_verbose_level_+1); |
| 1510 | break; |
| 1511 | |
| 1512 | case 'q': |
| 1513 | acutest_verbose_level_ = 0; |
| 1514 | break; |
| 1515 | |
| 1516 | case 'c': |
| 1517 | if(arg == NULL || strcmp(arg, "always") == 0) { |
| 1518 | acutest_colorize_ = 1; |
| 1519 | } else if(strcmp(arg, "never") == 0) { |
| 1520 | acutest_colorize_ = 0; |
| 1521 | } else if(strcmp(arg, "auto") == 0) { |
| 1522 | /*noop*/ |
| 1523 | } else { |
| 1524 | fprintf(stderr, "%s: Unrecognized argument '%s' for option --color.\n", acutest_argv0_, arg); |
| 1525 | fprintf(stderr, "Try '%s --help' for more information.\n", acutest_argv0_); |
| 1526 | acutest_exit_(2); |
| 1527 | } |
| 1528 | break; |
| 1529 | |
| 1530 | case 'C': |
| 1531 | acutest_colorize_ = 0; |
| 1532 | break; |
| 1533 | |
| 1534 | case 'h': |
| 1535 | acutest_help_(); |
| 1536 | acutest_exit_(0); |
| 1537 | break; |
| 1538 | |
| 1539 | case 'w': |
| 1540 | acutest_worker_ = 1; |
| 1541 | acutest_worker_index_ = atoi(arg); |
| 1542 | break; |
| 1543 | case 'x': |
| 1544 | acutest_xml_output_ = fopen(arg, "w"); |
| 1545 | if (!acutest_xml_output_) { |
| 1546 | fprintf(stderr, "Unable to open '%s': %s\n", arg, strerror(errno)); |
| 1547 | acutest_exit_(2); |
| 1548 | } |
| 1549 | break; |
| 1550 | |
| 1551 | case 0: |
| 1552 | if(acutest_lookup_(arg) == 0) { |
| 1553 | fprintf(stderr, "%s: Unrecognized unit test '%s'\n", acutest_argv0_, arg); |
| 1554 | fprintf(stderr, "Try '%s --list' for list of unit tests.\n", acutest_argv0_); |
| 1555 | acutest_exit_(2); |
| 1556 | } |
| 1557 | break; |
| 1558 | |
| 1559 | case ACUTEST_CMDLINE_OPTID_UNKNOWN_: |
| 1560 | fprintf(stderr, "Unrecognized command line option '%s'.\n", arg); |
| 1561 | fprintf(stderr, "Try '%s --help' for more information.\n", acutest_argv0_); |
| 1562 | acutest_exit_(2); |
| 1563 | break; |
| 1564 | |
| 1565 | case ACUTEST_CMDLINE_OPTID_MISSINGARG_: |
| 1566 | fprintf(stderr, "The command line option '%s' requires an argument.\n", arg); |
| 1567 | fprintf(stderr, "Try '%s --help' for more information.\n", acutest_argv0_); |
| 1568 | acutest_exit_(2); |
| 1569 | break; |
| 1570 | |
| 1571 | case ACUTEST_CMDLINE_OPTID_BOGUSARG_: |
| 1572 | fprintf(stderr, "The command line option '%s' does not expect an argument.\n", arg); |
| 1573 | fprintf(stderr, "Try '%s --help' for more information.\n", acutest_argv0_); |
| 1574 | acutest_exit_(2); |
| 1575 | break; |
| 1576 | } |
| 1577 | |
| 1578 | return 0; |
| 1579 | } |
| 1580 | |
| 1581 | |
| 1582 | #ifdef ACUTEST_LINUX_ |
| 1583 | static int |
| 1584 | acutest_is_tracer_present_(void) |
| 1585 | { |
| 1586 | /* Must be large enough so the line 'TracerPid: ${PID}' can fit in. */ |
| 1587 | static const int OVERLAP = 32; |
| 1588 | |
| 1589 | char buf[256+OVERLAP+1]; |
| 1590 | int tracer_present = 0; |
| 1591 | int fd; |
| 1592 | size_t n_read = 0; |
| 1593 | |
| 1594 | fd = open("/proc/self/status", O_RDONLY); |
| 1595 | if(fd == -1) |
| 1596 | return 0; |
| 1597 | |
| 1598 | while(1) { |
| 1599 | static const char pattern[] = "TracerPid:"; |
| 1600 | const char* field; |
| 1601 | |
| 1602 | while(n_read < sizeof(buf) - 1) { |
| 1603 | ssize_t n; |
| 1604 | |
| 1605 | n = read(fd, buf + n_read, sizeof(buf) - 1 - n_read); |
| 1606 | if(n <= 0) |
| 1607 | break; |
| 1608 | n_read += n; |
| 1609 | } |
| 1610 | buf[n_read] = '\0'; |
| 1611 | |
| 1612 | field = strstr(buf, pattern); |
| 1613 | if(field != NULL && field < buf + sizeof(buf) - OVERLAP) { |
| 1614 | pid_t tracer_pid = (pid_t) atoi(field + sizeof(pattern) - 1); |
| 1615 | tracer_present = (tracer_pid != 0); |
| 1616 | break; |
| 1617 | } |
| 1618 | |
| 1619 | if(n_read == sizeof(buf)-1) { |
| 1620 | memmove(buf, buf + sizeof(buf)-1 - OVERLAP, OVERLAP); |
| 1621 | n_read = OVERLAP; |
| 1622 | } else { |
| 1623 | break; |
| 1624 | } |
| 1625 | } |
| 1626 | |
| 1627 | close(fd); |
| 1628 | return tracer_present; |
| 1629 | } |
| 1630 | #endif |
| 1631 | |
| 1632 | int |
| 1633 | main(int argc, char** argv) |
| 1634 | { |
| 1635 | int i; |
| 1636 | |
| 1637 | acutest_argv0_ = argv[0]; |
| 1638 | |
| 1639 | #if defined ACUTEST_UNIX_ |
| 1640 | acutest_colorize_ = isatty(STDOUT_FILENO); |
| 1641 | #elif defined ACUTEST_WIN_ |
| 1642 | #if defined _BORLANDC_ |
| 1643 | acutest_colorize_ = isatty(_fileno(stdout)); |
| 1644 | #else |
| 1645 | acutest_colorize_ = _isatty(_fileno(stdout)); |
| 1646 | #endif |
| 1647 | #else |
| 1648 | acutest_colorize_ = 0; |
| 1649 | #endif |
| 1650 | |
| 1651 | /* Count all test units */ |
| 1652 | acutest_list_size_ = 0; |
| 1653 | for(i = 0; acutest_list_[i].func != NULL; i++) |
| 1654 | acutest_list_size_++; |
| 1655 | |
| 1656 | acutest_test_data_ = (struct acutest_test_data_*)calloc(acutest_list_size_, sizeof(struct acutest_test_data_)); |
| 1657 | if(acutest_test_data_ == NULL) { |
| 1658 | fprintf(stderr, "Out of memory.\n"); |
| 1659 | acutest_exit_(2); |
| 1660 | } |
| 1661 | |
| 1662 | /* Parse options */ |
| 1663 | acutest_cmdline_read_(acutest_cmdline_options_, argc, argv, acutest_cmdline_callback_); |
| 1664 | |
| 1665 | /* Initialize the proper timer. */ |
| 1666 | acutest_timer_init_(); |
| 1667 | |
| 1668 | #if defined(ACUTEST_WIN_) |
| 1669 | SetUnhandledExceptionFilter(acutest_seh_exception_filter_); |
| 1670 | #ifdef _MSC_VER |
| 1671 | _set_abort_behavior(0, _WRITE_ABORT_MSG); |
| 1672 | #endif |
| 1673 | #endif |
| 1674 | |
| 1675 | /* By default, we want to run all tests. */ |
| 1676 | if(acutest_count_ == 0) { |
| 1677 | for(i = 0; acutest_list_[i].func != NULL; i++) |
| 1678 | acutest_remember_(i); |
| 1679 | } |
| 1680 | |
| 1681 | /* Guess whether we want to run unit tests as child processes. */ |
| 1682 | if(acutest_no_exec_ < 0) { |
| 1683 | acutest_no_exec_ = 0; |
| 1684 | |
| 1685 | if(acutest_count_ <= 1) { |
| 1686 | acutest_no_exec_ = 1; |
| 1687 | } else { |
| 1688 | #ifdef ACUTEST_WIN_ |
| 1689 | if(IsDebuggerPresent()) |
| 1690 | acutest_no_exec_ = 1; |
| 1691 | #endif |
| 1692 | #ifdef ACUTEST_LINUX_ |
| 1693 | if(acutest_is_tracer_present_()) |
| 1694 | acutest_no_exec_ = 1; |
| 1695 | #endif |
| 1696 | #ifdef RUNNING_ON_VALGRIND |
| 1697 | /* RUNNING_ON_VALGRIND is provided by optionally included <valgrind.h> */ |
| 1698 | if(RUNNING_ON_VALGRIND) |
| 1699 | acutest_no_exec_ = 1; |
| 1700 | #endif |
| 1701 | } |
| 1702 | } |
| 1703 | |
| 1704 | if(acutest_tap_) { |
| 1705 | /* TAP requires we know test result ("ok", "not ok") before we output |
| 1706 | * anything about the test, and this gets problematic for larger verbose |
| 1707 | * levels. */ |
| 1708 | if(acutest_verbose_level_ > 2) |
| 1709 | acutest_verbose_level_ = 2; |
| 1710 | |
| 1711 | /* TAP harness should provide some summary. */ |
| 1712 | acutest_no_summary_ = 1; |
| 1713 | |
| 1714 | if(!acutest_worker_) |
| 1715 | printf("1..%d\n", (int) acutest_count_); |
| 1716 | } |
| 1717 | |
| 1718 | int index = acutest_worker_index_; |
| 1719 | for(i = 0; acutest_list_[i].func != NULL; i++) { |
| 1720 | int run = (acutest_test_data_[i].flags & ACUTEST_FLAG_RUN_); |
| 1721 | if (acutest_skip_mode_) /* Run all tests except those listed. */ |
| 1722 | run = !run; |
| 1723 | if(run) |
| 1724 | acutest_run_(´st_list_[i], index++, i); |
| 1725 | } |
| 1726 | |
| 1727 | /* Write a summary */ |
| 1728 | if(!acutest_no_summary_ && acutest_verbose_level_ >= 1) { |
| 1729 | if(acutest_verbose_level_ >= 3) { |
| 1730 | acutest_colored_printf_(ACUTEST_COLOR_DEFAULT_INTENSIVE_, "Summary:\n"); |
| 1731 | |
| 1732 | printf(" Count of all unit tests: %4d\n", (int) acutest_list_size_); |
| 1733 | printf(" Count of run unit tests: %4d\n", acutest_stat_run_units_); |
| 1734 | printf(" Count of failed unit tests: %4d\n", acutest_stat_failed_units_); |
| 1735 | printf(" Count of skipped unit tests: %4d\n", (int) acutest_list_size_ - acutest_stat_run_units_); |
| 1736 | } |
| 1737 | |
| 1738 | if(acutest_stat_failed_units_ == 0) { |
| 1739 | acutest_colored_printf_(ACUTEST_COLOR_GREEN_INTENSIVE_, "SUCCESS:"); |
| 1740 | printf(" All unit tests have passed.\n"); |
| 1741 | } else { |
| 1742 | acutest_colored_printf_(ACUTEST_COLOR_RED_INTENSIVE_, "FAILED:"); |
| 1743 | printf(" %d of %d unit tests %s failed.\n", |
| 1744 | acutest_stat_failed_units_, acutest_stat_run_units_, |
| 1745 | (acutest_stat_failed_units_ == 1) ? "has" : "have"); |
| 1746 | } |
| 1747 | |
| 1748 | if(acutest_verbose_level_ >= 3) |
| 1749 | printf("\n"); |
| 1750 | } |
| 1751 | |
| 1752 | if (acutest_xml_output_) { |
| 1753 | #if defined ACUTEST_UNIX_ |
| 1754 | char *suite_name = basename(argv[0]); |
| 1755 | #elif defined ACUTEST_WIN_ |
| 1756 | char suite_name[_MAX_FNAME]; |
| 1757 | _splitpath(argv[0], NULL, NULL, suite_name, NULL); |
| 1758 | #else |
| 1759 | const char *suite_name = argv[0]; |
| 1760 | #endif |
| 1761 | fprintf(acutest_xml_output_, "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"); |
| 1762 | fprintf(acutest_xml_output_, "<testsuite name=\"%s\" tests=\"%d\" errors=\"%d\" failures=\"%d\" skip=\"%d\">\n", |
| 1763 | suite_name, (int)acutest_list_size_, acutest_stat_failed_units_, acutest_stat_failed_units_, |
| 1764 | (int)acutest_list_size_ - acutest_stat_run_units_); |
| 1765 | for(i = 0; acutest_list_[i].func != NULL; i++) { |
| 1766 | struct acutest_test_data_ *details = ´st_test_data_[i]; |
| 1767 | fprintf(acutest_xml_output_, " <testcase name=\"%s\" time=\"%.2f\">\n", acutest_list_[i].name, details->duration); |
| 1768 | if (details->flags & ACUTEST_FLAG_FAILURE_) |
| 1769 | fprintf(acutest_xml_output_, " <failure />\n"); |
| 1770 | if (!(details->flags & ACUTEST_FLAG_FAILURE_) && !(details->flags & ACUTEST_FLAG_SUCCESS_)) |
| 1771 | fprintf(acutest_xml_output_, " <skipped />\n"); |
| 1772 | fprintf(acutest_xml_output_, " </testcase>\n"); |
| 1773 | } |
| 1774 | fprintf(acutest_xml_output_, "</testsuite>\n"); |
| 1775 | fclose(acutest_xml_output_); |
| 1776 | } |
| 1777 | |
| 1778 | acutest_cleanup_(); |
| 1779 | |
| 1780 | return (acutest_stat_failed_units_ == 0) ? 0 : 1; |
| 1781 | } |
| 1782 | |
| 1783 | |
| 1784 | #endif /* #ifndef TEST_NO_MAIN */ |
| 1785 | |
| 1786 | #ifdef _MSC_VER |
| 1787 | #pragma warning(pop) |
| 1788 | #endif |
| 1789 | |
| 1790 | #ifdef __cplusplus |
| 1791 | } /* extern "C" */ |
| 1792 | #endif |
| 1793 | |
| 1794 | #endif /* #ifndef ACUTEST_H */ |