Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion Makefile.cbm
Original file line number Diff line number Diff line change
Expand Up @@ -385,6 +385,10 @@ TEST_SECURITY_SRCS = tests/test_security.c

TEST_YAML_SRCS = tests/test_yaml.c

TEST_SEMANTIC_SRCS = tests/test_semantic.c
TEST_AST_PROFILE_SRCS = tests/test_ast_profile.c
TEST_SLAB_ALLOC_SRCS = tests/test_slab_alloc.c

TEST_SIMHASH_SRCS = tests/test_simhash.c

TEST_STACK_OVERFLOW_SRCS = tests/test_stack_overflow.c
Expand Down Expand Up @@ -447,7 +451,7 @@ TEST_REPRO_SRCS = \
tests/repro/repro_lsp_java_cs.c \
tests/repro/repro_lsp_kt_php_rust.c

ALL_TEST_SRCS =$(TEST_FOUNDATION_SRCS) $(TEST_EXTRACTION_SRCS) $(TEST_STORE_SRCS) $(TEST_CYPHER_SRCS) $(TEST_MCP_SRCS) $(TEST_DISCOVER_SRCS) $(TEST_GRAPH_BUFFER_SRCS) $(TEST_PIPELINE_SRCS) $(TEST_WATCHER_SRCS) $(TEST_LZ4_SRCS) $(TEST_ZSTD_SRCS) $(TEST_ARTIFACT_SRCS) $(TEST_SQLITE_WRITER_SRCS) $(TEST_GO_LSP_SRCS) $(TEST_C_LSP_SRCS) $(TEST_PHP_LSP_SRCS) $(TEST_CS_LSP_SRCS) $(TEST_CS_LSP_BENCH_SRCS) $(TEST_SCOPE_SRCS) $(TEST_TYPE_REP_SRCS) $(TEST_PY_LSP_SRCS) $(TEST_PY_LSP_BENCH_SRCS) $(TEST_PY_LSP_STRESS_SRCS) $(TEST_PY_LSP_SCALE_SRCS) $(TEST_TS_LSP_SRCS) $(TEST_JAVA_LSP_SRCS) $(TEST_KOTLIN_LSP_SRCS) $(TEST_RUST_LSP_SRCS) $(TEST_TRACES_SRCS) $(TEST_CLI_SRCS) $(TEST_MEM_SRCS) $(TEST_UI_SRCS) $(TEST_HTTPD_SRCS) $(TEST_SECURITY_SRCS) $(TEST_YAML_SRCS) $(TEST_SIMHASH_SRCS) $(TEST_STACK_OVERFLOW_SRCS) $(TEST_INTEGRATION_SRCS)
ALL_TEST_SRCS =$(TEST_FOUNDATION_SRCS) $(TEST_EXTRACTION_SRCS) $(TEST_STORE_SRCS) $(TEST_CYPHER_SRCS) $(TEST_MCP_SRCS) $(TEST_DISCOVER_SRCS) $(TEST_GRAPH_BUFFER_SRCS) $(TEST_PIPELINE_SRCS) $(TEST_WATCHER_SRCS) $(TEST_LZ4_SRCS) $(TEST_ZSTD_SRCS) $(TEST_ARTIFACT_SRCS) $(TEST_SQLITE_WRITER_SRCS) $(TEST_GO_LSP_SRCS) $(TEST_C_LSP_SRCS) $(TEST_PHP_LSP_SRCS) $(TEST_CS_LSP_SRCS) $(TEST_CS_LSP_BENCH_SRCS) $(TEST_SCOPE_SRCS) $(TEST_TYPE_REP_SRCS) $(TEST_PY_LSP_SRCS) $(TEST_PY_LSP_BENCH_SRCS) $(TEST_PY_LSP_STRESS_SRCS) $(TEST_PY_LSP_SCALE_SRCS) $(TEST_TS_LSP_SRCS) $(TEST_JAVA_LSP_SRCS) $(TEST_KOTLIN_LSP_SRCS) $(TEST_RUST_LSP_SRCS) $(TEST_TRACES_SRCS) $(TEST_CLI_SRCS) $(TEST_MEM_SRCS) $(TEST_UI_SRCS) $(TEST_HTTPD_SRCS) $(TEST_SECURITY_SRCS) $(TEST_YAML_SRCS) $(TEST_SEMANTIC_SRCS) $(TEST_AST_PROFILE_SRCS) $(TEST_SLAB_ALLOC_SRCS) $(TEST_SIMHASH_SRCS) $(TEST_STACK_OVERFLOW_SRCS) $(TEST_INTEGRATION_SRCS)


