test_cache.c 13.9 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
/*  Copyright (C) 2014 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

17
#include <libknot/internal/namedb/namedb_lmdb.h>
18
#include <ucw/mempool.h>
19 20

#include "tests/test.h"
21 22
#include "lib/cache.h"

23 24
#include <stdlib.h>
#include <time.h>
25
#include <dlfcn.h>
26 27

mm_ctx_t global_mm;
28
struct kr_cache_txn global_txn;
29 30
knot_rrset_t global_rr;
const char *global_env;
31 32 33 34 35 36
struct kr_cache_entry global_fake_ce;

#define NAMEDB_INTS 256
#define NAMEDB_DATA_SIZE (NAMEDB_INTS * sizeof(int))
uint8_t namedb_data[NAMEDB_DATA_SIZE];
namedb_val_t global_namedb_data = {namedb_data, NAMEDB_DATA_SIZE};
37
bool is_malloc_mocked = false;
38

39
#define CACHE_SIZE 10 * 4096
40 41
#define CACHE_TTL 10
#define CACHE_TIME 0
42

43
void * (*original_malloc) (size_t __size);
44 45
int (*original_knot_rdataset_add)(knot_rdataset_t *rrs, const knot_rdata_t *rr, mm_ctx_t *mm) = NULL;

46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65
void *malloc(size_t __size)
{
	Dl_info dli = {0};
	char insert_name[] = "kr_cache_insert";
	int err_mock = KNOT_EOK, insert_namelen = strlen(insert_name);

	if (original_malloc == NULL)
	{
		original_malloc = dlsym(RTLD_NEXT,"malloc");
		assert_non_null (malloc);
	}
	if (is_malloc_mocked)
	{
	    dladdr (__builtin_return_address (0), &dli);
	    if (dli.dli_sname && (strncmp(insert_name,dli.dli_sname,insert_namelen) == 0))
		    err_mock = mock();
	}
	return (err_mock != KNOT_EOK) ? NULL : original_malloc (__size);
}

66 67 68 69 70 71 72 73 74 75 76 77 78 79 80
int knot_rdataset_add(knot_rdataset_t *rrs, const knot_rdata_t *rr, mm_ctx_t *mm)
{
	int err, err_mock;
	err_mock = (int)mock();
	if (original_knot_rdataset_add == NULL)
	{
		original_knot_rdataset_add = dlsym(RTLD_NEXT,"knot_rdataset_add");
		assert_non_null (original_knot_rdataset_add);
	}	
	err = original_knot_rdataset_add(rrs, rr, mm);
	if (err_mock != KNOT_EOK)
	    err = err_mock;
	return err;
}

81
/* Simulate init failure */
82
static int fake_test_init(namedb_t **db_ptr, mm_ctx_t *mm, void *arg)
83
{
84 85
	static char db[1024];
	*db_ptr = db;
86
	return mock();
87 88
}

89 90 91 92 93
static void fake_test_deinit(namedb_t *db)
{
    return;
}

94
/* Simulate commit failure */
95
static int fake_test_commit(namedb_txn_t *txn)
96 97 98 99 100
{
	return KNOT_ESPACE;
}

/* Dummy abort */
101
static void fake_test_abort(namedb_txn_t *txn)
102 103 104 105
{
	return;
}

106
/* Stub for find */
107
static int fake_test_find(namedb_txn_t *txn, namedb_val_t *key, namedb_val_t *val, unsigned flags)
108
{
109 110
	val->data = &global_fake_ce;
	return KNOT_EOK;
111 112 113
}

/* Stub for insert */
114
static int fake_test_ins(namedb_txn_t *txn, namedb_val_t *key, namedb_val_t *val, unsigned flags)
115
{
116
	struct kr_cache_entry *header = val->data;
117
	int  res_cmp, err = (int)mock();
118 119 120 121
	if (val->len == sizeof (*header) + NAMEDB_DATA_SIZE)
	{
	    header = val->data;
	    res_cmp  = memcmp(header->data,namedb_data,NAMEDB_DATA_SIZE);
122 123 124 125 126 127 128
	    if (header->timestamp != global_fake_ce.timestamp ||
		header->ttl != global_fake_ce.ttl ||
		header->ttl != global_fake_ce.ttl ||
		res_cmp != 0)
	    {
		err = KNOT_EINVAL;
	    }
129 130
	}
	return err;
131 132
}

