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