/* * Copyright (c) 1993-1994 by Xerox Corporation. All rights reserved. * * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED * OR IMPLIED. ANY USE IS AT YOUR OWN RISK. * * Permission is hereby granted to use or copy this program * for any purpose, provided the above notices are retained on all copies. * Permission to modify the code and to distribute modified code is granted, * provided the above notices are retained, and a notice that the code was * modified is included with the above copyright notice. */ #include #include #include #include #include "gc.h" #include "gc/cord.h" /* * This is a very incomplete test of the cord package. It knows about * a few internals of the package (e.g. when C strings are returned) that * real clients should not rely on. */ #define ABORT(string) \ { \ fprintf(stderr, "FAILED: %s\n", string); \ abort(); \ } #if defined(CPPCHECK) # undef CORD_iter # undef CORD_next # undef CORD_pos_fetch # undef CORD_pos_to_cord # undef CORD_pos_to_index # undef CORD_pos_valid # undef CORD_prev #endif static int count; #define LOG_CORD_ITER_CNT 16 #define SUBSTR_POS_BASE 1000 #define PREPARE_CAT_COUNT 100 #define CORD_ITER_CNT (1 << LOG_CORD_ITER_CNT) #define SMALL_SUBSTR_POS (1 << (LOG_CORD_ITER_CNT - 6)) #define BIG_SUBSTR_POS (SUBSTR_POS_BASE * 36) static int test_fn(char c, void *client_data) { if ((GC_uintptr_t)client_data != 13U) ABORT("bad client data"); if (count < CORD_ITER_CNT + 1) { if ((count & 1) == 0) { if (c != 'b') ABORT("bad char"); } else { if (c != 'a') ABORT("bad char"); } count++; #if defined(CPPCHECK) GC_noop1_ptr(client_data); #endif return 0; } else { if (c != 'c') ABORT("bad char"); count++; return 1; } } static char id_cord_fn(size_t i, void *client_data) { if (client_data != 0) ABORT("id_cord_fn: bad client data"); #if defined(CPPCHECK) GC_noop1_ptr(client_data); #endif return (char)i; } static void test_cord_x1(CORD x) { CORD y; CORD_pos p; count = 0; if (CORD_iter5(x, CORD_ITER_CNT - 1, test_fn, CORD_NO_FN, (void *)(GC_uintptr_t)13) == 0) { ABORT("CORD_iter5 failed"); } if (count != CORD_ITER_CNT + 2) ABORT("CORD_iter5 failed"); count = 0; CORD_set_pos(p, x, CORD_ITER_CNT - 1); while (CORD_pos_valid(p)) { (void)test_fn(CORD_pos_fetch(p), (void *)(GC_uintptr_t)13); CORD_next(p); } if (count != CORD_ITER_CNT + 2) ABORT("Position based iteration failed"); y = CORD_substr(x, SMALL_SUBSTR_POS - 1, 5); if (!y) ABORT("CORD_substr returned NULL"); if (!CORD_IS_STRING(y)) ABORT("short cord should usually be a string"); if (strcmp(y, "babab") != 0) ABORT("bad CORD_substr result"); y = CORD_substr(x, SMALL_SUBSTR_POS, 8); if (!y) ABORT("CORD_substr returned NULL"); if (!CORD_IS_STRING(y)) ABORT("short cord should usually be a string"); if (strcmp(y, "abababab") != 0) ABORT("bad CORD_substr result (2)"); y = CORD_substr(x, 2 * CORD_ITER_CNT - 1, 8); if (!y) ABORT("CORD_substr returned NULL"); if (!CORD_IS_STRING(y)) ABORT("short cord should usually be a string"); if (strcmp(y, "bc") != 0) ABORT("bad CORD_substr result (3)"); } static void test_cord_x2(CORD x) { size_t i; CORD y; CORD_pos p; count = 0; if (CORD_iter5(x, CORD_ITER_CNT - 1, test_fn, CORD_NO_FN, (void *)(GC_uintptr_t)13) == 0) { ABORT("CORD_iter5 failed"); } if (count != CORD_ITER_CNT + 2) ABORT("CORD_iter5 failed"); y = CORD_substr(x, SMALL_SUBSTR_POS - 1, 5); if (!y) ABORT("CORD_substr returned NULL"); if (!CORD_IS_STRING(y)) ABORT("short cord should usually be a string"); if (strcmp(y, "babab") != 0) ABORT("bad CORD_substr result (4)"); y = CORD_from_fn(id_cord_fn, 0, 13); i = 0; CORD_set_pos(p, y, i); while (CORD_pos_valid(p)) { char c = CORD_pos_fetch(p); if ((size_t)(unsigned char)c != i) ABORT("Traversal of function node failed"); CORD_next(p); i++; } if (i != 13) ABORT("Bad apparent length for function node"); #if defined(CPPCHECK) /* TODO: Actually test these functions. */ CORD_prev(p); (void)CORD_pos_to_cord(p); (void)CORD_pos_to_index(p); CORD_dump(y); #endif } static void test_basics(void) { CORD x = CORD_from_char_star("ab"); size_t i; x = CORD_cat(x, x); if (x == CORD_EMPTY) ABORT("CORD_cat(x,x) returned empty cord"); if (!CORD_IS_STRING(x)) ABORT("short cord should usually be a string"); if (strcmp(x, "abab") != 0) ABORT("bad CORD_cat result"); for (i = 1; i < LOG_CORD_ITER_CNT; i++) { x = CORD_cat(x, x); } x = CORD_cat(x, "c"); if (CORD_len(x) != 2 * CORD_ITER_CNT + 1) ABORT("bad length"); test_cord_x1(x); x = CORD_balance(x); if (CORD_len(x) != 2 * CORD_ITER_CNT + 1) ABORT("bad length 2"); test_cord_x2(x); #if defined(CPPCHECK) /* TODO: Actually test these functions. */ (void)CORD_iter(CORD_EMPTY, test_fn, NULL); (void)CORD_riter(CORD_EMPTY, test_fn, NULL); #endif } static CORD prepare_cord_f1(CORD y) { CORD w = CORD_cat(CORD_cat(y, y), y); CORD x = "{}"; CORD z = CORD_catn(3, y, y, y); int i; if (CORD_cmp(w, z) != 0) ABORT("CORD_catn comparison wrong"); for (i = 1; i < PREPARE_CAT_COUNT; i++) { x = CORD_cat(x, y); } z = CORD_balance(x); if (CORD_cmp(x, z) != 0) ABORT("balanced string comparison wrong"); if (CORD_cmp(x, CORD_cat(z, CORD_nul(13))) >= 0) ABORT("comparison 2"); if (CORD_cmp(CORD_cat(x, CORD_nul(13)), z) <= 0) ABORT("comparison 3"); if (CORD_cmp(x, CORD_cat(z, "13")) >= 0) ABORT("comparison 4"); return z; } static void test_cords_f1b(CORD w, CORD z) { if (CORD_cmp(w, z) != 0) ABORT("File conversions differ"); if (CORD_chr(w, 0, '9') != 37) ABORT("CORD_chr failed 1"); if (CORD_chr(w, 3, 'a') != 38) ABORT("CORD_chr failed 2"); if (CORD_rchr(w, CORD_len(w) - 1, '}') != 1) ABORT("CORD_rchr failed"); } static void test_cords_f2(CORD w, CORD x, CORD y) { CORD u; if (CORD_len(w) != CORD_len(x)) ABORT("file length wrong"); if (CORD_cmp(w, x) != 0) ABORT("file comparison wrong"); if (CORD_cmp(CORD_substr(w, BIG_SUBSTR_POS, 36), y) != 0) ABORT("file substr wrong"); if (strcmp(CORD_to_char_star(CORD_substr(w, BIG_SUBSTR_POS, 36)), y) != 0) ABORT("char * file substr wrong"); u = CORD_substr(w, BIG_SUBSTR_POS, 2); if (!u) ABORT("CORD_substr returned NULL"); if (strcmp(u, "ab") != 0) ABORT("short file substr wrong"); if (CORD_str(x, 1, "9a") != 35) ABORT("CORD_str failed 1"); if (CORD_str(x, 0, "9abcdefghijk") != 35) ABORT("CORD_str failed 2"); if (CORD_str(x, 0, "9abcdefghijx") != CORD_NOT_FOUND) ABORT("CORD_str failed 3"); if (CORD_str(x, 0, "9>") != CORD_NOT_FOUND) ABORT("CORD_str failed 4"); } static void test_extras(void) { #define FNAME1 "cordtst1.tmp" /*< short name (8+3) for portability */ #define FNAME2 "cordtst2.tmp" int i; CORD y = "abcdefghijklmnopqrstuvwxyz0123456789"; CORD w, x, z; FILE *f, *f1a, *f1b, *f2; f = fopen(FNAME1, "w"); if (!f) ABORT("open 1 failed"); z = prepare_cord_f1(y); if (CORD_put(z, f) == EOF) ABORT("CORD_put failed"); if (fclose(f) == EOF) ABORT("fclose failed"); f1a = fopen(FNAME1, "rb"); if (!f1a) ABORT("open 1a failed"); w = CORD_from_file(f1a); if (CORD_len(w) != CORD_len(z)) ABORT("file length wrong"); if (CORD_cmp(w, z) != 0) ABORT("file comparison wrong"); if (CORD_cmp(CORD_substr(w, (PREPARE_CAT_COUNT / 2) * 36 + 2, 36), y) != 0) ABORT("file substr wrong (2)"); f1b = fopen(FNAME1, "rb"); if (!f1b) ABORT("open 1b failed"); test_cords_f1b(w, CORD_from_file_lazy(f1b)); f = fopen(FNAME2, "w"); if (!f) ABORT("open 2 failed"); #ifdef __DJGPP__ /* FIXME: DJGPP workaround. Why does this help? */ if (fflush(f) != 0) ABORT("fflush failed"); #endif x = y; for (i = 3; i < LOG_CORD_ITER_CNT; i++) { x = CORD_cat(x, x); } if (CORD_put(x, f) == EOF) ABORT("CORD_put failed"); if (fclose(f) == EOF) ABORT("fclose failed"); f2 = fopen(FNAME2, "rb"); if (!f2) ABORT("open 2a failed"); w = CORD_from_file(f2); test_cords_f2(w, x, y); /* Note: `f1a`, `f1b`, `f2` handles are closed lazily by `cord` library. */ /* TODO: Propose and use `CORD_fclose`. */ *(CORD volatile *)&w = CORD_EMPTY; *(CORD volatile *)&z = CORD_EMPTY; GC_gcollect(); #ifndef GC_NO_FINALIZATION GC_invoke_finalizers(); /* Of course, this does not guarantee the files are closed. */ #endif if (remove(FNAME1) != 0) { /* * On some systems, e.g. OS/2, this may fail if `f1` is still open. * But we cannot call `fclose` as it might lead to double close. */ fprintf(stderr, "WARNING: remove failed: " FNAME1 "\n"); } } static int wrap_vprintf(CORD format, ...) { va_list args; int result; va_start(args, format); result = CORD_vprintf(format, args); va_end(args); return result; } static int wrap_vfprintf(FILE *f, CORD format, ...) { va_list args; int result; va_start(args, format); result = CORD_vfprintf(f, format, args); va_end(args); return result; } #if defined(__DJGPP__) || defined(__DMC__) || defined(__STRICT_ANSI__) /* `snprintf` is missing in DJGPP (v2.0.3). */ #else # if defined(_MSC_VER) # if defined(_WIN32_WCE) /* `_snprintf` is deprecated in WinCE. */ # define GC_SNPRINTF StringCchPrintfA # else # define GC_SNPRINTF _snprintf # endif # else # define GC_SNPRINTF snprintf # endif #endif /* no static */ /* no const */ char *zu_format = (char *)"%zu"; static void test_printf(void) { CORD result; char result2[200]; long l = -1; short s = (short)-1; CORD x; int res; if (CORD_sprintf(&result, "%7.2f%ln", 3.14159F, &l) != 7) ABORT("CORD_sprintf failed 1"); if (CORD_cmp(result, " 3.14") != 0) ABORT("CORD_sprintf goofed 1"); if (l != 7) ABORT("CORD_sprintf goofed 2"); if (CORD_sprintf(&result, "%-7.2s%hn%c%s", "abcd", &s, 'x', "yz") != 10) ABORT("CORD_sprintf failed 2"); if (CORD_cmp(result, "ab xyz") != 0) ABORT("CORD_sprintf goofed 3"); if (s != 7) ABORT("CORD_sprintf goofed 4"); x = "abcdefghij"; x = CORD_cat(x, x); x = CORD_cat(x, x); x = CORD_cat(x, x); if (CORD_sprintf(&result, "->%-120.78r!\n", x) != 124) ABORT("CORD_sprintf failed 3"); #ifdef GC_SNPRINTF (void)GC_SNPRINTF(result2, sizeof(result2), "->%-120.78s!\n", CORD_to_char_star(x)); #else (void)sprintf(result2, "->%-120.78s!\n", CORD_to_char_star(x)); #endif result2[sizeof(result2) - 1] = '\0'; if (CORD_cmp(result, result2) != 0) ABORT("CORD_sprintf goofed 5"); #ifdef GC_SNPRINTF /* * Check whether "%zu" specifier is supported; pass the format string via * a variable to avoid a compiler warning if not. */ res = GC_SNPRINTF(result2, sizeof(result2), zu_format, (size_t)0); #else res = sprintf(result2, zu_format, (size_t)0); #endif result2[sizeof(result2) - 1] = '\0'; /* Is "%z" supported by `printf`? */ if (res == 1) { if (CORD_sprintf(&result, "%zu %zd 0x%0zx", (size_t)123, (size_t)4567, (size_t)0x4abc) != 15) ABORT("CORD_sprintf failed 5"); if (CORD_cmp(result, "123 4567 0x4abc") != 0) ABORT("CORD_sprintf goofed 5"); } else { (void)CORD_printf("printf lacks support of 'z' modifier\n"); } /* TODO: Better test CORD_[v][f]printf. */ (void)CORD_printf(CORD_EMPTY); (void)wrap_vfprintf(stdout, CORD_EMPTY); (void)wrap_vprintf(CORD_EMPTY); } int main(void) { GC_INIT(); #ifndef NO_INCREMENTAL GC_enable_incremental(); #endif if (GC_get_find_leak()) printf("This test program is not designed for leak detection mode\n"); CORD_set_oom_fn(CORD_get_oom_fn()); /*< just to test these are existing */ test_basics(); test_extras(); test_printf(); GC_gcollect(); /*< to close `f2` before the file removal */ if (remove(FNAME2) != 0) { fprintf(stderr, "WARNING: remove failed: " FNAME2 "\n"); } CORD_fprintf(stdout, "SUCCEEDED\n"); return 0; }