133 134 135 136 137
static int fake_test_txn_begin(namedb_t *db, namedb_txn_t *txn, unsigned flags)
{
    return KNOT_EOK;
}

138 139 140 141 142
/* Fake api */
static namedb_api_t *fake_namedb_lmdb_api(void)
{
	static namedb_api_t fake_api = {
		"lmdb_fake_api",
143 144
		fake_test_init, fake_test_deinit,
		fake_test_txn_begin, fake_test_commit, fake_test_abort,
145 146 147 148 149 150 151
		NULL, NULL, fake_test_find, fake_test_ins, NULL,
		NULL, NULL, NULL, NULL, NULL, NULL
	};

	return &fake_api;
}

152
/* Test cache open */
153
static int test_open(void **state, namedb_api_t *api)
154 155 156
{
	static struct kr_cache cache;
	struct namedb_lmdb_opts opts;
157
	memset(&cache, 0, sizeof(cache));
158 159 160 161
	memset(&opts, 0, sizeof(opts));
	opts.path = global_env;
	opts.mapsize = CACHE_SIZE;
	*state = &cache;
162 163 164 165 166 167 168
	return kr_cache_open(&cache, api, &opts, &global_mm);
}

/* fake api test open */
static void test_open_fake_api(void **state)
{
	bool res;
169 170
	will_return(fake_test_init,KNOT_EINVAL);
	assert_int_equal(test_open(state, fake_namedb_lmdb_api()),KNOT_EINVAL);
171 172 173 174 175 176 177 178 179 180 181 182
	will_return(fake_test_init,KNOT_EOK);
	assert_int_equal(test_open(state, fake_namedb_lmdb_api()),KNOT_EOK);
	res = (((struct kr_cache *)(*state))->api == fake_namedb_lmdb_api());
	assert_true(res);
}

static void test_open_conventional_api(void **state)
{
	bool res;
	assert_int_equal(test_open(state, NULL),KNOT_EOK);
	res = (((struct kr_cache *)(*state))->api == namedb_lmdb_api());
	assert_true(res);
183 184
}

185

186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207
/* Test cache teardown. */
static void test_close(void **state)
{
	kr_cache_close(*state);
	*state = NULL;
}

/* Open transaction */
static struct kr_cache_txn *test_txn_write(void **state)
{
	assert_non_null(*state);
	assert_int_equal(kr_cache_txn_begin(*state, &global_txn, 0), KNOT_EOK);
	return &global_txn;
}

/* Open transaction */
static struct kr_cache_txn *test_txn_rdonly(void **state)
{
	assert_non_null(*state);
	assert_int_equal(kr_cache_txn_begin(*state, &global_txn, NAMEDB_RDONLY), 0);
	return &global_txn;
}
208

209 210
/* test invalid parameters and some api failures */
static void test_fake_invalid (void **state)
211
{
212
	struct kr_cache_txn *txn = NULL;
213
	const namedb_api_t *api_saved = NULL;
214
	knot_dname_t dname[] = "";
215 216
	struct kr_cache_entry *entry = NULL;
	int ret = 0;
217

218
	assert_int_not_equal(kr_cache_txn_commit(txn), 0);
219
	txn = test_txn_write(state);
220 221 222
	assert_int_not_equal(kr_cache_txn_commit(txn), 0);
	ret = kr_cache_peek(txn, KR_CACHE_USER, dname, KNOT_RRTYPE_TSIG, &entry, 0);
	assert_int_equal(ret, 0);
223 224
	api_saved = txn->owner->api;
	txn->owner->api = NULL;
225
	ret = kr_cache_peek(txn, KR_CACHE_USER, dname, KNOT_RRTYPE_TSIG, &entry, 0);
226
	txn->owner->api = api_saved;
227
	assert_int_not_equal(ret, 0);
228 229
}

