/*
* testcode/unitneg.c - unit test for negative cache routines.
*
* Copyright (c) 2008, NLnet Labs. All rights reserved.
*
* This software is open source.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* Neither the name of the NLNET LABS nor the names of its contributors may
* be used to endorse or promote products derived from this software without
* specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
* TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
/**
* \file
* Calls negative cache unit tests. Exits with code 1 on a failure.
*/
/** get static pointer to random zone name */
static char* get_random_zone(void)
{
static char zname[36];
int labels = random() % 3;
int i;
char* p = zname;
int labnum;
/** get static pointer to random data names from and to */
static void get_random_data(char** fromp, char** top, char* zname)
{
static char buf1[256], buf2[256];
int type;
int lab1, lab2;
int labnum1[10], labnum2[10];
int i;
char* p;
memset(labnum1, 0, sizeof(int)*10);
memset(labnum2, 0, sizeof(int)*10);
/* pick a random zone */
walk = rbtree_first(&neg->tree); /* first highest parent, big count */
z = (struct val_neg_zone*)walk;
n = random() % (int)(z->count);
if(negverbose)
printf("neg stress delete zone %d\n", n);
i=0;
walk = rbtree_first(&neg->tree);
z = (struct val_neg_zone*)walk;
while(i!=n+1 && walk && walk != RBTREE_NULL && !z->in_use) {
walk = rbtree_next(walk);
z = (struct val_neg_zone*)walk;
if(z->in_use)
i++;
}
if(!walk || walk == RBTREE_NULL) {
lock_basic_unlock(&neg->lock);
return;
}
if(!z->in_use) {
lock_basic_unlock(&neg->lock);
return;
}
if(negverbose)
log_nametypeclass(0, "delete zone", z->name, 0, 0);
/* pick a random nsec item. - that is in use */
walk = rbtree_first(&z->tree); /* first is highest parent */
d = (struct val_neg_data*)walk;
n = random() % (int)(d->count);
if(negverbose)
printf("neg stress delete item %d\n", n);
i=0;
walk = rbtree_first(&z->tree);
d = (struct val_neg_data*)walk;
while(i!=n+1 && walk && walk != RBTREE_NULL && !d->in_use) {
walk = rbtree_next(walk);
d = (struct val_neg_data*)walk;
if(d->in_use)
i++;
}
if(!walk || walk == RBTREE_NULL) {
lock_basic_unlock(&neg->lock);
return;
}
if(d->in_use) {
if(negverbose)
log_nametypeclass(0, "neg delete item:", d->name, 0, 0);
neg_delete_data(neg, d);
}
lock_basic_unlock(&neg->lock);
}
/** sum up the zone trees */
static size_t sumtrees_all(struct val_neg_cache* neg)
{
size_t res = 0;
struct val_neg_zone* z;
RBTREE_FOR(z, struct val_neg_zone*, &neg->tree) {
res += z->tree.count;
}
return res;
}
/** sum up the zone trees, in_use only */
static size_t sumtrees_inuse(struct val_neg_cache* neg)
{
size_t res = 0;
struct val_neg_zone* z;
struct val_neg_data* d;
RBTREE_FOR(z, struct val_neg_zone*, &neg->tree) {
/* get count of highest parent for num in use */
d = (struct val_neg_data*)rbtree_first(&z->tree);
if(d && (rbnode_type*)d!=RBTREE_NULL)
res += d->count;
}
return res;
}
/** check if lru is still valid */
static void check_lru(struct val_neg_cache* neg)
{
struct val_neg_data* p, *np;
size_t num = 0;
size_t inuse;
p = neg->first;
while(p) {
if(!p->prev) {
unit_assert(neg->first == p);
}
np = p->next;
if(np) {
unit_assert(np->prev == p);
} else {
unit_assert(neg->last == p);
}
num++;
p = np;
}
inuse = sumtrees_inuse(neg);
if(negverbose)
printf("num lru %d, inuse %d, all %d\n",
(int)num, (int)sumtrees_inuse(neg),
(int)sumtrees_all(neg));
unit_assert( num == inuse);
unit_assert( inuse <= sumtrees_all(neg));
}
/** sum up number of items inuse in subtree */
static int sum_subtree_inuse(struct val_neg_zone* zone,
struct val_neg_data* data)
{
struct val_neg_data* d;
int num = 0;
RBTREE_FOR(d, struct val_neg_data*, &zone->tree) {
if(dname_subdomain_c(d->name, data->name)) {
if(d->in_use)
num++;
}
}
return num;
}
/** sum up number of items inuse in subtree */
static int sum_zone_subtree_inuse(struct val_neg_cache* neg,
struct val_neg_zone* zone)
{
struct val_neg_zone* z;
int num = 0;
RBTREE_FOR(z, struct val_neg_zone*, &neg->tree) {
if(dname_subdomain_c(z->name, zone->name)) {
if(z->in_use)
num++;
}
}
return num;
}
/** check point in data tree */
static void check_data(struct val_neg_zone* zone, struct val_neg_data* data)
{
unit_assert(data->count > 0);
if(data->parent) {
unit_assert(data->parent->count >= data->count);
if(data->parent->in_use) {
unit_assert(data->parent->count > data->count);
}
unit_assert(data->parent->labs == data->labs-1);
/* and parent must be one label shorter */
unit_assert(data->name[0] == (data->len-data->parent->len-1));
unit_assert(query_dname_compare(data->name + data->name[0]+1,
data->parent->name) == 0);
} else {
/* must be apex */
unit_assert(dname_is_root(data->name));
}
/* tree property: */
unit_assert(data->count == sum_subtree_inuse(zone, data));
}
/** check if tree of data in zone is valid */
static void checkzonetree(struct val_neg_zone* zone)
{
struct val_neg_data* d;
/* check all data in tree */
RBTREE_FOR(d, struct val_neg_data*, &zone->tree) {
check_data(zone, d);
}
}
/** check if negative cache is still valid */
static void check_zone_invariants(struct val_neg_cache* neg,
struct val_neg_zone* zone)
{
unit_assert(zone->nsec3_hash == 0);
unit_assert(zone->tree.cmp == &val_neg_data_compare);
unit_assert(zone->count != 0);