ocarina/tests/core/database.cpp

239 lines
5.9 KiB
C++
Raw Normal View History

/*
* Copyright 2014 (c) Anna Schumaker.
* Test a Database
*/
#include <core/database.h>
#include <core/string.h>
#include "test.h"
#include <vector>
/*
* Derive a DatabaseEntry for storing integerss
*/
struct int_entry : public DatabaseEntry {
unsigned int ie_val;
int_entry() : ie_val(0) {};
int_entry(unsigned int v) : ie_val(v) {};
int_entry(const std::string &k) { sscanf(k.c_str(), "%u", &ie_val); }
const std::string primary_key() const
{
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", ie_val); }
void read(file &f) { file_readf(&f, "%u", &ie_val); }
};
static void test_db_entry()
{
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_actual_size(&db), 0);
test_equal(db.db_size, 0);
}
static void test_stress(unsigned int N)
{
database<struct int_entry> db("stress.db", false);
std::vector<struct int_entry *> ptrs;
struct int_entry *dbe, *next, rmv(42);
unsigned int i;
gchar *key;
/* db_insert() */
for (i = 0; i < N; i++) {
dbe = db_insert(&db, new int_entry(i));
test_loop_not_equal(dbe, NULL, i);
test_loop_equal(dbe->index(), i, i);
test_loop_equal(dbe->ie_val, i, i);
ptrs.push_back(dbe);
} test_loop_passed();
dbe = db_insert(&db, (struct int_entry *)NULL);
test_equal(dbe, NULL);
test_equal(db.db_size, N);
test_equal(db_actual_size(&db), N);
/* db_at() */
for (i = 0; i < N; i++) {
dbe = db_at(&db, 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(&db, N), NULL);
/* db_get() */
for (i = 0; i < N; i++) {
key = g_strdup_printf("%u", i);
dbe = db_get(&db, 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_get(&db, key), NULL);
g_free(key);
/* db_find() */
for (i = 0; i <= N; i++) {
key = g_strdup_printf("%u", i);
dbe = db_find(&db, key);
test_loop_not_equal(dbe, NULL, i);
test_loop_equal(dbe->primary_key(), key, i);
test_loop_equal(dbe->ie_val, i, i);
if (i < N)
test_loop_equal(dbe, ptrs.at(i), i);
g_free(key);
} test_loop_passed();
test_equal(db.db_size, N + 1);
test_equal(db_actual_size(&db), N + 1);
/* db_remove(): Even indices only! */
for (i = 0; i <= (N + 2); i += 2) {
key = g_strdup_printf("%u", i);
dbe = db_get(&db, key);
db_remove(&db, dbe);
test_loop_equal(db_at(&db, i), NULL, i);
test_loop_equal(db_get(&db, key), NULL, i);
g_free(key);
} test_loop_passed();
db_remove(&db, &rmv);
test_equal(db.db_size, N / 2);
test_equal(db_actual_size(&db), N + 1);
/* db_for_each() (db_first() / db_next()) */
i = 1;
db_for_each(dbe, next, &db) {
test_loop_not_equal(dbe, NULL, i);
if (i == (N - 1)) {
test_loop_equal(next, NULL, i);
} else {
test_loop_not_equal(next, NULL, i);
test_loop_equal(next->ie_val, i + 2, i);
}
test_loop_equal(dbe, ptrs.at(i), i);
test_loop_equal(dbe->ie_val, i, i);
test_loop_equal(db_next(&db, dbe), next, 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()
{
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++)
db_insert(&db1, new int_entry(i));
db_load(&db2);
test_equal(db2.db_size, N);
test_equal(db_actual_size(&db2), N);
for (i = 0; i < N; i++) {
dbe = db_at(&db2, 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)
db_remove(&db1, db_at(&db1, i));
db2 = database<struct int_entry>("save_load.db", false);
db_load(&db2);
test_equal(db2.db_size, N / 2);
for (i = 1; i < N; i++) {
dbe = db_at(&db2, 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++)
db_insert(&db2, new int_entry(i));
for (i = N; i < (2 * N); i += 2)
db_remove(&db2, db_at(&db2, i));
db1 = database<struct int_entry>("save_load.db", false);
db_load(&db1);
test_equal(db1.db_size, N / 2);
db_save(&db2);
db1 = database<struct int_entry>("save_load.db", false);
db_load(&db1);
test_equal(db1.db_size, N);
test_equal(db_actual_size(&db1), 2 * N);
for (i = 1; i < (2 * N); i++) {
dbe = db_at(&db1, 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();
}
DECLARE_UNIT_TESTS(
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),
);