mirror of
https://github.com/DeusData/codebase-memory-mcp.git
synced 2026-11-06 17:49:47 +00:00
477 lines
24 KiB
C
477 lines
24 KiB
C
#include "test_framework.h"
|
|
#include "resolve/proof.h"
|
|
#include "resolve/policy.h"
|
|
#include "resolve/symbols.h"
|
|
#include "lsp/proof_bridge.h"
|
|
#include <string.h>
|
|
|
|
static cbm_resolve_result_t proof_outcome(const char *target) {
|
|
return (cbm_resolve_result_t){.binding = CBM_RESOLVE_BINDING_UNIQUE,
|
|
.domain = CBM_RESOLVE_DOMAIN_PROJECT,
|
|
.analysis = CBM_RESOLVE_ANALYSIS_COMPLETE,
|
|
.rule = CBM_RESOLVE_RULE_DECLARED_TYPE,
|
|
.facts = CBM_RESOLVE_FACT_EXACT_TARGET |
|
|
CBM_RESOLVE_FACT_LOOKUP_COMPLETE |
|
|
CBM_RESOLVE_FACT_TYPE_BOUND,
|
|
.target = target,
|
|
.candidate_count = 1,
|
|
.reason = "test_exact_binding"};
|
|
}
|
|
static const cbm_resolve_proof_t *proof_leaf(cbm_resolve_proof_pool_t *pool, const char *name,
|
|
cbm_resolve_analysis_t state) {
|
|
cbm_resolve_proof_source_t source = {.module = "project.module",
|
|
.content_identity = "captured-bytes",
|
|
.configuration_identity = "grammar-config-v1"};
|
|
cbm_resolve_proof_key_t key = {
|
|
.lookup_domain = "project.module", .lookup_key = name, .declaration_identity = name};
|
|
cbm_resolve_result_t outcome = proof_outcome(name);
|
|
outcome.analysis = state;
|
|
return cbm_resolve_proof_leaf(pool, CBM_PROOF_SOURCE_FACT, &source, &key, &outcome);
|
|
}
|
|
static const cbm_resolve_proof_t *proof_join(cbm_resolve_proof_pool_t *pool,
|
|
const cbm_resolve_proof_t *const *inputs, size_t count,
|
|
bool closed) {
|
|
cbm_resolve_result_t outcome = proof_outcome("project.Widget");
|
|
cbm_resolve_proof_key_t key = {.lookup_domain = "flow-join", .lookup_key = "join:40"};
|
|
return cbm_resolve_proof_derive(pool, CBM_PROOF_TYPE_JOIN, NULL, &key, &outcome, inputs, count,
|
|
closed);
|
|
}
|
|
|
|
TEST(resolver_proof_full_key_interning_and_collision_equality) {
|
|
CBMArena arena;
|
|
cbm_arena_init(&arena);
|
|
cbm_resolve_proof_pool_t pool;
|
|
cbm_resolve_proof_pool_init(&pool, &arena, 0, 0, 0);
|
|
const cbm_resolve_proof_t *a = proof_leaf(&pool, "project.a", CBM_RESOLVE_ANALYSIS_COMPLETE);
|
|
const cbm_resolve_proof_t *b = proof_leaf(&pool, "project.b", CBM_RESOLVE_ANALYSIS_COMPLETE);
|
|
ASSERT_NOT_NULL(a);
|
|
ASSERT_NOT_NULL(b);
|
|
ASSERT(a == proof_leaf(&pool, "project.a", CBM_RESOLVE_ANALYSIS_COMPLETE));
|
|
ASSERT(a != b);
|
|
cbm_resolve_proof_t collision = *b;
|
|
collision.value.fingerprint = a->value.fingerprint;
|
|
ASSERT(cbm_resolve_proof_compare(a, &collision) != 0);
|
|
cbm_resolve_proof_source_t changed = a->value.source;
|
|
changed.configuration_identity = "grammar-config-v2";
|
|
const cbm_resolve_proof_t *configuration = cbm_resolve_proof_leaf(
|
|
&pool, CBM_PROOF_SOURCE_FACT, &changed, &a->value.key, &a->value.outcome);
|
|
ASSERT_NOT_NULL(configuration);
|
|
ASSERT(configuration != a);
|
|
changed = a->value.source;
|
|
changed.content_identity = "different-bytes";
|
|
ASSERT(cbm_resolve_proof_leaf(&pool, CBM_PROOF_SOURCE_FACT, &changed, &a->value.key,
|
|
&a->value.outcome) != a);
|
|
ASSERT_EQ(pool.count, 4);
|
|
cbm_arena_destroy(&arena);
|
|
PASS();
|
|
}
|
|
|
|
TEST(resolver_proof_closed_join_keeps_all_distinct_supports) {
|
|
CBMArena arena;
|
|
cbm_arena_init(&arena);
|
|
cbm_resolve_proof_pool_t pool;
|
|
cbm_resolve_proof_pool_init(&pool, &arena, 0, 0, 0);
|
|
const cbm_resolve_proof_t *a = proof_leaf(&pool, "project.a", CBM_RESOLVE_ANALYSIS_COMPLETE);
|
|
const cbm_resolve_proof_t *b = proof_leaf(&pool, "project.b", CBM_RESOLVE_ANALYSIS_COMPLETE);
|
|
const cbm_resolve_proof_t *inputs[] = {a, b, a};
|
|
const cbm_resolve_proof_t *reverse[] = {b, a};
|
|
const cbm_resolve_proof_t *joined = proof_join(&pool, inputs, 3, true);
|
|
ASSERT_NOT_NULL(joined);
|
|
ASSERT(joined == proof_join(&pool, reverse, 2, true));
|
|
ASSERT_EQ(joined->value.premise_count, 2);
|
|
ASSERT(joined->value.premises[0] != joined->value.premises[1]);
|
|
ASSERT(joined->value.predecessors_closed);
|
|
ASSERT(cbm_resolve_admit(&joined->value.outcome));
|
|
ASSERT(joined->value.outcome.proof == NULL);
|
|
ASSERT(joined != proof_join(&pool, &a, 1, true));
|
|
cbm_arena_destroy(&arena);
|
|
PASS();
|
|
}
|
|
|
|
typedef struct {
|
|
const cbm_resolve_proof_t *nodes[128];
|
|
size_t count;
|
|
} proof_order_t;
|
|
static bool proof_postorder(const cbm_resolve_proof_t *node, void *opaque) {
|
|
proof_order_t *order = opaque;
|
|
if (order->count == sizeof(order->nodes) / sizeof(order->nodes[0]))
|
|
return false;
|
|
for (size_t i = 0; i < node->value.premise_count; i++) {
|
|
bool found = false;
|
|
for (size_t j = 0; j < order->count; j++)
|
|
if (order->nodes[j] == node->value.premises[i])
|
|
found = true;
|
|
if (!found)
|
|
return false;
|
|
}
|
|
order->nodes[order->count++] = node;
|
|
return true;
|
|
}
|
|
TEST(resolver_proof_clone_owns_dag_and_canonical_traversal) {
|
|
CBMArena source_arena, destination_arena;
|
|
cbm_arena_init(&source_arena);
|
|
cbm_arena_init(&destination_arena);
|
|
cbm_resolve_proof_pool_t source, destination;
|
|
cbm_resolve_proof_pool_init(&source, &source_arena, 0, 0, 0);
|
|
cbm_resolve_proof_pool_init(&destination, &destination_arena, 0, 0, 0);
|
|
const cbm_resolve_proof_t *inputs[] = {
|
|
proof_leaf(&source, "project.a", CBM_RESOLVE_ANALYSIS_COMPLETE),
|
|
proof_leaf(&source, "project.b", CBM_RESOLVE_ANALYSIS_COMPLETE)};
|
|
const cbm_resolve_proof_t *joined = proof_join(&source, inputs, 2, true);
|
|
const cbm_resolve_proof_t *copy = cbm_resolve_proof_clone(&destination, joined);
|
|
ASSERT_NOT_NULL(copy);
|
|
ASSERT(copy != joined);
|
|
ASSERT_EQ(cbm_resolve_proof_compare(copy, joined), 0);
|
|
ASSERT(copy->value.premises != joined->value.premises);
|
|
ASSERT(copy->value.premises[0] != joined->value.premises[0]);
|
|
ASSERT(copy == cbm_resolve_proof_clone(&destination, joined));
|
|
cbm_arena_destroy(&source_arena);
|
|
proof_order_t order = {0};
|
|
ASSERT(cbm_resolve_proof_visit(copy, 10, proof_postorder, &order));
|
|
ASSERT_EQ(order.count, 3);
|
|
ASSERT(order.nodes[2] == copy);
|
|
ASSERT_STR_EQ(order.nodes[0]->value.source.content_identity, "captured-bytes");
|
|
ASSERT_STR_EQ(order.nodes[0]->value.source.configuration_identity, "grammar-config-v1");
|
|
cbm_arena_destroy(&destination_arena);
|
|
PASS();
|
|
}
|
|
|
|
TEST(resolver_proof_incomplete_predecessors_revoke_authority) {
|
|
const cbm_resolve_analysis_t states[] = {
|
|
CBM_RESOLVE_ANALYSIS_PENDING, CBM_RESOLVE_ANALYSIS_UNKNOWN,
|
|
CBM_RESOLVE_ANALYSIS_UNSUPPORTED, CBM_RESOLVE_ANALYSIS_LIMITED,
|
|
CBM_RESOLVE_ANALYSIS_FAILED};
|
|
CBMArena arena;
|
|
cbm_arena_init(&arena);
|
|
cbm_resolve_proof_pool_t pool;
|
|
cbm_resolve_proof_pool_init(&pool, &arena, 0, 0, 0);
|
|
const cbm_resolve_proof_t *good = proof_leaf(&pool, "project.a", CBM_RESOLVE_ANALYSIS_COMPLETE);
|
|
for (size_t i = 0; i < sizeof(states) / sizeof(states[0]); i++) {
|
|
const cbm_resolve_proof_t *inputs[] = {good, proof_leaf(&pool, "project.b", states[i])};
|
|
const cbm_resolve_proof_t *joined = proof_join(&pool, inputs, 2, true);
|
|
ASSERT_NOT_NULL(joined);
|
|
ASSERT_EQ(joined->value.outcome.analysis, states[i]);
|
|
ASSERT_EQ(joined->value.premise_count, 2);
|
|
ASSERT_EQ(joined->value.outcome.binding, CBM_RESOLVE_BINDING_UNRESOLVED);
|
|
ASSERT_EQ(joined->value.outcome.facts, 0);
|
|
ASSERT(joined->value.outcome.target == NULL);
|
|
ASSERT(!cbm_resolve_admit(&joined->value.outcome));
|
|
}
|
|
const cbm_resolve_proof_t *pending = proof_join(&pool, &good, 1, false);
|
|
ASSERT_NOT_NULL(pending);
|
|
ASSERT_EQ(pending->value.outcome.analysis, CBM_RESOLVE_ANALYSIS_PENDING);
|
|
ASSERT(!pending->value.predecessors_closed);
|
|
cbm_arena_destroy(&arena);
|
|
PASS();
|
|
}
|
|
|
|
TEST(resolver_proof_limits_and_unknown_source_are_explicit) {
|
|
CBMArena arena;
|
|
cbm_arena_init(&arena);
|
|
cbm_resolve_proof_pool_t pool;
|
|
cbm_resolve_proof_pool_init(&pool, &arena, 1, 0, 0);
|
|
ASSERT_NOT_NULL(proof_leaf(&pool, "project.a", CBM_RESOLVE_ANALYSIS_COMPLETE));
|
|
ASSERT(proof_leaf(&pool, "project.b", CBM_RESOLVE_ANALYSIS_COMPLETE) == NULL);
|
|
ASSERT_EQ(pool.failure, CBM_RESOLVE_ANALYSIS_LIMITED);
|
|
cbm_resolve_proof_pool_init(&pool, &arena, 0, 0, 1);
|
|
ASSERT(proof_leaf(&pool, "project.a", CBM_RESOLVE_ANALYSIS_COMPLETE) == NULL);
|
|
ASSERT_EQ(pool.failure, CBM_RESOLVE_ANALYSIS_LIMITED);
|
|
cbm_resolve_proof_pool_init(&pool, &arena, 0, 0, 0);
|
|
cbm_resolve_result_t outcome = proof_outcome("project.Widget");
|
|
cbm_resolve_proof_key_t key = {.type_identity = "project.Widget", .origin = 1};
|
|
const cbm_resolve_proof_t *audit =
|
|
cbm_resolve_proof_leaf(&pool, CBM_PROOF_ADAPTER_TYPE, NULL, &key, &outcome);
|
|
ASSERT_NOT_NULL(audit);
|
|
ASSERT(audit->value.source.content_identity == NULL);
|
|
ASSERT_EQ(audit->value.kind, CBM_PROOF_ADAPTER_TYPE);
|
|
const cbm_resolve_proof_t *missing =
|
|
cbm_resolve_proof_leaf(&pool, CBM_PROOF_SOURCE_FACT, NULL, &key, &outcome);
|
|
ASSERT_NOT_NULL(missing);
|
|
ASSERT_EQ(missing->value.outcome.analysis, CBM_RESOLVE_ANALYSIS_UNSUPPORTED);
|
|
ASSERT_EQ(pool.failure, CBM_RESOLVE_ANALYSIS_COMPLETE);
|
|
cbm_arena_destroy(&arena);
|
|
PASS();
|
|
}
|
|
|
|
TEST(resolver_proof_same_pool_chain_does_not_revisit_ancestors) {
|
|
CBMArena arena;
|
|
cbm_arena_init(&arena);
|
|
cbm_resolve_proof_pool_t pool;
|
|
cbm_resolve_proof_pool_init(&pool, &arena, 0, 0, 0);
|
|
const cbm_resolve_proof_t *node = proof_leaf(&pool, "project.a", CBM_RESOLVE_ANALYSIS_COMPLETE);
|
|
uint64_t first_half = 0;
|
|
for (unsigned i = 0; i < 64; i++) {
|
|
cbm_resolve_proof_key_t key = {
|
|
.lookup_domain = "copy", .lookup_key = "same-width", .start_byte = i};
|
|
cbm_resolve_result_t outcome = proof_outcome("project.Widget");
|
|
node = cbm_resolve_proof_derive(&pool, CBM_PROOF_TYPE_COPY, NULL, &key, &outcome, &node, 1,
|
|
true);
|
|
ASSERT_NOT_NULL(node);
|
|
if (i == 31)
|
|
first_half = pool.work;
|
|
}
|
|
ASSERT(pool.work < first_half * 3);
|
|
ASSERT_EQ(pool.count, 65);
|
|
proof_order_t order = {0};
|
|
ASSERT(cbm_resolve_proof_visit(node, 65, proof_postorder, &order));
|
|
ASSERT_EQ(order.count, 65);
|
|
cbm_arena_destroy(&arena);
|
|
PASS();
|
|
}
|
|
|
|
TEST(resolver_proof_named_copy_keeps_rule_and_dependency_without_admission_flags) {
|
|
CBMArena arena;
|
|
cbm_arena_init(&arena);
|
|
cbm_resolve_proof_pool_t pool;
|
|
cbm_resolve_proof_pool_init(&pool, &arena, 0, 0, 0);
|
|
const cbm_resolve_proof_t *inputs[] = {
|
|
proof_leaf(&pool, "project.export_rule", CBM_RESOLVE_ANALYSIS_COMPLETE),
|
|
proof_leaf(&pool, "project.target", CBM_RESOLVE_ANALYSIS_COMPLETE)};
|
|
cbm_resolve_result_t outcome = {.analysis = CBM_RESOLVE_ANALYSIS_COMPLETE,
|
|
.binding = CBM_RESOLVE_BINDING_UNIQUE,
|
|
.target = "project.target"};
|
|
cbm_resolve_proof_key_t key = {.lookup_domain = "exports", .lookup_key = "public_name"};
|
|
const cbm_resolve_proof_t *node = cbm_resolve_proof_derive(&pool, CBM_PROOF_NAMED_COPY, NULL,
|
|
&key, &outcome, inputs, 2, true);
|
|
ASSERT_NOT_NULL(node);
|
|
ASSERT_EQ(node->value.outcome.analysis, CBM_RESOLVE_ANALYSIS_COMPLETE);
|
|
ASSERT_EQ(node->value.outcome.binding, CBM_RESOLVE_BINDING_UNIQUE);
|
|
ASSERT_EQ(node->value.outcome.facts, 0);
|
|
ASSERT_EQ(node->value.premise_count, 2);
|
|
ASSERT(!cbm_resolve_admit(&node->value.outcome));
|
|
cbm_arena_destroy(&arena);
|
|
PASS();
|
|
}
|
|
|
|
static cbm_resolve_method_candidate_t proof_classify(const cbm_resolve_method_query_t *query,
|
|
const CBMTypeRegistry *owner,
|
|
const CBMRegisteredFunc *candidate,
|
|
void *context) {
|
|
(void)query;
|
|
(void)owner;
|
|
(void)context;
|
|
return (cbm_resolve_method_candidate_t){
|
|
.exposed = strcmp(candidate->graph_qualified_name, "project.Widget.run#hidden") == 0
|
|
? CBM_RESOLVE_METHOD_NO
|
|
: CBM_RESOLVE_METHOD_YES,
|
|
.applicable = CBM_RESOLVE_METHOD_YES,
|
|
.domain = CBM_RESOLVE_DOMAIN_PROJECT,
|
|
.graph_target = candidate->graph_qualified_name,
|
|
.graph_target_exact = true,
|
|
.declaration_identity = candidate->graph_qualified_name,
|
|
.declaration_identity_exact = true};
|
|
}
|
|
TEST(resolver_proof_member_lookup_retains_receiver_domain_and_excluded_candidate) {
|
|
CBMArena arena;
|
|
cbm_arena_init(&arena);
|
|
CBMTypeRegistry registry;
|
|
cbm_registry_init(®istry, &arena);
|
|
const char *targets[] = {"project.Widget.run#visible", "project.Widget.run#hidden"};
|
|
for (size_t i = 0; i < 2; i++) {
|
|
cbm_registry_add_func(
|
|
®istry, (CBMRegisteredFunc){.qualified_name = "project.Widget.run",
|
|
.graph_qualified_name = targets[i],
|
|
.receiver_type = "project.Widget",
|
|
.short_name = "run",
|
|
.signature = cbm_type_func(&arena, NULL, NULL, NULL),
|
|
.min_params = 0});
|
|
}
|
|
cbm_registry_finalize(®istry);
|
|
CBMResolveTypeMap map;
|
|
cbm_resolve_types_init(&map, &arena, 0);
|
|
cbm_resolve_proof_source_t source = {.module = "project.caller",
|
|
.content_identity = "captured"};
|
|
ASSERT(cbm_resolve_types_set_context(&map, &source));
|
|
CBMScope *scope = cbm_scope_push(&arena, NULL);
|
|
cbm_resolve_result_t evidence = proof_outcome("project.Widget");
|
|
ASSERT(cbm_resolve_types_declare(&map, scope, "value", 10,
|
|
cbm_type_named(&arena, "project.Widget"),
|
|
CBM_RESOLVE_TYPE_ANNOTATION, &evidence));
|
|
cbm_resolve_type_fact_t receiver = cbm_resolve_types_lookup(&map, scope, "value", 20);
|
|
cbm_resolve_method_query_t query = {.registry = ®istry,
|
|
.receiver = &receiver,
|
|
.receiver_qn = "project.Widget",
|
|
.method_name = "run",
|
|
.argument_count = 0,
|
|
.adapter_facts = CBM_RESOLVE_METHOD_DIRECT_DOMAIN_COMPLETE |
|
|
CBM_RESOLVE_METHOD_FIXED_ARITY |
|
|
CBM_RESOLVE_METHOD_OCCURRENCE_EXACT,
|
|
.classify = proof_classify};
|
|
cbm_resolve_method_result_t result = cbm_resolve_method(&query);
|
|
ASSERT(cbm_resolve_admit(&result.result));
|
|
ASSERT_NOT_NULL(result.result.proof);
|
|
const cbm_resolve_proof_view_t *view = cbm_resolve_proof_view(result.result.proof);
|
|
ASSERT_EQ(view->kind, CBM_PROOF_REGISTRY_LOOKUP);
|
|
ASSERT_STR_EQ(view->key.lookup_domain, "project.Widget");
|
|
ASSERT_STR_EQ(view->key.lookup_key, "run");
|
|
ASSERT_STR_EQ(view->key.declaration_identity, "project.Widget.run#visible");
|
|
ASSERT_EQ(view->premise_count, 5);
|
|
bool has_receiver = false, has_excluded = false;
|
|
for (size_t i = 0; i < view->premise_count; i++) {
|
|
const cbm_resolve_proof_t *support = view->premises[i];
|
|
has_receiver |= support == receiver.evidence.proof;
|
|
has_excluded |=
|
|
support->value.key.declaration_identity &&
|
|
strcmp(support->value.key.declaration_identity, "project.Widget.run#hidden") == 0;
|
|
}
|
|
ASSERT(has_receiver);
|
|
ASSERT(has_excluded);
|
|
ASSERT(cbm_resolve_types_invalidate(&map, scope, "value", 30, CBM_RESOLVE_TYPE_UNKNOWN));
|
|
result = cbm_resolve_method(&query);
|
|
ASSERT(!cbm_resolve_admit(&result.result));
|
|
ASSERT(result.result.proof == NULL);
|
|
cbm_arena_destroy(&arena);
|
|
PASS();
|
|
}
|
|
|
|
TEST(resolver_proof_generation_import_session_visits_shared_ancestors_once) {
|
|
CBMArena source_arena, destination_arena;
|
|
cbm_arena_init(&source_arena);
|
|
cbm_arena_init(&destination_arena);
|
|
cbm_resolve_proof_pool_t source, destination;
|
|
cbm_resolve_proof_pool_init(&source, &source_arena, 0, 0, 0);
|
|
cbm_resolve_proof_pool_init(&destination, &destination_arena, 0, 0, 0);
|
|
const cbm_resolve_proof_t *roots[64];
|
|
const cbm_resolve_proof_t *node =
|
|
proof_leaf(&source, "project.a", CBM_RESOLVE_ANALYSIS_COMPLETE);
|
|
for (unsigned i = 0; i < 64; i++) {
|
|
cbm_resolve_proof_key_t key = {
|
|
.lookup_domain = "copy", .lookup_key = "same-width", .start_byte = i};
|
|
cbm_resolve_result_t outcome = proof_outcome("project.Widget");
|
|
node = cbm_resolve_proof_derive(&source, CBM_PROOF_TYPE_COPY, NULL, &key, &outcome, &node,
|
|
1, true);
|
|
ASSERT_NOT_NULL(node);
|
|
roots[i] = node;
|
|
}
|
|
cbm_resolve_proof_clone_session_t session;
|
|
ASSERT(cbm_resolve_proof_clone_begin(&session, &destination));
|
|
const cbm_resolve_proof_t *last = cbm_resolve_proof_clone_root(&session, roots[63]);
|
|
ASSERT_NOT_NULL(last);
|
|
uint64_t initial_work = destination.work;
|
|
for (size_t i = 0; i < 64; i++)
|
|
ASSERT_NOT_NULL(cbm_resolve_proof_clone_root(&session, roots[i]));
|
|
ASSERT_EQ(destination.work - initial_work, 64);
|
|
ASSERT_EQ(destination.count, 65);
|
|
cbm_resolve_proof_clone_end(&session);
|
|
cbm_arena_destroy(&source_arena);
|
|
proof_order_t order = {0};
|
|
ASSERT(cbm_resolve_proof_visit(last, 65, proof_postorder, &order));
|
|
ASSERT_EQ(order.count, 65);
|
|
cbm_arena_destroy(&destination_arena);
|
|
PASS();
|
|
}
|
|
|
|
TEST(resolver_proof_multiple_roots_have_canonical_shared_postorder) {
|
|
CBMArena arena;
|
|
cbm_arena_init(&arena);
|
|
cbm_resolve_proof_pool_t pool;
|
|
cbm_resolve_proof_pool_init(&pool, &arena, 0, 0, 0);
|
|
const cbm_resolve_proof_t *a = proof_leaf(&pool, "project.a", CBM_RESOLVE_ANALYSIS_COMPLETE);
|
|
const cbm_resolve_proof_t *b = proof_leaf(&pool, "project.b", CBM_RESOLVE_ANALYSIS_COMPLETE);
|
|
const cbm_resolve_proof_t *inputs[] = {a, b};
|
|
const cbm_resolve_proof_t *joined = proof_join(&pool, inputs, 2, true);
|
|
const cbm_resolve_proof_t *roots[] = {a, joined, b, joined};
|
|
const cbm_resolve_proof_t *reversed[] = {joined, b, a};
|
|
proof_order_t first = {0}, second = {0};
|
|
ASSERT(cbm_resolve_proof_visit_roots(roots, 4, 3, proof_postorder, &first));
|
|
ASSERT(cbm_resolve_proof_visit_roots(reversed, 3, 3, proof_postorder, &second));
|
|
ASSERT_EQ(first.count, 3);
|
|
ASSERT_EQ(second.count, first.count);
|
|
for (size_t i = 0; i < first.count; i++)
|
|
ASSERT(first.nodes[i] == second.nodes[i]);
|
|
cbm_arena_destroy(&arena);
|
|
PASS();
|
|
}
|
|
|
|
TEST(resolver_proof_standalone_context_keeps_publication_identity_unknown) {
|
|
CBMArena arena;
|
|
cbm_arena_init(&arena);
|
|
CBMResolveTypeMap maps[4];
|
|
const char *sources[] = {"a\0b", "a\0b", "a\0c", "a\0b"};
|
|
for (size_t i = 0; i < 4; i++) {
|
|
cbm_resolve_types_init(&maps[i], &arena, 0);
|
|
CBMSourceOrigin origin = i == 3 ? CBM_SOURCE_ORIGIN_PREPROCESSED : CBM_SOURCE_ORIGIN_RAW;
|
|
ASSERT(cbm_lsp_proof_standalone_context(&maps[i], sources[i], 3, "project.main", origin));
|
|
ASSERT_NOT_NULL(maps[i].proof_source.content_identity);
|
|
ASSERT_EQ(strlen(maps[i].proof_source.content_identity), 64);
|
|
ASSERT_EQ(maps[i].proof_source.source_origin, (uint32_t)origin);
|
|
ASSERT(maps[i].proof_source.project == NULL);
|
|
ASSERT(maps[i].proof_source.file == NULL);
|
|
ASSERT(maps[i].proof_source.configuration_identity == NULL);
|
|
ASSERT_EQ(maps[i].proofs.count, 0);
|
|
}
|
|
ASSERT_STR_EQ(maps[0].proof_source.content_identity, maps[1].proof_source.content_identity);
|
|
ASSERT(strcmp(maps[0].proof_source.content_identity, maps[2].proof_source.content_identity));
|
|
ASSERT(strcmp(maps[0].proof_source.content_identity, maps[3].proof_source.content_identity));
|
|
cbm_resolve_proof_key_t key = {.lookup_domain = "project.main.run", .lookup_key = "Widget"};
|
|
cbm_resolve_result_t outcome = proof_outcome("project.Widget");
|
|
outcome.facts |= CBM_RESOLVE_FACT_OCCURRENCE_EXACT;
|
|
outcome.proof = cbm_resolve_proof_leaf(&maps[0].proofs, CBM_PROOF_SOURCE_FACT,
|
|
&maps[0].proof_source, &key, &outcome);
|
|
ASSERT_NOT_NULL(outcome.proof);
|
|
ASSERT_EQ(outcome.proof->value.outcome.analysis, CBM_RESOLVE_ANALYSIS_COMPLETE);
|
|
ASSERT(cbm_resolve_admit(&outcome));
|
|
cbm_resolve_record_t record = {.file_path = "main.cpp",
|
|
.scope = "project.main.run",
|
|
.relation = "USES_TYPE",
|
|
.spelling = "Widget",
|
|
.start_byte = 0,
|
|
.end_byte = 6,
|
|
.content_identity = maps[0].proof_source.content_identity,
|
|
.source_origin = CBM_SOURCE_ORIGIN_RAW,
|
|
.result = outcome};
|
|
ASSERT(!cbm_resolve_record_candidate(&record));
|
|
ASSERT(!cbm_resolve_record_admit(&record));
|
|
cbm_arena_destroy(&arena);
|
|
PASS();
|
|
}
|
|
|
|
TEST(resolver_proof_result_snapshot_replaces_standalone_before_donation) {
|
|
CBMArena arena;
|
|
cbm_arena_init(&arena);
|
|
CBMResolveTypeMap map;
|
|
cbm_resolve_types_init(&map, &arena, 0);
|
|
ASSERT(cbm_lsp_proof_standalone_context(&map, "Widget value;", 13, "project.main",
|
|
CBM_SOURCE_ORIGIN_RAW));
|
|
ASSERT_EQ(map.proofs.count, 0);
|
|
CBMFileResult file = {.module_qn = "project.main",
|
|
.source_project = "project",
|
|
.source_file = "main.cpp",
|
|
.source_content_id = {"captured-raw", "captured-expanded"},
|
|
.source_configuration_id = "captured-configuration"};
|
|
cbm_resolve_proof_source_t captured =
|
|
cbm_result_proof_source(&file, CBM_SOURCE_ORIGIN_PREPROCESSED, "project.main.run");
|
|
ASSERT(cbm_lsp_proof_context(&map, &captured, file.module_qn));
|
|
ASSERT_EQ(map.proofs.count, 0);
|
|
cbm_resolve_proof_key_t key = {.lookup_domain = "project.main.run", .lookup_key = "Widget"};
|
|
cbm_resolve_result_t outcome = proof_outcome("project.Widget");
|
|
const cbm_resolve_proof_t *leaf = cbm_resolve_proof_leaf(&map.proofs, CBM_PROOF_SOURCE_FACT,
|
|
&map.proof_source, &key, &outcome);
|
|
ASSERT_NOT_NULL(leaf);
|
|
ASSERT_EQ(leaf->value.outcome.analysis, CBM_RESOLVE_ANALYSIS_COMPLETE);
|
|
ASSERT_STR_EQ(leaf->value.source.project, "project");
|
|
ASSERT_STR_EQ(leaf->value.source.file, "main.cpp");
|
|
ASSERT_STR_EQ(leaf->value.source.scope, "project.main.run");
|
|
ASSERT_STR_EQ(leaf->value.source.content_identity, "captured-expanded");
|
|
ASSERT_STR_EQ(leaf->value.source.configuration_identity, "captured-configuration");
|
|
ASSERT_EQ(leaf->value.source.source_origin, CBM_SOURCE_ORIGIN_PREPROCESSED);
|
|
ASSERT_EQ(map.proofs.count, 1);
|
|
cbm_arena_destroy(&arena);
|
|
PASS();
|
|
}
|
|
|
|
SUITE(resolver_proof) {
|
|
RUN_TEST(resolver_proof_standalone_context_keeps_publication_identity_unknown);
|
|
RUN_TEST(resolver_proof_result_snapshot_replaces_standalone_before_donation);
|
|
RUN_TEST(resolver_proof_multiple_roots_have_canonical_shared_postorder);
|
|
RUN_TEST(resolver_proof_generation_import_session_visits_shared_ancestors_once);
|
|
RUN_TEST(resolver_proof_member_lookup_retains_receiver_domain_and_excluded_candidate);
|
|
RUN_TEST(resolver_proof_full_key_interning_and_collision_equality);
|
|
RUN_TEST(resolver_proof_closed_join_keeps_all_distinct_supports);
|
|
RUN_TEST(resolver_proof_clone_owns_dag_and_canonical_traversal);
|
|
RUN_TEST(resolver_proof_incomplete_predecessors_revoke_authority);
|
|
RUN_TEST(resolver_proof_limits_and_unknown_source_are_explicit);
|
|
RUN_TEST(resolver_proof_same_pool_chain_does_not_revisit_ancestors);
|
|
RUN_TEST(resolver_proof_named_copy_keeps_rule_and_dependency_without_admission_flags);
|
|
}
|