summaryrefslogtreecommitdiffstats
path: root/tests/test_bin.c
diff options
context:
space:
mode:
authorEthan Werner <mail@ethanwerner.com>2026-09-19 06:09:54 +0000
committerEthan Werner <mail@ethanwerner.com>2026-09-19 06:09:54 +0000
commitbf19b9938508f70011395f4dc283ca758ec6a8d3 (patch)
tree46dc4c442ed8d8d43040433c6a4794e76f8e618a /tests/test_bin.c
parent96ecf29e8be977f51f351308ef2c7c492bb829aa (diff)
llm toolingHEADmaster
Diffstat (limited to 'tests/test_bin.c')
-rw-r--r--tests/test_bin.c273
1 files changed, 273 insertions, 0 deletions
diff --git a/tests/test_bin.c b/tests/test_bin.c
new file mode 100644
index 0000000..f12f336
--- /dev/null
+++ b/tests/test_bin.c
@@ -0,0 +1,273 @@
+// Tests for bin.h - flat file block storage operations
+#include <stdint.h>
+typedef int32_t bin_key_t;
+
+#define BIN_IMPLEMENTATION
+#include "../include/bin.h"
+
+#include <math.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#define ASSERT(cond, msg) \
+ do { \
+ if (!(cond)) { \
+ fprintf(stderr, "FAIL: %s (test_bin.c:%d)\n", msg, __LINE__); \
+ failures++; \
+ } \
+ } while (0)
+
+static int failures = 0;
+
+struct record {
+ bin_key_t key;
+ char _pad[4];
+ double value;
+};
+
+#define REC(k, v) (&((struct record){.key = (k), .value = (v)}))
+
+void cleanup(char const *path) {
+ remove(path);
+}
+
+void test_init_and_close() {
+ FILE *f = bin_init("/tmp/test_bin_init.bin", sizeof(struct record));
+ ASSERT(f != NULL, "bin_init returns non-NULL");
+ if (!f) return;
+ ASSERT(bin_length(f) == 0, "initial length is 0");
+ ASSERT(bin_block_size(f) == sizeof(struct record), "block size");
+ bin_close(f);
+ cleanup("/tmp/test_bin_init.bin");
+}
+
+void test_append_and_read() {
+ FILE *f = bin_init("/tmp/test_bin_append.bin", sizeof(struct record));
+ ASSERT(f != NULL, "bin_init returns non-NULL");
+ if (!f) return;
+
+ for (int i = 0; i < 10; i++) {
+ bin_append(f, REC(i, i * 2.5), 1);
+ }
+
+ ASSERT(bin_length(f) == 10, "length after 10 appends");
+
+ struct record buf[10];
+ ASSERT(bin_read(f, 0, buf, 10) == 10, "read 10 records");
+ for (int i = 0; i < 10; i++) {
+ ASSERT(buf[i].key == i, "read key");
+ ASSERT(fabs(buf[i].value - (double)(i * 2.5)) < 1e-9, "read value");
+ }
+
+ bin_close(f);
+ cleanup("/tmp/test_bin_append.bin");
+}
+
+void test_write() {
+ FILE *f = bin_init("/tmp/test_bin_write.bin", sizeof(struct record));
+ ASSERT(f != NULL, "bin_init returns non-NULL");
+ if (!f) return;
+
+ bin_append(f, REC(0, 0), 1);
+ bin_append(f, REC(1, 1), 1);
+
+ bin_write(f, 0, REC(99, 99), 1);
+
+ struct record r;
+ ASSERT(bin_read(f, 0, &r, 1) == 1, "read 1 record");
+ ASSERT(r.key == 99, "write overwrites");
+ ASSERT(r.value == 99, "write overwrites value");
+
+ bin_close(f);
+ cleanup("/tmp/test_bin_write.bin");
+}
+
+void test_insert() {
+ FILE *f = bin_init("/tmp/test_bin_insert.bin", sizeof(struct record));
+ ASSERT(f != NULL, "bin_init returns non-NULL");
+ if (!f) return;
+
+ bin_append(f, REC(0, 0), 1);
+ bin_append(f, REC(2, 2), 1);
+
+ bin_insert(f, 1, REC(1, 1), 1);
+
+ ASSERT(bin_length(f) == 3, "length after insert");
+
+ struct record buf[3];
+ ASSERT(bin_read(f, 0, buf, 3) == 3, "read 3 records");
+ ASSERT(buf[0].key == 0, "insert at 1 preserves [0]");
+ ASSERT(buf[1].key == 1, "insert at 1 sets [1]");
+ ASSERT(buf[2].key == 2, "insert at 1 preserves [2]");
+
+ bin_close(f);
+ cleanup("/tmp/test_bin_insert.bin");
+}
+
+void test_insert_at_end() {
+ FILE *f = bin_init("/tmp/test_bin_insert_end.bin", sizeof(struct record));
+ ASSERT(f != NULL, "bin_init returns non-NULL");
+ if (!f) return;
+
+ bin_append(f, REC(0, 0), 1);
+
+ bin_insert(f, 1, REC(1, 1), 1);
+
+ ASSERT(bin_length(f) == 2, "length after insert at end");
+
+ struct record buf[2];
+ ASSERT(bin_read(f, 0, buf, 2) == 2, "read 2 records");
+ ASSERT(buf[0].key == 0, "insert at end preserves [0]");
+ ASSERT(buf[1].key == 1, "insert at end sets [1]");
+
+ bin_close(f);
+ cleanup("/tmp/test_bin_insert_end.bin");
+}
+
+void test_insert_at_beginning() {
+ FILE *f = bin_init("/tmp/test_bin_insert_begin.bin", sizeof(struct record));
+ ASSERT(f != NULL, "bin_init returns non-NULL");
+ if (!f) return;
+
+ bin_append(f, REC(1, 1), 1);
+
+ bin_insert(f, 0, REC(0, 0), 1);
+
+ struct record buf[2];
+ ASSERT(bin_read(f, 0, buf, 2) == 2, "read 2 records");
+ ASSERT(buf[0].key == 0, "insert at 0 sets [0]");
+ ASSERT(buf[1].key == 1, "insert at 0 preserves [1]");
+
+ bin_close(f);
+ cleanup("/tmp/test_bin_insert_begin.bin");
+}
+
+void test_open() {
+ FILE *f = bin_init("/tmp/test_bin_open.bin", sizeof(struct record));
+ ASSERT(f != NULL, "bin_init returns non-NULL");
+ if (!f) {
+ cleanup("/tmp/test_bin_open.bin");
+ return;
+ }
+ bin_append(f, REC(42, 42), 1);
+ bin_close(f);
+
+ f = bin_open("/tmp/test_bin_open.bin");
+ ASSERT(f != NULL, "bin_open returns non-NULL");
+ if (!f) {
+ cleanup("/tmp/test_bin_open.bin");
+ return;
+ }
+ ASSERT(bin_length(f) == 1, "opened file has correct length");
+
+ struct record r;
+ ASSERT(bin_read(f, 0, &r, 1) == 1, "read 1 record");
+ ASSERT(r.key == 42, "read from opened file");
+
+ bin_close(f);
+ cleanup("/tmp/test_bin_open.bin");
+}
+
+void test_search() {
+ FILE *f = bin_init("/tmp/test_bin_search.bin", sizeof(struct record));
+ ASSERT(f != NULL, "bin_init returns non-NULL");
+ if (!f) return;
+
+ for (int i = 0; i < 20; i++) {
+ bin_append(f, REC(i * 5, 0), 1);
+ }
+
+ i64 found = bin_search(f, 25);
+ ASSERT(found == 5, "search finds existing key");
+
+ i64 not_found = bin_search(f, 26);
+ ASSERT(not_found < 0, "search returns negative for missing key");
+
+ i64 idx = bin_fuzzy_index(not_found);
+ ASSERT(idx == 6, "fuzzy_index returns insertion point");
+
+ bin_close(f);
+ cleanup("/tmp/test_bin_search.bin");
+}
+
+void test_search_empty() {
+ FILE *f = bin_init("/tmp/test_bin_search_empty.bin", sizeof(struct record));
+ ASSERT(f != NULL, "bin_init returns non-NULL");
+ if (!f) return;
+
+ i64 result = bin_search(f, 1);
+ ASSERT(result == -1, "search empty returns -1");
+
+ bin_close(f);
+ cleanup("/tmp/test_bin_search_empty.bin");
+}
+
+void test_append_multiple() {
+ FILE *f = bin_init("/tmp/test_bin_multi.bin", sizeof(struct record));
+ ASSERT(f != NULL, "bin_init returns non-NULL");
+ if (!f) return;
+
+ struct record batch[] = {
+ {.key = 10, .value = 10},
+ {.key = 20, .value = 20},
+ {.key = 30, .value = 30},
+ };
+
+ bin_append(f, batch, 3);
+ ASSERT(bin_length(f) == 3, "batch append length");
+
+ struct record buf[3];
+ ASSERT(bin_read(f, 0, buf, 3) == 3, "read 3 records");
+ ASSERT(buf[0].key == 10, "batch[0]");
+ ASSERT(buf[1].key == 20, "batch[1]");
+ ASSERT(buf[2].key == 30, "batch[2]");
+
+ bin_close(f);
+ cleanup("/tmp/test_bin_multi.bin");
+}
+
+void test_fuzzy_index_positive() {
+ u64 idx = bin_fuzzy_index(5);
+ ASSERT(idx == 5, "fuzzy_index passthrough positive");
+}
+
+void test_ok_variants() {
+ FILE *f = bin_init("/tmp/test_bin_ok.bin", sizeof(struct record));
+ ASSERT(f != NULL, "bin_init returns non-NULL");
+ if (!f) return;
+
+ ASSERT(bin_append_ok(f, REC(1, 1), 1), "append_ok returns 1");
+ ASSERT(bin_append_ok(f, NULL, 1) == 0, "append_ok NULL data returns 0");
+ ASSERT(bin_append_ok(NULL, REC(2, 2), 1) == 0, "append_ok NULL file returns 0");
+
+ ASSERT(bin_write_ok(f, 0, REC(99, 99), 1), "write_ok returns 1");
+ ASSERT(bin_write_ok(f, -1, REC(0, 0), 1) == 0, "write_ok negative index returns 0");
+ ASSERT(bin_write_ok(f, 99, REC(0, 0), 1) == 0, "write_ok out-of-range returns 0");
+
+ ASSERT(bin_insert_ok(f, 0, REC(0, 0), 1), "insert_ok returns 1");
+ ASSERT(bin_insert_ok(f, -1, REC(0, 0), 1) == 0, "insert_ok negative index returns 0");
+
+ ASSERT(bin_close(f), "close returns 1");
+ ASSERT(bin_close(NULL) == 0, "close NULL returns 0");
+
+ cleanup("/tmp/test_bin_ok.bin");
+}
+
+int main() {
+ test_init_and_close();
+ test_append_and_read();
+ test_write();
+ test_insert();
+ test_insert_at_end();
+ test_insert_at_beginning();
+ test_open();
+ test_search();
+ test_search_empty();
+ test_append_multiple();
+ test_fuzzy_index_positive();
+ test_ok_variants();
+
+ printf("test_bin: %d failures\n", failures);
+ return failures;
+}