Files
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

3874 lines
146 KiB
C

/*
* test_extraction.c — Regression tests for the extraction module.
*
* Port of internal/cbm/regression_test.go (1282 LOC, ~80 test cases).
* Exercises cbm_extract_file() on code snippets across 30+ languages,
* verifying definitions, calls, and imports are correctly extracted.
*/
#include "test_framework.h"
#include "cbm.h"
#include "../src/foundation/compat.h" /* cbm_clock_gettime (wide-flat scaling guard) */
#include <time.h>
/* ── Helpers ───────────────────────────────────────────────────── */
/* Check if any definition with the given label has the given name. */
static int has_def(CBMFileResult *r, const char *label, const char *name) {
for (int i = 0; i < r->defs.count; i++) {
if (strcmp(r->defs.items[i].label, label) == 0 && strcmp(r->defs.items[i].name, name) == 0)
return 1;
}
return 0;
}
/* Check if any definition has the given name (any label). */
static int has_def_any(CBMFileResult *r, const char *name) {
for (int i = 0; i < r->defs.count; i++) {
if (strcmp(r->defs.items[i].name, name) == 0)
return 1;
}
return 0;
}
/* Check if any call to the given callee exists. */
static int has_call(CBMFileResult *r, const char *callee) {
for (int i = 0; i < r->calls.count; i++) {
if (strstr(r->calls.items[i].callee_name, callee) != NULL)
return 1;
}
return 0;
}
/* Check if any import with the given module path exists. */
static int __attribute__((unused)) has_import(CBMFileResult *r, const char *path_substr) {
for (int i = 0; i < r->imports.count; i++) {
if (r->imports.items[i].module_path &&
strstr(r->imports.items[i].module_path, path_substr) != NULL)
return 1;
}
return 0;
}
/* Count definitions with a given label. */
static int count_defs_with_label(CBMFileResult *r, const char *label) {
int count = 0;
for (int i = 0; i < r->defs.count; i++) {
if (strcmp(r->defs.items[i].label, label) == 0)
count++;
}
return count;
}
/* Convenience: extract, assert no error, return result. Caller frees. */
static CBMFileResult *extract(const char *src, CBMLanguage lang, const char *proj,
const char *path) {
CBMFileResult *r = cbm_extract_file(src, (int)strlen(src), lang, proj, path, 0, NULL, NULL);
return r;
}
/* ═══════════════════════════════════════════════════════════════════
* Group A: OOP Languages
* ═══════════════════════════════════════════════════════════════════ */
/* --- R: box::use imports (#218) + module$fn calls (#219) --- */
TEST(extract_r_box_use_imports_issue218) {
CBMFileResult *r = extract("box::use(\n"
" shiny[moduleServer, NS],\n"
" app/logic/validation[validate_input],\n"
")\n"
"library(dplyr)\n"
"source(\"helpers.R\")\n",
CBM_LANG_R, "t", "app.R");
ASSERT_NOT_NULL(r);
ASSERT_FALSE(r->has_error);
/* box::use specs → one IMPORTS edge per module (symbol list stripped). */
ASSERT(has_import(r, "shiny"));
ASSERT(has_import(r, "app/logic/validation"));
/* base-R imports work too. */
ASSERT(has_import(r, "dplyr"));
ASSERT(has_import(r, "helpers"));
cbm_free_result(r);
PASS();
}
TEST(extract_r_dollar_call_issue219) {
CBMFileResult *r = extract("validation$validate_input(x)\n", CBM_LANG_R, "t", "app.R");
ASSERT_NOT_NULL(r);
ASSERT_FALSE(r->has_error);
/* module$fn() now produces a CALLS edge (was silently dropped). */
ASSERT(has_call(r, "validation.validate_input"));
cbm_free_result(r);
PASS();
}
/* --- TS: object-literal arrow methods from a factory (Zustand, #341) --- */
TEST(extract_ts_factory_object_methods_issue341) {
CBMFileResult *r = extract("export function createItemActions(set, get) {\n"
" return {\n"
" addItem: (type, id) => { return 1; },\n"
" moveItem: (id, target) => { return 2; },\n"
" deleteItem: (id) => { return 3; },\n"
" };\n"
"}\n",
CBM_LANG_TYPESCRIPT, "t", "item-actions.ts");
ASSERT_NOT_NULL(r);
ASSERT_FALSE(r->has_error);
/* The factory itself + each returned arrow method are Function nodes. */
ASSERT(has_def_any(r, "createItemActions"));
ASSERT(has_def_any(r, "addItem"));
ASSERT(has_def_any(r, "moveItem"));
ASSERT(has_def_any(r, "deleteItem"));
cbm_free_result(r);
PASS();
}
/* --- C/C++ preprocessor macros become Macro nodes (#375) --- */
TEST(extract_c_macros_issue375) {
CBMFileResult *r = extract("#define SIMPLE_MACRO 1\n"
"#define FN_MACRO(x) (2 * (x))\n"
"#define EMPTY_MACRO\n"
"int main(void) { return FN_MACRO(SIMPLE_MACRO); }\n",
CBM_LANG_C, "p", "macros.c");
ASSERT_NOT_NULL(r);
ASSERT_FALSE(r->has_error);
ASSERT(has_def(r, "Macro", "SIMPLE_MACRO"));
ASSERT(has_def(r, "Macro", "FN_MACRO"));
ASSERT(has_def(r, "Macro", "EMPTY_MACRO"));
ASSERT(has_def(r, "Function", "main")); /* macros don't displace function defs */
cbm_free_result(r);
PASS();
}
TEST(extract_cpp_macros_issue375) {
CBMFileResult *r = extract("#define MAX(a, b) ((a) > (b) ? (a) : (b))\n"
"#define PI 3.14159\n"
"namespace n {\n"
"int f() { return MAX(1, 2); }\n"
"}\n",
CBM_LANG_CPP, "p", "macros.cpp");
ASSERT_NOT_NULL(r);
ASSERT_FALSE(r->has_error);
ASSERT(has_def(r, "Macro", "MAX"));
ASSERT(has_def(r, "Macro", "PI"));
cbm_free_result(r);
PASS();
}
/* --- GDScript: AST -> graph visitor (Godot, #186) --- */
TEST(extract_gdscript_issue186) {
CBMFileResult *r = extract("extends Node\n"
"class_name Player\n"
"\n"
"var health = 100\n"
"\n"
"func _ready():\n"
" take_damage(10)\n"
"\n"
"func take_damage(amount):\n"
" health -= amount\n"
"\n"
"class Inner:\n"
" func helper():\n"
" pass\n",
CBM_LANG_GDSCRIPT, "game", "player.gd");
ASSERT_NOT_NULL(r);
ASSERT_FALSE(r->has_error);
ASSERT(has_def(r, "Function", "_ready"));
ASSERT(has_def(r, "Function", "take_damage"));
ASSERT(has_def(r, "Class", "Inner"));
ASSERT(has_call(r, "take_damage"));
cbm_free_result(r);
PASS();
}
/* --- PowerShell: AST -> graph visitor (#35) --- */
TEST(extract_powershell_issue35) {
CBMFileResult *r = extract("function Get-Greeting {\n"
" param($Name)\n"
" Write-Output \"Hello $Name\"\n"
"}\n"
"\n"
"function Set-Config {\n"
" Get-Greeting -Name 'World'\n"
"}\n",
CBM_LANG_POWERSHELL, "ops", "greet.ps1");
ASSERT_NOT_NULL(r);
ASSERT_FALSE(r->has_error);
ASSERT(count_defs_with_label(r, "Function") >= 2);
cbm_free_result(r);
PASS();
}
/* --- Luau: AST -> graph visitor (Roblox, #39) --- */
TEST(extract_luau_issue39) {
CBMFileResult *r = extract("local function add(a, b)\n"
" return a + b\n"
"end\n"
"\n"
"function multiply(a, b)\n"
" return add(a, a) * b\n"
"end\n",
CBM_LANG_LUAU, "game", "math.luau");
ASSERT_NOT_NULL(r);
ASSERT_FALSE(r->has_error);
ASSERT(count_defs_with_label(r, "Function") >= 2);
cbm_free_result(r);
PASS();
}
/* --- QML: AST -> graph visitor (Qt, #42) --- */
TEST(extract_qml_issue42) {
CBMFileResult *r = extract("import QtQuick 2.15\n"
"\n"
"Rectangle {\n"
" id: root\n"
" width: 100\n"
" property int counter: 0\n"
" signal clicked(int value)\n"
"\n"
" function increment() {\n"
" counter += 1\n"
" compute(counter)\n"
" }\n"
"\n"
" function compute(n) {\n"
" return n * 2\n"
" }\n"
"}\n",
CBM_LANG_QML, "app", "Main.qml");
ASSERT_NOT_NULL(r);
ASSERT_FALSE(r->has_error);
ASSERT(has_def(r, "Function", "increment"));
ASSERT(has_def(r, "Function", "compute"));
ASSERT(has_call(r, "compute"));
cbm_free_result(r);
PASS();
}
/* --- CFML script dialect: .cfc components (Lucee/ColdFusion, #38) --- */
TEST(extract_cfscript_issue38) {
CBMFileResult *r = extract("component {\n"
" public function getUser(numeric id) {\n"
" return loadUser(id);\n"
" }\n"
" function loadUser(id) {\n"
" return id * 2;\n"
" }\n"
"}\n",
CBM_LANG_CFSCRIPT, "app", "User.cfc");
ASSERT_NOT_NULL(r);
ASSERT_FALSE(r->has_error);
ASSERT(count_defs_with_label(r, "Function") >= 2);
ASSERT(has_def(r, "Function", "getUser"));
ASSERT(has_def(r, "Function", "loadUser"));
ASSERT(has_call(r, "loadUser"));
cbm_free_result(r);
PASS();
}
/* --- CFML tag dialect: .cfm templates with <cffunction> (#38) --- */
TEST(extract_cfml_tag_issue38) {
CBMFileResult *r = extract("<cffunction name=\"greet\" returntype=\"string\">\n"
" <cfargument name=\"who\" type=\"string\">\n"
" <cfreturn \"Hello \" & arguments.who>\n"
"</cffunction>\n",
CBM_LANG_CFML, "app", "index.cfm");
ASSERT_NOT_NULL(r);
ASSERT(has_def(r, "Function", "greet"));
cbm_free_result(r);
PASS();
}
/* --- Helm / Go template: named templates + include calls (#338) --- */
TEST(extract_helm_templates_issue338) {
CBMFileResult *r = extract("{{- define \"chart.fullname\" -}}\n"
"{{- .Release.Name -}}\n"
"{{- end -}}\n"
"\n"
"{{- define \"chart.labels\" -}}\n"
"app: {{ include \"chart.fullname\" . }}\n"
"chart: {{ template \"chart.fullname\" . }}\n"
"{{- end -}}\n",
CBM_LANG_GOTEMPLATE, "chart", "templates/_helpers.tpl");
ASSERT_NOT_NULL(r);
ASSERT_FALSE(r->has_error);
/* define -> Function nodes */
ASSERT(has_def(r, "Function", "chart.fullname"));
ASSERT(has_def(r, "Function", "chart.labels"));
/* include / template -> CALLS to the named template (not to "include") */
ASSERT(has_call(r, "chart.fullname"));
cbm_free_result(r);
PASS();
}
/* --- Helm values.yaml: top-level keys only, no leaf flood (#338) --- */
TEST(extract_helm_values_toplevel_issue338) {
CBMFileResult *r = extract("image:\n"
" repository: nginx\n"
" tag: latest\n"
"replicaCount: 3\n"
"service:\n"
" port: 80\n",
CBM_LANG_YAML, "chart", "values.yaml");
ASSERT_NOT_NULL(r);
ASSERT_FALSE(r->has_error);
ASSERT(has_def(r, "Variable", "image"));
ASSERT(has_def(r, "Variable", "replicaCount"));
ASSERT(has_def(r, "Variable", "service"));
/* Nested leaf keys must NOT explode into separate nodes. */
ASSERT(!has_def(r, "Variable", "repository"));
ASSERT(!has_def(r, "Variable", "tag"));
ASSERT(!has_def(r, "Variable", "port"));
ASSERT_EQ(count_defs_with_label(r, "Variable"), 3);
cbm_free_result(r);
PASS();
}
/* --- Java --- */
TEST(java_class) {
CBMFileResult *r = extract(
"public class Animal { private String name; public String getName() { return name; } }",
CBM_LANG_JAVA, "t", "Animal.java");
ASSERT_NOT_NULL(r);
ASSERT_FALSE(r->has_error);
ASSERT(has_def(r, "Class", "Animal"));
cbm_free_result(r);
PASS();
}
TEST(java_method) {
CBMFileResult *r = extract(
"public class Svc { public void doWork() {} public int compute(int x) { return x; } }",
CBM_LANG_JAVA, "t", "Svc.java");
ASSERT_NOT_NULL(r);
ASSERT_FALSE(r->has_error);
ASSERT(has_def(r, "Method", "doWork"));
ASSERT(has_def(r, "Method", "compute"));
cbm_free_result(r);
PASS();
}
TEST(java_interface) {
CBMFileResult *r =
extract("public interface Repository { void save(Object o); Object findById(long id); }",
CBM_LANG_JAVA, "t", "Repo.java");
ASSERT_NOT_NULL(r);
ASSERT_FALSE(r->has_error);
ASSERT(has_def_any(r, "Repository"));
cbm_free_result(r);
PASS();
}
/* Regression for #279: a Java class declaring both `extends` and
* `implements` must produce one INHERITS edge per base — the extends parent
* AND every implements interface — with bare type names (not the keyword
* text "extends Bar" / "implements Baz, Qux"). Before the fix:
* 1) the field loop returned on the first match → only the superclass
* was emitted, the interfaces were dropped.
* 2) the emitted name was the full field text including the keyword. */
TEST(java_class_extends_and_implements) {
CBMFileResult *r = extract("public class DefaultLinkTool extends DefaultDiagramTool implements "
"ILinkTool, Closeable { }",
CBM_LANG_JAVA, "t", "DefaultLinkTool.java");
ASSERT_NOT_NULL(r);
ASSERT_FALSE(r->has_error);
/* Find the class def and inspect its base_classes list. */
CBMDefinition *cls = NULL;
for (int i = 0; i < r->defs.count; i++) {
if (strcmp(r->defs.items[i].label, "Class") == 0 &&
strcmp(r->defs.items[i].name, "DefaultLinkTool") == 0) {
cls = &r->defs.items[i];
break;
}
}
ASSERT_NOT_NULL(cls);
ASSERT_NOT_NULL(cls->base_classes);
bool saw_super = false;
bool saw_iface_a = false;
bool saw_iface_b = false;
for (const char **b = cls->base_classes; *b; b++) {
/* The keyword-text bug would surface as "extends ..." or
* "implements ..." literally inside one of the entries. */
ASSERT_NULL(strstr(*b, "extends"));
ASSERT_NULL(strstr(*b, "implements"));
if (strcmp(*b, "DefaultDiagramTool") == 0)
saw_super = true;
if (strcmp(*b, "ILinkTool") == 0)
saw_iface_a = true;
if (strcmp(*b, "Closeable") == 0)
saw_iface_b = true;
}
ASSERT_TRUE(saw_super);
ASSERT_TRUE(saw_iface_a);
ASSERT_TRUE(saw_iface_b);
cbm_free_result(r);
PASS();
}
/* REPRODUCTION (RED until fixed) — Python `class Animal(Base):` must extract the
* BARE base name "Base", but extract_base_classes captures the whole
* `superclasses` argument_list text "(Base)" instead: collect_bases_from_field
* (internal/cbm/extract_defs.c) matches only type_identifier / generic_type /
* qualified_name / scoped_type_identifier / user_type, while tree-sitter-python
* uses a plain `identifier` node for the base — so no child matches and the
* raw-field fallback grabs the argument_list text "(Base)" (parens included).
* DOWNSTREAM SYMPTOM: that malformed name never resolves to the Base class node,
* so EVERY Python subclass yields ZERO INHERITS edges (observed in the P6
* graph-contract suite). Fix: have collect_bases_from_field accept `identifier`
* (or strip the argument_list parens). This test stays RED as the regression
* guard / reproduction until the fix lands — see CLAUDE.md "Bug Fixing —
* Reproduce-First". */
TEST(python_class_base_extracted_bare) {
CBMFileResult *r = extract("class Base:\n pass\n\n\nclass Animal(Base):\n pass\n",
CBM_LANG_PYTHON, "t", "models.py");
ASSERT_NOT_NULL(r);
CBMDefinition *cls = NULL;
for (int i = 0; i < r->defs.count; i++) {
if (r->defs.items[i].label && strcmp(r->defs.items[i].label, "Class") == 0 &&
r->defs.items[i].name && strcmp(r->defs.items[i].name, "Animal") == 0) {
cls = &r->defs.items[i];
break;
}
}
ASSERT_NOT_NULL(cls);
ASSERT_NOT_NULL(cls->base_classes); /* a subclass must record at least one base */
bool saw_bare_base = false;
for (const char **b = cls->base_classes; *b; b++) {
if (strcmp(*b, "Base") == 0) {
saw_bare_base = true;
}
}
/* CURRENTLY FAILS: base_classes holds "(Base)" (argument_list text), not the
* bare "Base" needed for INHERITS resolution. */
ASSERT_TRUE(saw_bare_base);
cbm_free_result(r);
PASS();
}
/* --- PHP --- */
TEST(php_class) {
CBMFileResult *r = extract("<?php\nclass User { public string $name; public function "
"getName(): string { return $this->name; } }",
CBM_LANG_PHP, "t", "User.php");
ASSERT_NOT_NULL(r);
ASSERT_FALSE(r->has_error);
ASSERT(has_def(r, "Class", "User"));
ASSERT(has_def(r, "Method", "getName"));
cbm_free_result(r);
PASS();
}
TEST(php_function) {
CBMFileResult *r =
extract("<?php\nfunction greet(string $name): string { return 'Hello ' . $name; }",
CBM_LANG_PHP, "t", "helpers.php");
ASSERT_NOT_NULL(r);
ASSERT_FALSE(r->has_error);
ASSERT(has_def(r, "Function", "greet"));
cbm_free_result(r);
PASS();
}
/* --- Ruby --- */
TEST(ruby_class) {
CBMFileResult *r = extract("class Animal\n def initialize(name)\n @name = name\n end\n "
"def speak\n puts @name\n end\nend\n",
CBM_LANG_RUBY, "t", "animal.rb");
ASSERT_NOT_NULL(r);
ASSERT_FALSE(r->has_error);
ASSERT(has_def(r, "Class", "Animal"));
ASSERT(has_def(r, "Method", "speak"));
cbm_free_result(r);
PASS();
}
TEST(ruby_module) {
CBMFileResult *r = extract("module Greetable\n def greet\n \"Hello\"\n end\nend\n",
CBM_LANG_RUBY, "t", "greetable.rb");
ASSERT_NOT_NULL(r);
ASSERT_FALSE(r->has_error);
ASSERT(has_def_any(r, "Greetable"));
cbm_free_result(r);
PASS();
}
/* --- C# --- */
TEST(csharp_class) {
CBMFileResult *r = extract("namespace App { public class Service { public void Run() {} public "
"int Compute(int x) => x * 2; } }",
CBM_LANG_CSHARP, "t", "Service.cs");
ASSERT_NOT_NULL(r);
ASSERT_FALSE(r->has_error);
ASSERT(has_def(r, "Class", "Service"));
ASSERT(has_def(r, "Method", "Run"));
cbm_free_result(r);
PASS();
}
TEST(csharp_interface) {
CBMFileResult *r = extract("public interface IService { void Execute(); string GetStatus(); }",
CBM_LANG_CSHARP, "t", "IService.cs");
ASSERT_NOT_NULL(r);
ASSERT_FALSE(r->has_error);
ASSERT(has_def_any(r, "IService"));
cbm_free_result(r);
PASS();
}
/* --- Swift --- */
TEST(swift_class) {
CBMFileResult *r = extract("class Vehicle {\n var speed: Int = 0\n func accelerate() { "
"speed += 10 }\n func stop() { speed = 0 }\n}\n",
CBM_LANG_SWIFT, "t", "Vehicle.swift");
ASSERT_NOT_NULL(r);
ASSERT_FALSE(r->has_error);
ASSERT(has_def(r, "Class", "Vehicle"));
ASSERT(has_def(r, "Method", "accelerate"));
cbm_free_result(r);
PASS();
}
/* --- Kotlin --- */
TEST(kotlin_function) {
CBMFileResult *r = extract("fun greet(name: String): String = \"Hello $name\"\nfun main() { "
"println(greet(\"World\")) }\n",
CBM_LANG_KOTLIN, "t", "main.kt");
ASSERT_NOT_NULL(r);
ASSERT_FALSE(r->has_error);
ASSERT(has_def(r, "Function", "greet"));
ASSERT(has_def(r, "Function", "main"));
cbm_free_result(r);
PASS();
}
TEST(kotlin_class) {
CBMFileResult *r =
extract("class User(val name: String) {\n fun display(): String = \"User: $name\"\n}\n",
CBM_LANG_KOTLIN, "t", "User.kt");
ASSERT_NOT_NULL(r);
ASSERT_FALSE(r->has_error);
ASSERT(has_def(r, "Class", "User"));
cbm_free_result(r);
PASS();
}
/* --- Scala --- */
TEST(scala_function) {
CBMFileResult *r =
extract("object Main {\n def greet(name: String): String = s\"Hello $name\"\n def "
"main(args: Array[String]): Unit = println(greet(\"World\"))\n}\n",
CBM_LANG_SCALA, "t", "Main.scala");
ASSERT_NOT_NULL(r);
ASSERT_FALSE(r->has_error);
ASSERT(has_def(r, "Method", "greet"));
cbm_free_result(r);
PASS();
}
TEST(scala_class) {
CBMFileResult *r =
extract("class Animal(val name: String) {\n def speak(): String = s\"I am $name\"\n}\n",
CBM_LANG_SCALA, "t", "Animal.scala");
ASSERT_NOT_NULL(r);
ASSERT_FALSE(r->has_error);
ASSERT(has_def(r, "Class", "Animal"));
cbm_free_result(r);
PASS();
}
/* --- Dart --- */
TEST(dart_class) {
CBMFileResult *r = extract("class Animal {\n String name;\n Animal(this.name);\n String "
"speak() => 'I am $name';\n}\n",
CBM_LANG_DART, "t", "animal.dart");
ASSERT_NOT_NULL(r);
ASSERT_FALSE(r->has_error);
ASSERT(has_def(r, "Class", "Animal"));
ASSERT(has_def(r, "Method", "speak"));
cbm_free_result(r);
PASS();
}
/* --- Groovy --- */
TEST(groovy_class) {
CBMFileResult *r =
extract("class Greeter {\n String name\n String greet() { \"Hello, $name\" }\n "
"static void main(args) { println new Greeter(name:'World').greet() }\n}\n",
CBM_LANG_GROOVY, "t", "Greeter.groovy");
ASSERT_NOT_NULL(r);
ASSERT_FALSE(r->has_error);
ASSERT(has_def(r, "Class", "Greeter"));
ASSERT(has_def(r, "Method", "greet"));
cbm_free_result(r);
PASS();
}
/* ═══════════════════════════════════════════════════════════════════
* Group B: Systems Languages
* ═══════════════════════════════════════════════════════════════════ */
/* --- Rust --- */
TEST(rust_function) {
CBMFileResult *r =
extract("fn main() { println!(\"Hello\"); }\npub fn add(a: i32, b: i32) -> i32 { a + b }\n",
CBM_LANG_RUST, "t", "main.rs");
ASSERT_NOT_NULL(r);
ASSERT_FALSE(r->has_error);
ASSERT(has_def(r, "Function", "main"));
ASSERT(has_def(r, "Function", "add"));
cbm_free_result(r);
PASS();
}
TEST(rust_struct) {
CBMFileResult *r = extract("pub struct Point { pub x: f64, pub y: f64 }\nimpl Point { pub fn "
"new(x: f64, y: f64) -> Self { Point { x, y } } }\n",
CBM_LANG_RUST, "t", "point.rs");
ASSERT_NOT_NULL(r);
ASSERT_FALSE(r->has_error);
ASSERT(has_def(r, "Struct", "Point"));
ASSERT(has_def(r, "Method", "new"));
cbm_free_result(r);
PASS();
}
/* --- Go --- */
TEST(go_function) {
CBMFileResult *r = extract("package main\nfunc Greet(name string) string { return \"Hello, \" "
"+ name }\nfunc main() { Greet(\"World\") }\n",
CBM_LANG_GO, "t", "main.go");
ASSERT_NOT_NULL(r);
ASSERT_FALSE(r->has_error);
ASSERT(has_def(r, "Function", "Greet"));
ASSERT(has_def(r, "Function", "main"));
cbm_free_result(r);
PASS();
}
TEST(go_struct) {
CBMFileResult *r = extract("package main\ntype Server struct { Host string; Port int }\nfunc "
"(s *Server) Start() error { return nil }\n",
CBM_LANG_GO, "t", "server.go");
ASSERT_NOT_NULL(r);
ASSERT_FALSE(r->has_error);
ASSERT(has_def(r, "Struct", "Server"));
ASSERT(has_def(r, "Method", "Start"));
cbm_free_result(r);
PASS();
}
TEST(go_interface) {
CBMFileResult *r =
extract("package main\ntype Handler interface { ServeHTTP() error; Close() }\n",
CBM_LANG_GO, "t", "handler.go");
ASSERT_NOT_NULL(r);
ASSERT_FALSE(r->has_error);
ASSERT(has_def_any(r, "Handler"));
cbm_free_result(r);
PASS();
}
/* --- Zig --- */
TEST(zig_function) {
CBMFileResult *r =
extract("const std = @import(\"std\");\npub fn add(a: i32, b: i32) i32 { return a + b; }\n",
CBM_LANG_ZIG, "t", "main.zig");
ASSERT_NOT_NULL(r);
ASSERT_FALSE(r->has_error);
ASSERT(has_def(r, "Function", "add"));
cbm_free_result(r);
PASS();
}
/* --- C --- */
TEST(c_function) {
CBMFileResult *r =
extract("int add(int a, int b) { return a + b; }\nvoid greet() { printf(\"Hello\"); }\n",
CBM_LANG_C, "t", "math.c");
ASSERT_NOT_NULL(r);
ASSERT_FALSE(r->has_error);
ASSERT(has_def(r, "Function", "add"));
ASSERT(has_def(r, "Function", "greet"));
cbm_free_result(r);
PASS();
}
TEST(c_struct) {
CBMFileResult *r =
extract("struct Point { int x; int y; };\nvoid init_point(struct Point *p) { p->x = 0; }\n",
CBM_LANG_C, "t", "point.c");
ASSERT_NOT_NULL(r);
ASSERT_FALSE(r->has_error);
ASSERT(has_def(r, "Function", "init_point"));
cbm_free_result(r);
PASS();
}
/* --- C++ --- */
TEST(cpp_class) {
CBMFileResult *r = extract(
"class Widget {\npublic:\n void draw() {}\n int width() const { return 0; }\n};\n",
CBM_LANG_CPP, "t", "widget.cpp");
ASSERT_NOT_NULL(r);
ASSERT_FALSE(r->has_error);
ASSERT(has_def(r, "Class", "Widget"));
ASSERT(has_def(r, "Method", "draw"));
cbm_free_result(r);
PASS();
}
/* ═══════════════════════════════════════════════════════════════════
* Group C: Scripting / Dynamic Languages
* ═══════════════════════════════════════════════════════════════════ */
/* --- Python --- */
TEST(python_function) {
CBMFileResult *r = extract(
"def greet(name):\n return f\"Hello {name}\"\n\ndef main():\n greet(\"World\")\n",
CBM_LANG_PYTHON, "t", "main.py");
ASSERT_NOT_NULL(r);
ASSERT_FALSE(r->has_error);
ASSERT(has_def(r, "Function", "greet"));
ASSERT(has_def(r, "Function", "main"));
cbm_free_result(r);
PASS();
}
TEST(python_class) {
CBMFileResult *r =
extract("class Dog:\n def __init__(self, name):\n self.name = name\n def "
"speak(self):\n return f\"Woof from {self.name}\"\n",
CBM_LANG_PYTHON, "t", "dog.py");
ASSERT_NOT_NULL(r);
ASSERT_FALSE(r->has_error);
ASSERT(has_def(r, "Class", "Dog"));
ASSERT(has_def(r, "Method", "speak"));
cbm_free_result(r);
PASS();
}
/* --- JavaScript --- */
TEST(js_function) {
CBMFileResult *r =
extract("function greet(name) { return `Hello ${name}`; }\nconst add = (a, b) => a + b;\n",
CBM_LANG_JAVASCRIPT, "t", "util.js");
ASSERT_NOT_NULL(r);
ASSERT_FALSE(r->has_error);
ASSERT(has_def(r, "Function", "greet"));
cbm_free_result(r);
PASS();
}
TEST(js_class) {
CBMFileResult *r =
extract("class Counter {\n constructor() { this.count = 0; }\n increment() { "
"this.count++; }\n get value() { return this.count; }\n}\n",
CBM_LANG_JAVASCRIPT, "t", "counter.js");
ASSERT_NOT_NULL(r);
ASSERT_FALSE(r->has_error);
ASSERT(has_def(r, "Class", "Counter"));
ASSERT(has_def(r, "Method", "increment"));
cbm_free_result(r);
PASS();
}
/* --- TypeScript --- */
TEST(ts_function) {
CBMFileResult *r = extract("export function greet(name: string): string { return `Hello "
"${name}`; }\nfunction helper(): void {}\n",
CBM_LANG_TYPESCRIPT, "t", "util.ts");
ASSERT_NOT_NULL(r);
ASSERT_FALSE(r->has_error);
ASSERT(has_def(r, "Function", "greet"));
cbm_free_result(r);
PASS();
}
TEST(ts_class) {
CBMFileResult *r =
extract("class Service {\n private name: string;\n constructor(name: string) { this.name "
"= name; }\n getName(): string { return this.name; }\n}\n",
CBM_LANG_TYPESCRIPT, "t", "service.ts");
ASSERT_NOT_NULL(r);
ASSERT_FALSE(r->has_error);
ASSERT(has_def(r, "Class", "Service"));
cbm_free_result(r);
PASS();
}
/* --- Lua --- */
TEST(lua_function) {
CBMFileResult *r = extract(
"function greet(name)\n return \"Hello \" .. name\nend\nlocal function helper() end\n",
CBM_LANG_LUA, "t", "main.lua");
ASSERT_NOT_NULL(r);
ASSERT_FALSE(r->has_error);
ASSERT(has_def(r, "Function", "greet"));
cbm_free_result(r);
PASS();
}
/* --- Bash --- */
TEST(bash_function) {
CBMFileResult *r =
extract("greet() {\n echo \"Hello $1\"\n}\nmain() {\n greet \"World\"\n}\n",
CBM_LANG_BASH, "t", "script.sh");
ASSERT_NOT_NULL(r);
ASSERT_FALSE(r->has_error);
ASSERT(has_def(r, "Function", "greet"));
cbm_free_result(r);
PASS();
}
/* --- Perl --- */
TEST(perl_function) {
CBMFileResult *r = extract("sub greet {\n my ($name) = @_;\n return \"Hello "
"$name\";\n}\nsub main { greet(\"World\"); }\n",
CBM_LANG_PERL, "t", "main.pl");
ASSERT_NOT_NULL(r);
ASSERT_FALSE(r->has_error);
ASSERT(has_def(r, "Function", "greet"));
cbm_free_result(r);
PASS();
}
/* --- R --- */
TEST(r_function) {
CBMFileResult *r = extract("add <- function(x, y) x + y\nmultiply <- function(x, y) x * y\n",
CBM_LANG_R, "t", "math.R");
ASSERT_NOT_NULL(r);
ASSERT_FALSE(r->has_error);
ASSERT(has_def(r, "Function", "add"));
cbm_free_result(r);
PASS();
}
/* ═══════════════════════════════════════════════════════════════════
* Group D: Functional Languages
* ═══════════════════════════════════════════════════════════════════ */
/* --- Elixir --- */
TEST(elixir_function) {
CBMFileResult *r = extract("defmodule Greeter do\n def greet(name), do: \"Hello #{name}\"\n "
"defp helper, do: nil\nend\n",
CBM_LANG_ELIXIR, "t", "greeter.ex");
ASSERT_NOT_NULL(r);
ASSERT_FALSE(r->has_error);
ASSERT(has_def(r, "Function", "greet"));
cbm_free_result(r);
PASS();
}
/* --- Haskell --- */
TEST(haskell_function) {
CBMFileResult *r = extract("add :: Int -> Int -> Int\nadd x y = x + y\n\nmultiply :: Int -> "
"Int -> Int\nmultiply x y = x * y\n",
CBM_LANG_HASKELL, "t", "Math.hs");
ASSERT_NOT_NULL(r);
ASSERT_FALSE(r->has_error);
ASSERT(has_def(r, "Function", "add"));
cbm_free_result(r);
PASS();
}
/* --- OCaml --- */
TEST(ocaml_function) {
CBMFileResult *r =
extract("let add x y = x + y\nlet multiply x y = x * y\n", CBM_LANG_OCAML, "t", "math.ml");
ASSERT_NOT_NULL(r);
ASSERT_FALSE(r->has_error);
ASSERT(has_def(r, "Function", "add"));
cbm_free_result(r);
PASS();
}
/* --- Erlang --- */
TEST(erlang_function) {
CBMFileResult *r = extract(
"-module(math).\n-export([add/2]).\nadd(X, Y) -> X + Y.\nmultiply(X, Y) -> X * Y.\n",
CBM_LANG_ERLANG, "t", "math.erl");
ASSERT_NOT_NULL(r);
ASSERT_FALSE(r->has_error);
ASSERT(has_def(r, "Function", "add"));
cbm_free_result(r);
PASS();
}
/* ═══════════════════════════════════════════════════════════════════
* Group E: Markup / Config / Helper Languages
* ═══════════════════════════════════════════════════════════════════ */
/* --- YAML --- */
TEST(yaml_variables) {
CBMFileResult *r =
extract("name: myapp\nversion: 1.0\ndatabase:\n host: localhost\n port: 5432\n",
CBM_LANG_YAML, "t", "config.yml");
ASSERT_NOT_NULL(r);
ASSERT_FALSE(r->has_error);
/* YAML should extract top-level keys as variables */
ASSERT_GT(r->defs.count, 0);
cbm_free_result(r);
PASS();
}
/* --- HCL --- */
TEST(hcl_blocks) {
CBMFileResult *r = extract("resource \"aws_instance\" \"web\" {\n ami = \"abc-123\"\n "
"instance_type = \"t2.micro\"\n}\n"
"variable \"region\" {\n default = \"us-east-1\"\n}\n",
CBM_LANG_HCL, "t", "main.tf");
ASSERT_NOT_NULL(r);
ASSERT_FALSE(r->has_error);
ASSERT_GT(r->defs.count, 0);
/* Block labels are folded into the name so blocks are distinguishable (#337). */
ASSERT(has_def(r, "Class", "resource.aws_instance.web"));
ASSERT(has_def(r, "Class", "variable.region"));
cbm_free_result(r);
PASS();
}
/* --- SQL --- */
TEST(sql_create_table) {
CBMFileResult *r = extract("CREATE TABLE users (\n id INTEGER PRIMARY KEY,\n name TEXT NOT "
"NULL\n);\nCREATE VIEW active_users AS SELECT * FROM users;\n",
CBM_LANG_SQL, "t", "schema.sql");
ASSERT_NOT_NULL(r);
ASSERT_FALSE(r->has_error);
cbm_free_result(r);
PASS();
}
/* --- Dockerfile --- */
TEST(dockerfile_stages) {
CBMFileResult *r = extract(
"FROM node:18 AS builder\nRUN npm install\nFROM node:18-slim\nCOPY --from=builder /app .\n",
CBM_LANG_DOCKERFILE, "t", "Dockerfile");
ASSERT_NOT_NULL(r);
ASSERT_FALSE(r->has_error);
cbm_free_result(r);
PASS();
}
/* ═══════════════════════════════════════════════════════════════════
* Group F: Scientific / Math Languages
* ═══════════════════════════════════════════════════════════════════ */
/* --- MATLAB --- */
TEST(matlab_function) {
CBMFileResult *r =
extract("function y = square(x)\n y = x.^2;\nend\n", CBM_LANG_MATLAB, "t", "square.m");
ASSERT_NOT_NULL(r);
ASSERT_FALSE(r->has_error);
ASSERT(has_def(r, "Function", "square"));
cbm_free_result(r);
PASS();
}
/* --- Lean 4 --- */
TEST(lean_function) {
CBMFileResult *r =
extract("def add (x y : Nat) : Nat := x + y\n", CBM_LANG_LEAN, "t", "Math.lean");
ASSERT_NOT_NULL(r);
ASSERT_FALSE(r->has_error);
ASSERT(has_def(r, "Function", "add"));
cbm_free_result(r);
PASS();
}
/* --- FORM --- */
TEST(form_procedure) {
CBMFileResult *r = extract("#procedure doSomething\n id x = y;\n#endprocedure\n",
CBM_LANG_FORM, "t", "test.frm");
ASSERT_NOT_NULL(r);
ASSERT_FALSE(r->has_error);
ASSERT(has_def(r, "Function", "doSomething"));
cbm_free_result(r);
PASS();
}
/* --- Wolfram --- */
TEST(wolfram_function) {
CBMFileResult *r =
extract("square[x_] := x^2\nadd[x_, y_] := x + y\n", CBM_LANG_WOLFRAM, "t", "math.wl");
ASSERT_NOT_NULL(r);
ASSERT_FALSE(r->has_error);
ASSERT(has_def(r, "Function", "square"));
ASSERT(has_def(r, "Function", "add"));
cbm_free_result(r);
PASS();
}
/* --- Magma --- */
TEST(magma_function) {
CBMFileResult *r = extract("function Factorial(n)\n if n le 1 then\n return 1;\n end "
"if;\n return n * Factorial(n - 1);\nend function;\n",
CBM_LANG_MAGMA, "t", "test.m");
ASSERT_NOT_NULL(r);
ASSERT_FALSE(r->has_error);
ASSERT(has_def(r, "Function", "Factorial"));
cbm_free_result(r);
PASS();
}
/* ═══════════════════════════════════════════════════════════════════
* Group G: v0.5 Expansion Languages
* ═══════════════════════════════════════════════════════════════════ */
/* --- F# --- */
TEST(fsharp_function) {
/* Go test only asserts >=1 def — F# name extraction is incomplete */
CBMFileResult *r = extract("module Greeter\nlet greet name = sprintf \"Hello %s\" name\n",
CBM_LANG_FSHARP, "t", "Greeter.fs");
ASSERT_NOT_NULL(r);
ASSERT_FALSE(r->has_error);
ASSERT_GTE(r->defs.count, 1);
cbm_free_result(r);
PASS();
}
/* --- Julia --- */
TEST(julia_function) {
CBMFileResult *r = extract("function add(x, y)\n x + y\nend\nadd2(x, y) = x + y\n",
CBM_LANG_JULIA, "t", "math.jl");
ASSERT_NOT_NULL(r);
ASSERT_FALSE(r->has_error);
ASSERT(has_def(r, "Function", "add"));
cbm_free_result(r);
PASS();
}
/* --- Elm --- */
TEST(elm_function) {
CBMFileResult *r =
extract("add x y = x + y\nmultiply x y = x * y\n", CBM_LANG_ELM, "t", "Math.elm");
ASSERT_NOT_NULL(r);
ASSERT_FALSE(r->has_error);
ASSERT(has_def(r, "Function", "add"));
cbm_free_result(r);
PASS();
}
/* --- Nix --- */
TEST(nix_function) {
CBMFileResult *r =
extract("{ pkgs ? import <nixpkgs> {} }:\nlet\n hello = pkgs.writeShellScriptBin "
"\"hello\" ''echo hello'';\nin { inherit hello; }\n",
CBM_LANG_NIX, "t", "default.nix");
ASSERT_NOT_NULL(r);
ASSERT_FALSE(r->has_error);
cbm_free_result(r);
PASS();
}
/* --- Fortran --- */
TEST(fortran_function) {
/* Fortran subroutine name extraction is incomplete — just verify no crash */
CBMFileResult *r = extract("subroutine greet(name)\n character(*), intent(in) :: name\n "
"print *, 'Hello ', name\nend subroutine\n",
CBM_LANG_FORTRAN, "t", "greet.f90");
ASSERT_NOT_NULL(r);
ASSERT_FALSE(r->has_error);
cbm_free_result(r);
PASS();
}
/* ═══════════════════════════════════════════════════════════════════
* Group A2: Missing OOP / Systems variants
* ═══════════════════════════════════════════════════════════════════ */
/* --- Swift struct --- */
TEST(swift_struct) {
CBMFileResult *r = extract("struct Point {\n var x: Double\n var y: Double\n func "
"distance() -> Double { return (x*x + y*y).squareRoot() }\n}\n",
CBM_LANG_SWIFT, "t", "Point.swift");
ASSERT_NOT_NULL(r);
ASSERT_FALSE(r->has_error);
ASSERT(has_def(r, "Method", "distance"));
cbm_free_result(r);
PASS();
}
/* --- Swift calls (port of PR #47 Go tests) --- */
TEST(swift_simple_call) {
CBMFileResult *r = extract("func main() { greet() }\nfunc greet() { print(\"hello\") }\n",
CBM_LANG_SWIFT, "t", "main.swift");
ASSERT_NOT_NULL(r);
ASSERT_FALSE(r->has_error);
ASSERT(has_call(r, "greet"));
cbm_free_result(r);
PASS();
}
TEST(swift_method_call) {
CBMFileResult *r =
extract("class Foo {\n func bar() { baz.run() }\n}\n", CBM_LANG_SWIFT, "t", "Foo.swift");
ASSERT_NOT_NULL(r);
ASSERT_FALSE(r->has_error);
ASSERT(has_call(r, "baz.run"));
cbm_free_result(r);
PASS();
}
TEST(swift_constructor_call) {
CBMFileResult *r =
extract("func create() { let x = MyClass() }\n", CBM_LANG_SWIFT, "t", "create.swift");
ASSERT_NOT_NULL(r);
ASSERT_FALSE(r->has_error);
ASSERT(has_call(r, "MyClass"));
cbm_free_result(r);
PASS();
}
TEST(swift_chained_call) {
CBMFileResult *r = extract("func setup() { AlarmScheduler.shared.startKeepAlive() }\n",
CBM_LANG_SWIFT, "t", "setup.swift");
ASSERT_NOT_NULL(r);
ASSERT_FALSE(r->has_error);
ASSERT(r->calls.count > 0);
cbm_free_result(r);
PASS();
}
/* --- Objective-C --- */
TEST(objc_interface) {
CBMFileResult *r =
extract("@interface Animal : NSObject\n- (NSString *)name;\n- (void)speak;\n@end\n",
CBM_LANG_OBJC, "t", "Animal.h");
ASSERT_NOT_NULL(r);
ASSERT_FALSE(r->has_error);
ASSERT_GTE(r->defs.count, 1);
cbm_free_result(r);
PASS();
}
TEST(objc_implementation) {
CBMFileResult *r = extract("@implementation Animal\n- (NSString *)name { return @\"Animal\"; "
"}\n- (void)speak { NSLog(@\"...\"); }\n@end\n",
CBM_LANG_OBJC, "t", "Animal.m");
ASSERT_NOT_NULL(r);
ASSERT_FALSE(r->has_error);
ASSERT_GTE(r->defs.count, 1);
cbm_free_result(r);
PASS();
}
/* --- Dart top-level function --- */
TEST(dart_top_level_function) {
CBMFileResult *r = extract(
"void main() {\n print('Hello');\n}\nString greet(String name) => 'Hello $name';\n",
CBM_LANG_DART, "t", "main.dart");
ASSERT_NOT_NULL(r);
ASSERT_FALSE(r->has_error);
ASSERT(has_def(r, "Function", "main"));
ASSERT(has_def(r, "Function", "greet"));
cbm_free_result(r);
PASS();
}
/* --- Rust enum --- */
TEST(rust_enum) {
CBMFileResult *r =
extract("pub enum Direction { North, South, East, West }\n", CBM_LANG_RUST, "t", "dir.rs");
ASSERT_NOT_NULL(r);
ASSERT_FALSE(r->has_error);
ASSERT_GTE(r->defs.count, 1);
cbm_free_result(r);
PASS();
}
/* --- Zig struct --- */
TEST(zig_struct) {
CBMFileResult *r = extract("const Point = struct { x: f32, y: f32, pub fn dist(self: Point) "
"f32 { return self.x + self.y; } };\n",
CBM_LANG_ZIG, "t", "point.zig");
ASSERT_NOT_NULL(r);
ASSERT_FALSE(r->has_error);
ASSERT_GTE(r->defs.count, 1);
cbm_free_result(r);
PASS();
}
/* --- C++ function (standalone) --- */
TEST(cpp_function) {
CBMFileResult *r = extract("#include <string>\nstd::string greet(const std::string& name) { "
"return \"Hello \" + name; }\nint main() { return 0; }\n",
CBM_LANG_CPP, "t", "main.cpp");
ASSERT_NOT_NULL(r);
ASSERT_FALSE(r->has_error);
ASSERT_GTE(r->defs.count, 1);
cbm_free_result(r);
PASS();
}
/* --- C++ out-of-line method definitions (#428) ---
* A .cpp defining methods of a class declared elsewhere (not in this TU).
* Pre-fix these were recorded as free Functions (label "Function", no
* parent_class); they must be Methods linked to their enclosing class via
* parent_class. (The helper also descends nested scopes `ns::Class::method` to
* the immediate class, but tree-sitter-cpp parses a synthetic doubly-qualified
* out-of-line def in isolation as an ERROR node, so that path is exercised by
* real codebases rather than this isolated unit fixture.) */
TEST(cpp_out_of_line_method_issue428) {
CBMFileResult *r = extract("void Foo::bar() {}\n"
"int Foo::baz() { return 0; }\n",
CBM_LANG_CPP, "t", "foo.cpp");
ASSERT_NOT_NULL(r);
ASSERT(has_def(r, "Method", "bar"));
ASSERT(has_def(r, "Method", "baz"));
ASSERT(!has_def(r, "Function", "bar")); /* not a free function */
/* parent_class links to the enclosing class QN */
int checked = 0;
for (int i = 0; i < r->defs.count; i++) {
const CBMDefinition *d = &r->defs.items[i];
if (strcmp(d->name, "bar") == 0 && strcmp(d->label, "Method") == 0) {
ASSERT_NOT_NULL(d->parent_class);
ASSERT(strstr(d->parent_class, "Foo") != NULL);
checked = 1;
}
}
ASSERT(checked);
cbm_free_result(r);
PASS();
}
/* --- COBOL paragraph --- */
TEST(cobol_paragraph) {
CBMFileResult *r =
extract("IDENTIFICATION DIVISION.\nPROGRAM-ID. HELLO.\nPROCEDURE DIVISION.\n "
"DISPLAY-GREETING.\n DISPLAY 'HELLO WORLD'.\n STOP RUN.\n",
CBM_LANG_COBOL, "t", "hello.cbl");
ASSERT_NOT_NULL(r);
ASSERT_FALSE(r->has_error);
ASSERT_GTE(r->defs.count, 1);
cbm_free_result(r);
PASS();
}
/* --- Verilog module --- */
TEST(verilog_module) {
CBMFileResult *r =
extract("module adder(input a, input b, output sum);\n assign sum = a + b;\nendmodule\n",
CBM_LANG_VERILOG, "t", "adder.v");
ASSERT_NOT_NULL(r);
ASSERT_FALSE(r->has_error);
ASSERT_GTE(r->defs.count, 1);
cbm_free_result(r);
PASS();
}
/* --- CUDA kernel --- */
TEST(cuda_kernel) {
CBMFileResult *r = extract("__global__ void vectorAdd(float *a, float *b, float *c, int n) {\n "
" int i = blockIdx.x * blockDim.x + threadIdx.x;\n if (i < n) "
"c[i] = a[i] + b[i];\n}\nint main() { return 0; }\n",
CBM_LANG_CUDA, "t", "vector.cu");
ASSERT_NOT_NULL(r);
ASSERT_FALSE(r->has_error);
ASSERT_GTE(r->defs.count, 1);
cbm_free_result(r);
PASS();
}
/* --- Python decorator --- */
TEST(python_decorator) {
CBMFileResult *r = extract("class Router:\n @staticmethod\n def route(path: str):\n "
" def decorator(func): return func\n return decorator\n",
CBM_LANG_PYTHON, "t", "router.py");
ASSERT_NOT_NULL(r);
ASSERT_FALSE(r->has_error);
ASSERT(has_def(r, "Class", "Router"));
cbm_free_result(r);
PASS();
}
/* --- TypeScript interface --- */
TEST(ts_interface) {
CBMFileResult *r = extract("export interface Repository<T> { findById(id: number): T; "
"save(entity: T): void; delete(id: number): void; }\n",
CBM_LANG_TYPESCRIPT, "t", "repo.ts");
ASSERT_NOT_NULL(r);
ASSERT_FALSE(r->has_error);
ASSERT_GTE(r->defs.count, 1);
cbm_free_result(r);
PASS();
}
/* --- TSX component --- */
TEST(tsx_component) {
CBMFileResult *r = extract(
"import React from 'react';\ninterface Props { name: string; }\nexport function Greeting({ "
"name }: Props) {\n return <div>Hello {name}</div>;\n}\nexport default Greeting;\n",
CBM_LANG_TSX, "t", "Greeting.tsx");
ASSERT_NOT_NULL(r);
ASSERT_FALSE(r->has_error);
ASSERT(has_def(r, "Function", "Greeting"));
cbm_free_result(r);
PASS();
}
/* --- Lua table method --- */
TEST(lua_table_method) {
CBMFileResult *r =
extract("local M = {}\nfunction M.create(name)\n return { name = name }\nend\nfunction "
"M.greet(self)\n return 'Hi ' .. self.name\nend\nreturn M\n",
CBM_LANG_LUA, "t", "module.lua");
ASSERT_NOT_NULL(r);
ASSERT_FALSE(r->has_error);
/* Should extract at least one Function from Lua table method */
int fn_count = 0;
for (int i = 0; i < r->defs.count; i++) {
if (strcmp(r->defs.items[i].label, "Function") == 0)
fn_count++;
}
ASSERT_GTE(fn_count, 1);
cbm_free_result(r);
PASS();
}
/* --- Emacs Lisp defun --- */
TEST(emacs_lisp_defun) {
CBMFileResult *r = extract("(defun greet (name)\n (message \"Hello %s\" name))\n(defun main "
"()\n (greet \"World\"))\n",
CBM_LANG_EMACSLISP, "t", "init.el");
ASSERT_NOT_NULL(r);
ASSERT_FALSE(r->has_error);
ASSERT(has_def(r, "Function", "greet"));
ASSERT(has_def(r, "Function", "main"));
cbm_free_result(r);
PASS();
}
/* --- Emacs Lisp defvar --- */
TEST(emacs_lisp_defvar) {
CBMFileResult *r = extract("(defvar my-count 0 \"A counter.\")\n(defcustom my-name \"World\" "
"\"The name.\"\n :type 'string)\n",
CBM_LANG_EMACSLISP, "t", "vars.el");
ASSERT_NOT_NULL(r);
ASSERT_FALSE(r->has_error);
ASSERT_GTE(r->defs.count, 1);
cbm_free_result(r);
PASS();
}
/* --- Haskell data type --- */
TEST(haskell_data_type) {
CBMFileResult *r =
extract("data Shape = Circle Double | Rectangle Double Double\narea :: Shape -> "
"Double\narea (Circle r) = pi * r * r\narea (Rectangle w h) = w * h\n",
CBM_LANG_HASKELL, "t", "Shape.hs");
ASSERT_NOT_NULL(r);
ASSERT_FALSE(r->has_error);
ASSERT_GTE(r->defs.count, 1);
cbm_free_result(r);
PASS();
}
/* --- Clojure function (known limitation: defn produces list_lit) --- */
TEST(clojure_function) {
CBMFileResult *r = extract("(ns greeter.core)\n(defn greet [name]\n (str \"Hello \" "
"name))\n(defn -main [& args]\n (println (greet \"World\")))\n",
CBM_LANG_CLOJURE, "t", "core.clj");
ASSERT_NOT_NULL(r);
ASSERT_FALSE(r->has_error);
/* Clojure uses list_lit for all forms — no function defs extracted (known limitation) */
cbm_free_result(r);
PASS();
}
/* ═══════════════════════════════════════════════════════════════════
* Group E2: Missing Config / Markup Languages
* ═══════════════════════════════════════════════════════════════════ */
/* --- HTML elements --- */
TEST(html_elements) {
CBMFileResult *r = extract(
"<!DOCTYPE "
"html><html><head><title>Test</title></head><body><h1>Hello</h1><p>World</p></body></html>",
CBM_LANG_HTML, "t", "index.html");
ASSERT_NOT_NULL(r);
ASSERT_FALSE(r->has_error);
ASSERT_GTE(r->defs.count, 1);
cbm_free_result(r);
PASS();
}
/* --- SQL function (CREATE FUNCTION) --- */
TEST(sql_function) {
CBMFileResult *r = extract("CREATE FUNCTION get_user_count() RETURNS INTEGER AS $$ SELECT "
"COUNT(*) FROM users; $$ LANGUAGE SQL;\n",
CBM_LANG_SQL, "t", "funcs.sql");
ASSERT_NOT_NULL(r);
ASSERT_FALSE(r->has_error);
ASSERT_GTE(r->defs.count, 1);
cbm_free_result(r);
PASS();
}
/* --- Meson project --- */
TEST(meson_project) {
CBMFileResult *r = extract(
"project('myapp', 'c', version: '1.0.0')\nexecutable('myapp', 'main.c', install: true)\n",
CBM_LANG_MESON, "t", "meson.build");
ASSERT_NOT_NULL(r);
ASSERT_FALSE(r->has_error);
ASSERT_GTE(r->defs.count, 1);
cbm_free_result(r);
PASS();
}
/* --- CSS rules --- */
TEST(css_rules) {
CBMFileResult *r = extract(
".container { display: flex; width: 100%; }\n.button { background: #007bff; color: white; "
"border: none; }\n@media (max-width: 768px) { .container { flex-direction: column; } }\n",
CBM_LANG_CSS, "t", "styles.css");
ASSERT_NOT_NULL(r);
ASSERT_FALSE(r->has_error);
ASSERT_GTE(r->defs.count, 1);
cbm_free_result(r);
PASS();
}
/* --- SCSS rules --- */
TEST(scss_rules) {
CBMFileResult *r = extract("$primary: #007bff;\n.container {\n width: 100%;\n .button {\n "
"background: $primary;\n &:hover { opacity: 0.8; }\n }\n}\n",
CBM_LANG_SCSS, "t", "styles.scss");
ASSERT_NOT_NULL(r);
ASSERT_FALSE(r->has_error);
ASSERT_GTE(r->defs.count, 1);
cbm_free_result(r);
PASS();
}
/* --- TOML basic --- */
TEST(toml_basic) {
CBMFileResult *r = extract("[server]\nhost = \"localhost\"\nport = 8080\n\n[database]\nurl = "
"\"postgres://localhost/db\"\nmax_connections = 10\n",
CBM_LANG_TOML, "t", "config.toml");
ASSERT_NOT_NULL(r);
ASSERT_FALSE(r->has_error);
ASSERT(has_def(r, "Class", "server"));
ASSERT(has_def(r, "Class", "database"));
ASSERT(has_def(r, "Variable", "host"));
ASSERT(has_def(r, "Variable", "port"));
cbm_free_result(r);
PASS();
}
/* --- CMake function --- */
TEST(cmake_function) {
CBMFileResult *r = extract(
"cmake_minimum_required(VERSION 3.16)\nproject(MyApp VERSION 1.0)\nadd_executable(myapp "
"main.cpp)\ntarget_compile_features(myapp PRIVATE cxx_std_17)\n",
CBM_LANG_CMAKE, "t", "CMakeLists.txt");
ASSERT_NOT_NULL(r);
ASSERT_FALSE(r->has_error);
ASSERT_GTE(r->defs.count, 1);
cbm_free_result(r);
PASS();
}
/* --- JSON object --- */
TEST(json_object) {
CBMFileResult *r = extract("{\"name\": \"myapp\", \"version\": \"1.0.0\", \"scripts\": "
"{\"build\": \"go build\", \"test\": \"go test ./...\"}}",
CBM_LANG_JSON, "t", "config.json");
ASSERT_NOT_NULL(r);
ASSERT_FALSE(r->has_error);
ASSERT(has_def(r, "Variable", "name"));
ASSERT(has_def(r, "Variable", "version"));
cbm_free_result(r);
PASS();
}
/* --- Protobuf message --- */
TEST(protobuf_message) {
CBMFileResult *r = extract(
"syntax = \"proto3\";\npackage user;\nmessage User { int64 id = 1; string name = 2; string "
"email = 3; }\nservice UserService { rpc GetUser(User) returns (User); }\n",
CBM_LANG_PROTOBUF, "t", "user.proto");
ASSERT_NOT_NULL(r);
ASSERT_FALSE(r->has_error);
ASSERT_GTE(r->defs.count, 1);
cbm_free_result(r);
PASS();
}
/* --- GraphQL type --- */
TEST(graphql_type) {
CBMFileResult *r = extract("type User {\n id: ID!\n name: String!\n email: String!\n}\ntype "
"Query {\n user(id: ID!): User\n users: [User!]!\n}\n",
CBM_LANG_GRAPHQL, "t", "schema.graphql");
ASSERT_NOT_NULL(r);
ASSERT_FALSE(r->has_error);
ASSERT_GTE(r->defs.count, 1);
cbm_free_result(r);
PASS();
}
/* --- Svelte component --- */
TEST(svelte_component) {
CBMFileResult *r = extract("<script>\n let name = 'World';\n function greet() {\n return "
"`Hello ${name}`;\n }\n</script>\n<h1>{greet()}</h1>\n",
CBM_LANG_SVELTE, "t", "App.svelte");
ASSERT_NOT_NULL(r);
ASSERT_FALSE(r->has_error);
ASSERT_GTE(r->defs.count, 1);
cbm_free_result(r);
PASS();
}
/* --- Vue component --- */
TEST(vue_component) {
CBMFileResult *r =
extract("<template><div>{{ message }}</div></template>\n<script>\nexport default {\n "
"name: 'App',\n data() { return { message: 'Hello World' }; },\n methods: { "
"greet() { return this.message; } }\n};\n</script>\n",
CBM_LANG_VUE, "t", "App.vue");
ASSERT_NOT_NULL(r);
ASSERT_FALSE(r->has_error);
ASSERT_GTE(r->defs.count, 1);
cbm_free_result(r);
PASS();
}
/* --- GLSL shader --- */
TEST(glsl_shader) {
CBMFileResult *r = extract(
"#version 330 core\nvoid main() {\n gl_Position = vec4(0.0, 0.0, 0.0, 1.0);\n}\nvec3 "
"transform(vec3 pos, mat4 mvp) {\n return (mvp * vec4(pos, 1.0)).xyz;\n}\n",
CBM_LANG_GLSL, "t", "vertex.glsl");
ASSERT_NOT_NULL(r);
ASSERT_FALSE(r->has_error);
ASSERT_GTE(r->defs.count, 1);
cbm_free_result(r);
PASS();
}
/* --- VimScript function --- */
TEST(vimscript_function) {
CBMFileResult *r = extract("function! SayHello()\n echo 'Hello'\nendfunction\n",
CBM_LANG_VIMSCRIPT, "t", "plugin.vim");
ASSERT_NOT_NULL(r);
ASSERT_FALSE(r->has_error);
/* VimScript extraction may or may not produce named functions */
int fn_count = 0;
for (int i = 0; i < r->defs.count; i++) {
if (strcmp(r->defs.items[i].label, "Function") == 0)
fn_count++;
}
if (fn_count > 0) {
ASSERT(has_def(r, "Function", "SayHello"));
}
cbm_free_result(r);
PASS();
}
/* ═══════════════════════════════════════════════════════════════════
* Group H: Scientific / Math — extended tests
* ═══════════════════════════════════════════════════════════════════ */
/* --- MATLAB parse (simple expression) --- */
TEST(matlab_parse) {
CBMFileResult *r = extract("x = 1;\ny = x + 2;\n", CBM_LANG_MATLAB, "t", "simple.matlab");
ASSERT_NOT_NULL(r);
ASSERT_FALSE(r->has_error);
cbm_free_result(r);
PASS();
}
/* --- MATLAB call --- */
TEST(matlab_call) {
CBMFileResult *r = extract("function y = foo(x)\n y = inv(x);\n disp hello\nend\n",
CBM_LANG_MATLAB, "t", "foo.matlab");
ASSERT_NOT_NULL(r);
ASSERT_FALSE(r->has_error);
ASSERT_GT(r->calls.count, 0);
ASSERT(has_call(r, "inv"));
ASSERT(has_call(r, "disp"));
cbm_free_result(r);
PASS();
}
/* --- Lean parse (theorem) --- */
TEST(lean_parse) {
CBMFileResult *r = extract("theorem add_comm (a b : Nat) : a + b = b + a := by omega\n",
CBM_LANG_LEAN, "t", "Comm.lean");
ASSERT_NOT_NULL(r);
ASSERT_FALSE(r->has_error);
cbm_free_result(r);
PASS();
}
/* --- Lean call (recursive fib) --- */
TEST(lean_call) {
CBMFileResult *r = extract("def fib : Nat \xe2\x86\x92 Nat\n | 0 => 1\n | 1 => 1\n | n + 2 "
"=> fib (n + 1) + fib n\n",
CBM_LANG_LEAN, "t", "Fib.lean");
ASSERT_NOT_NULL(r);
ASSERT_FALSE(r->has_error);
ASSERT_GT(r->calls.count, 0);
ASSERT(has_call(r, "fib"));
cbm_free_result(r);
PASS();
}
/* --- Lean type annotation not call --- */
TEST(lean_type_annotation_not_call) {
CBMFileResult *r = extract(
"def listLen (xs : List Nat) : Nat := 0\ndef greet : IO Unit := IO.println \"hi\"\n",
CBM_LANG_LEAN, "t", "Types.lean");
ASSERT_NOT_NULL(r);
ASSERT_FALSE(r->has_error);
/* "List" in binder type position should NOT be extracted as a call */
for (int i = 0; i < r->calls.count; i++) {
ASSERT_FALSE(strcmp(r->calls.items[i].callee_name, "List") == 0);
}
/* IO.println in the body should be present */
int found_println = 0;
for (int i = 0; i < r->calls.count; i++) {
if (strstr(r->calls.items[i].callee_name, "println") != NULL) {
found_println = 1;
}
}
ASSERT_TRUE(found_println);
cbm_free_result(r);
PASS();
}
/* --- FORM parse (simple expression) --- */
TEST(form_parse) {
CBMFileResult *r = extract("Symbols x, y;\nLocal F = x + y;\nPrint;\n.end\n", CBM_LANG_FORM,
"t", "example.frm");
ASSERT_NOT_NULL(r);
ASSERT_FALSE(r->has_error);
cbm_free_result(r);
PASS();
}
/* --- FORM call (#call) --- */
TEST(form_call) {
CBMFileResult *r = extract("#procedure myproc(x)\n id `x' = 0;\n#endprocedure\n#procedure "
"caller()\n #call myproc(1)\n#endprocedure\n",
CBM_LANG_FORM, "t", "calc.frm");
ASSERT_NOT_NULL(r);
ASSERT_FALSE(r->has_error);
ASSERT_GT(r->calls.count, 0);
ASSERT(has_call(r, "myproc"));
cbm_free_result(r);
PASS();
}
/* --- Magma procedure --- */
TEST(magma_procedure) {
CBMFileResult *r = extract("procedure PrintHello()\n print \"Hello\";\nend procedure;\n",
CBM_LANG_MAGMA, "t", "hello.mag");
ASSERT_NOT_NULL(r);
ASSERT_FALSE(r->has_error);
int fn_count = 0;
for (int i = 0; i < r->defs.count; i++) {
if (strcmp(r->defs.items[i].label, "Function") == 0)
fn_count++;
}
if (fn_count > 0) {
ASSERT(has_def(r, "Function", "PrintHello"));
}
cbm_free_result(r);
PASS();
}
/* --- Magma parse (simple) --- */
TEST(magma_parse) {
CBMFileResult *r = extract("x := 42;\ny := x + 1;\n", CBM_LANG_MAGMA, "t", "simple.mag");
ASSERT_NOT_NULL(r);
ASSERT_FALSE(r->has_error);
cbm_free_result(r);
PASS();
}
/* --- Magma import (load) --- */
TEST(magma_import) {
CBMFileResult *r = extract("load \"utils.mag\";\nload \"lib/helpers.mag\";\n", CBM_LANG_MAGMA,
"t", "main.mag");
ASSERT_NOT_NULL(r);
ASSERT_FALSE(r->has_error);
ASSERT_GTE(r->imports.count, 2);
cbm_free_result(r);
PASS();
}
/* --- Magma call --- */
TEST(magma_call) {
CBMFileResult *r = extract("function Foo(x)\n y := Bar(x);\n return y;\nend function;\n",
CBM_LANG_MAGMA, "t", "calls.mag");
ASSERT_NOT_NULL(r);
ASSERT_FALSE(r->has_error);
ASSERT_GT(r->calls.count, 0);
ASSERT(has_call(r, "Bar"));
cbm_free_result(r);
PASS();
}
/* --- Magma disambiguation (.m file as Magma) --- */
TEST(magma_disambiguation) {
CBMFileResult *r = extract("function Factorial(n)\n if n le 1 then\n return 1;\n end "
"if;\n return n * Factorial(n - 1);\nend function;\n",
CBM_LANG_MAGMA, "t", "test.m");
ASSERT_NOT_NULL(r);
ASSERT_FALSE(r->has_error);
int fn_count = 0;
for (int i = 0; i < r->defs.count; i++) {
if (strcmp(r->defs.items[i].label, "Function") == 0)
fn_count++;
}
ASSERT_GTE(fn_count, 1);
if (fn_count > 0) {
ASSERT(has_def(r, "Function", "Factorial"));
}
cbm_free_result(r);
PASS();
}
/* --- Wolfram function (both := and =) --- */
TEST(wolfram_function_extended) {
CBMFileResult *r = extract("f[x_] := x^2\ng[x_] = x + 1\n", CBM_LANG_WOLFRAM, "t", "funcs.wl");
ASSERT_NOT_NULL(r);
ASSERT_FALSE(r->has_error);
int fn_count = 0;
for (int i = 0; i < r->defs.count; i++) {
if (strcmp(r->defs.items[i].label, "Function") == 0)
fn_count++;
}
ASSERT_GTE(fn_count, 2);
ASSERT(has_def(r, "Function", "f"));
ASSERT(has_def(r, "Function", "g"));
cbm_free_result(r);
PASS();
}
/* --- Wolfram call --- */
TEST(wolfram_call) {
CBMFileResult *r = extract("f[x_] := g[x] + h[x]\n", CBM_LANG_WOLFRAM, "t", "calls.wl");
ASSERT_NOT_NULL(r);
ASSERT_FALSE(r->has_error);
ASSERT_GT(r->calls.count, 0);
ASSERT(has_call(r, "g"));
ASSERT(has_call(r, "h"));
/* "f" should NOT appear as a call (it's the definition LHS) */
for (int i = 0; i < r->calls.count; i++) {
ASSERT_FALSE(strcmp(r->calls.items[i].callee_name, "f") == 0);
}
cbm_free_result(r);
PASS();
}
/* --- Wolfram caller attribution --- */
TEST(wolfram_caller_attribution) {
CBMFileResult *r = extract("f[x_] := g[x] + h[x]\n", CBM_LANG_WOLFRAM, "t", "caller.wl");
ASSERT_NOT_NULL(r);
ASSERT_FALSE(r->has_error);
ASSERT_GT(r->calls.count, 0);
/* Calls inside f[] should have f as enclosing function, not the module path */
for (int i = 0; i < r->calls.count; i++) {
if (strcmp(r->calls.items[i].callee_name, "g") == 0 ||
strcmp(r->calls.items[i].callee_name, "h") == 0) {
/* enclosing_func_qn must NOT be empty or the file path */
ASSERT_NOT_NULL(r->calls.items[i].enclosing_func_qn);
ASSERT_FALSE(strcmp(r->calls.items[i].enclosing_func_qn, "") == 0);
ASSERT_FALSE(strcmp(r->calls.items[i].enclosing_func_qn, "t.caller") == 0);
}
}
cbm_free_result(r);
PASS();
}
/* Issue #438: a C function_definition has no `name` field — the name lives in the
* declarator chain. Calls inside a C function must be attributed to the enclosing
* function, not the module. Pre-fix, enclosing_func_qn fell back to the module QN. */
TEST(c_caller_attribution) {
CBMFileResult *r = extract("int helper(int x) { return x; }\n"
"int caller(void) { return helper(1); }\n",
CBM_LANG_C, "t", "main.c");
ASSERT_NOT_NULL(r);
ASSERT_FALSE(r->has_error);
ASSERT_GT(r->calls.count, 0);
int saw_helper = 0;
for (int i = 0; i < r->calls.count; i++) {
if (strcmp(r->calls.items[i].callee_name, "helper") == 0) {
saw_helper = 1;
/* enclosing_func_qn must be the function, NOT empty and NOT the module QN. */
ASSERT_NOT_NULL(r->calls.items[i].enclosing_func_qn);
ASSERT_FALSE(strcmp(r->calls.items[i].enclosing_func_qn, "") == 0);
ASSERT_FALSE(strcmp(r->calls.items[i].enclosing_func_qn, "t.main") == 0);
}
}
ASSERT(saw_helper);
cbm_free_result(r);
PASS();
}
/* adc8304 (the dedup refactor bundled into #463) re-pointed the C/C++ enclosing-
* function resolver at the canonical declarator walker: qualified names (Foo::bar)
* now resolve via resolve_qualified_name(), and `type_identifier` was dropped from
* the terminal-name set. These guard that out-of-line C++ method / ctor / dtor
* definitions still attribute their inner calls to the enclosing function, not the
* module — i.e. that the dedup did not reintroduce the #438 regression on the
* qualified-declarator path. Module QN for "m.cpp" under prefix "t" is "t.m". */
TEST(cpp_out_of_line_method_caller_attribution) {
CBMFileResult *r = extract("struct Foo { void bar(); };\n"
"int helper(int x) { return x; }\n"
"void Foo::bar() { helper(1); }\n",
CBM_LANG_CPP, "t", "m.cpp");
ASSERT_NOT_NULL(r);
ASSERT_FALSE(r->has_error);
ASSERT_GT(r->calls.count, 0);
int saw_helper = 0;
for (int i = 0; i < r->calls.count; i++) {
if (strcmp(r->calls.items[i].callee_name, "helper") == 0) {
saw_helper = 1;
/* enclosing must be the out-of-line method, NOT empty and NOT the module. */
ASSERT_NOT_NULL(r->calls.items[i].enclosing_func_qn);
ASSERT_FALSE(strcmp(r->calls.items[i].enclosing_func_qn, "") == 0);
ASSERT_FALSE(strcmp(r->calls.items[i].enclosing_func_qn, "t.m") == 0);
}
}
ASSERT(saw_helper);
cbm_free_result(r);
PASS();
}
/* Out-of-line constructor (Foo::Foo) and destructor (Foo::~Foo) exercise the
* identifier and destructor_name branches of resolve_qualified_name(). A call
* inside either must attribute to that special member, not the module. */
TEST(cpp_out_of_line_ctor_dtor_caller_attribution) {
CBMFileResult *r = extract("struct Foo { Foo(); ~Foo(); };\n"
"int helper(int x) { return x; }\n"
"Foo::Foo() { helper(1); }\n"
"Foo::~Foo() { helper(2); }\n",
CBM_LANG_CPP, "t", "m.cpp");
ASSERT_NOT_NULL(r);
ASSERT_FALSE(r->has_error);
ASSERT_GT(r->calls.count, 0);
int helper_calls = 0;
for (int i = 0; i < r->calls.count; i++) {
if (strcmp(r->calls.items[i].callee_name, "helper") == 0) {
helper_calls++;
ASSERT_NOT_NULL(r->calls.items[i].enclosing_func_qn);
ASSERT_FALSE(strcmp(r->calls.items[i].enclosing_func_qn, "") == 0);
ASSERT_FALSE(strcmp(r->calls.items[i].enclosing_func_qn, "t.m") == 0);
}
}
ASSERT(helper_calls >= 1);
cbm_free_result(r);
PASS();
}
/* --- Wolfram parse (simple assignment) --- */
TEST(wolfram_parse) {
CBMFileResult *r = extract("x = 42;\ny = x + 1;\n", CBM_LANG_WOLFRAM, "t", "simple.wl");
ASSERT_NOT_NULL(r);
ASSERT_FALSE(r->has_error);
cbm_free_result(r);
PASS();
}
/* --- Wolfram import --- */
TEST(wolfram_import) {
CBMFileResult *r =
extract("<< \"utils.wl\"\nNeeds[\"Package`\"]\n", CBM_LANG_WOLFRAM, "t", "main.wl");
ASSERT_NOT_NULL(r);
ASSERT_FALSE(r->has_error);
ASSERT_GT(r->imports.count, 0);
cbm_free_result(r);
PASS();
}
/* --- Wolfram nested def --- */
TEST(wolfram_nested_def) {
CBMFileResult *r = extract("main[x_] := Module[{localF}, localF[t_] := t + 1; localF[x]]\n",
CBM_LANG_WOLFRAM, "t", "nested.wl");
ASSERT_NOT_NULL(r);
ASSERT_FALSE(r->has_error);
ASSERT(has_def(r, "Function", "main"));
ASSERT(has_def(r, "Function", "localF"));
cbm_free_result(r);
PASS();
}
/* ═══════════════════════════════════════════════════════════════════
* Group I: cbm_test.go ports
* ═══════════════════════════════════════════════════════════════════ */
TEST(python_docstring) {
CBMFileResult *r = extract(
"def compute(x, y):\n \"\"\"Compute the sum of x and y.\"\"\"\n return x + y\n",
CBM_LANG_PYTHON, "test", "test.py");
ASSERT_NOT_NULL(r);
ASSERT_FALSE(r->has_error);
ASSERT(has_def(r, "Function", "compute"));
/* Check docstring is present */
int found = 0;
for (int i = 0; i < r->defs.count; i++) {
if (strcmp(r->defs.items[i].name, "compute") == 0) {
found = 1;
ASSERT_NOT_NULL(r->defs.items[i].docstring);
ASSERT_TRUE(strlen(r->defs.items[i].docstring) > 0);
}
}
ASSERT_TRUE(found);
cbm_free_result(r);
PASS();
}
TEST(go_function_extraction) {
CBMFileResult *r =
extract("package main\n\n// Greet returns a greeting.\nfunc Greet(name string) string "
"{\n\treturn \"Hello, \" + name\n}\n\nfunc main() {\n\tGreet(\"world\")\n}\n",
CBM_LANG_GO, "test", "main.go");
ASSERT_NOT_NULL(r);
ASSERT_FALSE(r->has_error);
ASSERT(has_def(r, "Function", "Greet"));
ASSERT(has_def(r, "Function", "main"));
ASSERT(has_call(r, "Greet"));
cbm_free_result(r);
PASS();
}
TEST(js_arrow_function) {
CBMFileResult *r = extract("const greet = (name) => {\n return \"Hello \" + "
"name;\n};\n\nconst result = greet(\"world\");\n",
CBM_LANG_JAVASCRIPT, "test", "app.js");
ASSERT_NOT_NULL(r);
ASSERT_FALSE(r->has_error);
ASSERT_GTE(r->defs.count, 1);
cbm_free_result(r);
PASS();
}
/* ═══════════════════════════════════════════════════════════════════
* Group J: language_failures_test.go ports
* ═══════════════════════════════════════════════════════════════════ */
/* CommonLisp — defun extraction (known limitation: grammar produces list_lit) */
TEST(commonlisp_defun) {
CBMFileResult *r =
extract("(defun hello () \"world\")\n", CBM_LANG_COMMONLISP, "test", "hello.lisp");
ASSERT_NOT_NULL(r);
ASSERT_FALSE(r->has_error);
/* Known limitation: CommonLisp grammar produces list_lit, not defun nodes.
* Function extraction returns 0 — this test documents the limitation. */
cbm_free_result(r);
PASS();
}
TEST(commonlisp_multiple_functions) {
CBMFileResult *r = extract("(defun add (a b) (+ a b))\n(defun mul (a b) (* a b))\n",
CBM_LANG_COMMONLISP, "test", "math.lisp");
ASSERT_NOT_NULL(r);
ASSERT_FALSE(r->has_error);
cbm_free_result(r);
PASS();
}
TEST(commonlisp_defmacro) {
CBMFileResult *r =
extract("(defmacro when2 (condition &body body)\n `(if ,condition (progn ,@body)))\n",
CBM_LANG_COMMONLISP, "test", "macros.lisp");
ASSERT_NOT_NULL(r);
ASSERT_FALSE(r->has_error);
cbm_free_result(r);
PASS();
}
TEST(makefile_rule_as_function) {
CBMFileResult *r = extract("all:\n\t@echo hello\n", CBM_LANG_MAKEFILE, "test", "Makefile");
ASSERT_NOT_NULL(r);
ASSERT_FALSE(r->has_error);
ASSERT(has_def(r, "Function", "all"));
cbm_free_result(r);
PASS();
}
TEST(makefile_multiple_targets) {
CBMFileResult *r = extract("all: main.o\n\tgcc -o all main.o\n\nbuild:\n\tgo build ./...\n",
CBM_LANG_MAKEFILE, "test", "Makefile");
ASSERT_NOT_NULL(r);
ASSERT_FALSE(r->has_error);
ASSERT(has_def(r, "Function", "all"));
ASSERT(has_def(r, "Function", "build"));
cbm_free_result(r);
PASS();
}
TEST(makefile_variable_extraction) {
CBMFileResult *r =
extract("CC := gcc\nCFLAGS := -Wall\n", CBM_LANG_MAKEFILE, "test", "Makefile");
ASSERT_NOT_NULL(r);
ASSERT_FALSE(r->has_error);
/* Variable extraction may or may not work depending on Makefile grammar support */
cbm_free_result(r);
PASS();
}
TEST(vimscript_function_extraction) {
CBMFileResult *r = extract("function! SayHello()\n echo 'Hello'\nendfunction\n",
CBM_LANG_VIMSCRIPT, "test", "plugin.vim");
ASSERT_NOT_NULL(r);
ASSERT_FALSE(r->has_error);
/* VimScript function extraction may or may not produce named functions */
int fn_count = 0;
for (int i = 0; i < r->defs.count; i++) {
if (strcmp(r->defs.items[i].label, "Function") == 0)
fn_count++;
}
if (fn_count > 0) {
ASSERT(has_def(r, "Function", "SayHello"));
}
cbm_free_result(r);
PASS();
}
TEST(vimscript_function_without_bang) {
CBMFileResult *r = extract("function MyFunc(arg)\n return arg\nendfunction\n",
CBM_LANG_VIMSCRIPT, "test", "plugin.vim");
ASSERT_NOT_NULL(r);
ASSERT_FALSE(r->has_error);
int fn_count = 0;
for (int i = 0; i < r->defs.count; i++) {
if (strcmp(r->defs.items[i].label, "Function") == 0)
fn_count++;
}
if (fn_count > 0) {
ASSERT(has_def(r, "Function", "MyFunc"));
}
cbm_free_result(r);
PASS();
}
TEST(julia_function_extraction) {
CBMFileResult *r = extract("function hello()\n println(\"Hello, World!\")\nend\n",
CBM_LANG_JULIA, "test", "hello.jl");
ASSERT_NOT_NULL(r);
ASSERT_FALSE(r->has_error);
int fn_count = 0;
for (int i = 0; i < r->defs.count; i++) {
if (strcmp(r->defs.items[i].label, "Function") == 0)
fn_count++;
}
if (fn_count > 0) {
ASSERT(has_def(r, "Function", "hello"));
}
cbm_free_result(r);
PASS();
}
TEST(julia_function_with_args) {
CBMFileResult *r = extract("function add(a::Int, b::Int)::Int\n return a + b\nend\n",
CBM_LANG_JULIA, "test", "math.jl");
ASSERT_NOT_NULL(r);
ASSERT_FALSE(r->has_error);
int fn_count = 0;
for (int i = 0; i < r->defs.count; i++) {
if (strcmp(r->defs.items[i].label, "Function") == 0)
fn_count++;
}
if (fn_count > 0) {
ASSERT(has_def(r, "Function", "add"));
}
cbm_free_result(r);
PASS();
}
/* ═══════════════════════════════════════════════════════════════════
* Cross-cutting: Calls + Imports
* ═══════════════════════════════════════════════════════════════════ */
TEST(python_calls) {
CBMFileResult *r =
extract("import os\ndef main():\n os.path.exists('/tmp')\n print('hello')\n",
CBM_LANG_PYTHON, "t", "main.py");
ASSERT_NOT_NULL(r);
ASSERT_FALSE(r->has_error);
/* Python unified extraction produces calls — verify at least some exist */
ASSERT_GT(r->calls.count, 0);
cbm_free_result(r);
PASS();
}
TEST(python_iris_classMethodValue) {
CBMFileResult *r =
extract("import iris\n"
"iris_obj = iris.cls('%Library.ObjectScript')\n"
"def call_bfs(n):\n"
" return iris_obj.classMethodValue('Graph.KG.TraversalBFS', 'BFSFastJson', n)\n",
CBM_LANG_PYTHON, "t", "store.py");
ASSERT_NOT_NULL(r);
ASSERT_FALSE(r->has_error);
ASSERT(has_call(r, "Graph.KG.TraversalBFS.BFSFastJson"));
cbm_free_result(r);
PASS();
}
TEST(go_calls) {
CBMFileResult *r =
extract("package main\nimport \"fmt\"\nfunc main() { fmt.Println(\"hello\") }\n",
CBM_LANG_GO, "t", "main.go");
ASSERT_NOT_NULL(r);
ASSERT_FALSE(r->has_error);
ASSERT(has_call(r, "fmt.Println"));
cbm_free_result(r);
PASS();
}
TEST(python_imports) {
CBMFileResult *r =
extract("import os\nfrom sys import argv\nfrom collections import defaultdict\n",
CBM_LANG_PYTHON, "t", "main.py");
ASSERT_NOT_NULL(r);
ASSERT_FALSE(r->has_error);
ASSERT_GT(r->imports.count, 0);
cbm_free_result(r);
PASS();
}
TEST(js_imports) {
CBMFileResult *r = extract("import React from 'react';\nimport { useState } from "
"'react';\nconst fs = require('fs');\n",
CBM_LANG_JAVASCRIPT, "t", "app.js");
ASSERT_NOT_NULL(r);
ASSERT_FALSE(r->has_error);
ASSERT_GT(r->imports.count, 0);
cbm_free_result(r);
PASS();
}
TEST(go_imports) {
CBMFileResult *r =
extract("package main\n\nimport \"fmt\"\nimport (\n \"os\"\n net \"net/http\"\n)\n",
CBM_LANG_GO, "t", "main.go");
ASSERT_NOT_NULL(r);
ASSERT_FALSE(r->has_error);
ASSERT_GT(r->imports.count, 0);
ASSERT(has_import(r, "fmt"));
cbm_free_result(r);
PASS();
}
TEST(java_imports) {
CBMFileResult *r = extract(
"import java.util.List;\nimport java.util.ArrayList;\nimport static java.lang.Math.PI;\n"
"public class Foo {}\n",
CBM_LANG_JAVA, "t", "Foo.java");
ASSERT_NOT_NULL(r);
ASSERT_FALSE(r->has_error);
ASSERT_GT(r->imports.count, 0);
ASSERT(has_import(r, "java.util.List"));
cbm_free_result(r);
PASS();
}
TEST(rust_imports) {
CBMFileResult *r = extract(
"use std::collections::HashMap;\nuse std::io::{self, Write};\nuse serde::Serialize;\n"
"fn main() {}\n",
CBM_LANG_RUST, "t", "main.rs");
ASSERT_NOT_NULL(r);
ASSERT_FALSE(r->has_error);
ASSERT_GT(r->imports.count, 0);
ASSERT(has_import(r, "std::collections::HashMap"));
cbm_free_result(r);
PASS();
}
TEST(c_imports) {
CBMFileResult *r = extract("#include <stdio.h>\n#include <stdlib.h>\n#include "
"\"mylib.h\"\n\nint main() { return 0; }\n",
CBM_LANG_C, "t", "main.c");
ASSERT_NOT_NULL(r);
ASSERT_FALSE(r->has_error);
ASSERT_GT(r->imports.count, 0);
ASSERT(has_import(r, "stdio.h"));
cbm_free_result(r);
PASS();
}
TEST(ruby_imports) {
CBMFileResult *r = extract(
"require 'json'\nrequire 'net/http'\nrequire_relative 'helpers'\n\nclass Foo; end\n",
CBM_LANG_RUBY, "t", "app.rb");
ASSERT_NOT_NULL(r);
ASSERT_FALSE(r->has_error);
ASSERT_GT(r->imports.count, 0);
ASSERT(has_import(r, "json"));
cbm_free_result(r);
PASS();
}
TEST(lua_imports) {
CBMFileResult *r = extract("local json = require(\"dkjson\")\nlocal http = "
"require(\"socket.http\")\n\nlocal function greet() end\n",
CBM_LANG_LUA, "t", "main.lua");
ASSERT_NOT_NULL(r);
ASSERT_FALSE(r->has_error);
ASSERT_GT(r->imports.count, 0);
ASSERT(has_import(r, "dkjson"));
cbm_free_result(r);
PASS();
}
TEST(import_stress_go) {
/* Stress test: 5,000 single-line Go imports.
* Verifies O(N) behaviour — would hang indefinitely with the O(N²) loop. */
const int N = 5000;
/* Each line: import "pkg/NNNNN"\n = ~20 chars; total ~100KB */
int buf_size = N * 24 + 64;
char *src = malloc((size_t)buf_size);
ASSERT_NOT_NULL(src);
int pos = 0;
pos += snprintf(src + pos, (size_t)(buf_size - pos), "package stress\n");
for (int k = 0; k < N; k++) {
pos += snprintf(src + pos, (size_t)(buf_size - pos), "import \"pkg/%05d\"\n", k);
}
CBMFileResult *r = extract(src, CBM_LANG_GO, "t", "stress.go");
free(src);
ASSERT_NOT_NULL(r);
ASSERT_FALSE(r->has_error);
ASSERT_EQ(r->imports.count, N);
cbm_free_result(r);
PASS();
}
/* ═══════════════════════════════════════════════════════════════════
* Embedded-language import extraction
* Host grammars (Svelte, Vue, HTML, Astro) keep <script> bodies as
* raw_text — the embedded-imports walker re-parses each block with the
* JS grammar so the standard ES import extractor sees real
* import_statement nodes.
* ═══════════════════════════════════════════════════════════════════ */
TEST(svelte_imports_basic) {
/* Default import + named imports + namespace import */
CBMFileResult *r = extract("<script>\n"
"import Foo from './Foo.svelte';\n"
"import { bar, baz } from '../lib/utils';\n"
"import * as helpers from './helpers';\n"
"export let value = 42;\n"
"</script>\n"
"<h1>Hello {value}</h1>\n",
CBM_LANG_SVELTE, "t", "Comp.svelte");
ASSERT_NOT_NULL(r);
ASSERT_FALSE(r->has_error);
ASSERT_GTE(r->imports.count, 3);
ASSERT(has_import(r, "Foo.svelte"));
ASSERT(has_import(r, "lib/utils"));
ASSERT(has_import(r, "helpers"));
cbm_free_result(r);
PASS();
}
TEST(svelte_imports_no_script) {
/* .svelte with no <script> block must not crash, 0 imports */
CBMFileResult *r = extract("<h1>Static page</h1>\n"
"<p>No script here.</p>\n",
CBM_LANG_SVELTE, "t", "Static.svelte");
ASSERT_NOT_NULL(r);
ASSERT_FALSE(r->has_error);
ASSERT_EQ(r->imports.count, 0);
cbm_free_result(r);
PASS();
}
TEST(vue_imports_basic) {
/* Vue SFC: same document→script_element→raw_text AST structure */
CBMFileResult *r = extract("<template><div>{{ msg }}</div></template>\n"
"<script>\n"
"import MyComp from './MyComp.vue';\n"
"import { ref } from 'vue';\n"
"export default { name: 'App' };\n"
"</script>\n",
CBM_LANG_VUE, "t", "App.vue");
ASSERT_NOT_NULL(r);
ASSERT_FALSE(r->has_error);
ASSERT_GTE(r->imports.count, 2);
ASSERT(has_import(r, "MyComp.vue"));
ASSERT(has_import(r, "vue"));
cbm_free_result(r);
PASS();
}
TEST(html_imports_basic) {
/* Plain HTML with inline ES module imports — same generic walker. */
CBMFileResult *r = extract("<!DOCTYPE html><html><head>\n"
"<script type=\"module\">\n"
"import { renderApp } from './app.js';\n"
"import * as utils from './utils.js';\n"
"renderApp();\n"
"</script>\n"
"</head><body></body></html>\n",
CBM_LANG_HTML, "t", "index.html");
ASSERT_NOT_NULL(r);
ASSERT_FALSE(r->has_error);
ASSERT_GTE(r->imports.count, 2);
ASSERT(has_import(r, "app.js"));
ASSERT(has_import(r, "utils.js"));
cbm_free_result(r);
PASS();
}
/* ═══════════════════════════════════════════════════════════════════
* config_extraction_test.go ports (25 tests)
* ═══════════════════════════════════════════════════════════════════ */
/* --- TOML (8 tests) --- */
TEST(toml_basic_table_and_pair) {
CBMFileResult *r = extract("[database]\nhost = \"localhost\"\nport = 5432\n", CBM_LANG_TOML,
"t", "config.toml");
ASSERT_NOT_NULL(r);
ASSERT_FALSE(r->has_error);
ASSERT_EQ(count_defs_with_label(r, "Class"), 1);
ASSERT(has_def(r, "Class", "database"));
ASSERT_GTE(count_defs_with_label(r, "Variable"), 2);
ASSERT(has_def(r, "Variable", "host"));
ASSERT(has_def(r, "Variable", "port"));
cbm_free_result(r);
PASS();
}
TEST(toml_nested_table) {
CBMFileResult *r = extract("[server.http]\nport = 8080\n", CBM_LANG_TOML, "t", "config.toml");
ASSERT_NOT_NULL(r);
ASSERT_FALSE(r->has_error);
ASSERT_GTE(count_defs_with_label(r, "Class"), 1);
cbm_free_result(r);
PASS();
}
TEST(toml_table_array_element) {
CBMFileResult *r = extract("[[servers]]\nname = \"alpha\"\n[[servers]]\nname = \"beta\"\n",
CBM_LANG_TOML, "t", "config.toml");
ASSERT_NOT_NULL(r);
ASSERT_FALSE(r->has_error);
ASSERT_GTE(count_defs_with_label(r, "Class"), 2);
ASSERT_GTE(count_defs_with_label(r, "Variable"), 2);
cbm_free_result(r);
PASS();
}
TEST(toml_dotted_key) {
CBMFileResult *r =
extract("database.host = \"localhost\"\n", CBM_LANG_TOML, "t", "config.toml");
ASSERT_NOT_NULL(r);
ASSERT_FALSE(r->has_error);
ASSERT_GTE(count_defs_with_label(r, "Variable"), 1);
cbm_free_result(r);
PASS();
}
TEST(toml_quoted_key) {
CBMFileResult *r = extract("\"unusual-key\" = \"value\"\n", CBM_LANG_TOML, "t", "config.toml");
ASSERT_NOT_NULL(r);
ASSERT_FALSE(r->has_error);
ASSERT_GTE(count_defs_with_label(r, "Variable"), 1);
cbm_free_result(r);
PASS();
}
TEST(toml_empty_table) {
CBMFileResult *r = extract("[empty]\n", CBM_LANG_TOML, "t", "config.toml");
ASSERT_NOT_NULL(r);
ASSERT_FALSE(r->has_error);
ASSERT_EQ(count_defs_with_label(r, "Class"), 1);
ASSERT(has_def(r, "Class", "empty"));
ASSERT_EQ(count_defs_with_label(r, "Variable"), 0);
cbm_free_result(r);
PASS();
}
TEST(toml_comments_only) {
CBMFileResult *r =
extract("# just a comment\n# another comment\n", CBM_LANG_TOML, "t", "config.toml");
ASSERT_NOT_NULL(r);
ASSERT_FALSE(r->has_error);
ASSERT_EQ(count_defs_with_label(r, "Class"), 0);
ASSERT_EQ(count_defs_with_label(r, "Variable"), 0);
cbm_free_result(r);
PASS();
}
TEST(toml_boolean_and_integer_values) {
CBMFileResult *r =
extract("enabled = true\ncount = 42\nname = \"test\"\n", CBM_LANG_TOML, "t", "config.toml");
ASSERT_NOT_NULL(r);
ASSERT_FALSE(r->has_error);
ASSERT_GTE(count_defs_with_label(r, "Variable"), 3);
cbm_free_result(r);
PASS();
}
/* --- INI (4 tests) --- */
TEST(ini_basic_section_and_setting) {
CBMFileResult *r =
extract("[database]\nhost = localhost\nport = 5432\n", CBM_LANG_INI, "t", "config.ini");
ASSERT_NOT_NULL(r);
ASSERT_FALSE(r->has_error);
ASSERT_GTE(count_defs_with_label(r, "Class"), 1);
ASSERT_GTE(count_defs_with_label(r, "Variable"), 2);
cbm_free_result(r);
PASS();
}
TEST(ini_multiple_sections) {
CBMFileResult *r = extract("[section1]\nkey1 = val1\n[section2]\nkey2 = val2\n", CBM_LANG_INI,
"t", "config.ini");
ASSERT_NOT_NULL(r);
ASSERT_FALSE(r->has_error);
ASSERT_GTE(count_defs_with_label(r, "Class"), 2);
ASSERT_GTE(count_defs_with_label(r, "Variable"), 2);
cbm_free_result(r);
PASS();
}
TEST(ini_global_keys) {
CBMFileResult *r = extract("key1 = value1\nkey2 = value2\n", CBM_LANG_INI, "t", "config.ini");
ASSERT_NOT_NULL(r);
ASSERT_FALSE(r->has_error);
ASSERT_EQ(count_defs_with_label(r, "Class"), 0);
ASSERT_GTE(count_defs_with_label(r, "Variable"), 2);
cbm_free_result(r);
PASS();
}
TEST(ini_comments) {
CBMFileResult *r = extract("; comment\n# another comment\n[section]\nkey = val\n", CBM_LANG_INI,
"t", "config.ini");
ASSERT_NOT_NULL(r);
ASSERT_FALSE(r->has_error);
ASSERT_GTE(count_defs_with_label(r, "Class"), 1);
ASSERT_GTE(count_defs_with_label(r, "Variable"), 1);
cbm_free_result(r);
PASS();
}
/* --- JSON (5 tests) --- */
TEST(json_basic_pair) {
CBMFileResult *r =
extract("{\"host\": \"localhost\", \"port\": 5432}", CBM_LANG_JSON, "t", "config.json");
ASSERT_NOT_NULL(r);
ASSERT_FALSE(r->has_error);
ASSERT_GTE(count_defs_with_label(r, "Variable"), 2);
ASSERT(has_def(r, "Variable", "host"));
ASSERT(has_def(r, "Variable", "port"));
cbm_free_result(r);
PASS();
}
TEST(json_nested_object) {
CBMFileResult *r = extract("{\"database\": {\"host\": \"localhost\", \"port\": 5432}}",
CBM_LANG_JSON, "t", "config.json");
ASSERT_NOT_NULL(r);
ASSERT_FALSE(r->has_error);
ASSERT_GTE(count_defs_with_label(r, "Variable"), 3);
cbm_free_result(r);
PASS();
}
TEST(json_empty_object) {
CBMFileResult *r = extract("{}", CBM_LANG_JSON, "t", "config.json");
ASSERT_NOT_NULL(r);
ASSERT_FALSE(r->has_error);
ASSERT_EQ(count_defs_with_label(r, "Variable"), 0);
cbm_free_result(r);
PASS();
}
TEST(json_boolean_null_values) {
CBMFileResult *r = extract("{\"enabled\": true, \"value\": null, \"name\": \"test\"}",
CBM_LANG_JSON, "t", "config.json");
ASSERT_NOT_NULL(r);
ASSERT_FALSE(r->has_error);
ASSERT_GTE(count_defs_with_label(r, "Variable"), 3);
cbm_free_result(r);
PASS();
}
TEST(json_package_json_deps) {
CBMFileResult *r =
extract("{\"name\":\"pkg\",\"dependencies\":{\"express\":\"^4.0\",\"lodash\":\"^4.17\"}}",
CBM_LANG_JSON, "t", "package.json");
ASSERT_NOT_NULL(r);
ASSERT_FALSE(r->has_error);
ASSERT_GTE(count_defs_with_label(r, "Variable"), 4);
ASSERT(has_def(r, "Variable", "name"));
ASSERT(has_def(r, "Variable", "dependencies"));
ASSERT(has_def(r, "Variable", "express"));
ASSERT(has_def(r, "Variable", "lodash"));
cbm_free_result(r);
PASS();
}
/* --- XML (4 tests) --- */
TEST(xml_basic_element) {
CBMFileResult *r = extract(
"<?xml version=\"1.0\"?><config><database><host>localhost</host></database></config>",
CBM_LANG_XML, "t", "config.xml");
ASSERT_NOT_NULL(r);
ASSERT_FALSE(r->has_error);
ASSERT_GTE(count_defs_with_label(r, "Class"), 3);
ASSERT(has_def(r, "Class", "config"));
ASSERT(has_def(r, "Class", "database"));
ASSERT(has_def(r, "Class", "host"));
cbm_free_result(r);
PASS();
}
TEST(xml_self_closing_tag) {
CBMFileResult *r =
extract("<?xml version=\"1.0\"?><config><feature enabled=\"true\"/></config>", CBM_LANG_XML,
"t", "config.xml");
ASSERT_NOT_NULL(r);
ASSERT_FALSE(r->has_error);
ASSERT_GTE(count_defs_with_label(r, "Class"), 2);
cbm_free_result(r);
PASS();
}
TEST(xml_empty_document) {
CBMFileResult *r = extract("<?xml version=\"1.0\"?><root/>", CBM_LANG_XML, "t", "config.xml");
ASSERT_NOT_NULL(r);
ASSERT_FALSE(r->has_error);
ASSERT_GTE(count_defs_with_label(r, "Class"), 1);
cbm_free_result(r);
PASS();
}
TEST(xml_multiple_children) {
CBMFileResult *r =
extract("<?xml version=\"1.0\"?><servers><server/><server/><server/></servers>",
CBM_LANG_XML, "t", "config.xml");
ASSERT_NOT_NULL(r);
ASSERT_FALSE(r->has_error);
ASSERT_GTE(count_defs_with_label(r, "Class"), 4); /* servers + 3x server */
cbm_free_result(r);
PASS();
}
/* --- Markdown (4 tests) --- */
TEST(markdown_atx_headings) {
CBMFileResult *r =
extract("# Title\n## Section\n### Subsection\n", CBM_LANG_MARKDOWN, "t", "README.md");
ASSERT_NOT_NULL(r);
ASSERT_FALSE(r->has_error);
ASSERT_GTE(count_defs_with_label(r, "Section"), 3);
ASSERT_EQ(count_defs_with_label(r, "Class"), 0); /* Markdown: Section, not Class */
cbm_free_result(r);
PASS();
}
TEST(markdown_setext_headings) {
CBMFileResult *r =
extract("Title\n=====\nSection\n------\n", CBM_LANG_MARKDOWN, "t", "README.md");
ASSERT_NOT_NULL(r);
ASSERT_FALSE(r->has_error);
ASSERT_GTE(count_defs_with_label(r, "Section"), 2);
cbm_free_result(r);
PASS();
}
TEST(markdown_heading_content) {
CBMFileResult *r = extract("# Installation Guide\n## Prerequisites\n## Setup\n",
CBM_LANG_MARKDOWN, "t", "README.md");
ASSERT_NOT_NULL(r);
ASSERT_FALSE(r->has_error);
ASSERT_GTE(count_defs_with_label(r, "Section"), 3);
ASSERT(has_def(r, "Section", "Installation Guide"));
ASSERT(has_def(r, "Section", "Prerequisites"));
ASSERT(has_def(r, "Section", "Setup"));
cbm_free_result(r);
PASS();
}
TEST(markdown_no_headings) {
CBMFileResult *r =
extract("Just a paragraph\n\nAnother paragraph\n", CBM_LANG_MARKDOWN, "t", "README.md");
ASSERT_NOT_NULL(r);
ASSERT_FALSE(r->has_error);
ASSERT_EQ(count_defs_with_label(r, "Section"), 0);
cbm_free_result(r);
PASS();
}
/* ═══════════════════════════════════════════════════════════════════
* Python __init__.py Module QN collision regression
* ═══════════════════════════════════════════════════════════════════ */
TEST(python_init_module_qn_not_collide_with_folder) {
/* Bug: __init__.py Module QN was identical to the Folder QN for the
* same directory, causing the Folder node to be overwritten when the
* Module was upserted. The Module QN must contain "__init__" to
* distinguish it from the Folder QN. */
CBMFileResult *r = extract("class Config:\n DEBUG = True\n\ndef setup():\n pass\n",
CBM_LANG_PYTHON, "proj", "mypackage/__init__.py");
ASSERT_NOT_NULL(r);
ASSERT_FALSE(r->has_error);
/* Module node must exist */
ASSERT_GTE(r->defs.count, 1);
ASSERT_STR_EQ(r->defs.items[0].label, "Module");
/* Module QN must contain __init__ (not be stripped to just "proj.mypackage") */
ASSERT_NOT_NULL(r->module_qn);
ASSERT_NOT_NULL(strstr(r->module_qn, "__init__"));
/* But symbols inside __init__.py should NOT have __init__ in their QN */
int found_config = 0;
for (int i = 0; i < r->defs.count; i++) {
if (strcmp(r->defs.items[i].name, "Config") == 0) {
ASSERT_NOT_NULL(r->defs.items[i].qualified_name);
/* Should be "proj.mypackage.Config", NOT "proj.mypackage.__init__.Config" */
ASSERT_STR_EQ(r->defs.items[i].qualified_name, "proj.mypackage.Config");
found_config = 1;
}
}
ASSERT_EQ(found_config, 1);
cbm_free_result(r);
PASS();
}
TEST(python_init_nested_module_qn) {
/* Deeply nested __init__.py — same collision must not happen */
CBMFileResult *r = extract("def greet():\n return 'hello'\n", CBM_LANG_PYTHON, "proj",
"docker-images/cloud-runs/bq-sync-api/__init__.py");
ASSERT_NOT_NULL(r);
ASSERT_FALSE(r->has_error);
ASSERT_NOT_NULL(r->module_qn);
/* Must contain __init__ to not collide with Folder QN */
ASSERT_NOT_NULL(strstr(r->module_qn, "__init__"));
cbm_free_result(r);
PASS();
}
TEST(js_index_module_qn_not_collide_with_folder) {
/* Same bug for JS/TS index.ts files */
CBMFileResult *r = extract("export function App() { return null; }\n", CBM_LANG_TYPESCRIPT,
"proj", "src/components/index.ts");
ASSERT_NOT_NULL(r);
ASSERT_FALSE(r->has_error);
ASSERT_NOT_NULL(r->module_qn);
/* Must contain "index" to not collide with Folder QN */
ASSERT_NOT_NULL(strstr(r->module_qn, "index"));
cbm_free_result(r);
PASS();
}
TEST(python_regular_module_qn_unchanged) {
/* Non-__init__.py Python files should be unaffected */
CBMFileResult *r =
extract("def helper():\n pass\n", CBM_LANG_PYTHON, "proj", "mypackage/utils.py");
ASSERT_NOT_NULL(r);
ASSERT_FALSE(r->has_error);
ASSERT_NOT_NULL(r->module_qn);
/* Regular module QN should not contain __init__ or index */
ASSERT_STR_EQ(r->module_qn, "proj.mypackage.utils");
cbm_free_result(r);
PASS();
}
/* Find a definition by name; returns the item or NULL. */
static const CBMDefinition *find_def_by_name(CBMFileResult *r, const char *name) {
for (int i = 0; i < r->defs.count; i++) {
if (r->defs.items[i].name && strcmp(r->defs.items[i].name, name) == 0) {
return &r->defs.items[i];
}
}
return NULL;
}
static int decorators_contain(const CBMDefinition *d, const char *needle) {
if (!d || !d->decorators) {
return 0;
}
for (int i = 0; d->decorators[i]; i++) {
if (strstr(d->decorators[i], needle)) {
return 1;
}
}
return 0;
}
/* Issue #382: Java Method nodes had empty decorators / signature. */
TEST(extract_java_method_annotations_issue382) {
CBMFileResult *r = extract("public class C {\n"
" @GetMapping(\"/x\")\n"
" public String cmd(String c) { return c; }\n"
"}\n",
CBM_LANG_JAVA, "t", "C.java");
ASSERT_NOT_NULL(r);
ASSERT_FALSE(r->has_error);
const CBMDefinition *m = find_def_by_name(r, "cmd");
ASSERT_NOT_NULL(m);
ASSERT(decorators_contain(m, "GetMapping"));
ASSERT_NOT_NULL(m->signature);
ASSERT(m->signature[0] != '\0');
cbm_free_result(r);
PASS();
}
/* Find an in-body call by its raw callee text; returns the call or NULL. */
static const CBMCall *find_call_by_callee(CBMFileResult *r, const char *callee) {
for (int i = 0; i < r->calls.count; i++) {
if (r->calls.items[i].callee_name && strcmp(r->calls.items[i].callee_name, callee) == 0) {
return &r->calls.items[i];
}
}
return NULL;
}
/* Reproduce-first: Java module QN must derive from the CONTAINING DIRECTORY, not
* the filename stem, so a top-level class `Outer` in `Outer.java` is `t.Outer`,
* NOT the doubled `t.Outer.Outer`. The nested method def QN must also equal the
* QN the textual calls-enclosing path records for an in-body call (the
* lsp_resolve join keys on exact caller_qn == enclosing_func_qn equality). */
TEST(extract_java_no_double_class_qn) {
CBMFileResult *r = extract("class Outer {\n"
" int helper(int x) { return x + 2; }\n"
" class Inner {\n"
" int run(int v) { return helper(v); }\n"
" }\n"
"}\n",
CBM_LANG_JAVA, "t", "Outer.java");
ASSERT_NOT_NULL(r);
ASSERT_FALSE(r->has_error);
/* Module QN is the directory (root) → just the project. */
ASSERT_NOT_NULL(r->module_qn);
ASSERT_STR_EQ(r->module_qn, "t");
/* No def QN anywhere may double the top-level class name. */
for (int i = 0; i < r->defs.count; i++) {
const char *qn = r->defs.items[i].qualified_name;
if (qn) {
ASSERT_EQ(strstr(qn, "Outer.Outer"), NULL);
}
}
/* The nested class and its method carry the single-form QN. */
const CBMDefinition *outer = find_def_by_name(r, "Outer");
ASSERT_NOT_NULL(outer);
ASSERT_STR_EQ(outer->qualified_name, "t.Outer");
const CBMDefinition *run = find_def_by_name(r, "run");
ASSERT_NOT_NULL(run);
ASSERT_STR_EQ(run->qualified_name, "t.Outer.Inner.run");
/* The in-body call to helper() must be attributed to the SAME QN as the
* method def — this is the equality the LSP cross-resolution join relies on
* for nested classes (the lsp_outer_dispatch repro). */
const CBMCall *call = find_call_by_callee(r, "helper");
ASSERT_NOT_NULL(call);
ASSERT_NOT_NULL(call->enclosing_func_qn);
ASSERT_STR_EQ(call->enclosing_func_qn, run->qualified_name);
cbm_free_result(r);
PASS();
}
/* Reproduce-first: Go module QN must derive from the CONTAINING DIRECTORY
* (package), not the filename stem, so a type/method in `myapp/db/conn.go`
* belongs to module `proj.myapp.db` and is NOT polluted with the `.conn.`
* filename segment. */
TEST(extract_go_no_filename_in_module_qn) {
CBMFileResult *r = extract("package db\n\n"
"type Conn struct{}\n\n"
"func (c *Conn) Query() {}\n",
CBM_LANG_GO, "proj", "myapp/db/conn.go");
ASSERT_NOT_NULL(r);
ASSERT_FALSE(r->has_error);
/* Module is the directory `myapp/db`, NOT `myapp/db/conn`. */
ASSERT_NOT_NULL(r->module_qn);
ASSERT_STR_EQ(r->module_qn, "proj.myapp.db");
/* The type and method QNs must not contain the filename segment `.conn.`. */
const CBMDefinition *conn = find_def_by_name(r, "Conn");
ASSERT_NOT_NULL(conn);
ASSERT_STR_EQ(conn->qualified_name, "proj.myapp.db.Conn");
/* Go method nodes keep a FLAT QN (module + name) with a separate
* parent_class link to the receiver type — the QN must carry the
* directory-based module and NOT the `.conn.` filename segment. */
const CBMDefinition *query = find_def_by_name(r, "Query");
ASSERT_NOT_NULL(query);
ASSERT_STR_EQ(query->qualified_name, "proj.myapp.db.Query");
ASSERT_EQ(strstr(query->qualified_name, ".conn."), NULL);
/* The method's parent_class must match the type node QN (for DEFINES_METHOD). */
ASSERT_NOT_NULL(query->parent_class);
ASSERT_STR_EQ(query->parent_class, "proj.myapp.db.Conn");
cbm_free_result(r);
PASS();
}
/* Issue #213: large TS files were indexed as a File node with zero children. */
TEST(extract_large_ts_has_functions_issue213) {
enum { NFUNCS = 4000 };
size_t cap = (size_t)NFUNCS * 80 + 64;
char *src = (char *)malloc(cap);
ASSERT_NOT_NULL(src);
size_t off = 0;
for (int i = 0; i < NFUNCS; i++) {
off +=
(size_t)snprintf(src + off, cap - off,
"export function fn%d(a: number): number { return a + %d; }\n", i, i);
}
CBMFileResult *r =
cbm_extract_file(src, (int)off, CBM_LANG_TYPESCRIPT, "t", "big.ts", 0, NULL, NULL);
ASSERT_NOT_NULL(r);
int fns = count_defs_with_label(r, "Function");
ASSERT_GT(fns, 0); /* must not silently produce zero children */
cbm_free_result(r);
free(src);
PASS();
}
/* ═══════════════════════════════════════════════════════════════════
* Group: per-function complexity metrics (Tier A — local AST metrics)
*
* cbm_compute_complexity stamps each Function/Method with cyclomatic,
* cognitive, loop_count and loop_depth in the same tree-sitter walk that
* extracts the definition. loop_depth (max nested-loop depth) is the
* polynomial-degree proxy used as a queryable bottleneck signal.
* ═══════════════════════════════════════════════════════════════════ */
/* Return the first definition with the given name, or NULL. */
static const CBMDefinition *find_def(CBMFileResult *r, const char *name) {
for (int i = 0; i < r->defs.count; i++) {
if (strcmp(r->defs.items[i].name, name) == 0)
return &r->defs.items[i];
}
return NULL;
}
TEST(complexity_nested_loops_depth) {
CBMFileResult *r = extract("package p\n"
"func deepLoops() {\n"
" for i := 0; i < 10; i++ {\n"
" for j := 0; j < 10; j++ {\n"
" for k := 0; k < 10; k++ {\n"
" doWork()\n"
" }\n"
" }\n"
" }\n"
"}\n",
CBM_LANG_GO, "t", "deep.go");
ASSERT_NOT_NULL(r);
ASSERT_FALSE(r->has_error);
const CBMDefinition *d = find_def(r, "deepLoops");
ASSERT_NOT_NULL(d);
ASSERT_EQ(d->loop_depth, 3); /* three nested for-loops */
ASSERT_EQ(d->loop_count, 3);
cbm_free_result(r);
PASS();
}
TEST(complexity_loop_with_branch) {
CBMFileResult *r = extract("package p\n"
"func single() {\n"
" for i := 0; i < 10; i++ {\n"
" if i > 5 {\n"
" doWork()\n"
" }\n"
" }\n"
"}\n",
CBM_LANG_GO, "t", "single.go");
ASSERT_NOT_NULL(r);
ASSERT_FALSE(r->has_error);
const CBMDefinition *d = find_def(r, "single");
ASSERT_NOT_NULL(d);
ASSERT_EQ(d->loop_depth, 1);
ASSERT_EQ(d->loop_count, 1);
/* the nested `if` contributes a branch, so cyclomatic > 1 and the
* nesting-weighted cognitive score is non-zero. */
ASSERT_GT(d->complexity, 1);
ASSERT_GT(d->cognitive, 0);
cbm_free_result(r);
PASS();
}
TEST(complexity_flat_no_loops) {
CBMFileResult *r = extract("package p\n"
"func flat() {\n"
" doWork()\n"
" doMore()\n"
"}\n",
CBM_LANG_GO, "t", "flat.go");
ASSERT_NOT_NULL(r);
ASSERT_FALSE(r->has_error);
const CBMDefinition *d = find_def(r, "flat");
ASSERT_NOT_NULL(d);
ASSERT_EQ(d->loop_depth, 0);
ASSERT_EQ(d->loop_count, 0);
cbm_free_result(r);
PASS();
}
/* A linear-scan call (contains) inside a loop → hidden O(n^2) signal. */
TEST(complexity_linear_scan_in_loop) {
CBMFileResult *r = extract("package p\n"
"func scanInLoop(xs []int, t int) bool {\n"
" for i := 0; i < len(xs); i++ {\n"
" if contains(xs, t) {\n"
" return true\n"
" }\n"
" }\n"
" return false\n"
"}\n",
CBM_LANG_GO, "t", "scan.go");
ASSERT_NOT_NULL(r);
ASSERT_FALSE(r->has_error);
const CBMDefinition *d = find_def(r, "scanInLoop");
ASSERT_NOT_NULL(d);
ASSERT_GT(d->linear_scan_in_loop, 0); /* contains() called inside the for-loop */
cbm_free_result(r);
PASS();
}
/* Self-call inside a loop, not guarded by any conditional → recursion_in_loop
* and unguarded_recursion both set. */
TEST(complexity_recursion_in_loop_unguarded) {
CBMFileResult *r = extract("package p\n"
"func recurInLoop(n int) {\n"
" for i := 0; i < n; i++ {\n"
" recurInLoop(n - 1)\n"
" }\n"
"}\n",
CBM_LANG_GO, "t", "recur.go");
ASSERT_NOT_NULL(r);
ASSERT_FALSE(r->has_error);
const CBMDefinition *d = find_def(r, "recurInLoop");
ASSERT_NOT_NULL(d);
ASSERT_TRUE(d->is_recursive);
ASSERT_TRUE(d->recursion_in_loop);
ASSERT_TRUE(d->unguarded_recursion); /* no self-call inside a conditional */
cbm_free_result(r);
PASS();
}
/* Self-call inside an `if` (a base-case guard) → recursive but NOT unguarded. */
TEST(complexity_guarded_recursion) {
CBMFileResult *r = extract("package p\n"
"func guarded(n int) int {\n"
" if n > 0 {\n"
" return guarded(n - 1)\n"
" }\n"
" return 0\n"
"}\n",
CBM_LANG_GO, "t", "guarded.go");
ASSERT_NOT_NULL(r);
ASSERT_FALSE(r->has_error);
const CBMDefinition *d = find_def(r, "guarded");
ASSERT_NOT_NULL(d);
ASSERT_TRUE(d->is_recursive);
ASSERT_FALSE(d->recursion_in_loop);
ASSERT_FALSE(d->unguarded_recursion); /* self-call is guarded by `if n > 0` */
cbm_free_result(r);
PASS();
}
/* #599: super().save() inside a method named save is a parent-class call —
* the receiver is super(), never self — so it must NOT flag self-recursion.
* super()-ONLY fixture: no self.save() alongside, so the assertion cannot pass
* vacuously off a genuine self-call. */
TEST(complexity_super_only_not_recursive) {
CBMFileResult *r = extract("class B(A):\n"
" def save(self):\n"
" super().save()\n",
CBM_LANG_PYTHON, "t", "super_only.py");
ASSERT_NOT_NULL(r);
ASSERT_FALSE(r->has_error);
const CBMDefinition *d = find_def(r, "save");
ASSERT_NOT_NULL(d);
ASSERT_FALSE(d->is_recursive); /* parent-class call, not self-recursion */
ASSERT_FALSE(d->unguarded_recursion);
cbm_free_result(r);
PASS();
}
/* #599: a same-named call on an unrelated receiver (axios.get inside a
* function also named get) is delegation, not self-recursion. */
TEST(complexity_same_name_other_receiver_not_recursive) {
CBMFileResult *r = extract("function get(url) {\n"
" return axios.get(url);\n"
"}\n",
CBM_LANG_JAVASCRIPT, "t", "axios_get.js");
ASSERT_NOT_NULL(r);
ASSERT_FALSE(r->has_error);
const CBMDefinition *d = find_def(r, "get");
ASSERT_NOT_NULL(d);
ASSERT_FALSE(d->is_recursive); /* axios.get targets axios, not this fn */
ASSERT_FALSE(d->unguarded_recursion);
cbm_free_result(r);
PASS();
}
/* Guard: genuine self-recursion through self/this receivers still trips the
* detector after the receiver-aware narrowing (#599). */
TEST(complexity_self_receiver_still_recursive) {
/* Python: self.recur() — same object. */
CBMFileResult *r = extract("class C:\n"
" def recur(self, n):\n"
" if n > 0:\n"
" self.recur(n - 1)\n",
CBM_LANG_PYTHON, "t", "self_recur.py");
ASSERT_NOT_NULL(r);
ASSERT_FALSE(r->has_error);
const CBMDefinition *d = find_def(r, "recur");
ASSERT_NOT_NULL(d);
ASSERT_TRUE(d->is_recursive);
ASSERT_FALSE(d->unguarded_recursion); /* guarded by `if n > 0` */
cbm_free_result(r);
/* JS: this.step() — same object. */
r = extract("class C {\n"
" step(n) {\n"
" if (n > 0) { this.step(n - 1); }\n"
" }\n"
"}\n",
CBM_LANG_JAVASCRIPT, "t", "this_step.js");
ASSERT_NOT_NULL(r);
ASSERT_FALSE(r->has_error);
d = find_def(r, "step");
ASSERT_NOT_NULL(d);
ASSERT_TRUE(d->is_recursive);
ASSERT_FALSE(d->unguarded_recursion); /* guarded by `if (n > 0)` */
cbm_free_result(r);
PASS();
}
/* #599: chained receiver — self.obj.recur() inside recur targets self's FIELD
* obj, a different object. The whole receiver chain ("self.obj") must be
* compared, not just its first segment ("self"). */
TEST(complexity_chained_receiver_not_self) {
CBMFileResult *r = extract("class C:\n"
" def recur(self, n):\n"
" self.obj.recur(n)\n",
CBM_LANG_PYTHON, "t", "chained_recur.py");
ASSERT_NOT_NULL(r);
ASSERT_FALSE(r->has_error);
const CBMDefinition *d = find_def(r, "recur");
ASSERT_NOT_NULL(d);
ASSERT_FALSE(d->is_recursive); /* self.obj is not self */
ASSERT_FALSE(d->unguarded_recursion);
cbm_free_result(r);
PASS();
}
/* #599: Go method receiver — the enclosing def's own receiver identifier
* (`s` in `func (s *Store) save()`) is whitelisted dynamically from
* CBMDefinition.receiver, so s.save() still counts as self-recursion while
* s.backup.save() (a field's same-named method) does not. */
TEST(complexity_go_method_receiver_self_recursion) {
CBMFileResult *r = extract("package p\n"
"type Store struct{}\n"
"func (s *Store) save(n int) {\n"
" if n > 0 {\n"
" s.save(n - 1)\n"
" }\n"
"}\n",
CBM_LANG_GO, "t", "store.go");
ASSERT_NOT_NULL(r);
ASSERT_FALSE(r->has_error);
const CBMDefinition *d = find_def(r, "save");
ASSERT_NOT_NULL(d);
ASSERT_TRUE(d->is_recursive); /* s.save() == receiver s → self */
ASSERT_FALSE(d->unguarded_recursion); /* guarded by `if n > 0` */
cbm_free_result(r);
/* Same-named method on a field of the receiver: NOT self-recursion. */
r = extract("package p\n"
"type Store struct{ backup *Store }\n"
"func (s *Store) save(n int) {\n"
" s.backup.save(n)\n"
"}\n",
CBM_LANG_GO, "t", "store_backup.go");
ASSERT_NOT_NULL(r);
ASSERT_FALSE(r->has_error);
d = find_def(r, "save");
ASSERT_NOT_NULL(d);
ASSERT_FALSE(d->is_recursive); /* s.backup is not s */
ASSERT_FALSE(d->unguarded_recursion);
cbm_free_result(r);
PASS();
}
/* #876: the delegation/store bucket left open after #599 — same-named calls
* whose receiver is a CALL RESULT (_get_store().get), a module singleton
* (_default.check), or a local cursor inside a loop (cur.execute) are not
* self-recursion. All three are the reporter's exact v0.8.1 false positives;
* fixed by the receiver-aware narrowing (87091ed), guarded here so the
* bucket never regresses. */
TEST(complexity_delegation_receivers_not_recursive_issue876) {
/* Store wrapper: _get_store().get() inside get — receiver is the call
* result, a different object. */
CBMFileResult *r = extract("def _get_store():\n"
" return object()\n"
"\n"
"def get(collection, record_id):\n"
" rec = _get_store().get(collection, record_id)\n"
" return rec\n",
CBM_LANG_PYTHON, "t", "soil.py");
ASSERT_NOT_NULL(r);
ASSERT_FALSE(r->has_error);
const CBMDefinition *d = find_def(r, "get");
ASSERT_NOT_NULL(d);
ASSERT_FALSE(d->is_recursive); /* _get_store() result is not this fn */
ASSERT_FALSE(d->unguarded_recursion);
cbm_free_result(r);
/* Module-singleton delegation: _default.check() inside check. */
r = extract("class _Checker:\n"
" def check(self, app_id, tool_name):\n"
" return (True, \"ok\")\n"
"\n"
"_default = _Checker()\n"
"\n"
"def check(app_id, tool_name):\n"
" return _default.check(app_id, tool_name)\n",
CBM_LANG_PYTHON, "t", "gleipnir.py");
ASSERT_NOT_NULL(r);
ASSERT_FALSE(r->has_error);
/* The module-level wrapper (not the method) must stay clean. */
d = NULL;
for (int i = 0; i < r->defs.count; i++) {
if (r->defs.items[i].name && strcmp(r->defs.items[i].name, "check") == 0 &&
strcmp(r->defs.items[i].label, "Function") == 0) {
d = &r->defs.items[i];
}
}
ASSERT_NOT_NULL(d);
ASSERT_FALSE(d->is_recursive); /* _default is not this fn */
ASSERT_FALSE(d->unguarded_recursion);
cbm_free_result(r);
/* Same-named method on a local, called inside a loop (willow.nuke case):
* neither recursive nor recursion_in_loop. */
r = extract("def execute(conn, paths):\n"
" for p in paths:\n"
" cur = conn.cursor()\n"
" cur.execute(\"DELETE FROM t WHERE p = %s\", (p,))\n"
" return True\n",
CBM_LANG_PYTHON, "t", "nuke.py");
ASSERT_NOT_NULL(r);
ASSERT_FALSE(r->has_error);
d = find_def(r, "execute");
ASSERT_NOT_NULL(d);
ASSERT_FALSE(d->is_recursive); /* cur is not this fn */
ASSERT_FALSE(d->unguarded_recursion);
ASSERT_FALSE(d->recursion_in_loop);
cbm_free_result(r);
PASS();
}
/* Deep chained member access + parameter count structure smells. */
TEST(complexity_access_depth_and_params) {
CBMFileResult *r = extract("package p\n"
"func deepAccess(x Foo, a int, b int, c int) int {\n"
" return x.alpha.beta.gamma.delta\n"
"}\n",
CBM_LANG_GO, "t", "access.go");
ASSERT_NOT_NULL(r);
ASSERT_FALSE(r->has_error);
const CBMDefinition *d = find_def(r, "deepAccess");
ASSERT_NOT_NULL(d);
ASSERT_GT(d->max_access_depth, 2); /* x.alpha.beta.gamma.delta */
ASSERT_GTE(d->param_count, 3); /* x, a, b, c (grouping may vary) */
cbm_free_result(r);
PASS();
}
/* ═══════════════════════════════════════════════════════════════════
* Perl call-graph noise (#459 follow-up)
* ═══════════════════════════════════════════════════════════════════ */
/* Count calls whose callee_name is exactly `name` (has_call is substring). */
static int count_calls_exact(CBMFileResult *r, const char *name) {
int n = 0;
for (int i = 0; i < r->calls.count; i++) {
if (r->calls.items[i].callee_name && strcmp(r->calls.items[i].callee_name, name) == 0)
n++;
}
return n;
}
/* (b) A dotted config string must never be extracted as a callee. */
TEST(extract_perl_config_string_not_a_callee) {
CBMFileResult *r = extract("package C;\n"
"sub run {\n"
" my $cfg = { \"log4perl.appender.File.utf8\" => 1 };\n"
" helper();\n"
"}\n"
"sub helper { return 1; }\n"
"1;\n",
CBM_LANG_PERL, "t", "app.pl");
ASSERT_NOT_NULL(r);
ASSERT_FALSE(r->has_error);
/* No callee may contain a '.' (config/string tokens are rejected). */
for (int i = 0; i < r->calls.count; i++) {
ASSERT_TRUE(strchr(r->calls.items[i].callee_name, '.') == NULL);
}
/* (d) The genuine intra-file function call is still extracted. */
ASSERT_TRUE(count_calls_exact(r, "helper") >= 1);
cbm_free_result(r);
PASS();
}
/* (a) A Perl builtin call is extracted as a non-method callee. Suppression of
* the resulting CALLS edge happens in the resolver (see test_registry.c /
* end-to-end); extraction itself keeps the bare builtin token. */
TEST(extract_perl_builtin_call_is_function_not_method) {
CBMFileResult *r = extract("package B;\n"
"sub run {\n"
" my @x;\n"
" push @x, 1;\n"
" keys %h;\n"
"}\n"
"1;\n",
CBM_LANG_PERL, "t", "b.pl");
ASSERT_NOT_NULL(r);
ASSERT_FALSE(r->has_error);
/* push / keys are extracted (they are valid identifiers) ... */
ASSERT_TRUE(has_call(r, "push"));
/* ... and crucially are NOT flagged as method calls. */
for (int i = 0; i < r->calls.count; i++) {
ASSERT_FALSE(r->calls.items[i].is_method);
}
cbm_free_result(r);
PASS();
}
/* (c) An arrow/method call is extracted with is_method=true so the resolver
* can suppress generic short-name matching for it. */
TEST(extract_perl_method_call_flags_is_method) {
CBMFileResult *r = extract("package M;\n"
"sub run {\n"
" my $self = shift;\n"
" $self->commit();\n"
" $dbh->commit();\n"
" helper();\n"
"}\n"
"sub helper { return 1; }\n"
"1;\n",
CBM_LANG_PERL, "t", "m.pl");
ASSERT_NOT_NULL(r);
ASSERT_FALSE(r->has_error);
/* Every "commit" call is a method call (is_method set). */
int commit_calls = 0;
for (int i = 0; i < r->calls.count; i++) {
if (strcmp(r->calls.items[i].callee_name, "commit") == 0) {
commit_calls++;
ASSERT_TRUE(r->calls.items[i].is_method);
}
/* The genuine function call is NOT a method. */
if (strcmp(r->calls.items[i].callee_name, "helper") == 0) {
ASSERT_FALSE(r->calls.items[i].is_method);
}
}
ASSERT_TRUE(commit_calls >= 1);
/* (d) genuine intra-file function call still extracted. */
ASSERT_TRUE(count_calls_exact(r, "helper") >= 1);
cbm_free_result(r);
PASS();
}
/* Languages OUTSIDE the is_method flag set (only Perl and TS/JS/TSX set it) must
* be unaffected: a Go method call never sets is_method. */
TEST(extract_flag_exempt_method_call_not_flagged_is_method) {
CBMFileResult *r = extract("package m\n"
"func run(o Obj) { o.Commit(); helper() }\n",
CBM_LANG_GO, "t", "x.go");
ASSERT_NOT_NULL(r);
ASSERT_FALSE(r->has_error);
for (int i = 0; i < r->calls.count; i++) {
ASSERT_FALSE(r->calls.items[i].is_method);
}
cbm_free_result(r);
PASS();
}
/* TS/JS/TSX receiver-aware flag (#592/#606; same intent as the Perl flag above).
* A member call x.foo() with a non-this/super receiver is flagged is_method so
* the resolver can suppress a weak short-name match (`re.test()` must not bind a
* project `test`); a bare call is not flagged. */
TEST(extract_ts_member_call_flags_is_method) {
CBMFileResult *r = extract("const re = /^a+$/;\n"
"export function checkFormat(s: string) { return re.test(s); }\n"
"function helper() { return 1; }\n"
"export function run() { return helper(); }\n",
CBM_LANG_TYPESCRIPT, "t", "a.ts");
ASSERT_NOT_NULL(r);
ASSERT_FALSE(r->has_error);
int member = 0;
int bare = 0;
for (int i = 0; i < r->calls.count; i++) {
const char *cn = r->calls.items[i].callee_name;
if (strcmp(cn, "re.test") == 0) {
member++;
ASSERT_TRUE(r->calls.items[i].is_method);
}
if (strcmp(cn, "helper") == 0) {
bare++;
ASSERT_FALSE(r->calls.items[i].is_method);
}
}
ASSERT_TRUE(member >= 1); /* re.test() flagged */
ASSERT_TRUE(bare >= 1); /* helper() not flagged */
cbm_free_result(r);
PASS();
}
/* this/super receivers keep the enclosing-class target, where a weak
* namespace-proximity match is usually correct — so they are NOT flagged. A
* new_expression has no member receiver and is never flagged either. */
TEST(extract_ts_this_super_receiver_not_flagged) {
CBMFileResult *r = extract("class A extends B {\n"
" m() { this.helper(); super.render(); return new A(); }\n"
" helper() {}\n"
"}\n",
CBM_LANG_TYPESCRIPT, "t", "b.ts");
ASSERT_NOT_NULL(r);
ASSERT_FALSE(r->has_error);
/* Every call here has a self receiver (this/super) or is a new-expression,
* so NONE may be flagged. */
for (int i = 0; i < r->calls.count; i++) {
ASSERT_FALSE(r->calls.items[i].is_method);
}
/* Sanity: the this/super member calls were actually extracted. */
ASSERT_TRUE(has_call(r, "helper")); /* this.helper */
ASSERT_TRUE(has_call(r, "render")); /* super.render */
cbm_free_result(r);
PASS();
}
/* JS dialect behaves like TS (the flag is gated on the language set, not the
* dialect). Replaces the pre-#592 test that asserted JS never flags is_method. */
TEST(extract_js_member_call_flags_is_method) {
CBMFileResult *r =
extract("function run(o){ o.commit(); helper(); }\n", CBM_LANG_JAVASCRIPT, "t", "x.js");
ASSERT_NOT_NULL(r);
ASSERT_FALSE(r->has_error);
int member = 0;
int bare = 0;
for (int i = 0; i < r->calls.count; i++) {
const char *cn = r->calls.items[i].callee_name;
if (strcmp(cn, "o.commit") == 0) {
member++;
ASSERT_TRUE(r->calls.items[i].is_method);
}
if (strcmp(cn, "helper") == 0) {
bare++;
ASSERT_FALSE(r->calls.items[i].is_method);
}
}
ASSERT_TRUE(member >= 1); /* o.commit() flagged */
ASSERT_TRUE(bare >= 1); /* helper() not flagged */
cbm_free_result(r);
PASS();
}
/* #961: a C function whose body braces are split across #ifdef/#else
* branches (one open brace per branch, a single shared close) parses with
* an ERROR region on the raw source — both branches are present at once —
* and the defs walk silently dropped the function while its callers stayed
* (cbm_path_within_root, handle_process_kill). The preprocessed second
* pass (simplecpp picks one branch, same-file token lines stay aligned)
* must recover the definition with its original line. */
TEST(extract_c_ifdef_split_brace_fn_recovered_issue961) {
CBMFileResult *r = extract("static int a(void) { return 1; }\n"
"static int b(void) { return 2; }\n"
"int split_brace_fn(int x) {\n"
"#ifdef _WIN32\n"
" if (a() && b()) {\n"
"#else\n"
" if (a() || b()) {\n"
"#endif\n"
" x += 1;\n"
" }\n"
" return x;\n"
"}\n"
"int after_fn(int x) { return x; }\n",
CBM_LANG_C, "t", "split.c");
ASSERT_NOT_NULL(r);
const CBMDefinition *d = find_def(r, "split_brace_fn");
if (!d) {
fprintf(stderr, " [961] FAIL split_brace_fn dropped (defs walk lost the "
"#ifdef-split function)\n");
}
ASSERT_NOT_NULL(d);
ASSERT_EQ((int)d->start_line, 3);
/* Error recovery must stay localized: neighbours extract either way. */
ASSERT_NOT_NULL(find_def(r, "a"));
ASSERT_NOT_NULL(find_def(r, "after_fn"));
cbm_free_result(r);
PASS();
}
/* #961 inverse guard: a clean C file must not gain duplicate or phantom
* defs from the recovery path (it only engages on raw-parse ERROR regions). */
TEST(extract_c_clean_file_no_recovery_duplicates_issue961) {
CBMFileResult *r = extract("#ifdef _WIN32\n"
"static int w(void) { return 1; }\n"
"#else\n"
"static int u(void) { return 2; }\n"
"#endif\n"
"int use(int x) { return x; }\n",
CBM_LANG_C, "t", "clean.c");
ASSERT_NOT_NULL(r);
int use_count = 0;
for (int i = 0; i < r->defs.count; i++) {
if (r->defs.items[i].name && strcmp(r->defs.items[i].name, "use") == 0)
use_count++;
}
ASSERT_EQ(use_count, 1);
cbm_free_result(r);
PASS();
}
/* #668: walk_defs used a fixed `walk_defs_frame_t stack[4096]` — a ~160 KB
* C-stack frame that overflowed small thread stacks (the reporter's crash was in
* the "definitions pass" on a large SQL file), and whose `top < 4096` push guards
* SILENTLY DROPPED every top-level definition past 4096. The growable heap stack
* fixes both: a file with > 4096 top-level defs must extract ALL of them. RED
* before the fix (extracted count capped near 4096), GREEN after. */
TEST(walk_defs_no_truncation_over_4096_issue668) {
enum { N = 5000 };
/* N top-level Python defs → N Function defs, all direct module children, so
* walk_defs pushes all N children at once — the >4096 truncation site. */
size_t cap = (size_t)N * 24 + 1;
char *src = (char *)malloc(cap);
ASSERT_NOT_NULL(src);
size_t off = 0;
for (int i = 0; i < N; i++) {
off += (size_t)snprintf(src + off, cap - off, "def f%d(): pass\n", i);
}
CBMFileResult *r = extract(src, CBM_LANG_PYTHON, "t", "big.py");
ASSERT_NOT_NULL(r);
ASSERT_FALSE(r->has_error);
int nfuncs = count_defs_with_label(r, "Function");
ASSERT(nfuncs >= N); /* all N present — not truncated at the old 4096 cap */
cbm_free_result(r);
free(src);
PASS();
}
/* ═══════════════════════════════════════════════════════════════════
* Suite
* ═══════════════════════════════════════════════════════════════════ */
/* Rust: inline #[test]/#[tokio::test] functions must be marked is_test so the
* store.c `is_test != 1` filter excludes them from graph context. Detection is
* otherwise file-path-based (cbm_is_test_file), so test fns in a regular .rs
* file leak. (#855) */
TEST(extract_rust_test_attr_marks_is_test_issue855) {
CBMFileResult *r = extract("pub fn real_fn() {}\n"
"\n"
"#[test]\n"
"fn sync_test() {}\n"
"\n"
"#[tokio::test]\n"
"async fn async_test() {}\n",
CBM_LANG_RUST, "t", "src/lib.rs");
ASSERT_NOT_NULL(r);
ASSERT_FALSE(r->has_error);
int real = -1, sync = -1, asyn = -1;
for (int i = 0; i < r->defs.count; i++) {
const char *n = r->defs.items[i].name;
if (!n) {
continue;
}
if (strcmp(n, "real_fn") == 0) {
real = r->defs.items[i].is_test ? 1 : 0;
} else if (strcmp(n, "sync_test") == 0) {
sync = r->defs.items[i].is_test ? 1 : 0;
} else if (strcmp(n, "async_test") == 0) {
asyn = r->defs.items[i].is_test ? 1 : 0;
}
}
ASSERT(real >= 0 && sync >= 0 && asyn >= 0 && "all three fns extracted");
ASSERT(real == 0 && "real_fn is NOT a test");
ASSERT(sync == 1 && "#[test] fn is_test");
ASSERT(asyn == 1 && "#[tokio::test] fn is_test");
cbm_free_result(r);
PASS();
}
/* Reproduce-first (ms-typescript reallyLargeFile.ts, 2026-07-07): a file
* whose root node has hundreds of thousands of FLAT SIBLINGS (580k ////
* comment lines in the 3.5 MB fourslash fixture) hung extraction for over
* 15 minutes: walk_defs pushed children via index-based ts_node_child(i),
* which is O(i) per call in tree-sitter — O(n^2) per wide node (~1.7e11
* iterator steps on the real file; 100% of stack samples inside
* ts_node_child_iterator_next). The supervisor then killed the silent
* worker as a hang and, via the stale extraction marker, quarantined
* INNOCENT files on every retry.
*
* This fixture is an 80k-sibling flat file: quadratic child access needs
* minutes under ASan; the linear TSTreeCursor collection finishes in
* milliseconds. The 30 s bound has ~100x headroom over the fixed cost —
* RED on index-based child pushes, GREEN on the cursor walk. The def-count
* guard keeps the test honest: extraction must actually process the whole
* breadth, not skip it. */
/* Extract a wide-flat comment-sibling fixture of n lines (the exact shape of
* ms-typescript's reallyLargeFile.ts: hundreds of thousands of flat comment
* children under the root, plus sparse real defs so the breadth check cannot
* pass vacuously). Returns elapsed milliseconds; stores the def count. */
static long extract_wide_flat_ms(int n, int *out_defs) {
const size_t cap = (size_t)n * 24 + (size_t)8192; /* "// wide filler N\n" <= 24 chars */
char *src = malloc(cap);
if (!src) {
return -1;
}
size_t off = 0;
/* CONSTANT def count (10), independent of n: defs that scale WITH n make
* the fixture superlinear through the separate per-def sibling-scan cost
* (O(defs x siblings)) — on windows-CLANG64 ASan that pushed the ratio
* of LINEAR walk code to 43x. Ten spread-out defs keep the breadth check
* honest while the sibling-scan term stays 10 x n = linear. */
const int def_stride = n / 10;
for (int i = 0; i < n; i++) {
off += (size_t)snprintf(src + off, cap - off, "// wide filler %d\n", i);
if (i % def_stride == 0) {
off += (size_t)snprintf(src + off, cap - off, "var wide_a%d = %d;\n", i, i);
}
}
struct timespec a;
struct timespec b;
cbm_clock_gettime(CLOCK_MONOTONIC, &a);
CBMFileResult *r =
cbm_extract_file(src, (int)off, CBM_LANG_JAVASCRIPT, "proj", "wide.js", 0, NULL, NULL);
cbm_clock_gettime(CLOCK_MONOTONIC, &b);
free(src);
if (!r) {
return -1;
}
*out_defs = r->defs.count;
cbm_free_result(r);
return (b.tv_sec - a.tv_sec) * 1000L + (b.tv_nsec - a.tv_nsec) / 1000000L;
}
TEST(extract_wide_flat_file_is_linear) {
/* SCALING-RATIO guard: assert the COMPLEXITY CLASS, not a wall-clock
* bound. Index-based ts_node_child(i) child loops are O(i) per call —
* quadratic per wide node — and hung a 580k-sibling file for hours
* (ms-typescript reallyLargeFile.ts). An absolute time bound conflates
* machine speed with complexity: the gcc-13-ARM ASan CI leg runs this
* extraction ~200x slower than clang at the SAME (measured perfectly
* linear: 27.1s/54.2s/108.3s for 50k/100k/200k) complexity, and flunked
* a 30s bound on linear code. Growing the input 4x must grow the time
* ~4x when linear and ~16x when quadratic; the 10x bound splits those
* decisively on every toolchain. The 120ms floor keeps clock noise from
* mattering on fast machines. */
/* Growth and bound CALIBRATED FROM MEASUREMENT, not models. The ASan
* test build carries a large LINEAR per-line baseline (~31us/line on
* clang-macOS, ~540us/line on gcc-13-ARM) that dilutes small-growth
* ratios: at 8x growth the measured quadratic ratio was 22.4 — a 24x
* bound false-passed the known-quadratic pre-merge walk. At 20x growth
* the measured ratios are ~20x for linear code (both toolchains) and
* ~128x for the quadratic walk (clang-macOS) — bound 40 sits >=2x from
* both. The 120ms floor keeps clock noise irrelevant on fast hosts. */
enum { WF_SMALL = 20 * 1000, WF_BIG = 400 * 1000, WF_RATIO_MAX = 40, WF_FLOOR_MS = 120 };
int defs_small = 0;
int defs_big = 0;
long t_small = extract_wide_flat_ms(WF_SMALL, &defs_small);
long t_big = extract_wide_flat_ms(WF_BIG, &defs_big);
ASSERT_GTE(t_small, 0);
ASSERT_GTE(t_big, 0);
/* Anti-vacuous guard: the breadth was actually walked at both sizes. */
ASSERT_GTE(defs_small, 8);
ASSERT_GTE(defs_big, 8);
fprintf(stderr, " [wide-flat] t(%d)=%ldms t(%d)=%ldms\n", WF_SMALL, t_small, WF_BIG, t_big);
long base = t_small > WF_FLOOR_MS ? t_small : WF_FLOOR_MS;
if (t_big > WF_RATIO_MAX * base) {
char msg[160];
snprintf(msg, sizeof(msg),
"wide-flat scaling 20x input: %ldms -> %ldms (> %dx base %ldms) — "
"quadratic child access",
t_small, t_big, WF_RATIO_MAX, base);
FAIL(msg);
}
PASS();
}
SUITE(extraction) {
/* Initialize extraction library */
cbm_init();
/* Wide-flat-file linearity (ms-typescript hang) */
RUN_TEST(extract_wide_flat_file_is_linear);
/* Perl call-graph noise (#459 follow-up) */
RUN_TEST(extract_perl_config_string_not_a_callee);
RUN_TEST(extract_perl_builtin_call_is_function_not_method);
RUN_TEST(extract_perl_method_call_flags_is_method);
RUN_TEST(extract_flag_exempt_method_call_not_flagged_is_method);
RUN_TEST(extract_ts_member_call_flags_is_method);
RUN_TEST(extract_ts_this_super_receiver_not_flagged);
RUN_TEST(extract_js_member_call_flags_is_method);
/* R box-module imports + member calls */
RUN_TEST(extract_r_box_use_imports_issue218);
RUN_TEST(extract_r_dollar_call_issue219);
RUN_TEST(extract_ts_factory_object_methods_issue341);
RUN_TEST(extract_c_macros_issue375);
RUN_TEST(extract_cpp_macros_issue375);
RUN_TEST(extract_gdscript_issue186);
RUN_TEST(extract_powershell_issue35);
RUN_TEST(extract_luau_issue39);
RUN_TEST(extract_qml_issue42);
RUN_TEST(extract_cfscript_issue38);
RUN_TEST(extract_cfml_tag_issue38);
RUN_TEST(extract_helm_templates_issue338);
RUN_TEST(extract_helm_values_toplevel_issue338);
/* OOP */
RUN_TEST(java_class);
RUN_TEST(java_method);
RUN_TEST(java_interface);
RUN_TEST(java_class_extends_and_implements);
RUN_TEST(python_class_base_extracted_bare);
RUN_TEST(php_class);
RUN_TEST(php_function);
RUN_TEST(ruby_class);
RUN_TEST(ruby_module);
RUN_TEST(csharp_class);
RUN_TEST(csharp_interface);
RUN_TEST(swift_class);
RUN_TEST(kotlin_function);
RUN_TEST(kotlin_class);
RUN_TEST(scala_function);
RUN_TEST(scala_class);
RUN_TEST(dart_class);
RUN_TEST(groovy_class);
/* Systems */
RUN_TEST(rust_function);
RUN_TEST(rust_struct);
RUN_TEST(go_function);
RUN_TEST(go_struct);
RUN_TEST(go_interface);
RUN_TEST(zig_function);
RUN_TEST(c_function);
RUN_TEST(c_struct);
RUN_TEST(cpp_class);
/* Scripting */
RUN_TEST(python_function);
RUN_TEST(python_class);
RUN_TEST(js_function);
RUN_TEST(js_class);
RUN_TEST(ts_function);
RUN_TEST(ts_class);
RUN_TEST(lua_function);
RUN_TEST(bash_function);
RUN_TEST(perl_function);
RUN_TEST(r_function);
/* Functional */
RUN_TEST(elixir_function);
RUN_TEST(haskell_function);
RUN_TEST(ocaml_function);
RUN_TEST(erlang_function);
/* Markup/Config */
RUN_TEST(yaml_variables);
RUN_TEST(hcl_blocks);
RUN_TEST(sql_create_table);
RUN_TEST(dockerfile_stages);
/* Scientific */
RUN_TEST(matlab_function);
RUN_TEST(lean_function);
RUN_TEST(form_procedure);
RUN_TEST(wolfram_function);
RUN_TEST(magma_function);
/* v0.5 expansion */
RUN_TEST(fsharp_function);
RUN_TEST(julia_function);
RUN_TEST(elm_function);
RUN_TEST(nix_function);
RUN_TEST(fortran_function);
/* OOP/Systems variants */
RUN_TEST(swift_struct);
RUN_TEST(swift_simple_call);
RUN_TEST(swift_method_call);
RUN_TEST(swift_constructor_call);
RUN_TEST(swift_chained_call);
RUN_TEST(objc_interface);
RUN_TEST(objc_implementation);
RUN_TEST(dart_top_level_function);
RUN_TEST(rust_enum);
RUN_TEST(zig_struct);
RUN_TEST(cpp_function);
RUN_TEST(cpp_out_of_line_method_issue428);
RUN_TEST(cobol_paragraph);
RUN_TEST(verilog_module);
RUN_TEST(cuda_kernel);
RUN_TEST(python_decorator);
RUN_TEST(ts_interface);
RUN_TEST(tsx_component);
RUN_TEST(lua_table_method);
RUN_TEST(emacs_lisp_defun);
RUN_TEST(emacs_lisp_defvar);
RUN_TEST(haskell_data_type);
RUN_TEST(clojure_function);
/* Config/Markup */
RUN_TEST(html_elements);
RUN_TEST(sql_function);
RUN_TEST(meson_project);
RUN_TEST(css_rules);
RUN_TEST(scss_rules);
RUN_TEST(toml_basic);
RUN_TEST(cmake_function);
RUN_TEST(json_object);
RUN_TEST(protobuf_message);
RUN_TEST(graphql_type);
RUN_TEST(svelte_component);
RUN_TEST(vue_component);
RUN_TEST(glsl_shader);
RUN_TEST(vimscript_function);
/* Scientific extended */
RUN_TEST(matlab_parse);
RUN_TEST(matlab_call);
RUN_TEST(lean_parse);
RUN_TEST(lean_call);
RUN_TEST(lean_type_annotation_not_call);
RUN_TEST(form_parse);
RUN_TEST(form_call);
RUN_TEST(magma_procedure);
RUN_TEST(magma_parse);
RUN_TEST(magma_import);
RUN_TEST(magma_call);
RUN_TEST(magma_disambiguation);
RUN_TEST(wolfram_function_extended);
RUN_TEST(wolfram_call);
RUN_TEST(wolfram_caller_attribution);
RUN_TEST(c_caller_attribution);
RUN_TEST(cpp_out_of_line_method_caller_attribution);
RUN_TEST(cpp_out_of_line_ctor_dtor_caller_attribution);
RUN_TEST(wolfram_parse);
RUN_TEST(wolfram_import);
RUN_TEST(wolfram_nested_def);
/* cbm_test.go ports */
RUN_TEST(python_docstring);
RUN_TEST(go_function_extraction);
RUN_TEST(js_arrow_function);
/* language_failures_test.go ports */
RUN_TEST(commonlisp_defun);
RUN_TEST(commonlisp_multiple_functions);
RUN_TEST(commonlisp_defmacro);
RUN_TEST(makefile_rule_as_function);
RUN_TEST(makefile_multiple_targets);
RUN_TEST(makefile_variable_extraction);
RUN_TEST(vimscript_function_extraction);
RUN_TEST(vimscript_function_without_bang);
RUN_TEST(julia_function_extraction);
RUN_TEST(julia_function_with_args);
/* Cross-cutting */
RUN_TEST(python_calls);
RUN_TEST(python_iris_classMethodValue);
RUN_TEST(go_calls);
RUN_TEST(python_imports);
RUN_TEST(js_imports);
RUN_TEST(go_imports);
RUN_TEST(java_imports);
RUN_TEST(rust_imports);
RUN_TEST(c_imports);
RUN_TEST(ruby_imports);
RUN_TEST(lua_imports);
RUN_TEST(import_stress_go);
RUN_TEST(svelte_imports_basic);
RUN_TEST(svelte_imports_no_script);
RUN_TEST(vue_imports_basic);
RUN_TEST(html_imports_basic);
/* config_extraction_test.go ports */
RUN_TEST(toml_basic_table_and_pair);
RUN_TEST(toml_nested_table);
RUN_TEST(toml_table_array_element);
RUN_TEST(toml_dotted_key);
RUN_TEST(toml_quoted_key);
RUN_TEST(toml_empty_table);
RUN_TEST(toml_comments_only);
RUN_TEST(toml_boolean_and_integer_values);
RUN_TEST(ini_basic_section_and_setting);
RUN_TEST(ini_multiple_sections);
RUN_TEST(ini_global_keys);
RUN_TEST(ini_comments);
RUN_TEST(json_basic_pair);
RUN_TEST(json_nested_object);
RUN_TEST(json_empty_object);
RUN_TEST(json_boolean_null_values);
RUN_TEST(json_package_json_deps);
RUN_TEST(xml_basic_element);
RUN_TEST(xml_self_closing_tag);
RUN_TEST(xml_empty_document);
RUN_TEST(xml_multiple_children);
RUN_TEST(markdown_atx_headings);
RUN_TEST(markdown_setext_headings);
RUN_TEST(markdown_heading_content);
RUN_TEST(markdown_no_headings);
/* __init__.py / index.ts Module QN collision regression */
RUN_TEST(python_init_module_qn_not_collide_with_folder);
RUN_TEST(python_init_nested_module_qn);
RUN_TEST(js_index_module_qn_not_collide_with_folder);
RUN_TEST(python_regular_module_qn_unchanged);
RUN_TEST(extract_java_method_annotations_issue382);
RUN_TEST(extract_java_no_double_class_qn);
RUN_TEST(extract_go_no_filename_in_module_qn);
RUN_TEST(extract_large_ts_has_functions_issue213);
/* Per-function complexity metrics (Tier A) */
RUN_TEST(complexity_nested_loops_depth);
RUN_TEST(complexity_loop_with_branch);
RUN_TEST(complexity_flat_no_loops);
RUN_TEST(complexity_linear_scan_in_loop);
RUN_TEST(complexity_recursion_in_loop_unguarded);
RUN_TEST(complexity_guarded_recursion);
RUN_TEST(complexity_super_only_not_recursive);
RUN_TEST(complexity_same_name_other_receiver_not_recursive);
RUN_TEST(complexity_self_receiver_still_recursive);
RUN_TEST(complexity_chained_receiver_not_self);
RUN_TEST(complexity_go_method_receiver_self_recursion);
RUN_TEST(complexity_delegation_receivers_not_recursive_issue876);
RUN_TEST(complexity_access_depth_and_params);
RUN_TEST(extract_c_ifdef_split_brace_fn_recovered_issue961);
RUN_TEST(extract_c_clean_file_no_recovery_duplicates_issue961);
RUN_TEST(walk_defs_no_truncation_over_4096_issue668);
RUN_TEST(extract_rust_test_attr_marks_is_test_issue855);
cbm_shutdown();
}