2014-03-09 14:58:06 -04:00
|
|
|
/*
|
|
|
|
* Copyright 2014 (c) Anna Schumaker.
|
|
|
|
* Test a Database
|
|
|
|
*/
|
2014-06-05 10:19:22 -04:00
|
|
|
#include <core/database.h>
|
2015-01-26 09:40:48 -05:00
|
|
|
#include <core/string.h>
|
2015-08-25 09:27:01 -04:00
|
|
|
#include "test.h"
|
2014-03-09 14:58:06 -04:00
|
|
|
|
2015-01-04 16:36:19 -05:00
|
|
|
#include <vector>
|
2014-03-09 14:58:06 -04:00
|
|
|
|
2015-01-04 16:36:19 -05:00
|
|
|
/*
|
2015-10-31 11:44:29 -04:00
|
|
|
* Derive a db_entry for storing integerss
|
2014-04-27 17:04:33 -04:00
|
|
|
*/
|
2015-10-31 11:44:29 -04:00
|
|
|
struct int_entry : public db_entry {
|
2015-10-13 09:51:12 -04:00
|
|
|
unsigned int ie_val;
|
2014-03-09 14:58:06 -04:00
|
|
|
|
2015-10-13 09:51:12 -04:00
|
|
|
int_entry() : ie_val(0) {};
|
|
|
|
int_entry(unsigned int v) : ie_val(v) {};
|
2014-04-27 17:04:33 -04:00
|
|
|
};
|
|
|
|
|
2015-11-04 12:23:00 -05:00
|
|
|
#define INT_ENTRY(dbe) ((struct int_entry *)dbe)
|
2015-10-05 20:12:32 -04:00
|
|
|
static unsigned int test_free_count = 0;
|
|
|
|
static unsigned int test_setup_count = 0;
|
2015-09-30 09:15:15 -04:00
|
|
|
|
2015-11-02 16:59:55 -05:00
|
|
|
static struct db_entry *int_alloc(const gchar *key)
|
|
|
|
{
|
|
|
|
struct int_entry *ent = new int_entry;
|
|
|
|
sscanf(key, "%u", &ent->ie_val);
|
|
|
|
return ent;
|
|
|
|
}
|
|
|
|
|
2015-09-30 09:15:15 -04:00
|
|
|
static void int_free(struct db_entry *dbe)
|
|
|
|
{
|
|
|
|
test_free_count++;
|
|
|
|
delete (struct int_entry *)dbe;
|
|
|
|
}
|
2014-03-09 14:58:06 -04:00
|
|
|
|
2015-09-23 09:33:55 -04:00
|
|
|
static gchar *int_key(struct db_entry *dbe)
|
|
|
|
{
|
|
|
|
struct int_entry *ent = (struct int_entry *)dbe;
|
|
|
|
return g_strdup_printf("%u", ent->ie_val);
|
|
|
|
}
|
|
|
|
|
2015-09-22 09:24:19 -04:00
|
|
|
static struct db_entry *int_read(struct file *f)
|
|
|
|
{
|
|
|
|
struct int_entry *ent = new int_entry;
|
|
|
|
file_readf(f, "%u", &ent->ie_val);
|
|
|
|
return ent;
|
|
|
|
}
|
|
|
|
|
2015-10-05 20:12:32 -04:00
|
|
|
static void int_setup(struct db_entry *dbe)
|
|
|
|
{
|
|
|
|
test_setup_count++;
|
|
|
|
}
|
|
|
|
|
2015-09-22 15:54:21 -04:00
|
|
|
static void int_write(struct file *file, struct db_entry *dbe)
|
|
|
|
{
|
|
|
|
struct int_entry *ent = (struct int_entry *)dbe;
|
|
|
|
file_writef(file, "%u", ent->ie_val);
|
|
|
|
}
|
|
|
|
|
2015-11-03 10:39:06 -05:00
|
|
|
static const struct db_ops int_ops = {
|
2015-11-02 16:59:55 -05:00
|
|
|
int_alloc,
|
2015-09-30 09:15:15 -04:00
|
|
|
int_free,
|
2015-09-23 09:33:55 -04:00
|
|
|
int_key,
|
2015-09-22 09:24:19 -04:00
|
|
|
int_read,
|
2015-10-05 20:12:32 -04:00
|
|
|
int_setup,
|
2015-09-22 15:54:21 -04:00
|
|
|
int_write,
|
2015-11-03 10:39:06 -05:00
|
|
|
};
|
|
|
|
|
|
|
|
|
2015-10-13 09:51:12 -04:00
|
|
|
static void test_db_entry()
|
2014-04-27 17:04:33 -04:00
|
|
|
{
|
2015-11-02 16:59:55 -05:00
|
|
|
struct int_entry *ent;
|
2015-10-13 09:51:12 -04:00
|
|
|
struct file f;
|
|
|
|
|
2015-11-02 16:59:55 -05:00
|
|
|
ent = (struct int_entry *)int_ops.dbe_alloc("1");
|
2015-11-02 14:48:00 -05:00
|
|
|
test_equal(ent->dbe_index, 0);
|
|
|
|
test_equal(ent->ie_val, 1);
|
2015-09-23 09:33:55 -04:00
|
|
|
test_str_equal(int_ops.dbe_key(ent), "1");
|
2015-10-13 09:51:12 -04:00
|
|
|
|
|
|
|
file_init(&f, "test_db_entry", 0);
|
|
|
|
file_open(&f, OPEN_WRITE);
|
2015-09-22 15:54:21 -04:00
|
|
|
int_ops.dbe_write(&f, ent);
|
2015-10-13 09:51:12 -04:00
|
|
|
file_close(&f);
|
2015-09-30 09:15:15 -04:00
|
|
|
|
|
|
|
int_ops.dbe_free(ent);
|
|
|
|
test_equal(test_free_count, 1);
|
2015-10-13 09:51:12 -04:00
|
|
|
|
|
|
|
file_open(&f, OPEN_READ);
|
2015-09-22 09:24:19 -04:00
|
|
|
ent = (struct int_entry *)int_ops.dbe_read(&f);
|
2015-10-13 09:51:12 -04:00
|
|
|
file_close(&f);
|
|
|
|
|
2015-10-05 20:12:32 -04:00
|
|
|
int_ops.dbe_setup(ent);
|
2015-10-13 09:51:12 -04:00
|
|
|
test_equal(ent->ie_val, 1);
|
2015-09-23 09:33:55 -04:00
|
|
|
test_str_equal(int_ops.dbe_key(ent), "1");
|
2015-10-05 20:12:32 -04:00
|
|
|
test_equal(test_setup_count, 1);
|
|
|
|
|
2015-09-30 09:15:15 -04:00
|
|
|
int_ops.dbe_free(ent);
|
|
|
|
test_equal(test_free_count, 2);
|
2014-04-27 17:04:33 -04:00
|
|
|
}
|
2014-03-09 14:58:06 -04:00
|
|
|
|
2015-10-13 09:51:12 -04:00
|
|
|
static void test_init()
|
2014-04-27 17:04:33 -04:00
|
|
|
{
|
2015-11-04 12:23:00 -05:00
|
|
|
struct database db;
|
2015-09-21 09:02:13 -04:00
|
|
|
|
2015-11-03 10:39:06 -05:00
|
|
|
db_init(&db, "init.db", false, &int_ops);
|
2014-03-09 14:58:06 -04:00
|
|
|
|
2015-10-13 09:51:12 -04:00
|
|
|
/* Check initial sizes. */
|
2015-11-05 11:56:47 -05:00
|
|
|
test_equal(db.db_entries->len, 0);
|
2015-09-21 09:02:13 -04:00
|
|
|
test_equal(db.db_keys.size(), 0);
|
2015-10-17 14:59:06 -04:00
|
|
|
test_equal(db.db_size, 0);
|
2015-09-21 09:02:13 -04:00
|
|
|
test_equal(db.db_autosave, false);
|
|
|
|
test_equal(db.db_file.f_version, 0);
|
|
|
|
test_equal(db.db_file.f_name, "init.db");
|
|
|
|
|
|
|
|
db_deinit(&db);
|
2014-04-27 17:04:33 -04:00
|
|
|
}
|
2014-03-09 14:58:06 -04:00
|
|
|
|
2015-10-13 09:51:12 -04:00
|
|
|
static void test_stress(unsigned int N)
|
2014-04-27 17:04:33 -04:00
|
|
|
{
|
2015-10-13 09:51:12 -04:00
|
|
|
std::vector<struct int_entry *> ptrs;
|
2015-11-04 12:23:00 -05:00
|
|
|
struct db_entry *dbe, *next;
|
|
|
|
struct int_entry rmv(42);
|
|
|
|
struct database db;
|
2015-10-13 09:51:12 -04:00
|
|
|
unsigned int i;
|
|
|
|
gchar *key;
|
|
|
|
|
2015-10-05 20:12:32 -04:00
|
|
|
test_free_count = 0;
|
|
|
|
test_setup_count = 0;
|
2015-11-03 10:39:06 -05:00
|
|
|
db_init(&db, "stress.db", false, &int_ops);
|
2015-09-21 09:02:13 -04:00
|
|
|
|
2015-10-18 10:59:20 -04:00
|
|
|
/* db_insert() */
|
2015-10-13 09:51:12 -04:00
|
|
|
for (i = 0; i < N; i++) {
|
2015-09-23 09:33:55 -04:00
|
|
|
key = g_strdup_printf("%u", i);
|
2015-10-18 10:59:20 -04:00
|
|
|
dbe = db_insert(&db, new int_entry(i));
|
2015-10-13 09:51:12 -04:00
|
|
|
test_loop_not_equal(dbe, NULL, i);
|
2015-11-02 14:48:00 -05:00
|
|
|
test_loop_equal(dbe->dbe_index, i, i);
|
2015-09-23 09:33:55 -04:00
|
|
|
test_loop_equal(dbe->dbe_key, key, i);
|
2015-11-04 12:23:00 -05:00
|
|
|
test_loop_equal(INT_ENTRY(dbe)->ie_val, i, i);
|
|
|
|
ptrs.push_back(INT_ENTRY(dbe));
|
2015-09-23 09:33:55 -04:00
|
|
|
g_free(key);
|
2015-08-25 09:27:01 -04:00
|
|
|
} test_loop_passed();
|
|
|
|
|
2015-10-18 10:59:20 -04:00
|
|
|
dbe = db_insert(&db, (struct int_entry *)NULL);
|
|
|
|
test_equal(dbe, NULL);
|
2015-10-17 14:59:06 -04:00
|
|
|
test_equal(db.db_size, N);
|
2015-10-17 15:12:40 -04:00
|
|
|
test_equal(db_actual_size(&db), N);
|
2015-10-05 20:12:32 -04:00
|
|
|
test_equal(test_setup_count, N);
|
2015-01-04 16:36:19 -05:00
|
|
|
|
2015-10-20 08:19:47 -04:00
|
|
|
/* db_at() */
|
2015-10-13 09:51:12 -04:00
|
|
|
for (i = 0; i < N; i++) {
|
2015-10-20 08:19:47 -04:00
|
|
|
dbe = db_at(&db, i);
|
2015-10-13 09:51:12 -04:00
|
|
|
test_loop_not_equal(dbe, NULL, i);
|
|
|
|
test_loop_equal(dbe, ptrs.at(i), i);
|
2015-11-04 12:23:00 -05:00
|
|
|
test_loop_equal(INT_ENTRY(dbe)->ie_val, i, i);
|
2015-08-25 09:27:01 -04:00
|
|
|
} test_loop_passed();
|
2015-10-20 08:19:47 -04:00
|
|
|
test_equal(db_at(&db, N), NULL);
|
2015-10-13 09:51:12 -04:00
|
|
|
|
2015-10-20 10:10:26 -04:00
|
|
|
/* db_get() */
|
2015-10-13 09:51:12 -04:00
|
|
|
for (i = 0; i < N; i++) {
|
|
|
|
key = g_strdup_printf("%u", i);
|
2015-10-20 10:10:26 -04:00
|
|
|
dbe = db_get(&db, key);
|
2015-10-13 09:51:12 -04:00
|
|
|
test_loop_not_equal(dbe, NULL, i);
|
2015-09-23 09:33:55 -04:00
|
|
|
test_loop_str_equal(int_ops.dbe_key(dbe), key, i);
|
2015-10-13 09:51:12 -04:00
|
|
|
test_loop_equal(dbe, ptrs.at(i), i);
|
2015-11-04 12:23:00 -05:00
|
|
|
test_loop_equal(INT_ENTRY(dbe)->ie_val, i, i);
|
2015-10-13 09:51:12 -04:00
|
|
|
g_free(key);
|
|
|
|
} test_loop_passed();
|
|
|
|
key = g_strdup_printf("%u", N);
|
2015-10-20 10:10:26 -04:00
|
|
|
test_equal(db_get(&db, key), NULL);
|
2015-10-13 09:51:12 -04:00
|
|
|
g_free(key);
|
|
|
|
|
2015-10-20 10:10:26 -04:00
|
|
|
/* db_find() */
|
|
|
|
for (i = 0; i <= N; i++) {
|
|
|
|
key = g_strdup_printf("%u", i);
|
|
|
|
dbe = db_find(&db, key);
|
|
|
|
test_loop_not_equal(dbe, NULL, i);
|
2015-09-23 09:33:55 -04:00
|
|
|
test_loop_str_equal(int_ops.dbe_key(dbe), key, i);
|
2015-11-04 12:23:00 -05:00
|
|
|
test_loop_equal(INT_ENTRY(dbe)->ie_val, i, i);
|
2015-10-20 10:10:26 -04:00
|
|
|
if (i < N)
|
|
|
|
test_loop_equal(dbe, ptrs.at(i), i);
|
|
|
|
g_free(key);
|
|
|
|
} test_loop_passed();
|
|
|
|
test_equal(db.db_size, N + 1);
|
|
|
|
test_equal(db_actual_size(&db), N + 1);
|
|
|
|
|
2015-10-20 08:05:13 -04:00
|
|
|
/* db_remove(): Even indices only! */
|
|
|
|
for (i = 0; i <= (N + 2); i += 2) {
|
2015-10-13 09:51:12 -04:00
|
|
|
key = g_strdup_printf("%u", i);
|
2015-10-20 10:10:26 -04:00
|
|
|
dbe = db_get(&db, key);
|
2015-10-20 08:05:13 -04:00
|
|
|
db_remove(&db, dbe);
|
2015-10-20 08:19:47 -04:00
|
|
|
test_loop_equal(db_at(&db, i), NULL, i);
|
2015-10-20 10:10:26 -04:00
|
|
|
test_loop_equal(db_get(&db, key), NULL, i);
|
2015-10-13 09:51:12 -04:00
|
|
|
g_free(key);
|
|
|
|
} test_loop_passed();
|
2015-10-20 08:05:13 -04:00
|
|
|
db_remove(&db, &rmv);
|
2015-10-17 14:59:06 -04:00
|
|
|
test_equal(db.db_size, N / 2);
|
2015-10-20 10:10:26 -04:00
|
|
|
test_equal(db_actual_size(&db), N + 1);
|
2015-09-30 09:15:15 -04:00
|
|
|
test_equal(test_free_count, (N / 2) + 1);
|
2015-10-13 09:51:12 -04:00
|
|
|
|
2015-09-18 09:37:48 -04:00
|
|
|
/* db_for_each() (db_first() / db_next()) */
|
2015-10-13 09:51:12 -04:00
|
|
|
i = 1;
|
2015-09-18 09:37:48 -04:00
|
|
|
db_for_each(dbe, next, &db) {
|
|
|
|
test_loop_not_equal(dbe, NULL, i);
|
|
|
|
if (i == (N - 1)) {
|
|
|
|
test_loop_equal(next, NULL, i);
|
|
|
|
} else {
|
|
|
|
test_loop_not_equal(next, NULL, i);
|
2015-11-04 12:23:00 -05:00
|
|
|
test_loop_equal(INT_ENTRY(next)->ie_val, i + 2, i);
|
2015-09-18 09:37:48 -04:00
|
|
|
}
|
|
|
|
test_loop_equal(dbe, ptrs.at(i), i);
|
2015-11-04 12:23:00 -05:00
|
|
|
test_loop_equal(INT_ENTRY(dbe)->ie_val, i, i);
|
2015-09-18 09:37:48 -04:00
|
|
|
test_loop_equal(db_next(&db, dbe), next, i);
|
2015-10-13 09:51:12 -04:00
|
|
|
i += 2;
|
|
|
|
} test_loop_passed();
|
|
|
|
test_equal(i, N + 1);
|
2015-09-21 09:02:13 -04:00
|
|
|
|
|
|
|
db_deinit(&db);
|
|
|
|
test_equal(db.db_size, 0);
|
2015-11-05 11:56:47 -05:00
|
|
|
test_equal(db_actual_size(&db), 0);
|
2015-09-21 09:02:13 -04:00
|
|
|
test_equal(db_first(&db), NULL);
|
2015-09-30 09:15:15 -04:00
|
|
|
test_equal(test_free_count, N + 1);
|
2014-04-27 17:04:33 -04:00
|
|
|
}
|
2014-03-09 14:58:06 -04:00
|
|
|
|
2015-10-13 09:51:12 -04:00
|
|
|
static void test_basics() { test_stress(10); }
|
|
|
|
static void test_stress_0() { test_stress(0); }
|
|
|
|
static void test_stress_100K() { test_stress(100000); }
|
2015-01-04 17:56:54 -05:00
|
|
|
|
2015-10-13 09:51:12 -04:00
|
|
|
static void test_save_load()
|
2014-04-27 17:04:33 -04:00
|
|
|
{
|
2015-11-04 12:23:00 -05:00
|
|
|
struct db_entry *dbe, *next;
|
2015-10-13 09:51:12 -04:00
|
|
|
const unsigned int N = 10;
|
2015-11-04 12:23:00 -05:00
|
|
|
struct database db1, db2;
|
2015-10-13 09:51:12 -04:00
|
|
|
unsigned int i;
|
2014-03-09 14:58:06 -04:00
|
|
|
|
2015-11-03 10:39:06 -05:00
|
|
|
db_init(&db1, "save_load.db", true, &int_ops);
|
|
|
|
db_init(&db2, "save_load.db", false, &int_ops);
|
2015-09-21 09:02:13 -04:00
|
|
|
|
2015-10-13 09:51:12 -04:00
|
|
|
/* 10 items should "autosave" when inserted */
|
|
|
|
for (i = 0; i < N; i++)
|
2015-10-18 10:59:20 -04:00
|
|
|
db_insert(&db1, new int_entry(i));
|
2014-03-09 14:58:06 -04:00
|
|
|
|
2015-09-21 09:02:13 -04:00
|
|
|
i = 0;
|
2015-09-17 08:12:25 -04:00
|
|
|
db_load(&db2);
|
2015-10-17 14:59:06 -04:00
|
|
|
test_equal(db2.db_size, N);
|
2015-10-17 15:12:40 -04:00
|
|
|
test_equal(db_actual_size(&db2), N);
|
2015-09-21 09:02:13 -04:00
|
|
|
db_for_each(dbe, next, &db2) {
|
2015-11-04 12:23:00 -05:00
|
|
|
test_loop_equal(INT_ENTRY(dbe)->ie_val, i, i);
|
2015-09-21 09:02:13 -04:00
|
|
|
i++;
|
2015-08-25 09:27:01 -04:00
|
|
|
} test_loop_passed();
|
2014-04-27 17:04:33 -04:00
|
|
|
|
2015-10-13 09:51:12 -04:00
|
|
|
/* Removing 5 items, should also trigger autosaving. */
|
|
|
|
for (i = 0; i < N; i += 2)
|
2015-10-20 08:19:47 -04:00
|
|
|
db_remove(&db1, db_at(&db1, i));
|
2015-01-04 16:36:19 -05:00
|
|
|
|
2015-09-21 09:02:13 -04:00
|
|
|
db_deinit(&db2);
|
2015-11-05 11:56:47 -05:00
|
|
|
db2.db_entries = g_ptr_array_new();
|
2015-10-05 20:12:32 -04:00
|
|
|
test_setup_count = 0;
|
2015-09-17 08:12:25 -04:00
|
|
|
db_load(&db2);
|
2015-10-17 14:59:06 -04:00
|
|
|
test_equal(db2.db_size, N / 2);
|
2015-10-05 20:12:32 -04:00
|
|
|
test_equal(test_setup_count, N / 2);
|
|
|
|
|
|
|
|
i = 1;
|
2015-09-21 09:02:13 -04:00
|
|
|
db_for_each(dbe, next, &db2) {
|
2015-11-04 12:23:00 -05:00
|
|
|
test_loop_equal(INT_ENTRY(dbe)->ie_val, i, i);
|
2015-09-21 09:02:13 -04:00
|
|
|
i += 2;
|
2015-10-13 09:51:12 -04:00
|
|
|
} test_loop_passed();
|
2015-08-25 09:27:01 -04:00
|
|
|
|
2015-09-21 09:02:13 -04:00
|
|
|
/* Manually turn autosave off. */
|
|
|
|
db1.db_autosave = false;
|
2015-10-13 09:51:12 -04:00
|
|
|
for (i = N; i < (2 * N); i++)
|
2015-09-21 09:02:13 -04:00
|
|
|
db_insert(&db1, new int_entry(i));
|
2015-10-13 09:51:12 -04:00
|
|
|
for (i = N; i < (2 * N); i += 2)
|
2015-09-21 09:02:13 -04:00
|
|
|
db_remove(&db1, db_at(&db1, i));
|
|
|
|
|
|
|
|
db_deinit(&db2);
|
2015-11-05 11:56:47 -05:00
|
|
|
db2.db_entries = g_ptr_array_new();
|
2015-09-21 09:02:13 -04:00
|
|
|
db_load(&db2);
|
|
|
|
test_equal(db2.db_size, N / 2);
|
|
|
|
|
|
|
|
db_save(&db1);
|
|
|
|
db_deinit(&db2);
|
2015-11-05 11:56:47 -05:00
|
|
|
db2.db_entries = g_ptr_array_new();
|
2015-09-21 09:02:13 -04:00
|
|
|
db_load(&db2);
|
|
|
|
|
|
|
|
i = 1;
|
|
|
|
test_equal(db2.db_size, N);
|
|
|
|
test_equal(db_actual_size(&db2), 2 * N);
|
|
|
|
db_for_each(dbe, next, &db2) {
|
2015-11-04 12:23:00 -05:00
|
|
|
test_loop_equal(INT_ENTRY(dbe)->ie_val, i, i);
|
2015-09-21 09:02:13 -04:00
|
|
|
i += 2;
|
2015-10-13 09:51:12 -04:00
|
|
|
} test_loop_passed();
|
2015-09-21 09:02:13 -04:00
|
|
|
|
|
|
|
db_deinit(&db1);
|
|
|
|
db_deinit(&db2);
|
2015-10-13 09:51:12 -04:00
|
|
|
}
|
2015-08-25 09:27:01 -04:00
|
|
|
|
|
|
|
DECLARE_UNIT_TESTS(
|
2015-10-13 09:51:12 -04:00
|
|
|
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),
|
2015-08-25 09:27:01 -04:00
|
|
|
);
|