atom.c 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189
  1. /*
  2. * Copyright © 2012 Ran Benita <ran234@gmail.com>
  3. * SPDX-License-Identifier: MIT
  4. */
  5. #include "config.h"
  6. #include "test-config.h"
  7. #include <time.h>
  8. #include "test.h"
  9. #include "atom.h"
  10. #include "utils-random.h"
  11. #define INTERN_LITERAL(table, literal) \
  12. atom_intern(table, literal, sizeof(literal) - 1, true)
  13. #define LOOKUP_LITERAL(table, literal) \
  14. atom_intern(table, literal, sizeof(literal) - 1, false)
  15. static void
  16. random_string(char **str_out, size_t *len_out)
  17. {
  18. /* Keep this small, so collisions might happen. */
  19. static const char random_chars[] = {
  20. 'a', 'b', 'c', 'd', 'e', 'f', 'g'
  21. };
  22. size_t len;
  23. char *str;
  24. len = random() % 15;
  25. str = malloc(len + 1);
  26. assert(str);
  27. for (size_t i = 0; i < len; i++)
  28. str[i] = random_chars[random() % ARRAY_SIZE(random_chars)];
  29. /* Don't always terminate it; should work without. */
  30. if (random() % 2 == 0)
  31. str[len] = '\0';
  32. *str_out = str;
  33. *len_out = len;
  34. }
  35. static void
  36. test_random_strings(void)
  37. {
  38. struct atom_string {
  39. xkb_atom_t atom;
  40. char *string;
  41. size_t len;
  42. };
  43. struct atom_table *table;
  44. struct atom_string *arr;
  45. int N;
  46. xkb_atom_t atom;
  47. const char *string;
  48. table = atom_table_new();
  49. assert(table);
  50. N = 1 + (int)random() % 100000;
  51. arr = calloc(N, sizeof(*arr));
  52. assert(arr);
  53. for (int i = 0; i < N; i++) {
  54. random_string(&arr[i].string, &arr[i].len);
  55. atom = atom_intern(table, arr[i].string, arr[i].len, false);
  56. if (atom != XKB_ATOM_NONE) {
  57. string = atom_text(table, atom);
  58. assert(string);
  59. if (arr[i].len != strlen(string) ||
  60. strncmp(string, arr[i].string, arr[i].len) != 0) {
  61. fprintf(stderr, "got a collision, but strings don't match!\n");
  62. fprintf(stderr, "existing length %zu, string %s\n",
  63. strlen(string), string);
  64. fprintf(stderr, "new length %zu, string %.*s\n",
  65. arr[i].len, (int) arr[i].len, arr[i].string);
  66. assert(false);
  67. }
  68. /* OK, got a real collision. */
  69. free(arr[i].string);
  70. i--;
  71. continue;
  72. }
  73. arr[i].atom = atom_intern(table, arr[i].string, arr[i].len, true);
  74. if (arr[i].atom == XKB_ATOM_NONE) {
  75. fprintf(stderr, "failed to intern! len: %zu, string: %.*s\n",
  76. arr[i].len, (int) arr[i].len, arr[i].string);
  77. assert(false);
  78. }
  79. }
  80. for (int i = 0; i < N; i++) {
  81. string = atom_text(table, arr[i].atom);
  82. assert(string);
  83. if (arr[i].len != strlen(string) ||
  84. strncmp(string, arr[i].string, arr[i].len) != 0) {
  85. fprintf(stderr, "looked-up string doesn't match!\n");
  86. fprintf(stderr, "found length %zu, string %s\n",
  87. strlen(string), string);
  88. fprintf(stderr, "expected length %zu, string %.*s\n",
  89. arr[i].len, (int) arr[i].len, arr[i].string);
  90. /* Since this is random, we need to dump the failing data,
  91. * so we might have some chance to reproduce. */
  92. fprintf(stderr, "START dump of arr, N=%d\n", N);
  93. for (int j = 0; j < N; j++) {
  94. fprintf(stderr, "%u\t\t%zu\t\t%.*s\n", arr[i].atom,
  95. arr[i].len, (int) arr[i].len, arr[i].string);
  96. }
  97. fprintf(stderr, "END\n");
  98. assert(false);
  99. }
  100. }
  101. for (int i = 0; i < N; i++)
  102. free(arr[i].string);
  103. free(arr);
  104. atom_table_free(table);
  105. }
  106. /* CLI positional arguments:
  107. * 1. Seed for the pseudo-random generator:
  108. * - Leave it unset or set it to “-” to use current time.
  109. * - Use an integer to set it explicitly.
  110. */
  111. int
  112. main(int argc, char *argv[])
  113. {
  114. struct atom_table *table;
  115. xkb_atom_t atom1, atom2, atom3;
  116. test_init();
  117. /* Initialize pseudo-random generator with program arg or current time */
  118. unsigned int seed;
  119. if (argc >= 2 && !streq(argv[1], "-")) {
  120. char *endp = argv[1];
  121. errno = 0;
  122. const unsigned long raw = strtoul(argv[1], &endp, 10);
  123. if (errno || endp == argv[1] || *endp != '\0' || raw > UINT_MAX) {
  124. fprintf(stderr, "ERROR: Invalid seed: \"%s\"\n", argv[1]);
  125. exit(TEST_SETUP_FAILURE);
  126. }
  127. seed = (unsigned int) raw;
  128. } else {
  129. seed = (unsigned int) time(NULL);
  130. }
  131. fprintf(stderr, "Seed for the pseudo-random generator: %u\n", seed);
  132. srandom(seed);
  133. table = atom_table_new();
  134. assert(table);
  135. assert(atom_text(table, XKB_ATOM_NONE) == NULL);
  136. assert(atom_intern(table, NULL, 0, false) == XKB_ATOM_NONE);
  137. atom1 = INTERN_LITERAL(table, "hello");
  138. assert(atom1 != XKB_ATOM_NONE);
  139. assert(atom1 == LOOKUP_LITERAL(table, "hello"));
  140. assert(streq(atom_text(table, atom1), "hello"));
  141. atom2 = atom_intern(table, "hello", 3, true);
  142. assert(atom2 != XKB_ATOM_NONE);
  143. assert(atom1 != atom2);
  144. assert(streq(atom_text(table, atom2), "hel"));
  145. assert(LOOKUP_LITERAL(table, "hel") == atom2);
  146. assert(LOOKUP_LITERAL(table, "hell") == XKB_ATOM_NONE);
  147. assert(LOOKUP_LITERAL(table, "hello") == atom1);
  148. atom3 = atom_intern(table, "", 0, true);
  149. assert(atom3 != XKB_ATOM_NONE);
  150. assert(LOOKUP_LITERAL(table, "") == atom3);
  151. atom_table_free(table);
  152. test_random_strings();
  153. return 0;
  154. }