Files
deusdata--codebase-memory-mcp/tests/test_scope.c
T
wehub-resource-sync 41cb1c0170
OpenSSF Scorecard / scorecard (push) Failing after 0s
DCO / dco (push) Failing after 0s
CodeQL SAST / analyze (push) Failing after 1s
Deploy Pages / deploy (push) Failing after 1s
chore: import upstream snapshot with attribution
2026-07-13 12:28:05 +08:00

197 lines
6.9 KiB
C

/*
* test_scope.c — Tests for the chunked linked-frame CBMScope.
*
* Phase 0 of Python LSP integration: replaces the legacy fixed 64-binding
* array with growable per-scope chunks. Tests verify dynamic growth,
* binding-shadowing semantics, and the parent-chain lookup contract that
* Go and C/C++ LSP implementations rely on.
*/
#include "test_framework.h"
#include "cbm.h"
#include "lsp/scope.h"
#include "lsp/type_rep.h"
/* Build a NAMED type for a fixture string. Arena-allocated. */
static const CBMType *named_t(CBMArena *a, const char *qn) {
return cbm_type_named(a, qn);
}
/* ── Basic API ─────────────────────────────────────────────────── */
TEST(scope_push_returns_distinct_scope) {
CBMArena a;
cbm_arena_init(&a);
CBMScope *root = cbm_scope_push(&a, NULL);
CBMScope *child = cbm_scope_push(&a, root);
ASSERT_NOT_NULL(root);
ASSERT_NOT_NULL(child);
ASSERT(child != root);
ASSERT(child->parent == root);
cbm_arena_destroy(&a);
PASS();
}
TEST(scope_pop_returns_parent) {
CBMArena a;
cbm_arena_init(&a);
CBMScope *root = cbm_scope_push(&a, NULL);
CBMScope *child = cbm_scope_push(&a, root);
ASSERT(cbm_scope_pop(child) == root);
ASSERT(cbm_scope_pop(root) == NULL);
cbm_arena_destroy(&a);
PASS();
}
TEST(scope_lookup_unbound_returns_unknown) {
CBMArena a;
cbm_arena_init(&a);
CBMScope *s = cbm_scope_push(&a, NULL);
const CBMType *t = cbm_scope_lookup(s, "missing");
ASSERT_NOT_NULL(t);
ASSERT(cbm_type_is_unknown(t));
cbm_arena_destroy(&a);
PASS();
}
/* ── Binding semantics ─────────────────────────────────────────── */
TEST(scope_bind_then_lookup) {
CBMArena a;
cbm_arena_init(&a);
CBMScope *s = cbm_scope_push(&a, NULL);
cbm_scope_bind(s, "x", named_t(&a, "int"));
const CBMType *t = cbm_scope_lookup(s, "x");
ASSERT_NOT_NULL(t);
ASSERT(t->kind == CBM_TYPE_NAMED);
ASSERT_STR_EQ(t->data.named.qualified_name, "int");
cbm_arena_destroy(&a);
PASS();
}
TEST(scope_rebind_overwrites_in_place) {
CBMArena a;
cbm_arena_init(&a);
CBMScope *s = cbm_scope_push(&a, NULL);
cbm_scope_bind(s, "x", named_t(&a, "int"));
cbm_scope_bind(s, "x", named_t(&a, "string"));
const CBMType *t = cbm_scope_lookup(s, "x");
ASSERT_STR_EQ(t->data.named.qualified_name, "string");
cbm_arena_destroy(&a);
PASS();
}
TEST(scope_lookup_walks_parent_chain) {
CBMArena a;
cbm_arena_init(&a);
CBMScope *root = cbm_scope_push(&a, NULL);
CBMScope *child = cbm_scope_push(&a, root);
cbm_scope_bind(root, "outer", named_t(&a, "Outer"));
cbm_scope_bind(child, "inner", named_t(&a, "Inner"));
const CBMType *outer_in_child = cbm_scope_lookup(child, "outer");
const CBMType *inner_in_root = cbm_scope_lookup(root, "inner");
ASSERT_STR_EQ(outer_in_child->data.named.qualified_name, "Outer");
ASSERT(cbm_type_is_unknown(inner_in_root));
cbm_arena_destroy(&a);
PASS();
}
TEST(scope_child_shadows_parent) {
CBMArena a;
cbm_arena_init(&a);
CBMScope *root = cbm_scope_push(&a, NULL);
CBMScope *child = cbm_scope_push(&a, root);
cbm_scope_bind(root, "x", named_t(&a, "ParentInt"));
cbm_scope_bind(child, "x", named_t(&a, "ChildStr"));
const CBMType *t = cbm_scope_lookup(child, "x");
ASSERT_STR_EQ(t->data.named.qualified_name, "ChildStr");
cbm_arena_destroy(&a);
PASS();
}
/* ── Dynamic growth — the Phase 0 win ───────────────────────────── */
TEST(scope_dynamic_growth_300_bindings) {
CBMArena a;
cbm_arena_init(&a);
CBMScope *s = cbm_scope_push(&a, NULL);
char names[300][16];
for (int i = 0; i < 300; i++) {
snprintf(names[i], sizeof(names[0]), "v%d", i);
char qn[32];
snprintf(qn, sizeof(qn), "T%d", i);
cbm_scope_bind(s, names[i], named_t(&a, qn));
}
/* All 300 bindings retrievable — old 64-cap would have dropped 236 */
for (int i = 0; i < 300; i++) {
const CBMType *t = cbm_scope_lookup(s, names[i]);
ASSERT_NOT_NULL(t);
ASSERT(t->kind == CBM_TYPE_NAMED);
char expected[32];
snprintf(expected, sizeof(expected), "T%d", i);
ASSERT_STR_EQ(t->data.named.qualified_name, expected);
}
cbm_arena_destroy(&a);
PASS();
}
TEST(scope_growth_chunk_boundary) {
CBMArena a;
cbm_arena_init(&a);
CBMScope *s = cbm_scope_push(&a, NULL);
char names[CBM_SCOPE_CHUNK_BINDINGS + 1][16];
/* Fill exactly one chunk + spillover: forces second chunk allocation */
for (int i = 0; i <= CBM_SCOPE_CHUNK_BINDINGS; i++) {
snprintf(names[i], sizeof(names[0]), "n%d", i);
char qn[16];
snprintf(qn, sizeof(qn), "T%d", i);
cbm_scope_bind(s, names[i], named_t(&a, qn));
}
/* First-chunk binding still retrievable */
const CBMType *first = cbm_scope_lookup(s, "n0");
ASSERT_STR_EQ(first->data.named.qualified_name, "T0");
/* Spillover binding (would have fallen off legacy 64-cap if cap < 17) */
char last_name[16];
snprintf(last_name, sizeof(last_name), "n%d", CBM_SCOPE_CHUNK_BINDINGS);
char last_qn[16];
snprintf(last_qn, sizeof(last_qn), "T%d", CBM_SCOPE_CHUNK_BINDINGS);
const CBMType *last = cbm_scope_lookup(s, last_name);
ASSERT_STR_EQ(last->data.named.qualified_name, last_qn);
cbm_arena_destroy(&a);
PASS();
}
TEST(scope_rebind_in_old_chunk) {
/* After many bindings have spilled into newer chunks, rebinding a name
* from the original chunk must overwrite that binding in place rather
* than create a duplicate in the head chunk. */
CBMArena a;
cbm_arena_init(&a);
CBMScope *s = cbm_scope_push(&a, NULL);
cbm_scope_bind(s, "early", named_t(&a, "EarlyV1"));
char names[100][16];
for (int i = 0; i < 100; i++) {
snprintf(names[i], sizeof(names[0]), "fill%d", i);
cbm_scope_bind(s, names[i], named_t(&a, "Filler"));
}
cbm_scope_bind(s, "early", named_t(&a, "EarlyV2"));
const CBMType *t = cbm_scope_lookup(s, "early");
ASSERT_STR_EQ(t->data.named.qualified_name, "EarlyV2");
cbm_arena_destroy(&a);
PASS();
}
/* ── Suite registration ────────────────────────────────────────── */
SUITE(scope) {
RUN_TEST(scope_push_returns_distinct_scope);
RUN_TEST(scope_pop_returns_parent);
RUN_TEST(scope_lookup_unbound_returns_unknown);
RUN_TEST(scope_bind_then_lookup);
RUN_TEST(scope_rebind_overwrites_in_place);
RUN_TEST(scope_lookup_walks_parent_chain);
RUN_TEST(scope_child_shadows_parent);
RUN_TEST(scope_dynamic_growth_300_bindings);
RUN_TEST(scope_growth_chunk_boundary);
RUN_TEST(scope_rebind_in_old_chunk);
}