230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252
static void test_fake_insert(void **state)
{
	int ret_cache_ins_ok, ret_cache_lowmem, ret_cache_ins_inval;
	knot_dname_t dname[] = "";
	struct kr_cache_txn *txn = test_txn_write(state);
	test_randstr((char *)&global_fake_ce,sizeof(global_fake_ce));
	test_randstr((char *)namedb_data,NAMEDB_DATA_SIZE);

	is_malloc_mocked = true;
	will_return(malloc,KNOT_EINVAL);
	ret_cache_lowmem = kr_cache_insert(txn, KR_CACHE_USER, dname,
		KNOT_RRTYPE_TSIG, &global_fake_ce, global_namedb_data);
	is_malloc_mocked = false;
	will_return(fake_test_ins,KNOT_EOK);
	ret_cache_ins_ok = kr_cache_insert(txn, KR_CACHE_USER, dname,
		KNOT_RRTYPE_TSIG, &global_fake_ce, global_namedb_data);
	will_return(fake_test_ins,KNOT_EINVAL);
	ret_cache_ins_inval = kr_cache_insert(txn, KR_CACHE_USER, dname,
		KNOT_RRTYPE_TSIG, &global_fake_ce, global_namedb_data);
	assert_int_equal(ret_cache_lowmem, KNOT_ENOMEM);
	assert_int_equal(ret_cache_ins_ok, KNOT_EOK);
	assert_int_equal(ret_cache_ins_inval, KNOT_EINVAL);
}
253

254
/* Test invalid parameters and some api failures. */
255 256
static void test_invalid(void **state)
{
257
	knot_dname_t dname[] = "";
258
	uint32_t timestamp = CACHE_TIME;
259
	struct namedb_lmdb_opts opts;
260
	struct kr_cache_entry *entry = NULL;
261 262 263 264

	memset(&opts, 0, sizeof(opts));
	opts.path = global_env;
	opts.mapsize = CACHE_SIZE;
265

266 267
	knot_rrset_init_empty(&global_rr);

268
	assert_int_equal(kr_cache_open(NULL, NULL, &opts, &global_mm),KNOT_EINVAL);
269
	assert_int_not_equal(kr_cache_txn_begin(NULL, &global_txn, 0), 0);
270
	assert_int_not_equal(kr_cache_txn_begin(*state, NULL, 0), 0);
271
	assert_int_not_equal(kr_cache_txn_commit(NULL), 0);
272 273 274
	assert_int_not_equal(kr_cache_peek(NULL, KR_CACHE_USER, dname, KNOT_RRTYPE_TSIG, NULL, &timestamp), 0);
	assert_int_not_equal(kr_cache_peek(&global_txn, 0, dname, KNOT_RRTYPE_TSIG, &entry, &timestamp), 0);
	assert_int_not_equal(kr_cache_peek(&global_txn, KR_CACHE_USER, NULL, KNOT_RRTYPE_TSIG, &entry, &timestamp), 0);
275 276 277 278
	assert_int_not_equal(kr_cache_peek_rr(NULL, NULL, NULL), 0);
	assert_int_not_equal(kr_cache_peek_rr(&global_txn, NULL, NULL), 0);
	assert_int_not_equal(kr_cache_insert_rr(&global_txn, NULL, 0), 0);
	assert_int_not_equal(kr_cache_insert_rr(NULL, NULL, 0), 0);
279
	assert_int_equal(kr_cache_insert_rr(&global_txn, &global_rr, 0), 0);
280
	assert_int_not_equal(kr_cache_insert(NULL, KR_CACHE_USER, dname,
281
		KNOT_RRTYPE_TSIG, &global_fake_ce, global_namedb_data), 0);
282
	assert_int_not_equal(kr_cache_insert(&global_txn, 0, dname,
283 284 285
		KNOT_RRTYPE_TSIG, &global_fake_ce, global_namedb_data), 0);
	assert_int_not_equal(kr_cache_insert(&global_txn, KR_CACHE_USER, NULL,
		KNOT_RRTYPE_TSIG, &global_fake_ce, global_namedb_data), 0);
286
	assert_int_not_equal(kr_cache_insert(&global_txn, KR_CACHE_USER, dname,
287
		KNOT_RRTYPE_TSIG, NULL, global_namedb_data), 0);
288 289 290 291
	assert_int_not_equal(kr_cache_remove(&global_txn, 0, NULL, 0), 0);
	assert_int_not_equal(kr_cache_remove(&global_txn, KR_CACHE_RR, NULL, 0), 0);
	assert_int_not_equal(kr_cache_remove(NULL, 0, NULL, 0), 0);
	assert_int_not_equal(kr_cache_clear(NULL), 0);
292 293 294
}

