Raw
1 #define DISABLE_SIGN_COMPARE_WARNINGS
2
3 #include "test-lib.h"
4
5 enum result {
6 RESULT_NONE,
7 RESULT_FAILURE,
8 RESULT_SKIP,
9 RESULT_SUCCESS,
10 RESULT_TODO
11 };
12
13 static struct {
14 enum result result;
15 int count;
16 unsigned failed :1;
17 unsigned lazy_plan :1;
18 unsigned running :1;
19 unsigned skip_all :1;
20 unsigned todo :1;
21 char location[100];
22 char description[100];
23 } ctx = {
24 .lazy_plan = 1,
25 .result = RESULT_NONE,
26 };
27
28 /*
29 * Visual C interpolates the absolute Windows path for `__FILE__`,
30 * but we want to see relative paths, as verified by t0080.
31 * There are other compilers that do the same, and are not for
32 * Windows.
33 */
34 #include "dir.h"
35
36 static const char *make_relative(const char *location)
37 {
38 static char prefix[] = __FILE__, buf[PATH_MAX], *p;
39 static size_t prefix_len;
40 static int need_bs_to_fs = -1;
41
42 /* one-time preparation */
43 if (need_bs_to_fs < 0) {
44 size_t len = strlen(prefix);
45 char needle[] = "t\\unit-tests\\test-lib.c";
46 size_t needle_len = strlen(needle);
47
48 if (len < needle_len)
49 die("unexpected prefix '%s'", prefix);
50
51 /*
52 * The path could be relative (t/unit-tests/test-lib.c)
53 * or full (/home/user/git/t/unit-tests/test-lib.c).
54 * Check the slash between "t" and "unit-tests".
55 */
56 prefix_len = len - needle_len;
57 if (prefix[prefix_len + 1] == '/') {
58 /* Oh, we're not Windows */
59 for (size_t i = 0; i < needle_len; i++)
60 if (needle[i] == '\\')
61 needle[i] = '/';
62 need_bs_to_fs = 0;
63 } else {
64 need_bs_to_fs = 1;
65 }
66
67 /*
68 * prefix_len == 0 if the compiler gives paths relative
69 * to the root of the working tree. Otherwise, we want
70 * to see that we did find the needle[] at a directory
71 * boundary. Again we rely on that needle[] begins with
72 * "t" followed by the directory separator.
73 */
74 if (fspathcmp(needle, prefix + prefix_len) ||
75 (prefix_len && prefix[prefix_len - 1] != needle[1]))
76 die("unexpected suffix of '%s'", prefix);
77 }
78
79 /*
80 * Does it not start with the expected prefix?
81 * Return it as-is without making it worse.
82 */
83 if (prefix_len && fspathncmp(location, prefix, prefix_len))
84 return location;
85
86 /*
87 * If we do not need to munge directory separator, we can return
88 * the substring at the tail of the location.
89 */
90 if (!need_bs_to_fs)
91 return location + prefix_len;
92
93 /* convert backslashes to forward slashes */
94 strlcpy(buf, location + prefix_len, sizeof(buf));
95 for (p = buf; *p; p++)
96 if (*p == '\\')
97 *p = '/';
98 return buf;
99 }
100
101 static void msg_with_prefix(const char *prefix, const char *format, va_list ap)
102 {
103 fflush(stderr);
104 if (prefix)
105 fprintf(stdout, "%s", prefix);
106 vprintf(format, ap); /* TODO: handle newlines */
107 putc('\n', stdout);
108 fflush(stdout);
109 }
110
111 void test_msg(const char *format, ...)
112 {
113 va_list ap;
114
115 va_start(ap, format);
116 msg_with_prefix("# ", format, ap);
117 va_end(ap);
118 }
119
120 void test_plan(int count)
121 {
122 assert(!ctx.running);
123
124 fflush(stderr);
125 printf("1..%d\n", count);
126 fflush(stdout);
127 ctx.lazy_plan = 0;
128 }
129
130 int test_done(void)
131 {
132 if (ctx.running && ctx.location[0] && ctx.description[0])
133 test__run_end(1, ctx.location, "%s", ctx.description);
134 assert(!ctx.running);
135
136 if (ctx.lazy_plan)
137 test_plan(ctx.count);
138
139 return ctx.failed;
140 }
141
142 void test_skip(const char *format, ...)
143 {
144 va_list ap;
145
146 assert(ctx.running);
147
148 ctx.result = RESULT_SKIP;
149 va_start(ap, format);
150 if (format)
151 msg_with_prefix("# skipping test - ", format, ap);
152 va_end(ap);
153 }
154
155 void test_skip_all(const char *format, ...)
156 {
157 va_list ap;
158 const char *prefix;
159
160 if (!ctx.count && ctx.lazy_plan) {
161 /* We have not printed a test plan yet */
162 prefix = "1..0 # SKIP ";
163 ctx.lazy_plan = 0;
164 } else {
165 /* We have already printed a test plan */
166 prefix = "Bail out! # ";
167 ctx.failed = 1;
168 }
169 ctx.skip_all = 1;
170 ctx.result = RESULT_SKIP;
171 va_start(ap, format);
172 msg_with_prefix(prefix, format, ap);
173 va_end(ap);
174 }
175
176 void test__run_describe(const char *location, const char *format, ...)
177 {
178 va_list ap;
179 int len;
180
181 assert(ctx.running);
182 assert(!ctx.location[0]);
183 assert(!ctx.description[0]);
184
185 xsnprintf(ctx.location, sizeof(ctx.location), "%s",
186 make_relative(location));
187
188 va_start(ap, format);
189 len = vsnprintf(ctx.description, sizeof(ctx.description), format, ap);
190 va_end(ap);
191 if (len < 0)
192 die("unable to format message: %s", format);
193 if (len >= sizeof(ctx.description))
194 BUG("ctx.description too small to format %s", format);
195 }
196
197 int test__run_begin(void)
198 {
199 if (ctx.running && ctx.location[0] && ctx.description[0])
200 test__run_end(1, ctx.location, "%s", ctx.description);
201 assert(!ctx.running);
202
203 ctx.count++;
204 ctx.result = RESULT_NONE;
205 ctx.running = 1;
206 ctx.location[0] = '\0';
207 ctx.description[0] = '\0';
208
209 return ctx.skip_all;
210 }
211
212 static void print_description(const char *format, va_list ap)
213 {
214 if (format) {
215 fputs(" - ", stdout);
216 vprintf(format, ap);
217 }
218 }
219
220 int test__run_end(int was_run UNUSED, const char *location, const char *format, ...)
221 {
222 va_list ap;
223
224 assert(ctx.running);
225 assert(!ctx.todo);
226
227 fflush(stderr);
228 va_start(ap, format);
229 if (!ctx.skip_all) {
230 switch (ctx.result) {
231 case RESULT_SUCCESS:
232 printf("ok %d", ctx.count);
233 print_description(format, ap);
234 break;
235
236 case RESULT_FAILURE:
237 printf("not ok %d", ctx.count);
238 print_description(format, ap);
239 break;
240
241 case RESULT_TODO:
242 printf("not ok %d", ctx.count);
243 print_description(format, ap);
244 printf(" # TODO");
245 break;
246
247 case RESULT_SKIP:
248 printf("ok %d", ctx.count);
249 print_description(format, ap);
250 printf(" # SKIP");
251 break;
252
253 case RESULT_NONE:
254 test_msg("BUG: test has no checks at %s",
255 make_relative(location));
256 printf("not ok %d", ctx.count);
257 print_description(format, ap);
258 ctx.result = RESULT_FAILURE;
259 break;
260 }
261 }
262 va_end(ap);
263 ctx.running = 0;
264 if (ctx.skip_all)
265 return 1;
266 putc('\n', stdout);
267 fflush(stdout);
268 ctx.failed |= ctx.result == RESULT_FAILURE;
269
270 return ctx.result != RESULT_FAILURE;
271 }
272
273 static void test_fail(void)
274 {
275 assert(ctx.result != RESULT_SKIP);
276
277 ctx.result = RESULT_FAILURE;
278 }
279
280 static void test_pass(void)
281 {
282 assert(ctx.result != RESULT_SKIP);
283
284 if (ctx.result == RESULT_NONE)
285 ctx.result = RESULT_SUCCESS;
286 }
287
288 static void test_todo(void)
289 {
290 assert(ctx.result != RESULT_SKIP);
291
292 if (ctx.result != RESULT_FAILURE)
293 ctx.result = RESULT_TODO;
294 }
295
296 int test_assert(const char *location, const char *check, int ok)
297 {
298 if (!ctx.running) {
299 test_msg("BUG: check outside of test at %s",
300 make_relative(location));
301 ctx.failed = 1;
302 return 0;
303 }
304
305 if (ctx.result == RESULT_SKIP) {
306 test_msg("skipping check '%s' at %s", check,
307 make_relative(location));
308 return 1;
309 }
310 if (!ctx.todo) {
311 if (ok) {
312 test_pass();
313 } else {
314 test_msg("check \"%s\" failed at %s", check,
315 make_relative(location));
316 test_fail();
317 }
318 }
319
320 return !!ok;
321 }
322
323 void test__todo_begin(void)
324 {
325 assert(ctx.running);
326 assert(!ctx.todo);
327
328 ctx.todo = 1;
329 }
330
331 int test__todo_end(const char *location, const char *check, int res)
332 {
333 assert(ctx.running);
334 assert(ctx.todo);
335
336 ctx.todo = 0;
337 if (ctx.result == RESULT_SKIP)
338 return 1;
339 if (res) {
340 test_msg("todo check '%s' succeeded at %s", check,
341 make_relative(location));
342 test_fail();
343 } else {
344 test_todo();
345 }
346
347 return !res;
348 }
349
350 int check_bool_loc(const char *loc, const char *check, int ok)
351 {
352 return test_assert(loc, check, ok);
353 }
354
355 union test__tmp test__tmp[2];
356
357 int check_pointer_eq_loc(const char *loc, const char *check, int ok,
358 const void *a, const void *b)
359 {
360 int ret = test_assert(loc, check, ok);
361
362 if (!ret) {
363 test_msg(" left: %p", a);
364 test_msg(" right: %p", b);
365 }
366
367 return ret;
368 }
369
370 int check_int_loc(const char *loc, const char *check, int ok,
371 intmax_t a, intmax_t b)
372 {
373 int ret = test_assert(loc, check, ok);
374
375 if (!ret) {
376 test_msg(" left: %"PRIdMAX, a);
377 test_msg(" right: %"PRIdMAX, b);
378 }
379
380 return ret;
381 }
382
383 int check_uint_loc(const char *loc, const char *check, int ok,
384 uintmax_t a, uintmax_t b)
385 {
386 int ret = test_assert(loc, check, ok);
387
388 if (!ret) {
389 test_msg(" left: %"PRIuMAX, a);
390 test_msg(" right: %"PRIuMAX, b);
391 }
392
393 return ret;
394 }
395
396 static void print_one_char(char ch, char quote)
397 {
398 if ((unsigned char)ch < 0x20u || ch == 0x7f) {
399 char esc;
400 switch (ch) {
401 case '\a': esc = 'a'; break;
402 case '\b': esc = 'b'; break;
403 case '\t': esc = 't'; break;
404 case '\n': esc = 'n'; break;
405 case '\v': esc = 'v'; break;
406 case '\f': esc = 'f'; break;
407 case '\r': esc = 'r'; break;
408 default: esc = 0; break;
409 }
410 if (esc) {
411 putc('\\', stdout);
412 putc(esc, stdout);
413 } else {
414 printf("\\%03o", (unsigned char)ch);
415 }
416 } else {
417 if (ch == '\\' || ch == quote)
418 putc('\\', stdout);
419 putc(ch, stdout);
420 }
421 }
422
423 static void print_char(const char *prefix, char ch)
424 {
425 printf("# %s: '", prefix);
426 print_one_char(ch, '\'');
427 fputs("'\n", stdout);
428 }
429
430 int check_char_loc(const char *loc, const char *check, int ok, char a, char b)
431 {
432 int ret = test_assert(loc, check, ok);
433
434 if (!ret) {
435 fflush(stderr);
436 print_char(" left", a);
437 print_char(" right", b);
438 fflush(stdout);
439 }
440
441 return ret;
442 }
443
444 static void print_str(const char *prefix, const char *str)
445 {
446 printf("# %s: ", prefix);
447 if (!str) {
448 fputs("NULL\n", stdout);
449 } else {
450 putc('"', stdout);
451 while (*str)
452 print_one_char(*str++, '"');
453 fputs("\"\n", stdout);
454 }
455 }
456
457 int check_str_loc(const char *loc, const char *check,
458 const char *a, const char *b)
459 {
460 int ok = (!a && !b) || (a && b && !strcmp(a, b));
461 int ret = test_assert(loc, check, ok);
462
463 if (!ret) {
464 fflush(stderr);
465 print_str(" left", a);
466 print_str(" right", b);
467 fflush(stdout);
468 }
469
470 return ret;
471 }