diff --git a/tests/core/database.cpp b/tests/core/database.cpp index 4019c567..f28ab1ac 100644 --- a/tests/core/database.cpp +++ b/tests/core/database.cpp @@ -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 *DB = NULL; - - -static void test_insertion(unsigned int n, bool autosave) +static void test_db_entry() { - test_rm_data_dir(); - DB = new Database("database.db", autosave); - std::vector 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 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 db("stress.db", false); + Database::iterator it; + std::vector 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::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 db2("database.db", false); - IntEntry *ie1, *ie2; + Database db1("save_load.db", true); + Database 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("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("save_load.db", false); + db1.load(); + test_equal(db1.size(), N / 2); + + db2.save(); + db1 = Database("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), );