diff --git a/tests/Sconscript b/tests/Sconscript index d05abdba..e1b7f05c 100644 --- a/tests/Sconscript +++ b/tests/Sconscript @@ -14,8 +14,8 @@ for arg in sys.argv: src = SConscript("src/Sconscript") -tests = [ "version" , "file" ] -#, "file", "database", "index", "filter", "idle", "tag_db", +tests = [ "version" , "file", "database" ] +# "database", "index", "filter", "idle", "tag_db", # "queue" ] #scripts = [ "playlist", "library", "deck", "audio", "gui" ] diff --git a/tests/database b/tests/database deleted file mode 100755 index 77d19137..00000000 --- a/tests/database +++ /dev/null @@ -1,53 +0,0 @@ -#!/bin/bash -# Copyright 2014 (c) Anna Schumaker - -. $(dirname $0)/_functions - - -### -# -# Now, test the actual database -# - -function test_autosave -{ - new_test "Database Test (n = $1, autosave = true)" - src/database.run -a $1 - if [ ! -f $DATA_DIR/database.db ] && [ $1 != 0 ]; then - echo "ERROR: $DATA_DIR/database.db doesn't exist!" - exit 1 - fi -} - -function test_noautosave -{ - new_test "Database Test (n = $1, autosave = false)" - src/database.run $1 - if [ -f $DATA_DIR/database.db ]; then - echo "ERROR: $DATA_DIR/database.db exists!" - exit 1 - fi -} - -function run_test -{ - rm $DATA_DIR/* 2>/dev/null || true - - if [ $1 -le 1000 ]; then - test_autosave $1 - else - test_noautosave $1 - fi -} - -run_test 0 -echo -run_test 10 -echo -run_test 100 -echo -run_test 1000 -echo -run_test 10000 -echo -run_test 100000 diff --git a/tests/src/database.cpp b/tests/src/database.cpp index 84887c10..a72e228c 100644 --- a/tests/src/database.cpp +++ b/tests/src/database.cpp @@ -4,15 +4,16 @@ */ #include -#include +#include "test.h" #include -#include -#include -unsigned int test_num = 0; - +/*** + * + * Derive a DatabaseEntry for storing ints + * + */ class IntEntry : public DatabaseEntry { public: @@ -23,11 +24,11 @@ public: const std::string primary_key() const; void write(File &); void read(File &); - void print(); }; IntEntry :: IntEntry() : val(0) {} IntEntry :: IntEntry(unsigned int v) : val(v) {} + const std::string IntEntry :: primary_key() const { std::stringstream ss; @@ -37,224 +38,268 @@ const std::string IntEntry :: primary_key() const void IntEntry :: write(File &f) { f << val; } void IntEntry :: read(File &f) { f >> val; } -void IntEntry :: print() { :: print(primary_key().c_str()); } -void test_results(bool success, unsigned int line) +static IntEntry *INT_NULL = NULL; + + + +/*** + * + * Struct for passing around arguments to tests + * + */ + +struct TestArgs { + const unsigned int n; + unsigned int size; + unsigned int actual; + bool autosave; + + IntEntry *one; + IntEntry *three; + Database *db; + + TestArgs(const unsigned int, bool, Database *); +}; + +TestArgs :: TestArgs(const unsigned int _n, bool _asv, Database *_db) + : n(_n), size(0), actual(0), autosave(_asv), + one(NULL), three(NULL), db(_db) +{} + + +/*** + * + * Run tests with varying database sizes + * + */ + +static void test_insertion(struct TestArgs *args) { - print(" %u: ", test_num); - if (success) - print("Success!\n"); - else { - print("FAILED (%u) =(\n", line); - exit(1); - } - test_num++; -} - -void test_size(Database &db, unsigned int size, - unsigned int actual, unsigned int line) -{ - test_results( (db.size() == size) && (db.actual_size() == actual), line ); -} - -int main(int argc, char **argv) -{ - bool autosave = false; - unsigned int n = 0, size = 0, actual = 0; - char c; - - while ((c = getopt(argc, argv, "a")) != -1) { - switch (c) { - case 'a': - autosave = true; - break; - } - } - - n = atoi(argv[optind]); - Database db("database.db", autosave); - - - /** - * 0: Test initial size + /* + * Initial size should be 0 */ - test_size(db, size, actual, __LINE__); + test_equal(args->db->size(), args->size); + test_equal(args->db->actual_size(), args->actual); - - /** - * 1: Test insertion - */ - IntEntry *one, *three; - for (unsigned int i = 0; i < n; i++) { - IntEntry *it = db.insert(IntEntry(i)); - if (it == NULL) - test_results(false, __LINE__); - if (it->id != i) - test_results(false, __LINE__); - if (it->val != i) - test_results(false, __LINE__); + test :: begin(); + for (unsigned int i = 0; i < args->n; i++) { + IntEntry *it = args->db->insert(IntEntry(i)); + check_not_equal(it, INT_NULL); + check_equal(it->id, i); + check_equal(it->val, i); /* * Pointers should still be valid after more insertions. * These will be checked later */ if (i == 1) - one = it; + args->one = it; if (i == 3) - three = it; + args->three = it; + + args->size++; + args->actual++; } - test_results(true, __LINE__); - size += n; - actual += n; + test :: success(); - - /** - * 2: Test that size changes + /* + * Size should change */ - test_size(db, size, actual, __LINE__); + test_equal(args->db->size(), args->size); + test_equal(args->db->actual_size(), args->actual); +} - - /** - * 3: Test inserting ... again. - */ - for (unsigned int i = 0; i < n; i++) { - IntEntry *it = db.insert(IntEntry(i)); - if (it != NULL) - test_results(false, __LINE__); +static void test_insertion2(struct TestArgs *args) +{ + test :: begin(); + for (unsigned int i = 0; i < args->n; i++) { + IntEntry *it = args->db->insert(IntEntry(i)); + check_equal(it, INT_NULL); } - test_results(true, __LINE__); + test :: success(); - - /** - * 4: Test that size didn't change + /* + * Size should not change */ - test_size(db, size, actual, __LINE__); + test_equal(args->db->size(), args->size); + test_equal(args->db->actual_size(), args->actual); +} - - /** - * 5: Test that size changes when removing - * Also test out-of-bounds removal - * - * Note: This test removes all even-index entries - */ - for (unsigned int i = 0; i < n + 10; i+=2) { - db.remove(i); - if (i < n) - size--; +static void test_removal(struct TestArgs *args) +{ + test :: begin(); + for (unsigned int i = 0; i < args->n; i+=2) { + args->db->remove(i); + args->size--; } - test_size(db, size, actual, __LINE__); + test :: success(); - - /** - * 6: Test that removing again doesn't change anything + /* + * Size should change */ - for (unsigned int i = 0; i < n + 10; i+=2) - db.remove(i); - test_size(db, size, actual, __LINE__); + test_equal(args->db->size(), args->size); + test_equal(args->db->actual_size(), args->actual); - - /** - * 7: Test iterating and setting ID + /* + * Test out-of-bounds removal */ - Database::iterator it; + test :: begin(); + for (unsigned int i = 0; i < 10; i++) + args->db->remove(args->n + i); + test :: success(); + + /* + * Size should not change + */ + test_equal(args->db->size(), args->size); + test_equal(args->db->actual_size(), args->actual); +} + +static void test_removal2(struct TestArgs *args) +{ + test :: begin(); + for (unsigned int i = 0; i < args->n; i+=2) + args->db->remove(i); + test :: success(); + + /* + * Size should not change + */ + test_equal(args->db->size(), args->size); + test_equal(args->db->actual_size(), args->actual); +} + +static void test_iterator(struct TestArgs *args) +{ unsigned int index = 1; - for (it = db.begin(); it != db.end(); it = db.next(it)) { - if ((*it) == NULL) - test_results(false, __LINE__); - if ((*it)->val != index) - test_results(false, __LINE__); - if ((*it)->id != index) - test_results(false, __LINE__); - index+=2; + Database::iterator it; + + test :: begin(); + for (it = args->db->begin(); it != args->db->end(); it = args->db->next(it)) { + check_not_equal((*it), INT_NULL); + check_equal((*it)->val, index); + check_equal((*it)->id, index); + index += 2; }; - test_results(true, __LINE__); + test :: success(); +} - - /** - * 8. Test access by id - */ - for (unsigned int i = 0; i < n + 10; i++) { - IntEntry *it = db.at(i); - if (i >= n) { - if (it != NULL) - test_results(false, __LINE__); - continue; - } else if (i % 2 == 0) { - if (it != NULL) - test_results(false, __LINE__); - } else if (it == NULL) - test_results(false, __LINE__); - - /* - * Remember those pointers we stored earlier? - * The should still be valid now. - */ - if (i == 1 && it != one) - test_results(false, __LINE__); - if (i == 3 && it != three) - test_results(false, __LINE__); +static void test_access(struct TestArgs *args) +{ + test :: begin(); + for (unsigned int i = 0; i < args->n; i+=2) { + IntEntry *it = args->db->at(i); + check_equal(it, INT_NULL); } - test_results(true, __LINE__); + test :: success(); - - /** - * 9. Test inserting once again - */ - for (unsigned int i = 0; i < n; i++) { - IntEntry *it = db.insert(IntEntry(i)); - if ((i % 2) == 0) { - size++; - actual++; - if (it->id != (n + (i / 2))) - test_results(false, __LINE__); - } else { - if (it != NULL) - test_results(false, __LINE__); - } + test :: begin(); + for (unsigned int i = 1; i < args->n; i+=2) { + IntEntry *it = args->db->at(i); + check_not_equal(it, INT_NULL); } - test_results(true, __LINE__); + test :: success(); + test :: begin(); + for (unsigned int i = 0; i < 10; i++) { + IntEntry *it = args->db->at(args->n + i); + check_equal(it, INT_NULL); + } + test :: success(); +} - /** - * 10. Test that size changed for every other insert +static void test_pointers(struct TestArgs *args) +{ + IntEntry *it = args->db->at(1); + test_equal(it, args->one); + + it = args->db->at(3); + test_equal(it, args->three); +} + +static void test_insertion3(struct TestArgs *args) +{ + test :: begin(); + for (unsigned int i = 0; i < args->n; i+=2) { + IntEntry *it = args->db->insert(IntEntry(i)); + + args->size++; + args->actual++; + check_equal(it->id, args->n + (i / 2)); + } + test :: success(); + + /* + * Size should change */ - test_size(db, size, actual, __LINE__); + test_equal(args->db->size(), args->size); + test_equal(args->db->actual_size(), args->actual); +} - - /** - * Everything after this point tests loading from a file - */ - if (autosave == false) - return 0; - - Database db2("database.db", autosave); +static void test_autosave(struct TestArgs *args) +{ + Database db2("database.db", args->autosave); db2.load(); + if (args->autosave == false) { + test_equal(db2.size(), (unsigned)0); + test_equal(db2.actual_size(), (unsigned)0); - /** - * 11. Sizes should match - */ - test_size(db2, db.size(), db.actual_size(), __LINE__); + args->db->save(); + db2.load(); + } + test_equal(db2.size(), args->db->size()); + test_equal(db2.actual_size(), args->db->actual_size()); - /** - * 12. Values should match - */ + Database::iterator it1; Database::iterator it2 = db2.begin(); - for (it = db.begin(); it != db.end(); it++) { - if ((*it) == NULL && (*it2) != NULL) - test_results(false, __LINE__); - if ((*it) != NULL && (*it2) == NULL) - test_results(false, __LINE__); - if ((*it) != NULL) { - if ((*it)->val != (*it2)->val) - test_results(false, __LINE__); - if ((*it)->id != (*it2)->id) - test_results(false, __LINE__); + + test :: begin(); + for (it1 = args->db->begin(); it1 != args->db->end(); it1++) { + if (*it1 == INT_NULL) + check_equal(*it2, INT_NULL); + else { + check_not_equal(*it2, INT_NULL); + check_equal((*it1)->id, (*it2)->id); + check_equal((*it1)->val, (*it2)->val); } it2++; } - test_results(true, __LINE__); + test :: success(); +} + +static void db_test(unsigned int n, bool autosave) +{ + test :: rm_data_dir(); + + std::stringstream ss; + ss << " (n = " << n << ")"; + const std::string n_str = ss.str(); + + Database db("database.db", autosave); + struct TestArgs args(n, autosave, &db); + + run_test("Database Insertion Test" + n_str, test_insertion, &args); + run_test("Database Second Insertion Test" + n_str, test_insertion2, &args); + run_test("Database Removal Test" + n_str, test_removal, &args); + run_test("Database Second Removal Test" + n_str, test_removal2, &args); + run_test("Database Iterator Test" + n_str, test_iterator, &args); + run_test("Database Access Test" + n_str, test_access, &args); + run_test("Database Pointer Test" + n_str, test_pointers, &args); + run_test("Database Third Insertion Test" + n_str, test_insertion3, &args); + run_test("Database Save and Load Test" + n_str, test_autosave, &args); +} + +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; } diff --git a/tests/src/test.h b/tests/src/test.h index 0bda488b..d3ba05ad 100644 --- a/tests/src/test.h +++ b/tests/src/test.h @@ -36,17 +36,36 @@ namespace test } } + void success() + { + std::cout << "Success!" << std::endl; + } + + template + void failure(const T &lhs, const T &rhs, unsigned int line) + { + std::cout << "Failed at line " << line << ":" << std::endl; + std::cout << " Actual: " << lhs << std::endl; + std::cout << " Expected: " << rhs << std::endl; + failed++; + } + template void check_equal(const T &lhs, const T &rhs, unsigned int line) { if (lhs == rhs) - std::cout << "Success!" << std::endl; - else { - std::cout << "Failed at line " << line << ":" << std::endl; - std::cout << " Actual: " << lhs << std::endl; - std::cout << " Expected: " << rhs << std::endl; - failed++; - } + success(); + else + failure(lhs, rhs, line); + } + + template + void check_not_equal(const T &lhs, const T &rhs, unsigned int line) + { + if (lhs != rhs) + success(); + else + failure(lhs, rhs, line); } template @@ -89,7 +108,7 @@ namespace test #define run_test(name, func, ...) \ do { \ test :: new_test(name); \ - func(##__VA_ARGS__); \ + func( __VA_ARGS__ ); \ test :: end(); \ } while (0) @@ -97,3 +116,15 @@ namespace test do { \ test :: equal(lhs, rhs, __LINE__); \ } while (0) + +#define check_equal(lhs, rhs) \ + do { \ + if (lhs != rhs) \ + test :: failure(lhs, rhs, __LINE__); \ + } while (0) + +#define check_not_equal(lhs, rhs) \ + do { \ + if (lhs == rhs) \ + test :: failure(lhs, rhs, __LINE__); \ + } while (0)