# ── Build directories ────────────────────────────────────────────
Expand Down
177 changes: 177 additions & 0 deletions tests/test_ast_profile.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,177 @@
/*
* test_ast_profile.c — Unit tests for ast_profile.c (serialization/deserialization).
*
* Covers: to_str, from_str round-trip, to_vector, edge cases.
*/
#include "test_framework.h"
#include <semantic/ast_profile.h>

#include <string.h>

/* ── Helper ──────────────────────────────────────────────────────── */

static cbm_ast_profile_t make_profile(void) {
cbm_ast_profile_t p;
memset(&p, 0, sizeof(p));
p.if_count = 5;
p.for_count = 3;
p.while_count = 1;
p.switch_count = 2;
p.try_count = 0;
p.return_count = 4;
p.max_nesting_depth = 3;
p.avg_nesting_depth_x10 = 15;
p.comparison_ops = 10;
p.arithmetic_ops = 8;
p.logical_ops = 2;
p.assignment_count = 7;
p.string_literals = 3;
p.number_literals = 5;
p.bool_literals = 1;
p.param_count = 4;
p.params_in_returns = 2;
p.params_in_conditions = 1;
p.variable_reassigns = 3;
p.unique_operators = 12;
p.unique_operands = 20;
p.total_operators = 45;
p.total_operands = 60;
p.body_lines = 30;
p.body_tokens = 200;
return p;
}

/* ── to_str + from_str round-trip ────────────────────────────────── */

TEST(ast_profile_roundtrip) {
cbm_ast_profile_t original = make_profile();
char buf[200];
cbm_ast_profile_to_str(&original, buf, sizeof(buf));

cbm_ast_profile_t decoded;
ASSERT_TRUE(cbm_ast_profile_from_str(buf, &decoded));

ASSERT_EQ(original.if_count, decoded.if_count);
ASSERT_EQ(original.for_count, decoded.for_count);
ASSERT_EQ(original.while_count, decoded.while_count);
ASSERT_EQ(original.switch_count, decoded.switch_count);
ASSERT_EQ(original.try_count, decoded.try_count);
ASSERT_EQ(original.return_count, decoded.return_count);
ASSERT_EQ(original.max_nesting_depth, decoded.max_nesting_depth);
ASSERT_EQ(original.avg_nesting_depth_x10, decoded.avg_nesting_depth_x10);
ASSERT_EQ(original.comparison_ops, decoded.comparison_ops);
ASSERT_EQ(original.arithmetic_ops, decoded.arithmetic_ops);
ASSERT_EQ(original.logical_ops, decoded.logical_ops);
ASSERT_EQ(original.assignment_count, decoded.assignment_count);
ASSERT_EQ(original.string_literals, decoded.string_literals);
ASSERT_EQ(original.number_literals, decoded.number_literals);
ASSERT_EQ(original.bool_literals, decoded.bool_literals);
ASSERT_EQ(original.param_count, decoded.param_count);
ASSERT_EQ(original.params_in_returns, decoded.params_in_returns);
ASSERT_EQ(original.params_in_conditions, decoded.params_in_conditions);
ASSERT_EQ(original.variable_reassigns, decoded.variable_reassigns);
ASSERT_EQ(original.unique_operators, decoded.unique_operators);
ASSERT_EQ(original.unique_operands, decoded.unique_operands);
ASSERT_EQ(original.total_operators, decoded.total_operators);
ASSERT_EQ(original.total_operands, decoded.total_operands);
ASSERT_EQ(original.body_lines, decoded.body_lines);
ASSERT_EQ(original.body_tokens, decoded.body_tokens);
PASS();
}

TEST(ast_profile_to_str_null) {
char buf[200];
cbm_ast_profile_to_str(NULL, buf, sizeof(buf)); /* should not crash */
PASS();
}

TEST(ast_profile_to_str_small_buf) {
cbm_ast_profile_t p = make_profile();
/* buf too small: should write empty string */
char buf[1] = {'X'};
cbm_ast_profile_to_str(&p, buf, 0);
/* 0-length buffer: function should handle gracefully */
PASS();
}

