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 *
suite_create(const char * name)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 *
tcase_create(const char * name)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
suite_add_tcase(Suite * suite,TCase * tc)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
tcase_add_checked_fixture(TCase * tc,tcase_setup_function setup,tcase_teardown_function teardown)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
tcase_add_test(TCase * tc,tcase_test_function test)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
tcase_free(TCase * tc)108 tcase_free(TCase *tc) {
109 if (! tc) {
110 return;
111 }
112
113 free(tc->tests);
114 free(tc);
115 }
116
117 static void
suite_free(Suite * suite)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 *
srunner_create(Suite * suite)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
_check_set_test_info(char const * function,char const * filename,int lineno)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
set_subtest(char const * fmt,...)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
handle_success(int verbosity)172 handle_success(int verbosity) {
173 if (verbosity >= CK_VERBOSE) {
174 printf("PASS: %s\n", _check_current_function);
175 }
176 }
177
178 static void
handle_failure(SRunner * runner,int verbosity,const char * context,const char * phase_info)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
srunner_run_all(SRunner * runner,const char * context,int verbosity)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
srunner_summarize(SRunner * runner,int verbosity)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
_fail(const char * file,int line,const char * msg)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
srunner_ntests_failed(SRunner * runner)262 srunner_ntests_failed(SRunner *runner) {
263 assert(runner != NULL);
264 return runner->nfailures;
265 }
266
267 void
srunner_free(SRunner * runner)268 srunner_free(SRunner *runner) {
269 if (! runner) {
270 return;
271 }
272
273 suite_free(runner->suite);
274 free(runner);
275 }
276