/*
* Copyright 2011 Steven Gribble
*
* This file is part of the UW CSE 333 course project sequence
* (333proj).
*
* 333proj 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.
*
* 333proj 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 333proj. If not, see .
*/
#include
#include
#include
#include "hashtable.h"
#include "CUnit/Basic.h"
// we're white-box testing; clients normally don't need to include
// any of the following three
#include "hashtable_priv.h"
#include "ll.h"
#include "ll_priv.h"
// our CUnit suite initialization/cleanup functions
void HTSAddPoints(int points);
int HTSInit(void);
int HTSCleanup(void);
// our CUnit test functions
void HTSTestAllocFree(void);
void HTSTestInsertLookupRemove(void);
void HTSTestIterator(void);
// our main function
int main(int argc, char **argv) {
CU_pSuite pSuite = NULL;
// initialize the test registry
if (CU_initialize_registry() != CUE_SUCCESS) {
return CU_get_error();
}
// add a suite to the registry
pSuite = CU_add_suite("hash table suite", HTSInit, HTSCleanup);
if (pSuite == NULL) {
CU_cleanup_registry();
return CU_get_error();
}
// add the tests to the suite
if ((CU_ADD_TEST(pSuite, HTSTestAllocFree) == NULL) ||
(CU_ADD_TEST(pSuite, HTSTestInsertLookupRemove) == NULL) ||
(CU_ADD_TEST(pSuite, HTSTestIterator) == NULL)) {
CU_cleanup_registry();
return CU_get_error();
}
// run all of the tests using the CUnit basic interface
CU_basic_set_mode(CU_BRM_VERBOSE);
CU_basic_run_tests();
CU_cleanup_registry();
return CU_get_error();
}
/*** OUR TEST FUNCTIONS AND SUITE INITIALIZATION GO BELOW HERE ***/
// our payload structure
typedef struct payload_st {
int magic_num;
int payload_num;
} Payload;
// number of payload frees
static int num_payload_frees;
// our payload free test function
void TestPayloadFree(void *payload) {
num_payload_frees++;
CU_ASSERT_EQUAL_FATAL(((Payload *) payload)->magic_num, 0xDEADBEEF);
free(payload);
}
static const int MAXGRADE = 100;
static int yourgrade = 0;
void HTSAddPoints(int points) {
yourgrade += points;
printf("(%d/%d)", yourgrade, MAXGRADE);
}
void HTSFinalGrade() {
printf("\n\nYour unit test grade is %d/%d [%.02f%%]\n",
yourgrade, MAXGRADE, (100.0*yourgrade)/MAXGRADE);
}
int HTSInit(void) {
yourgrade = 0;
printf("There are %d unit-testing points available.\n",
MAXGRADE);
return 0; // return 0 on success, non-zero otherwise
}
int HTSCleanup(void) {
HTSFinalGrade();
return 0; // return 0 on success, non-zero otherwise
}
void HTSTestAllocFree(void) {
// simple create / delete test
HashTable table = AllocateHashTable(3);
HashTableRecordPtr ht = (HashTableRecordPtr) table;
CU_ASSERT_EQUAL_FATAL(ht->num_elements, 0);
CU_ASSERT_EQUAL_FATAL(ht->num_buckets, 3);
CU_ASSERT_PTR_NOT_NULL(ht->buckets);
CU_ASSERT_EQUAL_FATAL(NumElementsInLinkedList(ht->buckets[0]), 0);
CU_ASSERT_EQUAL_FATAL(NumElementsInLinkedList(ht->buckets[1]), 0);
CU_ASSERT_EQUAL_FATAL(NumElementsInLinkedList(ht->buckets[2]), 0);
FreeHashTable(table, &TestPayloadFree);
HTSAddPoints(10);
}
void HTSTestInsertLookupRemove(void) {
// create the table
HashTable table = AllocateHashTable(3);
HashTableRecordPtr ht = (HashTableRecordPtr) table;
// allocate and insert a bunch of elements
uint64_t i;
for (i = 0; i < 100; i++) {
// do the insert
Payload *np = (Payload *) malloc(sizeof(Payload));
HTKeyValue old, new;
assert(np != NULL);
np->magic_num = 0xDEADBEEF;
np->payload_num = (int) i;
new.key = i;
new.value = (void *) np;
CU_ASSERT_EQUAL_FATAL(InsertHashTable(table, new, &old), 1);
// test double insert
CU_ASSERT_EQUAL_FATAL(InsertHashTable(table, new, &old), 2);
CU_ASSERT_EQUAL_FATAL(old.key, i);
CU_ASSERT_PTR_EQUAL_FATAL(old.value, (void *) np);
// test lookup
old.key = 1;
old.value = NULL;
CU_ASSERT_EQUAL_FATAL(LookupHashTable(table, i, &old), 1);
CU_ASSERT_EQUAL_FATAL(old.key, i);
CU_ASSERT_PTR_EQUAL_FATAL(old.value, (void *) np);
// test bad lookup
CU_ASSERT_EQUAL_FATAL(LookupHashTable(table, i+1, &old), 0);
// test bad remove
CU_ASSERT_EQUAL_FATAL(RemoveFromHashTable(table, i+1, &old), 0);
// test good remove and reinsert
old.key = 1;
old.value = NULL;
CU_ASSERT_EQUAL_FATAL(RemoveFromHashTable(table, i, &old), 1);
CU_ASSERT_EQUAL_FATAL(old.key, i);
CU_ASSERT_PTR_EQUAL_FATAL(old.value, (void *) np);
CU_ASSERT_EQUAL(NumElementsInHashTable(table), (uint64_t) i);
CU_ASSERT_EQUAL_FATAL(InsertHashTable(table, new, &old), 1);
CU_ASSERT_EQUAL_FATAL(InsertHashTable(table, new, &old), 2);
CU_ASSERT_EQUAL(NumElementsInHashTable(table), (uint64_t) i+1);
}
HTSAddPoints(10);
// delete every other key
for (i = 0; i < 100; i += 2) {
HTKeyValue old;
CU_ASSERT_EQUAL_FATAL(RemoveFromHashTable(table, i, &old), 1);
CU_ASSERT_EQUAL_FATAL(old.key, i);
TestPayloadFree(old.value);
CU_ASSERT_EQUAL_FATAL(RemoveFromHashTable(table, i, &old), 0);
}
CU_ASSERT_EQUAL_FATAL(ht->num_elements, 50);
// delete the remaining keys
for (i = 1; i < 100; i += 2) {
HTKeyValue old;
CU_ASSERT_EQUAL_FATAL(RemoveFromHashTable(table, i, &old), 1);
CU_ASSERT_EQUAL_FATAL(old.key, i);
TestPayloadFree(old.value);
CU_ASSERT_EQUAL_FATAL(RemoveFromHashTable(table, i, &old), 0);
}
CU_ASSERT_EQUAL_FATAL(ht->num_elements, 0);
// try a final add / remove pass
for (i = 0; i < 100; i++) {
// do the insert
Payload *np = (Payload *) malloc(sizeof(Payload));
HTKeyValue old, new;
assert(np != NULL);
np->magic_num = 0xDEADBEEF;
np->payload_num = (int) i;
new.key = i;
new.value = (void *) np;
CU_ASSERT_EQUAL_FATAL(InsertHashTable(table, new, &old), 1);
// test lookup
old.key = 1;
old.value = NULL;
CU_ASSERT_EQUAL_FATAL(LookupHashTable(table, i, &old), 1);
CU_ASSERT_EQUAL_FATAL(old.key, i);
CU_ASSERT_PTR_EQUAL_FATAL(old.value, (void *) np);
}
CU_ASSERT_EQUAL_FATAL(ht->num_elements, 100);
// delete most of the remaining keys
for (i = 0; i < 98; i++) {
HTKeyValue old;
CU_ASSERT_EQUAL_FATAL(RemoveFromHashTable(table, i, &old), 1);
CU_ASSERT_EQUAL_FATAL(old.key, i);
TestPayloadFree(old.value);
CU_ASSERT_EQUAL_FATAL(RemoveFromHashTable(table, i, &old), 0);
}
CU_ASSERT_EQUAL_FATAL(ht->num_elements, 2);
// delete the HT and the final keys
num_payload_frees = 0;
FreeHashTable(table, &TestPayloadFree);
CU_ASSERT_EQUAL_FATAL(num_payload_frees, 2);
HTSAddPoints(10);
}
void HTSTestIterator(void) {
HTKeyValue old, new;
// create the table
HashTable table = AllocateHashTable(3);
// try using an iterator on an empty table
HTIter iter = HashTableMakeIterator(table);
HTIterRecord *it = (HTIterRecord *) iter;
CU_ASSERT_EQUAL_FATAL(it->is_valid, 0);
CU_ASSERT_EQUAL_FATAL(HTIteratorPastEnd(iter), 1);
CU_ASSERT_EQUAL_FATAL(HTIteratorGet(iter, &old), 0);
HTIteratorFree(iter);
HTSAddPoints(10);
// allocate and insert a bunch of elements
uint64_t i;
for (i = 0; i < 100; i++) {
// do the insert
Payload *np = (Payload *) malloc(sizeof(Payload));
assert(np != NULL);
np->magic_num = 0xDEADBEEF;
np->payload_num = (int) i;
new.key = i;
new.value = (void *) np;
CU_ASSERT_EQUAL_FATAL(InsertHashTable(table, new, &old), 1);
}
// create an iterator on the table
iter = HashTableMakeIterator(table);
it = (HTIterRecord *) iter;
CU_ASSERT_EQUAL_FATAL(it->is_valid, 1);
HTSAddPoints(10);
// iterate through, verifying each value is found exactly once
int valarray[100] = { 0 }; // array of 100 0's
for (i = 0; i < 100; i++) {
Payload *op;
CU_ASSERT_EQUAL_FATAL(HTIteratorPastEnd(iter), 0);
CU_ASSERT_EQUAL_FATAL(HTIteratorGet(iter, &old), 1);
CU_ASSERT_EQUAL_FATAL(valarray[(int) old.key], 0);
valarray[(int) old.key] = 1;
op = (Payload *) old.value;
CU_ASSERT_EQUAL_FATAL(op->magic_num, 0xDEADBEEF);
CU_ASSERT_EQUAL_FATAL(op->payload_num, (int) old.key);
if (i == 99) {
CU_ASSERT_EQUAL_FATAL(HTIteratorNext(iter), 0);
CU_ASSERT_EQUAL_FATAL(HTIteratorPastEnd(iter), 1);
} else {
CU_ASSERT_EQUAL_FATAL(HTIteratorNext(iter), 1);
CU_ASSERT_EQUAL_FATAL(HTIteratorPastEnd(iter), 0);
}
}
for (i = 0; i < 100; i++) {
CU_ASSERT_EQUAL_FATAL(valarray[i], 1);
}
CU_ASSERT_EQUAL_FATAL(HTIteratorNext(iter), 0);
HTIteratorFree(iter);
HTSAddPoints(20);
// Iterate through again, deleting every third key.
iter = HashTableMakeIterator(table);
it = (HTIterRecord *) iter;
CU_ASSERT_EQUAL_FATAL(it->is_valid, 1);
for (i = 0; i < 100; i++) {
Payload *op;
valarray[i] = 0;
CU_ASSERT_EQUAL_FATAL(HTIteratorGet(iter, &old), 1);
if (((int) old.key) % 3 == 0) {
int oldnumelements, newnumelements;
oldnumelements = (int) NumElementsInHashTable(table);
op = (Payload *) old.value;
CU_ASSERT_EQUAL_FATAL(op->payload_num, (int) old.key);
if (i == 99) {
CU_ASSERT_EQUAL_FATAL(HTIteratorDelete(iter, &old), 2);
} else {
CU_ASSERT_EQUAL_FATAL(HTIteratorDelete(iter, &old), 1);
}
newnumelements = (int) NumElementsInHashTable(table);
CU_ASSERT_EQUAL_FATAL(oldnumelements-1, newnumelements);
op = (Payload *) old.value;
free(op);
} else {
if (i == 99) {
CU_ASSERT_EQUAL_FATAL(HTIteratorNext(iter), 0);
CU_ASSERT_EQUAL_FATAL(HTIteratorPastEnd(iter), 1);
} else {
CU_ASSERT_EQUAL_FATAL(HTIteratorNext(iter), 1);
CU_ASSERT_EQUAL_FATAL(HTIteratorPastEnd(iter), 0);
}
}
}
// Free the iterator
HTIteratorFree(iter);
HTSAddPoints(10);
// Iterate through one last time, making sure we get only
// the non-zero mod 3 keys.
iter = HashTableMakeIterator(table);
it = (HTIterRecord *) iter;
CU_ASSERT_EQUAL_FATAL(it->is_valid, 1);
for (i = 0; i < 66; i++) {
CU_ASSERT_EQUAL_FATAL(HTIteratorGet(iter, &old), 1);
valarray[(int) old.key] = 1;
if (i == 65) {
CU_ASSERT_EQUAL_FATAL(HTIteratorNext(iter), 0);
CU_ASSERT_EQUAL_FATAL(HTIteratorPastEnd(iter), 1);
} else {
CU_ASSERT_EQUAL_FATAL(HTIteratorNext(iter), 1);
CU_ASSERT_EQUAL_FATAL(HTIteratorPastEnd(iter), 0);
}
}
for (i = 0; i < 100; i++) {
if (i % 3 == 0) {
CU_ASSERT_EQUAL_FATAL(valarray[i], 0);
} else {
CU_ASSERT_EQUAL_FATAL(valarray[i], 1);
}
}
// Free the iterator
HTIteratorFree(iter);
HTSAddPoints(10);
// delete the HT and the final keys
num_payload_frees = 0;
FreeHashTable(table, &TestPayloadFree);
CU_ASSERT_EQUAL_FATAL(num_payload_frees, 66);
HTSAddPoints(10);
}