#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include "remdb.h"
typedef struct TestRecord {
uint32_t id;
char name[16];
float value;
} TestRecord;
static void test_init_and_get() {
printf("Testing init and get... ");
RemDbFieldDef fields[] = {
{ "id", REMDB_TYPE_UINT32, sizeof(uint32_t), offsetof(TestRecord, id) },
{ "name", REMDB_TYPE_STRING, sizeof(((TestRecord*)0)->name), offsetof(TestRecord, name) },
{ "value", REMDB_TYPE_FLOAT32, sizeof(float), offsetof(TestRecord, value) }
};
size_t fields_count = sizeof(fields) / sizeof(fields[0]);
RemDbTableDef table = {
.id = 0,
.name = "test_table",
.fields = fields,
.fields_count = fields_count,
.primary_key = 0,
.secondary_index = -1,
.record_size = sizeof(TestRecord),
.max_records = 100
};
RemDbTableDef tables[] = { table };
RemDbConfig config = {
.tables = tables,
.tables_count = sizeof(tables) / sizeof(tables[0]),
.total_memory = 1024 * 1024,
.low_power_mode_supported = 1,
.low_power_max_records = 50
};
RemDbHandle handle = NULL;
enum RemDbError err = remdb_init_global(&config, &handle);
assert(err == REMDB_SUCCESS);
assert(handle != NULL);
RemDbHandle handle2 = NULL;
err = remdb_get_global(&handle2);
assert(err == REMDB_SUCCESS);
assert(handle2 == handle);
printf("✓\n");
}
static void test_table_operations() {
printf("Testing table operations... ");
RemDbFieldDef fields[] = {
{ "id", REMDB_TYPE_UINT32, sizeof(uint32_t), offsetof(TestRecord, id) },
{ "name", REMDB_TYPE_STRING, sizeof(((TestRecord*)0)->name), offsetof(TestRecord, name) },
{ "value", REMDB_TYPE_FLOAT32, sizeof(float), offsetof(TestRecord, value) }
};
size_t fields_count = sizeof(fields) / sizeof(fields[0]);
RemDbTableDef table = {
.id = 0,
.name = "test_table",
.fields = fields,
.fields_count = fields_count,
.primary_key = 0,
.secondary_index = -1,
.record_size = sizeof(TestRecord),
.max_records = 100
};
RemDbTableDef tables[] = { table };
RemDbConfig config = {
.tables = tables,
.tables_count = sizeof(tables) / sizeof(tables[0]),
.total_memory = 1024 * 1024,
.low_power_mode_supported = 1,
.low_power_max_records = 50
};
RemDbHandle handle = NULL;
enum RemDbError err = remdb_init_global(&config, &handle);
assert(err == REMDB_SUCCESS);
TestRecord record = { .id = 1, .name = "test", .value = 3.14f };
err = remdb_table_insert(handle, 0, &record);
assert(err == REMDB_SUCCESS);
size_t count = 0;
err = remdb_table_get_record_count(handle, 0, &count);
assert(err == REMDB_SUCCESS);
assert(count == 1);
TestRecord retrieved;
RemDbValue key;
key.u32 = 1;
err = remdb_table_get(handle, 0, &key, &retrieved);
assert(err == REMDB_SUCCESS);
assert(retrieved.id == record.id);
assert(strcmp(retrieved.name, record.name) == 0);
assert(retrieved.value == record.value);
TestRecord updated = { .id = 1, .name = "updated", .value = 6.28f };
err = remdb_table_update(handle, 0, &key, &updated);
assert(err == REMDB_SUCCESS);
err = remdb_table_get(handle, 0, &key, &retrieved);
assert(err == REMDB_SUCCESS);
assert(retrieved.id == updated.id);
assert(strcmp(retrieved.name, updated.name) == 0);
assert(retrieved.value == updated.value);
err = remdb_table_delete(handle, 0, &key);
assert(err == REMDB_SUCCESS);
err = remdb_table_get(handle, 0, &key, &retrieved);
assert(err == REMDB_ERROR_RECORD_NOT_FOUND);
err = remdb_table_get_record_count(handle, 0, &count);
assert(err == REMDB_SUCCESS);
assert(count == 0);
printf("✓\n");
}
static void test_transactions() {
printf("Testing transactions... ");
RemDbFieldDef fields[] = {
{ "id", REMDB_TYPE_UINT32, sizeof(uint32_t), offsetof(TestRecord, id) },
{ "name", REMDB_TYPE_STRING, sizeof(((TestRecord*)0)->name), offsetof(TestRecord, name) },
{ "value", REMDB_TYPE_FLOAT32, sizeof(float), offsetof(TestRecord, value) }
};
size_t fields_count = sizeof(fields) / sizeof(fields[0]);
RemDbTableDef table = {
.id = 0,
.name = "test_table",
.fields = fields,
.fields_count = fields_count,
.primary_key = 0,
.secondary_index = -1,
.record_size = sizeof(TestRecord),
.max_records = 100
};
RemDbTableDef tables[] = { table };
RemDbConfig config = {
.tables = tables,
.tables_count = sizeof(tables) / sizeof(tables[0]),
.total_memory = 1024 * 1024,
.low_power_mode_supported = 1,
.low_power_max_records = 50
};
RemDbHandle handle = NULL;
enum RemDbError err = remdb_init_global(&config, &handle);
assert(err == REMDB_SUCCESS);
err = remdb_begin_transaction(handle, REMDB_TX_WRITE, REMDB_ISO_READ_COMMITTED);
assert(err == REMDB_SUCCESS);
TestRecord record1 = { .id = 1, .name = "tx_test", .value = 1.0f };
err = remdb_table_insert(handle, 0, &record1);
assert(err == REMDB_SUCCESS);
err = remdb_commit_transaction(handle);
assert(err == REMDB_SUCCESS);
size_t count = 0;
err = remdb_table_get_record_count(handle, 0, &count);
assert(err == REMDB_SUCCESS);
assert(count == 1);
err = remdb_begin_transaction(handle, REMDB_TX_WRITE, REMDB_ISO_READ_COMMITTED);
assert(err == REMDB_SUCCESS);
TestRecord record2 = { .id = 2, .name = "tx_rollback", .value = 2.0f };
err = remdb_table_insert(handle, 0, &record2);
assert(err == REMDB_SUCCESS);
err = remdb_rollback_transaction(handle);
assert(err == REMDB_SUCCESS);
err = remdb_table_get_record_count(handle, 0, &count);
assert(err == REMDB_SUCCESS);
assert(count == 1);
err = remdb_begin_transaction(handle, REMDB_TX_WRITE, REMDB_ISO_READ_COMMITTED);
assert(err == REMDB_SUCCESS);
TestRecord duplicate = { .id = 1, .name = "duplicate", .value = 3.0f };
err = remdb_table_insert(handle, 0, &duplicate);
assert(err == REMDB_ERROR_DUPLICATE_KEY);
err = remdb_rollback_transaction(handle);
assert(err == REMDB_SUCCESS);
printf("✓\n");
}
static void test_low_power_mode() {
printf("Testing low power mode... ");
RemDbFieldDef fields[] = {
{ "id", REMDB_TYPE_UINT32, sizeof(uint32_t), offsetof(TestRecord, id) },
{ "name", REMDB_TYPE_STRING, sizeof(((TestRecord*)0)->name), offsetof(TestRecord, name) },
{ "value", REMDB_TYPE_FLOAT32, sizeof(float), offsetof(TestRecord, value) }
};
size_t fields_count = sizeof(fields) / sizeof(fields[0]);
RemDbTableDef table = {
.id = 0,
.name = "test_table",
.fields = fields,
.fields_count = fields_count,
.primary_key = 0,
.secondary_index = -1,
.record_size = sizeof(TestRecord),
.max_records = 100
};
RemDbTableDef tables[] = { table };
RemDbConfig config = {
.tables = tables,
.tables_count = sizeof(tables) / sizeof(tables[0]),
.total_memory = 1024 * 1024,
.low_power_mode_supported = 1,
.low_power_max_records = 50
};
RemDbHandle handle = NULL;
enum RemDbError err = remdb_init_global(&config, &handle);
assert(err == REMDB_SUCCESS);
uint8_t is_low_power = 1;
err = remdb_is_low_power_mode(handle, &is_low_power);
assert(err == REMDB_SUCCESS);
assert(is_low_power == 0);
err = remdb_enter_low_power_mode(handle);
assert(err == REMDB_SUCCESS);
err = remdb_is_low_power_mode(handle, &is_low_power);
assert(err == REMDB_SUCCESS);
assert(is_low_power == 1);
err = remdb_exit_low_power_mode(handle);
assert(err == REMDB_SUCCESS);
err = remdb_is_low_power_mode(handle, &is_low_power);
assert(err == REMDB_SUCCESS);
assert(is_low_power == 0);
printf("✓\n");
}
static void test_metrics_and_health() {
printf("Testing metrics and health... ");
RemDbFieldDef fields[] = {
{ "id", REMDB_TYPE_UINT32, sizeof(uint32_t), offsetof(TestRecord, id) },
{ "name", REMDB_TYPE_STRING, sizeof(((TestRecord*)0)->name), offsetof(TestRecord, name) },
{ "value", REMDB_TYPE_FLOAT32, sizeof(float), offsetof(TestRecord, value) }
};
size_t fields_count = sizeof(fields) / sizeof(fields[0]);
RemDbTableDef table = {
.id = 0,
.name = "test_table",
.fields = fields,
.fields_count = fields_count,
.primary_key = 0,
.secondary_index = -1,
.record_size = sizeof(TestRecord),
.max_records = 100
};
RemDbTableDef tables[] = { table };
RemDbConfig config = {
.tables = tables,
.tables_count = sizeof(tables) / sizeof(tables[0]),
.total_memory = 1024 * 1024,
.low_power_mode_supported = 1,
.low_power_max_records = 50
};
RemDbHandle handle = NULL;
enum RemDbError err = remdb_init_global(&config, &handle);
assert(err == REMDB_SUCCESS);
RemDbMetricsSnapshot snapshot;
err = remdb_get_metrics_snapshot(handle, &snapshot);
assert(err == REMDB_SUCCESS);
assert(snapshot.total_memory == config.total_memory);
err = remdb_reset_metrics(handle);
assert(err == REMDB_SUCCESS);
char buffer[512];
size_t written = 0;
err = remdb_dump_metrics(handle, buffer, sizeof(buffer), &written);
assert(err == REMDB_SUCCESS);
assert(written > 0);
RemDbHealthCheckResult health;
err = remdb_health_check(handle, &health);
assert(err == REMDB_SUCCESS);
assert(health.status == REMDB_HEALTH_HEALTHY || health.status == REMDB_HEALTH_WARNING);
uint32_t version = 0;
err = remdb_get_snapshot_version(handle, &version);
assert(err == REMDB_SUCCESS);
assert(version == 0);
printf("✓\n");
}
static void test_snapshot_management() {
printf("Testing snapshot management... ");
RemDbFieldDef fields[] = {
{ "id", REMDB_TYPE_UINT32, sizeof(uint32_t), offsetof(TestRecord, id) },
{ "name", REMDB_TYPE_STRING, sizeof(((TestRecord*)0)->name), offsetof(TestRecord, name) },
{ "value", REMDB_TYPE_FLOAT32, sizeof(float), offsetof(TestRecord, value) }
};
size_t fields_count = sizeof(fields) / sizeof(fields[0]);
RemDbTableDef table = {
.id = 0,
.name = "test_table",
.fields = fields,
.fields_count = fields_count,
.primary_key = 0,
.secondary_index = -1,
.record_size = sizeof(TestRecord),
.max_records = 100
};
RemDbTableDef tables[] = { table };
RemDbConfig config = {
.tables = tables,
.tables_count = sizeof(tables) / sizeof(tables[0]),
.total_memory = 1024 * 1024,
.low_power_mode_supported = 1,
.low_power_max_records = 50
};
RemDbHandle handle = NULL;
enum RemDbError err = remdb_init_global(&config, &handle);
assert(err == REMDB_SUCCESS);
TestRecord record = { .id = 1, .name = "snapshot_test", .value = 1.0f };
err = remdb_table_insert(handle, 0, &record);
assert(err == REMDB_SUCCESS);
err = remdb_save_snapshot(handle, "test_snapshot");
assert(err == REMDB_SUCCESS);
TestRecord record2 = { .id = 2, .name = "before_restore", .value = 2.0f };
err = remdb_table_insert(handle, 0, &record2);
assert(err == REMDB_SUCCESS);
size_t count = 0;
err = remdb_table_get_record_count(handle, 0, &count);
assert(err == REMDB_SUCCESS);
assert(count == 2);
err = remdb_restore_snapshot(handle, "test_snapshot");
assert(err == REMDB_SUCCESS);
err = remdb_table_get_record_count(handle, 0, &count);
assert(err == REMDB_SUCCESS);
assert(count == 1);
err = remdb_save_incremental_snapshot(handle, "test_incremental_snapshot");
assert(err == REMDB_SUCCESS);
printf("✓\n");
}
static void test_error_handling() {
printf("Testing error handling... ");
enum RemDbError err = remdb_get_global(NULL);
assert(err == REMDB_ERROR_CONFIG_ERROR);
RemDbHandle handle = NULL;
RemDbTableDef tables[0] = {};
RemDbConfig config = {
.tables = tables,
.tables_count = 0,
.total_memory = 1024 * 1024,
.low_power_mode_supported = 0,
.low_power_max_records = -1
};
err = remdb_init_global(&config, &handle);
assert(err == REMDB_SUCCESS);
TestRecord record = { .id = 1, .name = "error_test", .value = 0.0f };
err = remdb_table_insert(handle, 1, &record);
assert(err == REMDB_ERROR_TABLE_NOT_FOUND);
RemDbValue key;
key.u32 = 999;
TestRecord retrieved;
err = remdb_table_get(handle, 0, &key, &retrieved);
assert(err == REMDB_ERROR_RECORD_NOT_FOUND);
printf("✓\n");
}
static void test_table_get_by_name() {
printf("Testing table get by name... ");
RemDbFieldDef fields[] = {
{ "id", REMDB_TYPE_UINT32, sizeof(uint32_t), offsetof(TestRecord, id) },
{ "name", REMDB_TYPE_STRING, sizeof(((TestRecord*)0)->name), offsetof(TestRecord, name) },
{ "value", REMDB_TYPE_FLOAT32, sizeof(float), offsetof(TestRecord, value) }
};
size_t fields_count = sizeof(fields) / sizeof(fields[0]);
RemDbTableDef tables[] = {
{
.id = 0,
.name = "table1",
.fields = fields,
.fields_count = fields_count,
.primary_key = 0,
.secondary_index = -1,
.record_size = sizeof(TestRecord),
.max_records = 100
},
{
.id = 1,
.name = "table2",
.fields = fields,
.fields_count = fields_count,
.primary_key = 0,
.secondary_index = -1,
.record_size = sizeof(TestRecord),
.max_records = 100
}
};
RemDbConfig config = {
.tables = tables,
.tables_count = sizeof(tables) / sizeof(tables[0]),
.total_memory = 1024 * 1024,
.low_power_mode_supported = 0,
.low_power_max_records = -1
};
RemDbHandle handle = NULL;
enum RemDbError err = remdb_init_global(&config, &handle);
assert(err == REMDB_SUCCESS);
size_t table_id = 0;
err = remdb_table_get_by_name(handle, "table1", &table_id);
assert(err == REMDB_SUCCESS);
assert(table_id == 0);
err = remdb_table_get_by_name(handle, "table2", &table_id);
assert(err == REMDB_SUCCESS);
assert(table_id == 1);
err = remdb_table_get_by_name(handle, "nonexistent_table", &table_id);
assert(err == REMDB_ERROR_TABLE_NOT_FOUND);
printf("✓\n");
}
int main() {
printf("RemDB C API Unit Tests\n");
printf("====================\n\n");
test_init_and_get();
test_table_operations();
test_transactions();
test_low_power_mode();
test_metrics_and_health();
test_snapshot_management();
test_error_handling();
test_table_get_by_name();
printf("\nAll tests passed successfully!\n");
return 0;
}