blob: 946856198190438e14fb2490ab447ccf6b527bc2 [file]
/* $Id$
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to you under the Apache License, Version
* 2.0 (the "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/*
* test_hashtable.c
* tests the C implementation of etch hashtable.
* this hashtable implementation uses the public domain jenkins hashtable.
*/
#include "apr_time.h" /* some apr must be included first */
#include "etchthread.h"
#include <stdio.h>
#include <conio.h>
#include "cunit.h"
#include "basic.h"
#include "automated.h"
#include "etch_global.h"
#define TESTDATAPATH "./jenktest.txt"
#define MAXLINELEN 64
#define DATASIZE 4
#define DATAVALUE "foo"
#define INITIAL_TABLE_SIZE 64
int apr_setup(void);
int apr_teardown(void);
int this_setup();
int this_teardown();
apr_pool_t* g_apr_mempool;
const char* pooltag = "etchpool";
int g_is_automated_test, g_bytes_allocated;
#define IS_DEBUG_CONSOLE FALSE
/* - - - - - - - - - - - - - -
* unit test infrastructure
* - - - - - - - - - - - - - -
*/
int init_suite(void)
{
apr_setup();
etch_runtime_init(TRUE);
return this_setup();
}
int clean_suite(void)
{
this_teardown();
etch_runtime_cleanup(0,0); /* free memtable and cache etc */
apr_teardown();
return 0;
}
/*
* apr_setup()
* establish apache portable runtime environment
*/
int apr_setup(void)
{
int result = apr_initialize();
if (result == 0)
{ result = etch_apr_init();
g_apr_mempool = etch_apr_mempool;
}
if (g_apr_mempool)
apr_pool_tag(g_apr_mempool, pooltag);
else result = -1;
return result;
}
/*
* apr_teardown()
* free apache portable runtime environment
*/
int apr_teardown(void)
{
if (g_apr_mempool)
apr_pool_destroy(g_apr_mempool);
g_apr_mempool = NULL;
apr_terminate();
return 0;
}
int this_setup()
{
etch_apr_mempool = g_apr_mempool;
return 0;
}
int this_teardown()
{
return 0;
}
/**
* test_new_hashtable
* unit test for new_hashtable
*/
void test_new_hashtable(void)
{
/* new_hashtable always returns mimimal initial size if the input value is too small */
etch_hashtable* eht = new_hashtable(-1);
CU_ASSERT_PTR_NOT_NULL(eht);
CU_ASSERT_EQUAL(jenkins_size(eht->realtable, 0, 0), ETCH_DEFAULT_HASHTABLE_SIZE);
eht->destroy(eht);
/* the hashtable size will be modified to previous power of 2 */
eht = new_hashtable(63);
CU_ASSERT_PTR_NOT_NULL(eht);
CU_ASSERT_EQUAL(jenkins_size(eht->realtable, 0, 0), 32); /* size will be 3 2instead of 64 */
eht->destroy(eht);
/* good input case, the size is within boundary and power of 2 */
eht = new_hashtable(32);
CU_ASSERT_PTR_NOT_NULL(eht);
CU_ASSERT_EQUAL(jenkins_size(eht->realtable, 0, 0), 32);
eht->destroy(eht);
/* the maximum initial size is MAX_INITIAL_HASHTABLE_SIZE */
eht = new_hashtable(MAX_INITIAL_HASHTABLE_SIZE+1);
CU_ASSERT_PTR_NOT_NULL(eht);
CU_ASSERT_EQUAL(jenkins_size(eht->realtable, 0, 0), MAX_INITIAL_HASHTABLE_SIZE);
eht->destroy(eht);
g_bytes_allocated = etch_showmem(0, IS_DEBUG_CONSOLE); /* verify all memory freed */
CU_ASSERT_EQUAL(g_bytes_allocated, 0);
memtable_clear(); /* start fresh for next test */
}
/**
* test_new_etch_hashtable
* unit test for new_etch_hashtable
*/
void test_new_etch_hashtable(void)
{
/* new_etch_hashtable does not have meat in it */
etch_hashtable* eht = new_etch_hashtable();
CU_ASSERT_PTR_NOT_NULL_FATAL(eht);
CU_ASSERT_PTR_NULL(eht->realtable);
CU_ASSERT_PTR_NULL(eht->vtab);
etch_free(eht);
}
/**
* test_destroy_hashtable
* unit test for destroy_hashtable
*/
void test_destroy_hashtable(void)
{
wchar_t *key = NULL, *val = NULL;
/* try to delete key and value but nothing in it */
etch_hashtable* eht = new_etch_hashtable();
destroy_hashtable(eht, TRUE, TRUE);
/* negative test with NULL hash table */
destroy_hashtable(NULL, TRUE, TRUE);
/* TODO: add more cases to verify key/value deletion */
}
/**
* test_jenkins_insert
* unit test for jenkins_insert
*/
void test_jenkins_insert(void)
{
int keylen = 0;
wchar_t key[100];
etch_hashtable* eht = new_hashtable(16);
CU_ASSERT_PTR_NOT_NULL(eht);
CU_ASSERT_EQUAL(jenkins_size(eht->realtable, 0, 0), 16);
CU_ASSERT_EQUAL(jenkins_count(eht->realtable, 0, 0), 0);
swprintf(key, 100, L"key%d", 0);
keylen = (int)wcslen(key);
jenkins_insert(eht->realtable, key, keylen*2+2, L"val", 8, 0, 0);
CU_ASSERT_EQUAL(jenkins_size(eht->realtable, 0, 0), 16);
CU_ASSERT_EQUAL(jenkins_count(eht->realtable, 0, 0), 1);
/* insert the same key value pair */
jenkins_insert(eht->realtable, key, keylen*2+2, L"val", 8, 0, 0);
CU_ASSERT_EQUAL(jenkins_count(eht->realtable, 0, 0), 1);
/* insert the same key but different value */
jenkins_insert(eht->realtable, key, keylen*2+2, L"va1", 8, 0, 0);
CU_ASSERT_EQUAL(jenkins_count(eht->realtable, 0, 0), 1);
/* insert different key but the same value */
swprintf(key, 100, L"key%d", 1);
jenkins_insert(eht->realtable, key, keylen*2+2, L"val", 8, 0, 0);
CU_ASSERT_EQUAL(jenkins_count(eht->realtable, 0, 0), 2);
/* insert different key value pair */
swprintf(key, 100, L"key%d", 2);
jenkins_insert(eht->realtable, key, keylen*2+2, L"va1", 8, 0, 0);
CU_ASSERT_EQUAL(jenkins_count(eht->realtable, 0, 0), 3);
/* insert NULL key and value */
jenkins_insert(eht->realtable, NULL, 0, NULL, 0, 0, 0);
CU_ASSERT_EQUAL(jenkins_count(eht->realtable, 0, 0), 3);
eht->destroy(eht);
}
/**
* test_jenkins_find
* unit test for jenkins_find
*/
void test_jenkins_find(void)
{
int result = 0, keylen = 0, vallen = 0;
wchar_t* key1 = 0;
wchar_t* key2 = 0;
wchar_t* val1 = 0;
wchar_t* val2 = 0;
etch_hashitem* outentry;
etch_hashtable* eht = 0;
eht = new_hashtable(32);
CU_ASSERT_PTR_NOT_NULL(eht);
CU_ASSERT_EQUAL(jenkins_size(eht->realtable, 0, 0), 32);
outentry = (etch_hashitem*)malloc(sizeof(etch_hashitem));
outentry->key = malloc(sizeof(wchar_t) * 100);
outentry->value = malloc(sizeof(wchar_t) * 100);
key1 = malloc(sizeof(wchar_t) * 100);
key2 = malloc(sizeof(wchar_t) * 100);
swprintf(key1, 100, L"key%d", 1);
swprintf(key2, 100, L"key%d", 2);
keylen = (int)wcslen(key1);
val1 = malloc(sizeof(wchar_t) * 100);
val2 = malloc(sizeof(wchar_t) * 100);
swprintf(val1, 100, L"val%d", 1);
swprintf(val2, 100, L"val%d", 2);
vallen = (int)wcslen(val1);
jenkins_insert(eht->realtable, key1, keylen*2+2, val1, vallen*2+2, 0, 0);
CU_ASSERT_EQUAL(jenkins_count(eht->realtable, 0, 0), 1);
/* get first, first key = first key */
result = jenkins_find(eht->realtable, key2, keylen*2+2, 0, &outentry);
CU_ASSERT_EQUAL(result, -1);
jenkins_insert(eht->realtable, key2, keylen*2+2, val2, vallen*2+2, 0, 0);
CU_ASSERT_EQUAL(jenkins_count(eht->realtable, 0, 0), 2);
/* get first, first key = first key */
result = jenkins_find(eht->realtable, key2, keylen*2+2, 0, &outentry);
CU_ASSERT_EQUAL(result, 0);
CU_ASSERT_PTR_EQUAL(key2, outentry->key);
eht->destroy(eht);
g_bytes_allocated = etch_showmem(0, IS_DEBUG_CONSOLE); /* verify all memory freed */
CU_ASSERT_EQUAL(g_bytes_allocated, 0);
memtable_clear(); /* start fresh for next test */
}
/**
* test_jenkins_findh
* unit test for jenkins_findh
*/
void test_jenkins_findh(void)
{
int result = 0, keylen = 0, vallen = 0, hash = 0, bytes_allocated = 0;
wchar_t* key1 = 0;
wchar_t* key2 = 0;
wchar_t* val1 = 0;
wchar_t* val2 = 0;
etch_hashitem* outentry;
etch_hashtable* eht = new_hashtable(32);
CU_ASSERT_PTR_NOT_NULL(eht);
CU_ASSERT_EQUAL(jenkins_size(eht->realtable, 0, 0), 32);
outentry = (etch_hashitem*)malloc(sizeof(etch_hashitem));
outentry->key = malloc(sizeof(wchar_t) * 100);
outentry->value = malloc(sizeof(wchar_t) * 100);
key1 = malloc(sizeof(wchar_t) * 100);
key2 = malloc(sizeof(wchar_t) * 100);
swprintf(key1, 100, L"key%d", 1);
swprintf(key2, 100, L"key%d", 2);
keylen = (int)wcslen(key1);
val1 = malloc(sizeof(wchar_t) * 100);
val2 = malloc(sizeof(wchar_t) * 100);
swprintf(val1, 100, L"val%d", 1);
swprintf(val2, 100, L"val%d", 2);
vallen = (int)wcslen(val1);
jenkins_insert(eht->realtable, key1, keylen*2+2, val1, vallen*2+2, 0, 0);
CU_ASSERT_EQUAL(jenkins_count(eht->realtable, 0, 0), 1);
jenkins_insert(eht->realtable, key2, keylen*2+2, val2, vallen*2+2, 0, 0);
CU_ASSERT_EQUAL(jenkins_count(eht->realtable, 0, 0), 2);
/* Find the second key */
result = jenkins_find(eht->realtable, key2, keylen*2+2, 0, &outentry);
CU_ASSERT_EQUAL(result, 0);
CU_ASSERT_PTR_EQUAL(key2, outentry->key);
eht->destroy(eht);
g_bytes_allocated = etch_showmem(0, IS_DEBUG_CONSOLE); /* verify all memory freed */
CU_ASSERT_EQUAL(g_bytes_allocated, 0);
memtable_clear(); /* start fresh for next test */
}
/**
* etch_testobject
* test object for test_jenkins_inserth
*/
typedef struct etch_testobject
{
unsigned int hashkey;
unsigned short obj_type;
unsigned short class_id;
struct vtabmask* vtab;
int (*destroy)(void*);
void* (*clone) (void*);
obj_gethashkey get_hashkey;
struct objmask* parent;
etchresult* result;
unsigned int refcount;
unsigned int length;
unsigned char is_null;
unsigned char is_copy;
unsigned char is_static;
unsigned char reserved;
char* hashvalue;
} etch_testobject;
/**
* test_inserth_get_hashkey
* object hashkey computation virtual for test_jenkins_inserth
* attempts to compute and set hashkey in object, returning hashkey so set, or zero.
*/
int test_inserth_get_hashkey(objmask* obj)
{
char* hashitem = ((etch_testobject*)obj)->hashvalue;
if (NULL == hashitem) return 0;
return obj->hashkey = jenkins_hashx(hashitem, (int) strlen(hashitem), 0);
}
/**
* new_testobject
* construct test object for test_jenkins_inserth
* illustrates that constructor should set the get_hashkey virtual.
*/
etch_testobject* new_testobject(char* hashsource)
{
etch_testobject* newobj = (etch_testobject*) new_object
(sizeof(etch_testobject), ETCHTYPEB_NONE, CLASSID_NONE);
newobj->hashvalue = hashsource;
newobj->get_hashkey = test_inserth_get_hashkey;
newobj->get_hashkey((objmask*)newobj);
return newobj;
}
/**
* test_inserth_freehandler
* hashtable content memory management callback for test_jenkins_inserth
*/
int test_inserth_freehandler(objmask* key, objmask* val)
{
if (key) key->destroy(key);
if (val) val->destroy(val);
return 0;
}
/**
* test_jenkins_inserth
* unit test for jenkins_inserth
* this test was written before the etch objmask* included a get_hashkey virtual
*/
void test_jenkins_inserth(void)
{
int result = 0;
unsigned hash1 = 0, hash2 = 0;
etch_hashitem outentry, *outitem = &outentry;
char *s1 = "abracadabra", *s2 = "gilgamesh";
etch_testobject *key1, *key2;
objmask *val1, *val2;
etch_hashtable* eht;
memset(outitem, 0, sizeof(etch_hashitem));
key1 = new_testobject(s1);
key2 = new_testobject(s2);
val1 = new_object(sizeof(objmask), ETCHTYPEB_NONE, CLASSID_NONE);
val2 = new_object(sizeof(objmask), ETCHTYPEB_NONE, CLASSID_NONE);
eht = new_hashtable(32);
CU_ASSERT_PTR_NOT_NULL_FATAL(eht);
/* set hook to free all hashtable content when we destroy hashtable */
eht->freehook = test_inserth_freehandler;
CU_ASSERT_NOT_EQUAL_FATAL(key1->hashkey, 0); /* ensure test object ctor set hashkey */
CU_ASSERT_NOT_EQUAL_FATAL(key2->hashkey, 0);
/* recompute expected hashkeys and compare to effective hashkeys */
hash1 = jenkins_hashx(s1, (int) strlen(s1), 0);
hash2 = jenkins_hashx(s2, (int) strlen(s2), 0);
CU_ASSERT_EQUAL(hash1,key1->hashkey);
CU_ASSERT_EQUAL(hash2,key2->hashkey);
/* insert pairs to map using the pre-computed hashkeys */
result = jenkins_inserth(eht->realtable, key1, val1, NULL, NULL);
CU_ASSERT_EQUAL(result,0);
CU_ASSERT_EQUAL(jenkins_count(eht->realtable, 0, 0), 1);
result = jenkins_inserth(eht->realtable, key2, val2, NULL, NULL);
CU_ASSERT_EQUAL(result,0);
CU_ASSERT_EQUAL(jenkins_count(eht->realtable, 0, 0), 2);
/* look them up using expected hashkeys */
result = jenkins_findh(eht->realtable, hash1, 0, &outitem);
CU_ASSERT_EQUAL(result, 0);
CU_ASSERT_PTR_EQUAL(key1, outentry.key);
result = jenkins_findh(eht->realtable, hash2, 0, &outitem);
CU_ASSERT_EQUAL(result, 0);
CU_ASSERT_PTR_EQUAL(key2, outentry.key);
eht->destroy(eht);
g_bytes_allocated = etch_showmem(0, IS_DEBUG_CONSOLE); /* verify all memory freed */
CU_ASSERT_EQUAL(g_bytes_allocated, 0);
memtable_clear(); /* start fresh for next test */
}
/**
* default_get_hashkey
* object hashkey computation virtual for test_default_hashkey
* attempts to compute and set a hashkey in object,
* for use when no dedicated override is supplied.
*/
int default_get_hashkey(objmask* obj)
{
void* hashitem = obj; /* we use the object address as hash source */
return obj->hashkey = jenkins_hashx((char*)&hashitem, sizeof(void*), 0);
}
/**
* new_default_object
* test object constructor for test_default_hashkey
* sets a default hashkey function and invokes it
*/
etch_testobject* new_default_object()
{
etch_testobject* newobj = (etch_testobject*) new_object
(sizeof(etch_testobject), ETCHTYPEB_NONE, CLASSID_NONE);
newobj->get_hashkey = default_get_hashkey;
newobj->get_hashkey((objmask*)newobj);
return newobj;
}
/**
* test_default_hashkey
* unit test for hashing using the default hashkey function
* this test was written before the etch objmask* included a get_hashkey virtual
*/
void test_default_hashkey(void)
{
int result = 0;
unsigned hash1 = 0, hash2 = 0;
etch_hashitem outentry, *outitem = &outentry;
etch_testobject *key1, *key2, *val1, *val2;
etch_hashtable* eht;
memset(outitem, 0, sizeof(etch_hashitem));
key1 = new_default_object();
key2 = new_default_object();
CU_ASSERT_NOT_EQUAL_FATAL(key1->hashkey, 0); /* ensure test object ctor set hashkey */
CU_ASSERT_NOT_EQUAL_FATAL(key2->hashkey, 0);
val1 = new_default_object();
val2 = new_default_object();
eht = new_hashtable(32);
/* set hook to free all hashtable content when we destroy hashtable */
eht->freehook = test_inserth_freehandler;
/* insert pairs to map using the pre-computed hashkeys */
result = jenkins_inserth(eht->realtable, key1, val1, NULL, NULL);
CU_ASSERT_EQUAL(result,0);
CU_ASSERT_EQUAL(jenkins_count(eht->realtable, 0, 0), 1);
result = jenkins_inserth(eht->realtable, key2, val2, NULL, NULL);
CU_ASSERT_EQUAL(result,0);
CU_ASSERT_EQUAL(jenkins_count(eht->realtable, 0, 0), 2);
/* look them up using effective hashkeys */
result = jenkins_findh(eht->realtable, key1->hashkey, 0, &outitem);
CU_ASSERT_EQUAL(result, 0);
CU_ASSERT_PTR_EQUAL(key1, outentry.key);
result = jenkins_findh(eht->realtable, key2->hashkey, 0, &outitem);
CU_ASSERT_EQUAL(result, 0);
CU_ASSERT_PTR_EQUAL(key2, outentry.key);
/* compute expected hashkeys from addresses of key objects */
hash1 = jenkins_hashx((char*)&key1, sizeof(void*), 0);
hash2 = jenkins_hashx((char*)&key2, sizeof(void*), 0);
/* look them up using computed hashkeys */
result = jenkins_findh(eht->realtable, hash1, 0, &outitem);
CU_ASSERT_EQUAL(result, 0);
CU_ASSERT_PTR_EQUAL(key1, outentry.key);
result = jenkins_findh(eht->realtable, hash2, 0, &outitem);
CU_ASSERT_EQUAL(result, 0);
CU_ASSERT_PTR_EQUAL(key2, outentry.key);
eht->destroy(eht);
g_bytes_allocated = etch_showmem(0, IS_DEBUG_CONSOLE); /* verify all memory freed */
CU_ASSERT_EQUAL(g_bytes_allocated, 0);
memtable_clear(); /* start fresh for next test */
}
/**
* test_jenkins_first
* unit test for jenkins_first
*/
void test_jenkins_first(void)
{
int result = 0, keylen = 0, vallen = 0;
wchar_t* key1 = 0;
wchar_t* key2 = 0;
wchar_t* val1 = 0;
wchar_t* val2 = 0;
etch_hashitem* outentry;
etch_hashtable* eht = new_hashtable(32);
CU_ASSERT_PTR_NOT_NULL(eht);
CU_ASSERT_EQUAL(jenkins_size(eht->realtable, 0, 0), 32);
outentry = (etch_hashitem*)malloc(sizeof(etch_hashitem));
outentry->key = malloc(sizeof(wchar_t) * 100);
outentry->value = malloc(sizeof(wchar_t) * 100);
key1 = malloc(sizeof(wchar_t) * 100);
key2 = malloc(sizeof(wchar_t) * 100);
swprintf(key1, 100, L"key%d", 1);
swprintf(key2, 100, L"key%d", 2);
keylen = (int)wcslen(key1);
val1 = malloc(sizeof(wchar_t) * 100);
val2 = malloc(sizeof(wchar_t) * 100);
swprintf(val1, 100, L"val%d", 1);
swprintf(val2, 100, L"val%d", 2);
vallen = (int)wcslen(val1);
jenkins_insert(eht->realtable, key1, keylen*2+2, val1, vallen*2+2, 0, 0);
jenkins_insert(eht->realtable, key2, keylen*2+2, val2, vallen*2+2, 0, 0);
CU_ASSERT_EQUAL(jenkins_count(eht->realtable, 0, 0), 2);
/* get first, first key = first key */
result = jenkins_first(eht->realtable, 0, &outentry);
CU_ASSERT_EQUAL(result, 0);
CU_ASSERT_PTR_EQUAL(key1, outentry->key);
/* next is key2 */
result = jenkins_next(eht->realtable, 0, &outentry);
CU_ASSERT_EQUAL(result, 0);
CU_ASSERT_PTR_EQUAL(key2, outentry->key);
eht->destroy(eht);
}
/**
* test_jenkins_next
* unit test for jenkins_next
*/
void test_jenkins_next(void)
{
int result = 0, keylen = 0, vallen = 0;
wchar_t* key1 = 0;
wchar_t* key2 = 0;
wchar_t* key3 = 0;
wchar_t* val1 = 0;
wchar_t* val2 = 0;
wchar_t* val3 = 0;
etch_hashitem* outentry;
etch_hashtable* eht = new_hashtable(32);
CU_ASSERT_PTR_NOT_NULL(eht);
CU_ASSERT_EQUAL(jenkins_size(eht->realtable, 0, 0), 32);
outentry = (etch_hashitem*)malloc(sizeof(etch_hashitem));
outentry->key = malloc(sizeof(wchar_t) * 100);
outentry->value = malloc(sizeof(wchar_t) * 100);
key1 = malloc(sizeof(wchar_t) * 100);
key2 = malloc(sizeof(wchar_t) * 100);
key3 = malloc(sizeof(wchar_t) * 100);
swprintf(key1, 100, L"key%d", 1);
swprintf(key2, 100, L"key%d", 2);
swprintf(key3, 100, L"key%d", 3);
keylen = (int)wcslen(key1);
val1 = malloc(sizeof(wchar_t) * 100);
val2 = malloc(sizeof(wchar_t) * 100);
val3 = malloc(sizeof(wchar_t) * 100);
swprintf(val1, 100, L"val%d", 1);
swprintf(val2, 100, L"val%d", 2);
swprintf(val3, 100, L"val%d", 3);
vallen = (int)wcslen(val1);
jenkins_insert(eht->realtable, key1, keylen*2+2, val1, vallen*2+2, 0, 0);
jenkins_insert(eht->realtable, key2, keylen*2+2, val2, vallen*2+2, 0, 0);
jenkins_insert(eht->realtable, key3, keylen*2+2, val3, vallen*2+2, 0, 0);
CU_ASSERT_EQUAL(jenkins_count(eht->realtable, 0, 0), 3);
result = jenkins_first(eht->realtable, 0, &outentry);
CU_ASSERT_EQUAL(result, 0);
result = jenkins_next(eht->realtable, 0, &outentry);
CU_ASSERT_EQUAL(result, 0);
result = jenkins_next(eht->realtable, 0, &outentry);
CU_ASSERT_EQUAL(result, 0);
result = jenkins_next(eht->realtable, 0, &outentry);
CU_ASSERT_EQUAL(result, -1);
eht->destroy(eht);
}
/**
* test_jenkins_current
* unit test for jenkins_current
*/
void test_jenkins_current(void)
{
int result = 0, keylen = 0, vallen = 0;
wchar_t* key1 = 0;
wchar_t* key2 = 0;
wchar_t* val1 = 0;
wchar_t* val2 = 0;
etch_hashitem* outentry;
etch_hashtable* eht = new_hashtable(32);
CU_ASSERT_PTR_NOT_NULL(eht);
CU_ASSERT_EQUAL(jenkins_size(eht->realtable, 0, 0), 32);
/* get current, no key */
outentry = (etch_hashitem*)malloc(sizeof(etch_hashitem));
outentry->key = malloc(sizeof(wchar_t) * 100);
outentry->value = malloc(sizeof(wchar_t) * 100);
result = jenkins_current(eht->realtable, 0, &outentry);
CU_ASSERT_EQUAL(result, -1);
key1 = malloc(sizeof(wchar_t) * 100);
key2 = malloc(sizeof(wchar_t) * 100);
swprintf(key1, 100, L"key%d", 1);
swprintf(key2, 100, L"key%d", 2);
keylen = (int)wcslen(key1);
val1 = malloc(sizeof(wchar_t) * 100);
val2 = malloc(sizeof(wchar_t) * 100);
swprintf(val1, 100, L"val%d", 1);
swprintf(val2, 100, L"val%d", 2);
vallen = (int)wcslen(val1);
jenkins_insert(eht->realtable, key1, keylen*2+2, val1, vallen*2+2, 0, 0);
jenkins_insert(eht->realtable, key2, keylen*2+2, val2, vallen*2+2, 0, 0);
CU_ASSERT_EQUAL(jenkins_count(eht->realtable, 0, 0), 2);
/* get current, current key = key2 */
result = jenkins_current(eht->realtable, 0, &outentry);
CU_ASSERT_EQUAL(result, 0);
CU_ASSERT_PTR_EQUAL(key2, outentry->key);
/* remove second entry */
result = jenkins_remove(eht->realtable, key2, keylen*2+2, 0, &outentry);
CU_ASSERT_EQUAL(result, 0);
CU_ASSERT_PTR_EQUAL(key2, outentry->key);
/* get current, current key = key1 */
result = jenkins_current(eht->realtable, 0, &outentry);
CU_ASSERT_EQUAL(result, 0);
CU_ASSERT_PTR_EQUAL(key1, outentry->key);
eht->destroy(eht);
}
/**
* test_jenkins_remove
* unit test for jenkins_remove
*/
void test_jenkins_remove(void)
{
int count = 0, keylen = 0, vallen = 0;
wchar_t* key1 = 0;
wchar_t* key2 = 0;
wchar_t* val1 = 0;
//wchar_t* val2 = 0;
etch_hashitem* outentry;
etch_hashtable* eht = new_hashtable(32);
CU_ASSERT_PTR_NOT_NULL(eht);
CU_ASSERT_EQUAL(jenkins_size(eht->realtable, 0, 0), 32);
key1 = malloc(sizeof(wchar_t) * 100);
key2 = malloc(sizeof(wchar_t) * 100);
swprintf(key1, 100, L"key%d", 1);
swprintf(key2, 100, L"key%d", 2);
keylen = (int)wcslen(key1);
val1 = malloc(sizeof(wchar_t) * 100);
//val2 = malloc(sizeof(wchar_t) * 100);
swprintf(val1, 100, L"val%d", 1);
//swprintf(val2, 100, L"val%d", 2);
vallen = (int)wcslen(val1);
outentry = (etch_hashitem*)malloc(sizeof(etch_hashitem));
outentry->key = malloc(sizeof(wchar_t) * 100);
outentry->value = malloc(sizeof(wchar_t) * 100);
jenkins_insert(eht->realtable, key1, keylen*2+2, val1, vallen*2+2, 0, 0);
CU_ASSERT_EQUAL(jenkins_count(eht->realtable, 0, 0), 1);
/* remove wrong entry */
count = jenkins_remove(eht->realtable, key2, keylen*2+2, 0, &outentry);
CU_ASSERT_EQUAL(count, -1);
/* remove the entry */
count = jenkins_remove(eht->realtable, key1, keylen*2+2, 0, &outentry);
CU_ASSERT_EQUAL(count, 0);
CU_ASSERT_PTR_EQUAL(key1, outentry->key);
eht->destroy(eht);
}
/**
* test_jenkins_clear
* unit test for jenkins_clear
*/
void test_jenkins_clear(void)
{
int count = 0, keylen = 0, vallen = 0;
wchar_t* key1 = 0;
wchar_t* key2 = 0;
wchar_t* val1 = 0;
wchar_t* val2 = 0;
etch_hashtable* eht = new_hashtable(32);
CU_ASSERT_PTR_NOT_NULL(eht);
CU_ASSERT_EQUAL(jenkins_size(eht->realtable, 0, 0), 32);
key1 = malloc(sizeof(wchar_t) * 100);
key2 = malloc(sizeof(wchar_t) * 100);
swprintf(key1, 100, L"key%d", 1);
swprintf(key2, 100, L"key%d", 2);
keylen = (int)wcslen(key1);
val1 = malloc(sizeof(wchar_t) * 100);
val2 = malloc(sizeof(wchar_t) * 100);
swprintf(val1, 100, L"val%d", 1);
swprintf(val2, 100, L"val%d", 2);
vallen = (int)wcslen(val1);
jenkins_insert(eht->realtable, key1, keylen*2+2, val1, vallen*2+2, 0, 0);
jenkins_insert(eht->realtable, key2, keylen*2+2, val2, vallen*2+2, 0, 0);
CU_ASSERT_EQUAL(jenkins_count(eht->realtable, 0, 0), 2);
count = jenkins_clear(eht->realtable, TRUE, TRUE, 0, 0);
/* clear 2 key/val pair */
CU_ASSERT_EQUAL(count, 2);
CU_ASSERT_PTR_NOT_NULL(eht->realtable);
eht->destroy(eht);
}
/**
* test_jenkins_count
* unit test for jenkins_count
*/
void test_jenkins_count(void)
{
etch_hashtable* eht = new_hashtable(32);
CU_ASSERT_PTR_NOT_NULL(eht);
CU_ASSERT_EQUAL(jenkins_size(eht->realtable, 0, 0), 32);
CU_ASSERT_EQUAL(jenkins_count(eht->realtable, 0, 0), 0);
jenkins_insert(eht->realtable, L"key", 8, L"val", 8, 0, 0);
CU_ASSERT_EQUAL(jenkins_size(eht->realtable, 0, 0), 32);
CU_ASSERT_EQUAL(jenkins_count(eht->realtable, 0, 0), 1);
eht->destroy(eht);
}
/**
* test_jenkins_size
* unit test for jenkins_size
*/
void test_jenkins_size(void)
{
int i = 0;
int keylen = 0;
wchar_t key[100];
etch_hashtable* eht = new_hashtable(16);
CU_ASSERT_PTR_NOT_NULL(eht);
CU_ASSERT_EQUAL(jenkins_size(eht->realtable, 0, 0), 16);
CU_ASSERT_EQUAL(jenkins_count(eht->realtable, 0, 0), 0);
for (i=0; i<17; i++)
{
swprintf(key, 100, L"key%d", i);
keylen = (int)wcslen(key);
jenkins_insert(eht->realtable, key, keylen*2+2, L"val", 8, 0, 0);
}
CU_ASSERT_EQUAL(jenkins_size(eht->realtable, 0, 0), 32);
CU_ASSERT_EQUAL(jenkins_count(eht->realtable, 0, 0), 17);
eht->destroy(eht);
}
/**
* test_jenkins_stats
* unit test for jenkins_stats
*/
void test_jenkins_stats(void)
{
}
/**
* test_jenkins_hash
* unit test for jenkins_hash
*/
void test_jenkins_hash(void)
{
int hash = 0;
etch_hashtable* eht = new_etch_hashtable();
CU_ASSERT_PTR_NOT_NULL(eht);
CU_ASSERT_PTR_NULL(eht->realtable);
/* realtable is NULL */
hash = jenkins_hash(eht->realtable, "key", 3, 0, 0, 0);
CU_ASSERT_EQUAL(hash, 0);
destroy_hashtable(eht, TRUE, TRUE);
eht = new_hashtable(16);
CU_ASSERT_PTR_NOT_NULL(eht);
CU_ASSERT_EQUAL(jenkins_size(eht->realtable, 0, 0), 16);
CU_ASSERT_EQUAL(jenkins_count(eht->realtable, 0, 0), 0);
/* key is NULL */
hash = jenkins_hash(eht->realtable, 0, 0, 0, 0, 0);
CU_ASSERT_EQUAL(hash, 0);
/* key length is 0 */
hash = jenkins_hash(eht->realtable, "key", 0, 0, 0, 0);
CU_ASSERT_EQUAL(hash, 0);
/* key length is MAX_ETCHHASHKEY + 1 */
hash = jenkins_hash(eht->realtable, "key", MAX_ETCHHASHKEY+1, 0, 0, 0);
CU_ASSERT_EQUAL(hash, 0);
/* get a good hash */
hash = jenkins_hash(eht->realtable, "key", 3, 0, 0, 0);
CU_ASSERT_NOT_EQUAL(hash, 0);
/* hash should be the same is input arguments are the same */
CU_ASSERT_EQUAL(hash, jenkins_hash(eht->realtable, "key", 3, 0, 0, 0));
/* hash should be different if input arguments are different */
CU_ASSERT_NOT_EQUAL(hash, jenkins_hash(eht->realtable, "kee", 3, 0, 0, 0));
eht->destroy(eht);
}
/**
* test_intkey
* test using a 32 bit int as a key
*/
#pragma warning (disable:4996)
void test_intkey(void)
{
etch_hashitem hashbucket;
etch_hashitem* myentry = &hashbucket;
char* test_string = "it works";
unsigned int key = 12345, key2 = 11111, key3 = 11111;
int value_len = (int)strlen(test_string)+1; /* string length of value */
etch_hashtable* ht = new_hashtable(4);
/* our hash key is a 4-byte item, which we will use to contain the
value of a 32 bit int.
*/
char* pkey = malloc(sizeof(int));
/* the string pval is our hash value */
char* pval = malloc(value_len);
strcpy(pval, test_string);
/* copy the int into our hash key.
*/
memcpy(pkey, &key, sizeof(int));
CU_ASSERT_EQUAL(ht->vtab->insert(ht->realtable, pkey, sizeof(int), pval, value_len, 0, 0), 0);
CU_ASSERT_EQUAL(ht->vtab->find (ht->realtable, pkey, sizeof(int), NULL, &myentry), 0);
CU_ASSERT_EQUAL(ht->vtab->find (ht->realtable, &key, sizeof(int), NULL, &myentry), 0);
/* test two pointers to the same value.
*/
CU_ASSERT_EQUAL(ht->vtab->insert(ht->realtable, &key2, sizeof(int), pval, value_len, 0, 0), 0);
CU_ASSERT_EQUAL(ht->vtab->find (ht->realtable, &key3, sizeof(int), NULL, &myentry), 0);
destroy_hashtable(ht, FALSE, FALSE);
free(pkey);
free(pval);
}
/**
* test_jenkins_destroy
* unit test for jenkins_destroy
*/
void test_jenkins_destroy(void)
{
int result = 0;
etch_hashtable* eht = new_etch_hashtable();
CU_ASSERT_PTR_NOT_NULL(eht);
CU_ASSERT_PTR_NULL(eht->realtable);
/* realtable is NULL */
result = jenkins_destroy(eht->realtable, 0, 0);
CU_ASSERT_EQUAL(result, -1);
eht = new_hashtable(16);
CU_ASSERT_PTR_NOT_NULL(eht);
CU_ASSERT_EQUAL(jenkins_size(eht->realtable, 0, 0), 16);
CU_ASSERT_EQUAL(jenkins_count(eht->realtable, 0, 0), 0);
/* realtable is not NULL */
result = jenkins_destroy(eht->realtable, 0, 0);
CU_ASSERT_EQUAL(result, 0);
}
/**
* test_jenkins_create
* unit test for jenkins_create
*/
void test_jenkins_create(void)
{
int result = 0;
etch_hashtable* eht = new_etch_hashtable();
CU_ASSERT_PTR_NOT_NULL(eht);
CU_ASSERT_PTR_NULL(eht->realtable);
/* realtable is NULL */
result = jenkins_create(16, 0, eht->realtable);
CU_ASSERT_EQUAL(result, -1);
destroy_hashtable(eht, FALSE, FALSE);
eht = new_hashtable(16);
CU_ASSERT_PTR_NOT_NULL(eht);
CU_ASSERT_PTR_NOT_NULL(eht->realtable);
/* realtable is NULL */
result = jenkins_create(16, 0, eht->realtable);
CU_ASSERT_EQUAL(result, 0);
CU_ASSERT_EQUAL(jenkins_size(eht->realtable, 0, 0), 16);
CU_ASSERT_EQUAL(jenkins_count(eht->realtable, 0, 0), 0);
destroy_hashtable(eht, FALSE, FALSE);
}
/**
* test_ctor_jenkins_hashtable
* unit test for ctor_jenkins_hashtable
*/
void test_ctor_jenkins_hashtable(void)
{
etch_hashtable* eht = ctor_jenkins_hashtable(16);
CU_ASSERT_PTR_NOT_NULL(eht);
CU_ASSERT_PTR_NOT_NULL(eht->realtable);
CU_ASSERT_PTR_NOT_NULL(eht->vtab);
CU_ASSERT_EQUAL(jenkins_size(eht->realtable, 0, 0), 16);
CU_ASSERT_EQUAL(jenkins_count(eht->realtable, 0, 0), 0);
destroy_hashtable(eht, FALSE, FALSE);
}
#pragma warning (disable:4996)
/*
* test_key_pointer: test using a memory address as a hash key.
* return 1 if OK, zero if failed.
*/
int test_key_pointer(etch_hashtable* ht)
{
etch_hashitem hashbucket;
etch_hashitem* myentry = &hashbucket;
char* test_string = "it works";
const int value_len = (int)strlen(test_string)+1; /* string length of value */
const int key_len = sizeof(char*);
/* our hash key is a 4-byte item, which we will use to contain the
value of a memory address.
*/
char* pkey = malloc(key_len);
/* the value of the pval memory address is our hash key.
* the string pval is our hash value. Of course, our hash table stores pointers,
* not values, so the key is &pval, and the value is pval.
*/
char* pval = malloc(value_len);
strcpy(pval, test_string);
/* copy the memory address into our hash key.
*/
memcpy(pkey, &pval, sizeof(char*));
CU_ASSERT_EQUAL(ht->vtab->insert(ht->realtable, pkey, key_len, pval, value_len, 0, 0), 0);
CU_ASSERT_EQUAL(ht->vtab->find(ht->realtable, pkey, key_len, NULL, &myentry), 0);
CU_ASSERT_PTR_NOT_NULL(myentry->value);
/* This code demonstrates what we've done here. The key is the value of a
* char*, and the value is that same char*. So, indirectly dereferencing
* the key must elicit the same result as dereferencing the value.
*/
CU_ASSERT_EQUAL(memcmp(myentry->key, &myentry->value, sizeof(char*)), 0);
CU_ASSERT_EQUAL(strcmp(*(char**)myentry->key, (char*)myentry->value), 0);
return 1;
}
/**
* test_all
* unit test for various functions strung together
*/
void test_combo(void)
{
char buf[MAXLINELEN];
char* pkey; void* pdata;
int n, keylen, myincount=0, myhashcount=0, myfreecount=0;
etch_hashtable* myhash;
etch_hashitem hashbucket;
etch_hashitem* myentry = &hashbucket;
unsigned hashed;
char c = 0, *p;
FILE* f = 0;
p = "abracadabra"; n = (int)strlen(p); // Test the hash function
hashed = etchhash(p, n, 0);
f = fopen(TESTDATAPATH,"r");
CU_ASSERT_PTR_NOT_NULL_FATAL(f);
myhash = new_hashtable(INITIAL_TABLE_SIZE); /* create hash table */
CU_ASSERT_PTR_NOT_NULL_FATAL(myhash);
myhash->vtab->stats(myhash->realtable, 0, 0); /* show table stats */
n = myhash->vtab->size(myhash->realtable, 0, 0);
CU_ASSERT_EQUAL(n, INITIAL_TABLE_SIZE);
while (fgets((char*)buf, MAXLINELEN, f)) /* read the testdata file line by line */
{
unsigned char* p = buf; while(*p++) if (*p <= 0x0d) *p = 0; /* strip crlf */
keylen = (int)strlen(buf); /* we use null termed key, but it is not necessary */
pkey = malloc(keylen+1); strcpy(pkey, buf);
pdata = malloc(DATASIZE); /* note that user allocates memory for key and data */
strcpy(pdata, DATAVALUE); /* hashtable does not make copies of key or of data */
/* insert the new hashtable entry */
if (0 == myhash->vtab->insert(myhash->realtable, pkey, keylen, pdata, DATASIZE, 0, 0))
{ myincount++; /* point at current entry in hashtable */
myhash->vtab->current(myhash->realtable, 0, &myentry);
CU_ASSERT_PTR_EQUAL(myentry->key, pkey); /* ensure items just inserted are there */
CU_ASSERT_PTR_EQUAL(myentry->value, pdata);
}
else /* insert failure - probably duplicate */
{ free(pkey);
free(pdata);
}
}
fclose(f); f = 0;
myhash->vtab->stats(myhash->realtable, 0, 0); /* show table stats */
pkey = "banana"; /* test a hashtable lookup */
CU_ASSERT_EQUAL(myhash->vtab->find(myhash->realtable, pkey, (int)strlen(pkey), NULL, &myentry), 0);
CU_ASSERT_EQUAL(strcmp(pkey, myentry->key), 0);
CU_ASSERT_EQUAL(myhash->vtab->first(myhash->realtable, NULL, &myentry), 0);
myhashcount = 1;
while(0 == myhash->vtab->next(myhash->realtable, NULL, &myentry))
{
myhashcount++;
}
/* test using pointer as key, adding 0 or 1 to myhashcount */
myhashcount += test_key_pointer(myhash);
/* destroy hash table and all its contents (free key and data memory) */
myfreecount = myhash->vtab->clear(myhash->realtable, TRUE, TRUE, 0, 0);
CU_ASSERT_EQUAL(myfreecount, myhashcount);
}
/**
* _tmain
* unit test entry point
*/
int _tmain(int argc, _TCHAR* argv[])
{
char c=0;
CU_pSuite pSuite = NULL;
g_is_automated_test = argc > 1 && 0 != wcscmp(argv[1], L"-a");
if (CUE_SUCCESS != CU_initialize_registry()) return 0;
CU_set_output_filename("../test_etchhash");
etch_watch_id = 0;
pSuite = CU_add_suite("suite_etchhash", init_suite, clean_suite);
CU_add_test(pSuite, "new_hashtable", test_new_hashtable);
CU_add_test(pSuite, "new_etch_hashtable", test_new_etch_hashtable);
CU_add_test(pSuite, "destroy_hashtable", test_destroy_hashtable);
CU_add_test(pSuite, "jenkins_hash", test_jenkins_hash);
CU_add_test(pSuite, "jenkins_find", test_jenkins_find);
CU_add_test(pSuite, "jenkins_findh", test_jenkins_findh);
CU_add_test(pSuite, "jenkins default key", test_default_hashkey);
CU_add_test(pSuite, "jenkins_first", test_jenkins_first);
CU_add_test(pSuite, "jenkins_next", test_jenkins_next);
CU_add_test(pSuite, "jenkins_current", test_jenkins_current);
CU_add_test(pSuite, "jenkins_remove", test_jenkins_remove);
CU_add_test(pSuite, "jenkins_clear", test_jenkins_clear);
CU_add_test(pSuite, "jenkins_count", test_jenkins_count);
CU_add_test(pSuite, "jenkins_size", test_jenkins_size);
CU_add_test(pSuite, "jenkins_insert", test_jenkins_insert);
CU_add_test(pSuite, "jenkins_inserth", test_jenkins_inserth);
CU_add_test(pSuite, "jenkins_stats", test_jenkins_stats);
CU_add_test(pSuite, "jenkins_destroy", test_jenkins_destroy);
CU_add_test(pSuite, "jenkins_create", test_jenkins_create);
CU_add_test(pSuite, "ctor_jenkins_hashtable", test_ctor_jenkins_hashtable);
CU_add_test(pSuite, "test integer key", test_intkey);
CU_add_test(pSuite, "test combinations", test_combo);
if (g_is_automated_test)
CU_automated_run_tests();
else
{ CU_basic_set_mode(CU_BRM_VERBOSE);
CU_basic_run_tests();
}
if (!g_is_automated_test) { printf("any key ..."); while(!c) c = _getch(); wprintf(L"\n"); }
CU_cleanup_registry();
return CU_get_error();
}