/* * test_complexity.c — Complexity guard: superlinearity detection at tiny input * sizes, gated on DETERMINISTIC work counters — never time. * * Why this suite exists: the v0.9.0→v0.10.x indexing regression was a * files×corpus_defs coupling in cross-file LSP (#1669). Finding it took an * 11-corpus A/B across two release binaries, because nothing in CI could see * an O(n^2) forming. This suite makes that class of bug fail a unit test. * * Method — replicated independent modules: * Build a synthetic corpus of k INDEPENDENT module copies per language * (module i never references module j). Then every extensive quantity — * nodes, edges, and Σ per-file work — MUST grow linearly in k. Run the full * in-process pipeline at k and 2k and assert counter RATIOS: * * linear pipeline ratio ≈ 2 (gate: within [lo, hi]) * files×corpus bug ratio ≈ 4 (per-file work itself grows with k) * * Ratios expose the exponent at TINY sizes (dozens of files, seconds of * runtime): a counter that counts the quadratic term directly doubles its * growth per doubling regardless of absolute scale. No large corpora needed. * * Determinism doctrine (O9): a verdict must be a pure function of * (code, input). Work counters are sums over per-file work and do not depend * on scheduling; wall time does. Therefore ONLY counters gate. Throughput * (nodes/s, edges/s) is measured and written to a LOCAL report under private/ * as information for trend comparison — it never gates, and the report step is * skipped entirely under CBM_SKIP_PERF (starved fidelity legs would record * meaningless rates). * * Dynamic coverage: languages come from two providers, iterated over the full * CBM_LANG_COUNT enum — * 1. embedded templates below (the LSP-hybrid languages, where cross-file * machinery — and therefore files×corpus coupling risk — lives); * 2. auto-discovered fixture dirs tests/fixtures/complexity// * (drop files there when adding a language; no test edit needed). * Languages with neither provider are recorded in the report as skipped with * the reason "no complexity template": their extractors are per-file by * construction (grammar-only, no cross-file resolution), so the coupling this * suite hunts cannot arise from them; the shared passes they feed (registry, * similarity, semantic) are exercised by the template corpus. */ #include "test_framework.h" #include "test_helpers.h" #include "../src/foundation/compat.h" #include "../src/foundation/compat_fs.h" #include "../src/foundation/log.h" #include "../src/foundation/profile.h" #include "cbm.h" #include "discover/discover.h" #include "lang_specs.h" #include "lsp/py_lsp.h" #include "tree_sitter/api.h" #include "pipeline/pass_lsp_cross.h" #include "pipeline/pipeline.h" #include "pipeline/pipeline_internal.h" #include "store/store.h" #include "yyjson/yyjson.h" #include #include #include #include #include #include /* Tail-match scan counters (defined in pass_parallel.c, declared in * lsp_resolve.h — re-declared here to avoid pulling that header's statics). */ extern _Atomic uint64_t g_lsp_tail_lookups; extern _Atomic uint64_t g_lsp_tail_candidates; /* Infra resolver candidate counters (pass_k8s.c, pass_operator.c, * pass_shell.c, pass_terraform.c). */ extern _Atomic uint64_t g_k8s_candidates; extern _Atomic uint64_t g_op_candidates; extern _Atomic uint64_t g_sh_candidates; extern _Atomic uint64_t g_tf_candidates; extern _Atomic uint64_t g_dk_hub_edges; /* ── Corpus scale ────────────────────────────────────────────────────── * Small on purpose: the gate reads exponents from ratios, not magnitudes. * K_BASE modules vs 2*K_BASE modules, CX_FILES_PER_MOD files each, per * provider language. Total runtime target for the whole suite: seconds. */ enum { CX_K_BASE = 3, CX_FILES_PER_MOD = 5, /* Gate only when the base run produced enough work for the ratio to be * meaningful — a near-zero denominator would make the gate noise. The * false-guard audit rule applies: a separate assertion below proves the * counter is genuinely nonzero so this gate can never pass vacuously. */ CX_MIN_BASE_WORK = 40, /* The pipeline resolves sequentially below MIN_FILES_FOR_PARALLEL (50 * files, pipeline.c) and that path builds no shared registries — so both * legs of every corpus pair must exceed it or the gates measure the wrong * code path. The sequential path's own per-file registry work is bounded * by the 50-file ceiling and is deliberately out of scope here. */ CX_BIGPKG_FILES_PER_K = 20, CX_PYEXT_FILES_PER_MOD = 20, CX_PYEXT_DEFS_PER_FILE = 120, CX_PYEXT_PROBES_PER_FILE = 3, }; /* Linear growth bounds for a 2x input doubling. Fixed structural overhead * (Project/root nodes) pulls node ratios slightly under 2; template boundary * effects push edge ratios slightly around 2. A files×corpus coupling lands * at ~4 — far outside. */ #define CX_RATIO_LO 1.45 #define CX_RATIO_HI 2.75 /* ── Language templates ──────────────────────────────────────────────── * Each emitter writes file j of a module. Files reference file j-1 of the * SAME module (cross-file resolution work); modules never reference each * other (independence — the property the linearity gate rests on). */ static void cx_emit_java(FILE *f, int m, int j) { fprintf(f, "package mod%d;\n\npublic class C%d {\n", m, j); fprintf(f, " public int val%d(int x) {\n return x + %d;\n }\n", j, j); if (j > 0) { fprintf(f, " public int chain() {\n"); fprintf(f, " C%d prev = new C%d();\n", j - 1, j - 1); fprintf(f, " return prev.val%d(1) + val%d(2);\n }\n", j - 1, j); } else { fprintf(f, " public int chain() {\n return val0(2);\n }\n"); } fprintf(f, "}\n"); } static void cx_emit_python(FILE *f, int m, int j) { (void)m; if (j > 0) { fprintf(f, "import f%d\n\n", j - 1); } fprintf(f, "def val%d(x):\n return x + %d\n\n", j, j); if (j > 0) { fprintf(f, "def chain%d():\n return f%d.val%d(1) + val%d(2)\n", j, j - 1, j - 1, j); } else { fprintf(f, "def chain0():\n return val0(2)\n"); } } static void cx_emit_go(FILE *f, int m, int j) { fprintf(f, "package mod%d\n\n", m); fprintf(f, "func Val%d(x int) int {\n\treturn x + %d\n}\n\n", j, j); if (j > 0) { fprintf(f, "func Chain%d() int {\n\treturn Val%d(1) + Val%d(2)\n}\n", j, j - 1, j); } else { fprintf(f, "func Chain0() int {\n\treturn Val0(2)\n}\n"); } } static void cx_emit_ts(FILE *f, int m, int j) { (void)m; if (j > 0) { fprintf(f, "import { val%d } from \"./f%d\";\n\n", j - 1, j - 1); } fprintf(f, "export function val%d(x: number): number {\n return x + %d;\n}\n\n", j, j); if (j > 0) { fprintf(f, "export function chain%d(): number {\n return val%d(1) + val%d(2);\n}\n", j, j - 1, j); } else { fprintf(f, "export function chain0(): number {\n return val0(2);\n}\n"); } } typedef struct { CBMLanguage lang; const char *dirname; /* corpus subdir, doubles as the per-lang scope */ const char *file_prefix; const char *ext; void (*emit)(FILE *f, int m, int j); } CxTemplate; static const CxTemplate CX_TEMPLATES[] = { {CBM_LANG_JAVA, "javasrc", "C", ".java", cx_emit_java}, {CBM_LANG_PYTHON, "pysrc", "f", ".py", cx_emit_python}, {CBM_LANG_GO, "gosrc", "f", ".go", cx_emit_go}, {CBM_LANG_TYPESCRIPT, "tssrc", "f", ".ts", cx_emit_ts}, }; enum { CX_TEMPLATE_COUNT = sizeof(CX_TEMPLATES) / sizeof(CX_TEMPLATES[0]) }; /* Fixture-dir provider: tests/fixtures/complexity// — every file * and directory in it (dot-names aside) is copied verbatim into each module * dir. Lets a new language join the * guard by dropping fixtures, with no edit to this suite. */ static bool cx_fixture_dir_for(CBMLanguage lang, char *out, size_t cap) { const char *name = cbm_language_name(lang); if (!name || !name[0]) { return false; } snprintf(out, cap, "tests/fixtures/complexity/%s", name); cbm_dir_t *d = cbm_opendir(out); if (!d) { return false; } cbm_closedir(d); return true; } static int cx_copy_file(const char *src, const char *dst) { FILE *in = fopen(src, "rb"); if (!in) { return -1; } FILE *out = fopen(dst, "wb"); if (!out) { fclose(in); return -1; } char buf[4096]; size_t n; while ((n = fread(buf, 1, sizeof(buf), in)) > 0) { fwrite(buf, 1, n, out); } fclose(in); fclose(out); return 0; } /* A fixture entry into `dst`: a file, or a directory with its contents. */ static int cx_copy_entry(const char *src, const char *dst, bool is_dir) { if (!is_dir) { return cx_copy_file(src, dst); } if (th_mkdir_p(dst) != 0) { return -1; } cbm_dir_t *d = cbm_opendir(src); if (!d) { return -1; } int rc = 0; cbm_dirent_t *entry; while ((entry = cbm_readdir(d)) != NULL) { if (entry->name[0] == '.') { continue; } char from[1024]; char to[1024]; snprintf(from, sizeof(from), "%s/%s", src, entry->name); snprintf(to, sizeof(to), "%s/%s", dst, entry->name); if (cx_copy_entry(from, to, entry->is_dir) != 0) { rc = -1; } } cbm_closedir(d); return rc; } /* ── Corpus builder ──────────────────────────────────────────────────── */ static int cx_build_corpus(const char *root, int k_modules) { char dir[1024]; char path[1200]; for (int t = 0; t < CX_TEMPLATE_COUNT; t++) { const CxTemplate *tp = &CX_TEMPLATES[t]; for (int m = 0; m < k_modules; m++) { snprintf(dir, sizeof(dir), "%s/%s/mod%d", root, tp->dirname, m); if (th_mkdir_p(dir) != 0) { return -1; } for (int j = 0; j < CX_FILES_PER_MOD; j++) { snprintf(path, sizeof(path), "%s/%s%d%s", dir, tp->file_prefix, j, tp->ext); FILE *f = fopen(path, "w"); if (!f) { return -1; } tp->emit(f, m, j); fclose(f); } } } /* Fixture-dir providers: replicate each discovered language dir into * k module copies. Distinct paths make distinct modules/QNs, which is all * the independence argument needs. */ for (int lang = 0; lang < CBM_LANG_COUNT; lang++) { /* a templated language may add fixtures too (Go: the operator * corpus); they land in their own fx_ root */ char fixdir[512]; if (!cx_fixture_dir_for((CBMLanguage)lang, fixdir, sizeof(fixdir))) { continue; } cbm_dir_t *d = cbm_opendir(fixdir); if (!d) { continue; } cbm_dirent_t *entry; while ((entry = cbm_readdir(d)) != NULL) { if (entry->name[0] == '.') { continue; } for (int m = 0; m < k_modules; m++) { snprintf(dir, sizeof(dir), "%s/fx_%s/mod%d", root, cbm_language_name((CBMLanguage)lang), m); if (th_mkdir_p(dir) != 0) { continue; } char src[1024]; snprintf(src, sizeof(src), "%s/%s", fixdir, entry->name); snprintf(path, sizeof(path), "%s/%s", dir, entry->name); (void)cx_copy_entry(src, path, entry->is_dir); } } cbm_closedir(d); } return 0; } /* Corpus shape #2 — the growing shared package. Real monorepos concentrate * files in a few large packages (org..common, …), and the JVM filter * branch includes every def sharing the file's namespace — so per-file work * tracks PACKAGE size. A package whose file count scales with the corpus is * therefore the honest reproducer for the #1669 growth pattern that fully * independent modules cannot show: here every extensive quantity must STILL * be linear in k, while a namespace/module-scoped per-file registry build * goes quadratic. */ static int cx_build_bigpkg(const char *root, int k) { char dir[1024]; char path[1200]; snprintf(dir, sizeof(dir), "%s/bigsrc/bigpkg", root); if (th_mkdir_p(dir) != 0) { return -1; } int files = k * CX_BIGPKG_FILES_PER_K; for (int j = 0; j < files; j++) { snprintf(path, sizeof(path), "%s/B%d.java", dir, j); FILE *f = fopen(path, "w"); if (!f) { return -1; } fprintf(f, "package bigpkg;\n\npublic class B%d {\n", j); fprintf(f, " public int val%d(int x) {\n return x + %d;\n }\n", j, j); if (j > 0) { fprintf(f, " public int chain() {\n"); fprintf(f, " B%d prev = new B%d();\n", j - 1, j - 1); fprintf(f, " return prev.val%d(1) + val%d(2);\n }\n", j - 1, j); } else { fprintf(f, " public int chain() {\n return val0(2);\n }\n"); } fprintf(f, "}\n"); fclose(f); } return 0; } /* Every file probes absent external submodules and retains a real local-call * control. Independent module copies grow the shared registry without adding * cross-copy dependencies. Both sizes exceed the parallel-driver threshold. */ static int cx_build_pyext(const char *root, int k) { char dir[1024]; char path[1200]; for (int m = 0; m < k; m++) { int n = snprintf(dir, sizeof(dir), "%s/pyext/mod%d", root, m); if (n < 0 || (size_t)n >= sizeof(dir) || th_mkdir_p(dir) != 0) return -1; for (int j = 0; j < CX_PYEXT_FILES_PER_MOD; j++) { n = snprintf(path, sizeof(path), "%s/p%d.py", dir, j); if (n < 0 || (size_t)n >= sizeof(path)) return -1; FILE *f = fopen(path, "w"); if (!f) return -1; bool written = fprintf(f, "import extlib\n\n") >= 0; for (int d = 0; written && d < CX_PYEXT_DEFS_PER_FILE; d++) { written = fprintf(f, "def cx_pyext_definition_%d(x):\n return x + %d\n\n", d, d) >= 0; } if (written) written = fprintf(f, "def cx_pyext_use():\n return (cx_pyext_definition_0(0)") >= 0; for (int probe = 0; written && probe < CX_PYEXT_PROBES_PER_FILE; probe++) { written = fprintf(f, " + extlib.sub%d.helper(%d)", probe, probe) >= 0; } if (written) written = fprintf(f, ")\n") >= 0; int closed = fclose(f); if (!written || closed != 0) return -1; } } return 0; } /* ── Metrics ─────────────────────────────────────────────────────────── */ /* Per-pass timing capture: a TEE log sink parses `pass.timing` lines during a * run. Information for the report only — pass timings are wall-clock and never * gate (O9). */ enum { CX_MAX_PASSES = 48 }; typedef struct { char name[64]; long ms; } CxPassMs; static CxPassMs g_cx_passes[CX_MAX_PASSES]; static _Atomic int g_cx_pass_count = 0; static void cx_pass_sink(const char *line) { if (!line || !strstr(line, "pass.timing")) { return; } const char *pp = strstr(line, "pass="); const char *ee = strstr(line, "elapsed_ms="); if (!pp || !ee) { return; } int n = atomic_fetch_add_explicit(&g_cx_pass_count, 1, memory_order_relaxed); if (n >= CX_MAX_PASSES) { return; } size_t i = 0; pp += 5; while (pp[i] && pp[i] != ' ' && i < sizeof(g_cx_passes[n].name) - 1) { g_cx_passes[n].name[i] = pp[i]; i++; } g_cx_passes[n].name[i] = '\0'; g_cx_passes[n].ms = atol(ee + 11); } /* Fixture-provided infra corpora (tests/fixtures/complexity//), scoped * by their copy root: Kubernetes manifests with a kustomization (and a Flux * HelmRelease whose chartRef names an OCIRepository), a Go operator * (controller, API types with +groupName, an embedded CRD, a chart loader), a * shell deploy chain (composite CI action -> make -> scripts -> kubectl / helm * of the copy's own manifests and chart), and Terraform helm_releases (one of * a chart dir of the module, one of an external chart). An API * group/kind is one identity across a repository: copies declaring the same * one ARE joined (a CRD links to every package marking its group), so the * fixtures' CRDs use groups no copy's Go code declares -- the guard measures * accidental growth, not that by-design join. The container layer (Docker) * has Dockerfiles with stage ENV, an ENTRYPOINT script and a compose project * building two components of one repository; an image repository is one * identity too, so every copy's builds of it join one Image -- that join * must stay linear (the component split examines its edges). */ static const char *const CX_INFRA_DIRS[] = {"fx_Kubernetes", "fx_Go", "fx_Bash", "fx_HCL", "fx_Dockerfile"}; enum { CX_INFRA_COUNT = sizeof(CX_INFRA_DIRS) / sizeof(CX_INFRA_DIRS[0]) }; typedef struct { int nodes; int edges; int lang_nodes[CX_TEMPLATE_COUNT]; int lang_edges[CX_TEMPLATE_COUNT]; CxPassMs passes[CX_MAX_PASSES]; int pass_count; uint64_t perfile_defs; /* Σ defs registered by per-file/overlay registry builds */ uint64_t build_files; uint64_t filter_failed; uint64_t tail_lookups; uint64_t tail_candidates; uint64_t fallback_rows; uint64_t imp_nodes; /* symbols scored by the importance pass */ uint64_t imp_name_visits; /* same-name-group members visited by that pass */ uint64_t k8s_candidates; /* objects the K8s resolvers examined */ uint64_t op_candidates; /* Go types / dirs the operator pass examined */ uint64_t sh_candidates; /* paths / make rules the shell layer examined */ uint64_t tf_candidates; /* blocks the Terraform layer examined */ uint64_t dk_hub_edges; /* Image edges the component split examined */ int infra_nodes[CX_INFRA_COUNT]; int infra_edges[CX_INFRA_COUNT]; uint64_t py_probes; uint64_t py_probe_visits; int pyext_files; int pyext_defs; int pyext_controls; uint64_t py_class_binds; uint64_t py_class_visits; uint64_t py_wildcards; uint64_t py_wildcard_visits; int pyrange_files; int pyrange_defs; int pyrange_classes; int pyrange_peer_classes; int pyrange_controls; int errors; double wall_s; } CxMetrics; /* Count the intended corpus, then prove each use() kept its exact local LSP * call. Query errors are failures, never indistinguishable from zero rows. */ static bool cx_pyext_control(cbm_store_t *store, const char *project, const cbm_node_t *caller) { if (!caller->qualified_name || !caller->file_path) return false; const char *dot = strrchr(caller->qualified_name, '.'); if (!dot || dot - caller->qualified_name > 1000) return false; char target_qn[1200]; int n = snprintf(target_qn, sizeof(target_qn), "%.*s.cx_pyext_definition_0", (int)(dot - caller->qualified_name), caller->qualified_name); if (n < 0 || (size_t)n >= sizeof(target_qn)) return false; cbm_node_t target = {0}; cbm_edge_t *edges = NULL; int count = 0; bool found = false; if (cbm_store_find_node_by_qn(store, project, target_qn, &target) == CBM_STORE_OK && target.label && strcmp(target.label, "Function") == 0 && target.file_path && strcmp(target.file_path, caller->file_path) == 0 && cbm_store_find_edges_by_source_type(store, caller->id, "CALLS", &edges, &count) == CBM_STORE_OK) { for (int i = 0; i < count; i++) { if (edges[i].target_id != target.id || !edges[i].properties_json) continue; /* an LSP resolution records an lsp_* strategy (lsp_direct here) */ yyjson_doc *doc = yyjson_read(edges[i].properties_json, strlen(edges[i].properties_json), 0); const char *strategy = doc ? yyjson_get_str(yyjson_obj_get(yyjson_doc_get_root(doc), "strategy")) : NULL; found = found || (strategy && strncmp(strategy, "lsp_", 4) == 0); yyjson_doc_free(doc); } } cbm_store_free_edges(edges, count); cbm_node_free_fields(&target); return found; } static bool cx_pyext_population(cbm_store_t *store, const char *project, CxMetrics *out) { cbm_node_t *nodes = NULL; int count = 0; bool ok = cbm_store_find_nodes_by_label(store, project, "File", &nodes, &count) == CBM_STORE_OK; for (int i = 0; ok && i < count; i++) { if (nodes[i].file_path && strncmp(nodes[i].file_path, "pyext/", 6) == 0) out->pyext_files++; } cbm_store_free_nodes(nodes, count); nodes = NULL; count = 0; ok = ok && cbm_store_find_nodes_by_label(store, project, "Function", &nodes, &count) == CBM_STORE_OK; for (int i = 0; ok && i < count; i++) { const cbm_node_t *node = &nodes[i]; if (!node->file_path || strncmp(node->file_path, "pyext/", 6) != 0 || !node->name) continue; static const char definition_prefix[] = "cx_pyext_definition_"; if (strncmp(node->name, definition_prefix, sizeof(definition_prefix) - 1) == 0) out->pyext_defs++; if (strcmp(node->name, "cx_pyext_use") == 0 && cx_pyext_control(store, project, node)) out->pyext_controls++; } cbm_store_free_nodes(nodes, count); return ok; } /* Each package has many registered direct members; other packages are decoys. * Wildcards precede definitions so the fixture retains explicit LSP positives. * Exact invalidation-order negatives live in test_py_lsp.c. */ enum { CX_PYRANGE_FILES_PER_K = 20, CX_PYRANGE_DEFS = 40, CX_PYRANGE_WILDCARDS = 4 }; static int cx_build_pyrange(const char *root, int k) { for (int m = 0; m < k; m++) { for (int j = 0; j < CX_PYRANGE_FILES_PER_K; j++) { char dir[1024], path[1200]; int dn = snprintf(dir, sizeof(dir), "%s/pyrange/mod%d/p%d", root, m, j); if (dn < 0 || (size_t)dn >= sizeof(dir) || th_mkdir_p(dir) != 0) return -1; int pn = snprintf(path, sizeof(path), "%s/__init__.py", dir); if (pn < 0 || (size_t)pn >= sizeof(path)) return -1; FILE *f = fopen(path, "w"); if (!f) return -1; bool written = true; for (int w = 0; written && w < CX_PYRANGE_WILDCARDS; w++) written = fprintf(f, "from range_vendor_%d import *\n", w) >= 0; int peer = (j + 1) % CX_PYRANGE_FILES_PER_K; if (written) written = fprintf(f, "from pyrange.mod%d.p%d import Peer_%d_%d\n\n", m, peer, m, peer) >= 0; for (int d = 0; written && d < CX_PYRANGE_DEFS; d++) { written = fprintf(f, "def cx_pyrange_definition_%d(x):\n return x + %d\n\n" "class RangeClass_%d:\n def value(self):\n" " return %d\n\n", d, d, d, d) >= 0; } if (written) written = fprintf(f, "class Peer_%d_%d:\n def remote_%d_%d(self):\n" " return %d\n\n" "def cx_pyrange_use():\n obj = RangeClass_0()\n" " peer = Peer_%d_%d()\n" " return (cx_pyrange_definition_0(1) + obj.value()\n" " + peer.remote_%d_%d())\n", m, j, m, j, j, m, peer, m, peer) >= 0; int closed = fclose(f); if (!written || closed != 0) return -1; } } return 0; } static bool cx_pyrange_exact_control(cbm_store_t *store, const char *project, const cbm_node_t *caller, const char *qn, const char *target_file, const char *label) { if (!caller->qualified_name || !caller->file_path || !caller->label || strcmp(caller->label, "Function") != 0) return false; cbm_node_t target = {0}; cbm_edge_t *edges = NULL; int count = 0, exact = 0; bool ok = cbm_store_find_node_by_qn(store, project, qn, &target) == CBM_STORE_OK && target.qualified_name && strcmp(target.qualified_name, qn) == 0 && target.label && strcmp(target.label, label) == 0 && target.file_path && strcmp(target.file_path, target_file) == 0 && cbm_store_find_edges_by_source_type(store, caller->id, "CALLS", &edges, &count) == CBM_STORE_OK; for (int i = 0; ok && i < count; i++) { if (edges[i].target_id != target.id) continue; yyjson_doc *doc = edges[i].properties_json ? yyjson_read(edges[i].properties_json, strlen(edges[i].properties_json), 0) : NULL; /* an LSP resolution records an lsp_* strategy */ const char *strategy = doc ? yyjson_get_str(yyjson_obj_get(yyjson_doc_get_root(doc), "strategy")) : NULL; if (strategy && strncmp(strategy, "lsp_", 4) == 0) exact++; yyjson_doc_free(doc); } cbm_store_free_edges(edges, count); cbm_node_free_fields(&target); return ok && exact == 1; } /* A remote method has no declaration in the importer/raw file registry. Its * source=lsp edge proves that cross-file registry evidence reached resolution. */ static bool cx_pyrange_controls(cbm_store_t *store, const char *project, const cbm_node_t *caller) { if (!caller->qualified_name || !caller->file_path) return false; const char *dot = strrchr(caller->qualified_name, '.'); if (!dot || dot - caller->qualified_name > 1000) return false; char module[1200], qn[1400]; int n = snprintf(module, sizeof(module), "%.*s", (int)(dot - caller->qualified_name), caller->qualified_name); if (n < 0 || (size_t)n >= sizeof(module)) return false; n = snprintf(qn, sizeof(qn), "%s.cx_pyrange_definition_0", module); bool function = n >= 0 && (size_t)n < sizeof(qn) && cx_pyrange_exact_control(store, project, caller, qn, caller->file_path, "Function"); n = snprintf(qn, sizeof(qn), "%s.RangeClass_0.value", module); bool method = n >= 0 && (size_t)n < sizeof(qn) && cx_pyrange_exact_control(store, project, caller, qn, caller->file_path, "Method"); int m = -1, j = -1; if (sscanf(caller->file_path, "pyrange/mod%d/p%d/__init__.py", &m, &j) != 2 || m < 0 || j < 0 || j >= CX_PYRANGE_FILES_PER_K) return false; char path[1200], slot_name[64]; n = snprintf(path, sizeof(path), "pyrange/mod%d/p%d/__init__.py", m, j); if (n < 0 || (size_t)n >= sizeof(path) || strcmp(path, caller->file_path) != 0) return false; n = snprintf(slot_name, sizeof(slot_name), "p%d", j); char *slot = strrchr(module, '.'); if (n < 0 || (size_t)n >= sizeof(slot_name) || !slot || strcmp(slot + 1, slot_name) != 0) return false; *slot = '\0'; int peer = (j + 1) % CX_PYRANGE_FILES_PER_K; n = snprintf(qn, sizeof(qn), "%s.p%d.Peer_%d_%d.remote_%d_%d", module, peer, m, peer, m, peer); int pn = snprintf(path, sizeof(path), "pyrange/mod%d/p%d/__init__.py", m, peer); bool remote = n >= 0 && (size_t)n < sizeof(qn) && pn >= 0 && (size_t)pn < sizeof(path) && strcmp(path, caller->file_path) != 0 && cx_pyrange_exact_control(store, project, caller, qn, path, "Method"); return function && method && remote; } static bool cx_pyrange_population(cbm_store_t *store, const char *project, CxMetrics *out) { static const char *const labels[] = {"File", "Function", "Class"}; bool ok = true; for (int l = 0; l < 3; l++) { cbm_node_t *nodes = NULL; int count = 0; bool queried = cbm_store_find_nodes_by_label(store, project, labels[l], &nodes, &count) == CBM_STORE_OK; ok = queried && ok; for (int i = 0; queried && i < count; i++) { const cbm_node_t *node = &nodes[i]; if (!node->file_path || strncmp(node->file_path, "pyrange/", 8) != 0) continue; if (l == 0) out->pyrange_files++; if (l == 1 && node->name) { static const char def_prefix[] = "cx_pyrange_definition_"; if (strncmp(node->name, def_prefix, sizeof(def_prefix) - 1) == 0) out->pyrange_defs++; if (strcmp(node->name, "cx_pyrange_use") == 0 && cx_pyrange_controls(store, project, node)) out->pyrange_controls++; } if (l == 2 && node->name && strncmp(node->name, "RangeClass_", 11) == 0) out->pyrange_classes++; if (l == 2 && node->name && strncmp(node->name, "Peer_", 5) == 0) out->pyrange_peer_classes++; } cbm_store_free_nodes(nodes, count); } return ok; } static double cx_now_s(void) { struct timespec ts; cbm_profile_now(&ts); return (double)ts.tv_sec + (double)ts.tv_nsec / 1e9; } static int cx_run(const char *root, const char *db_path, CxMetrics *out, bool pyext, bool pyrange) { memset(out, 0, sizeof(*out)); uint64_t d0; uint64_t b0; uint64_t f0; uint64_t x0; cbm_pxc_filter_stats(&d0, &b0, &f0, &x0); uint64_t tl0 = atomic_load_explicit(&g_lsp_tail_lookups, memory_order_relaxed); uint64_t tc0 = atomic_load_explicit(&g_lsp_tail_candidates, memory_order_relaxed); uint64_t fb0 = cbm_pp_lsp_linear_fallback_rows(); uint64_t in0 = atomic_load_explicit(&g_importance_nodes, memory_order_relaxed); uint64_t iv0 = atomic_load_explicit(&g_importance_name_visits, memory_order_relaxed); uint64_t kc0 = atomic_load_explicit(&g_k8s_candidates, memory_order_relaxed); uint64_t oc0 = atomic_load_explicit(&g_op_candidates, memory_order_relaxed); uint64_t sc0 = atomic_load_explicit(&g_sh_candidates, memory_order_relaxed); uint64_t fc0 = atomic_load_explicit(&g_tf_candidates, memory_order_relaxed); uint64_t dh0 = atomic_load_explicit(&g_dk_hub_edges, memory_order_relaxed); uint64_t pp0 = atomic_load_explicit(&cbm_py_submodule_probes, memory_order_relaxed); uint64_t pv0 = atomic_load_explicit(&cbm_py_submodule_probe_visits, memory_order_relaxed); uint64_t pc0 = atomic_load_explicit(&cbm_py_module_class_binds, memory_order_relaxed); uint64_t pcv0 = atomic_load_explicit(&cbm_py_module_class_bind_visits, memory_order_relaxed); uint64_t pw0 = atomic_load_explicit(&cbm_py_wildcard_invalidations, memory_order_relaxed); uint64_t pwv0 = atomic_load_explicit(&cbm_py_wildcard_invalidation_visits, memory_order_relaxed); atomic_store_explicit(&g_cx_pass_count, 0, memory_order_relaxed); cbm_log_set_sink_ex(cx_pass_sink, CBM_LOG_SINK_TEE); double t0 = cx_now_s(); cbm_pipeline_t *p = cbm_pipeline_new(root, db_path, CBM_MODE_FULL); if (!p) { return -1; } int rc = cbm_pipeline_run(p); out->wall_s = cx_now_s() - t0; cbm_pipeline_get_file_errors(p, NULL, &out->errors); cbm_log_set_sink(NULL); int captured = atomic_load_explicit(&g_cx_pass_count, memory_order_relaxed); out->pass_count = captured < CX_MAX_PASSES ? captured : CX_MAX_PASSES; memcpy(out->passes, g_cx_passes, (size_t)out->pass_count * sizeof(CxPassMs)); char project[512]; snprintf(project, sizeof(project), "%s", cbm_pipeline_project_name(p)); cbm_pipeline_free(p); if (rc != 0) { return rc; } uint64_t d1; uint64_t b1; uint64_t f1; uint64_t x1; cbm_pxc_filter_stats(&d1, &b1, &f1, &x1); out->perfile_defs = d1 - d0; out->build_files = b1 - b0; out->filter_failed = x1 - x0; out->tail_lookups = atomic_load_explicit(&g_lsp_tail_lookups, memory_order_relaxed) - tl0; out->tail_candidates = atomic_load_explicit(&g_lsp_tail_candidates, memory_order_relaxed) - tc0; out->fallback_rows = cbm_pp_lsp_linear_fallback_rows() - fb0; out->imp_nodes = atomic_load_explicit(&g_importance_nodes, memory_order_relaxed) - in0; out->imp_name_visits = atomic_load_explicit(&g_importance_name_visits, memory_order_relaxed) - iv0; out->k8s_candidates = atomic_load_explicit(&g_k8s_candidates, memory_order_relaxed) - kc0; out->op_candidates = atomic_load_explicit(&g_op_candidates, memory_order_relaxed) - oc0; out->sh_candidates = atomic_load_explicit(&g_sh_candidates, memory_order_relaxed) - sc0; out->tf_candidates = atomic_load_explicit(&g_tf_candidates, memory_order_relaxed) - fc0; out->dk_hub_edges = atomic_load_explicit(&g_dk_hub_edges, memory_order_relaxed) - dh0; out->py_probes = atomic_load_explicit(&cbm_py_submodule_probes, memory_order_relaxed) - pp0; out->py_probe_visits = atomic_load_explicit(&cbm_py_submodule_probe_visits, memory_order_relaxed) - pv0; cbm_store_t *s = cbm_store_open_path(db_path); if (!s) { return -1; } out->nodes = cbm_store_count_nodes(s, project); out->edges = cbm_store_count_edges(s, project); for (int t = 0; t < CX_TEMPLATE_COUNT; t++) { out->lang_nodes[t] = cbm_store_count_nodes_scoped(s, project, CX_TEMPLATES[t].dirname); out->lang_edges[t] = cbm_store_count_edges_scoped(s, project, CX_TEMPLATES[t].dirname); } for (int i = 0; i < CX_INFRA_COUNT; i++) { out->infra_nodes[i] = cbm_store_count_nodes_scoped(s, project, CX_INFRA_DIRS[i]); out->infra_edges[i] = cbm_store_count_edges_scoped(s, project, CX_INFRA_DIRS[i]); } out->py_class_binds = atomic_load_explicit(&cbm_py_module_class_binds, memory_order_relaxed) - pc0; out->py_class_visits = atomic_load_explicit(&cbm_py_module_class_bind_visits, memory_order_relaxed) - pcv0; out->py_wildcards = atomic_load_explicit(&cbm_py_wildcard_invalidations, memory_order_relaxed) - pw0; out->py_wildcard_visits = atomic_load_explicit(&cbm_py_wildcard_invalidation_visits, memory_order_relaxed) - pwv0; bool extra_ok = (!pyext || cx_pyext_population(s, project, out)) && (!pyrange || cx_pyrange_population(s, project, out)); cbm_store_close(s); return extra_ok ? 0 : -1; } static double cx_ratio(double num, double den) { return den > 0.0 ? num / den : 0.0; } typedef int (*CxCorpusBuilder)(const char *root, int k); static int cx_remove_corpus(const char *root) { int rc = th_rmtree(root); if (rc != 0) { fprintf(stderr, " [complexity] WARN: could not remove temp corpus %s\n", root); } return rc; } /* Build one corpus shape at k and 2k in two fresh temp dirs, run the pipeline * on both, and remove both dirs before returning — on success and on every * failure path. Only the metrics outlive the call; the corpora used to stay * behind in the temp root, four per suite run. Only dirs this call created * are removed (the temp root is shared with concurrent runs). */ static int cx_measure(CxCorpusBuilder build, const char *prefix_a, const char *prefix_b, CxMetrics *out_a, CxMetrics *out_b) { const char *tmp = cbm_tmpdir(); char root_a[512]; char root_b[512]; int na = snprintf(root_a, sizeof(root_a), "%s/%s_XXXXXX", tmp, prefix_a); int nb = snprintf(root_b, sizeof(root_b), "%s/%s_XXXXXX", tmp, prefix_b); if (na < 0 || (size_t)na >= sizeof(root_a) || nb < 0 || (size_t)nb >= sizeof(root_b)) return -1; bool made_a = cbm_mkdtemp(root_a) != NULL; bool made_b = made_a && cbm_mkdtemp(root_b) != NULL; int rc = -1; if (made_b && build(root_a, CX_K_BASE) == 0 && build(root_b, CX_K_BASE * 2) == 0) { char db_a[600]; char db_b[600]; int da = snprintf(db_a, sizeof(db_a), "%s/cx.db", root_a); int db = snprintf(db_b, sizeof(db_b), "%s/cx.db", root_b); bool paths = da >= 0 && (size_t)da < sizeof(db_a) && db >= 0 && (size_t)db < sizeof(db_b); bool pyext = build == cx_build_pyext; bool pyrange = build == cx_build_pyrange; if (paths && cx_run(root_a, db_a, out_a, pyext, pyrange) == 0 && cx_run(root_b, db_b, out_b, pyext, pyrange) == 0) { rc = 0; } } if (made_b && cx_remove_corpus(root_b) != 0 && build == cx_build_pyrange) rc = -1; if (made_a && cx_remove_corpus(root_a) != 0 && build == cx_build_pyrange) rc = -1; return rc; } /* Shared across the suite so the report test reuses the measured pair instead * of paying two more pipeline runs. */ static CxMetrics g_cx_base; static CxMetrics g_cx_doubled; static bool g_cx_measured = false; static int cx_measure_pair(void) { if (g_cx_measured) { return 0; } if (cx_measure(cx_build_corpus, "cbm_cx_base", "cbm_cx_dbl", &g_cx_base, &g_cx_doubled) != 0) { return -1; } g_cx_measured = true; return 0; } /* ── Tests ───────────────────────────────────────────────────────────── */ /* The flagship gate. Independent module copies ⇒ every extensive quantity must * scale linearly in the copy count. A files×corpus coupling shows up as a * ratio near 4 on the counter that sums per-file work. */ TEST(complexity_replicated_modules_scale_linearly) { if (cx_measure_pair() != 0) { FAIL("failed to build/run the complexity corpus pair"); } const CxMetrics *a = &g_cx_base; const CxMetrics *b = &g_cx_doubled; /* Sanity: the corpus is real. */ ASSERT_GT(a->nodes, 50); ASSERT_GT(a->edges, 20); double node_r = cx_ratio(b->nodes, a->nodes); double edge_r = cx_ratio(b->edges, a->edges); printf(" nodes %d -> %d (ratio %.2f) edges %d -> %d (ratio %.2f)\n", a->nodes, b->nodes, node_r, a->edges, b->edges, edge_r); ASSERT_TRUE(node_r >= CX_RATIO_LO && node_r <= CX_RATIO_HI); ASSERT_TRUE(edge_r >= CX_RATIO_LO && edge_r <= CX_RATIO_HI); /* Per-language linearity, from the same run pair (scoped by subtree). If * scoped counting returns 0 for the base run the scope semantics changed — * surface that rather than silently skipping. */ for (int t = 0; t < CX_TEMPLATE_COUNT; t++) { ASSERT_GT(a->lang_nodes[t], 0); double lr = cx_ratio(b->lang_nodes[t], a->lang_nodes[t]); printf(" %-8s nodes %d -> %d (ratio %.2f)\n", CX_TEMPLATES[t].dirname, a->lang_nodes[t], b->lang_nodes[t], lr); ASSERT_TRUE(lr >= CX_RATIO_LO && lr <= CX_RATIO_HI); } PASS(); } /* The infra resolvers (K8s references / selectors / CRDs / chartRefs, the Go * operator's controller kinds, CRD types and loaded charts, the shell layer's * manifest dirs and make rules, Terraform's helm_release blocks) examine * candidates by name. Independent * copies share every name, so a candidate set that is not scoped to the * bundle / package / module grows with the copy count: the examined count * goes ~4x per doubling while the edges stay ~2x. Both are gated, on the * fixture corpora's own subtrees, and neither may be vacuous. */ TEST(complexity_infra_resolution_is_linear) { if (cx_measure_pair() != 0) { FAIL("failed to build/run the complexity corpus pair"); } const CxMetrics *a = &g_cx_base; const CxMetrics *b = &g_cx_doubled; printf(" k8s_candidates %llu -> %llu op_candidates %llu -> %llu\n", (unsigned long long)a->k8s_candidates, (unsigned long long)b->k8s_candidates, (unsigned long long)a->op_candidates, (unsigned long long)b->op_candidates); ASSERT_GT((long long)a->k8s_candidates, 0); ASSERT_GT((long long)a->op_candidates, 0); double kr = cx_ratio((double)b->k8s_candidates, (double)a->k8s_candidates); double orr = cx_ratio((double)b->op_candidates, (double)a->op_candidates); printf(" k8s_candidates ratio %.2f op_candidates ratio %.2f\n", kr, orr); ASSERT_TRUE(kr >= CX_RATIO_LO && kr <= CX_RATIO_HI); ASSERT_TRUE(orr >= CX_RATIO_LO && orr <= CX_RATIO_HI); printf(" sh_candidates %llu -> %llu tf_candidates %llu -> %llu\n", (unsigned long long)a->sh_candidates, (unsigned long long)b->sh_candidates, (unsigned long long)a->tf_candidates, (unsigned long long)b->tf_candidates); ASSERT_GT((long long)a->sh_candidates, 0); ASSERT_GT((long long)a->tf_candidates, 0); double sr = cx_ratio((double)b->sh_candidates, (double)a->sh_candidates); double fr = cx_ratio((double)b->tf_candidates, (double)a->tf_candidates); printf(" sh_candidates ratio %.2f tf_candidates ratio %.2f\n", sr, fr); ASSERT_TRUE(sr >= CX_RATIO_LO && sr <= CX_RATIO_HI); ASSERT_TRUE(fr >= CX_RATIO_LO && fr <= CX_RATIO_HI); printf(" dk_hub_edges %llu -> %llu\n", (unsigned long long)a->dk_hub_edges, (unsigned long long)b->dk_hub_edges); ASSERT_GT((long long)a->dk_hub_edges, 0); double dr = cx_ratio((double)b->dk_hub_edges, (double)a->dk_hub_edges); printf(" dk_hub_edges ratio %.2f\n", dr); ASSERT_TRUE(dr >= CX_RATIO_LO && dr <= CX_RATIO_HI); for (int i = 0; i < CX_INFRA_COUNT; i++) { ASSERT_GT(a->infra_nodes[i], 0); ASSERT_GT(a->infra_edges[i], 0); double nr = cx_ratio(b->infra_nodes[i], a->infra_nodes[i]); double er = cx_ratio(b->infra_edges[i], a->infra_edges[i]); printf(" %-14s nodes %d -> %d (%.2f) edges %d -> %d (%.2f)\n", CX_INFRA_DIRS[i], a->infra_nodes[i], b->infra_nodes[i], nr, a->infra_edges[i], b->infra_edges[i], er); ASSERT_TRUE(nr >= CX_RATIO_LO && nr <= CX_RATIO_HI); ASSERT_TRUE(er >= CX_RATIO_LO && er <= CX_RATIO_HI); } PASS(); } /* Σ per-file registry work must be linear in independent copies. This is the * counter that measured ~4x/doubling on the v0.10.x Java path (defs_per_file * tracked defs_total — #1669). Guarded against vacuous passes: the base run * must have produced real registry work, so an accidental zeroing of the * counter fails loudly instead of green-washing the gate. */ TEST(complexity_perfile_registry_work_is_linear) { if (cx_measure_pair() != 0) { FAIL("failed to build/run the complexity corpus pair"); } const CxMetrics *a = &g_cx_base; const CxMetrics *b = &g_cx_doubled; printf(" perfile_defs %llu -> %llu build_files %llu -> %llu filter_failed %llu\n", (unsigned long long)a->perfile_defs, (unsigned long long)b->perfile_defs, (unsigned long long)a->build_files, (unsigned long long)b->build_files, (unsigned long long)(a->filter_failed + b->filter_failed)); /* Non-vacuous: the corpus includes languages that register per-file defs * (Java at minimum). If this is ever 0 the counter wiring broke — that is * a test defect to fix, not a pass. */ ASSERT_GT((long long)a->perfile_defs, (long long)CX_MIN_BASE_WORK); double defs_r = cx_ratio((double)b->perfile_defs, (double)a->perfile_defs); printf(" perfile_defs ratio %.2f (linear ~2, files x corpus ~4)\n", defs_r); ASSERT_TRUE(defs_r <= CX_RATIO_HI); /* Recorded, deliberately NOT gated (the O10 note): tail_candidates and * fallback_rows are legitimately superlinear under replication TODAY — * same-short-name candidate sets grow with the copy count by design of * the current tail scan. Both measured ~1 ns/unit (#1669: indexing the * scan away cut 242M visits for 0% wall). Gate them only after those * scans are bounded; until then they are trend data for the report. */ printf(" [info] tail_lookups %llu -> %llu tail_candidates %llu -> %llu fallback_rows " "%llu -> %llu\n", (unsigned long long)a->tail_lookups, (unsigned long long)b->tail_lookups, (unsigned long long)a->tail_candidates, (unsigned long long)b->tail_candidates, (unsigned long long)a->fallback_rows, (unsigned long long)b->fallback_rows); PASS(); } static CxMetrics g_cx_big_base; static CxMetrics g_cx_big_doubled; static bool g_cx_big_measured = false; static int cx_measure_bigpkg_pair(void) { if (g_cx_big_measured) { return 0; } if (cx_measure(cx_build_bigpkg, "cbm_cxbig_a", "cbm_cxbig_b", &g_cx_big_base, &g_cx_big_doubled) != 0) { return -1; } g_cx_big_measured = true; return 0; } /* One growing package instead of independent modules. Nodes and edges must * still be linear in file count — and so must Σ per-file registry work: a * build scoped to the file's MODULE or NAMESPACE pays package-size per file * and lands at ratio ~4 here. Only a per-FILE-scoped build stays at ~2. This * is the exact #1669 growth pattern (per-file work tracking corpus share). */ TEST(complexity_shared_package_growth_stays_linear) { if (cx_measure_bigpkg_pair() != 0) { FAIL("failed to build/run the big-package corpus pair"); } const CxMetrics *a = &g_cx_big_base; const CxMetrics *b = &g_cx_big_doubled; double node_r = cx_ratio(b->nodes, a->nodes); double edge_r = cx_ratio(b->edges, a->edges); printf(" nodes %d -> %d (ratio %.2f) edges %d -> %d (ratio %.2f)\n", a->nodes, b->nodes, node_r, a->edges, b->edges, edge_r); ASSERT_GT(a->nodes, 30); ASSERT_TRUE(node_r >= CX_RATIO_LO && node_r <= CX_RATIO_HI); ASSERT_TRUE(edge_r >= CX_RATIO_LO && edge_r <= CX_RATIO_HI); printf(" perfile_defs %llu -> %llu (ratio %.2f; linear ~2, namespace/module-scoped ~4)\n", (unsigned long long)a->perfile_defs, (unsigned long long)b->perfile_defs, cx_ratio((double)b->perfile_defs, (double)a->perfile_defs)); /* Non-vacuous floor (false-guard audit): the package produces real * registry work; zero means the counter wiring broke. */ ASSERT_GT((long long)a->perfile_defs, (long long)CX_MIN_BASE_WORK); double defs_r = cx_ratio((double)b->perfile_defs, (double)a->perfile_defs); ASSERT_TRUE(defs_r <= CX_RATIO_HI); PASS(); } TEST(complexity_py_submodule_probe_avoids_registry_scan) { static const char *const keys[] = {"CBM_WORKERS", "CBM_INDEX_SINGLE_THREAD"}; char *saved[2] = {NULL, NULL}; bool ready = true; for (int i = 0; i < 2; i++) { const char *old = getenv(keys[i]); saved[i] = old ? strdup(old) : NULL; if (old && !saved[i]) ready = false; } CxMetrics a = {0}, b = {0}; int rc = -1, restored = 0; if (ready) { int configured = cbm_setenv(keys[0], "2", 1); configured |= cbm_unsetenv(keys[1]); if (configured == 0) rc = cx_measure(cx_build_pyext, "cbm_cxpy_a", "cbm_cxpy_b", &a, &b); for (int i = 0; i < 2; i++) restored |= saved[i] ? cbm_setenv(keys[i], saved[i], 1) : cbm_unsetenv(keys[i]); } for (int i = 0; i < 2; i++) free(saved[i]); /* cx_measure removes both owned corpora before any assertion below. */ ASSERT_TRUE(ready); ASSERT_EQ(restored, 0); ASSERT_EQ(rc, 0); int files = CX_K_BASE * CX_PYEXT_FILES_PER_MOD; ASSERT_EQ(a.errors, 0); ASSERT_EQ(b.errors, 0); ASSERT_EQ(a.pyext_files, files); ASSERT_EQ(b.pyext_files, files * 2); ASSERT_EQ(a.pyext_defs, files * CX_PYEXT_DEFS_PER_FILE); ASSERT_EQ(b.pyext_defs, files * 2 * CX_PYEXT_DEFS_PER_FILE); ASSERT_EQ(a.pyext_controls, files); ASSERT_EQ(b.pyext_controls, files * 2); ASSERT_GT((long long)a.py_probes, (long long)CX_MIN_BASE_WORK); ASSERT_GT((long long)b.py_probes, (long long)CX_MIN_BASE_WORK); ASSERT_GT((long long)a.py_probe_visits, 0); ASSERT_GT((long long)b.py_probe_visits, 0); double probes_ratio = cx_ratio((double)b.py_probes, (double)a.py_probes); double visits_ratio = cx_ratio((double)b.py_probe_visits, (double)a.py_probe_visits); printf(" py_probes %llu -> %llu (%.2f), visits %llu -> %llu (%.2f)\n", (unsigned long long)a.py_probes, (unsigned long long)b.py_probes, probes_ratio, (unsigned long long)a.py_probe_visits, (unsigned long long)b.py_probe_visits, visits_ratio); ASSERT_TRUE(probes_ratio >= CX_RATIO_LO && probes_ratio <= CX_RATIO_HI); ASSERT_TRUE(visits_ratio <= CX_RATIO_HI); /* The direct registry test also enforces a logarithmic comparison bound, * independently of fixed stdlib size and this corpus-growth ratio. */ PASS(); } /* Throughput report — information only, never a gate (CI-determinism rule: * rates depend on the machine and scheduler, so a threshold would be a * lottery). Written locally under private/ (gitignored); CBM_COMPLEXITY_ * REPORT_DIR overrides. Skipped on starved legs where rates are meaningless. */ TEST(complexity_throughput_report_written) { const char *skip_perf = getenv("CBM_SKIP_PERF"); if (skip_perf && skip_perf[0] == '1') { /* Deliberate operator config, not a hidden environment failure * (no-skips policy): rates measured under CBM_SKIP_PERF starvation * would only mislead, so reporting is OFF and there is nothing left * for this test to assert. */ fprintf(stderr, " [complexity] CBM_SKIP_PERF=1: throughput report disabled by config\n"); PASS(); } if (cx_measure_pair() != 0) { FAIL("failed to build/run the complexity corpus pair"); } const char *dir = getenv("CBM_COMPLEXITY_REPORT_DIR"); if (!dir || !dir[0]) { dir = "private/benchmarks"; } if (th_mkdir_p(dir) != 0) { FAIL("report dir not creatable (set CBM_COMPLEXITY_REPORT_DIR to a writable path)"); } char path[1024]; snprintf(path, sizeof(path), "%s/complexity-%lld.json", dir, (long long)time(NULL)); FILE *f = fopen(path, "w"); ASSERT_NOT_NULL(f); const CxMetrics *a = &g_cx_base; const CxMetrics *b = &g_cx_doubled; #if defined(__APPLE__) const char *plat = "darwin"; #elif defined(_WIN32) const char *plat = "windows"; #else const char *plat = "linux"; #endif #if defined(__aarch64__) || defined(_M_ARM64) const char *arch = "arm64"; #else const char *arch = "x86_64"; #endif fprintf(f, "{\n \"schema\": 1,\n \"suite\": \"complexity\",\n"); fprintf(f, " \"timestamp\": %lld,\n \"platform\": \"%s\",\n \"arch\": \"%s\",\n", (long long)time(NULL), plat, arch); fprintf(f, " \"k_base\": %d,\n \"files_per_module\": %d,\n", CX_K_BASE, CX_FILES_PER_MOD); fprintf(f, " \"runs\": [\n"); const CxMetrics *runs[2] = {a, b}; for (int i = 0; i < 2; i++) { const CxMetrics *m = runs[i]; fprintf(f, " {\"k\": %d, \"nodes\": %d, \"edges\": %d, \"wall_s\": %.3f,\n" " \"nodes_per_s\": %.0f, \"edges_per_s\": %.0f,\n" " \"perfile_defs\": %llu, \"tail_candidates\": %llu, \"fallback_rows\": " "%llu}%s\n", i == 0 ? CX_K_BASE : CX_K_BASE * 2, m->nodes, m->edges, m->wall_s, m->wall_s > 0 ? (double)m->nodes / m->wall_s : 0.0, m->wall_s > 0 ? (double)m->edges / m->wall_s : 0.0, (unsigned long long)m->perfile_defs, (unsigned long long)m->tail_candidates, (unsigned long long)m->fallback_rows, i == 0 ? "," : ""); } fprintf(f, " ],\n"); fprintf(f, " \"languages\": [\n"); for (int t = 0; t < CX_TEMPLATE_COUNT; t++) { fprintf(f, " {\"name\": \"%s\", \"nodes\": [%d, %d], \"edges\": [%d, %d], " "\"node_ratio\": %.3f}%s\n", CX_TEMPLATES[t].dirname, a->lang_nodes[t], b->lang_nodes[t], a->lang_edges[t], b->lang_edges[t], cx_ratio(b->lang_nodes[t], a->lang_nodes[t]), t + 1 < CX_TEMPLATE_COUNT ? "," : ""); } fprintf(f, " ],\n"); fprintf(f, " \"passes_ms\": {\n"); for (int i = 0; i < 2; i++) { const CxMetrics *m = runs[i]; fprintf(f, " \"k%d\": {", i == 0 ? CX_K_BASE : CX_K_BASE * 2); for (int j = 0; j < m->pass_count; j++) { fprintf(f, "%s\"%s\": %ld", j == 0 ? "" : ", ", m->passes[j].name, m->passes[j].ms); } fprintf(f, "}%s\n", i == 0 ? "," : ""); } fprintf(f, " },\n"); fprintf(f, " \"ratios\": {\"nodes\": %.3f, \"edges\": %.3f, \"perfile_defs\": %.3f},\n", cx_ratio(b->nodes, a->nodes), cx_ratio(b->edges, a->edges), cx_ratio((double)b->perfile_defs, (double)a->perfile_defs)); /* Languages without a provider, so the coverage boundary is explicit in * the artifact rather than implied. */ fprintf(f, " \"skipped_languages\": ["); bool first = true; for (int lang = 0; lang < CBM_LANG_COUNT; lang++) { bool covered = false; for (int t = 0; t < CX_TEMPLATE_COUNT; t++) { if (CX_TEMPLATES[t].lang == (CBMLanguage)lang) { covered = true; } } char fixdir[512]; if (!covered && cx_fixture_dir_for((CBMLanguage)lang, fixdir, sizeof(fixdir))) { covered = true; } if (!covered) { const char *name = cbm_language_name((CBMLanguage)lang); if (name && name[0]) { fprintf(f, "%s\"%s\"", first ? "" : ", ", name); first = false; } } } fprintf(f, "]\n}\n"); fclose(f); printf(" report: %s\n", path); PASS(); } /* Importance scoring must stay linear in the graph. Its generic-name * multiplier needs |{files a name is defined in}|; computing that per NODE * instead of once per distinct NAME makes a same-name group of size k cost * O(k^3) — measured on a large Java corpus as a multi-minute cost for a * handful of names. Under replication the same-name groups grow with the copy * count, so the per-node shape lands at ~4x per doubling here while the * memoized shape stays at ~2x. Gated on the counter ratio, never on wall time * (O9). This counter is GATED, not merely recorded: the whole point of the * work is that this quantity must not go superlinear. */ TEST(complexity_importance_scoring_is_linear) { if (cx_measure_pair() != 0) { FAIL("failed to build/run the complexity corpus pair"); } const CxMetrics *a = &g_cx_base; const CxMetrics *b = &g_cx_doubled; printf(" imp_nodes %llu -> %llu imp_name_visits %llu -> %llu\n", (unsigned long long)a->imp_nodes, (unsigned long long)b->imp_nodes, (unsigned long long)a->imp_name_visits, (unsigned long long)b->imp_name_visits); /* Non-vacuous, twice over: the pass must have run at all (a miscounted * PREDUMP_PASS_COUNT would silently skip it and zero both counters), and * the base leg must have produced enough work for a ratio to mean * anything. A zero here is a wiring defect to fix, never a pass. */ ASSERT_GT((long long)a->imp_nodes, 0); ASSERT_GT((long long)a->imp_name_visits, (long long)CX_MIN_BASE_WORK); double node_r = cx_ratio((double)b->imp_nodes, (double)a->imp_nodes); double visit_r = cx_ratio((double)b->imp_name_visits, (double)a->imp_name_visits); printf(" imp_nodes ratio %.2f imp_name_visits ratio %.2f (linear ~2, per-node ~4)\n", node_r, visit_r); ASSERT_TRUE(node_r >= CX_RATIO_LO && node_r <= CX_RATIO_HI); ASSERT_TRUE(visit_r <= CX_RATIO_HI); PASS(); } /* ── Lexer work in error recovery (#2176) ────────────────────────────── * tree-sitter lexes an unparseable stretch by retrying at every byte with * every external token marked valid. The ReScript scanner then ran its * template-string loop from each byte to the next '`', '$', '\\' or NUL — to * the end of the file when there is none — and threw the result away. That * is O(stretch) per byte, O(n^2) per file, all inside lexing where the parse * budget's progress callback never runs; a binary Godot `.res` of high bytes * (or plain text such as a run of '~') was dropped by the clock instead of * parsed. Work is counted as the bytes the lexer pulls through a chunked * TSInput: a pure function of (grammar, input), independent of speed. */ enum { CX_LEX_CHUNK = 64, CX_LEX_BASE_BYTES = 4096 }; typedef struct { const char *src; uint32_t len; uint64_t bytes_pulled; } CxLexInput; static const char *cx_lex_read(void *payload, uint32_t byte_index, TSPoint position, uint32_t *bytes_read) { (void)position; CxLexInput *in = (CxLexInput *)payload; if (byte_index >= in->len) { *bytes_read = 0; return ""; } uint32_t n = in->len - byte_index; if (n > CX_LEX_CHUNK) { n = CX_LEX_CHUNK; } in->bytes_pulled += n; *bytes_read = n; return in->src + byte_index; } /* Bytes pulled while parsing `len` copies of `fill` as ReScript; 0 on failure. */ static uint64_t cx_rescript_lex_work(unsigned char fill, uint32_t len) { char *src = malloc(len); TSParser *parser = ts_parser_new(); uint64_t work = 0; if (src && parser && ts_parser_set_language(parser, cbm_ts_language(CBM_LANG_RESCRIPT))) { memset(src, fill, len); CxLexInput in = {src, len, 0}; TSInput input = {&in, cx_lex_read, TSInputEncodingUTF8, NULL}; TSTree *tree = ts_parser_parse(parser, NULL, input); if (tree) { work = in.bytes_pulled; ts_tree_delete(tree); } } if (parser) { ts_parser_delete(parser); } free(src); return work; } TEST(complexity_rescript_error_recovery_lexing_is_linear) { /* 0xFF: the reporter's binary bytes (invalid UTF-8). '~': plain ASCII text * that ReScript cannot parse either — the defect is not binary-only. */ static const unsigned char fills[] = {0xFF, '~'}; for (size_t i = 0; i < sizeof(fills); i++) { uint64_t base = cx_rescript_lex_work(fills[i], CX_LEX_BASE_BYTES); uint64_t doubled = cx_rescript_lex_work(fills[i], 2 * CX_LEX_BASE_BYTES); double r = cx_ratio((double)doubled, (double)base); printf(" fill 0x%02x: lexer bytes %llu -> %llu ratio %.2f (linear ~2, quadratic ~4)\n", fills[i], (unsigned long long)base, (unsigned long long)doubled, r); /* Non-vacuous: the lexer must at least have read the input once. */ ASSERT_GTE(base, (uint64_t)CX_LEX_BASE_BYTES); ASSERT_TRUE(r >= CX_RATIO_LO && r <= CX_RATIO_HI); } PASS(); } TEST(complexity_py_module_ranges_preserve_calls_with_bounded_work) { static const char *const keys[] = {"CBM_WORKERS", "CBM_INDEX_SINGLE_THREAD"}; char *saved[2] = {NULL, NULL}; bool ready = true; for (int i = 0; i < 2; i++) { const char *old = getenv(keys[i]); saved[i] = old ? strdup(old) : NULL; if (old && !saved[i]) ready = false; } CxMetrics a = {0}, b = {0}; int rc = -1, restored = 0; if (ready) { int configured = cbm_setenv(keys[0], "2", 1); configured |= cbm_unsetenv(keys[1]); if (configured == 0) rc = cx_measure(cx_build_pyrange, "cbm_cxrange_a", "cbm_cxrange_b", &a, &b); for (int i = 0; i < 2; i++) restored |= saved[i] ? cbm_setenv(keys[i], saved[i], 1) : cbm_unsetenv(keys[i]); } for (int i = 0; i < 2; i++) free(saved[i]); /* Both owned corpora are removed and environment restored before assertions. */ ASSERT_TRUE(ready); ASSERT_EQ(restored, 0); ASSERT_EQ(rc, 0); const int files = CX_K_BASE * CX_PYRANGE_FILES_PER_K; ASSERT_GT(files, 50); ASSERT_EQ(a.errors, 0); ASSERT_EQ(b.errors, 0); ASSERT_EQ(a.pyrange_files, files); ASSERT_EQ(b.pyrange_files, files * 2); ASSERT_EQ(a.pyrange_defs, files * CX_PYRANGE_DEFS); ASSERT_EQ(b.pyrange_defs, files * 2 * CX_PYRANGE_DEFS); ASSERT_EQ(a.pyrange_classes, files * CX_PYRANGE_DEFS); ASSERT_EQ(b.pyrange_classes, files * 2 * CX_PYRANGE_DEFS); ASSERT_EQ(a.pyrange_peer_classes, files); ASSERT_EQ(b.pyrange_peer_classes, files * 2); ASSERT_EQ(a.pyrange_controls, files); ASSERT_EQ(b.pyrange_controls, files * 2); ASSERT_GTE(a.py_class_binds, (uint64_t)files); ASSERT_GTE(b.py_class_binds, (uint64_t)(files * 2)); ASSERT_GTE(a.py_wildcards, (uint64_t)(files * CX_PYRANGE_WILDCARDS)); ASSERT_GTE(b.py_wildcards, (uint64_t)(files * 2 * CX_PYRANGE_WILDCARDS)); ASSERT_GT(a.py_class_visits, (uint64_t)0); ASSERT_GT(b.py_class_visits, (uint64_t)0); ASSERT_GT(a.py_wildcard_visits, (uint64_t)0); ASSERT_GT(b.py_wildcard_visits, (uint64_t)0); double classes = cx_ratio((double)b.py_class_binds, (double)a.py_class_binds); double wildcards = cx_ratio((double)b.py_wildcards, (double)a.py_wildcards); ASSERT_TRUE(classes >= CX_RATIO_LO && classes <= CX_RATIO_HI); ASSERT_TRUE(wildcards >= CX_RATIO_LO && wildcards <= CX_RATIO_HI); ASSERT_TRUE(cx_ratio((double)b.py_class_visits, (double)a.py_class_visits) <= CX_RATIO_HI); ASSERT_TRUE(cx_ratio((double)b.py_wildcard_visits, (double)a.py_wildcard_visits) <= CX_RATIO_HI); /* These counters exclude sorting/string-length work. The direct registry * tests separately pin absolute bounds and exact ordered answers. */ PASS(); } SUITE(complexity) { RUN_TEST(complexity_py_module_ranges_preserve_calls_with_bounded_work); RUN_TEST(complexity_rescript_error_recovery_lexing_is_linear); RUN_TEST(complexity_replicated_modules_scale_linearly); RUN_TEST(complexity_perfile_registry_work_is_linear); RUN_TEST(complexity_infra_resolution_is_linear); RUN_TEST(complexity_importance_scoring_is_linear); RUN_TEST(complexity_shared_package_growth_stays_linear); RUN_TEST(complexity_py_submodule_probe_avoids_registry_scan); RUN_TEST(complexity_throughput_report_written); }