From 4e2f094d29ff8140f2b46a059128c780560db0f1 Mon Sep 17 00:00:00 2001 From: Tavian Barnes Date: Thu, 29 Feb 2024 13:23:02 -0500 Subject: tests: New bfs_check() macro We now report failures and continue, rather than aborting after the first failure. --- tests/alloc.c | 22 +++--- tests/bfstd.c | 65 +++++++++-------- tests/bit.c | 108 ++++++++++++++-------------- tests/tests.h | 20 ++++++ tests/trie.c | 46 ++++++------ tests/xtime.c | 220 ++++++++++++++++++++++++++-------------------------------- 6 files changed, 247 insertions(+), 234 deletions(-) diff --git a/tests/alloc.c b/tests/alloc.c index e14f131..4ce23d4 100644 --- a/tests/alloc.c +++ b/tests/alloc.c @@ -9,30 +9,32 @@ #include bool check_alloc(void) { + bool ret = true; + // Check sizeof_flex() struct flexible { alignas(64) int foo[8]; int bar[]; }; - bfs_verify(sizeof_flex(struct flexible, bar, 0) >= sizeof(struct flexible)); - bfs_verify(sizeof_flex(struct flexible, bar, 16) % alignof(struct flexible) == 0); + ret &= bfs_check(sizeof_flex(struct flexible, bar, 0) >= sizeof(struct flexible)); + ret &= bfs_check(sizeof_flex(struct flexible, bar, 16) % alignof(struct flexible) == 0); size_t too_many = SIZE_MAX / sizeof(int) + 1; - bfs_verify(sizeof_flex(struct flexible, bar, too_many) == align_floor(alignof(struct flexible), SIZE_MAX)); + ret &= bfs_check(sizeof_flex(struct flexible, bar, too_many) == align_floor(alignof(struct flexible), SIZE_MAX)); // Corner case: sizeof(type) > align_ceil(alignof(type), offsetof(type, member)) // Doesn't happen in typical ABIs - bfs_verify(flex_size(8, 16, 4, 4, 1) == 16); + ret &= bfs_check(flex_size(8, 16, 4, 4, 1) == 16); // Make sure we detect allocation size overflows #if __GNUC__ && !__clang__ # pragma GCC diagnostic ignored "-Walloc-size-larger-than=" #endif - bfs_verify(ALLOC_ARRAY(int, too_many) == NULL && errno == EOVERFLOW); - bfs_verify(ZALLOC_ARRAY(int, too_many) == NULL && errno == EOVERFLOW); - bfs_verify(ALLOC_FLEX(struct flexible, bar, too_many) == NULL && errno == EOVERFLOW); - bfs_verify(ZALLOC_FLEX(struct flexible, bar, too_many) == NULL && errno == EOVERFLOW); + ret &= bfs_check(ALLOC_ARRAY(int, too_many) == NULL && errno == EOVERFLOW); + ret &= bfs_check(ZALLOC_ARRAY(int, too_many) == NULL && errno == EOVERFLOW); + ret &= bfs_check(ALLOC_FLEX(struct flexible, bar, too_many) == NULL && errno == EOVERFLOW); + ret &= bfs_check(ZALLOC_FLEX(struct flexible, bar, too_many) == NULL && errno == EOVERFLOW); // varena tests struct varena varena; @@ -41,9 +43,9 @@ bool check_alloc(void) { for (size_t i = 0; i < 256; ++i) { bfs_verify(varena_alloc(&varena, i)); struct arena *arena = &varena.arenas[varena.narenas - 1]; - bfs_verify(arena->size >= sizeof_flex(struct flexible, bar, i)); + ret &= bfs_check(arena->size >= sizeof_flex(struct flexible, bar, i)); } varena_destroy(&varena); - return true; + return ret; } diff --git a/tests/bfstd.c b/tests/bfstd.c index d385c6b..4d0ec23 100644 --- a/tests/bfstd.c +++ b/tests/bfstd.c @@ -14,61 +14,68 @@ #include /** Check the result of xdirname()/xbasename(). */ -static void check_base_dir(const char *path, const char *dir, const char *base) { +static bool check_base_dir(const char *path, const char *dir, const char *base) { + bool ret = true; + char *xdir = xdirname(path); bfs_verify(xdir, "xdirname(): %s", xstrerror(errno)); - bfs_verify(strcmp(xdir, dir) == 0, "xdirname('%s') == '%s' (!= '%s')", path, xdir, dir); + ret &= bfs_check(strcmp(xdir, dir) == 0, "xdirname('%s') == '%s' (!= '%s')", path, xdir, dir); free(xdir); char *xbase = xbasename(path); bfs_verify(xbase, "xbasename(): %s", xstrerror(errno)); - bfs_verify(strcmp(xbase, base) == 0, "xbasename('%s') == '%s' (!= '%s')", path, xbase, base); + ret &= bfs_check(strcmp(xbase, base) == 0, "xbasename('%s') == '%s' (!= '%s')", path, xbase, base); free(xbase); + + return ret; } /** Check the result of wordesc(). */ -static void check_wordesc(const char *str, const char *exp, enum wesc_flags flags) { +static bool check_wordesc(const char *str, const char *exp, enum wesc_flags flags) { char buf[256]; char *end = buf + sizeof(buf); - char *ret = wordesc(buf, end, str, flags); - bfs_verify(ret != end); - bfs_verify(strcmp(buf, exp) == 0, "wordesc(%s) == %s (!= %s)", str, buf, exp); + char *esc = wordesc(buf, end, str, flags); + + return bfs_check(esc != end) + && bfs_check(strcmp(buf, exp) == 0, "wordesc('%s') == '%s' (!= '%s')", str, buf, exp); } bool check_bfstd(void) { + bool ret = true; + // Try to set a UTF-8 locale if (!setlocale(LC_ALL, "C.UTF-8")) { setlocale(LC_ALL, ""); } // From man 3p basename - check_base_dir("usr", ".", "usr"); - check_base_dir("usr/", ".", "usr"); - check_base_dir("", ".", "."); - check_base_dir("/", "/", "/"); + ret &= check_base_dir("usr", ".", "usr"); + ret &= check_base_dir("usr/", ".", "usr"); + ret &= check_base_dir("", ".", "."); + ret &= check_base_dir("/", "/", "/"); // check_base_dir("//", "/" or "//", "/" or "//"); - check_base_dir("///", "/", "/"); - check_base_dir("/usr/", "/", "usr"); - check_base_dir("/usr/lib", "/usr", "lib"); - check_base_dir("//usr//lib//", "//usr", "lib"); - check_base_dir("/home//dwc//test", "/home//dwc", "test"); + ret &= check_base_dir("///", "/", "/"); + ret &= check_base_dir("/usr/", "/", "usr"); + ret &= check_base_dir("/usr/lib", "/usr", "lib"); + ret &= check_base_dir("//usr//lib//", "//usr", "lib"); + ret &= check_base_dir("/home//dwc//test", "/home//dwc", "test"); - check_wordesc("", "\"\"", WESC_SHELL); - check_wordesc("word", "word", WESC_SHELL); - check_wordesc("two words", "\"two words\"", WESC_SHELL); - check_wordesc("word's", "\"word's\"", WESC_SHELL); - check_wordesc("\"word\"", "'\"word\"'", WESC_SHELL); - check_wordesc("\"word's\"", "'\"word'\\''s\"'", WESC_SHELL); - check_wordesc("\033[1mbold's\033[0m", "$'\\e[1mbold\\'s\\e[0m'", WESC_SHELL | WESC_TTY); - check_wordesc("\x7F", "$'\\x7F'", WESC_SHELL | WESC_TTY); + ret &= check_wordesc("", "\"\"", WESC_SHELL); + ret &= check_wordesc("word", "word", WESC_SHELL); + ret &= check_wordesc("two words", "\"two words\"", WESC_SHELL); + ret &= check_wordesc("word's", "\"word's\"", WESC_SHELL); + ret &= check_wordesc("\"word\"", "'\"word\"'", WESC_SHELL); + ret &= check_wordesc("\"word's\"", "'\"word'\\''s\"'", WESC_SHELL); + ret &= check_wordesc("\033[1mbold's\033[0m", "$'\\e[1mbold\\'s\\e[0m'", WESC_SHELL | WESC_TTY); + ret &= check_wordesc("\x7F", "$'\\x7F'", WESC_SHELL | WESC_TTY); const char *charmap = nl_langinfo(CODESET); if (strcmp(charmap, "UTF-8") == 0) { - check_wordesc("\xF0", "$'\\xF0'", WESC_SHELL | WESC_TTY); - check_wordesc("\xF0\x9F", "$'\\xF0\\x9F'", WESC_SHELL | WESC_TTY); - check_wordesc("\xF0\x9F\x98", "$'\\xF0\\x9F\\x98'", WESC_SHELL | WESC_TTY); - check_wordesc("\xF0\x9F\x98\x80", "\xF0\x9F\x98\x80", WESC_SHELL | WESC_TTY); + ret &= check_wordesc("\xF0", "$'\\xF0'", WESC_SHELL | WESC_TTY); + ret &= check_wordesc("\xF0\x9F", "$'\\xF0\\x9F'", WESC_SHELL | WESC_TTY); + ret &= check_wordesc("\xF0\x9F\x98", "$'\\xF0\\x9F\\x98'", WESC_SHELL | WESC_TTY); + ret &= check_wordesc("\xF0\x9F\x98\x80", "\xF0\x9F\x98\x80", WESC_SHELL | WESC_TTY); } - return true; + return ret; } diff --git a/tests/bit.c b/tests/bit.c index 1c6a4de..b944748 100644 --- a/tests/bit.c +++ b/tests/bit.c @@ -52,75 +52,77 @@ bfs_static_assert(UINTMAX_MAX == UWIDTH_MAX(UINTMAX_WIDTH)); bfs_static_assert(INTMAX_MIN == IWIDTH_MIN(INTMAX_WIDTH)); bfs_static_assert(INTMAX_MAX == IWIDTH_MAX(INTMAX_WIDTH)); -#define verify_eq(a, b) \ - bfs_verify((a) == (b), "(0x%jX) %s != %s (0x%jX)", (uintmax_t)(a), #a, #b, (uintmax_t)(b)) +#define check_eq(a, b) \ + bfs_check((a) == (b), "(0x%jX) %s != %s (0x%jX)", (uintmax_t)(a), #a, #b, (uintmax_t)(b)) bool check_bit(void) { - verify_eq(bswap((uint8_t)0x12), 0x12); - verify_eq(bswap((uint16_t)0x1234), 0x3412); - verify_eq(bswap((uint32_t)0x12345678), 0x78563412); - verify_eq(bswap((uint64_t)0x1234567812345678), 0x7856341278563412); - - verify_eq(count_ones(0x0), 0); - verify_eq(count_ones(0x1), 1); - verify_eq(count_ones(0x2), 1); - verify_eq(count_ones(0x3), 2); - verify_eq(count_ones(0x137F), 10); - - verify_eq(count_zeros(0), INT_WIDTH); - verify_eq(count_zeros(0L), LONG_WIDTH); - verify_eq(count_zeros(0LL), LLONG_WIDTH); - verify_eq(count_zeros((uint8_t)0), 8); - verify_eq(count_zeros((uint16_t)0), 16); - verify_eq(count_zeros((uint32_t)0), 32); - verify_eq(count_zeros((uint64_t)0), 64); - - verify_eq(rotate_left((uint8_t)0xA1, 4), 0x1A); - verify_eq(rotate_left((uint16_t)0x1234, 12), 0x4123); - verify_eq(rotate_left((uint32_t)0x12345678, 20), 0x67812345); - verify_eq(rotate_left((uint32_t)0x12345678, 0), 0x12345678); - - verify_eq(rotate_right((uint8_t)0xA1, 4), 0x1A); - verify_eq(rotate_right((uint16_t)0x1234, 12), 0x2341); - verify_eq(rotate_right((uint32_t)0x12345678, 20), 0x45678123); - verify_eq(rotate_right((uint32_t)0x12345678, 0), 0x12345678); + bool ret = true; + + ret &= check_eq(bswap((uint8_t)0x12), 0x12); + ret &= check_eq(bswap((uint16_t)0x1234), 0x3412); + ret &= check_eq(bswap((uint32_t)0x12345678), 0x78563412); + ret &= check_eq(bswap((uint64_t)0x1234567812345678), 0x7856341278563412); + + ret &= check_eq(count_ones(0x0), 0); + ret &= check_eq(count_ones(0x1), 1); + ret &= check_eq(count_ones(0x2), 1); + ret &= check_eq(count_ones(0x3), 2); + ret &= check_eq(count_ones(0x137F), 10); + + ret &= check_eq(count_zeros(0), INT_WIDTH); + ret &= check_eq(count_zeros(0L), LONG_WIDTH); + ret &= check_eq(count_zeros(0LL), LLONG_WIDTH); + ret &= check_eq(count_zeros((uint8_t)0), 8); + ret &= check_eq(count_zeros((uint16_t)0), 16); + ret &= check_eq(count_zeros((uint32_t)0), 32); + ret &= check_eq(count_zeros((uint64_t)0), 64); + + ret &= check_eq(rotate_left((uint8_t)0xA1, 4), 0x1A); + ret &= check_eq(rotate_left((uint16_t)0x1234, 12), 0x4123); + ret &= check_eq(rotate_left((uint32_t)0x12345678, 20), 0x67812345); + ret &= check_eq(rotate_left((uint32_t)0x12345678, 0), 0x12345678); + + ret &= check_eq(rotate_right((uint8_t)0xA1, 4), 0x1A); + ret &= check_eq(rotate_right((uint16_t)0x1234, 12), 0x2341); + ret &= check_eq(rotate_right((uint32_t)0x12345678, 20), 0x45678123); + ret &= check_eq(rotate_right((uint32_t)0x12345678, 0), 0x12345678); for (int i = 0; i < 16; ++i) { uint16_t n = (uint16_t)1 << i; for (int j = i; j < 16; ++j) { uint16_t m = (uint16_t)1 << j; uint16_t nm = n | m; - verify_eq(count_ones(nm), 1 + (n != m)); - verify_eq(count_zeros(nm), 15 - (n != m)); - verify_eq(leading_zeros(nm), 15 - j); - verify_eq(trailing_zeros(nm), i); - verify_eq(first_leading_one(nm), j + 1); - verify_eq(first_trailing_one(nm), i + 1); - verify_eq(bit_width(nm), j + 1); - verify_eq(bit_floor(nm), m); + ret &= check_eq(count_ones(nm), 1 + (n != m)); + ret &= check_eq(count_zeros(nm), 15 - (n != m)); + ret &= check_eq(leading_zeros(nm), 15 - j); + ret &= check_eq(trailing_zeros(nm), i); + ret &= check_eq(first_leading_one(nm), j + 1); + ret &= check_eq(first_trailing_one(nm), i + 1); + ret &= check_eq(bit_width(nm), j + 1); + ret &= check_eq(bit_floor(nm), m); if (n == m) { - verify_eq(bit_ceil(nm), m); - bfs_verify(has_single_bit(nm)); + ret &= check_eq(bit_ceil(nm), m); + ret &= bfs_check(has_single_bit(nm)); } else { if (j < 15) { - verify_eq(bit_ceil(nm), (m << 1)); + ret &= check_eq(bit_ceil(nm), (m << 1)); } - bfs_verify(!has_single_bit(nm)); + ret &= bfs_check(!has_single_bit(nm)); } } } - verify_eq(leading_zeros((uint16_t)0), 16); - verify_eq(trailing_zeros((uint16_t)0), 16); - verify_eq(first_leading_one(0), 0); - verify_eq(first_trailing_one(0), 0); - verify_eq(bit_width(0), 0); - verify_eq(bit_floor(0), 0); - verify_eq(bit_ceil(0), 1); + ret &= check_eq(leading_zeros((uint16_t)0), 16); + ret &= check_eq(trailing_zeros((uint16_t)0), 16); + ret &= check_eq(first_leading_one(0), 0); + ret &= check_eq(first_trailing_one(0), 0); + ret &= check_eq(bit_width(0), 0); + ret &= check_eq(bit_floor(0), 0); + ret &= check_eq(bit_ceil(0), 1); - bfs_verify(!has_single_bit(0)); - bfs_verify(!has_single_bit(UINT32_MAX)); - bfs_verify(has_single_bit((uint32_t)1 << (UINT_WIDTH - 1))); + ret &= bfs_check(!has_single_bit(0)); + ret &= bfs_check(!has_single_bit(UINT32_MAX)); + ret &= bfs_check(has_single_bit((uint32_t)1 << (UINT_WIDTH - 1))); - return true; + return ret; } diff --git a/tests/tests.h b/tests/tests.h index d8adbb9..6629dcf 100644 --- a/tests/tests.h +++ b/tests/tests.h @@ -9,6 +9,7 @@ #define BFS_TESTS_H #include "../src/config.h" +#include "../src/diag.h" /** Unit test function type. */ typedef bool test_fn(void); @@ -31,4 +32,23 @@ bool check_trie(void); /** Time tests. */ bool check_xtime(void); +/** Don't ignore the bfs_check() return value. */ +attr(nodiscard) +static inline bool bfs_check(bool ret) { + return ret; +} + +/** + * Check a condition, logging a message on failure but continuing. + */ +#define bfs_check(...) \ + bfs_check(bfs_check_(#__VA_ARGS__, __VA_ARGS__, "", "")) + +#define bfs_check_(str, cond, format, ...) \ + ((cond) ? true : (bfs_diag( \ + sizeof(format) > 1 \ + ? "%.0s" format "%s%s" \ + : "Check failed: `%s`%s", \ + str, __VA_ARGS__), false)) + #endif // BFS_TESTS_H diff --git a/tests/trie.c b/tests/trie.c index f94d7c8..fec0de2 100644 --- a/tests/trie.c +++ b/tests/trie.c @@ -40,11 +40,13 @@ const char *keys[] = { const size_t nkeys = countof(keys); bool check_trie(void) { + bool ret = true; + struct trie trie; trie_init(&trie); for (size_t i = 0; i < nkeys; ++i) { - bfs_verify(!trie_find_str(&trie, keys[i])); + ret &= bfs_check(!trie_find_str(&trie, keys[i])); const char *prefix = NULL; for (size_t j = 0; j < i; ++j) { @@ -58,37 +60,37 @@ bool check_trie(void) { struct trie_leaf *leaf = trie_find_prefix(&trie, keys[i]); if (prefix) { bfs_verify(leaf); - bfs_verify(strcmp(prefix, leaf->key) == 0); + ret &= bfs_check(strcmp(prefix, leaf->key) == 0); } else { - bfs_verify(!leaf); + ret &= bfs_check(!leaf); } leaf = trie_insert_str(&trie, keys[i]); bfs_verify(leaf); - bfs_verify(strcmp(keys[i], leaf->key) == 0); - bfs_verify(leaf->length == strlen(keys[i]) + 1); + ret &= bfs_check(strcmp(keys[i], leaf->key) == 0); + ret &= bfs_check(leaf->length == strlen(keys[i]) + 1); } { size_t i = 0; for_trie (leaf, &trie) { - bfs_verify(leaf == trie_find_str(&trie, keys[i])); - bfs_verify(!leaf->prev || leaf->prev->next == leaf); - bfs_verify(!leaf->next || leaf->next->prev == leaf); + ret &= bfs_check(leaf == trie_find_str(&trie, keys[i])); + ret &= bfs_check(!leaf->prev || leaf->prev->next == leaf); + ret &= bfs_check(!leaf->next || leaf->next->prev == leaf); ++i; } - bfs_verify(i == nkeys); + ret &= bfs_check(i == nkeys); } for (size_t i = 0; i < nkeys; ++i) { struct trie_leaf *leaf = trie_find_str(&trie, keys[i]); bfs_verify(leaf); - bfs_verify(strcmp(keys[i], leaf->key) == 0); - bfs_verify(leaf->length == strlen(keys[i]) + 1); + ret &= bfs_check(strcmp(keys[i], leaf->key) == 0); + ret &= bfs_check(leaf->length == strlen(keys[i]) + 1); trie_remove(&trie, leaf); leaf = trie_find_str(&trie, keys[i]); - bfs_verify(!leaf); + ret &= bfs_check(!leaf); const char *postfix = NULL; for (size_t j = i + 1; j < nkeys; ++j) { @@ -102,14 +104,14 @@ bool check_trie(void) { leaf = trie_find_postfix(&trie, keys[i]); if (postfix) { bfs_verify(leaf); - bfs_verify(strcmp(postfix, leaf->key) == 0); + ret &= bfs_check(strcmp(postfix, leaf->key) == 0); } else { - bfs_verify(!leaf); + ret &= bfs_check(!leaf); } } for_trie (leaf, &trie) { - bfs_verify(false); + ret &= bfs_check(false, "trie should be empty"); } // This tests the "jump" node handling on 32-bit platforms @@ -118,18 +120,18 @@ bool check_trie(void) { bfs_verify(longstr); memset(longstr, 0xAC, longsize); - bfs_verify(!trie_find_mem(&trie, longstr, longsize)); - bfs_verify(trie_insert_mem(&trie, longstr, longsize)); + ret &= bfs_check(!trie_find_mem(&trie, longstr, longsize)); + ret &= bfs_check(trie_insert_mem(&trie, longstr, longsize)); memset(longstr + longsize / 2, 0xAB, longsize / 2); - bfs_verify(!trie_find_mem(&trie, longstr, longsize)); - bfs_verify(trie_insert_mem(&trie, longstr, longsize)); + ret &= bfs_check(!trie_find_mem(&trie, longstr, longsize)); + ret &= bfs_check(trie_insert_mem(&trie, longstr, longsize)); memset(longstr, 0xAA, longsize / 2); - bfs_verify(!trie_find_mem(&trie, longstr, longsize)); - bfs_verify(trie_insert_mem(&trie, longstr, longsize)); + ret &= bfs_check(!trie_find_mem(&trie, longstr, longsize)); + ret &= bfs_check(trie_insert_mem(&trie, longstr, longsize)); free(longstr); trie_destroy(&trie); - return true; + return ret; } diff --git a/tests/xtime.c b/tests/xtime.c index 4a7d55c..f853428 100644 --- a/tests/xtime.c +++ b/tests/xtime.c @@ -12,121 +12,122 @@ #include static bool tm_equal(const struct tm *tma, const struct tm *tmb) { - if (tma->tm_year != tmb->tm_year) { - return false; - } - if (tma->tm_mon != tmb->tm_mon) { - return false; - } - if (tma->tm_mday != tmb->tm_mday) { - return false; - } - if (tma->tm_hour != tmb->tm_hour) { - return false; - } - if (tma->tm_min != tmb->tm_min) { - return false; - } - if (tma->tm_sec != tmb->tm_sec) { - return false; - } - if (tma->tm_wday != tmb->tm_wday) { - return false; - } - if (tma->tm_yday != tmb->tm_yday) { - return false; - } - if (tma->tm_isdst != tmb->tm_isdst) { - return false; - } - - return true; -} - -static void tm_print(FILE *file, const struct tm *tm) { - fprintf(file, "Y%d M%d D%d h%d m%d s%d wd%d yd%d%s\n", - tm->tm_year, tm->tm_mon, tm->tm_mday, - tm->tm_hour, tm->tm_min, tm->tm_sec, - tm->tm_wday, tm->tm_yday, - tm->tm_isdst ? (tm->tm_isdst < 0 ? " (DST?)" : " (DST)") : ""); + return tma->tm_year == tmb->tm_year + && tma->tm_mon == tmb->tm_mon + && tma->tm_mday == tmb->tm_mday + && tma->tm_hour == tmb->tm_hour + && tma->tm_min == tmb->tm_min + && tma->tm_sec == tmb->tm_sec + && tma->tm_wday == tmb->tm_wday + && tma->tm_yday == tmb->tm_yday + && tma->tm_isdst == tmb->tm_isdst; } /** Check one xgetdate() result. */ -static bool compare_xgetdate(const char *str, int error, time_t expected) { +static bool check_one_xgetdate(const char *str, int error, time_t expected) { struct timespec ts; int ret = xgetdate(str, &ts); if (error) { - if (ret != -1 || errno != error) { - fprintf(stderr, "xgetdate('%s'): %s\n", str, xstrerror(errno)); - return false; - } - } else if (ret != 0) { - fprintf(stderr, "xgetdate('%s'): %s\n", str, xstrerror(errno)); - return false; - } else if (ts.tv_sec != expected || ts.tv_nsec != 0) { - fprintf(stderr, "xgetdate('%s'): %jd.%09jd != %jd\n", str, (intmax_t)ts.tv_sec, (intmax_t)ts.tv_nsec, (intmax_t)expected); - return false; + return bfs_check(ret == -1 && errno == error, "xgetdate('%s'): %s", str, xstrerror(errno)); + } else { + return bfs_check(ret == 0, "xgetdate('%s'): %s", str, xstrerror(errno)) + && bfs_check(ts.tv_sec == expected && ts.tv_nsec == 0, + "xgetdate('%s'): %jd.%09jd != %jd", + str, (intmax_t)ts.tv_sec, (intmax_t)ts.tv_nsec, (intmax_t)expected); } - - return true; } /** xgetdate() tests. */ static bool check_xgetdate(void) { - return compare_xgetdate("", EINVAL, 0) - & compare_xgetdate("????", EINVAL, 0) - & compare_xgetdate("1991", EINVAL, 0) - & compare_xgetdate("1991-??", EINVAL, 0) - & compare_xgetdate("1991-12", EINVAL, 0) - & compare_xgetdate("1991-12-", EINVAL, 0) - & compare_xgetdate("1991-12-??", EINVAL, 0) - & compare_xgetdate("1991-12-14", 0, 692668800) - & compare_xgetdate("1991-12-14-", EINVAL, 0) - & compare_xgetdate("1991-12-14T", EINVAL, 0) - & compare_xgetdate("1991-12-14T??", EINVAL, 0) - & compare_xgetdate("1991-12-14T10", 0, 692704800) - & compare_xgetdate("1991-12-14T10:??", EINVAL, 0) - & compare_xgetdate("1991-12-14T10:11", 0, 692705460) - & compare_xgetdate("1991-12-14T10:11:??", EINVAL, 0) - & compare_xgetdate("1991-12-14T10:11:12", 0, 692705472) - & compare_xgetdate("1991-12-14T10Z", 0, 692704800) - & compare_xgetdate("1991-12-14T10:11Z", 0, 692705460) - & compare_xgetdate("1991-12-14T10:11:12Z", 0, 692705472) - & compare_xgetdate("1991-12-14T10:11:12?", EINVAL, 0) - & compare_xgetdate("1991-12-14T03-07", 0, 692704800) - & compare_xgetdate("1991-12-14T06:41-03:30", 0, 692705460) - & compare_xgetdate("1991-12-14T03:11:12-07:00", 0, 692705472) - & compare_xgetdate("19911214 031112-0700", 0, 692705472); + bool ret = true; + + ret &= check_one_xgetdate("", EINVAL, 0); + ret &= check_one_xgetdate("????", EINVAL, 0); + ret &= check_one_xgetdate("1991", EINVAL, 0); + ret &= check_one_xgetdate("1991-??", EINVAL, 0); + ret &= check_one_xgetdate("1991-12", EINVAL, 0); + ret &= check_one_xgetdate("1991-12-", EINVAL, 0); + ret &= check_one_xgetdate("1991-12-??", EINVAL, 0); + ret &= check_one_xgetdate("1991-12-14", 0, 692668800); + ret &= check_one_xgetdate("1991-12-14-", EINVAL, 0); + ret &= check_one_xgetdate("1991-12-14T", EINVAL, 0); + ret &= check_one_xgetdate("1991-12-14T??", EINVAL, 0); + ret &= check_one_xgetdate("1991-12-14T10", 0, 692704800); + ret &= check_one_xgetdate("1991-12-14T10:??", EINVAL, 0); + ret &= check_one_xgetdate("1991-12-14T10:11", 0, 692705460); + ret &= check_one_xgetdate("1991-12-14T10:11:??", EINVAL, 0); + ret &= check_one_xgetdate("1991-12-14T10:11:12", 0, 692705472); + ret &= check_one_xgetdate("1991-12-14T10Z", 0, 692704800); + ret &= check_one_xgetdate("1991-12-14T10:11Z", 0, 692705460); + ret &= check_one_xgetdate("1991-12-14T10:11:12Z", 0, 692705472); + ret &= check_one_xgetdate("1991-12-14T10:11:12?", EINVAL, 0); + ret &= check_one_xgetdate("1991-12-14T03-07", 0, 692704800); + ret &= check_one_xgetdate("1991-12-14T06:41-03:30", 0, 692705460); + ret &= check_one_xgetdate("1991-12-14T03:11:12-07:00", 0, 692705472); + ret &= check_one_xgetdate("19911214 031112-0700", 0, 692705472);; + + return ret; +} + +#define TM_FORMAT "%04d-%02d-%02d %02d:%02d:%02d (%d/7, %d/365%s)" + +#define TM_PRINTF(tm) \ + (1900 + (tm).tm_year), (tm).tm_mon, (tm).tm_mday, \ + (tm).tm_hour, (tm).tm_min, (tm).tm_sec, \ + ((tm).tm_wday + 1), ((tm).tm_yday + 1), \ + ((tm).tm_isdst ? ((tm).tm_isdst < 0 ? ", DST?" : ", DST") : "") + +/** Check one xmktime() result. */ +static bool check_one_xmktime(time_t expected) { + struct tm tm; + if (xlocaltime(&expected, &tm) != 0) { + bfs_diag("xlocaltime(%jd): %s", (intmax_t)expected, xstrerror(errno)); + return false; + } + + time_t actual; + return bfs_check(xmktime(&tm, &actual) == 0, "xmktime(" TM_FORMAT "): %s", TM_PRINTF(tm), xstrerror(errno)) + && bfs_check(actual == expected, "xmktime(" TM_FORMAT "): %jd != %jd", TM_PRINTF(tm), (intmax_t)actual, (intmax_t)expected); } /** xmktime() tests. */ static bool check_xmktime(void) { + bool ret = true; + for (time_t time = -10; time <= 10; ++time) { - struct tm tm; - if (xlocaltime(&time, &tm) != 0) { - perror("xlocaltime()"); - return false; - } - - time_t made; - if (xmktime(&tm, &made) != 0) { - perror("xmktime()"); - return false; - } - - if (time != made) { - fprintf(stderr, "Mismatch: %jd != %jd\n", (intmax_t)time, (intmax_t)made); - tm_print(stderr, &tm); - return false; - } + ret &= check_one_xmktime(time); + } + + return ret; +} + +/** Check one xtimegm() result. */ +static bool check_one_xtimegm(const struct tm *tm) { + struct tm tma = *tm, tmb = *tm; + time_t ta, tb; + ta = mktime(&tma); + if (xtimegm(&tmb, &tb) != 0) { + tb = -1; } - return true; + bool ret = true; + ret &= bfs_check(ta == tb, "%jd != %jd", (intmax_t)ta, (intmax_t)tb); + ret &= bfs_check(ta == -1 || tm_equal(&tma, &tmb)); + + if (!ret) { + bfs_diag("mktime(): " TM_FORMAT, TM_PRINTF(tma)); + bfs_diag("xtimegm(): " TM_FORMAT, TM_PRINTF(tmb)); + bfs_diag("(input): " TM_FORMAT, TM_PRINTF(*tm)); + } + + return ret; } /** xtimegm() tests. */ static bool check_xtimegm(void) { + bool ret = true; + struct tm tm = { .tm_isdst = -1, }; @@ -137,33 +138,10 @@ static bool check_xtimegm(void) { for (tm.tm_hour = -1; tm.tm_hour <= 24; tm.tm_hour += 5) for (tm.tm_min = -1; tm.tm_min <= 60; tm.tm_min += 31) for (tm.tm_sec = -60; tm.tm_sec <= 120; tm.tm_sec += 5) { - struct tm tma = tm, tmb = tm; - time_t ta, tb; - ta = mktime(&tma); - if (xtimegm(&tmb, &tb) != 0) { - tb = -1; - } - - bool fail = false; - if (ta != tb) { - printf("Mismatch: %jd != %jd\n", (intmax_t)ta, (intmax_t)tb); - fail = true; - } - if (ta != -1 && !tm_equal(&tma, &tmb)) { - printf("mktime(): "); - tm_print(stdout, &tma); - printf("xtimegm(): "); - tm_print(stdout, &tmb); - fail = true; - } - if (fail) { - printf("Input: "); - tm_print(stdout, &tm); - return false; - } + ret &= check_one_xtimegm(&tm); } - return true; + return ret; } bool check_xtime(void) { @@ -173,7 +151,9 @@ bool check_xtime(void) { } tzset(); - return check_xgetdate() - & check_xmktime() - & check_xtimegm(); + bool ret = true; + ret &= check_xgetdate(); + ret &= check_xmktime(); + ret &= check_xtimegm(); + return ret; } -- cgit v1.2.3