test_conscache.c 11 KB

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