/* Test cache write */
295
static void test_insert_rr(void **state)
296 297
{
	test_random_rr(&global_rr, CACHE_TTL);
298
	struct kr_cache_txn *txn = test_txn_write(state);
299
	int ret = kr_cache_insert_rr(txn, &global_rr, CACHE_TIME);
300 301 302 303 304 305 306 307
	if (ret == KNOT_EOK) {
		ret = kr_cache_txn_commit(txn);
	} else {
		kr_cache_txn_abort(txn);
	}
	assert_int_equal(ret, KNOT_EOK);
}

308 309 310 311 312 313 314 315 316
static void test_materialize(void **state)
{
	knot_rrset_t output_rr;
	knot_dname_t * owner_saved = global_rr.owner;
	bool res_cmp_ok_empty, res_cmp_fail_empty;
	bool res_cmp_ok, res_cmp_fail;

	global_rr.owner = NULL;
	knot_rrset_init(&output_rr, NULL, 0, 0);
317
	kr_cache_materialize(&output_rr, &global_rr, 0, &global_mm);
318 319 320 321 322 323 324 325 326
	res_cmp_ok_empty = knot_rrset_equal(&global_rr, &output_rr, KNOT_RRSET_COMPARE_HEADER);
	res_cmp_fail_empty = knot_rrset_equal(&global_rr, &output_rr, KNOT_RRSET_COMPARE_WHOLE);
	knot_rrset_clear(&output_rr,&global_mm);
	global_rr.owner = owner_saved;
	assert_true(res_cmp_ok_empty);
	assert_false(res_cmp_fail_empty);

	knot_rrset_init(&output_rr, NULL, 0, 0);
	will_return (knot_rdataset_add,KNOT_EOK);
327
	kr_cache_materialize(&output_rr, &global_rr, 0, &global_mm);
328 329 330 331 332 333
	res_cmp_ok = knot_rrset_equal(&global_rr, &output_rr, KNOT_RRSET_COMPARE_WHOLE);
	knot_rrset_clear(&output_rr,&global_mm);
	assert_true(res_cmp_ok);

	knot_rrset_init(&output_rr, NULL, 0, 0);
	will_return (knot_rdataset_add,KNOT_EINVAL);
334
	kr_cache_materialize(&output_rr, &global_rr, 0, &global_mm);
335 336 337 338 339
	res_cmp_fail = knot_rrset_equal(&global_rr, &output_rr, KNOT_RRSET_COMPARE_WHOLE);
	knot_rrset_clear(&output_rr,&global_mm);
	assert_false(res_cmp_fail);
}

340 341 342 343 344 345 346
/* Test cache read */
static void test_query(void **state)
{

	knot_rrset_t cache_rr;
	knot_rrset_init(&cache_rr, global_rr.owner, global_rr.type, global_rr.rclass);

347
	struct kr_cache_txn *txn = test_txn_rdonly(state);
348

349 350
	for (uint32_t timestamp = CACHE_TIME; timestamp < CACHE_TIME + CACHE_TTL; ++timestamp) {
		uint32_t drift = timestamp;
351
		int query_ret = kr_cache_peek_rr(txn, &cache_rr, &drift);
352 353 354 355 356 357
		bool rr_equal = knot_rrset_equal(&global_rr, &cache_rr, KNOT_RRSET_COMPARE_WHOLE);
		assert_int_equal(query_ret, KNOT_EOK);
		assert_true(rr_equal);
	}

	kr_cache_txn_abort(txn);
358 359 360 361 362
}

