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:
parent
968ca30484
commit
f627f8337d
|
@ -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" ]
|
||||
|
||||
|
|
|
@ -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
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
|
Loading…
Reference in New Issue