test_conscache.c 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340
  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_open_failure(void *arg)
  12. {
  13. (void) arg;
  14. /* Try opening a directory that doesn't exist and which we shouldn't be
  15. * able to create. */
  16. consensus_cache_t *cache = consensus_cache_open("a/b/c/d/e/f/g", 128);
  17. tt_ptr_op(cache, OP_EQ, NULL);
  18. done:
  19. ;
  20. }
  21. static void
  22. test_conscache_simple_usage(void *arg)
  23. {
  24. (void)arg;
  25. consensus_cache_entry_t *ent = NULL, *ent2 = NULL;
  26. /* Make a temporary datadir for these tests */
  27. char *ddir_fname = tor_strdup(get_fname_rnd("datadir_cache"));
  28. tor_free(get_options_mutable()->CacheDirectory);
  29. get_options_mutable()->CacheDirectory = tor_strdup(ddir_fname);
  30. check_private_dir(ddir_fname, CPD_CREATE, NULL);
  31. consensus_cache_t *cache = consensus_cache_open("cons", 128);
  32. tt_assert(cache);
  33. /* Create object; make sure it exists. */
  34. config_line_t *labels = NULL;
  35. config_line_append(&labels, "Hello", "world");
  36. config_line_append(&labels, "Adios", "planetas");
  37. ent = consensus_cache_add(cache,
  38. labels, (const uint8_t *)"A\0B\0C", 5);
  39. config_free_lines(labels);
  40. labels = NULL;
  41. tt_assert(ent);
  42. /* Make a second object */
  43. config_line_append(&labels, "Hello", "mundo");
  44. config_line_append(&labels, "Adios", "planets");
  45. ent2 = consensus_cache_add(cache,
  46. labels, (const uint8_t *)"xyzzy", 5);
  47. config_free_lines(labels);
  48. labels = NULL;
  49. tt_assert(ent2);
  50. tt_assert(! consensus_cache_entry_is_mapped(ent2));
  51. consensus_cache_entry_decref(ent2);
  52. ent2 = NULL;
  53. /* Check get_value */
  54. tt_ptr_op(NULL, OP_EQ, consensus_cache_entry_get_value(ent, "hebbo"));
  55. tt_str_op("world", OP_EQ, consensus_cache_entry_get_value(ent, "Hello"));
  56. /* Check find_first */
  57. ent2 = consensus_cache_find_first(cache, "Hello", "world!");
  58. tt_ptr_op(ent2, OP_EQ, NULL);
  59. ent2 = consensus_cache_find_first(cache, "Hello", "world");
  60. tt_ptr_op(ent2, OP_EQ, ent);
  61. ent2 = consensus_cache_find_first(cache, "Hello", "mundo");
  62. tt_ptr_op(ent2, OP_NE, ent);
  63. tt_assert(! consensus_cache_entry_is_mapped(ent));
  64. /* Check get_body */
  65. const uint8_t *bp = NULL;
  66. size_t sz = 0;
  67. int r = consensus_cache_entry_get_body(ent, &bp, &sz);
  68. tt_int_op(r, OP_EQ, 0);
  69. tt_u64_op(sz, OP_EQ, 5);
  70. tt_mem_op(bp, OP_EQ, "A\0B\0C", 5);
  71. tt_assert(consensus_cache_entry_is_mapped(ent));
  72. /* Free and re-create the cache, to rescan the directory. */
  73. consensus_cache_free(cache);
  74. consensus_cache_entry_decref(ent);
  75. cache = consensus_cache_open("cons", 128);
  76. /* Make sure the entry is still there */
  77. ent = consensus_cache_find_first(cache, "Hello", "mundo");
  78. tt_assert(ent);
  79. ent2 = consensus_cache_find_first(cache, "Adios", "planets");
  80. tt_ptr_op(ent, OP_EQ, ent2);
  81. consensus_cache_entry_incref(ent);
  82. tt_assert(! consensus_cache_entry_is_mapped(ent));
  83. r = consensus_cache_entry_get_body(ent, &bp, &sz);
  84. tt_int_op(r, OP_EQ, 0);
  85. tt_u64_op(sz, OP_EQ, 5);
  86. tt_mem_op(bp, OP_EQ, "xyzzy", 5);
  87. tt_assert(consensus_cache_entry_is_mapped(ent));
  88. /* There should be two entries total. */
  89. smartlist_t *entries = smartlist_new();
  90. consensus_cache_find_all(entries, cache, NULL, NULL);
  91. int n = smartlist_len(entries);
  92. smartlist_free(entries);
  93. tt_int_op(n, OP_EQ, 2);
  94. done:
  95. consensus_cache_entry_decref(ent);
  96. tor_free(ddir_fname);
  97. consensus_cache_free(cache);
  98. }
  99. static void
  100. test_conscache_cleanup(void *arg)
  101. {
  102. (void)arg;
  103. const int N = 20;
  104. consensus_cache_entry_t **ents =
  105. tor_calloc(N, sizeof(consensus_cache_entry_t*));
  106. /* Make a temporary datadir for these tests */
  107. char *ddir_fname = tor_strdup(get_fname_rnd("datadir_cache"));
  108. tor_free(get_options_mutable()->CacheDirectory);
  109. get_options_mutable()->CacheDirectory = tor_strdup(ddir_fname);
  110. check_private_dir(ddir_fname, CPD_CREATE, NULL);
  111. consensus_cache_t *cache = consensus_cache_open("cons", 128);
  112. tt_assert(cache);
  113. /* Create a bunch of entries. */
  114. int i;
  115. for (i = 0; i < N; ++i) {
  116. config_line_t *labels = NULL;
  117. char num[8];
  118. tor_snprintf(num, sizeof(num), "%d", i);
  119. config_line_append(&labels, "test-id", "cleanup");
  120. config_line_append(&labels, "index", num);
  121. size_t bodylen = i * 3;
  122. uint8_t *body = tor_malloc(bodylen);
  123. memset(body, i, bodylen);
  124. ents[i] = consensus_cache_add(cache, labels, body, bodylen);
  125. tor_free(body);
  126. config_free_lines(labels);
  127. tt_assert(ents[i]);
  128. /* We're still holding a reference to each entry at this point. */
  129. }
  130. /* Page all of the entries into RAM */
  131. for (i = 0; i < N; ++i) {
  132. const uint8_t *bp;
  133. size_t sz;
  134. tt_assert(! consensus_cache_entry_is_mapped(ents[i]));
  135. consensus_cache_entry_get_body(ents[i], &bp, &sz);
  136. tt_assert(consensus_cache_entry_is_mapped(ents[i]));
  137. }
  138. /* Mark some of the entries as deletable. */
  139. for (i = 7; i < N; i += 7) {
  140. consensus_cache_entry_mark_for_removal(ents[i]);
  141. tt_assert(consensus_cache_entry_is_mapped(ents[i]));
  142. }
  143. /* Mark some of the entries as aggressively unpaged. */
  144. for (i = 3; i < N; i += 3) {
  145. consensus_cache_entry_mark_for_aggressive_release(ents[i]);
  146. tt_assert(consensus_cache_entry_is_mapped(ents[i]));
  147. }
  148. /* Incref some of the entries again */
  149. for (i = 0; i < N; i += 2) {
  150. consensus_cache_entry_incref(ents[i]);
  151. }
  152. /* Now we're going to decref everything. We do so at a specific time. I'm
  153. * picking the moment when I was writing this test, at 2017-04-05 12:16:48
  154. * UTC. */
  155. const time_t example_time = 1491394608;
  156. update_approx_time(example_time);
  157. for (i = 0; i < N; ++i) {
  158. consensus_cache_entry_decref(ents[i]);
  159. if (i % 2) {
  160. ents[i] = NULL; /* We're no longer holding any reference here. */
  161. }
  162. }
  163. /* At this point, the aggressively-released items with refcount 1 should
  164. * be unmapped. Nothing should be deleted. */
  165. consensus_cache_entry_t *e_tmp;
  166. e_tmp = consensus_cache_find_first(cache, "index", "3");
  167. tt_assert(e_tmp);
  168. tt_assert(! consensus_cache_entry_is_mapped(e_tmp));
  169. e_tmp = consensus_cache_find_first(cache, "index", "5");
  170. tt_assert(e_tmp);
  171. tt_assert(consensus_cache_entry_is_mapped(e_tmp));
  172. e_tmp = consensus_cache_find_first(cache, "index", "6");
  173. tt_assert(e_tmp);
  174. tt_assert(consensus_cache_entry_is_mapped(e_tmp));
  175. e_tmp = consensus_cache_find_first(cache, "index", "7");
  176. tt_ptr_op(e_tmp, OP_EQ, NULL); // not found because pending deletion.
  177. /* Delete the pending-deletion items. */
  178. consensus_cache_delete_pending(cache, 0);
  179. {
  180. smartlist_t *entries = smartlist_new();
  181. consensus_cache_find_all(entries, cache, NULL, NULL);
  182. int n = smartlist_len(entries);
  183. smartlist_free(entries);
  184. tt_int_op(n, OP_EQ, 20 - 2); /* 1 entry was deleted; 1 is not-found. */
  185. }
  186. e_tmp = consensus_cache_find_first(cache, "index", "7"); // refcnt == 1...
  187. tt_ptr_op(e_tmp, OP_EQ, NULL); // so deleted.
  188. e_tmp = consensus_cache_find_first(cache, "index", "14"); // refcnt == 2
  189. tt_ptr_op(e_tmp, OP_EQ, NULL); // not deleted; but not found.
  190. /* Now do lazy unmapping. */
  191. // should do nothing.
  192. consensus_cache_unmap_lazy(cache, example_time - 10);
  193. e_tmp = consensus_cache_find_first(cache, "index", "11");
  194. tt_assert(e_tmp);
  195. tt_assert(consensus_cache_entry_is_mapped(e_tmp));
  196. // should actually unmap
  197. consensus_cache_unmap_lazy(cache, example_time + 10);
  198. e_tmp = consensus_cache_find_first(cache, "index", "11");
  199. tt_assert(e_tmp);
  200. tt_assert(! consensus_cache_entry_is_mapped(e_tmp));
  201. // This one will still be mapped, since it has a reference.
  202. e_tmp = consensus_cache_find_first(cache, "index", "16");
  203. tt_assert(e_tmp);
  204. tt_assert(consensus_cache_entry_is_mapped(e_tmp));
  205. for (i = 0; i < N; ++i) {
  206. consensus_cache_entry_decref(ents[i]);
  207. ents[i] = NULL;
  208. }
  209. /* Free and re-create the cache, to rescan the directory. Make sure the
  210. * deleted thing is still deleted, along with the other deleted thing. */
  211. consensus_cache_free(cache);
  212. cache = consensus_cache_open("cons", 128);
  213. {
  214. smartlist_t *entries = smartlist_new();
  215. consensus_cache_find_all(entries, cache, NULL, NULL);
  216. int n = smartlist_len(entries);
  217. smartlist_free(entries);
  218. tt_int_op(n, OP_EQ, 18);
  219. }
  220. done:
  221. for (i = 0; i < N; ++i) {
  222. consensus_cache_entry_decref(ents[i]);
  223. }
  224. tor_free(ents);
  225. tor_free(ddir_fname);
  226. consensus_cache_free(cache);
  227. }
  228. static void
  229. test_conscache_filter(void *arg)
  230. {
  231. (void)arg;
  232. const int N = 30;
  233. smartlist_t *lst = NULL;
  234. /* Make a temporary datadir for these tests */
  235. char *ddir_fname = tor_strdup(get_fname_rnd("datadir_cache"));
  236. tor_free(get_options_mutable()->CacheDirectory);
  237. get_options_mutable()->CacheDirectory = tor_strdup(ddir_fname);
  238. check_private_dir(ddir_fname, CPD_CREATE, NULL);
  239. consensus_cache_t *cache = consensus_cache_open("cons", 128);
  240. tt_assert(cache);
  241. /* Create a bunch of entries with different labels */
  242. int i;
  243. for (i = 0; i < N; ++i) {
  244. config_line_t *labels = NULL;
  245. char num[8];
  246. tor_snprintf(num, sizeof(num), "%d", i);
  247. config_line_append(&labels, "test-id", "filter");
  248. config_line_append(&labels, "index", num);
  249. tor_snprintf(num, sizeof(num), "%d", i % 3);
  250. config_line_append(&labels, "mod3", num);
  251. tor_snprintf(num, sizeof(num), "%d", i % 5);
  252. config_line_append(&labels, "mod5", num);
  253. size_t bodylen = i * 3;
  254. uint8_t *body = tor_malloc(bodylen);
  255. memset(body, i, bodylen);
  256. consensus_cache_entry_t *ent =
  257. consensus_cache_add(cache, labels, body, bodylen);
  258. tor_free(body);
  259. config_free_lines(labels);
  260. tt_assert(ent);
  261. consensus_cache_entry_decref(ent);
  262. }
  263. lst = smartlist_new();
  264. /* Find nothing. */
  265. consensus_cache_find_all(lst, cache, "mod5", "5");
  266. tt_int_op(smartlist_len(lst), OP_EQ, 0);
  267. /* Find everything. */
  268. consensus_cache_find_all(lst, cache, "test-id", "filter");
  269. tt_int_op(smartlist_len(lst), OP_EQ, N);
  270. /* Now filter to find the entries that have i%3 == 1 */
  271. consensus_cache_filter_list(lst, "mod3", "1");
  272. tt_int_op(smartlist_len(lst), OP_EQ, 10);
  273. /* Now filter to find the entries that also have i%5 == 3 */
  274. consensus_cache_filter_list(lst, "mod5", "3");
  275. tt_int_op(smartlist_len(lst), OP_EQ, 2);
  276. /* So now we have those entries for which i%15 == 13. */
  277. consensus_cache_entry_t *ent1 = smartlist_get(lst, 0);
  278. consensus_cache_entry_t *ent2 = smartlist_get(lst, 1);
  279. const char *idx1 = consensus_cache_entry_get_value(ent1, "index");
  280. const char *idx2 = consensus_cache_entry_get_value(ent2, "index");
  281. tt_assert( (!strcmp(idx1, "28") && !strcmp(idx2, "13")) ||
  282. (!strcmp(idx1, "13") && !strcmp(idx2, "28")) );
  283. done:
  284. tor_free(ddir_fname);
  285. consensus_cache_free(cache);
  286. smartlist_free(lst);
  287. }
  288. #define ENT(name) \
  289. { #name, test_conscache_ ## name, TT_FORK, NULL, NULL }
  290. struct testcase_t conscache_tests[] = {
  291. ENT(open_failure),
  292. ENT(simple_usage),
  293. ENT(cleanup),
  294. ENT(filter),
  295. END_OF_TESTCASES
  296. };