2017-04-04 19:51:34 +02:00
|
|
|
/* Copyright (c) 2017, The Tor Project, Inc. */
|
|
|
|
/* See LICENSE for licensing information */
|
|
|
|
|
|
|
|
#include "or.h"
|
|
|
|
#include "config.h"
|
|
|
|
#include "conscache.h"
|
|
|
|
#include "test.h"
|
|
|
|
|
|
|
|
#ifdef HAVE_UTIME_H
|
|
|
|
#include <utime.h>
|
|
|
|
#endif
|
|
|
|
|
2017-04-05 15:32:25 +02:00
|
|
|
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:
|
|
|
|
;
|
|
|
|
}
|
|
|
|
|
2017-04-04 19:51:34 +02:00
|
|
|
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()->DataDirectory);
|
|
|
|
get_options_mutable()->DataDirectory = 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);
|
|
|
|
}
|
|
|
|
|
2017-04-05 14:37:33 +02:00
|
|
|
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()->DataDirectory);
|
|
|
|
get_options_mutable()->DataDirectory = 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");
|
2017-04-11 21:50:06 +02:00
|
|
|
tt_assert(e_tmp == NULL); // not found because pending deletion.
|
2017-04-05 14:37:33 +02:00
|
|
|
|
|
|
|
/* Delete the pending-deletion items. */
|
|
|
|
consensus_cache_delete_pending(cache);
|
|
|
|
{
|
|
|
|
smartlist_t *entries = smartlist_new();
|
|
|
|
consensus_cache_find_all(entries, cache, NULL, NULL);
|
|
|
|
int n = smartlist_len(entries);
|
|
|
|
smartlist_free(entries);
|
2017-04-11 21:50:06 +02:00
|
|
|
tt_int_op(n, OP_EQ, 20 - 2); /* 1 entry was deleted; 1 is not-found. */
|
2017-04-05 14:37:33 +02:00
|
|
|
}
|
|
|
|
e_tmp = consensus_cache_find_first(cache, "index", "7"); // refcnt == 1...
|
|
|
|
tt_assert(e_tmp == NULL); // so deleted.
|
|
|
|
e_tmp = consensus_cache_find_first(cache, "index", "14"); // refcnt == 2
|
2017-04-11 21:50:06 +02:00
|
|
|
tt_assert(e_tmp == NULL); // not deleted; but not found.
|
2017-04-05 14:37:33 +02:00
|
|
|
|
|
|
|
/* 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);
|
|
|
|
}
|
|
|
|
|
2017-04-05 15:32:25 +02:00
|
|
|
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()->DataDirectory);
|
|
|
|
get_options_mutable()->DataDirectory = 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);
|
|
|
|
}
|
|
|
|
|
2017-04-04 19:51:34 +02:00
|
|
|
#define ENT(name) \
|
|
|
|
{ #name, test_conscache_ ## name, TT_FORK, NULL, NULL }
|
|
|
|
|
|
|
|
struct testcase_t conscache_tests[] = {
|
2017-04-05 15:32:25 +02:00
|
|
|
ENT(open_failure),
|
2017-04-04 19:51:34 +02:00
|
|
|
ENT(simple_usage),
|
2017-04-05 14:37:33 +02:00
|
|
|
ENT(cleanup),
|
2017-04-05 15:32:25 +02:00
|
|
|
ENT(filter),
|
2017-04-04 19:51:34 +02:00
|
|
|
END_OF_TESTCASES
|
|
|
|
};
|
|
|
|
|