123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340 |
- /* Copyright (c) 2017-2018, The Tor Project, Inc. */
- /* See LICENSE for licensing information */
- #include "core/or/or.h"
- #include "app/config/config.h"
- #include "feature/dircache/conscache.h"
- #include "lib/encoding/confline.h"
- #include "test/test.h"
- #ifdef HAVE_UTIME_H
- #include <utime.h>
- #endif
- static void
- test_conscache_open_failure(void *arg)
- {
- (void) arg;
- /* Try opening a directory that doesn't exist and which we shouldn't be
- * able to create. */
- consensus_cache_t *cache = consensus_cache_open("a/b/c/d/e/f/g", 128);
- tt_ptr_op(cache, OP_EQ, NULL);
- done:
- ;
- }
- static void
- test_conscache_simple_usage(void *arg)
- {
- (void)arg;
- consensus_cache_entry_t *ent = NULL, *ent2 = NULL;
- /* Make a temporary datadir for these tests */
- char *ddir_fname = tor_strdup(get_fname_rnd("datadir_cache"));
- tor_free(get_options_mutable()->CacheDirectory);
- get_options_mutable()->CacheDirectory = tor_strdup(ddir_fname);
- check_private_dir(ddir_fname, CPD_CREATE, NULL);
- consensus_cache_t *cache = consensus_cache_open("cons", 128);
- tt_assert(cache);
- /* Create object; make sure it exists. */
- config_line_t *labels = NULL;
- config_line_append(&labels, "Hello", "world");
- config_line_append(&labels, "Adios", "planetas");
- ent = consensus_cache_add(cache,
- labels, (const uint8_t *)"A\0B\0C", 5);
- config_free_lines(labels);
- labels = NULL;
- tt_assert(ent);
- /* Make a second object */
- config_line_append(&labels, "Hello", "mundo");
- config_line_append(&labels, "Adios", "planets");
- ent2 = consensus_cache_add(cache,
- labels, (const uint8_t *)"xyzzy", 5);
- config_free_lines(labels);
- labels = NULL;
- tt_assert(ent2);
- tt_assert(! consensus_cache_entry_is_mapped(ent2));
- consensus_cache_entry_decref(ent2);
- ent2 = NULL;
- /* Check get_value */
- tt_ptr_op(NULL, OP_EQ, consensus_cache_entry_get_value(ent, "hebbo"));
- tt_str_op("world", OP_EQ, consensus_cache_entry_get_value(ent, "Hello"));
- /* Check find_first */
- ent2 = consensus_cache_find_first(cache, "Hello", "world!");
- tt_ptr_op(ent2, OP_EQ, NULL);
- ent2 = consensus_cache_find_first(cache, "Hello", "world");
- tt_ptr_op(ent2, OP_EQ, ent);
- ent2 = consensus_cache_find_first(cache, "Hello", "mundo");
- tt_ptr_op(ent2, OP_NE, ent);
- tt_assert(! consensus_cache_entry_is_mapped(ent));
- /* Check get_body */
- const uint8_t *bp = NULL;
- size_t sz = 0;
- int r = consensus_cache_entry_get_body(ent, &bp, &sz);
- tt_int_op(r, OP_EQ, 0);
- tt_u64_op(sz, OP_EQ, 5);
- tt_mem_op(bp, OP_EQ, "A\0B\0C", 5);
- tt_assert(consensus_cache_entry_is_mapped(ent));
- /* Free and re-create the cache, to rescan the directory. */
- consensus_cache_free(cache);
- consensus_cache_entry_decref(ent);
- cache = consensus_cache_open("cons", 128);
- /* Make sure the entry is still there */
- ent = consensus_cache_find_first(cache, "Hello", "mundo");
- tt_assert(ent);
- ent2 = consensus_cache_find_first(cache, "Adios", "planets");
- tt_ptr_op(ent, OP_EQ, ent2);
- consensus_cache_entry_incref(ent);
- tt_assert(! consensus_cache_entry_is_mapped(ent));
- r = consensus_cache_entry_get_body(ent, &bp, &sz);
- tt_int_op(r, OP_EQ, 0);
- tt_u64_op(sz, OP_EQ, 5);
- tt_mem_op(bp, OP_EQ, "xyzzy", 5);
- tt_assert(consensus_cache_entry_is_mapped(ent));
- /* There should be two entries total. */
- smartlist_t *entries = smartlist_new();
- consensus_cache_find_all(entries, cache, NULL, NULL);
- int n = smartlist_len(entries);
- smartlist_free(entries);
- tt_int_op(n, OP_EQ, 2);
- done:
- consensus_cache_entry_decref(ent);
- tor_free(ddir_fname);
- consensus_cache_free(cache);
- }
- static void
- test_conscache_cleanup(void *arg)
- {
- (void)arg;
- const int N = 20;
- consensus_cache_entry_t **ents =
- tor_calloc(N, sizeof(consensus_cache_entry_t*));
- /* Make a temporary datadir for these tests */
- char *ddir_fname = tor_strdup(get_fname_rnd("datadir_cache"));
- tor_free(get_options_mutable()->CacheDirectory);
- get_options_mutable()->CacheDirectory = tor_strdup(ddir_fname);
- check_private_dir(ddir_fname, CPD_CREATE, NULL);
- consensus_cache_t *cache = consensus_cache_open("cons", 128);
- tt_assert(cache);
- /* Create a bunch of entries. */
- int i;
- for (i = 0; i < N; ++i) {
- config_line_t *labels = NULL;
- char num[8];
- tor_snprintf(num, sizeof(num), "%d", i);
- config_line_append(&labels, "test-id", "cleanup");
- config_line_append(&labels, "index", num);
- size_t bodylen = i * 3;
- uint8_t *body = tor_malloc(bodylen);
- memset(body, i, bodylen);
- ents[i] = consensus_cache_add(cache, labels, body, bodylen);
- tor_free(body);
- config_free_lines(labels);
- tt_assert(ents[i]);
- /* We're still holding a reference to each entry at this point. */
- }
- /* Page all of the entries into RAM */
- for (i = 0; i < N; ++i) {
- const uint8_t *bp;
- size_t sz;
- tt_assert(! consensus_cache_entry_is_mapped(ents[i]));
- consensus_cache_entry_get_body(ents[i], &bp, &sz);
- tt_assert(consensus_cache_entry_is_mapped(ents[i]));
- }
- /* Mark some of the entries as deletable. */
- for (i = 7; i < N; i += 7) {
- consensus_cache_entry_mark_for_removal(ents[i]);
- tt_assert(consensus_cache_entry_is_mapped(ents[i]));
- }
- /* Mark some of the entries as aggressively unpaged. */
- for (i = 3; i < N; i += 3) {
- consensus_cache_entry_mark_for_aggressive_release(ents[i]);
- tt_assert(consensus_cache_entry_is_mapped(ents[i]));
- }
- /* Incref some of the entries again */
- for (i = 0; i < N; i += 2) {
- consensus_cache_entry_incref(ents[i]);
- }
- /* Now we're going to decref everything. We do so at a specific time. I'm
- * picking the moment when I was writing this test, at 2017-04-05 12:16:48
- * UTC. */
- const time_t example_time = 1491394608;
- update_approx_time(example_time);
- for (i = 0; i < N; ++i) {
- consensus_cache_entry_decref(ents[i]);
- if (i % 2) {
- ents[i] = NULL; /* We're no longer holding any reference here. */
- }
- }
- /* At this point, the aggressively-released items with refcount 1 should
- * be unmapped. Nothing should be deleted. */
- consensus_cache_entry_t *e_tmp;
- e_tmp = consensus_cache_find_first(cache, "index", "3");
- tt_assert(e_tmp);
- tt_assert(! consensus_cache_entry_is_mapped(e_tmp));
- e_tmp = consensus_cache_find_first(cache, "index", "5");
- tt_assert(e_tmp);
- tt_assert(consensus_cache_entry_is_mapped(e_tmp));
- e_tmp = consensus_cache_find_first(cache, "index", "6");
- tt_assert(e_tmp);
- tt_assert(consensus_cache_entry_is_mapped(e_tmp));
- e_tmp = consensus_cache_find_first(cache, "index", "7");
- tt_ptr_op(e_tmp, OP_EQ, NULL); // not found because pending deletion.
- /* Delete the pending-deletion items. */
- consensus_cache_delete_pending(cache, 0);
- {
- smartlist_t *entries = smartlist_new();
- consensus_cache_find_all(entries, cache, NULL, NULL);
- int n = smartlist_len(entries);
- smartlist_free(entries);
- tt_int_op(n, OP_EQ, 20 - 2); /* 1 entry was deleted; 1 is not-found. */
- }
- e_tmp = consensus_cache_find_first(cache, "index", "7"); // refcnt == 1...
- tt_ptr_op(e_tmp, OP_EQ, NULL); // so deleted.
- e_tmp = consensus_cache_find_first(cache, "index", "14"); // refcnt == 2
- tt_ptr_op(e_tmp, OP_EQ, NULL); // not deleted; but not found.
- /* Now do lazy unmapping. */
- // should do nothing.
- consensus_cache_unmap_lazy(cache, example_time - 10);
- e_tmp = consensus_cache_find_first(cache, "index", "11");
- tt_assert(e_tmp);
- tt_assert(consensus_cache_entry_is_mapped(e_tmp));
- // should actually unmap
- consensus_cache_unmap_lazy(cache, example_time + 10);
- e_tmp = consensus_cache_find_first(cache, "index", "11");
- tt_assert(e_tmp);
- tt_assert(! consensus_cache_entry_is_mapped(e_tmp));
- // This one will still be mapped, since it has a reference.
- e_tmp = consensus_cache_find_first(cache, "index", "16");
- tt_assert(e_tmp);
- tt_assert(consensus_cache_entry_is_mapped(e_tmp));
- for (i = 0; i < N; ++i) {
- consensus_cache_entry_decref(ents[i]);
- ents[i] = NULL;
- }
- /* Free and re-create the cache, to rescan the directory. Make sure the
- * deleted thing is still deleted, along with the other deleted thing. */
- consensus_cache_free(cache);
- cache = consensus_cache_open("cons", 128);
- {
- smartlist_t *entries = smartlist_new();
- consensus_cache_find_all(entries, cache, NULL, NULL);
- int n = smartlist_len(entries);
- smartlist_free(entries);
- tt_int_op(n, OP_EQ, 18);
- }
- done:
- for (i = 0; i < N; ++i) {
- consensus_cache_entry_decref(ents[i]);
- }
- tor_free(ents);
- tor_free(ddir_fname);
- consensus_cache_free(cache);
- }
- static void
- test_conscache_filter(void *arg)
- {
- (void)arg;
- const int N = 30;
- smartlist_t *lst = NULL;
- /* Make a temporary datadir for these tests */
- char *ddir_fname = tor_strdup(get_fname_rnd("datadir_cache"));
- tor_free(get_options_mutable()->CacheDirectory);
- get_options_mutable()->CacheDirectory = tor_strdup(ddir_fname);
- check_private_dir(ddir_fname, CPD_CREATE, NULL);
- consensus_cache_t *cache = consensus_cache_open("cons", 128);
- tt_assert(cache);
- /* Create a bunch of entries with different labels */
- int i;
- for (i = 0; i < N; ++i) {
- config_line_t *labels = NULL;
- char num[8];
- tor_snprintf(num, sizeof(num), "%d", i);
- config_line_append(&labels, "test-id", "filter");
- config_line_append(&labels, "index", num);
- tor_snprintf(num, sizeof(num), "%d", i % 3);
- config_line_append(&labels, "mod3", num);
- tor_snprintf(num, sizeof(num), "%d", i % 5);
- config_line_append(&labels, "mod5", num);
- size_t bodylen = i * 3;
- uint8_t *body = tor_malloc(bodylen);
- memset(body, i, bodylen);
- consensus_cache_entry_t *ent =
- consensus_cache_add(cache, labels, body, bodylen);
- tor_free(body);
- config_free_lines(labels);
- tt_assert(ent);
- consensus_cache_entry_decref(ent);
- }
- lst = smartlist_new();
- /* Find nothing. */
- consensus_cache_find_all(lst, cache, "mod5", "5");
- tt_int_op(smartlist_len(lst), OP_EQ, 0);
- /* Find everything. */
- consensus_cache_find_all(lst, cache, "test-id", "filter");
- tt_int_op(smartlist_len(lst), OP_EQ, N);
- /* Now filter to find the entries that have i%3 == 1 */
- consensus_cache_filter_list(lst, "mod3", "1");
- tt_int_op(smartlist_len(lst), OP_EQ, 10);
- /* Now filter to find the entries that also have i%5 == 3 */
- consensus_cache_filter_list(lst, "mod5", "3");
- tt_int_op(smartlist_len(lst), OP_EQ, 2);
- /* So now we have those entries for which i%15 == 13. */
- consensus_cache_entry_t *ent1 = smartlist_get(lst, 0);
- consensus_cache_entry_t *ent2 = smartlist_get(lst, 1);
- const char *idx1 = consensus_cache_entry_get_value(ent1, "index");
- const char *idx2 = consensus_cache_entry_get_value(ent2, "index");
- tt_assert( (!strcmp(idx1, "28") && !strcmp(idx2, "13")) ||
- (!strcmp(idx1, "13") && !strcmp(idx2, "28")) );
- done:
- tor_free(ddir_fname);
- consensus_cache_free(cache);
- smartlist_free(lst);
- }
- #define ENT(name) \
- { #name, test_conscache_ ## name, TT_FORK, NULL, NULL }
- struct testcase_t conscache_tests[] = {
- ENT(open_failure),
- ENT(simple_usage),
- ENT(cleanup),
- ENT(filter),
- END_OF_TESTCASES
- };
|