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