Mercurial > hg > th-libs
view tests.c @ 789:d61d3eb29053 default tip
Bump copyright.
author | Matti Hamalainen <ccr@tnsp.org> |
---|---|
date | Fri, 08 Mar 2024 15:26:24 +0200 |
parents | c51f056f3557 |
children |
line wrap: on
line source
#include "th_types.h" #include "th_args.h" #include "th_util.h" #include "th_string.h" #include "th_crypto.h" #include "th_ioctx.h" #include "th_config.h" #include "th_file.h" #include "th_regex.h" #define SET_BUF_SIZE 128 #define SET_BUF_SIZE_2 ((SET_BUF_SIZE) + 32) #define SET_MAX_TESTS 64 #define SET_SENTINEL_BYTE 0x0e5 #define NCOUNT(xxx) (sizeof(xxx) / sizeof(xxx[0])) enum { TST_SUPERFLUOUS = 0x0001, TST_CORNERCASE = 0x0002, TST_OVERFLOW = 0x0004, TST_BROKEN = 0x1000, TST_ALL = 0xffff, }; typedef struct { char *header; bool shown, // Has test result value been shown? failed; // Did the test fail? (used by some tests to show extra info) } test_ctx; // Global variables int tests_failed, // Total number of tests failed tests_passed, // Total number of tests passed tests_total, // Total number of tests RUN sets_total, // Number of test sets sets_nenabled; // Number of test sets enabled int sets_enabled[SET_MAX_TESTS]; char buf1[SET_BUF_SIZE_2], buf2[SET_BUF_SIZE_2]; int optFlags = TST_ALL; th_ioctx_t testio; // Define option arguments static const th_optarg arg_opts[] = { { 0, '?', "help" , NULL, "Show this help", OPT_NONE }, { 1, 'v', "verbose" , NULL, "Be more verbose", OPT_NONE }, { 2, 's', "sets" , "sets", "Perform test sets -s <set>[,<set2>..]", OPT_ARGREQ }, { 3, 't', "tests" , "tests", "Perform only tests (see below)", OPT_ARGREQ }, }; static const int arg_nopts = sizeof(arg_opts) / sizeof(arg_opts[0]); bool tprintv(const int level, const char *fmt, va_list ap) { if (level <= th_verbosity) { vfprintf(stdout, fmt, ap); return true; } else return false; } bool tprint(const int level, const char *fmt, ...) { bool retv; va_list ap; va_start(ap, fmt); retv = tprintv(level, fmt, ap); va_end(ap); return retv; } void arg_show_help(void) { th_print_banner(stdout, th_prog_name, "[options]"); th_args_help(stdout, arg_opts, arg_nopts, 0, 80 - 2); } bool arg_handle_opt(const int optN, char *optArg, char *currArg) { switch (optN) { case 0: arg_show_help(); exit(0); break; case 1: th_verbosity++; break; case 2: { bool ret = true; char *pos, *pstr, *next; pos = pstr = th_strdup(optArg); memset(sets_enabled, 0, sizeof(sets_enabled)); do { if ((next = strchr(pos, ',')) != NULL) *next = 0; char *tmp = th_strdup_trim(pos, TH_TRIM_BOTH); if (tmp != NULL) { int val = atoi(tmp); if (val > 0 && val <= SET_MAX_TESTS) sets_enabled[val - 1] = 1; else { THERR("Invalid test number #%d, out of range [%d .. %d]\n", val, 1, SET_MAX_TESTS); ret = false; } th_free(tmp); } if (next != NULL) pos = next + 1; } while (next != NULL); th_free(pstr); return ret; } break; case 3: optFlags = atoi(optArg); break; default: THERR("Unknown option '%s'.\n", currArg); return false; } return true; } void test_end(test_ctx *ctx) { th_free_r(&ctx->header); } void test_start_v(test_ctx *ctx, const char *fmt, va_list ap) { tests_total++; memset(ctx, 0, sizeof(test_ctx)); ctx->header = th_strdup_vprintf(fmt, ap); } void test_start(test_ctx *ctx, const char *fmt, ...) { va_list ap; va_start(ap, fmt); test_start_v(ctx, fmt, ap); va_end(ap); } void test_result_msg_v(test_ctx *ctx, bool check, const char *fmt, va_list ap) { if (check) { if (!ctx->shown && tprint(2, "%s: OK\n", ctx->header)) ctx->shown = true; tests_passed++; } else { if (!ctx->shown && tprint(0, "%s: FAIL\n", ctx->header)) ctx->shown = true; if (fmt != NULL) { tprint(0, " - "); tprintv(0, fmt, ap); tprint(0, "\n"); } tests_failed++; ctx->failed = true; } } bool test_result_msg(test_ctx *ctx, bool check, const char *fmt, ...) { va_list ap; va_start(ap, fmt); test_result_msg_v(ctx, check, fmt, ap); va_end(ap); return check; } bool test_result(test_ctx *ctx, bool check) { test_result_msg_v(ctx, check, NULL, NULL); return check; } void test_snprintf_do(size_t len, const char *msg, const char *fmt, va_list ap) { int ret1, ret2; va_list tmp; test_ctx ctx; // Setup printf debug value th_printf_debug = th_verbosity >= 3; th_printf_debug_prefix = " - "; // Test basic *printf() functionality test_start(&ctx, "th_vsnprintf(buflen=%" PRIu_SIZE_T ", \"%s\", %s)", len, fmt, msg); memset(buf1, SET_SENTINEL_BYTE, SET_BUF_SIZE_2); buf1[SET_BUF_SIZE_2-1] = 0; memset(buf2, SET_SENTINEL_BYTE, SET_BUF_SIZE_2); buf2[SET_BUF_SIZE_2-1] = 0; va_copy(tmp, ap); ret1 = th_vsnprintf(buf1, len, fmt, tmp); va_copy(tmp, ap); ret2 = vsnprintf(buf2, len, fmt, tmp); test_result_msg(&ctx, ret1 == ret2, "retval mismatch %d [th] != %d [libc]", ret1, ret2); test_result_msg(&ctx, strcmp(buf1, buf2) == 0, "result mismatch '%s' [th] != '%s' [libc]", buf1, buf2); if (optFlags & TST_OVERFLOW) { test_result_msg(&ctx, (unsigned char) buf1[len] == SET_SENTINEL_BYTE, "buffer #1 overflow, sentinel 0x%02x", buf1[len]); test_result_msg(&ctx, (unsigned char) buf2[len] == SET_SENTINEL_BYTE, "buffer #2 overflow, sentinel 0x%02x", buf2[len]); } if (ctx.failed && !th_printf_debug && th_verbosity >= 1) { th_printf_debug = true; va_copy(tmp, ap); ret1 = th_vsnprintf(buf1, len, fmt, tmp); va_copy(tmp, ap); ret2 = vsnprintf(buf2, len, fmt, tmp); } test_end(&ctx); } void test_snprintf(const char *msg, const char *fmt, ...) { test_ctx ctx; va_list ap, tmp; va_start(ap, fmt); if (optFlags & TST_CORNERCASE) { va_copy(tmp, ap); test_snprintf_do(0, msg, fmt, tmp); va_copy(tmp, ap); test_snprintf_do(1, msg, fmt, tmp); va_copy(tmp, ap); test_snprintf_do(2, msg, fmt, tmp); va_copy(tmp, ap); test_snprintf_do(16, msg, fmt, tmp); } va_copy(tmp, ap); test_snprintf_do(SET_BUF_SIZE, msg, fmt, tmp); // Test th_strdup_vprintf() if (optFlags & TST_SUPERFLUOUS) { test_start(&ctx, "th_strdup_vprintf('%s')", fmt); va_copy(tmp, ap); char *str = th_strdup_vprintf(fmt, tmp); test_result_msg(&ctx, str != NULL, "result NULL"); th_free(str); test_end(&ctx); } va_end(ap); tprint(2, "-----------------------------------------------------\n"); } bool test_set_start(const char *str) { if (sets_enabled[sets_total++]) { sets_nenabled++; tprint(1, "======================================================\n" " Set #%d : %s tests\n" "======================================================\n", sets_total, str); return true; } else return false; } #define TEST_ASSERT(xcond) do { \ if (!(xcond)) \ { \ THERR("Assertion '" # xcond "' failed.\n"); \ return -1; \ } \ } while (0) #define TEST_1A(fun) do { \ test_ctx ctx; \ test_start(&ctx, # fun ); \ test_result(&ctx, fun); \ test_end(&ctx); \ } while (0) #define TEST_1B(fmt, fun, fcmp, fres) do { \ test_ctx ctx; \ test_start(&ctx, #fun " " #fcmp " " fmt " (" fmt ")", fres, fun); \ test_result(&ctx, fun fcmp fres ); \ test_end(&ctx); \ } while (0) #define TEST_2A(fun, str1, str2, ret) do { \ test_ctx ctx; \ test_start(&ctx, # fun "('%s', '%s')", str1, str2); \ test_result(&ctx, ( fun (str1, str2) == 0) == ret); \ test_end(&ctx); \ } while (0) #define TEST_2B(fun, str1, str2, ret) do { \ test_ctx ctx; \ test_start(&ctx, # fun "('%s', '%s')", str1, str2); \ test_result(&ctx, fun (str1, str2) == ret); \ test_end(&ctx); \ } while (0) #define TEST_2C(fun, str1, str2, ret) do { \ test_ctx ctx; \ test_start(&ctx, # fun "('%s', '%s')", str1, str2); \ test_result(&ctx, (fun (str1, str2) != NULL) == ret); \ test_end(&ctx); \ } while (0) #define TEST_3A(fun, str1, str2, len, ret) do { \ test_ctx ctx; \ test_start(&ctx, # fun "('%s', '%s', %d)", str1, str2, len); \ test_result(&ctx, ( fun (str1, str2, len) == 0) == ret); \ test_end(&ctx); \ } while (0) #define TEST_3B(fun, str1, str2, sbool, ret) do { \ test_ctx ctx; \ test_start(&ctx, # fun "('%s', '%s', %s) == %s", str1, str2, sbool ? "true" : "false", ret ? "true" : "false"); \ test_result(&ctx, fun (str1, str2, sbool) == ret); \ test_end(&ctx); \ } while (0) void test_config_values(th_cfgitem_t *cfg) { th_cfgitem_t *item; int nsubtest = 0; test_ctx ctx; test_start(&ctx, "Test configuration value search #%d", ++nsubtest); test_result(&ctx, (item = th_cfg_item_find(cfg, "inside_sect", "intval", -1)) != NULL && *item->v.val_int == 112); test_end(&ctx); test_start(&ctx, "Test configuration value search #%d", ++nsubtest); test_result(&ctx, (item = th_cfg_item_find(cfg, "another_sect", "boolval", -1)) != NULL && *item->v.val_bool); test_end(&ctx); test_start(&ctx, "Test configuration value search #%d", ++nsubtest); test_result(&ctx, th_cfg_item_find(cfg, "no_match", NULL, -1) == NULL); test_end(&ctx); test_start(&ctx, "Test configuration value search #%d", ++nsubtest); test_result(&ctx, th_cfg_item_find(cfg, NULL, "no_match", -1) == NULL); test_end(&ctx); test_start(&ctx, "Test configuration value search #%d", ++nsubtest); test_result(&ctx, (item = th_cfg_item_find(cfg, NULL, "hexval", -1)) != NULL && *item->v.val_uint == 0x11223344); test_end(&ctx); test_start(&ctx, "Test configuration value search #%d", ++nsubtest); test_result(&ctx, (item = th_cfg_item_find(cfg, NULL, "a_string_setting", -1)) != NULL && strcmp(*item->v.val_str, "v_str1") == 0); test_end(&ctx); } void test_config_free(th_cfgitem_t *node) { switch (node->type) { case CFG_ITEM_STRING: th_free(*(node->v.val_str)); break; case CFG_ITEM_STRING_LIST: break; } } void test_ioctx_error(th_ioctx_t *fh, const int val, const char *msg) { (void) fh; (void) val; tprint(0, "IOCTX ERROR: %s", msg); } void test_ioctx_msg(th_ioctx_t *fh, const int val, const char *msg) { (void) fh; (void) val; tprint(1, "IOCTX MSG: %s", msg); } int test_config_strcmp(const void *v1, const void *v2) { return strcmp((const char *) v1, (const char *) v2); } static const char *test_config_strings[] = { "zoo", "foo", "b\"ar", }; void test_config_read(th_cfgitem_t *cfg, const char *filename) { int nsubtest; th_ioctx_t *fh = NULL; test_ctx ctx; th_cfgitem_t *item; tprint(1, "Reading configuration from '%s'.\n", filename); // Attempt to read the previously written file if (th_io_fopen(&fh, &th_stdio_io_ops, filename, "r") != THERR_OK) { int err = th_get_error(); THERR("Could not open configuration file '%s', %d: %s\n", filename, err, th_error_str(err)); goto out; } th_io_set_handlers(fh, test_ioctx_error, test_ioctx_msg); th_cfg_read(fh, cfg); // Test read values against expected values test_config_values(cfg); // Additional tests test_start(&ctx, "Additional config tests"); item = th_cfg_item_find(cfg, NULL, "string_list", -1); test_result(&ctx, item != NULL); test_end(&ctx); if (item != NULL) { static const char *nostr = "not to be found"; th_llist_t **plist = (th_llist_t **) item->v.list; for (nsubtest = 0; nsubtest < (int) NCOUNT(test_config_strings); nsubtest++) { test_start(&ctx, "Test configuration string list values #%d: '%s'", nsubtest + 1, test_config_strings[nsubtest]); test_result(&ctx, th_llist_find_func(*plist, test_config_strings[nsubtest], test_config_strcmp) != NULL); test_end(&ctx); } test_start(&ctx, "Test configuration string list values #%d: '%s'", ++nsubtest, nostr); test_result(&ctx, th_llist_find_func(*plist, nostr, test_config_strcmp) == NULL); test_end(&ctx); th_llist_free_func_data(*plist, th_free); *plist = NULL; } out: th_io_close(fh); } void test_config_file(void) { static const char *filename = "cfg.temp"; test_ctx ctx; th_ioctx_t *fh = NULL; th_cfgitem_t *sect1, *sect2, *cfg = NULL, *item; char *v_str1 = NULL; unsigned int v_uint1; int v_int1; bool v_bool1, v_bool2; th_llist_t *v_str_list = NULL; // Create v_str_list for (size_t n = 0; n < NCOUNT(test_config_strings); n++) th_llist_append(&v_str_list, th_strdup(test_config_strings[n])); // Create the configuration structure tprint(2, "Creating configuration structure\n"); sect1 = NULL; th_cfg_add_comment(§1, "A comment that\nspans multiple\nlines automatically"); th_cfg_add_string(§1, "a_string_setting", &v_str1, "v_str1"); th_cfg_add_comment(§1, "Hex triplet value setting"); th_cfg_add_hex_triplet(§1, "hexval", &v_uint1, 0x11223344); th_cfg_add_comment(§1, "A boolean value"); th_cfg_add_bool(§1, "boolval", &v_bool1, false); th_cfg_add_comment(§1, "A string list"); th_cfg_add_string_list(§1, "string_list", &v_str_list); th_cfg_add_section(&cfg, "general", sect1); sect1 = NULL; th_cfg_add_comment(§1, "Another section"); th_cfg_add_bool(§1, "boolval", &v_bool2, true); sect2 = NULL; th_cfg_add_comment(§2, "Section inside a section"); th_cfg_add_int(§2, "intval", &v_int1, 112); th_cfg_add_section(§1, "inside_sect", sect2); th_cfg_add_section(&cfg, "another_sect", sect1); // Test ptr test_start(&ctx, "Test configuration string list ptr"); test_result(&ctx, (item = th_cfg_item_find(cfg, NULL, "string_list", -1)) != NULL && item->v.list == &v_str_list); test_end(&ctx); // Test value finding test_config_values(cfg); // Attempt to write the file if (th_io_fopen(&fh, &th_stdio_io_ops, filename, "w") != THERR_OK) { int err = th_get_error(); THERR("Could not create configuration to file '%s', %d: %s\n", filename, err, th_error_str(err)); goto out; } th_io_set_handlers(fh, test_ioctx_error, test_ioctx_msg); th_cfg_write(fh, cfg); th_io_close(fh); fh = NULL; // Test against written configuration file test_config_read(cfg, filename); // Test against manually edited configuration file test_config_read(cfg, "cfg.test01"); out: // Free the data for v_str_list th_io_close(fh); th_llist_free_func_data(v_str_list, th_free); th_cfg_free(cfg, test_config_free); } bool test_linked_list_length(th_llist_t *list, const size_t n) { test_ctx ctx; bool ok = th_llist_length(list) == n; test_start(&ctx, "Test list length matching"); test_result(&ctx, ok); test_end(&ctx); return ok; } bool test_linked_list_validity(th_llist_t *list, const size_t nstart, const ssize_t ndelta) { test_ctx ctx; th_llist_t *node; size_t n; bool ok = true; for (n = nstart, node = list; node != NULL; node = node->next, n += ndelta) { if ((size_t) node->data != n) { ok = false; break; } } test_start(&ctx, "Test list data contents"); test_result_msg(&ctx, ok, "failure at index #%d", n); test_end(&ctx); return ok; } int test_llist_sortfunc(const th_llist_t *lnode, const th_llist_t *rnode, void *userdata) { (void) userdata; return lnode->data <= rnode->data; } void test_linked_lists(void) { th_llist_t *list = NULL, *node; size_t nnodes = 128, n; tprint(2, "Creating linked list\n"); for (n = 0; n < nnodes; n++) { if ((node = th_llist_append(&list, (void *) n)) == NULL) { THERR("Error allocating memory.\n"); goto out; } } // Test test_linked_list_length(list, nnodes); test_linked_list_validity(list, 0, 1); // Reverse th_llist_reverse(&list); test_linked_list_length(list, nnodes); test_linked_list_validity(list, nnodes - 1, -1); // Sort th_llist_mergesort(&list, test_llist_sortfunc, NULL); // Test count test_linked_list_length(list, nnodes); test_linked_list_validity(list, 0, 1); // Prepend tprint(2, "Prepend node to list\n"); for (size_t k = 0; k < 16; k++) { if ((node = th_llist_prepend(&list, (void *) n)) == NULL) { THERR("Error allocating memory.\n"); goto out; } nnodes++; n++; } // Test count test_linked_list_length(list, nnodes); // Sort th_llist_mergesort(&list, test_llist_sortfunc, NULL); test_linked_list_validity(list, 0, 1); // Delete node = list; th_llist_delete_node(&list, node); test_linked_list_length(list, nnodes - 1); test_linked_list_validity(list, 1, 1); th_free(node); out: th_llist_free(list); } #ifdef TH_EXPERIMENTAL_REGEX typedef struct { const th_char_t *str; const int flags; const th_char_t *expected[4]; } test_regex_def1; typedef struct { const th_char_t *pattern; const th_char_t *str; const int flags; const th_char_t *expected[4]; } test_regex_def2; bool test_regex_list_matches(const th_char_t *str, const th_regex_match_t *matches, const th_char_t * const *expected, const bool testOnly) { size_t nmatch = 0; char *match = NULL; for (const th_regex_match_t *mt = matches; mt != NULL; mt = (th_regex_match_t *) mt->node.next, nmatch++) { match = th_strndup(str + mt->start, mt->len); if (expected[nmatch] == NULL) { if (!testOnly) { THERR("Expected[%" PRIu_SIZE_T "] == NULL, but match '%s' returned.\n", nmatch, match); } goto error; } else { bool seqMatch = strcmp(match, expected[nmatch]) == 0; if (testOnly && !seqMatch) goto error; if (th_verbosity >= 1 || !seqMatch) { tprint(0, " [%3" PRIu_SIZE_T " ++ %3" PRIu_SIZE_T "]: '%s' == '%s': %s\n", mt->start, mt->len, match, expected[nmatch], seqMatch ? "YES" : "NO!"); } } th_free(match); } return true; error: th_free(match); return false; } void test_regex_list1(const test_regex_def1 *list, const th_char_t *pattern) { th_regex_match_t *matches = NULL; th_regex_t *expr = NULL; int res; if ((res = th_regex_compile(&expr, pattern)) != THERR_OK) { THERR("Regex \"%s\" compilation failed: %s\n", pattern, th_error_str(res)); goto out; } tprint(1, "\n----------------------------------------\n" "\"%s\"\n", pattern); if (th_verbosity >= 2) th_regex_dump(&testio, 1, expr); for (const test_regex_def1 *def = list; def->str != NULL; def++) { size_t nmatches; bool matchOK; tprint(3, "\n----------------------------------------\n"); if ((res = th_regex_match(expr, def->str, &nmatches, &matches, -1, def->flags)) != THERR_OK) { THERR("Regex match returned error: %s\n", th_error_str(res)); goto out; } matchOK = test_regex_list_matches(def->str, matches, def->expected, true); if (th_verbosity < 1 && !matchOK) { tprint(0, "\n----------------------------------------\n" " \"%s\" vs \"%s\" failures:\n", def->str, pattern); } else { tprint(1, " \"%s\": matched %" PRIu_SIZE_T " time(s)\n", def->str, nmatches); } #ifdef TH_EXPERIMENTAL_REGEX_DEBUG if (!matchOK && th_dbg_fh == NULL) { th_dbg_fh = &testio; th_regex_match(expr, def->str, NULL, NULL, -1, def->flags); th_dbg_fh = NULL; } #endif test_regex_list_matches(def->str, matches, def->expected, false); th_regex_free_matches(matches); matches = NULL; } out: th_regex_free_matches(matches); th_regex_free(expr); } void test_regex_list2(const test_regex_def2 *list) { for (const test_regex_def2 *def = list; def->str != NULL; def++) { th_regex_t *expr = NULL; th_regex_match_t *matches = NULL; size_t nmatches; int res; if ((res = th_regex_compile(&expr, def->pattern)) != THERR_OK) { THERR("Regex \"%s\" compilation failed: %s\n", def->pattern, th_error_str(res)); goto out; } if (th_verbosity >= 2) th_regex_dump(&testio, 1, expr); tprint(3, "----------------------------------------\n"); if ((res = th_regex_match(expr, def->str, &nmatches, &matches, -1, def->flags)) != THERR_OK) { THERR("Regex match returned error: %s\n", th_error_str(res)); goto out; } tprint(1, "\"%s\" vs \"%s\": matched %" PRIu_SIZE_T " time(s)\n", def->pattern, def->str, nmatches); test_regex_list_matches(def->str, matches, def->expected, false); out: th_regex_free_matches(matches); th_regex_free(expr); } } #endif int main(int argc, char *argv[]) { size_t i1, i2, i3, i4; char buf[64], buf2[64]; // // Initialization // th_init("th-test", "th-libs unit tests", "0.2", NULL, NULL); th_verbosity = 0; TEST_ASSERT(sizeof(char) == sizeof(unsigned char)); TEST_ASSERT(sizeof(char) == 1); TEST_ASSERT(sizeof(uint16_t) == 2); TEST_ASSERT(sizeof(uint32_t) == 4); TEST_ASSERT(sizeof(uint64_t) == 8); tests_failed = tests_passed = tests_total = sets_total = sets_nenabled = 0; for (i1 = 0; i1 < SET_MAX_TESTS; i1++) sets_enabled[i1] = 1; th_io_init_stdio(&testio, stdout); // // Parse command line arguments // if (!th_args_process(argc, argv, arg_opts, arg_nopts, arg_handle_opt, NULL, 0)) return 0; tprint(1, "Enabled test types are 0x%04x.\n", optFlags); // // File functions // if (test_set_start("file functions")) { char *home = th_get_home_dir(), *cfg = th_get_config_dir(); th_stat_data stat; #ifdef TH_PLAT_UNIX int res; #endif TEST_1B("%s", home, !=, NULL); TEST_1B("%s", cfg, !=, NULL); TEST_1A(th_stat_path(home, &stat) == true); TEST_1B("%d", (stat.flags & TH_IS_DIR), !=, 0); #ifdef TH_PLAT_UNIX TEST_1A(th_stat_path("/nonexist", &stat) == false); TEST_1A(th_stat_path("/root", &stat) == true); TEST_1B("%d", (stat.flags & TH_IS_WRITABLE), ==, 0); TEST_1A((res = th_mkdir_path("/tmp/thlibtest/foobar/baz", 0)) == THERR_OK); TEST_1B("%d", res, ==, THERR_OK); if (res == THERR_OK) { TEST_1A(th_stat_path("/tmp/thlibtest/foobar/baz", &stat) == true); TEST_1B("%d", (stat.flags & TH_IS_DIR), !=, 0); } #endif th_free(home); th_free(cfg); } // // Test series for printf() // char *i_fmts[] = { "", "05", "6", ".4", "1.1", "1.0", "8.5", "08.5", "3", "2.1", "3", "1", "18", "018", ".0", "0" }; char *i_mods[] = { "", "-", "+", "#", " ", }; char *i_types[] = { "d", "u", "i", "x", "X", "o", }; if (test_set_start("printf() integer")) { int i_vals[] = { 0, -0, 1, -1, 10, -10, 512, -512, -1024, 612342, -612342, 0x1fff, 0x8000000, -123456789, 2147483647, -2147483648 }; for (i1 = 0; i1 < NCOUNT(i_vals); i1++) { snprintf(buf, sizeof(buf), "%d", i_vals[i1]); for (i4 = 0; i4 < NCOUNT(i_mods); i4++) for (i3 = 0; i3 < NCOUNT(i_types); i3++) for (i2 = 0; i2 < NCOUNT(i_fmts); i2++) { snprintf(buf2, sizeof(buf2), "%%%s%s%s", i_mods[i4], i_fmts[i2], i_types[i3]); test_snprintf(buf, buf2, i_vals[i1]); } } } if (test_set_start("printf() integer 64bit generic")) { int64_t i_vals64[] = { 0, -0, 1, -1, 10, -10, 512, -512, -1024, 612342, -612342, 0x1fff, 0x8000000, -123456789, 4294967295, -2147483648, 0x3342344341fff, 0x1f8000000, }; for (i1 = 0; i1 < NCOUNT(i_vals64); i1++) { snprintf(buf, sizeof(buf), "%" PRId64, i_vals64[i1]); for (i4 = 0; i4 < NCOUNT(i_mods); i4++) for (i3 = 0; i3 < NCOUNT(i_types); i3++) for (i2 = 0; i2 < NCOUNT(i_fmts); i2++) { snprintf(buf2, sizeof(buf2), "%%%s%sll%s", i_mods[i4], i_fmts[i2], i_types[i3]); test_snprintf(buf, buf2, i_vals64[i1]); } } } if (test_set_start("printf() integer 64bit PRI* specifiers")) { int64_t i_vals64[] = { 0, -0, 1, -1, 10, -10, 512, -512, -1024, 612342, -612342, 0x1fff, 0x8000000, -123456789, 4294967295, -2147483648, 0x3342344341fff, 0x1f8000000, }; for (i1 = 0; i1 < NCOUNT(i_vals64); i1++) { snprintf(buf, sizeof(buf), "%" PRId64, i_vals64[i1]); for (i4 = 0; i3 < NCOUNT(i_mods); i3++) for (i2 = 0; i2 < NCOUNT(i_fmts); i2++) { snprintf(buf2, sizeof(buf2), "%%%s%s" PRId64, i_mods[i3], i_fmts[i2]); test_snprintf(buf, buf2, i_vals64[i1]); snprintf(buf2, sizeof(buf2), "%%%s%s" PRIx64, i_mods[i3], i_fmts[i2]); test_snprintf(buf, buf2, i_vals64[i1]); snprintf(buf2, sizeof(buf2), "%%%s%s" PRIX64, i_mods[i3], i_fmts[i2]); test_snprintf(buf, buf2, i_vals64[i1]); } } } #ifdef TH_WIP_FLOAT_SUPPORT if (test_set_start("printf() float")) { double f_vals[] = { 0, 1, 2, 3, 2.02, 612342.234, -2.07, -612342.12, 437692.9876543219, 0x1fff, 0x8000000, 0.15625 }; char *f_fmts[] = { "%f", "%1.1f", "%8.5f", "%5f", "%-5f", "", "%-5.2f", "%08.5f" }; for (i1 = 0; i1 < NCOUNT(f_vals); i1++) { snprintf(buf, sizeof(buf), "%f", f_vals[i1]); for (i2 = 0; i2 < NCOUNT(f_fmts); i2++) test_snprintf(buf, f_fmts[i2], f_vals[i1]); } } #endif if (test_set_start("printf() string")) { char *s_vals[] = { "", "XYZXYZ", "xxx yyy zzz ppp fff", NULL, "X", "abcde", "dx", "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ", }; // char *s_fmts[] = { "%s", "%2s", "%-2s", "%5s", "%-5s", "%16s", "%-16s", "%1s", "%-1s", "% 2s", "%03s", "% -12s", "% 03s", "%-.15s", "%.8s" }; char *s_fmts[] = { "", "-", " ", "0", " 0", ".", "-.", }; int s_widths[] = { 0, 1, 2, 5, 16, }; for (i1 = 0; i1 < NCOUNT(s_vals); i1++) for (i2 = 0; i2 < NCOUNT(s_fmts); i2++) for (i3 = 0; i3 < NCOUNT(s_widths); i3++) { snprintf(buf, sizeof(buf), "%%%s%ds", s_fmts[i2], s_widths[i3]); test_snprintf(s_vals[i1], buf, s_vals[i1]); } } if (test_set_start("printf() char")) { const char c_val = 'x'; const char *c_msg = "x"; char *c_fmts[] = { "a%cBC", "%c", "", "%0c", "%1c", "% c", "%-3c", "%3c", "%.3c", "%-.3c", "%-3.3c", "%.c", "%05c", "%-05c", }; for (i1 = 0; i1 < NCOUNT(c_fmts); i1++) test_snprintf(c_msg, c_fmts[i1], c_val); } if (test_set_start("printf() pointers")) { char *p_fmts[] = { "%p", "%2p", "%.2p", "%03p", "%04p", "%-3p", "%0.3p", "%8p", "%32p", "%032p", "%-32p", "%-032p", "%16.8p", "%016.8p" }; void *p_vals[] = { NULL, (void *) 1, &p_fmts, }; for (i1 = 0; i1 < NCOUNT(p_vals); i1++) { snprintf(buf, sizeof(buf), "%p", p_vals[i1]); for (i2 = 0; i2 < NCOUNT(p_fmts); i2++) test_snprintf(buf, p_fmts[i2], p_vals[i1]); } } // // String matching functions // if (test_set_start("String compare #1")) { TEST_2A(th_strcasecmp, "aSdFq" , "asdfq" , true); TEST_2A(th_strcasecmp, "aSdFq" , "asFfq" , false); TEST_2A(th_strcasecmp, "abcde" , "abcde" , true); TEST_2A(th_strcasecmp, "öäå" , "öäå" , true); TEST_2A(th_strcasecmp, "aöäå" , "aöäå" , true); } if (test_set_start("String compare #2")) { TEST_3A(th_strncasecmp, "aSdFq" , "asFfqB" , 4, false); TEST_3A(th_strncasecmp, "aSdFq" , "asFfqQ" , 2, true); TEST_3A(th_strncasecmp, "aSdFq" , "asDfq" , 3, true); TEST_3A(th_strncasecmp, "aSdFq" , "asDfq" , 2, true); TEST_3A(th_strncasecmp, "aSdFq" , "asDfq" , 0, true); TEST_3A(th_strncasecmp, "aSdFq" , "QsDfq" , 0, true); TEST_3A(th_strncasecmp, "aSdFq" , "QsDfq" , 1, false); TEST_3A(th_strncasecmp, "max=" , "max=4" , 4, true); TEST_3A(th_strncasecmp, "max=" , "max" , 4, false); } if (test_set_start("String compare #3")) { TEST_2C(th_strrcasecmp, "foo aSdFq", " asdfq", true); TEST_2C(th_strrcasecmp, "aSdFq", " asdfq", false); TEST_2C(th_strrcasecmp, "foo aSdFq baz", "asdfq", false); } if (test_set_start("String matching #1 (case-sensitive)")) { TEST_3B(th_strmatch, "abba ABBAkukka lol" , "*lol" , false, true); TEST_3B(th_strmatch, "abba ABBAkukka lol" , "*lo*" , false, true); TEST_3B(th_strmatch, "abba ABBAkukka lol" , "*lo" , false, false); TEST_3B(th_strmatch, "abba ABBAkukka lol" , "abba" , false, false); TEST_3B(th_strmatch, "abba ABBAkukka lol" , "*bba*" , false, true); TEST_3B(th_strmatch, "abba ABBAkukka lol" , "abba*" , false, true); TEST_3B(th_strmatch, "abba ABBAkukka lol" , "abbak*" , false, false); TEST_3B(th_strmatch, "abba ABBAöökukka lol" , "*abbaö?" , false, false); } if (test_set_start("String matching #2 (case-insensitive)")) { TEST_3B(th_strcasematch, "abba ABBAkukka lol" , "abbak*" , false, false); TEST_3B(th_strcasematch, "abba ABBAkukka lol" , "*abbak*" , false, true); TEST_3B(th_strcasematch, "abba ABBAkukka lol" , "*ab?ak*" , false, true); TEST_3B(th_strcasematch, "abba ABBAkukka lol" , "*abbak?" , false, false); TEST_3B(th_strcasematch, "abba ABBAkukka lol" , "?bba?abba*" , false, true); } if (test_set_start("String matching #3 (escapes)")) { TEST_3B(th_strcasematch, "abba ABBA*kukka lol" , "*abba\\*ku*" , true, true); TEST_3B(th_strcasematch, "abba ABBAkuk?ka lol" , "*kuk\\?ka*" , true, true); TEST_3B(th_strcasematch, "abba ABBA*kukka lol" , "*abba*ku*" , true, true); TEST_3B(th_strcasematch, "abba ABBAkuk?ka lol" , "*kuk?ka*" , true, true); TEST_3B(th_strcasematch, "abba ABBAkuk?ka lol" , "*kuk?ka\\*" , true, false); TEST_3B(th_strcasematch, "abba ABBAkuk\\ka lol" , "*kuk\\\\ka*" , true, true); } // Tests that test for things that do not work correctly yet // Unicode / multibyte UTF-8 causes problems here if ((optFlags & TST_BROKEN) && test_set_start("Invalid UTF-8 handling")) { TEST_2A(th_strcasecmp, "ÖÄÅ", "öäå", false); // if it worked, SHOULD match TEST_3A(th_strncasecmp, "Aäöå", "aöå", 2, true); // if worked, it should NOT match TEST_3B(th_strmatch, "öriÖRI! lol", "?ri?RI!*", false, false); // should match } // // printf() PRI* format specifiers, also a compile time test // if (test_set_start("PRI* specifiers")) { char tmp[32]; uint32_t u32 = 0xaabbccdd; uint64_t u64 = 0xaabbccdd11223344; size_t usiz = #if TH_ARCH == 32 0x11223344; #elif TH_ARCH == 64 0xaabbccdd11223344; #else # error Unsupported TH_ARCH value. #endif snprintf(tmp, sizeof(tmp), "%16" PRIx_SIZE_T "h", usiz); #if TH_ARCH == 32 TEST_2A(strcmp, tmp, "0000000011223344h", true); #else TEST_2A(strcmp, tmp, "aabbccdd11223344h", true); #endif snprintf(tmp, sizeof(tmp), "%08" PRIx32 "h", u32); TEST_2A(strcmp, tmp, "aabbccddh", true); snprintf(tmp, sizeof(tmp), "%16" PRIx64 "h", u64); TEST_2A(strcmp, tmp, "aabbccdd11223344h", true); } // // Configuration file handling // if (test_set_start("Configuration file handling")) { test_config_file(); } // // Linked lists // if (test_set_start("Linked lists")) { test_linked_lists(); } // // String functions // if (test_set_start("String functions")) { unsigned int tmpUint; TEST_1A(th_get_hex_triplet("0fac11", &tmpUint) == true); TEST_1B("0x%06x", tmpUint, ==, 0x0fac11); TEST_1A(th_get_hex_triplet("120fac11", &tmpUint) == true); TEST_1B("0x%06x", tmpUint, ==, 0x120fac11); TEST_1A(th_get_hex_triplet("x120fac11", &tmpUint) == false); } // // Regular expressions // #ifdef TH_EXPERIMENTAL_REGEX if (test_set_start("Regular expressions")) { #ifdef TH_EXPERIMENTAL_REGEX_DEBUG th_dbg_fh = (th_verbosity >= 3) ? &testio : NULL; #endif if (1) { const char *str = "z*k+abba fabboa? [a-zA-Z_-] \\{\\} k{4} ([0-9]+ yay){1,2} foo(bar|zoo)?"; th_regex_t *expr = NULL; int res = th_regex_compile(&expr, str); printf("REGEX: \"%s\"\n", str); if (res == THERR_OK) th_regex_dump(&testio, 1, expr); else printf("ERROR: %s\n", th_error_str(res)); th_regex_free(expr); } if (1) { static const test_regex_def1 tlist[] = { { "abcfoabcccg" , 0, { "abcfoabcccg", } }, { "sabcbcfoabcccgz" , 0, { "abcbcfoabcccg", } }, { "abcbcfoabccg abcbcfoabccccg" , 0, { "abcbcfoabccg", "abcbcfoabccccg" } }, { NULL , 0, { NULL } } }; test_regex_list1(tlist, "a(bc){1,2}fo[oab]*cc?g"); } if (1) { static const test_regex_def1 tlist[] = { { "abcfoabccg" , 0, { "abcfoabccg", } }, { "abcbcfoabccg" , 0, { "abcbcfoabccg", } }, { "abcbcfoabccgabcbcfoabccg" , 0, { "abcbcfoabccg", } }, { "ffdsafS abcbcfoabccg zasdf" , 0, { NULL } }, { NULL , 0, { NULL } } }; test_regex_list1(tlist, "^a(bc){1,2}fo[oab]*cc?g"); } if (1) { static const test_regex_def1 tlist[] = { { "cg" , 0, { "g", } }, { "g" , 0, { "g", } }, { "" , 0, { NULL, } }, { "c" , 0, { NULL, } }, { NULL , 0, { NULL } } }; test_regex_list1(tlist, "g$"); } if (1) { static const test_regex_def1 tlist[] = { { "kzoobarzz" , 0, { "zoobar", } }, { "hehzoo lol baromg" , 0, { "zoo lol bar", } }, { "hoho zoo lol lol bar bar f" , 0, { "zoo lol lol bar", } }, { "hoho zoobar bar heh" , 0, { "zoobar", } }, { NULL , 0, { NULL } } }; test_regex_list1(tlist, "zoo.*?bar"); } if (1) { static const test_regex_def1 tlist[] = { { "kzoobarzz" , 0, { "zoobar", } }, { "hehzoo lol baromg" , 0, { "zoo lol bar", } }, { "hoho zoo lol lol bar bar f" , 0, { "zoo lol lol bar bar", } }, { "hoho zoobar bar heh" , 0, { "zoobar bar", } }, { NULL , 0, { NULL } } }; test_regex_list1(tlist, "zoo.*bar"); } } #endif // // Print summary and exit // tprint(1, "======================================================\n"); tprint(0, "%d tests failed, %d passed (%d main tests), %d test sets of %d sets total.\n\n", tests_failed, tests_passed, tests_total, sets_nenabled, sets_total); return 0; }