/* * Copyright 2014 (c) Anna Schumaker. * Test a Database */ #include #include #include #include /* * Derive a DatabaseEntry for storing integerss */ class IntEntry : public DatabaseEntry { public: unsigned int val; IntEntry() : val(0) {}; IntEntry(unsigned int v) : val(v) {}; const std::string primary_key() const { return string :: utos(val); } void write(File &f) { f << val; } void read(File &f) { f >> val; } }; static unsigned int N = 0; static bool AUTOSAVE = false; static Database *DB = NULL; static Database::iterator IT; static std::vector *POINTERS; static unsigned int _test_insertion_check(unsigned int i) { IntEntry *it = DB->insert(IntEntry(i)); if ((it == NULL) || (it->index() != i) || (it->val != i)) return LOOP_FAILED; POINTERS->push_back(it); return (DB->insert(IntEntry(i)) == NULL) ? LOOP_PASSED : LOOP_FAILED; } static unsigned int _test_insertion_pointer_check(unsigned int i) { return (POINTERS->at(i) == DB->at(i)) ? LOOP_PASSED : LOOP_FAILED; } static void test_insertion() { /* Check initial sizes. */ test_equal(DB->size(), (unsigned)0); test_equal(DB->actual_size(), (unsigned)0); test_for_each(0, N, 1, _test_insertion_check); test_equal(DB->size(), N); test_equal(DB->actual_size(), N); /* Pointers should still be valid. */ test_for_each(0, N, 1, _test_insertion_pointer_check); } static void test_removal() { /* 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 unsigned int _test_access_null(unsigned int i) { return (DB->at(i) == NULL) ? LOOP_PASSED : LOOP_FAILED; } static unsigned int _test_access_valid(unsigned int i) { IntEntry *ie = DB->at(i); if ((ie == NULL) || (*IT != ie) || (ie->val != i) || (ie->index() != i)) return LOOP_FAILED; IT = DB->next(IT); return LOOP_PASSED; } static void test_access() { IT = DB->begin(); test_for_each(0, N, 2, _test_access_null); test_for_each(1, N, 2, _test_access_valid); test_for_each(N, N + 10, 1, _test_access_null); } static unsigned int _test_reinsert_check(unsigned int i) { IntEntry *it = DB->insert(IntEntry(i)); return (it->index() == (N + (i / 2))) ? LOOP_PASSED : LOOP_FAILED; } static void test_reinsert() { test_for_each(0, N, 2, _test_reinsert_check); test_equal(DB->size(), N); test_equal(DB->actual_size(), N + (N / 2)); } static Database *DB2; static unsigned int _test_save_load_check(unsigned int i) { IntEntry *ie1 = DB->at(i); IntEntry *ie2 = DB2->at(i); if (ie1 == NULL) return (ie2 == NULL) ? LOOP_PASSED : LOOP_FAILED; else { if ((ie2 == NULL) || (ie1->index() != ie2->index()) || (ie1->val != ie2->val)) return LOOP_FAILED; } return LOOP_PASSED; } static void test_save_load() { Database db2("database.db", false); db2.load(); DB2 = &db2; 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()); test_for_each(0, DB->actual_size(), 1, _test_save_load_check); } static void db_test(unsigned int n, bool autosave) { std::string n_str = " (n = " + string :: utos(n) + ")"; Database db("database.db", autosave); std::vector pointers; N = n; DB = &db; AUTOSAVE = autosave; POINTERS = &pointers; test :: rm_data_dir(); test :: run("Database Insertion Test" + n_str, test_insertion); test :: run("Database Removal Test" + n_str, test_removal); test :: run("Database Access Test" + n_str, test_access); test :: run("Database Reinsertion Test" + n_str, test_reinsert); test :: run("Database Save and Load Test" + n_str, test_save_load); } int main(int argc, char **argv) { db_test(0, true); db_test(10, true); db_test(100, true); db_test(1000, true); db_test(10000, false); db_test(100000, false); return 0; }