core/database: Rewrite unit test
I want a more thorough set of tests before I begin modifying the database class. Signed-off-by: Anna Schumaker <Anna@OcarinaProject.net>
This commit is contained in:
parent
0b8dd94213
commit
385515edbc
|
@ -12,174 +12,205 @@
|
|||
/*
|
||||
* Derive a DatabaseEntry for storing integerss
|
||||
*/
|
||||
class IntEntry : public DatabaseEntry {
|
||||
public:
|
||||
unsigned int val;
|
||||
struct int_entry : public DatabaseEntry {
|
||||
unsigned int ie_val;
|
||||
|
||||
IntEntry() : val(0) {};
|
||||
IntEntry(unsigned int v) : val(v) {};
|
||||
int_entry() : ie_val(0) {};
|
||||
int_entry(unsigned int v) : ie_val(v) {};
|
||||
|
||||
const std::string primary_key() const
|
||||
{
|
||||
gchar *g_val = g_strdup_printf("%u", val);
|
||||
gchar *g_val = g_strdup_printf("%u", ie_val);
|
||||
std::string res = g_val;
|
||||
g_free(g_val);
|
||||
return res;
|
||||
}
|
||||
|
||||
void write(file &f) { file_writef(&f, "%u", val); }
|
||||
void read(file &f) { file_readf(&f, "%u", &val); }
|
||||
void write(file &f) { file_writef(&f, "%u", ie_val); }
|
||||
void read(file &f) { file_readf(&f, "%u", &ie_val); }
|
||||
};
|
||||
|
||||
|
||||
static Database<IntEntry> *DB = NULL;
|
||||
|
||||
|
||||
static void test_insertion(unsigned int n, bool autosave)
|
||||
static void test_db_entry()
|
||||
{
|
||||
test_rm_data_dir();
|
||||
DB = new Database<IntEntry>("database.db", autosave);
|
||||
std::vector<IntEntry *> pointers;
|
||||
struct int_entry *ent = new struct int_entry(1);
|
||||
struct file f;
|
||||
|
||||
test_equal(ent->index(), 0);
|
||||
test_equal(ent->ie_val, 1);
|
||||
test_equal(ent->primary_key(), "1");
|
||||
|
||||
file_init(&f, "test_db_entry", 0);
|
||||
file_open(&f, OPEN_WRITE);
|
||||
ent->write(f);
|
||||
file_close(&f);
|
||||
delete ent;
|
||||
|
||||
ent = new int_entry();
|
||||
test_equal(ent->ie_val, 0);
|
||||
test_equal(ent->primary_key(), "0");
|
||||
|
||||
file_open(&f, OPEN_READ);
|
||||
ent->read(f);
|
||||
file_close(&f);
|
||||
|
||||
test_equal(ent->ie_val, 1);
|
||||
test_equal(ent->primary_key(), "1");
|
||||
delete ent;
|
||||
}
|
||||
|
||||
static void test_init()
|
||||
{
|
||||
Database<struct int_entry> db("database.db", false);
|
||||
|
||||
/* Check initial sizes. */
|
||||
test_equal(DB->size(), (unsigned)0);
|
||||
test_equal(DB->actual_size(), (unsigned)0);
|
||||
test_equal(db.actual_size(), 0);
|
||||
test_equal(db.size(), 0);
|
||||
}
|
||||
|
||||
for (unsigned int i = 0; i < n; i++) {
|
||||
IntEntry *it = DB->insert(IntEntry(i));
|
||||
test_loop_not_equal(it, NULL, i);
|
||||
test_loop_equal(it->index(), i, i);
|
||||
test_loop_equal(it->val, i, i);
|
||||
test_loop_equal(DB->insert(IntEntry(i)), NULL, i);
|
||||
pointers.push_back(it);
|
||||
static void test_stress(unsigned int N)
|
||||
{
|
||||
Database<struct int_entry> db("stress.db", false);
|
||||
Database<struct int_entry>::iterator it;
|
||||
std::vector<struct int_entry *> ptrs;
|
||||
struct int_entry *dbe;
|
||||
unsigned int i;
|
||||
gchar *key;
|
||||
|
||||
/* database.insert() */
|
||||
for (i = 0; i < N; i++) {
|
||||
dbe = db.insert(int_entry(i));
|
||||
test_loop_not_equal(dbe, NULL, i);
|
||||
test_loop_equal(db.insert(int_entry(i)), NULL, i);
|
||||
test_loop_equal(dbe->index(), i, i);
|
||||
test_loop_equal(dbe->ie_val, i, i);
|
||||
ptrs.push_back(dbe);
|
||||
} test_loop_passed();
|
||||
|
||||
test_equal(DB->size(), n);
|
||||
test_equal(DB->actual_size(), n);
|
||||
test_equal(db.size(), N);
|
||||
test_equal(db.actual_size(), N);
|
||||
|
||||
/* Pointers should still be valid. */
|
||||
for (unsigned int i = 0; i < n; i++)
|
||||
test_loop_equal(pointers.at(i), DB->at(i), i);
|
||||
test_loop_passed();
|
||||
}
|
||||
|
||||
static void test_removal(unsigned int n, bool autosave)
|
||||
{
|
||||
/* Size should have only changed once each iteration. */
|
||||
for (unsigned int i = 0; i < n; i+=2) {
|
||||
DB->remove(i);
|
||||
DB->remove(i);
|
||||
}
|
||||
|
||||
test_equal(DB->size(), n / 2);
|
||||
test_equal(DB->actual_size(), n);
|
||||
|
||||
/* Test out-of-bounds removal. */
|
||||
for (unsigned int i = 0; i < 10; i++)
|
||||
DB->remove(n + i);
|
||||
|
||||
test_equal(DB->size(), n / 2);
|
||||
test_equal(DB->actual_size(), n);
|
||||
}
|
||||
|
||||
static void test_access(unsigned int n, bool autosave)
|
||||
{
|
||||
Database<IntEntry>::iterator it;
|
||||
IntEntry *ie;
|
||||
|
||||
for (unsigned int i = 0; i < n; i += 2)
|
||||
test_loop_equal(DB->at(i), NULL, i);
|
||||
test_loop_passed();
|
||||
|
||||
it = DB->begin();
|
||||
for (unsigned int i = 1; i < n; i += 2) {
|
||||
ie = DB->at(i);
|
||||
test_loop_not_equal(ie, NULL, i);
|
||||
test_loop_equal(ie->val, i, i);
|
||||
test_loop_equal(ie->index(), i, i);
|
||||
test_loop_equal(*it, ie, i);
|
||||
it = DB->next(it);
|
||||
/* database.at() */
|
||||
for (i = 0; i < N; i++) {
|
||||
dbe = db.at(i);
|
||||
test_loop_not_equal(dbe, NULL, i);
|
||||
test_loop_equal(dbe, ptrs.at(i), i);
|
||||
test_loop_equal(dbe->ie_val, i, i);
|
||||
} test_loop_passed();
|
||||
test_equal(db.at(N), NULL);
|
||||
|
||||
for (unsigned int i = n; i < n + 10; i++)
|
||||
test_loop_equal(DB->at(i), NULL, i);
|
||||
test_loop_passed();
|
||||
}
|
||||
|
||||
|
||||
static void test_reinsertion(unsigned int n, bool autosave)
|
||||
{
|
||||
IntEntry *it;
|
||||
|
||||
for (unsigned int i = 0; i < n; i += 2) {
|
||||
it = DB->insert(IntEntry(i));
|
||||
test_loop_equal(it->index(), (n + (i / 2)), i);
|
||||
/* database.find() */
|
||||
for (i = 0; i < N; i++) {
|
||||
key = g_strdup_printf("%u", i);
|
||||
dbe = db.find(key);
|
||||
test_loop_not_equal(dbe, NULL, i);
|
||||
test_loop_equal(dbe->primary_key(), key, i);
|
||||
test_loop_equal(dbe, ptrs.at(i), i);
|
||||
test_loop_equal(dbe->ie_val, i, i);
|
||||
g_free(key);
|
||||
} test_loop_passed();
|
||||
key = g_strdup_printf("%u", N);
|
||||
test_equal(db.find(key), NULL);
|
||||
g_free(key);
|
||||
|
||||
test_equal(DB->size(), n);
|
||||
test_equal(DB->actual_size(), n + (n / 2));
|
||||
/* database.remove(): Even indices only! */
|
||||
for (i = 0; i < N; i += 2) {
|
||||
key = g_strdup_printf("%u", i);
|
||||
db.remove(i);
|
||||
db.remove(i);
|
||||
test_loop_equal(db.at(i), NULL, i);
|
||||
test_loop_equal(db.find(key), NULL, i);
|
||||
g_free(key);
|
||||
} test_loop_passed();
|
||||
db.remove(N);
|
||||
test_equal(db.size(), N / 2);
|
||||
test_equal(db.actual_size(), N);
|
||||
|
||||
/* database.first(), database.next(), database.end() */
|
||||
i = 1;
|
||||
for (it = db.begin(); it != db.end(); it = db.next(it)) {
|
||||
test_loop_not_equal(*it, NULL, i);
|
||||
test_loop_equal(*it, ptrs.at(i), i);
|
||||
test_loop_equal((*it)->ie_val, i, i);
|
||||
i += 2;
|
||||
} test_loop_passed();
|
||||
test_equal(i, N + 1);
|
||||
}
|
||||
|
||||
static void test_basics() { test_stress(10); }
|
||||
static void test_stress_0() { test_stress(0); }
|
||||
static void test_stress_100K() { test_stress(100000); }
|
||||
|
||||
static void test_save_load(unsigned int n, bool autosave)
|
||||
static void test_save_load()
|
||||
{
|
||||
Database<IntEntry> db2("database.db", false);
|
||||
IntEntry *ie1, *ie2;
|
||||
Database<struct int_entry> db1("save_load.db", true);
|
||||
Database<struct int_entry> db2("save_load.db", false);
|
||||
struct int_entry *dbe;
|
||||
const unsigned int N = 10;
|
||||
unsigned int i;
|
||||
|
||||
/* 10 items should "autosave" when inserted */
|
||||
for (i = 0; i < N; i++)
|
||||
db1.insert(int_entry(i));
|
||||
|
||||
db2.load();
|
||||
|
||||
if (autosave == false) {
|
||||
test_equal(db2.size(), (unsigned)0);
|
||||
test_equal(db2.actual_size(), (unsigned)0);
|
||||
|
||||
DB->save();
|
||||
db2.load();
|
||||
}
|
||||
|
||||
test_equal(db2.size(), DB->size());
|
||||
test_equal(db2.actual_size(), DB->actual_size());
|
||||
|
||||
for (unsigned int i = 0; i < DB->actual_size(); i++) {
|
||||
ie1 = DB->at(i);
|
||||
ie2 = db2.at(i);
|
||||
if (ie1) {
|
||||
test_loop_equal(ie2->index(), ie1->index(), i);
|
||||
test_loop_equal(ie2->val, ie1->val, i);
|
||||
} else
|
||||
test_loop_equal(ie2, NULL, i);
|
||||
test_equal(db2.size(), N);
|
||||
test_equal(db2.actual_size(), N);
|
||||
for (i = 0; i < N; i++) {
|
||||
dbe = db2.at(i);
|
||||
test_loop_not_equal(dbe, NULL, i);
|
||||
test_loop_equal(dbe->ie_val, i, i);
|
||||
} test_loop_passed();
|
||||
|
||||
/* Removing 5 items, should also trigger autosaving. */
|
||||
for (i = 0; i < N; i += 2)
|
||||
db1.remove(i);
|
||||
|
||||
db2 = Database<struct int_entry>("save_load.db", false);
|
||||
db2.load();
|
||||
test_equal(db2.size(), N / 2);
|
||||
for (i = 1; i < N; i++) {
|
||||
dbe = db2.at(i);
|
||||
if ((i % 2) == 0) {
|
||||
test_loop_equal(dbe, NULL, i);
|
||||
} else {
|
||||
test_loop_not_equal(dbe, NULL, i);
|
||||
test_loop_equal(dbe->ie_val, i, i);
|
||||
}
|
||||
} test_loop_passed();
|
||||
|
||||
/* Test with autosave turned off. */
|
||||
for (i = N; i < (2 * N); i++)
|
||||
db2.insert(int_entry(i));
|
||||
for (i = N; i < (2 * N); i += 2)
|
||||
db2.remove(i);
|
||||
|
||||
db1 = Database<struct int_entry>("save_load.db", false);
|
||||
db1.load();
|
||||
test_equal(db1.size(), N / 2);
|
||||
|
||||
db2.save();
|
||||
db1 = Database<struct int_entry>("save_load.db", false);
|
||||
db1.load();
|
||||
|
||||
test_equal(db1.size(), N);
|
||||
test_equal(db1.actual_size(), 2 * N);
|
||||
for (i = 1; i < (2 * N); i++) {
|
||||
dbe = db1.at(i);
|
||||
if ((i % 2) == 0) {
|
||||
test_loop_equal(dbe, NULL, i);
|
||||
} else {
|
||||
test_loop_not_equal(dbe, NULL, i);
|
||||
test_loop_equal(dbe->ie_val, i, i);
|
||||
}
|
||||
} test_loop_passed();
|
||||
delete DB;
|
||||
}
|
||||
|
||||
|
||||
#define DB_TEST_FUNCS(n, autosave) \
|
||||
static void test_insertion_##n( ) { test_insertion(n, autosave); } \
|
||||
static void test_removal_##n() { test_removal(n, autosave); } \
|
||||
static void test_access_##n() { test_access(n, autosave); } \
|
||||
static void test_reinsertion_##n() { test_reinsertion(n, autosave); } \
|
||||
static void test_save_load_##n() { test_save_load(n, autosave); }
|
||||
|
||||
|
||||
DB_TEST_FUNCS(0, true)
|
||||
DB_TEST_FUNCS(10, true)
|
||||
DB_TEST_FUNCS(100, true)
|
||||
DB_TEST_FUNCS(1000, true)
|
||||
DB_TEST_FUNCS(10000, false)
|
||||
DB_TEST_FUNCS(100000, false)
|
||||
|
||||
|
||||
#define DB_UNIT_TESTS(n) \
|
||||
UNIT_TEST("Database Insertion (n = " #n ")", test_insertion_##n), \
|
||||
UNIT_TEST("Database Removal (n = " #n ")", test_removal_##n), \
|
||||
UNIT_TEST("Database Access (n = " #n ")", test_access_##n), \
|
||||
UNIT_TEST("Database Reinsertion (n = " #n ")", test_reinsertion_##n), \
|
||||
UNIT_TEST("Database Save and Load (n = " #n ")", test_save_load_##n)
|
||||
|
||||
|
||||
DECLARE_UNIT_TESTS(
|
||||
DB_UNIT_TESTS(0),
|
||||
DB_UNIT_TESTS(10),
|
||||
DB_UNIT_TESTS(100),
|
||||
DB_UNIT_TESTS(1000),
|
||||
DB_UNIT_TESTS(10000),
|
||||
DB_UNIT_TESTS(100000),
|
||||
UNIT_TEST("Database Entry", test_db_entry),
|
||||
UNIT_TEST("Database Initialization", test_init),
|
||||
UNIT_TEST("Database Basics", test_basics),
|
||||
UNIT_TEST("Database Stress (n = 0)", test_stress_0),
|
||||
UNIT_TEST("Database Stress (n = 100,000)", test_stress_100K),
|
||||
UNIT_TEST("Database Save and Load", test_save_load),
|
||||
);
|
||||
|
|
Loading…
Reference in New Issue