test_conscache.c 8.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257
  1. /* Copyright (c) 2017, The Tor Project, Inc. */
  2. /* See LICENSE for licensing information */
  3. #include "or.h"
  4. #include "config.h"
  5. #include "conscache.h"
  6. #include "test.h"
  7. #ifdef HAVE_UTIME_H
  8. #include <utime.h>
  9. #endif
  10. static void
  11. test_conscache_simple_usage(void *arg)
  12. {
  13. (void)arg;
  14. consensus_cache_entry_t *ent = NULL, *ent2 = NULL;
  15. /* Make a temporary datadir for these tests */
  16. char *ddir_fname = tor_strdup(get_fname_rnd("datadir_cache"));
  17. tor_free(get_options_mutable()->DataDirectory);
  18. get_options_mutable()->DataDirectory = tor_strdup(ddir_fname);
  19. check_private_dir(ddir_fname, CPD_CREATE, NULL);
  20. consensus_cache_t *cache = consensus_cache_open("cons", 128);
  21. tt_assert(cache);
  22. /* Create object; make sure it exists. */
  23. config_line_t *labels = NULL;
  24. config_line_append(&labels, "Hello", "world");
  25. config_line_append(&labels, "Adios", "planetas");
  26. ent = consensus_cache_add(cache,
  27. labels, (const uint8_t *)"A\0B\0C", 5);
  28. config_free_lines(labels);
  29. labels = NULL;
  30. tt_assert(ent);
  31. /* Make a second object */
  32. config_line_append(&labels, "Hello", "mundo");
  33. config_line_append(&labels, "Adios", "planets");
  34. ent2 = consensus_cache_add(cache,
  35. labels, (const uint8_t *)"xyzzy", 5);
  36. config_free_lines(labels);
  37. labels = NULL;
  38. tt_assert(ent2);
  39. tt_assert(! consensus_cache_entry_is_mapped(ent2));
  40. consensus_cache_entry_decref(ent2);
  41. ent2 = NULL;
  42. /* Check get_value */
  43. tt_ptr_op(NULL, OP_EQ, consensus_cache_entry_get_value(ent, "hebbo"));
  44. tt_str_op("world", OP_EQ, consensus_cache_entry_get_value(ent, "Hello"));
  45. /* Check find_first */
  46. ent2 = consensus_cache_find_first(cache, "Hello", "world!");
  47. tt_ptr_op(ent2, OP_EQ, NULL);
  48. ent2 = consensus_cache_find_first(cache, "Hello", "world");
  49. tt_ptr_op(ent2, OP_EQ, ent);
  50. ent2 = consensus_cache_find_first(cache, "Hello", "mundo");
  51. tt_ptr_op(ent2, OP_NE, ent);
  52. tt_assert(! consensus_cache_entry_is_mapped(ent));
  53. /* Check get_body */
  54. const uint8_t *bp = NULL;
  55. size_t sz = 0;
  56. int r = consensus_cache_entry_get_body(ent, &bp, &sz);
  57. tt_int_op(r, OP_EQ, 0);
  58. tt_u64_op(sz, OP_EQ, 5);
  59. tt_mem_op(bp, OP_EQ, "A\0B\0C", 5);
  60. tt_assert(consensus_cache_entry_is_mapped(ent));
  61. /* Free and re-create the cache, to rescan the directory. */
  62. consensus_cache_free(cache);
  63. consensus_cache_entry_decref(ent);
  64. cache = consensus_cache_open("cons", 128);
  65. /* Make sure the entry is still there */
  66. ent = consensus_cache_find_first(cache, "Hello", "mundo");
  67. tt_assert(ent);
  68. ent2 = consensus_cache_find_first(cache, "Adios", "planets");
  69. tt_ptr_op(ent, OP_EQ, ent2);
  70. consensus_cache_entry_incref(ent);
  71. tt_assert(! consensus_cache_entry_is_mapped(ent));
  72. r = consensus_cache_entry_get_body(ent, &bp, &sz);
  73. tt_int_op(r, OP_EQ, 0);
  74. tt_u64_op(sz, OP_EQ, 5);
  75. tt_mem_op(bp, OP_EQ, "xyzzy", 5);
  76. tt_assert(consensus_cache_entry_is_mapped(ent));
  77. /* There should be two entries total. */
  78. smartlist_t *entries = smartlist_new();
  79. consensus_cache_find_all(entries, cache, NULL, NULL);
  80. int n = smartlist_len(entries);
  81. smartlist_free(entries);
  82. tt_int_op(n, OP_EQ, 2);
  83. done:
  84. consensus_cache_entry_decref(ent);
  85. tor_free(ddir_fname);
  86. consensus_cache_free(cache);
  87. }
  88. static void
  89. test_conscache_cleanup(void *arg)
  90. {
  91. (void)arg;
  92. const int N = 20;
  93. consensus_cache_entry_t **ents =
  94. tor_calloc(N, sizeof(consensus_cache_entry_t*));
  95. /* Make a temporary datadir for these tests */
  96. char *ddir_fname = tor_strdup(get_fname_rnd("datadir_cache"));
  97. tor_free(get_options_mutable()->DataDirectory);
  98. get_options_mutable()->DataDirectory = tor_strdup(ddir_fname);
  99. check_private_dir(ddir_fname, CPD_CREATE, NULL);
  100. consensus_cache_t *cache = consensus_cache_open("cons", 128);
  101. tt_assert(cache);
  102. /* Create a bunch of entries. */
  103. int i;
  104. for (i = 0; i < N; ++i) {
  105. config_line_t *labels = NULL;
  106. char num[8];
  107. tor_snprintf(num, sizeof(num), "%d", i);
  108. config_line_append(&labels, "test-id", "cleanup");
  109. config_line_append(&labels, "index", num);
  110. size_t bodylen = i * 3;
  111. uint8_t *body = tor_malloc(bodylen);
  112. memset(body, i, bodylen);
  113. ents[i] = consensus_cache_add(cache, labels, body, bodylen);
  114. tor_free(body);
  115. config_free_lines(labels);
  116. tt_assert(ents[i]);
  117. /* We're still holding a reference to each entry at this point. */
  118. }
  119. /* Page all of the entries into RAM */
  120. for (i = 0; i < N; ++i) {
  121. const uint8_t *bp;
  122. size_t sz;
  123. tt_assert(! consensus_cache_entry_is_mapped(ents[i]));
  124. consensus_cache_entry_get_body(ents[i], &bp, &sz);
  125. tt_assert(consensus_cache_entry_is_mapped(ents[i]));
  126. }
  127. /* Mark some of the entries as deletable. */
  128. for (i = 7; i < N; i += 7) {
  129. consensus_cache_entry_mark_for_removal(ents[i]);
  130. tt_assert(consensus_cache_entry_is_mapped(ents[i]));
  131. }
  132. /* Mark some of the entries as aggressively unpaged. */
  133. for (i = 3; i < N; i += 3) {
  134. consensus_cache_entry_mark_for_aggressive_release(ents[i]);
  135. tt_assert(consensus_cache_entry_is_mapped(ents[i]));
  136. }
  137. /* Incref some of the entries again */
  138. for (i = 0; i < N; i += 2) {
  139. consensus_cache_entry_incref(ents[i]);
  140. }
  141. /* Now we're going to decref everything. We do so at a specific time. I'm
  142. * picking the moment when I was writing this test, at 2017-04-05 12:16:48
  143. * UTC. */
  144. const time_t example_time = 1491394608;
  145. update_approx_time(example_time);
  146. for (i = 0; i < N; ++i) {
  147. consensus_cache_entry_decref(ents[i]);
  148. if (i % 2) {
  149. ents[i] = NULL; /* We're no longer holding any reference here. */
  150. }
  151. }
  152. /* At this point, the aggressively-released items with refcount 1 should
  153. * be unmapped. Nothing should be deleted. */
  154. consensus_cache_entry_t *e_tmp;
  155. e_tmp = consensus_cache_find_first(cache, "index", "3");
  156. tt_assert(e_tmp);
  157. tt_assert(! consensus_cache_entry_is_mapped(e_tmp));
  158. e_tmp = consensus_cache_find_first(cache, "index", "5");
  159. tt_assert(e_tmp);
  160. tt_assert(consensus_cache_entry_is_mapped(e_tmp));
  161. e_tmp = consensus_cache_find_first(cache, "index", "6");
  162. tt_assert(e_tmp);
  163. tt_assert(consensus_cache_entry_is_mapped(e_tmp));
  164. e_tmp = consensus_cache_find_first(cache, "index", "7");
  165. tt_assert(e_tmp);
  166. tt_assert(consensus_cache_entry_is_mapped(e_tmp));
  167. /* Delete the pending-deletion items. */
  168. consensus_cache_delete_pending(cache);
  169. {
  170. smartlist_t *entries = smartlist_new();
  171. consensus_cache_find_all(entries, cache, NULL, NULL);
  172. int n = smartlist_len(entries);
  173. smartlist_free(entries);
  174. tt_int_op(n, OP_EQ, 20 - 1); /* 1 entry was deleted */
  175. }
  176. e_tmp = consensus_cache_find_first(cache, "index", "7"); // refcnt == 1...
  177. tt_assert(e_tmp == NULL); // so deleted.
  178. e_tmp = consensus_cache_find_first(cache, "index", "14"); // refcnt == 2
  179. tt_assert(e_tmp); // so, not deleted.
  180. /* Now do lazy unmapping. */
  181. // should do nothing.
  182. consensus_cache_unmap_lazy(cache, example_time - 10);
  183. e_tmp = consensus_cache_find_first(cache, "index", "11");
  184. tt_assert(e_tmp);
  185. tt_assert(consensus_cache_entry_is_mapped(e_tmp));
  186. // should actually unmap
  187. consensus_cache_unmap_lazy(cache, example_time + 10);
  188. e_tmp = consensus_cache_find_first(cache, "index", "11");
  189. tt_assert(e_tmp);
  190. tt_assert(! consensus_cache_entry_is_mapped(e_tmp));
  191. // This one will still be mapped, since it has a reference.
  192. e_tmp = consensus_cache_find_first(cache, "index", "16");
  193. tt_assert(e_tmp);
  194. tt_assert(consensus_cache_entry_is_mapped(e_tmp));
  195. for (i = 0; i < N; ++i) {
  196. consensus_cache_entry_decref(ents[i]);
  197. ents[i] = NULL;
  198. }
  199. /* Free and re-create the cache, to rescan the directory. Make sure the
  200. * deleted thing is still deleted, along with the other deleted thing. */
  201. consensus_cache_free(cache);
  202. cache = consensus_cache_open("cons", 128);
  203. {
  204. smartlist_t *entries = smartlist_new();
  205. consensus_cache_find_all(entries, cache, NULL, NULL);
  206. int n = smartlist_len(entries);
  207. smartlist_free(entries);
  208. tt_int_op(n, OP_EQ, 18);
  209. }
  210. done:
  211. for (i = 0; i < N; ++i) {
  212. consensus_cache_entry_decref(ents[i]);
  213. }
  214. tor_free(ents);
  215. tor_free(ddir_fname);
  216. consensus_cache_free(cache);
  217. }
  218. #define ENT(name) \
  219. { #name, test_conscache_ ## name, TT_FORK, NULL, NULL }
  220. struct testcase_t conscache_tests[] = {
  221. ENT(simple_usage),
  222. ENT(cleanup),
  223. END_OF_TESTCASES
  224. };