Extract callbacks and enabled state to non-packed structure
[libside.git] / tests / utils / tap.c
1 /*
2 * SPDX-License-Identifier: BSD-2-Clause
3 * SPDX-FileCopyrightText: 2004 Nik Clayton
4 * SPDX-FileCopyrightText: 2017 Jérémie Galarneau
5 */
6
7 #include <ctype.h>
8 #include <stdarg.h>
9 #include <stdio.h>
10 #include <stdlib.h>
11 #include <string.h>
12 #include <limits.h>
13 #include <assert.h>
14
15 #include "tap.h"
16
17 static int no_plan = 0;
18 static int skip_all = 0;
19 static int have_plan = 0;
20 static unsigned int test_count = 0; /* Number of tests that have been run */
21 static unsigned int e_tests = 0; /* Expected number of tests to run */
22 static unsigned int failures = 0; /* Number of tests that failed */
23 static char *todo_msg = NULL;
24 static const char *todo_msg_fixed = "libtap malloc issue";
25 static int todo = 0;
26 static int test_died = 0;
27
28 /* Encapsulate the pthread code in a conditional. In the absence of
29 libpthread the code does nothing */
30 #ifdef HAVE_LIBPTHREAD
31 #include <pthread.h>
32 static pthread_mutex_t M = PTHREAD_MUTEX_INITIALIZER;
33 # define LOCK pthread_mutex_lock(&M);
34 # define UNLOCK pthread_mutex_unlock(&M);
35 #else
36 # define LOCK
37 # define UNLOCK
38 #endif
39
40 static void _expected_tests(unsigned int);
41 static void _tap_init(void);
42 static void _cleanup(void);
43
44 #ifdef __MINGW32__
45 static inline
46 void flockfile (FILE * filehandle) {
47 return;
48 }
49
50 static inline
51 void funlockfile(FILE * filehandle) {
52 return;
53 }
54 #endif
55
56 /*
57 * Generate a test result.
58 *
59 * ok -- boolean, indicates whether or not the test passed.
60 * test_name -- the name of the test, may be NULL
61 * test_comment -- a comment to print afterwards, may be NULL
62 */
63 unsigned int
64 _gen_result(int ok, const char *func, const char *file, unsigned int line,
65 const char *test_name, ...)
66 {
67 va_list ap;
68 char *local_test_name = NULL;
69 char *c;
70 int name_is_digits;
71
72 LOCK;
73
74 test_count++;
75
76 /* Start by taking the test name and performing any printf()
77 expansions on it */
78 if(test_name != NULL) {
79 va_start(ap, test_name);
80 if (vasprintf(&local_test_name, test_name, ap) == -1) {
81 local_test_name = NULL;
82 }
83 va_end(ap);
84
85 /* Make sure the test name contains more than digits
86 and spaces. Emit an error message and exit if it
87 does */
88 if(local_test_name) {
89 name_is_digits = 1;
90 for(c = local_test_name; *c != '\0'; c++) {
91 if(!isdigit((unsigned char) *c) && !isspace((unsigned char) *c)) {
92 name_is_digits = 0;
93 break;
94 }
95 }
96
97 if(name_is_digits) {
98 diag(" You named your test '%s'. You shouldn't use numbers for your test names.", local_test_name);
99 diag(" Very confusing.");
100 }
101 }
102 }
103
104 if(!ok) {
105 printf("not ");
106 failures++;
107 }
108
109 printf("ok %d", test_count);
110
111 if(test_name != NULL) {
112 printf(" - ");
113
114 /* Print the test name, escaping any '#' characters it
115 might contain */
116 if(local_test_name != NULL) {
117 flockfile(stdout);
118 for(c = local_test_name; *c != '\0'; c++) {
119 if(*c == '#')
120 fputc('\\', stdout);
121 fputc((int)*c, stdout);
122 }
123 funlockfile(stdout);
124 } else { /* vasprintf() failed, use a fixed message */
125 printf("%s", todo_msg_fixed);
126 }
127 }
128
129 /* If we're in a todo_start() block then flag the test as being
130 TODO. todo_msg should contain the message to print at this
131 point. If it's NULL then asprintf() failed, and we should
132 use the fixed message.
133
134 This is not counted as a failure, so decrement the counter if
135 the test failed. */
136 if(todo) {
137 printf(" # TODO %s", todo_msg ? todo_msg : todo_msg_fixed);
138 if(!ok)
139 failures--;
140 }
141
142 printf("\n");
143
144 if(!ok) {
145 if(getenv("HARNESS_ACTIVE") != NULL)
146 fputs("\n", stderr);
147
148 diag(" Failed %stest (%s:%s() at line %d)",
149 todo ? "(TODO) " : "", file, func, line);
150 }
151 free(local_test_name);
152
153 UNLOCK;
154
155 /* We only care (when testing) that ok is positive, but here we
156 specifically only want to return 1 or 0 */
157 return ok ? 1 : 0;
158 }
159
160 /*
161 * Initialise the TAP library. Will only do so once, however many times it's
162 * called.
163 */
164 void
165 _tap_init(void)
166 {
167 static int run_once = 0;
168
169 if(!run_once) {
170 atexit(_cleanup);
171
172 /* stdout needs to be unbuffered so that the output appears
173 in the same place relative to stderr output as it does
174 with Test::Harness */
175 setbuf(stdout, 0);
176 run_once = 1;
177 }
178 }
179
180 /*
181 * Note that there's no plan.
182 */
183 int
184 plan_no_plan(void)
185 {
186
187 LOCK;
188
189 _tap_init();
190
191 if(have_plan != 0) {
192 fprintf(stderr, "You tried to plan twice!\n");
193 test_died = 1;
194 UNLOCK;
195 exit(255);
196 }
197
198 have_plan = 1;
199 no_plan = 1;
200
201 UNLOCK;
202
203 return 1;
204 }
205
206 /*
207 * Note that the plan is to skip all tests
208 */
209 int
210 plan_skip_all(const char *reason)
211 {
212
213 LOCK;
214
215 _tap_init();
216
217 skip_all = 1;
218
219 printf("1..0");
220
221 if(reason != NULL)
222 printf(" # Skip %s", reason);
223
224 printf("\n");
225
226 UNLOCK;
227
228 exit(0);
229 }
230
231 /*
232 * Note the number of tests that will be run.
233 */
234 int
235 plan_tests(unsigned int tests)
236 {
237
238 LOCK;
239
240 _tap_init();
241
242 if(have_plan != 0) {
243 fprintf(stderr, "You tried to plan twice!\n");
244 test_died = 1;
245 UNLOCK;
246 exit(255);
247 }
248
249 if(tests == 0) {
250 fprintf(stderr, "You said to run 0 tests! You've got to run something.\n");
251 test_died = 1;
252 UNLOCK;
253 exit(255);
254 }
255
256 have_plan = 1;
257
258 _expected_tests(tests);
259
260 UNLOCK;
261
262 return e_tests;
263 }
264
265 unsigned int
266 diag(const char *fmt, ...)
267 {
268 va_list ap;
269
270 fputs("# ", stderr);
271
272 va_start(ap, fmt);
273 vfprintf(stderr, fmt, ap);
274 va_end(ap);
275
276 fputs("\n", stderr);
277
278 return 0;
279 }
280
281 void
282 diag_multiline(const char *val)
283 {
284 size_t len, i, line_start_idx = 0;
285
286 assert(val);
287 len = strlen(val);
288
289 for (i = 0; i < len; i++) {
290 int line_length;
291
292 if (val[i] != '\n') {
293 continue;
294 }
295
296 assert((i - line_start_idx + 1) <= INT_MAX);
297 line_length = i - line_start_idx + 1;
298 fprintf(stderr, "# %.*s", line_length, &val[line_start_idx]);
299 line_start_idx = i + 1;
300 }
301 }
302
303 void
304 _expected_tests(unsigned int tests)
305 {
306
307 printf("1..%d\n", tests);
308 e_tests = tests;
309 }
310
311 int
312 skip(unsigned int n, const char *fmt, ...)
313 {
314 va_list ap;
315 char *skip_msg = NULL;
316
317 LOCK;
318
319 va_start(ap, fmt);
320 if (vasprintf(&skip_msg, fmt, ap) == -1) {
321 skip_msg = NULL;
322 }
323 va_end(ap);
324
325 while(n-- > 0) {
326 test_count++;
327 printf("ok %d # skip %s\n", test_count,
328 skip_msg != NULL ?
329 skip_msg : "libtap():malloc() failed");
330 }
331
332 free(skip_msg);
333
334 UNLOCK;
335
336 return 1;
337 }
338
339 void
340 todo_start(const char *fmt, ...)
341 {
342 va_list ap;
343
344 LOCK;
345
346 va_start(ap, fmt);
347 if (vasprintf(&todo_msg, fmt, ap) == -1) {
348 todo_msg = NULL;
349 }
350 va_end(ap);
351
352 todo = 1;
353
354 UNLOCK;
355 }
356
357 void
358 todo_end(void)
359 {
360
361 LOCK;
362
363 todo = 0;
364 free(todo_msg);
365
366 UNLOCK;
367 }
368
369 int
370 exit_status(void)
371 {
372 int r;
373
374 LOCK;
375
376 /* If there's no plan, just return the number of failures */
377 if(no_plan || !have_plan) {
378 UNLOCK;
379 return failures;
380 }
381
382 /* Ran too many tests? Return the number of tests that were run
383 that shouldn't have been */
384 if(e_tests < test_count) {
385 r = test_count - e_tests;
386 UNLOCK;
387 return r;
388 }
389
390 /* Return the number of tests that failed + the number of tests
391 that weren't run */
392 r = failures + e_tests - test_count;
393 UNLOCK;
394
395 return r;
396 }
397
398 /*
399 * Cleanup at the end of the run, produce any final output that might be
400 * required.
401 */
402 void
403 _cleanup(void)
404 {
405
406 LOCK;
407
408 /* If plan_no_plan() wasn't called, and we don't have a plan,
409 and we're not skipping everything, then something happened
410 before we could produce any output */
411 if(!no_plan && !have_plan && !skip_all) {
412 diag("Looks like your test died before it could output anything.");
413 UNLOCK;
414 return;
415 }
416
417 if(test_died) {
418 diag("Looks like your test died just after %d.", test_count);
419 UNLOCK;
420 return;
421 }
422
423
424 /* No plan provided, but now we know how many tests were run, and can
425 print the header at the end */
426 if(!skip_all && (no_plan || !have_plan)) {
427 printf("1..%d\n", test_count);
428 }
429
430 if((have_plan && !no_plan) && e_tests < test_count) {
431 diag("Looks like you planned %d %s but ran %d extra.",
432 e_tests, e_tests == 1 ? "test" : "tests", test_count - e_tests);
433 UNLOCK;
434 return;
435 }
436
437 if((have_plan || !no_plan) && e_tests > test_count) {
438 diag("Looks like you planned %d %s but only ran %d.",
439 e_tests, e_tests == 1 ? "test" : "tests", test_count);
440 UNLOCK;
441 return;
442 }
443
444 if(failures)
445 diag("Looks like you failed %d %s of %d.",
446 failures, failures == 1 ? "test" : "tests", test_count);
447
448 UNLOCK;
449 }
This page took 0.049054 seconds and 4 git commands to generate.