#include <TestShell.h>
#include <TestSuiteAddon.h>
#include <cppunit/extensions/HelperMacros.h>
#include <os/support/ObjectList.h>
#include <shared/AutoDeleter.h>
#include <util/OpenHashTable.h>
namespace {
class Entry {
public:
Entry(uint32_t value)
:
fValue(value),
fNext(NULL)
{
}
const uint32_t Value() const { return fValue; }
Entry* Next() const { return fNext; }
private:
uint32_t fValue;
Entry* fNext;
friend class EntryDefinition;
};
class EntryDefinition {
public:
typedef uint32_t KeyType;
typedef Entry ValueType;
size_t HashKey(const KeyType& key) const { return key; }
size_t Hash(Entry* entry) const { return entry->fValue; }
bool Compare(const KeyType& key, Entry* entry) const { return key == entry->fValue; }
Entry*& GetLink(Entry* entry) const { return entry->fNext; }
};
}
class OpenHashTableTest : public CppUnit::TestFixture {
CPPUNIT_TEST_SUITE(OpenHashTableTest);
CPPUNIT_TEST(Init_ZeroSize_ReturnsOk);
CPPUNIT_TEST(Clear_EmptiesTable);
CPPUNIT_TEST(Clear_ReturnElementsSet_ClearsAndReturnsEntries);
CPPUNIT_TEST(Insert_AutoExpandDisabled_InsertsEntriesWithoutResize);
CPPUNIT_TEST(Insert_DuplicateEntry_ReturnsError);
CPPUNIT_TEST(Insert_InsertsEntry_ReturnsOk);
CPPUNIT_TEST(InsertUnchecked_InsertsEntry_ReturnsOk);
CPPUNIT_TEST(IterateAndCount_IteratesEntries_ReturnsCorrectCount);
CPPUNIT_TEST(Lookup_ExistingKey_ReturnsEntry);
CPPUNIT_TEST(Remove_ExistingEntry_ReturnsOk);
CPPUNIT_TEST(Remove_NotPresent_ReturnsNull);
CPPUNIT_TEST(RemoveUnchecked_RemovesEntry_ReturnsOk);
CPPUNIT_TEST(Resize_InsertsManyEntries_ResizesSuccessfully);
CPPUNIT_TEST_SUITE_END();
public:
void Init_ZeroSize_ReturnsOk()
{
Entry entry(123);
BOpenHashTable<EntryDefinition> table;
CPPUNIT_ASSERT_EQUAL(table.Init(0), B_OK);
}
void Clear_EmptiesTable()
{
const size_t kEntryCount = 3;
BObjectList<Entry, true> entries(20);
BOpenHashTable<EntryDefinition> table;
CPPUNIT_ASSERT_EQUAL(table.Init(), B_OK);
for (uint32_t i = 0; i < kEntryCount; ++i) {
Entry* entry = new Entry(i);
entries.AddItem(entry);
CPPUNIT_ASSERT_EQUAL(table.Insert(entry), B_OK);
}
CPPUNIT_ASSERT_EQUAL(table.CountElements(), kEntryCount);
CPPUNIT_ASSERT(table.Lookup(2) != NULL);
CPPUNIT_ASSERT_EQUAL(table.Clear(false), NULL);
CPPUNIT_ASSERT_EQUAL(table.CountElements(), 0);
CPPUNIT_ASSERT_EQUAL(table.Lookup(2), NULL);
CPPUNIT_ASSERT_EQUAL(table.GetIterator().HasNext(), false);
}
void Clear_ReturnElementsSet_ClearsAndReturnsEntries()
{
const size_t kEntryCount = 3;
BOpenHashTable<EntryDefinition> table;
CPPUNIT_ASSERT_EQUAL(table.Init(), B_OK);
for (uint32_t i = 0; i < kEntryCount; ++i) {
Entry* entry = new Entry(i);
CPPUNIT_ASSERT_EQUAL(table.Insert(entry), B_OK);
}
CPPUNIT_ASSERT_EQUAL(table.CountElements(), kEntryCount);
CPPUNIT_ASSERT(table.Lookup(2) != NULL);
Entry* head = table.Clear(true);
CPPUNIT_ASSERT(head != NULL);
CPPUNIT_ASSERT_EQUAL(table.CountElements(), 0);
CPPUNIT_ASSERT_EQUAL(table.Lookup(2), NULL);
CPPUNIT_ASSERT_EQUAL(table.GetIterator().HasNext(), false);
size_t items_returned = 0;
while (head != NULL) {
Entry* next = head->Next();
delete head;
head = next;
++items_returned;
}
CPPUNIT_ASSERT_EQUAL(items_returned, kEntryCount);
}
void Insert_AutoExpandDisabled_InsertsEntriesWithoutResize()
{
Entry entry1(123);
Entry entry2(456);
BOpenHashTable<EntryDefinition, false> table;
CPPUNIT_ASSERT_EQUAL(table.Init(1), B_OK);
CPPUNIT_ASSERT_EQUAL(table.Insert(&entry1), B_OK);
CPPUNIT_ASSERT_EQUAL(table.Insert(&entry2), B_OK);
CPPUNIT_ASSERT_EQUAL(table.CountElements(), 2);
}
void Insert_DuplicateEntry_ReturnsError()
{
Entry entry(123);
BOpenHashTable<EntryDefinition, true, true> table;
CPPUNIT_ASSERT_EQUAL(table.Init(0), B_OK);
CPPUNIT_ASSERT_EQUAL(table.Insert(&entry), B_OK);
CPPUNIT_ASSERT_EQUAL(table.Lookup(123), &entry);
CPPUNIT_ASSERT_DEBUGGER(table.Insert(&entry));
CPPUNIT_ASSERT_EQUAL(table.Lookup(123), &entry);
CPPUNIT_ASSERT_DEBUGGER(table.Remove(&entry));
CPPUNIT_ASSERT_EQUAL(table.Lookup(123), &entry);
CPPUNIT_ASSERT_DEBUGGER(table.Remove(&entry));
CPPUNIT_ASSERT_EQUAL(table.Lookup(123), &entry);
}
void Insert_InsertsEntry_ReturnsOk()
{
Entry entry(123);
BOpenHashTable<EntryDefinition> table;
CPPUNIT_ASSERT_EQUAL(table.Init(), B_OK);
CPPUNIT_ASSERT_EQUAL(table.Insert(&entry), B_OK);
}
void InsertUnchecked_InsertsEntry_ReturnsOk()
{
Entry entry(123);
BOpenHashTable<EntryDefinition> table;
CPPUNIT_ASSERT_EQUAL(table.Init(), B_OK);
table.InsertUnchecked(&entry);
}
void IterateAndCount_IteratesEntries_ReturnsCorrectCount()
{
const size_t kEntryCount = 20;
BObjectList<Entry, true> entries(20);
BOpenHashTable<EntryDefinition> table;
CPPUNIT_ASSERT_EQUAL(table.Init(kEntryCount * 2), B_OK);
for (uint32_t i = 0; i < kEntryCount; ++i) {
Entry* entry = new Entry(i);
entries.AddItem(entry);
CPPUNIT_ASSERT_EQUAL(table.Insert(entry), B_OK);
}
uint64_t map = 0;
BOpenHashTable<EntryDefinition>::Iterator iterator = table.GetIterator();
while (iterator.HasNext()) {
Entry* entry = iterator.Next();
CPPUNIT_ASSERT_EQUAL(0, map & (1 << entry->Value()));
map |= (1 << entry->Value());
}
CPPUNIT_ASSERT_EQUAL(map, (1 << kEntryCount) - 1);
CPPUNIT_ASSERT_EQUAL(kEntryCount, table.CountElements());
}
void Lookup_ExistingKey_ReturnsEntry()
{
Entry entry(123);
BOpenHashTable<EntryDefinition> table;
CPPUNIT_ASSERT_EQUAL(table.Init(0), B_OK);
CPPUNIT_ASSERT_EQUAL(table.Insert(&entry), B_OK);
CPPUNIT_ASSERT_EQUAL(table.Lookup(123), &entry);
}
void Remove_ExistingEntry_ReturnsOk()
{
Entry entry(123);
BOpenHashTable<EntryDefinition> table;
CPPUNIT_ASSERT_EQUAL(table.Init(0), B_OK);
CPPUNIT_ASSERT_EQUAL(table.Insert(&entry), B_OK);
CPPUNIT_ASSERT_EQUAL(table.Lookup(123), &entry);
table.Remove(&entry);
CPPUNIT_ASSERT_EQUAL(table.Lookup(123), NULL);
}
void Remove_NotPresent_ReturnsNull()
{
Entry entry1(123);
Entry entry2(456);
Entry entry3(789);
BOpenHashTable<EntryDefinition> table;
CPPUNIT_ASSERT_EQUAL(table.Init(), B_OK);
table.Insert(&entry1);
table.Insert(&entry2);
table.Remove(&entry3);
table.RemoveUnchecked(&entry3);
CPPUNIT_ASSERT_EQUAL(table.CountElements(), 2);
CPPUNIT_ASSERT_EQUAL(table.Lookup(123), &entry1);
CPPUNIT_ASSERT_EQUAL(table.Lookup(456), &entry2);
CPPUNIT_ASSERT_EQUAL(table.Lookup(789), NULL);
}
void RemoveUnchecked_RemovesEntry_ReturnsOk()
{
Entry entry(123);
BOpenHashTable<EntryDefinition> table;
CPPUNIT_ASSERT_EQUAL(table.Init(0), B_OK);
CPPUNIT_ASSERT_EQUAL(table.Insert(&entry), B_OK);
CPPUNIT_ASSERT_EQUAL(table.Lookup(123), &entry);
table.RemoveUnchecked(&entry);
CPPUNIT_ASSERT_EQUAL(table.Lookup(123), NULL);
}
void Resize_InsertsManyEntries_ResizesSuccessfully()
{
const size_t kEntryCount = 20;
BObjectList<Entry, true> entries(20);
BOpenHashTable<EntryDefinition> table;
CPPUNIT_ASSERT_EQUAL(table.Init(8), B_OK);
for (uint32_t i = 0; i < kEntryCount; ++i) {
Entry* entry = new Entry(i);
entries.AddItem(entry);
CPPUNIT_ASSERT_EQUAL(table.Insert(entry), B_OK);
}
uint64_t map = 0;
BOpenHashTable<EntryDefinition>::Iterator iterator = table.GetIterator();
while (iterator.HasNext()) {
Entry* entry = iterator.Next();
CPPUNIT_ASSERT_EQUAL(0, map & (1 << entry->Value()));
map |= (1 << entry->Value());
}
CPPUNIT_ASSERT_EQUAL(map, (1 << kEntryCount) - 1);
CPPUNIT_ASSERT_EQUAL(kEntryCount, table.CountElements());
}
};
CPPUNIT_TEST_SUITE_NAMED_REGISTRATION(OpenHashTableTest, getTestSuiteName());