database: Update unit test

As a bonus, I now have a test for manual database saving.

Signed-off-by: Anna Schumaker <Anna@OcarinaProject.net>
This commit is contained in:
Anna Schumaker 2014-04-27 17:04:33 -04:00
parent 968ca30484
commit f627f8337d
4 changed files with 267 additions and 244 deletions

View File

@ -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" ]

View File

@ -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

View File

@ -4,15 +4,16 @@
*/
#include <database.h>
#include <print.h>
#include "test.h"
#include <sstream>
#include <stdlib.h>
#include <unistd.h>
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<IntEntry> *db;
TestArgs(const unsigned int, bool, Database<IntEntry> *);
};
TestArgs :: TestArgs(const unsigned int _n, bool _asv, Database<IntEntry> *_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<IntEntry> &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<IntEntry> 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<IntEntry>::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<IntEntry>::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<IntEntry> db2("database.db", autosave);
static void test_autosave(struct TestArgs *args)
{
Database<IntEntry> 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<IntEntry>::iterator it1;
Database<IntEntry>::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<IntEntry> 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;
}

View File

@ -36,17 +36,36 @@ namespace test
}
}
void success()
{
std::cout << "Success!" << std::endl;
}
template <class T>
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 <class T>
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 <class T>
void check_not_equal(const T &lhs, const T &rhs, unsigned int line)
{
if (lhs != rhs)
success();
else
failure(lhs, rhs, line);
}
template <class T>
@ -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)