TEST(ast_profile_from_str_null) {
ASSERT_FALSE(cbm_ast_profile_from_str(NULL, NULL));
PASS();
}

TEST(ast_profile_from_str_invalid) {
cbm_ast_profile_t out;
ASSERT_FALSE(cbm_ast_profile_from_str("not,a,valid,string", &out));
ASSERT_FALSE(cbm_ast_profile_from_str("", &out));
PASS();
}

TEST(ast_profile_from_str_too_few_fields) {
cbm_ast_profile_t out;
/* Only 5 fields instead of 25 */
ASSERT_FALSE(cbm_ast_profile_from_str("1,2,3,4,5", &out));
PASS();
}

/* ── to_vector ───────────────────────────────────────────────────── */

TEST(ast_profile_to_vector_range) {
cbm_ast_profile_t p = make_profile();
float vec[25];
cbm_ast_profile_to_vector(&p, vec);
/* All values should be in [0, 1] range (normalized) */
for (int i = 0; i < 25; i++) {
ASSERT_GTE(vec[i], 0.0f);
ASSERT_LTE(vec[i], 1.0f);
}
PASS();
}

TEST(ast_profile_to_vector_zero) {
cbm_ast_profile_t p;
memset(&p, 0, sizeof(p));
float vec[25];
cbm_ast_profile_to_vector(&p, vec);
/* All zeros should produce all-zero vector */
for (int i = 0; i < 25; i++) {
ASSERT_FLOAT_EQ(vec[i], 0.0f, 0.001f);
}
PASS();
}

TEST(ast_profile_to_vector_null) {
float vec[25];
memset(vec, 0xFF, sizeof(vec));
cbm_ast_profile_to_vector(NULL, vec); /* should not crash or write */
PASS();
}

/* ── to_vector extremes (saturation) ─────────────────────────────── */

TEST(ast_profile_to_vector_saturates) {
cbm_ast_profile_t p;
memset(&p, 0, sizeof(p));
/* count / MAX_COUNT (100) → can exceed 1.0; body_tokens / MAX_TOKENS (2000) = 2.5 */
p.if_count = 500;
p.body_tokens = 5000;
float vec[25];
cbm_ast_profile_to_vector(&p, vec);
ASSERT_TRUE(vec[0] > 1.0f);
ASSERT_TRUE(vec[24] > 1.0f);
PASS();
}

/* ── Suite ───────────────────────────────────────────────────────── */

SUITE(ast_profile) {
RUN_TEST(ast_profile_roundtrip);
RUN_TEST(ast_profile_to_str_null);
RUN_TEST(ast_profile_to_str_small_buf);
RUN_TEST(ast_profile_from_str_null);
RUN_TEST(ast_profile_from_str_invalid);
RUN_TEST(ast_profile_from_str_too_few_fields);
RUN_TEST(ast_profile_to_vector_range);
RUN_TEST(ast_profile_to_vector_zero);
RUN_TEST(ast_profile_to_vector_null);
RUN_TEST(ast_profile_to_vector_saturates);
}
6 changes: 6 additions & 0 deletions tests/test_main.c
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,9 @@ extern void suite_grammar_probe_e(void);
extern void suite_grammar_probe_f(void);
extern void suite_grammar_probe_g(void);
extern void suite_incremental(void);
extern void suite_semantic(void);
extern void suite_ast_profile(void);
extern void suite_slab_alloc(void);
extern void suite_simhash(void);
extern void suite_stack_overflow(void);
extern void suite_dump_verify(void);
Expand Down Expand Up @@ -239,6 +242,7 @@ int main(int argc, char **argv) {
RUN_SELECTED_SUITE(parallel);

/* mem + arena + slab integration */
RUN_SELECTED_SUITE(slab_alloc);
RUN_SELECTED_SUITE(mem);

/* UI (config, embedded assets, layout) */
Expand All @@ -254,6 +258,8 @@ int main(int argc, char **argv) {
RUN_SELECTED_SUITE(yaml);

/* SimHash / SIMILAR_TO */
RUN_SELECTED_SUITE(semantic);
RUN_SELECTED_SUITE(ast_profile);
RUN_SELECTED_SUITE(simhash);

/* Stack overflow regression (GitHub #199) */
Expand Down
Loading
Loading