| 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 | } |