Git fork
1#define DISABLE_SIGN_COMPARE_WARNINGS
2
3#include "test-lib.h"
4
5enum result {
6 RESULT_NONE,
7 RESULT_FAILURE,
8 RESULT_SKIP,
9 RESULT_SUCCESS,
10 RESULT_TODO
11};
12
13static 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
36static 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
101static 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
111void 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
120void 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
130int 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
142void 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
155void 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
176void 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
197int 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
212static void print_description(const char *format, va_list ap)
213{
214 if (format) {
215 fputs(" - ", stdout);
216 vprintf(format, ap);
217 }
218}
219
220int 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
273static void test_fail(void)
274{
275 assert(ctx.result != RESULT_SKIP);
276
277 ctx.result = RESULT_FAILURE;
278}
279
280static 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
288static 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
296int 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
323void test__todo_begin(void)
324{
325 assert(ctx.running);
326 assert(!ctx.todo);
327
328 ctx.todo = 1;
329}
330
331int 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
350int check_bool_loc(const char *loc, const char *check, int ok)
351{
352 return test_assert(loc, check, ok);
353}
354
355union test__tmp test__tmp[2];
356
357int 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
370int 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
383int 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
396static void print_one_char(char ch, char quote)
397{
398 if ((unsigned char)ch < 0x20u || ch == 0x7f) {
399 /* TODO: improve handling of \a, \b, \f ... */
400 printf("\\%03o", (unsigned char)ch);
401 } else {
402 if (ch == '\\' || ch == quote)
403 putc('\\', stdout);
404 putc(ch, stdout);
405 }
406}
407
408static void print_char(const char *prefix, char ch)
409{
410 printf("# %s: '", prefix);
411 print_one_char(ch, '\'');
412 fputs("'\n", stdout);
413}
414
415int check_char_loc(const char *loc, const char *check, int ok, char a, char b)
416{
417 int ret = test_assert(loc, check, ok);
418
419 if (!ret) {
420 fflush(stderr);
421 print_char(" left", a);
422 print_char(" right", b);
423 fflush(stdout);
424 }
425
426 return ret;
427}
428
429static void print_str(const char *prefix, const char *str)
430{
431 printf("# %s: ", prefix);
432 if (!str) {
433 fputs("NULL\n", stdout);
434 } else {
435 putc('"', stdout);
436 while (*str)
437 print_one_char(*str++, '"');
438 fputs("\"\n", stdout);
439 }
440}
441
442int check_str_loc(const char *loc, const char *check,
443 const char *a, const char *b)
444{
445 int ok = (!a && !b) || (a && b && !strcmp(a, b));
446 int ret = test_assert(loc, check, ok);
447
448 if (!ret) {
449 fflush(stderr);
450 print_str(" left", a);
451 print_str(" right", b);
452 fflush(stdout);
453 }
454
455 return ret;
456}