/* Test cache read (simulate aged entry) */
static void test_query_aged(void **state)
{
363
	uint32_t timestamp = CACHE_TIME + CACHE_TTL + 1;
364 365 366
	knot_rrset_t cache_rr;
	knot_rrset_init(&cache_rr, global_rr.owner, global_rr.type, global_rr.rclass);

367
	struct kr_cache_txn *txn = test_txn_rdonly(state);
368
	int ret = kr_cache_peek_rr(txn, &cache_rr, &timestamp);
369
	assert_int_equal(ret, kr_error(ESTALE));
370
	kr_cache_txn_abort(txn);
371 372
}

373 374 375 376 377 378 379
/* Test cache removal */
static void test_remove(void **state)
{
	uint32_t timestamp = CACHE_TIME;
	knot_rrset_t cache_rr;
	knot_rrset_init(&cache_rr, global_rr.owner, global_rr.type, global_rr.rclass);

380
	struct kr_cache_txn *txn = test_txn_write(state);
381
	int ret = kr_cache_remove(txn, KR_CACHE_RR, cache_rr.owner, cache_rr.type);
382
	assert_int_equal(ret, KNOT_EOK);
383
	ret = kr_cache_peek_rr(txn, &cache_rr, &timestamp);
384 385 386 387
	assert_int_equal(ret, KNOT_ENOENT);
	kr_cache_txn_commit(txn);
}

388 389 390
/* Test cache fill */
static void test_fill(void **state)
{
391
	struct kr_cache_txn *txn = test_txn_write(state);
392 393 394 395 396 397

	/* Fill with random values. */
	int ret = KNOT_EOK;
	for (unsigned i = 0; i < CACHE_SIZE; ++i) {
		knot_rrset_t rr;
		test_random_rr(&rr, CACHE_TTL);
398
		ret = kr_cache_insert_rr(txn, &rr, CACHE_TTL - 1);
399 400 401 402 403 404 405 406 407 408 409 410 411
		if (ret != KNOT_EOK) {
			break;
		}
		/* Intermediate commit */
		if (i % 10 == 0) {
			ret = kr_cache_txn_commit(txn);
			if (ret != KNOT_EOK) {
				txn = NULL;
				break;
			}
			txn = test_txn_write(state);
		}
	}
412 413 414

	/* Abort last transaction (if valid) */
	kr_cache_txn_abort(txn);
415 416 417 418 419 420 421 422

	/* Expect we run out of space */
	assert_int_equal(ret, KNOT_ESPACE);
}

/* Test cache clear */
static void test_clear(void **state)
{
423
	struct kr_cache_txn *txn = test_txn_write(state);
424 425 426 427 428 429 430 431 432 433 434 435
	int preempt_ret = kr_cache_clear(txn);
	int commit_ret = kr_cache_txn_commit(txn);
	assert_int_equal(preempt_ret, KNOT_EOK);
	assert_int_equal(commit_ret, KNOT_EOK);
}

int main(void)
{
	/* Initialize */
	test_mm_ctx_init(&global_mm);
	global_env = test_tmpdir_create();

436 437
	/* Invalid input */
	const UnitTest tests_bad[] = {
438 439
		group_test_setup(test_open_fake_api),
		unit_test(test_fake_invalid),
440
	        unit_test(test_fake_insert),
441 442 443 444
		group_test_teardown(test_close)
	};

	const UnitTest tests[] = {
445 446
		/* Invalid input */
	        unit_test(test_invalid),
447
	        /* Cache persistence */
448 449
	        group_test_setup(test_open_conventional_api),
	        unit_test(test_insert_rr),
450
	        unit_test(test_materialize),
451 452 453
	        unit_test(test_query),
	        /* Cache aging */
	        unit_test(test_query_aged),
454 455
	        /* Removal */
	        unit_test(test_remove),
456 457 458 459 460 461
	        /* Cache fill */
	        unit_test(test_fill),
	        unit_test(test_clear),
	        group_test_teardown(test_close)
	};

462 463 464 465
	int ret = run_group_tests(tests_bad);
	if (ret == 0) {
		ret = run_group_tests(tests);
	}
466 467 468 469 470

	/* Cleanup */
	test_tmpdir_remove(global_env);
	return ret;
}