vis
a vi-like editor based on Plan 9's structural regular expressions
git clone https://9o.is/git/vis.git
tap.c
(9292B)
1 /*-
2 * Copyright (c) 2004 Nik Clayton
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24 * SUCH DAMAGE.
25 */
26 #include "config.h"
27 #include <ctype.h>
28 #include <stdarg.h>
29 #include <stdio.h>
30 #include <stdlib.h>
31 #include <unistd.h>
32
33 #include "tap.h"
34
35 void (*tap_fail_callback)(void) = NULL;
36
37 static int no_plan = 0;
38 static int skip_all = 0;
39 static int have_plan = 0;
40 static unsigned int test_count = 0; /* Number of tests that have been run */
41 static unsigned int e_tests = 0; /* Expected number of tests to run */
42 static unsigned int failures = 0; /* Number of tests that failed */
43 static char *todo_msg = NULL;
44 static const char *todo_msg_fixed = "libtap malloc issue";
45 static int todo = 0;
46 static int test_died = 0;
47 static int test_pid;
48
49 /* Encapsulate the pthread code in a conditional. In the absence of
50 libpthread the code does nothing.
51
52 If you have multiple threads calling ok() etc. at the same time you would
53 need this, but in that case your test numbers will be random and I'm not
54 sure it makes sense. --RR
55 */
56 #ifdef WANT_PTHREAD
57 #include <pthread.h>
58 static pthread_mutex_t M = PTHREAD_MUTEX_INITIALIZER;
59 # define LOCK pthread_mutex_lock(&M)
60 # define UNLOCK pthread_mutex_unlock(&M)
61 #else
62 # define LOCK
63 # define UNLOCK
64 #endif
65
66 static void
67 _expected_tests(unsigned int tests)
68 {
69
70 printf("1..%d\n", tests);
71 e_tests = tests;
72 }
73
74 static void
75 diagv(const char *fmt, va_list ap)
76 {
77 fputs("# ", stdout);
78 vfprintf(stdout, fmt, ap);
79 fputs("\n", stdout);
80 }
81
82 static void
83 _diag(const char *fmt, ...)
84 {
85 va_list ap;
86 va_start(ap, fmt);
87 diagv(fmt, ap);
88 va_end(ap);
89 }
90
91 /*
92 * Generate a test result.
93 *
94 * ok -- boolean, indicates whether or not the test passed.
95 * test_name -- the name of the test, may be NULL
96 * test_comment -- a comment to print afterwards, may be NULL
97 */
98 unsigned int
99 _gen_result(int ok, const char *func, const char *file, unsigned int line,
100 const char *test_name, ...)
101 {
102 va_list ap;
103 char *local_test_name = NULL;
104 char *c;
105 int name_is_digits;
106
107 LOCK;
108
109 test_count++;
110
111 /* Start by taking the test name and performing any printf()
112 expansions on it */
113 if(test_name != NULL) {
114 va_start(ap, test_name);
115 if (vasprintf(&local_test_name, test_name, ap) < 0)
116 local_test_name = NULL;
117 va_end(ap);
118
119 /* Make sure the test name contains more than digits
120 and spaces. Emit an error message and exit if it
121 does */
122 if(local_test_name) {
123 name_is_digits = 1;
124 for(c = local_test_name; *c != '\0'; c++) {
125 if(!isdigit((unsigned char)*c)
126 && !isspace((unsigned char)*c)) {
127 name_is_digits = 0;
128 break;
129 }
130 }
131
132 if(name_is_digits) {
133 _diag(" You named your test '%s'. You shouldn't use numbers for your test names.", local_test_name);
134 _diag(" Very confusing.");
135 }
136 }
137 }
138
139 if(!ok) {
140 printf("not ");
141 failures++;
142 }
143
144 printf("ok %d", test_count);
145
146 if(test_name != NULL) {
147 printf(" - ");
148
149 /* Print the test name, escaping any '#' characters it
150 might contain */
151 if(local_test_name != NULL) {
152 flockfile(stdout);
153 for(c = local_test_name; *c != '\0'; c++) {
154 if(*c == '#')
155 fputc('\\', stdout);
156 fputc((int)*c, stdout);
157 }
158 funlockfile(stdout);
159 } else { /* vasprintf() failed, use a fixed message */
160 printf("%s", todo_msg_fixed);
161 }
162 }
163
164 /* If we're in a todo_start() block then flag the test as being
165 TODO. todo_msg should contain the message to print at this
166 point. If it's NULL then asprintf() failed, and we should
167 use the fixed message.
168
169 This is not counted as a failure, so decrement the counter if
170 the test failed. */
171 if(todo) {
172 printf(" # TODO %s", todo_msg ? todo_msg : todo_msg_fixed);
173 if(!ok)
174 failures--;
175 }
176
177 printf("\n");
178
179 if(!ok)
180 _diag(" Failed %stest (%s:%s() at line %d)",
181 todo ? "(TODO) " : "", file, func, line);
182
183 free(local_test_name);
184
185 UNLOCK;
186
187 if (!ok && tap_fail_callback)
188 tap_fail_callback();
189
190 /* We only care (when testing) that ok is positive, but here we
191 specifically only want to return 1 or 0 */
192 return ok ? 1 : 0;
193 }
194
195 /*
196 * Cleanup at the end of the run, produce any final output that might be
197 * required.
198 */
199 static void
200 _cleanup(void)
201 {
202 /* If we forked, don't do cleanup in child! */
203 if (getpid() != test_pid)
204 return;
205
206 LOCK;
207
208 /* If plan_no_plan() wasn't called, and we don't have a plan,
209 and we're not skipping everything, then something happened
210 before we could produce any output */
211 if(!no_plan && !have_plan && !skip_all) {
212 _diag("Looks like your test died before it could output anything.");
213 UNLOCK;
214 return;
215 }
216
217 if(test_died) {
218 _diag("Looks like your test died just after %d.", test_count);
219 UNLOCK;
220 return;
221 }
222
223
224 /* No plan provided, but now we know how many tests were run, and can
225 print the header at the end */
226 if(!skip_all && (no_plan || !have_plan)) {
227 printf("1..%d\n", test_count);
228 }
229
230 if((have_plan && !no_plan) && e_tests < test_count) {
231 _diag("Looks like you planned %d tests but ran %d extra.",
232 e_tests, test_count - e_tests);
233 UNLOCK;
234 return;
235 }
236
237 if((have_plan || !no_plan) && e_tests > test_count) {
238 _diag("Looks like you planned %d tests but only ran %d.",
239 e_tests, test_count);
240 if(failures) {
241 _diag("Looks like you failed %d tests of %d run.",
242 failures, test_count);
243 }
244 UNLOCK;
245 return;
246 }
247
248 if(failures)
249 _diag("Looks like you failed %d tests of %d.",
250 failures, test_count);
251
252 UNLOCK;
253 }
254
255 /*
256 * Initialise the TAP library. Will only do so once, however many times it's
257 * called.
258 */
259 static void
260 _tap_init(void)
261 {
262 static int run_once = 0;
263
264 if(!run_once) {
265 test_pid = getpid();
266 atexit(_cleanup);
267
268 /* stdout needs to be unbuffered so that the output appears
269 in the same place relative to stderr output as it does
270 with Test::Harness */
271 // setbuf(stdout, 0);
272 run_once = 1;
273 }
274 }
275
276 /*
277 * Note that there's no plan.
278 */
279 void
280 plan_no_plan(void)
281 {
282
283 LOCK;
284
285 _tap_init();
286
287 if(have_plan != 0) {
288 fprintf(stderr, "You tried to plan twice!\n");
289 test_died = 1;
290 UNLOCK;
291 exit(255);
292 }
293
294 have_plan = 1;
295 no_plan = 1;
296
297 UNLOCK;
298 }
299
300 /*
301 * Note that the plan is to skip all tests
302 */
303 void
304 plan_skip_all(const char *reason)
305 {
306
307 LOCK;
308
309 _tap_init();
310
311 skip_all = 1;
312
313 printf("1..0");
314
315 if(reason != NULL)
316 printf(" # Skip %s", reason);
317
318 printf("\n");
319
320 UNLOCK;
321 }
322
323 /*
324 * Note the number of tests that will be run.
325 */
326 void
327 plan_tests(unsigned int tests)
328 {
329
330 LOCK;
331
332 _tap_init();
333
334 if(have_plan != 0) {
335 fprintf(stderr, "You tried to plan twice!\n");
336 test_died = 1;
337 UNLOCK;
338 exit(255);
339 }
340
341 if(tests == 0) {
342 fprintf(stderr, "You said to run 0 tests! You've got to run something.\n");
343 test_died = 1;
344 UNLOCK;
345 exit(255);
346 }
347
348 have_plan = 1;
349
350 _expected_tests(tests);
351
352 UNLOCK;
353 }
354
355 void
356 diag(const char *fmt, ...)
357 {
358 va_list ap;
359
360 LOCK;
361
362 va_start(ap, fmt);
363 diagv(fmt, ap);
364 va_end(ap);
365
366 UNLOCK;
367 }
368
369 void
370 skip(unsigned int n, const char *fmt, ...)
371 {
372 va_list ap;
373 char *skip_msg;
374
375 LOCK;
376
377 va_start(ap, fmt);
378 if (vasprintf(&skip_msg, fmt, ap) < 0)
379 skip_msg = NULL;
380 va_end(ap);
381
382 while(n-- > 0) {
383 test_count++;
384 printf("ok %d # skip %s\n", test_count,
385 skip_msg != NULL ?
386 skip_msg : "libtap():malloc() failed");
387 }
388
389 free(skip_msg);
390
391 UNLOCK;
392 }
393
394 void
395 todo_start(const char *fmt, ...)
396 {
397 va_list ap;
398
399 LOCK;
400
401 va_start(ap, fmt);
402 if (vasprintf(&todo_msg, fmt, ap) < 0)
403 todo_msg = NULL;
404 va_end(ap);
405
406 todo = 1;
407
408 UNLOCK;
409 }
410
411 void
412 todo_end(void)
413 {
414
415 LOCK;
416
417 todo = 0;
418 free(todo_msg);
419
420 UNLOCK;
421 }
422
423 static int
424 exit_status_(void)
425 {
426 int r;
427
428 LOCK;
429
430 /* If there's no plan, just return the number of failures */
431 if(no_plan || !have_plan) {
432 UNLOCK;
433 return failures;
434 }
435
436 /* Ran too many tests? Return the number of tests that were run
437 that shouldn't have been */
438 if(e_tests < test_count) {
439 r = test_count - e_tests;
440 UNLOCK;
441 return r;
442 }
443
444 /* Return the number of tests that failed + the number of tests
445 that weren't run */
446 r = failures + e_tests - test_count;
447 UNLOCK;
448
449 return r;
450 }
451
452 int
453 exit_status(void)
454 {
455 int r = exit_status_();
456 if (r > 255)
457 r = 255;
458 return r;
459 }