inital nodes and props

This commit is contained in:
chrisbell committed 2026-11-02 19:42:48 -06:00
1 parent 98f798ad45
commit 5dad34f2e3
12 files changed
+1107 -9

No files matched your search

+4
View File
@@ -7,3 +7,7 @@ if(TARGET SDL3::SDL3)
target_link_libraries(smoke_sdl3 PRIVATE SDL3::SDL3)
add_test(NAME smoke_sdl3 COMMAND smoke_sdl3)
endif()
add_executable(test_nodes nodes.c)
target_link_libraries(test_nodes PRIVATE regolith)
add_test(NAME nodes COMMAND test_nodes)
+191
View File
@@ -0,0 +1,191 @@
#include "testing.h"
#include <string.h>
static int child_count(rg_context *ctx, rg_node parent) {
int n = 0;
rg_node c;
for (c = rg_node_first_child(ctx, parent); c != RG_NODE_NONE;
c = rg_node_next_sibling(ctx, c))
n++;
return n;
}
static void test_tree(rg_context *ctx) {
rg_node root = rg_root(ctx);
rg_node a = rg_node_create(ctx, RG_NODE_VBOX);
rg_node b = rg_node_create(ctx, RG_NODE_LABEL);
rg_node c = rg_node_create(ctx, RG_NODE_BUTTON);
rg_node d = rg_node_create(ctx, RG_NODE_LABEL);
CHECK(rg_node_valid(ctx, root));
CHECK(rg_node_type(ctx, root) == RG_NODE_ROOT);
CHECK(rg_node_create(ctx, RG_NODE_ROOT) == RG_NODE_NONE);
CHECK(rg_node_append(ctx, root, a) == RG_OK);
CHECK(rg_node_append(ctx, a, b) == RG_OK);
CHECK(rg_node_append(ctx, a, c) == RG_OK);
CHECK(rg_node_insert_before(ctx, a, d, c) == RG_OK);
CHECK(child_count(ctx, a) == 3);
CHECK(rg_node_first_child(ctx, a) == b);
CHECK(rg_node_next_sibling(ctx, b) == d);
CHECK(rg_node_next_sibling(ctx, d) == c);
CHECK(rg_node_next_sibling(ctx, c) == RG_NODE_NONE);
CHECK(rg_node_parent(ctx, d) == a);
CHECK(rg_node_append(ctx, a, b) == RG_OK);
CHECK(rg_node_next_sibling(ctx, c) == b);
CHECK(child_count(ctx, a) == 3);
CHECK(rg_node_append(ctx, b, a) == RG_ERR_STATE);
CHECK(rg_node_append(ctx, a, a) == RG_ERR_STATE);
CHECK(rg_node_append(ctx, a, root) == RG_ERR_STATE);
CHECK(rg_node_insert_before(ctx, root, b, c) == RG_ERR_STATE);
CHECK(rg_node_detach(ctx, d) == RG_OK);
CHECK(rg_node_parent(ctx, d) == RG_NODE_NONE);
CHECK(child_count(ctx, a) == 2);
rg_node_destroy(ctx, a);
rg_node_destroy(ctx, d);
}
static void test_handles(rg_context *ctx) {
rg_node root = rg_root(ctx);
rg_node a = rg_node_create(ctx, RG_NODE_PANEL);
rg_node b = rg_node_create(ctx, RG_NODE_LABEL);
rg_node c = rg_node_create(ctx, RG_NODE_LABEL);
rg_node fresh;
CHECK(rg_node_append(ctx, root, a) == RG_OK);
CHECK(rg_node_append(ctx, a, b) == RG_OK);
CHECK(rg_node_append(ctx, b, c) == RG_OK);
rg_node_destroy(ctx, a);
CHECK(!rg_node_valid(ctx, a));
CHECK(!rg_node_valid(ctx, b));
CHECK(!rg_node_valid(ctx, c));
CHECK(rg_set_float(ctx, b, RG_PROP_GAP, 1.0f) == RG_ERR_HANDLE);
CHECK(child_count(ctx, root) == 0);
fresh = rg_node_create(ctx, RG_NODE_BOX);
CHECK(fresh != a && fresh != b && fresh != c);
CHECK(!rg_node_valid(ctx, a));
CHECK(rg_node_valid(ctx, fresh));
rg_node_destroy(ctx, root);
CHECK(rg_node_valid(ctx, root));
rg_node_destroy(ctx, fresh);
}
static void test_props(rg_context *ctx) {
rg_node hbox = rg_node_create(ctx, RG_NODE_HBOX);
rg_node vbox = rg_node_create(ctx, RG_NODE_VBOX);
rg_node btn = rg_node_create(ctx, RG_NODE_BUTTON);
rg_node lbl = rg_node_create(ctx, RG_NODE_LABEL);
rg_value v;
char buf[16];
int i;
for (i = 0; i < RG_PROP_COUNT; i++) {
CHECK(rg_prop_name(i) != NULL);
CHECK(rg_prop_find(rg_prop_name(i)) == i);
CHECK(rg_prop_type(i) != RG_VAL_NONE);
}
CHECK(rg_prop_find("no_such_prop") == -1);
CHECK(rg_type_find("Button") == RG_NODE_BUTTON);
CHECK(rg_type_find("Nope") == -1);
CHECK(rg_get(ctx, hbox, RG_PROP_DIRECTION, &v) == RG_OK && v.i == RG_DIR_ROW);
CHECK(rg_get(ctx, vbox, RG_PROP_DIRECTION, &v) == RG_OK &&
v.i == RG_DIR_COLUMN);
CHECK(rg_get(ctx, btn, RG_PROP_PADDING, &v) == RG_OK);
CHECK(v.f[0] == 8.0f && v.f[1] == 16.0f && v.f[2] == 8.0f && v.f[3] == 16.0f);
CHECK(rg_get(ctx, lbl, RG_PROP_OPACITY, &v) == RG_OK && v.f[0] == 1.0f);
CHECK(rg_get(ctx, lbl, RG_PROP_TEXT, &v) == RG_OK && strcmp(v.s, "") == 0);
CHECK(rg_set_int(ctx, vbox, RG_PROP_DIRECTION, RG_DIR_ROW) == RG_OK);
CHECK(rg_get(ctx, vbox, RG_PROP_DIRECTION, &v) == RG_OK && v.i == RG_DIR_ROW);
CHECK(rg_unset(ctx, vbox, RG_PROP_DIRECTION) == RG_OK);
CHECK(rg_get(ctx, vbox, RG_PROP_DIRECTION, &v) == RG_OK &&
v.i == RG_DIR_COLUMN);
CHECK(rg_unset(ctx, vbox, RG_PROP_DIRECTION) == RG_OK);
CHECK(rg_set_color(ctx, btn, RG_PROP_BACKGROUND, 1, 0, 0, 1) == RG_OK);
CHECK(rg_get(ctx, btn, RG_PROP_BACKGROUND, &v) == RG_OK && v.f[0] == 1.0f &&
v.f[1] == 0.0f);
CHECK(rg_set_float(ctx, btn, RG_PROP_BACKGROUND, 1.0f) == RG_ERR_TYPE);
CHECK(rg_set_int(ctx, btn, RG_PROP_GAP, 1) == RG_ERR_TYPE);
CHECK(rg_set(ctx, btn, RG_PROP_GAP, NULL) == RG_ERR_TYPE);
CHECK(rg_set_string(ctx, btn, RG_PROP_TEXT, NULL) == RG_ERR_TYPE);
CHECK(rg_set_float(ctx, btn, 9999, 1.0f) == RG_ERR_PROP);
CHECK(rg_get(ctx, btn, -1, &v) == RG_ERR_PROP);
memset(&v, 0, sizeof v);
v.type = RG_VAL_SIZE;
v.i = 99;
CHECK(rg_set(ctx, btn, RG_PROP_WIDTH, &v) == RG_ERR_TYPE);
v.i = RG_SIZE_FIXED;
v.f[0] = 200.0f;
CHECK(rg_set(ctx, btn, RG_PROP_WIDTH, &v) == RG_OK);
strcpy(buf, "Start");
CHECK(rg_set_string(ctx, lbl, RG_PROP_TEXT, buf) == RG_OK);
strcpy(buf, "Other");
CHECK(rg_get(ctx, lbl, RG_PROP_TEXT, &v) == RG_OK &&
strcmp(v.s, "Start") == 0);
CHECK(rg_set_string(ctx, lbl, RG_PROP_TEXT, v.s) == RG_OK);
CHECK(rg_get(ctx, lbl, RG_PROP_TEXT, &v) == RG_OK &&
strcmp(v.s, "Start") == 0);
CHECK(rg_set_string(ctx, lbl, RG_PROP_TEXT,
"A much longer replacement string") == RG_OK);
CHECK(rg_get(ctx, lbl, RG_PROP_TEXT, &v) == RG_OK &&
strcmp(v.s, "A much longer replacement string") == 0);
for (i = 0; i < RG_PROP_COUNT; i++) {
rg_value tmp;
memset(&tmp, 0, sizeof tmp);
tmp.type = rg_prop_type(i);
tmp.s = tmp.type == RG_VAL_STRING ? "x" : NULL;
if (tmp.type == RG_VAL_SIZE)
tmp.i = RG_SIZE_FIT;
CHECK(rg_set(ctx, btn, i, &tmp) == RG_OK);
}
rg_node_destroy(ctx, hbox);
rg_node_destroy(ctx, vbox);
rg_node_destroy(ctx, btn);
rg_node_destroy(ctx, lbl);
}
int main(void) {
testing_alloc ta = {0, 0};
rg_config cfg;
rg_context *ctx;
rg_config_init(&cfg);
testing_use_counting_allocator(&cfg, &ta);
cfg.log_level = RG_LOG_WARN;
ctx = rg_create(&cfg);
CHECK(ctx != NULL);
if (!ctx)
return 1;
test_tree(ctx);
test_handles(ctx);
test_props(ctx);
rg_destroy(ctx);
CHECK(ta.live == 0);
if (ta.live != 0)
printf("leak: %zu bytes still live\n", ta.live);
printf("nodes: %s, peak %zu bytes\n", g_failures ? "FAILED" : "ok", ta.peak);
return g_failures ? 1 : 0;
}
+58
View File
@@ -0,0 +1,58 @@
#ifndef RG_TESTING_H
#define RG_TESTING_H
#include "regolith.h"
#include <stdio.h>
#include <stdlib.h>
static int g_failures;
#define CHECK(cond) \
do { \
if (!(cond)) { \
printf("FAIL %s:%d: %s\n", __FILE__, __LINE__, #cond); \
g_failures++; \
} \
} while (0)
typedef struct testing_alloc {
size_t live;
size_t peak;
} testing_alloc;
static void ta_track(testing_alloc *t, size_t add, size_t sub) {
t->live = t->live + add - sub;
if (t->live > t->peak)
t->peak = t->live;
}
static void *ta_alloc(size_t size, void *user) {
void *p = malloc(size);
if (p)
ta_track(user, size, 0);
return p;
}
static void *ta_realloc(void *ptr, size_t old_size, size_t new_size,
void *user) {
void *p = realloc(ptr, new_size);
if (p)
ta_track(user, new_size, old_size);
return p;
}
static void ta_free(void *ptr, size_t size, void *user) {
ta_track(user, 0, size);
free(ptr);
}
static void testing_use_counting_allocator(rg_config *cfg, testing_alloc *t) {
cfg->allocator.alloc = ta_alloc;
cfg->allocator.realloc = ta_realloc;
cfg->allocator.free = ta_free;
cfg->allocator.user = t;
}
#endif