1 /* Miniature re-implementation of the "check" library. 2 3 This is intended to support just enough of check to run the Expat 4 tests. This interface is based entirely on the portion of the 5 check library being used. 6 __ __ _ 7 ___\ \/ /_ __ __ _| |_ 8 / _ \\ /| '_ \ / _` | __| 9 | __// \| |_) | (_| | |_ 10 \___/_/\_\ .__/ \__,_|\__| 11 |_| XML parser 12 13 Copyright (c) 2004-2006 Fred L. Drake, Jr. <fdrake@users.sourceforge.net> 14 Copyright (c) 2016-2023 Sebastian Pipping <sebastian@pipping.org> 15 Copyright (c) 2017 Rhodri James <rhodri@wildebeest.org.uk> 16 Copyright (c) 2018 Marco Maggi <marco.maggi-ipsu@poste.it> 17 Copyright (c) 2019 David Loffredo <loffredo@steptools.com> 18 Copyright (c) 2023-2024 Sony Corporation / Snild Dolkow <snild@sony.com> 19 Licensed under the MIT license: 20 21 Permission is hereby granted, free of charge, to any person obtaining 22 a copy of this software and associated documentation files (the 23 "Software"), to deal in the Software without restriction, including 24 without limitation the rights to use, copy, modify, merge, publish, 25 distribute, sublicense, and/or sell copies of the Software, and to permit 26 persons to whom the Software is furnished to do so, subject to the 27 following conditions: 28 29 The above copyright notice and this permission notice shall be included 30 in all copies or substantial portions of the Software. 31 32 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 33 EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 34 MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN 35 NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 36 DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 37 OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE 38 USE OR OTHER DEALINGS IN THE SOFTWARE. 39 */ 40 41 #if defined(NDEBUG) 42 # undef NDEBUG /* because test suite relies on assert(...) at the moment */ 43 #endif 44 45 #include <stdarg.h> 46 #include <stdio.h> 47 #include <stdlib.h> 48 #include <setjmp.h> 49 #include <assert.h> 50 #include <string.h> 51 52 #include "internal.h" /* for UNUSED_P only */ 53 #include "minicheck.h" 54 55 Suite * 56 suite_create(const char *name) { 57 Suite *suite = (Suite *)calloc(1, sizeof(Suite)); 58 if (suite != NULL) { 59 suite->name = name; 60 } 61 return suite; 62 } 63 64 TCase * 65 tcase_create(const char *name) { 66 TCase *tc = (TCase *)calloc(1, sizeof(TCase)); 67 if (tc != NULL) { 68 tc->name = name; 69 } 70 return tc; 71 } 72 73 void 74 suite_add_tcase(Suite *suite, TCase *tc) { 75 assert(suite != NULL); 76 assert(tc != NULL); 77 assert(tc->next_tcase == NULL); 78 79 tc->next_tcase = suite->tests; 80 suite->tests = tc; 81 } 82 83 void 84 tcase_add_checked_fixture(TCase *tc, tcase_setup_function setup, 85 tcase_teardown_function teardown) { 86 assert(tc != NULL); 87 tc->setup = setup; 88 tc->teardown = teardown; 89 } 90 91 void 92 tcase_add_test(TCase *tc, tcase_test_function test) { 93 assert(tc != NULL); 94 if (tc->allocated == tc->ntests) { 95 int nalloc = tc->allocated + 100; 96 size_t new_size = sizeof(tcase_test_function) * nalloc; 97 tcase_test_function *const new_tests 98 = (tcase_test_function *)realloc(tc->tests, new_size); 99 assert(new_tests != NULL); 100 tc->tests = new_tests; 101 tc->allocated = nalloc; 102 } 103 tc->tests[tc->ntests] = test; 104 tc->ntests++; 105 } 106 107 static void 108 tcase_free(TCase *tc) { 109 if (! tc) { 110 return; 111 } 112 113 free(tc->tests); 114 free(tc); 115 } 116 117 static void 118 suite_free(Suite *suite) { 119 if (! suite) { 120 return; 121 } 122 123 while (suite->tests != NULL) { 124 TCase *next = suite->tests->next_tcase; 125 tcase_free(suite->tests); 126 suite->tests = next; 127 } 128 free(suite); 129 } 130 131 SRunner * 132 srunner_create(Suite *suite) { 133 SRunner *const runner = (SRunner *)calloc(1, sizeof(SRunner)); 134 if (runner != NULL) { 135 runner->suite = suite; 136 } 137 return runner; 138 } 139 140 static jmp_buf env; 141 142 #define SUBTEST_LEN (50) // informative, but not too long 143 static char const *_check_current_function = NULL; 144 static char _check_current_subtest[SUBTEST_LEN]; 145 static int _check_current_lineno = -1; 146 static char const *_check_current_filename = NULL; 147 148 void 149 _check_set_test_info(char const *function, char const *filename, int lineno) { 150 _check_current_function = function; 151 set_subtest("%s", ""); 152 _check_current_lineno = lineno; 153 _check_current_filename = filename; 154 } 155 156 void 157 set_subtest(char const *fmt, ...) { 158 va_list ap; 159 va_start(ap, fmt); 160 vsnprintf(_check_current_subtest, SUBTEST_LEN, fmt, ap); 161 va_end(ap); 162 // replace line feeds with spaces, for nicer error logs 163 for (size_t i = 0; i < SUBTEST_LEN; ++i) { 164 if (_check_current_subtest[i] == '\n') { 165 _check_current_subtest[i] = ' '; 166 } 167 } 168 _check_current_subtest[SUBTEST_LEN - 1] = '\0'; // ensure termination 169 } 170 171 static void 172 handle_success(int verbosity) { 173 if (verbosity >= CK_VERBOSE) { 174 printf("PASS: %s\n", _check_current_function); 175 } 176 } 177 178 static void 179 handle_failure(SRunner *runner, int verbosity, const char *context, 180 const char *phase_info) { 181 runner->nfailures++; 182 if (verbosity != CK_SILENT) { 183 if (strlen(_check_current_subtest) != 0) { 184 phase_info = _check_current_subtest; 185 } 186 printf("FAIL [%s]: %s (%s at %s:%d)\n", context, _check_current_function, 187 phase_info, _check_current_filename, _check_current_lineno); 188 } 189 } 190 191 void 192 srunner_run_all(SRunner *runner, const char *context, int verbosity) { 193 Suite *suite; 194 TCase *volatile tc; 195 assert(runner != NULL); 196 suite = runner->suite; 197 tc = suite->tests; 198 while (tc != NULL) { 199 volatile int i; 200 for (i = 0; i < tc->ntests; ++i) { 201 runner->nchecks++; 202 set_subtest("%s", ""); 203 204 if (tc->setup != NULL) { 205 /* setup */ 206 if (setjmp(env)) { 207 handle_failure(runner, verbosity, context, "during setup"); 208 continue; 209 } 210 tc->setup(); 211 } 212 /* test */ 213 if (setjmp(env)) { 214 handle_failure(runner, verbosity, context, "during actual test"); 215 continue; 216 } 217 (tc->tests[i])(); 218 set_subtest("%s", ""); 219 220 /* teardown */ 221 if (tc->teardown != NULL) { 222 if (setjmp(env)) { 223 handle_failure(runner, verbosity, context, "during teardown"); 224 continue; 225 } 226 tc->teardown(); 227 } 228 229 handle_success(verbosity); 230 } 231 tc = tc->next_tcase; 232 } 233 } 234 235 void 236 srunner_summarize(SRunner *runner, int verbosity) { 237 if (verbosity != CK_SILENT) { 238 int passed = runner->nchecks - runner->nfailures; 239 double percentage = ((double)passed) / runner->nchecks; 240 int display = (int)(percentage * 100); 241 printf("%d%%: Checks: %d, Failed: %d\n", display, runner->nchecks, 242 runner->nfailures); 243 } 244 } 245 246 void 247 _fail(const char *file, int line, const char *msg) { 248 /* Always print the error message so it isn't lost. In this case, 249 we have a failure, so there's no reason to be quiet about what 250 it is. 251 */ 252 _check_current_filename = file; 253 _check_current_lineno = line; 254 if (msg != NULL) { 255 const int has_newline = (msg[strlen(msg) - 1] == '\n'); 256 fprintf(stderr, "ERROR: %s%s", msg, has_newline ? "" : "\n"); 257 } 258 longjmp(env, 1); 259 } 260 261 int 262 srunner_ntests_failed(SRunner *runner) { 263 assert(runner != NULL); 264 return runner->nfailures; 265 } 266 267 void 268 srunner_free(SRunner *runner) { 269 if (! runner) { 270 return; 271 } 272 273 suite_free(runner->suite); 274 free(runner); 275 } 276