cordtest.c raw

   1  /*
   2   * Copyright (c) 1993-1994 by Xerox Corporation.  All rights reserved.
   3   *
   4   * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED
   5   * OR IMPLIED.  ANY USE IS AT YOUR OWN RISK.
   6   *
   7   * Permission is hereby granted to use or copy this program
   8   * for any purpose, provided the above notices are retained on all copies.
   9   * Permission to modify the code and to distribute modified code is granted,
  10   * provided the above notices are retained, and a notice that the code was
  11   * modified is included with the above copyright notice.
  12   */
  13  
  14  #include <stdarg.h>
  15  #include <stdio.h>
  16  #include <stdlib.h>
  17  #include <string.h>
  18  
  19  #include "gc.h"
  20  #include "gc/cord.h"
  21  
  22  /*
  23   * This is a very incomplete test of the cord package.  It knows about
  24   * a few internals of the package (e.g. when C strings are returned) that
  25   * real clients should not rely on.
  26   */
  27  
  28  #define ABORT(string)                        \
  29    {                                          \
  30      fprintf(stderr, "FAILED: %s\n", string); \
  31      abort();                                 \
  32    }
  33  
  34  #if defined(CPPCHECK)
  35  #  undef CORD_iter
  36  #  undef CORD_next
  37  #  undef CORD_pos_fetch
  38  #  undef CORD_pos_to_cord
  39  #  undef CORD_pos_to_index
  40  #  undef CORD_pos_valid
  41  #  undef CORD_prev
  42  #endif
  43  
  44  static int count;
  45  
  46  #define LOG_CORD_ITER_CNT 16
  47  #define SUBSTR_POS_BASE 1000
  48  #define PREPARE_CAT_COUNT 100
  49  
  50  #define CORD_ITER_CNT (1 << LOG_CORD_ITER_CNT)
  51  #define SMALL_SUBSTR_POS (1 << (LOG_CORD_ITER_CNT - 6))
  52  #define BIG_SUBSTR_POS (SUBSTR_POS_BASE * 36)
  53  
  54  static int
  55  test_fn(char c, void *client_data)
  56  {
  57    if ((GC_uintptr_t)client_data != 13U)
  58      ABORT("bad client data");
  59    if (count < CORD_ITER_CNT + 1) {
  60      if ((count & 1) == 0) {
  61        if (c != 'b')
  62          ABORT("bad char");
  63      } else {
  64        if (c != 'a')
  65          ABORT("bad char");
  66      }
  67      count++;
  68  #if defined(CPPCHECK)
  69      GC_noop1_ptr(client_data);
  70  #endif
  71      return 0;
  72    } else {
  73      if (c != 'c')
  74        ABORT("bad char");
  75      count++;
  76      return 1;
  77    }
  78  }
  79  
  80  static char
  81  id_cord_fn(size_t i, void *client_data)
  82  {
  83    if (client_data != 0)
  84      ABORT("id_cord_fn: bad client data");
  85  #if defined(CPPCHECK)
  86    GC_noop1_ptr(client_data);
  87  #endif
  88    return (char)i;
  89  }
  90  
  91  static void
  92  test_cord_x1(CORD x)
  93  {
  94    CORD y;
  95    CORD_pos p;
  96  
  97    count = 0;
  98    if (CORD_iter5(x, CORD_ITER_CNT - 1, test_fn, CORD_NO_FN,
  99                   (void *)(GC_uintptr_t)13)
 100        == 0) {
 101      ABORT("CORD_iter5 failed");
 102    }
 103    if (count != CORD_ITER_CNT + 2)
 104      ABORT("CORD_iter5 failed");
 105  
 106    count = 0;
 107    CORD_set_pos(p, x, CORD_ITER_CNT - 1);
 108    while (CORD_pos_valid(p)) {
 109      (void)test_fn(CORD_pos_fetch(p), (void *)(GC_uintptr_t)13);
 110      CORD_next(p);
 111    }
 112    if (count != CORD_ITER_CNT + 2)
 113      ABORT("Position based iteration failed");
 114  
 115    y = CORD_substr(x, SMALL_SUBSTR_POS - 1, 5);
 116  
 117    if (!y)
 118      ABORT("CORD_substr returned NULL");
 119    if (!CORD_IS_STRING(y))
 120      ABORT("short cord should usually be a string");
 121    if (strcmp(y, "babab") != 0)
 122      ABORT("bad CORD_substr result");
 123  
 124    y = CORD_substr(x, SMALL_SUBSTR_POS, 8);
 125    if (!y)
 126      ABORT("CORD_substr returned NULL");
 127    if (!CORD_IS_STRING(y))
 128      ABORT("short cord should usually be a string");
 129    if (strcmp(y, "abababab") != 0)
 130      ABORT("bad CORD_substr result (2)");
 131  
 132    y = CORD_substr(x, 2 * CORD_ITER_CNT - 1, 8);
 133    if (!y)
 134      ABORT("CORD_substr returned NULL");
 135    if (!CORD_IS_STRING(y))
 136      ABORT("short cord should usually be a string");
 137    if (strcmp(y, "bc") != 0)
 138      ABORT("bad CORD_substr result (3)");
 139  }
 140  
 141  static void
 142  test_cord_x2(CORD x)
 143  {
 144    size_t i;
 145    CORD y;
 146    CORD_pos p;
 147  
 148    count = 0;
 149    if (CORD_iter5(x, CORD_ITER_CNT - 1, test_fn, CORD_NO_FN,
 150                   (void *)(GC_uintptr_t)13)
 151        == 0) {
 152      ABORT("CORD_iter5 failed");
 153    }
 154    if (count != CORD_ITER_CNT + 2)
 155      ABORT("CORD_iter5 failed");
 156  
 157    y = CORD_substr(x, SMALL_SUBSTR_POS - 1, 5);
 158    if (!y)
 159      ABORT("CORD_substr returned NULL");
 160    if (!CORD_IS_STRING(y))
 161      ABORT("short cord should usually be a string");
 162    if (strcmp(y, "babab") != 0)
 163      ABORT("bad CORD_substr result (4)");
 164  
 165    y = CORD_from_fn(id_cord_fn, 0, 13);
 166    i = 0;
 167    CORD_set_pos(p, y, i);
 168    while (CORD_pos_valid(p)) {
 169      char c = CORD_pos_fetch(p);
 170  
 171      if ((size_t)(unsigned char)c != i)
 172        ABORT("Traversal of function node failed");
 173      CORD_next(p);
 174      i++;
 175    }
 176    if (i != 13)
 177      ABORT("Bad apparent length for function node");
 178  #if defined(CPPCHECK)
 179    /* TODO: Actually test these functions. */
 180    CORD_prev(p);
 181    (void)CORD_pos_to_cord(p);
 182    (void)CORD_pos_to_index(p);
 183    CORD_dump(y);
 184  #endif
 185  }
 186  
 187  static void
 188  test_basics(void)
 189  {
 190    CORD x = CORD_from_char_star("ab");
 191    size_t i;
 192  
 193    x = CORD_cat(x, x);
 194    if (x == CORD_EMPTY)
 195      ABORT("CORD_cat(x,x) returned empty cord");
 196    if (!CORD_IS_STRING(x))
 197      ABORT("short cord should usually be a string");
 198    if (strcmp(x, "abab") != 0)
 199      ABORT("bad CORD_cat result");
 200    for (i = 1; i < LOG_CORD_ITER_CNT; i++) {
 201      x = CORD_cat(x, x);
 202    }
 203    x = CORD_cat(x, "c");
 204    if (CORD_len(x) != 2 * CORD_ITER_CNT + 1)
 205      ABORT("bad length");
 206    test_cord_x1(x);
 207  
 208    x = CORD_balance(x);
 209    if (CORD_len(x) != 2 * CORD_ITER_CNT + 1)
 210      ABORT("bad length 2");
 211    test_cord_x2(x);
 212  
 213  #if defined(CPPCHECK)
 214    /* TODO: Actually test these functions. */
 215    (void)CORD_iter(CORD_EMPTY, test_fn, NULL);
 216    (void)CORD_riter(CORD_EMPTY, test_fn, NULL);
 217  #endif
 218  }
 219  
 220  static CORD
 221  prepare_cord_f1(CORD y)
 222  {
 223    CORD w = CORD_cat(CORD_cat(y, y), y);
 224    CORD x = "{}";
 225    CORD z = CORD_catn(3, y, y, y);
 226    int i;
 227  
 228    if (CORD_cmp(w, z) != 0)
 229      ABORT("CORD_catn comparison wrong");
 230    for (i = 1; i < PREPARE_CAT_COUNT; i++) {
 231      x = CORD_cat(x, y);
 232    }
 233    z = CORD_balance(x);
 234    if (CORD_cmp(x, z) != 0)
 235      ABORT("balanced string comparison wrong");
 236    if (CORD_cmp(x, CORD_cat(z, CORD_nul(13))) >= 0)
 237      ABORT("comparison 2");
 238    if (CORD_cmp(CORD_cat(x, CORD_nul(13)), z) <= 0)
 239      ABORT("comparison 3");
 240    if (CORD_cmp(x, CORD_cat(z, "13")) >= 0)
 241      ABORT("comparison 4");
 242    return z;
 243  }
 244  
 245  static void
 246  test_cords_f1b(CORD w, CORD z)
 247  {
 248    if (CORD_cmp(w, z) != 0)
 249      ABORT("File conversions differ");
 250    if (CORD_chr(w, 0, '9') != 37)
 251      ABORT("CORD_chr failed 1");
 252    if (CORD_chr(w, 3, 'a') != 38)
 253      ABORT("CORD_chr failed 2");
 254    if (CORD_rchr(w, CORD_len(w) - 1, '}') != 1)
 255      ABORT("CORD_rchr failed");
 256  }
 257  
 258  static void
 259  test_cords_f2(CORD w, CORD x, CORD y)
 260  {
 261    CORD u;
 262  
 263    if (CORD_len(w) != CORD_len(x))
 264      ABORT("file length wrong");
 265    if (CORD_cmp(w, x) != 0)
 266      ABORT("file comparison wrong");
 267    if (CORD_cmp(CORD_substr(w, BIG_SUBSTR_POS, 36), y) != 0)
 268      ABORT("file substr wrong");
 269    if (strcmp(CORD_to_char_star(CORD_substr(w, BIG_SUBSTR_POS, 36)), y) != 0)
 270      ABORT("char * file substr wrong");
 271    u = CORD_substr(w, BIG_SUBSTR_POS, 2);
 272    if (!u)
 273      ABORT("CORD_substr returned NULL");
 274    if (strcmp(u, "ab") != 0)
 275      ABORT("short file substr wrong");
 276    if (CORD_str(x, 1, "9a") != 35)
 277      ABORT("CORD_str failed 1");
 278    if (CORD_str(x, 0, "9abcdefghijk") != 35)
 279      ABORT("CORD_str failed 2");
 280    if (CORD_str(x, 0, "9abcdefghijx") != CORD_NOT_FOUND)
 281      ABORT("CORD_str failed 3");
 282    if (CORD_str(x, 0, "9>") != CORD_NOT_FOUND)
 283      ABORT("CORD_str failed 4");
 284  }
 285  
 286  static void
 287  test_extras(void)
 288  {
 289  #define FNAME1 "cordtst1.tmp" /*< short name (8+3) for portability */
 290  #define FNAME2 "cordtst2.tmp"
 291    int i;
 292    CORD y = "abcdefghijklmnopqrstuvwxyz0123456789";
 293    CORD w, x, z;
 294    FILE *f, *f1a, *f1b, *f2;
 295  
 296    f = fopen(FNAME1, "w");
 297    if (!f)
 298      ABORT("open 1 failed");
 299    z = prepare_cord_f1(y);
 300    if (CORD_put(z, f) == EOF)
 301      ABORT("CORD_put failed");
 302    if (fclose(f) == EOF)
 303      ABORT("fclose failed");
 304  
 305    f1a = fopen(FNAME1, "rb");
 306    if (!f1a)
 307      ABORT("open 1a failed");
 308    w = CORD_from_file(f1a);
 309    if (CORD_len(w) != CORD_len(z))
 310      ABORT("file length wrong");
 311    if (CORD_cmp(w, z) != 0)
 312      ABORT("file comparison wrong");
 313    if (CORD_cmp(CORD_substr(w, (PREPARE_CAT_COUNT / 2) * 36 + 2, 36), y) != 0)
 314      ABORT("file substr wrong (2)");
 315  
 316    f1b = fopen(FNAME1, "rb");
 317    if (!f1b)
 318      ABORT("open 1b failed");
 319    test_cords_f1b(w, CORD_from_file_lazy(f1b));
 320  
 321    f = fopen(FNAME2, "w");
 322    if (!f)
 323      ABORT("open 2 failed");
 324  #ifdef __DJGPP__
 325    /* FIXME: DJGPP workaround.  Why does this help? */
 326    if (fflush(f) != 0)
 327      ABORT("fflush failed");
 328  #endif
 329    x = y;
 330    for (i = 3; i < LOG_CORD_ITER_CNT; i++) {
 331      x = CORD_cat(x, x);
 332    }
 333  
 334    if (CORD_put(x, f) == EOF)
 335      ABORT("CORD_put failed");
 336    if (fclose(f) == EOF)
 337      ABORT("fclose failed");
 338  
 339    f2 = fopen(FNAME2, "rb");
 340    if (!f2)
 341      ABORT("open 2a failed");
 342    w = CORD_from_file(f2);
 343    test_cords_f2(w, x, y);
 344  
 345    /* Note: `f1a`, `f1b`, `f2` handles are closed lazily by `cord` library. */
 346    /* TODO: Propose and use `CORD_fclose`. */
 347    *(CORD volatile *)&w = CORD_EMPTY;
 348    *(CORD volatile *)&z = CORD_EMPTY;
 349    GC_gcollect();
 350  #ifndef GC_NO_FINALIZATION
 351    GC_invoke_finalizers();
 352    /* Of course, this does not guarantee the files are closed. */
 353  #endif
 354    if (remove(FNAME1) != 0) {
 355      /*
 356       * On some systems, e.g. OS/2, this may fail if `f1` is still open.
 357       * But we cannot call `fclose` as it might lead to double close.
 358       */
 359      fprintf(stderr, "WARNING: remove failed: " FNAME1 "\n");
 360    }
 361  }
 362  
 363  static int
 364  wrap_vprintf(CORD format, ...)
 365  {
 366    va_list args;
 367    int result;
 368  
 369    va_start(args, format);
 370    result = CORD_vprintf(format, args);
 371    va_end(args);
 372    return result;
 373  }
 374  
 375  static int
 376  wrap_vfprintf(FILE *f, CORD format, ...)
 377  {
 378    va_list args;
 379    int result;
 380  
 381    va_start(args, format);
 382    result = CORD_vfprintf(f, format, args);
 383    va_end(args);
 384    return result;
 385  }
 386  
 387  #if defined(__DJGPP__) || defined(__DMC__) || defined(__STRICT_ANSI__)
 388  /* `snprintf` is missing in DJGPP (v2.0.3). */
 389  #else
 390  #  if defined(_MSC_VER)
 391  #    if defined(_WIN32_WCE)
 392  /* `_snprintf` is deprecated in WinCE. */
 393  #      define GC_SNPRINTF StringCchPrintfA
 394  #    else
 395  #      define GC_SNPRINTF _snprintf
 396  #    endif
 397  #  else
 398  #    define GC_SNPRINTF snprintf
 399  #  endif
 400  #endif
 401  
 402  /* no static */ /* no const */ char *zu_format = (char *)"%zu";
 403  
 404  static void
 405  test_printf(void)
 406  {
 407    CORD result;
 408    char result2[200];
 409    long l = -1;
 410    short s = (short)-1;
 411    CORD x;
 412    int res;
 413  
 414    if (CORD_sprintf(&result, "%7.2f%ln", 3.14159F, &l) != 7)
 415      ABORT("CORD_sprintf failed 1");
 416    if (CORD_cmp(result, "   3.14") != 0)
 417      ABORT("CORD_sprintf goofed 1");
 418    if (l != 7)
 419      ABORT("CORD_sprintf goofed 2");
 420    if (CORD_sprintf(&result, "%-7.2s%hn%c%s", "abcd", &s, 'x', "yz") != 10)
 421      ABORT("CORD_sprintf failed 2");
 422    if (CORD_cmp(result, "ab     xyz") != 0)
 423      ABORT("CORD_sprintf goofed 3");
 424    if (s != 7)
 425      ABORT("CORD_sprintf goofed 4");
 426    x = "abcdefghij";
 427    x = CORD_cat(x, x);
 428    x = CORD_cat(x, x);
 429    x = CORD_cat(x, x);
 430    if (CORD_sprintf(&result, "->%-120.78r!\n", x) != 124)
 431      ABORT("CORD_sprintf failed 3");
 432  #ifdef GC_SNPRINTF
 433    (void)GC_SNPRINTF(result2, sizeof(result2), "->%-120.78s!\n",
 434                      CORD_to_char_star(x));
 435  #else
 436    (void)sprintf(result2, "->%-120.78s!\n", CORD_to_char_star(x));
 437  #endif
 438    result2[sizeof(result2) - 1] = '\0';
 439    if (CORD_cmp(result, result2) != 0)
 440      ABORT("CORD_sprintf goofed 5");
 441  
 442  #ifdef GC_SNPRINTF
 443    /*
 444     * Check whether "%zu" specifier is supported; pass the format string via
 445     * a variable to avoid a compiler warning if not.
 446     */
 447    res = GC_SNPRINTF(result2, sizeof(result2), zu_format, (size_t)0);
 448  #else
 449    res = sprintf(result2, zu_format, (size_t)0);
 450  #endif
 451    result2[sizeof(result2) - 1] = '\0';
 452    /* Is "%z" supported by `printf`? */
 453    if (res == 1) {
 454      if (CORD_sprintf(&result, "%zu %zd 0x%0zx", (size_t)123, (size_t)4567,
 455                       (size_t)0x4abc)
 456          != 15)
 457        ABORT("CORD_sprintf failed 5");
 458      if (CORD_cmp(result, "123 4567 0x4abc") != 0)
 459        ABORT("CORD_sprintf goofed 5");
 460    } else {
 461      (void)CORD_printf("printf lacks support of 'z' modifier\n");
 462    }
 463  
 464    /* TODO: Better test CORD_[v][f]printf. */
 465    (void)CORD_printf(CORD_EMPTY);
 466    (void)wrap_vfprintf(stdout, CORD_EMPTY);
 467    (void)wrap_vprintf(CORD_EMPTY);
 468  }
 469  
 470  int
 471  main(void)
 472  {
 473    GC_INIT();
 474  #ifndef NO_INCREMENTAL
 475    GC_enable_incremental();
 476  #endif
 477    if (GC_get_find_leak())
 478      printf("This test program is not designed for leak detection mode\n");
 479    CORD_set_oom_fn(CORD_get_oom_fn()); /*< just to test these are existing */
 480  
 481    test_basics();
 482    test_extras();
 483    test_printf();
 484  
 485    GC_gcollect(); /*< to close `f2` before the file removal */
 486    if (remove(FNAME2) != 0) {
 487      fprintf(stderr, "WARNING: remove failed: " FNAME2 "\n");
 488    }
 489    CORD_fprintf(stdout, "SUCCEEDED\n");
 490    return 0;
 491  }
 492