From cda6ed5d4d5942ad41a93bcfec02eb9ef818a5ad Mon Sep 17 00:00:00 2001 From: sunerpy Date: Sat, 10 Oct 2026 16:00:40 +0800 Subject: [PATCH 001/183] test: re-extract every golden corpus in the test suite equivalence.rs only proved that each committed colby.db agrees with its own JSON; nothing in the suite ran the extractor, so a change that moved extraction output passed CI until someone ran the out-of-repo regeneration helper by hand. - tests/golden_reextract.rs indexes every re-indexable corpus from crates/codegraph-bench/fixtures/ with the binary under test, dumps it through the same canonicalizer as `bench --gen-golden`, and compares the five artifacts byte for byte with reference/golden/. A failure names the changed artifacts, the canonical row diff and the regeneration command. A guard test keeps the corpus list in step with reference/golden/. - scripts/regen-goldens.sh runs the shared recipe: --check (writes nothing), --write (only the named corpora) and --mini-transplant (mini's schema rebuild). Scratch directories come from mktemp and are removed on exit. - equivalence.rs generates each corpus's two byte-drift tests from one macro, keeping the existing test names, and fails when a committed corpus has no test pair. - docs/equivalence.md describes the recipe once, adds the regeneration log the v0.55.0 program records changes in, and each corpus section now says what it guards plus the one regeneration command. Every corpus re-extracts byte-identically on this tree; a tampered go/nodes.json makes the gate fail with a Tier-1 node diff. --- crates/codegraph-bench/tests/equivalence.rs | 629 +++--------------- .../codegraph-cli/tests/golden_reextract.rs | 196 ++++++ docs/equivalence.md | 335 ++-------- scripts/regen-goldens.sh | 213 ++++++ 4 files changed, 558 insertions(+), 815 deletions(-) create mode 100644 crates/codegraph-cli/tests/golden_reextract.rs create mode 100755 scripts/regen-goldens.sh diff --git a/crates/codegraph-bench/tests/equivalence.rs b/crates/codegraph-bench/tests/equivalence.rs index 79e01872..cd8d783d 100644 --- a/crates/codegraph-bench/tests/equivalence.rs +++ b/crates/codegraph-bench/tests/equivalence.rs @@ -36,398 +36,79 @@ fn an_unparseable_known_diffs_file_fails_the_equivalence_assertion() { assert!(message.contains("may not be allowlisted"), "got: {message}"); } -#[test] -fn generated_golden_matches_committed_mini_fixture() { - let tempdir = TestDir::new("generated-golden"); - write_golden(&mini_db(), tempdir.path()).unwrap(); - - let expected = load_golden(&mini_golden_dir()).unwrap(); - let actual = load_golden(tempdir.path()).unwrap(); - - diff_canonical(&expected, &actual, None).unwrap(); -} - -#[test] -fn upstream_db_is_self_equivalent_to_mini_golden() { - assert_equivalent(&mini_db(), &mini_golden_dir()).unwrap(); -} - -#[test] -fn generated_golden_matches_committed_godot_fixture() { - // Guards Godot extraction (F1 autoload-call→func + F2 signal-handler edges) - // against byte-drift: regenerating the canonical golden from the committed - // godot db must reproduce the committed JSON exactly. - let tempdir = TestDir::new("generated-golden-godot"); - write_golden(&godot_db(), tempdir.path()).unwrap(); - - let expected = load_golden(&godot_golden_dir()).unwrap(); - let actual = load_golden(tempdir.path()).unwrap(); - - diff_canonical(&expected, &actual, None).unwrap(); -} - -#[test] -fn upstream_db_is_self_equivalent_to_godot_golden() { - assert_equivalent(&godot_db(), &godot_golden_dir()).unwrap(); -} - -#[test] -fn generated_golden_matches_committed_ruby_fixture() { - // Guards Ruby #1110 receiver.method extraction (instance-call → Calls, - // class-method call → Calls, `Const.new` → Instantiates, bare include → - // Implements) against byte-drift: regenerating the canonical golden from the - // committed ruby db must reproduce the committed JSON exactly. - let tempdir = TestDir::new("generated-golden-ruby"); - write_golden(&ruby_db(), tempdir.path()).unwrap(); - - let expected = load_golden(&ruby_golden_dir()).unwrap(); - let actual = load_golden(tempdir.path()).unwrap(); - - diff_canonical(&expected, &actual, None).unwrap(); -} - -#[test] -fn upstream_db_is_self_equivalent_to_ruby_golden() { - assert_equivalent(&ruby_db(), &ruby_golden_dir()).unwrap(); -} - -#[test] -fn generated_golden_matches_committed_cpp_fixture() { - // Guards C++ #1043 base_class_clause inheritance (general Extends extraction - // + templated-base stripping) against byte-drift: regenerating the canonical - // golden from the committed cpp db must reproduce the committed JSON exactly. - let tempdir = TestDir::new("generated-golden-cpp"); - write_golden(&cpp_db(), tempdir.path()).unwrap(); - - let expected = load_golden(&cpp_golden_dir()).unwrap(); - let actual = load_golden(tempdir.path()).unwrap(); - - diff_canonical(&expected, &actual, None).unwrap(); -} - -#[test] -fn cpp_db_is_self_equivalent_to_cpp_golden() { - assert_equivalent(&cpp_db(), &cpp_golden_dir()).unwrap(); -} - -#[test] -fn generated_golden_matches_committed_rust_fixture() { - // Guards item 7 (Rust unit structs are indexed, so `impl Trait for Unit` - // forms) and item 9-Rust (`union` is a first-class kind) against byte-drift. - let tempdir = TestDir::new("generated-golden-rust"); - write_golden(&rust_db(), tempdir.path()).unwrap(); - - let expected = load_golden(&rust_golden_dir()).unwrap(); - let actual = load_golden(tempdir.path()).unwrap(); - - diff_canonical(&expected, &actual, None).unwrap(); -} - -#[test] -fn rust_db_is_self_equivalent_to_rust_golden() { - assert_equivalent(&rust_db(), &rust_golden_dir()).unwrap(); -} - -#[test] -fn generated_golden_matches_committed_lua_fixture() { - // Guards #1616 Lua function expressions: local assignment functions become - // Function nodes without duplicate Variables, table members become Methods - // with full qualified names, nested table members retain their namespace, - // body calls belong to the synthesized callable, and both dot/colon calls - // resolve to the same static table member. - let tempdir = TestDir::new("generated-golden-lua"); - write_golden(&lua_db(), tempdir.path()).unwrap(); - - let expected = load_golden(&lua_golden_dir()).unwrap(); - let actual = load_golden(tempdir.path()).unwrap(); - - diff_canonical(&expected, &actual, None).unwrap(); -} - -#[test] -fn lua_db_is_self_equivalent_to_lua_golden() { - assert_equivalent(&lua_db(), &lua_golden_dir()).unwrap(); -} - -#[test] -fn generated_golden_matches_committed_go_fixture() { - // Guards #1500 content-header detection: `files.generated` is byte-pinned at - // BOTH values — 1 for the Go banner, the Wrangler double-`by` banner, and the - // path-only `.pb.go`; 0 for the hand-written sibling, the single-`by` prose - // negative, and the generator whose banner is a `const` in its body. - let tempdir = TestDir::new("generated-golden-go"); - write_golden(&go_db(), tempdir.path()).unwrap(); - - let expected = load_golden(&go_golden_dir()).unwrap(); - let actual = load_golden(tempdir.path()).unwrap(); - - diff_canonical(&expected, &actual, None).unwrap(); -} - -#[test] -fn go_db_is_self_equivalent_to_go_golden() { - assert_equivalent(&go_db(), &go_golden_dir()).unwrap(); -} - -#[test] -fn generated_golden_matches_committed_python_fixture() { - let tempdir = TestDir::new("generated-golden-python"); - write_golden(&python_db(), tempdir.path()).unwrap(); - - let expected = load_golden(&python_golden_dir()).unwrap(); - let actual = load_golden(tempdir.path()).unwrap(); - - diff_canonical(&expected, &actual, None).unwrap(); -} - -#[test] -fn python_db_is_self_equivalent_to_python_golden() { - assert_equivalent(&python_db(), &python_golden_dir()).unwrap(); -} - -#[test] -fn generated_golden_matches_committed_kotlin_fixture() { - let tempdir = TestDir::new("generated-golden-kotlin"); - write_golden(&kotlin_db(), tempdir.path()).unwrap(); - - let expected = load_golden(&kotlin_golden_dir()).unwrap(); - let actual = load_golden(tempdir.path()).unwrap(); - - diff_canonical(&expected, &actual, None).unwrap(); -} - -#[test] -fn kotlin_db_is_self_equivalent_to_kotlin_golden() { - assert_equivalent(&kotlin_db(), &kotlin_golden_dir()).unwrap(); -} - -#[test] -fn generated_golden_matches_committed_scala_fixture() { - // Guards Scala grammar 0.26.2's multi-parameter-list inheritance shape, - // every `extends`/`with` parent, companion object -> Module classification, - // inheritance preference for the real type, and eta-expansion method values. - let tempdir = TestDir::new("generated-golden-scala"); - write_golden(&scala_db(), tempdir.path()).unwrap(); - - let expected = load_golden(&scala_golden_dir()).unwrap(); - let actual = load_golden(tempdir.path()).unwrap(); - - diff_canonical(&expected, &actual, None).unwrap(); -} - -#[test] -fn scala_db_is_self_equivalent_to_scala_golden() { - assert_equivalent(&scala_db(), &scala_golden_dir()).unwrap(); -} - -#[test] -fn generated_golden_matches_committed_dart_fixture() { - // Guards Dart 3 `extension type`: the type is class-like and owns getter / - // method nodes rather than leaving top-level functions or dropping members. - let tempdir = TestDir::new("generated-golden-dart"); - write_golden(&dart_db(), tempdir.path()).unwrap(); - - let expected = load_golden(&dart_golden_dir()).unwrap(); - let actual = load_golden(tempdir.path()).unwrap(); - - diff_canonical(&expected, &actual, None).unwrap(); -} - -#[test] -fn dart_db_is_self_equivalent_to_dart_golden() { - assert_equivalent(&dart_db(), &dart_golden_dir()).unwrap(); -} - -#[test] -fn generated_golden_matches_committed_typescript_fixture() { - let tempdir = TestDir::new("generated-golden-typescript"); - write_golden(&typescript_db(), tempdir.path()).unwrap(); - - let expected = load_golden(&typescript_golden_dir()).unwrap(); - let actual = load_golden(tempdir.path()).unwrap(); - - diff_canonical(&expected, &actual, None).unwrap(); -} - -#[test] -fn typescript_db_is_self_equivalent_to_typescript_golden() { - assert_equivalent(&typescript_db(), &typescript_golden_dir()).unwrap(); -} - -#[test] -fn generated_golden_matches_committed_metal_fixture() { - // Guards Metal (#1121): `.metal`→cpp mapping + the `[[attribute]]` blank that - // prevents the spurious `VertexIn extends float4` inheritance edge. - let tempdir = TestDir::new("generated-golden-metal"); - write_golden(&metal_db(), tempdir.path()).unwrap(); - - let expected = load_golden(&metal_golden_dir()).unwrap(); - let actual = load_golden(tempdir.path()).unwrap(); - - diff_canonical(&expected, &actual, None).unwrap(); -} - -#[test] -fn metal_db_is_self_equivalent_to_metal_golden() { - assert_equivalent(&metal_db(), &metal_golden_dir()).unwrap(); -} - -#[test] -fn generated_golden_matches_committed_cuda_fixture() { - // Guards CUDA (#1172 CUDA-lang parts): `.cu`→cpp mapping, the `<<<…>>>` - // launch-config blank that preserves the host→kernel Calls edge (plain + - // templated), and macro-defined-kernel name recovery (`my_kernel`). - let tempdir = TestDir::new("generated-golden-cuda"); - write_golden(&cuda_db(), tempdir.path()).unwrap(); - - let expected = load_golden(&cuda_golden_dir()).unwrap(); - let actual = load_golden(tempdir.path()).unwrap(); - - diff_canonical(&expected, &actual, None).unwrap(); -} - -#[test] -fn cuda_db_is_self_equivalent_to_cuda_golden() { - assert_equivalent(&cuda_db(), &cuda_golden_dir()).unwrap(); -} - -#[test] -fn generated_golden_matches_committed_arkts_fixture() { - // Guards ArkTS extraction (upstream #1186, extraction slice only): the - // `.ets`->ArkTs mapping, `@Component struct`->NodeKind::Struct via the - // dedicated tree-sitter-arkts grammar, function/class/import/call extraction. - let tempdir = TestDir::new("generated-golden-arkts"); - write_golden(&arkts_db(), tempdir.path()).unwrap(); - - let expected = load_golden(&arkts_golden_dir()).unwrap(); - let actual = load_golden(tempdir.path()).unwrap(); - - diff_canonical(&expected, &actual, None).unwrap(); -} - -#[test] -fn arkts_db_is_self_equivalent_to_arkts_golden() { - assert_equivalent(&arkts_db(), &arkts_golden_dir()).unwrap(); -} - -#[test] -fn generated_golden_matches_committed_solidity_fixture() { - // Guards Solidity extraction (upstream #1170): the `.sol`->Solidity mapping, - // contract/library->Class + interface->Interface + struct->Struct + - // enum->Enum, synthetic constructor/fallback/receive method names, - // state-var/struct-member/event/error->Field, `is`-inheritance->Extends - // (resolver promotes to Implements), and emit/modifier-guard call edges. - let tempdir = TestDir::new("generated-golden-solidity"); - write_golden(&solidity_db(), tempdir.path()).unwrap(); - - let expected = load_golden(&solidity_golden_dir()).unwrap(); - let actual = load_golden(tempdir.path()).unwrap(); - - diff_canonical(&expected, &actual, None).unwrap(); -} - -#[test] -fn solidity_db_is_self_equivalent_to_solidity_golden() { - assert_equivalent(&solidity_db(), &solidity_golden_dir()).unwrap(); -} - -#[test] -fn generated_golden_matches_committed_nix_fixture() { - // Guards Nix extraction (upstream #1190, extraction slice only): the - // `.nix`->Nix mapping, `binding`->Function|Variable, curried lambda->Function - // with a formatted signature, `inherit`->Variable names, `import`/ - // `callPackage`/`imports`-list literal paths->Import node + Imports ref, and - // `apply_expression`->Calls ref with curried-chain dedup. The module-system - // synthesizer / lexical-scope gates / callback synthesizer / import-resolver - // wiring are DEFERRED, so path refs stay unresolved. - let tempdir = TestDir::new("generated-golden-nix"); - write_golden(&nix_db(), tempdir.path()).unwrap(); - - let expected = load_golden(&nix_golden_dir()).unwrap(); - let actual = load_golden(tempdir.path()).unwrap(); - - diff_canonical(&expected, &actual, None).unwrap(); -} - -#[test] -fn nix_db_is_self_equivalent_to_nix_golden() { - assert_equivalent(&nix_db(), &nix_golden_dir()).unwrap(); -} - -#[test] -fn generated_golden_matches_committed_terraform_fixture() { - // Guards Terraform/HCL extraction (upstream #1173, extraction slice only): - // the `.tf`/`.tfvars`/`.tofu`->Terraform mapping, block-type dispatch - // (resource/data->Class, module->Module, variable/output->Variable, - // provider->Namespace, locals->Constant per attr) with qualified names, and - // plain attribute-expression traversal refs - // (var.X/local.X/module.M/data.T.N/.)->References with built-ins - // skipped. The module-boundary TerraformResolver, emitModuleWiring's - // :-scoped refs, and the .tfvars var ref are DEFERRED, so no :-scoped ref is - // emitted; the undeclared aws_kms_key.logs stays unresolved. - let tempdir = TestDir::new("generated-golden-terraform"); - write_golden(&terraform_db(), tempdir.path()).unwrap(); - - let expected = load_golden(&terraform_golden_dir()).unwrap(); - let actual = load_golden(tempdir.path()).unwrap(); - - diff_canonical(&expected, &actual, None).unwrap(); -} - -#[test] -fn terraform_db_is_self_equivalent_to_terraform_golden() { - assert_equivalent(&terraform_db(), &terraform_golden_dir()).unwrap(); -} - -#[test] -fn generated_golden_matches_committed_erlang_fixture() { - // Guards Erlang extraction (upstream #1165, extraction slice only): the - // `.erl`/`.hrl`->Erlang mapping, `-module`->Namespace (so functions qualify - // as `m::f`), clause-merge dedup (a 2-clause `f/1`->one Function), - // `record_decl`->Struct + Field children, `-define`->Constant, - // `type_alias`/`opaque`->TypeAlias, `-include`->Import + file edge, local - // `call`->Calls and remote `mod:f()`->Calls `mod::f`, `fun f/1` value and - // `#state{}` usage->References (not calls). The `-spec`/`-callback` and - // record-field type-position `call` nodes mint NO bogus type call refs. The - // `-behaviour`, gen_server, spawn/apply MFA, and `.app` resource-tuple - // bridges are DEFERRED, so `other::h` (other module absent) stays unresolved. - let tempdir = TestDir::new("generated-golden-erlang"); - write_golden(&erlang_db(), tempdir.path()).unwrap(); - - let expected = load_golden(&erlang_golden_dir()).unwrap(); - let actual = load_golden(tempdir.path()).unwrap(); - - diff_canonical(&expected, &actual, None).unwrap(); -} - -#[test] -fn erlang_db_is_self_equivalent_to_erlang_golden() { - assert_equivalent(&erlang_db(), &erlang_golden_dir()).unwrap(); -} - -#[test] -fn generated_golden_matches_committed_cfml_fixture() { - // Guards CFML extraction (upstream #1153, scope-B extraction slice): the - // `.cfc`/`.cfm`/`.cfs`->Cfml mapping, the dual-grammar dialect switch - // (`is_bare_script_cfml`: cfscript for bare-script, cfml tag grammar for - // tag files), a bare-script `component`->Class named from the FILE (unnamed - // in the grammar) + script-style `extends`->Extends, a tag - // ``->Class from `name` attr + ``->Method with - // access/returntype + tag `extends`->Extends. Both extends resolve to - // `Base.cfc`; `helper()` stays unresolved. The ``-in-tag delegation - // + cfquery SQL-body extraction + framework resolvers are DEFERRED. - let tempdir = TestDir::new("generated-golden-cfml"); - write_golden(&cfml_db(), tempdir.path()).unwrap(); - - let expected = load_golden(&cfml_golden_dir()).unwrap(); - let actual = load_golden(tempdir.path()).unwrap(); - - diff_canonical(&expected, &actual, None).unwrap(); -} - -#[test] -fn cfml_db_is_self_equivalent_to_cfml_golden() { - assert_equivalent(&cfml_db(), &cfml_golden_dir()).unwrap(); +/// One pair of byte-drift tests per committed golden corpus. Each corpus's +/// docs/equivalence.md section says what it guards; the regeneration recipe is +/// shared (`scripts/regen-goldens.sh`). The first test regenerates the canonical +/// artifacts from the committed `colby.db` and compares them with the committed +/// JSON; the second runs the full oracle over the same pair. +macro_rules! golden_corpora { + ($($corpus:literal => $generated:ident, $equivalent:ident;)+) => { + /// Every corpus with a committed `colby.db`, in declaration order. + const GOLDEN_CORPORA: &[&str] = &[$($corpus),+]; + + $( + #[test] + fn $generated() { + let tempdir = TestDir::new(concat!("generated-golden-", $corpus)); + write_golden(&corpus_db($corpus), tempdir.path()).unwrap(); + + let expected = load_golden(&corpus_golden_dir($corpus)).unwrap(); + let actual = load_golden(tempdir.path()).unwrap(); + + diff_canonical(&expected, &actual, None).unwrap(); + } + + #[test] + fn $equivalent() { + assert_equivalent(&corpus_db($corpus), &corpus_golden_dir($corpus)).unwrap(); + } + )+ + }; +} + +golden_corpora! { + "mini" => generated_golden_matches_committed_mini_fixture, upstream_db_is_self_equivalent_to_mini_golden; + "godot" => generated_golden_matches_committed_godot_fixture, upstream_db_is_self_equivalent_to_godot_golden; + "ruby" => generated_golden_matches_committed_ruby_fixture, upstream_db_is_self_equivalent_to_ruby_golden; + "cpp" => generated_golden_matches_committed_cpp_fixture, cpp_db_is_self_equivalent_to_cpp_golden; + "rust" => generated_golden_matches_committed_rust_fixture, rust_db_is_self_equivalent_to_rust_golden; + "lua" => generated_golden_matches_committed_lua_fixture, lua_db_is_self_equivalent_to_lua_golden; + "go" => generated_golden_matches_committed_go_fixture, go_db_is_self_equivalent_to_go_golden; + "python" => generated_golden_matches_committed_python_fixture, python_db_is_self_equivalent_to_python_golden; + "kotlin" => generated_golden_matches_committed_kotlin_fixture, kotlin_db_is_self_equivalent_to_kotlin_golden; + "scala" => generated_golden_matches_committed_scala_fixture, scala_db_is_self_equivalent_to_scala_golden; + "dart" => generated_golden_matches_committed_dart_fixture, dart_db_is_self_equivalent_to_dart_golden; + "typescript" => generated_golden_matches_committed_typescript_fixture, typescript_db_is_self_equivalent_to_typescript_golden; + "metal" => generated_golden_matches_committed_metal_fixture, metal_db_is_self_equivalent_to_metal_golden; + "cuda" => generated_golden_matches_committed_cuda_fixture, cuda_db_is_self_equivalent_to_cuda_golden; + "arkts" => generated_golden_matches_committed_arkts_fixture, arkts_db_is_self_equivalent_to_arkts_golden; + "solidity" => generated_golden_matches_committed_solidity_fixture, solidity_db_is_self_equivalent_to_solidity_golden; + "nix" => generated_golden_matches_committed_nix_fixture, nix_db_is_self_equivalent_to_nix_golden; + "terraform" => generated_golden_matches_committed_terraform_fixture, terraform_db_is_self_equivalent_to_terraform_golden; + "erlang" => generated_golden_matches_committed_erlang_fixture, erlang_db_is_self_equivalent_to_erlang_golden; + "cfml" => generated_golden_matches_committed_cfml_fixture, cfml_db_is_self_equivalent_to_cfml_golden; +} + +#[test] +fn every_committed_golden_corpus_has_its_test_pair() { + let mut found: Vec = std::fs::read_dir(workspace_root().join("reference/golden")) + .unwrap() + .filter_map(|entry| { + let entry = entry.unwrap(); + entry + .path() + .join("colby.db") + .is_file() + .then(|| entry.file_name().into_string().unwrap()) + }) + .collect(); + found.sort(); + let mut listed: Vec = GOLDEN_CORPORA.iter().map(|c| (*c).to_string()).collect(); + listed.sort(); + assert_eq!( + found, listed, + "every reference/golden//colby.db must be listed in golden_corpora!" + ); } #[test] @@ -467,7 +148,6 @@ fn tier2_edges_are_order_independent_but_counted() { .any(|entry| entry.tier == Tier::Tier2 && entry.surface == "edges") ); } - fn workspace_root() -> PathBuf { Path::new(env!("CARGO_MANIFEST_DIR")) .parent() @@ -476,164 +156,23 @@ fn workspace_root() -> PathBuf { .to_path_buf() } -fn mini_db() -> PathBuf { - workspace_root().join("reference/golden/mini/colby.db") -} - -fn mini_golden_dir() -> PathBuf { - workspace_root().join("reference/golden/mini") -} - -fn godot_db() -> PathBuf { - workspace_root().join("reference/golden/godot/colby.db") -} - -fn godot_golden_dir() -> PathBuf { - workspace_root().join("reference/golden/godot") -} - -fn ruby_db() -> PathBuf { - workspace_root().join("reference/golden/ruby/colby.db") -} - -fn ruby_golden_dir() -> PathBuf { - workspace_root().join("reference/golden/ruby") -} - -fn cpp_db() -> PathBuf { - workspace_root().join("reference/golden/cpp/colby.db") -} - -fn cpp_golden_dir() -> PathBuf { - workspace_root().join("reference/golden/cpp") -} - -fn rust_db() -> PathBuf { - workspace_root().join("reference/golden/rust/colby.db") -} - -fn rust_golden_dir() -> PathBuf { - workspace_root().join("reference/golden/rust") -} - -fn lua_db() -> PathBuf { - workspace_root().join("reference/golden/lua/colby.db") -} - -fn lua_golden_dir() -> PathBuf { - workspace_root().join("reference/golden/lua") -} - -fn go_db() -> PathBuf { - workspace_root().join("reference/golden/go/colby.db") -} - -fn go_golden_dir() -> PathBuf { - workspace_root().join("reference/golden/go") -} - -fn python_db() -> PathBuf { - workspace_root().join("reference/golden/python/colby.db") -} - -fn python_golden_dir() -> PathBuf { - workspace_root().join("reference/golden/python") +fn corpus_db(corpus: &str) -> PathBuf { + workspace_root() + .join("reference/golden") + .join(corpus) + .join("colby.db") } -fn kotlin_db() -> PathBuf { - workspace_root().join("reference/golden/kotlin/colby.db") +fn corpus_golden_dir(corpus: &str) -> PathBuf { + workspace_root().join("reference/golden").join(corpus) } -fn kotlin_golden_dir() -> PathBuf { - workspace_root().join("reference/golden/kotlin") -} - -fn scala_db() -> PathBuf { - workspace_root().join("reference/golden/scala/colby.db") -} - -fn scala_golden_dir() -> PathBuf { - workspace_root().join("reference/golden/scala") -} - -fn dart_db() -> PathBuf { - workspace_root().join("reference/golden/dart/colby.db") -} - -fn dart_golden_dir() -> PathBuf { - workspace_root().join("reference/golden/dart") -} - -fn typescript_db() -> PathBuf { - workspace_root().join("reference/golden/typescript/colby.db") -} - -fn typescript_golden_dir() -> PathBuf { - workspace_root().join("reference/golden/typescript") -} - -fn metal_db() -> PathBuf { - workspace_root().join("reference/golden/metal/colby.db") -} - -fn metal_golden_dir() -> PathBuf { - workspace_root().join("reference/golden/metal") -} - -fn cuda_db() -> PathBuf { - workspace_root().join("reference/golden/cuda/colby.db") -} - -fn cuda_golden_dir() -> PathBuf { - workspace_root().join("reference/golden/cuda") -} - -fn arkts_db() -> PathBuf { - workspace_root().join("reference/golden/arkts/colby.db") -} - -fn arkts_golden_dir() -> PathBuf { - workspace_root().join("reference/golden/arkts") -} - -fn solidity_db() -> PathBuf { - workspace_root().join("reference/golden/solidity/colby.db") -} - -fn solidity_golden_dir() -> PathBuf { - workspace_root().join("reference/golden/solidity") -} - -fn nix_db() -> PathBuf { - workspace_root().join("reference/golden/nix/colby.db") -} - -fn nix_golden_dir() -> PathBuf { - workspace_root().join("reference/golden/nix") -} - -fn terraform_db() -> PathBuf { - workspace_root().join("reference/golden/terraform/colby.db") -} - -fn terraform_golden_dir() -> PathBuf { - workspace_root().join("reference/golden/terraform") -} - -fn erlang_db() -> PathBuf { - workspace_root().join("reference/golden/erlang/colby.db") -} - -fn erlang_golden_dir() -> PathBuf { - workspace_root().join("reference/golden/erlang") -} - -fn cfml_db() -> PathBuf { - workspace_root().join("reference/golden/cfml/colby.db") +fn mini_db() -> PathBuf { + corpus_db("mini") } -fn cfml_golden_dir() -> PathBuf { - workspace_root().join("reference/golden/cfml") +fn mini_golden_dir() -> PathBuf { + corpus_golden_dir("mini") } struct TestDir { diff --git a/crates/codegraph-cli/tests/golden_reextract.rs b/crates/codegraph-cli/tests/golden_reextract.rs new file mode 100644 index 00000000..ef51a208 --- /dev/null +++ b/crates/codegraph-cli/tests/golden_reextract.rs @@ -0,0 +1,196 @@ +//! Re-extraction gate for the committed golden corpora. +//! +//! `crates/codegraph-bench/tests/equivalence.rs` only proves that each committed +//! `colby.db` and its canonical JSON agree with each other; it never runs the +//! extractor. This suite closes that gap: every re-indexable corpus is indexed +//! afresh from `crates/codegraph-bench/fixtures//` with the binary under +//! test, dumped through the same canonicalizer `bench --gen-golden` uses, and +//! compared byte for byte with `reference/golden//`. A change that moves +//! extraction or resolution output therefore fails here until the affected +//! golden is regenerated (`scripts/regen-goldens.sh --write `) and the +//! change is recorded in `docs/equivalence.md`. +//! +//! `mini` is not re-indexable (upstream-derived rows); it is covered by +//! `crates/codegraph-resolve/tests/golden_resolution.rs` instead. + +use std::path::{Path, PathBuf}; +use std::process::Command; + +use codegraph_bench::oracle::{diff_canonical, load_golden, write_golden}; + +/// The canonical artifacts `bench --gen-golden` writes, compared byte for byte. +const ARTIFACTS: &[&str] = &[ + "nodes.json", + "edges.json", + "refs.json", + "files.json", + "schema.sql", +]; + +macro_rules! reextract_corpora { + ($($corpus:ident),+ $(,)?) => { + /// Every corpus this suite re-extracts, in declaration order. + const CORPORA: &[&str] = &[$(stringify!($corpus)),+]; + + $( + mod $corpus { + #[test] + fn reextracts_byte_identically() { + super::reextract(stringify!($corpus)); + } + } + )+ + }; +} + +reextract_corpora!( + arkts, cfml, cpp, cuda, dart, erlang, go, godot, kotlin, lua, metal, nix, python, ruby, rust, + scala, solidity, terraform, typescript, +); + +#[test] +fn every_reindexable_golden_corpus_is_re_extracted() { + let root = workspace_root(); + let mut found: Vec = std::fs::read_dir(root.join("reference/golden")) + .expect("reference/golden is readable") + .filter_map(|entry| { + let entry = entry.expect("reference/golden entry"); + let name = entry.file_name().into_string().ok()?; + let is_corpus = entry.path().join("colby.db").is_file() + && root + .join("crates/codegraph-bench/fixtures") + .join(&name) + .is_dir(); + (is_corpus && name != "mini").then_some(name) + }) + .collect(); + found.sort(); + let mut listed: Vec = CORPORA.iter().map(|name| (*name).to_string()).collect(); + listed.sort(); + assert_eq!( + found, listed, + "every re-indexable corpus under reference/golden/ must be listed in reextract_corpora!" + ); +} + +fn reextract(corpus: &str) { + let root = workspace_root(); + let fixture = root.join("crates/codegraph-bench/fixtures").join(corpus); + let golden = root.join("reference/golden").join(corpus); + + let work = TempDir::new(&format!("work-{corpus}")); + copy_tree(&fixture, work.path()); + let output = Command::new(env!("CARGO_BIN_EXE_codegraph")) + .arg("init") + .arg(work.path()) + .env("CODEGRAPH_NO_DAEMON", "1") + .env("CODEGRAPH_NO_WATCH", "1") + .env_remove("CODEGRAPH_DIR") + .output() + .expect("spawn codegraph init"); + assert!( + output.status.success(), + "codegraph init failed for corpus {corpus}:\nstdout:\n{}\nstderr:\n{}", + String::from_utf8_lossy(&output.stdout), + String::from_utf8_lossy(&output.stderr) + ); + + let dumped = TempDir::new(&format!("golden-{corpus}")); + write_golden(&work.path().join(".codegraph/codegraph.db"), dumped.path()) + .unwrap_or_else(|error| panic!("dump canonical golden for {corpus}: {error:#}")); + + let changed: Vec<&str> = ARTIFACTS + .iter() + .copied() + .filter(|artifact| { + let fresh = std::fs::read(dumped.path().join(artifact)) + .unwrap_or_else(|error| panic!("read fresh {artifact}: {error}")); + let committed = std::fs::read(golden.join(artifact)) + .unwrap_or_else(|error| panic!("read committed {corpus}/{artifact}: {error}")); + fresh != committed + }) + .collect(); + if changed.is_empty() { + return; + } + + let detail = match (load_golden(&golden), load_golden(dumped.path())) { + (Ok(expected), Ok(actual)) => match diff_canonical(&expected, &actual, None) { + Ok(()) => { + "canonical rows are equal; only artifact bytes differ (e.g. schema.sql order)" + .to_string() + } + Err(report) => report.to_string(), + }, + (expected, actual) => format!( + "could not load goldens for a detailed diff: committed={:?} fresh={:?}", + expected.err(), + actual.err() + ), + }; + panic!( + "re-extracting corpus `{corpus}` changed {changed:?}.\n\ + If the change is intended, regenerate it with \ + `scripts/regen-goldens.sh --write {corpus}` and record it in docs/equivalence.md.\n\ + {detail}" + ); +} + +fn workspace_root() -> PathBuf { + Path::new(env!("CARGO_MANIFEST_DIR")) + .parent() + .and_then(Path::parent) + .expect("codegraph-cli is under crates/") + .to_path_buf() +} + +fn copy_tree(from: &Path, to: &Path) { + for entry in std::fs::read_dir(from).unwrap_or_else(|error| { + panic!("read fixture directory {}: {error}", from.display()); + }) { + let entry = entry.expect("fixture entry"); + let source = entry.path(); + let target = to.join(entry.file_name()); + let file_type = entry.file_type().expect("fixture entry type"); + if file_type.is_dir() { + std::fs::create_dir_all(&target).expect("create fixture subdirectory"); + copy_tree(&source, &target); + } else if file_type.is_file() { + std::fs::copy(&source, &target).expect("copy fixture file"); + } else { + panic!( + "fixture {} contains a non-regular entry; goldens must be built from plain files", + source.display() + ); + } + } +} + +struct TempDir { + path: PathBuf, +} + +impl TempDir { + fn new(label: &str) -> Self { + let path = std::env::temp_dir().join(format!( + "codegraph-golden-reextract-{label}-{}-{}", + std::process::id(), + std::time::SystemTime::now() + .duration_since(std::time::UNIX_EPOCH) + .expect("clock after epoch") + .as_nanos() + )); + std::fs::create_dir_all(&path).expect("create temp dir"); + Self { path } + } + + fn path(&self) -> &Path { + &self.path + } +} + +impl Drop for TempDir { + fn drop(&mut self) { + let _ = std::fs::remove_dir_all(&self.path); + } +} diff --git a/docs/equivalence.md b/docs/equivalence.md index 4b77d848..aed9c5a1 100644 --- a/docs/equivalence.md +++ b/docs/equivalence.md @@ -142,13 +142,51 @@ Canonical fixture files are committed under `reference/golden//`: - `files.json` - `schema.sql` -Regenerate from a reference SQLite database with: +Every corpus except `mini` is re-indexable and is regenerated with one recipe: +copy its source corpus from `crates/codegraph-bench/fixtures//` into a +clean directory, index it with the current binary +(`CODEGRAPH_NO_DAEMON=1 CODEGRAPH_NO_WATCH=1 codegraph init`), commit the produced +database as `reference/golden//colby.db`, and dump the canonical +artifacts from that same database with `bench --gen-golden`. Never hand-write a +golden. `scripts/regen-goldens.sh` runs exactly that recipe: + +```bash +cargo build --locked --release -p codegraph-rs -p codegraph-bench +scripts/regen-goldens.sh --check # every corpus; exit 1 on any difference +scripts/regen-goldens.sh --check cpp go # only the named corpora +scripts/regen-goldens.sh --write cpp # rewrite colby.db + artifacts of cpp only +scripts/regen-goldens.sh --mini-transplant # mini; see "Mini schema rebuild" below +``` + +`--check` writes nothing into the repository; `--write` writes only under +`reference/golden//`. Both use `target/release/codegraph` and +`target/release/bench` unless `CODEGRAPH_BIN` / `BENCH_BIN` say otherwise, and +work in `mktemp` scratch directories that are removed on exit. + +The same comparison runs in the test suite: +`cargo test -p codegraph-rs --locked --test golden_reextract` indexes every +re-indexable corpus with the test build of the binary and fails, naming the +changed artifacts and the canonical row diff, when extraction or resolution +output moved without its golden being regenerated. `equivalence.rs` (below) +only proves that each committed `colby.db` agrees with its own JSON; it never +runs the extractor. + +To dump a single database by hand (for example while investigating a diff): ```bash cargo run -p codegraph-bench --bin bench -- \ --gen-golden reference/golden/mini/colby.db reference/golden/mini ``` +#### Regeneration log + +Every intentional golden change is recorded here with the commit, the corpora +and artifacts it changed, why the graph change is intended, and the command +that regenerated it. Older regenerations are described in each corpus section. + +| Commit | Corpus | Artifacts | Intent | Command | +| ------ | ------ | --------- | ------ | ------- | + The canonicalizer strips inherently unstable timestamp columns (`nodes.updated_at`, `files.modified_at`, `files.indexed_at`), parses JSON text columns before re-serializing them with deterministic key order, asserts all @@ -182,25 +220,7 @@ The minimal source corpus lives at `crates/codegraph-bench/fixtures/godot/` (`project.godot`, `game_flow.gd`, `stage_manager.gd`, `main.tscn`, `effect_manager.gd`, `effect_manager.gd.uid`, `combo_ui.tscn`). -Regenerate the committed database + canonical JSON reproducibly from the corpus: - -```bash -# 1. Copy the corpus to a clean directory (keeps the workspace index out of it). -rm -rf /tmp/cg-fixture-godot -cp -r crates/codegraph-bench/fixtures/godot /tmp/cg-fixture-godot - -# 2. Index it with OUR binary (never hand-write the golden). -cargo build --release -p codegraph-rs -CODEGRAPH_NO_DAEMON=1 CODEGRAPH_NO_WATCH=1 \ - ./target/release/codegraph init /tmp/cg-fixture-godot - -# 3. Commit the produced database as the fixture's colby.db. -cp /tmp/cg-fixture-godot/.codegraph/codegraph.db reference/golden/godot/colby.db - -# 4. Dump the canonical golden JSON + schema from that database. -cargo run -p codegraph-bench --bin bench -- \ - --gen-golden reference/golden/godot/colby.db reference/golden/godot -``` +Regenerate it with `scripts/regen-goldens.sh --write godot` (the shared recipe in [Regenerating goldens](#regenerating-goldens)). The extraction and `--gen-golden` steps are both byte-stable: re-running the index or the dump reproduces identical `nodes.json`/`edges.json`/`refs.json`/ @@ -248,25 +268,7 @@ shapes: The minimal source corpus lives at `crates/codegraph-bench/fixtures/ruby/` (`service.rb`, `logger.rb`). -Regenerate the committed database + canonical JSON reproducibly from the corpus: - -```bash -# 1. Copy the corpus to a clean directory (keeps the workspace index out of it). -rm -rf /tmp/cg-fixture-ruby -cp -r crates/codegraph-bench/fixtures/ruby /tmp/cg-fixture-ruby - -# 2. Index it with OUR binary (never hand-write the golden). -cargo build --release -p codegraph-rs -CODEGRAPH_NO_DAEMON=1 CODEGRAPH_NO_WATCH=1 \ - ./target/release/codegraph init /tmp/cg-fixture-ruby - -# 3. Commit the produced database as the fixture's colby.db. -cp /tmp/cg-fixture-ruby/.codegraph/codegraph.db reference/golden/ruby/colby.db - -# 4. Dump the canonical golden JSON + schema from that database. -cargo run -p codegraph-bench --bin bench -- \ - --gen-golden reference/golden/ruby/colby.db reference/golden/ruby -``` +Regenerate it with `scripts/regen-goldens.sh --write ruby` (the shared recipe in [Regenerating goldens](#regenerating-goldens)). Like the Godot fixture, both the index and the dump are byte-stable, and the `generated_golden_matches_committed_ruby_fixture` and @@ -314,26 +316,7 @@ Since extraction version 14 the `pkg/__init__.py` file node carries its module docstring (upstream #1905): a bare string literal first in a module, class or function body is that node's docstring, joined after any preceding comment. -Regenerate the committed database and canonical artifacts from a clean corpus: - -```bash -# 1. Copy the corpus to a clean directory (keeps the workspace index out of it). -rm -rf /tmp/cg-fixture-python -cp -r crates/codegraph-bench/fixtures/python /tmp/cg-fixture-python - -# 2. Index it with OUR release binary (never hand-write the golden). -cargo build --release -p codegraph-rs -CODEGRAPH_NO_DAEMON=1 CODEGRAPH_NO_WATCH=1 \ - ./target/release/codegraph init /tmp/cg-fixture-python - -# 3. Commit the produced database as the fixture's colby.db. -mkdir -p reference/golden/python -cp /tmp/cg-fixture-python/.codegraph/codegraph.db reference/golden/python/colby.db - -# 4. Dump canonical JSON + schema from that exact database. -cargo run -p codegraph-bench --bin bench -- \ - --gen-golden reference/golden/python/colby.db reference/golden/python -``` +Regenerate it with `scripts/regen-goldens.sh --write python` (the shared recipe in [Regenerating goldens](#regenerating-goldens)). As with every fixture, compare only `nodes.json`, `edges.json`, `refs.json`, `files.json`, and `schema.sql` byte-for-byte. `colby.db` itself is not a @@ -354,19 +337,7 @@ class method with a nullable generic return, and an extension function. The `Processor` primary constructor is the negative boundary: the class signature stays null and no constructor method is synthesized. -Regenerate the committed database and canonical artifacts from a clean corpus: - -```bash -rm -rf /tmp/cg-fixture-kotlin -cp -r crates/codegraph-bench/fixtures/kotlin /tmp/cg-fixture-kotlin -cargo build --release -p codegraph-rs -CODEGRAPH_NO_DAEMON=1 CODEGRAPH_NO_WATCH=1 \ - ./target/release/codegraph init /tmp/cg-fixture-kotlin -mkdir -p reference/golden/kotlin -cp /tmp/cg-fixture-kotlin/.codegraph/codegraph.db reference/golden/kotlin/colby.db -cargo run -p codegraph-bench --bin bench -- \ - --gen-golden reference/golden/kotlin/colby.db reference/golden/kotlin -``` +Regenerate it with `scripts/regen-goldens.sh --write kotlin` (the shared recipe in [Regenerating goldens](#regenerating-goldens)). As with every fixture, only the five text artifacts are byte-compared; `colby.db` is not byte-reproducible. Compare `schema.sql` by normalized statement @@ -389,19 +360,7 @@ fixes from upstream #1823 and #1824. Its two-file corpus under `trait`; the inheritance edge resolves to the trait even when the object appears first in the file. -Regenerate it from the committed source corpus: - -```bash -rm -rf /tmp/cg-fixture-scala -cp -r crates/codegraph-bench/fixtures/scala /tmp/cg-fixture-scala -cargo build --release -p codegraph-rs -CODEGRAPH_NO_DAEMON=1 CODEGRAPH_NO_WATCH=1 \ - ./target/release/codegraph init /tmp/cg-fixture-scala -mkdir -p reference/golden/scala -cp /tmp/cg-fixture-scala/.codegraph/codegraph.db reference/golden/scala/colby.db -cargo run -p codegraph-bench --bin bench -- \ - --gen-golden reference/golden/scala/colby.db reference/golden/scala -``` +Regenerate it with `scripts/regen-goldens.sh --write scala` (the shared recipe in [Regenerating goldens](#regenerating-goldens)). `generated_golden_matches_committed_scala_fixture` and `scala_db_is_self_equivalent_to_scala_golden` pin the canonical artifacts and @@ -416,19 +375,7 @@ corpus at `crates/codegraph-bench/fixtures/dart/extension_type.dart` proves an ordinary-class control. This prevents either the old top-level-function shape or the later complete member drop from returning unnoticed. -Regenerate it from the committed source corpus: - -```bash -rm -rf /tmp/cg-fixture-dart -cp -r crates/codegraph-bench/fixtures/dart /tmp/cg-fixture-dart -cargo build --release -p codegraph-rs -CODEGRAPH_NO_DAEMON=1 CODEGRAPH_NO_WATCH=1 \ - ./target/release/codegraph init /tmp/cg-fixture-dart -mkdir -p reference/golden/dart -cp /tmp/cg-fixture-dart/.codegraph/codegraph.db reference/golden/dart/colby.db -cargo run -p codegraph-bench --bin bench -- \ - --gen-golden reference/golden/dart/colby.db reference/golden/dart -``` +Regenerate it with `scripts/regen-goldens.sh --write dart` (the shared recipe in [Regenerating goldens](#regenerating-goldens)). `generated_golden_matches_committed_dart_fixture` and `dart_db_is_self_equivalent_to_dart_golden` enforce the same two-layer @@ -615,25 +562,7 @@ which maps to `Language::C` by extension); `ue_actor.h` deliberately uses `.h` t guard the content-based C++ reclassification, and `attr_macro.c` uses `.c` so the C walker (not the C++ one) is the thing under test. -Regenerate the committed database + canonical JSON reproducibly from the corpus: - -```bash -# 1. Copy the corpus to a clean directory (keeps the workspace index out of it). -rm -rf /tmp/cg-fixture-cpp -cp -r crates/codegraph-bench/fixtures/cpp /tmp/cg-fixture-cpp - -# 2. Index it with OUR binary (never hand-write the golden). -cargo build --release -p codegraph-rs -CODEGRAPH_NO_DAEMON=1 CODEGRAPH_NO_WATCH=1 \ - ./target/release/codegraph init /tmp/cg-fixture-cpp - -# 3. Commit the produced database as the fixture's colby.db. -cp /tmp/cg-fixture-cpp/.codegraph/codegraph.db reference/golden/cpp/colby.db - -# 4. Dump the canonical golden JSON + schema from that database. -cargo run -p codegraph-bench --bin bench -- \ - --gen-golden reference/golden/cpp/colby.db reference/golden/cpp -``` +Regenerate it with `scripts/regen-goldens.sh --write cpp` (the shared recipe in [Regenerating goldens](#regenerating-goldens)). Like the Ruby fixture, both the index and the dump are byte-stable, and the `generated_golden_matches_committed_cpp_fixture` and @@ -688,19 +617,7 @@ manifest inside the workspace tree could confuse `cargo`. The four source files are `lib.rs`, `consumer.rs`, `impl_ownership.rs`, and `self_field.rs`. -Regenerate reproducibly (identical recipe to the C++ fixture, substituting `rust`): - -```bash -mkdir -p reference/golden/rust -rm -rf /tmp/cg-fixture-rust -cp -r crates/codegraph-bench/fixtures/rust /tmp/cg-fixture-rust -cargo build --release -p codegraph-rs -CODEGRAPH_NO_DAEMON=1 CODEGRAPH_NO_WATCH=1 \ - ./target/release/codegraph init /tmp/cg-fixture-rust -cp /tmp/cg-fixture-rust/.codegraph/codegraph.db reference/golden/rust/colby.db -cargo run -p codegraph-bench --bin bench -- \ - --gen-golden reference/golden/rust/colby.db reference/golden/rust -``` +Regenerate it with `scripts/regen-goldens.sh --write rust` (the shared recipe in [Regenerating goldens](#regenerating-goldens)). `generated_golden_matches_committed_rust_fixture` and `rust_db_is_self_equivalent_to_rust_golden` enforce byte-stability. @@ -721,19 +638,7 @@ table fields. Its single `handlers.lua` source pins: - `localFn()`, `M.assignedFn()`, and `M:assignedFn()` resolving to the intended target, with dot and colon calls sharing `M::assignedFn`. -Regenerate reproducibly: - -```bash -mkdir -p reference/golden/lua -rm -rf /tmp/cg-fixture-lua -cp -r crates/codegraph-bench/fixtures/lua /tmp/cg-fixture-lua -cargo build --release -p codegraph-rs -CODEGRAPH_NO_DAEMON=1 CODEGRAPH_NO_WATCH=1 \ - ./target/release/codegraph init /tmp/cg-fixture-lua -cp /tmp/cg-fixture-lua/.codegraph/codegraph.db reference/golden/lua/colby.db -cargo run -p codegraph-bench --bin bench -- \ - --gen-golden reference/golden/lua/colby.db reference/golden/lua -``` +Regenerate it with `scripts/regen-goldens.sh --write lua` (the shared recipe in [Regenerating goldens](#regenerating-goldens)). `generated_golden_matches_committed_lua_fixture` and `lua_db_is_self_equivalent_to_lua_golden` enforce byte-stability. @@ -766,19 +671,7 @@ Six files pin BOTH values of the flag, three each way: the ranking effect observable: without the content signal the generated definition wins on name overlap alone. -Regenerate reproducibly (identical recipe to the Rust fixture, substituting `go`): - -```bash -mkdir -p reference/golden/go -rm -rf /tmp/cg-fixture-go -cp -r crates/codegraph-bench/fixtures/go /tmp/cg-fixture-go -cargo build --release -p codegraph-rs -CODEGRAPH_NO_DAEMON=1 CODEGRAPH_NO_WATCH=1 \ - ./target/release/codegraph init /tmp/cg-fixture-go -cp /tmp/cg-fixture-go/.codegraph/codegraph.db reference/golden/go/colby.db -cargo run -p codegraph-bench --bin bench -- \ - --gen-golden reference/golden/go/colby.db reference/golden/go -``` +Regenerate it with `scripts/regen-goldens.sh --write go` (the shared recipe in [Regenerating goldens](#regenerating-goldens)). `generated_golden_matches_committed_go_fixture` and `go_db_is_self_equivalent_to_go_golden` enforce byte-stability. @@ -799,41 +692,18 @@ migration order while a freshly-created one carries them in `BASE_SCHEMA` order. That is why `mini` and `godot` used to carry `idx_edges_identity` LAST while the other corpora carried it alphabetically. -For a schema migration, rebuild `mini` on the fresh schema and transplant its rows: +For a schema migration, rebuild `mini` on the fresh schema and transplant its rows. +`scripts/regen-goldens.sh --mini-transplant` does exactly this: a fresh `init` over +`crates/codegraph-bench/fixtures/mini/` creates a database from the current +`BASE_SCHEMA`, its freshly-extracted rows are replaced with mini's committed rows +(an `ATTACH` of `reference/golden/mini/colby.db`), the result is committed as +`colby.db`, and the JSON + schema goldens are re-derived from it: ```bash -# 1. A fresh index over mini's own fixture → a database created from the CURRENT -# BASE_SCHEMA, so every index lands in declaration order. -rm -rf /tmp/mini-rebuild && mkdir -p /tmp/mini-rebuild -cp -a crates/codegraph-bench/fixtures/mini/. /tmp/mini-rebuild/ -CODEGRAPH_NO_DAEMON=1 CODEGRAPH_NO_WATCH=1 \ - ./target/release/codegraph init /tmp/mini-rebuild -DB=/tmp/mini-rebuild/.codegraph/codegraph.db - -# 2. Replace its freshly-extracted rows with mini's COMMITTED rows, preserving -# the upstream-derived values instead of re-extracting them. Run from the repo -# root — the ATTACH path is relative. -sqlite3 "$DB" " - ATTACH DATABASE 'reference/golden/mini/colby.db' AS src; - BEGIN; - DELETE FROM unresolved_refs; DELETE FROM edges; DELETE FROM files; - DELETE FROM nodes; DELETE FROM project_metadata; - INSERT INTO nodes SELECT * FROM src.nodes; - INSERT INTO edges SELECT * FROM src.edges; - INSERT INTO unresolved_refs SELECT * FROM src.unresolved_refs; - INSERT INTO project_metadata SELECT * FROM src.project_metadata; - INSERT INTO files (path, content_hash, language, size, modified_at, indexed_at, node_count, errors) - SELECT path, content_hash, language, size, modified_at, indexed_at, node_count, errors FROM src.files; - COMMIT; - DETACH src;" - -# 3. Commit as the fixture's colby.db, then re-derive the JSON + schema goldens. -cp "$DB" reference/golden/mini/colby.db -cargo run -p codegraph-bench --bin bench -- \ - --gen-golden reference/golden/mini/colby.db reference/golden/mini +scripts/regen-goldens.sh --mini-transplant ``` -Two constraints in that transplant are load-bearing: +Two constraints in that transplant are load-bearing, and the script keeps both: - **The `files` insert MUST use an explicit column list.** `SELECT *` fails with `table files has N columns but 8 values were supplied` once a column is added, @@ -905,17 +775,7 @@ The CUDA blank fires for `.cu`/`.cuh` files OR any C/C++-family file whose conte carries a strong CUDA marker (`__global__`/`__device__`/`__constant__`/ `cudaStream_t`), so CUDA living in `.h`/`.hpp` headers is recognized. -Regenerate both new fixtures reproducibly (identical recipe to the C++ fixture, -substituting `metal`/`cuda`): - -```bash -rm -rf /tmp/cg-fixture-metal && cp -r crates/codegraph-bench/fixtures/metal /tmp/cg-fixture-metal -cargo build --release -p codegraph-rs -CODEGRAPH_NO_DAEMON=1 CODEGRAPH_NO_WATCH=1 ./target/release/codegraph init /tmp/cg-fixture-metal -cp /tmp/cg-fixture-metal/.codegraph/codegraph.db reference/golden/metal/colby.db -cargo run -p codegraph-bench --bin bench -- --gen-golden reference/golden/metal/colby.db reference/golden/metal -# …and the same for cuda. -``` +Regenerate both fixtures with `scripts/regen-goldens.sh --write metal cuda` (the shared recipe in [Regenerating goldens](#regenerating-goldens)). The `generated_golden_matches_committed_{metal,cuda}_fixture` and `{metal,cuda}_db_is_self_equivalent_to_{metal,cuda}_golden` tests in @@ -951,16 +811,7 @@ edges) and does NOT override `extract_modifiers` (the decorator hook). Adding th variant is byte-neutral for `colby.schema.sql` (language is a stored TEXT value, not DDL) and for the six existing goldens (none holds a `.ets` file). -Regenerate reproducibly (identical recipe to the C++ fixture, substituting -`arkts`): - -```bash -rm -rf /tmp/cg-fixture-arkts && cp -r crates/codegraph-bench/fixtures/arkts /tmp/cg-fixture-arkts -cargo build --release -p codegraph-rs -CODEGRAPH_NO_DAEMON=1 CODEGRAPH_NO_WATCH=1 ./target/release/codegraph init /tmp/cg-fixture-arkts -cp /tmp/cg-fixture-arkts/.codegraph/codegraph.db reference/golden/arkts/colby.db -cargo run -p codegraph-bench --bin bench -- --gen-golden reference/golden/arkts/colby.db reference/golden/arkts -``` +Regenerate it with `scripts/regen-goldens.sh --write arkts` (the shared recipe in [Regenerating goldens](#regenerating-goldens)). The `generated_golden_matches_committed_arkts_fixture` and `arkts_db_is_self_equivalent_to_arkts_golden` tests in @@ -1001,16 +852,7 @@ post-resolution state. No `FrameworkResolver` impl is involved; the `colby.schema.sql` (language is a stored TEXT value, not DDL) and for the seven existing goldens (none holds a `.sol` file). -Regenerate reproducibly (identical recipe to the ArkTS fixture, substituting -`solidity`): - -```bash -rm -rf /tmp/cg-fixture-solidity && cp -r crates/codegraph-bench/fixtures/solidity /tmp/cg-fixture-solidity -cargo build --release -p codegraph-rs -CODEGRAPH_NO_DAEMON=1 CODEGRAPH_NO_WATCH=1 ./target/release/codegraph init /tmp/cg-fixture-solidity -cp /tmp/cg-fixture-solidity/.codegraph/codegraph.db reference/golden/solidity/colby.db -cargo run -p codegraph-bench --bin bench -- --gen-golden reference/golden/solidity/colby.db reference/golden/solidity -``` +Regenerate it with `scripts/regen-goldens.sh --write solidity` (the shared recipe in [Regenerating goldens](#regenerating-goldens)). The `generated_golden_matches_committed_solidity_fixture` and `solidity_db_is_self_equivalent_to_solidity_golden` tests in @@ -1049,16 +891,7 @@ resolve code binds anything. Adding the variant is byte-neutral for `colby.schema.sql` (language is a stored TEXT value, not DDL) and for the eight existing goldens (none holds a `.nix` file). -Regenerate reproducibly (identical recipe to the Solidity fixture, substituting -`nix`): - -```bash -rm -rf /tmp/cg-fixture-nix && cp -r crates/codegraph-bench/fixtures/nix /tmp/cg-fixture-nix -cargo build --release -p codegraph-rs -CODEGRAPH_NO_DAEMON=1 CODEGRAPH_NO_WATCH=1 ./target/release/codegraph init /tmp/cg-fixture-nix -cp /tmp/cg-fixture-nix/.codegraph/codegraph.db reference/golden/nix/colby.db -cargo run -p codegraph-bench --bin bench -- --gen-golden reference/golden/nix/colby.db reference/golden/nix -``` +Regenerate it with `scripts/regen-goldens.sh --write nix` (the shared recipe in [Regenerating goldens](#regenerating-goldens)). The `generated_golden_matches_committed_nix_fixture` and `nix_db_is_self_equivalent_to_nix_golden` tests in @@ -1102,16 +935,7 @@ top-level-assignment `var.X` ref, and the `module.M:output.` scoped half of byte-neutral for `colby.schema.sql` (language is a stored TEXT value, not DDL) and for the nine existing goldens (none holds a `.tf`/`.tfvars`/`.tofu` file). -Regenerate reproducibly (identical recipe to the Nix fixture, substituting -`terraform`): - -```bash -rm -rf /tmp/cg-fixture-terraform && cp -r crates/codegraph-bench/fixtures/terraform /tmp/cg-fixture-terraform -cargo build --release -p codegraph-rs -CODEGRAPH_NO_DAEMON=1 CODEGRAPH_NO_WATCH=1 ./target/release/codegraph init /tmp/cg-fixture-terraform -cp /tmp/cg-fixture-terraform/.codegraph/codegraph.db reference/golden/terraform/colby.db -cargo run -p codegraph-bench --bin bench -- --gen-golden reference/golden/terraform/colby.db reference/golden/terraform -``` +Regenerate it with `scripts/regen-goldens.sh --write terraform` (the shared recipe in [Regenerating goldens](#regenerating-goldens)). The `generated_golden_matches_committed_terraform_fixture` and `terraform_db_is_self_equivalent_to_terraform_golden` tests in @@ -1153,16 +977,7 @@ dispatch, dynamic MFA targets, behaviour callback contracts, and node lookup normalize Erlang's source spelling `mod:fn/3` to the stored `mod::fn/3` form. -Regenerate reproducibly (identical recipe to the Terraform fixture, substituting -`erlang`): - -```bash -rm -rf /tmp/cg-fixture-erlang && cp -r crates/codegraph-bench/fixtures/erlang /tmp/cg-fixture-erlang -cargo build --release -p codegraph-rs -CODEGRAPH_NO_DAEMON=1 CODEGRAPH_NO_WATCH=1 ./target/release/codegraph init /tmp/cg-fixture-erlang -cp /tmp/cg-fixture-erlang/.codegraph/codegraph.db reference/golden/erlang/colby.db -cargo run -p codegraph-bench --bin bench -- --gen-golden reference/golden/erlang/colby.db reference/golden/erlang -``` +Regenerate it with `scripts/regen-goldens.sh --write erlang` (the shared recipe in [Regenerating goldens](#regenerating-goldens)). The `generated_golden_matches_committed_erlang_fixture` and `erlang_db_is_self_equivalent_to_erlang_golden` tests in @@ -1201,15 +1016,7 @@ are all **DEFERRED**. Adding the variant is byte-neutral for `colby.schema.sql` (language is a stored TEXT value, not DDL) and for the eleven existing goldens (none holds a `.cfc`/`.cfm`/`.cfs` file). -Regenerate reproducibly (identical recipe, substituting `cfml`): - -```bash -rm -rf /tmp/cg-fixture-cfml && cp -r crates/codegraph-bench/fixtures/cfml /tmp/cg-fixture-cfml -cargo build --release -p codegraph-rs -CODEGRAPH_NO_DAEMON=1 CODEGRAPH_NO_WATCH=1 ./target/release/codegraph init /tmp/cg-fixture-cfml -cp /tmp/cg-fixture-cfml/.codegraph/codegraph.db reference/golden/cfml/colby.db -cargo run -p codegraph-bench --bin bench -- --gen-golden reference/golden/cfml/colby.db reference/golden/cfml -``` +Regenerate it with `scripts/regen-goldens.sh --write cfml` (the shared recipe in [Regenerating goldens](#regenerating-goldens)). The `generated_golden_matches_committed_cfml_fixture` and `cfml_db_is_self_equivalent_to_cfml_golden` tests in @@ -1276,19 +1083,7 @@ all four canonical JSON artifacts and proving the original 14-file rows byte-for-byte unchanged. This is the required review shape for future corpus growth: an additive fixture must not silently perturb old resolution confidence. -Regenerate the committed database and canonical artifacts from a clean corpus: - -```bash -rm -rf /tmp/cg-fixture-typescript -cp -r crates/codegraph-bench/fixtures/typescript /tmp/cg-fixture-typescript -cargo build --release -p codegraph-rs -CODEGRAPH_NO_DAEMON=1 CODEGRAPH_NO_WATCH=1 \ - ./target/release/codegraph init /tmp/cg-fixture-typescript -mkdir -p reference/golden/typescript -cp /tmp/cg-fixture-typescript/.codegraph/codegraph.db reference/golden/typescript/colby.db -cargo run -p codegraph-bench --bin bench -- \ - --gen-golden reference/golden/typescript/colby.db reference/golden/typescript -``` +Regenerate it with `scripts/regen-goldens.sh --write typescript` (the shared recipe in [Regenerating goldens](#regenerating-goldens)). The `generated_golden_matches_committed_typescript_fixture` and `typescript_db_is_self_equivalent_to_typescript_golden` tests in diff --git a/scripts/regen-goldens.sh b/scripts/regen-goldens.sh new file mode 100755 index 00000000..b96f2abe --- /dev/null +++ b/scripts/regen-goldens.sh @@ -0,0 +1,213 @@ +#!/usr/bin/env bash +# Regenerate or check the golden corpora under reference/golden/. +# +# usage: +# scripts/regen-goldens.sh --check [corpus...] index each corpus afresh and compare +# its canonical artifacts with the +# committed golden (exit 1 on any +# difference); writes nothing into +# the repository +# scripts/regen-goldens.sh --write corpus... rewrite colby.db and the canonical +# artifacts of the named corpora only +# scripts/regen-goldens.sh --mini-transplant rebuild mini on the current schema, +# keeping its committed (upstream) rows +# +# With no corpus named, --check covers every directory under reference/golden/ +# that has a source corpus under crates/codegraph-bench/fixtures/. mini is never +# re-indexed: it has no re-indexable provenance (see docs/equivalence.md, "Mini +# schema rebuild"), so only --mini-transplant touches it. +# +# Binaries default to target/release/codegraph and target/release/bench; build +# them with `cargo build --locked --release -p codegraph-rs -p codegraph-bench` +# or point CODEGRAPH_BIN / BENCH_BIN elsewhere. Scratch directories come from +# mktemp and are removed on exit. +set -euo pipefail +shopt -s inherit_errexit + +script_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +repo="$(cd "$script_dir/.." && pwd)" +codegraph_bin="${CODEGRAPH_BIN:-$repo/target/release/codegraph}" +bench_bin="${BENCH_BIN:-$repo/target/release/bench}" +artifacts=(nodes.json edges.json refs.json files.json schema.sql) + +scratch=() +cleanup() { + local dir + for dir in "${scratch[@]+"${scratch[@]}"}"; do + rm -rf -- "$dir" + done +} +trap cleanup EXIT + +die() { + printf 'regen-goldens: %s\n' "$*" >&2 + exit 2 +} + +usage() { + sed -n '2,23p' "${BASH_SOURCE[0]}" | sed 's/^# \{0,1\}//' >&2 + exit 2 +} + +# Sets scratch_dir. Called directly (never in a command substitution) so the +# EXIT trap sees every directory it created. +new_scratch() { + scratch_dir="$(mktemp -d "${TMPDIR:-/tmp}/codegraph-regen-$1.XXXXXX")" + scratch+=("$scratch_dir") +} + +require_binaries() { + [ -x "$codegraph_bin" ] || die "codegraph binary not found at $codegraph_bin (build it or set CODEGRAPH_BIN)" + [ -x "$bench_bin" ] || die "bench binary not found at $bench_bin (build it or set BENCH_BIN)" +} + +default_corpora() { + local dir name + for dir in "$repo"/reference/golden/*/; do + name="$(basename "$dir")" + [ "$name" = mini ] && continue + [ -d "$repo/crates/codegraph-bench/fixtures/$name" ] || continue + printf '%s\n' "$name" + done +} + +validate_corpus() { + local name="$1" + case "$name" in + "" | *[!A-Za-z0-9_-]*) die "invalid corpus name: '$name'" ;; + mini) die "mini is not re-indexable; use --mini-transplant" ;; + esac + [ -d "$repo/crates/codegraph-bench/fixtures/$name" ] || die "no source corpus at crates/codegraph-bench/fixtures/$name" +} + +# Index one corpus into a scratch directory; sets indexed_db. +index_corpus() { + local name="$1" work log + new_scratch "$name" + work="$scratch_dir" + cp -a "$repo/crates/codegraph-bench/fixtures/$name/." "$work/" + log="$work.init.log" + scratch+=("$log") + if ! env -u CODEGRAPH_DIR CODEGRAPH_NO_DAEMON=1 CODEGRAPH_NO_WATCH=1 \ + "$codegraph_bin" init "$work" >"$log" 2>&1; then + cat "$log" >&2 + die "codegraph init failed for corpus $name" + fi + indexed_db="$work/.codegraph/codegraph.db" +} + +# Dump canonical artifacts of a database into a directory. +dump_golden() { + local db="$1" out="$2" log + mkdir -p "$out" + log="$out.gen.log" + scratch+=("$log") + if ! "$bench_bin" --gen-golden "$db" "$out" >"$log" 2>&1; then + cat "$log" >&2 + die "bench --gen-golden failed for $db" + fi +} + +check_corpus() { + local name="$1" out changed=() file + index_corpus "$name" + new_scratch "$name-golden" + out="$scratch_dir" + dump_golden "$indexed_db" "$out" + for file in "${artifacts[@]}"; do + if ! cmp -s "$out/$file" "$repo/reference/golden/$name/$file"; then + changed+=("$file") + fi + done + if [ "${#changed[@]}" -eq 0 ]; then + printf '%-12s identical\n' "$name" + return 0 + fi + printf '%-12s CHANGED: %s\n' "$name" "${changed[*]}" + return 1 +} + +write_corpus() { + local name="$1" target + index_corpus "$name" + target="$repo/reference/golden/$name" + mkdir -p "$target" + cp "$indexed_db" "$target/colby.db" + dump_golden "$target/colby.db" "$target" + printf '%-12s written\n' "$name" +} + +mini_transplant() { + command -v sqlite3 >/dev/null 2>&1 || die "--mini-transplant needs the sqlite3 CLI" + local committed="$repo/reference/golden/mini/colby.db" fresh work + [ -f "$committed" ] || die "missing $committed" + new_scratch mini + work="$scratch_dir" + cp -a "$repo/crates/codegraph-bench/fixtures/mini/." "$work/" + if ! env -u CODEGRAPH_DIR CODEGRAPH_NO_DAEMON=1 CODEGRAPH_NO_WATCH=1 \ + "$codegraph_bin" init "$work" >"$work.init.log" 2>&1; then + cat "$work.init.log" >&2 + die "codegraph init failed for mini" + fi + scratch+=("$work.init.log") + fresh="$work/.codegraph/codegraph.db" + # Keep the fresh schema_versions rows: copying mini's would leave + # MAX(version) behind the schema the fresh database already has. + sqlite3 "$fresh" " + ATTACH DATABASE '$committed' AS src; + BEGIN; + DELETE FROM unresolved_refs; DELETE FROM edges; DELETE FROM files; + DELETE FROM nodes; DELETE FROM project_metadata; + INSERT INTO nodes SELECT * FROM src.nodes; + INSERT INTO edges SELECT * FROM src.edges; + INSERT INTO unresolved_refs SELECT * FROM src.unresolved_refs; + INSERT INTO project_metadata SELECT * FROM src.project_metadata; + INSERT INTO files (path, content_hash, language, size, modified_at, indexed_at, node_count, errors) + SELECT path, content_hash, language, size, modified_at, indexed_at, node_count, errors FROM src.files; + COMMIT; + DETACH src;" + cp "$fresh" "$committed" + dump_golden "$committed" "$repo/reference/golden/mini" + printf '%-12s transplanted\n' mini +} + +[ "$#" -ge 1 ] || usage +mode="$1" +shift + +case "$mode" in + --check) + require_binaries + corpora=("$@") + if [ "${#corpora[@]}" -eq 0 ]; then + mapfile -t corpora < <(default_corpora) + fi + status=0 + for name in "${corpora[@]}"; do + validate_corpus "$name" + check_corpus "$name" || status=1 + done + exit "$status" + ;; + --write) + [ "$#" -ge 1 ] || die "--write needs at least one corpus name" + require_binaries + for name in "$@"; do + validate_corpus "$name" + done + for name in "$@"; do + write_corpus "$name" + done + ;; + --mini-transplant) + [ "$#" -eq 0 ] || die "--mini-transplant takes no corpus names" + require_binaries + mini_transplant + ;; + -h | --help) + usage + ;; + *) + die "unknown mode '$mode' (use --check, --write or --mini-transplant)" + ;; +esac From 755f83b4ae5f930853203add48dda32459746594 Mon Sep 17 00:00:00 2001 From: sunerpy Date: Sat, 10 Oct 2026 16:11:10 +0800 Subject: [PATCH 002/183] test(bench): add C#, Java, Swift, ObjC, PHP, Vue, Svelte, Python-bases and CommonJS corpora The goldens had no corpus for C#, Java, Swift, Objective-C, PHP, Vue or Svelte, and the Python corpora had no class with bases, so whole families of extraction never reached the equivalence oracle. Each new corpus is a small source tree under crates/codegraph-bench/fixtures/ with its golden under reference/golden/, generated by `scripts/regen-goldens.sh --write`. They capture today's output on purpose, misses included, so the corrections that follow show up as reviewable diffs attributed to one change each. Both golden suites now cover them, and docs/equivalence.md says what each one exercises and logs the addition. --- .../fixtures/commonjs/index.js | 5 + .../fixtures/commonjs/lib/app.js | 15 + .../fixtures/commonjs/lib/format.js | 7 + .../fixtures/commonjs/lib/service.js | 9 + .../fixtures/commonjs/lib/util.js | 5 + .../fixtures/commonjs/package.json | 8 + .../fixtures/commonjs/test/app.test.js | 12 + .../fixtures/csharp/src/App/Nested.cs | 26 + .../fixtures/csharp/src/App/Partial.cs | 12 + .../fixtures/csharp/src/App/Store.cs | 55 + .../fixtures/csharp/src/Support/TypeMap.cs | 13 + .../java/src/main/java/app/a/Field.java | 5 + .../java/app/b/EmailVerificationService.java | 5 + .../java/src/main/java/app/b/Field.java | 5 + .../java/src/main/java/app/shapes/Main.java | 35 + .../java/src/main/java/app/shapes/Shape.java | 5 + .../java/src/main/java/app/shapes/Square.java | 19 + .../java/src/main/java/app/util/MathUtil.java | 7 + .../java/src/test/java/app/MainTest.java | 21 + .../fixtures/objc/Sources/Base.h | 9 + .../fixtures/objc/Sources/Base.m | 7 + .../fixtures/objc/Sources/Sub.m | 16 + .../fixtures/php/src/Controller.php | 41 + .../fixtures/php/src/Field/Base.php | 11 + .../fixtures/php/src/Field/FirstName.php | 26 + .../fixtures/php/src/Util/helpers.php | 11 + .../fixtures/php/tests/ControllerTest.php | 14 + .../fixtures/python_bases/pkg/__init__.py | 3 + .../python_bases/pkg/models/__init__.py | 1 + .../fixtures/python_bases/pkg/models/base.py | 16 + .../fixtures/python_bases/pkg/models/user.py | 12 + .../fixtures/python_bases/pkg/service.py | 8 + .../fixtures/python_bases/tests/conftest.py | 8 + .../python_bases/tests/test_service.py | 9 + .../fixtures/svelte/package.json | 7 + .../fixtures/svelte/src/lib/Widget.svelte | 18 + .../fixtures/svelte/src/lib/util.ts | 7 + .../fixtures/swift/Sources/App/Client.swift | 18 + .../fixtures/swift/Sources/App/Models.swift | 38 + .../codegraph-bench/fixtures/vue/package.json | 7 + .../fixtures/vue/src/components/App.vue | 18 + .../fixtures/vue/src/components/Counter.vue | 27 + .../vue/src/composables/useCounter.ts | 13 + crates/codegraph-bench/tests/equivalence.rs | 9 + .../codegraph-cli/tests/golden_reextract.rs | 30 +- docs/equivalence.md | 49 +- reference/golden/commonjs/colby.db | Bin 0 -> 155648 bytes reference/golden/commonjs/edges.json | 187 +++ reference/golden/commonjs/files.json | 56 + reference/golden/commonjs/nodes.json | 376 +++++ reference/golden/commonjs/refs.json | 122 ++ reference/golden/commonjs/schema.sql | 119 ++ reference/golden/csharp/colby.db | Bin 0 -> 180224 bytes reference/golden/csharp/edges.json | 509 +++++++ reference/golden/csharp/files.json | 38 + reference/golden/csharp/nodes.json | 1036 ++++++++++++++ reference/golden/csharp/refs.json | 32 + reference/golden/csharp/schema.sql | 119 ++ reference/golden/java/colby.db | Bin 0 -> 192512 bytes reference/golden/java/edges.json | 593 ++++++++ reference/golden/java/files.json | 74 + reference/golden/java/nodes.json | 1212 +++++++++++++++++ reference/golden/java/refs.json | 72 + reference/golden/java/schema.sql | 119 ++ reference/golden/objc/colby.db | Bin 0 -> 155648 bytes reference/golden/objc/edges.json | 152 +++ reference/golden/objc/files.json | 29 + reference/golden/objc/nodes.json | 288 ++++ reference/golden/objc/refs.json | 32 + reference/golden/objc/schema.sql | 119 ++ reference/golden/php/colby.db | Bin 0 -> 155648 bytes reference/golden/php/edges.json | 356 +++++ reference/golden/php/files.json | 47 + reference/golden/php/nodes.json | 684 ++++++++++ reference/golden/php/refs.json | 62 + reference/golden/php/schema.sql | 119 ++ reference/golden/python_bases/colby.db | Bin 0 -> 163840 bytes reference/golden/python_bases/edges.json | 461 +++++++ reference/golden/python_bases/files.json | 65 + reference/golden/python_bases/nodes.json | 662 +++++++++ reference/golden/python_bases/refs.json | 102 ++ reference/golden/python_bases/schema.sql | 119 ++ reference/golden/svelte/colby.db | Bin 0 -> 155648 bytes reference/golden/svelte/edges.json | 80 ++ reference/golden/svelte/files.json | 20 + reference/golden/svelte/nodes.json | 178 +++ reference/golden/svelte/refs.json | 12 + reference/golden/svelte/schema.sql | 119 ++ reference/golden/swift/colby.db | Bin 0 -> 155648 bytes reference/golden/swift/edges.json | 338 +++++ reference/golden/swift/files.json | 20 + reference/golden/swift/nodes.json | 486 +++++++ reference/golden/swift/refs.json | 22 + reference/golden/swift/schema.sql | 119 ++ reference/golden/vue/colby.db | Bin 0 -> 155648 bytes reference/golden/vue/edges.json | 84 ++ reference/golden/vue/files.json | 29 + reference/golden/vue/nodes.json | 178 +++ reference/golden/vue/refs.json | 62 + reference/golden/vue/schema.sql | 119 ++ 100 files changed, 10525 insertions(+), 4 deletions(-) create mode 100644 crates/codegraph-bench/fixtures/commonjs/index.js create mode 100644 crates/codegraph-bench/fixtures/commonjs/lib/app.js create mode 100644 crates/codegraph-bench/fixtures/commonjs/lib/format.js create mode 100644 crates/codegraph-bench/fixtures/commonjs/lib/service.js create mode 100644 crates/codegraph-bench/fixtures/commonjs/lib/util.js create mode 100644 crates/codegraph-bench/fixtures/commonjs/package.json create mode 100644 crates/codegraph-bench/fixtures/commonjs/test/app.test.js create mode 100644 crates/codegraph-bench/fixtures/csharp/src/App/Nested.cs create mode 100644 crates/codegraph-bench/fixtures/csharp/src/App/Partial.cs create mode 100644 crates/codegraph-bench/fixtures/csharp/src/App/Store.cs create mode 100644 crates/codegraph-bench/fixtures/csharp/src/Support/TypeMap.cs create mode 100644 crates/codegraph-bench/fixtures/java/src/main/java/app/a/Field.java create mode 100644 crates/codegraph-bench/fixtures/java/src/main/java/app/b/EmailVerificationService.java create mode 100644 crates/codegraph-bench/fixtures/java/src/main/java/app/b/Field.java create mode 100644 crates/codegraph-bench/fixtures/java/src/main/java/app/shapes/Main.java create mode 100644 crates/codegraph-bench/fixtures/java/src/main/java/app/shapes/Shape.java create mode 100644 crates/codegraph-bench/fixtures/java/src/main/java/app/shapes/Square.java create mode 100644 crates/codegraph-bench/fixtures/java/src/main/java/app/util/MathUtil.java create mode 100644 crates/codegraph-bench/fixtures/java/src/test/java/app/MainTest.java create mode 100644 crates/codegraph-bench/fixtures/objc/Sources/Base.h create mode 100644 crates/codegraph-bench/fixtures/objc/Sources/Base.m create mode 100644 crates/codegraph-bench/fixtures/objc/Sources/Sub.m create mode 100644 crates/codegraph-bench/fixtures/php/src/Controller.php create mode 100644 crates/codegraph-bench/fixtures/php/src/Field/Base.php create mode 100644 crates/codegraph-bench/fixtures/php/src/Field/FirstName.php create mode 100644 crates/codegraph-bench/fixtures/php/src/Util/helpers.php create mode 100644 crates/codegraph-bench/fixtures/php/tests/ControllerTest.php create mode 100644 crates/codegraph-bench/fixtures/python_bases/pkg/__init__.py create mode 100644 crates/codegraph-bench/fixtures/python_bases/pkg/models/__init__.py create mode 100644 crates/codegraph-bench/fixtures/python_bases/pkg/models/base.py create mode 100644 crates/codegraph-bench/fixtures/python_bases/pkg/models/user.py create mode 100644 crates/codegraph-bench/fixtures/python_bases/pkg/service.py create mode 100644 crates/codegraph-bench/fixtures/python_bases/tests/conftest.py create mode 100644 crates/codegraph-bench/fixtures/python_bases/tests/test_service.py create mode 100644 crates/codegraph-bench/fixtures/svelte/package.json create mode 100644 crates/codegraph-bench/fixtures/svelte/src/lib/Widget.svelte create mode 100644 crates/codegraph-bench/fixtures/svelte/src/lib/util.ts create mode 100644 crates/codegraph-bench/fixtures/swift/Sources/App/Client.swift create mode 100644 crates/codegraph-bench/fixtures/swift/Sources/App/Models.swift create mode 100644 crates/codegraph-bench/fixtures/vue/package.json create mode 100644 crates/codegraph-bench/fixtures/vue/src/components/App.vue create mode 100644 crates/codegraph-bench/fixtures/vue/src/components/Counter.vue create mode 100644 crates/codegraph-bench/fixtures/vue/src/composables/useCounter.ts create mode 100644 reference/golden/commonjs/colby.db create mode 100644 reference/golden/commonjs/edges.json create mode 100644 reference/golden/commonjs/files.json create mode 100644 reference/golden/commonjs/nodes.json create mode 100644 reference/golden/commonjs/refs.json create mode 100644 reference/golden/commonjs/schema.sql create mode 100644 reference/golden/csharp/colby.db create mode 100644 reference/golden/csharp/edges.json create mode 100644 reference/golden/csharp/files.json create mode 100644 reference/golden/csharp/nodes.json create mode 100644 reference/golden/csharp/refs.json create mode 100644 reference/golden/csharp/schema.sql create mode 100644 reference/golden/java/colby.db create mode 100644 reference/golden/java/edges.json create mode 100644 reference/golden/java/files.json create mode 100644 reference/golden/java/nodes.json create mode 100644 reference/golden/java/refs.json create mode 100644 reference/golden/java/schema.sql create mode 100644 reference/golden/objc/colby.db create mode 100644 reference/golden/objc/edges.json create mode 100644 reference/golden/objc/files.json create mode 100644 reference/golden/objc/nodes.json create mode 100644 reference/golden/objc/refs.json create mode 100644 reference/golden/objc/schema.sql create mode 100644 reference/golden/php/colby.db create mode 100644 reference/golden/php/edges.json create mode 100644 reference/golden/php/files.json create mode 100644 reference/golden/php/nodes.json create mode 100644 reference/golden/php/refs.json create mode 100644 reference/golden/php/schema.sql create mode 100644 reference/golden/python_bases/colby.db create mode 100644 reference/golden/python_bases/edges.json create mode 100644 reference/golden/python_bases/files.json create mode 100644 reference/golden/python_bases/nodes.json create mode 100644 reference/golden/python_bases/refs.json create mode 100644 reference/golden/python_bases/schema.sql create mode 100644 reference/golden/svelte/colby.db create mode 100644 reference/golden/svelte/edges.json create mode 100644 reference/golden/svelte/files.json create mode 100644 reference/golden/svelte/nodes.json create mode 100644 reference/golden/svelte/refs.json create mode 100644 reference/golden/svelte/schema.sql create mode 100644 reference/golden/swift/colby.db create mode 100644 reference/golden/swift/edges.json create mode 100644 reference/golden/swift/files.json create mode 100644 reference/golden/swift/nodes.json create mode 100644 reference/golden/swift/refs.json create mode 100644 reference/golden/swift/schema.sql create mode 100644 reference/golden/vue/colby.db create mode 100644 reference/golden/vue/edges.json create mode 100644 reference/golden/vue/files.json create mode 100644 reference/golden/vue/nodes.json create mode 100644 reference/golden/vue/refs.json create mode 100644 reference/golden/vue/schema.sql diff --git a/crates/codegraph-bench/fixtures/commonjs/index.js b/crates/codegraph-bench/fixtures/commonjs/index.js new file mode 100644 index 00000000..855394be --- /dev/null +++ b/crates/codegraph-bench/fixtures/commonjs/index.js @@ -0,0 +1,5 @@ +'use strict'; + +const app = require('./lib/app'); + +module.exports = app; diff --git a/crates/codegraph-bench/fixtures/commonjs/lib/app.js b/crates/codegraph-bench/fixtures/commonjs/lib/app.js new file mode 100644 index 00000000..c36311ff --- /dev/null +++ b/crates/codegraph-bench/fixtures/commonjs/lib/app.js @@ -0,0 +1,15 @@ +'use strict'; + +const { default: Service } = require('./service'); +const util = require('./util'), + format = require('./format'); + +function createApp() { + const service = new Service(); + return util.wrap(format.title(service.name())); +} + +module.exports = createApp; +module.exports.setCharset = function setCharset(value) { + return format.title(value); +}; diff --git a/crates/codegraph-bench/fixtures/commonjs/lib/format.js b/crates/codegraph-bench/fixtures/commonjs/lib/format.js new file mode 100644 index 00000000..af81dba2 --- /dev/null +++ b/crates/codegraph-bench/fixtures/commonjs/lib/format.js @@ -0,0 +1,7 @@ +'use strict'; + +function title(value) { + return String(value).toUpperCase(); +} + +module.exports = { title }; diff --git a/crates/codegraph-bench/fixtures/commonjs/lib/service.js b/crates/codegraph-bench/fixtures/commonjs/lib/service.js new file mode 100644 index 00000000..5718c66a --- /dev/null +++ b/crates/codegraph-bench/fixtures/commonjs/lib/service.js @@ -0,0 +1,9 @@ +'use strict'; + +class Service { + name() { + return 'service'; + } +} + +exports.default = Service; diff --git a/crates/codegraph-bench/fixtures/commonjs/lib/util.js b/crates/codegraph-bench/fixtures/commonjs/lib/util.js new file mode 100644 index 00000000..c35c629d --- /dev/null +++ b/crates/codegraph-bench/fixtures/commonjs/lib/util.js @@ -0,0 +1,5 @@ +'use strict'; + +exports.wrap = function wrap(value) { + return '[' + value + ']'; +}; diff --git a/crates/codegraph-bench/fixtures/commonjs/package.json b/crates/codegraph-bench/fixtures/commonjs/package.json new file mode 100644 index 00000000..ae3c8557 --- /dev/null +++ b/crates/codegraph-bench/fixtures/commonjs/package.json @@ -0,0 +1,8 @@ +{ + "name": "commonjs-fixture", + "private": true, + "main": "index.js", + "dependencies": { + "supertest": "6.3.0" + } +} diff --git a/crates/codegraph-bench/fixtures/commonjs/test/app.test.js b/crates/codegraph-bench/fixtures/commonjs/test/app.test.js new file mode 100644 index 00000000..779be076 --- /dev/null +++ b/crates/codegraph-bench/fixtures/commonjs/test/app.test.js @@ -0,0 +1,12 @@ +'use strict'; + +const request = require('supertest'); +const createApp = require('../'); +const { setCharset } = require('../lib/app'); + +describe('app', () => { + it('builds', () => { + request(createApp()); + setCharset('utf-8'); + }); +}); diff --git a/crates/codegraph-bench/fixtures/csharp/src/App/Nested.cs b/crates/codegraph-bench/fixtures/csharp/src/App/Nested.cs new file mode 100644 index 00000000..3021f82f --- /dev/null +++ b/crates/codegraph-bench/fixtures/csharp/src/App/Nested.cs @@ -0,0 +1,26 @@ +namespace Outer +{ + namespace Inner + { + public class Deep + { + public void Dive() { Surface(); } + public void Surface() { } + } + } + + public class Shallow + { + public void Call() { new Inner.Deep().Dive(); } + } +} + +namespace JetBrains.Annotations +{ + public class Guard { } +} + +namespace Second +{ + public class Two { } +} diff --git a/crates/codegraph-bench/fixtures/csharp/src/App/Partial.cs b/crates/codegraph-bench/fixtures/csharp/src/App/Partial.cs new file mode 100644 index 00000000..eec4955d --- /dev/null +++ b/crates/codegraph-bench/fixtures/csharp/src/App/Partial.cs @@ -0,0 +1,12 @@ +namespace App +{ + public partial class Report + { + public void Render() { Header(); } + } + + public partial class Report + { + private void Header() { } + } +} diff --git a/crates/codegraph-bench/fixtures/csharp/src/App/Store.cs b/crates/codegraph-bench/fixtures/csharp/src/App/Store.cs new file mode 100644 index 00000000..d18f2478 --- /dev/null +++ b/crates/codegraph-bench/fixtures/csharp/src/App/Store.cs @@ -0,0 +1,55 @@ +using System; +using System.IO; +using static System.Math; +using App.Support; + +namespace App +{ + public interface IStore + { + void Save(int x); + } + + public class BaseStore + { + protected int Seed() { return 1; } + } + + // base_list: one class base plus one interface. + public class Store : BaseStore, IStore + { + private readonly TextWriter _innerWriter; + private int seed = Seed(); + private readonly TypeMap map = new(); + + public Store(TextWriter writer) { _innerWriter = writer; } + + public int Count { get { return Measure(); } } + public int Size => Measure(); + public TypeMap Map { get; set; } + + public void Save(int x) + { + _innerWriter.WriteLine(x); + Log("saved"); + var squared = Pow(x, 2); + } + + public void Log(string message) => Log(message, 0); + public void Log(string message, int level) { Flush(); } + + private int Measure() { return seed; } + private void Flush() { } + } + + public class User + { + public Store Make(Store s) + { + TypeMap t = new TypeMap(); + var store = new Store(Console.Out); + store.Save(1); + return store; + } + } +} diff --git a/crates/codegraph-bench/fixtures/csharp/src/Support/TypeMap.cs b/crates/codegraph-bench/fixtures/csharp/src/Support/TypeMap.cs new file mode 100644 index 00000000..35fc3878 --- /dev/null +++ b/crates/codegraph-bench/fixtures/csharp/src/Support/TypeMap.cs @@ -0,0 +1,13 @@ +namespace App.Support +{ + public class TypeMap + { + public int Lookup(string key) { return 0; } + } + + public class Other + { + public void Flush() { } + public void WriteLine(int value) { } + } +} diff --git a/crates/codegraph-bench/fixtures/java/src/main/java/app/a/Field.java b/crates/codegraph-bench/fixtures/java/src/main/java/app/a/Field.java new file mode 100644 index 00000000..c632ab40 --- /dev/null +++ b/crates/codegraph-bench/fixtures/java/src/main/java/app/a/Field.java @@ -0,0 +1,5 @@ +package app.a; + +public class Field { + public String name() { return "a"; } +} diff --git a/crates/codegraph-bench/fixtures/java/src/main/java/app/b/EmailVerificationService.java b/crates/codegraph-bench/fixtures/java/src/main/java/app/b/EmailVerificationService.java new file mode 100644 index 00000000..1a27bd73 --- /dev/null +++ b/crates/codegraph-bench/fixtures/java/src/main/java/app/b/EmailVerificationService.java @@ -0,0 +1,5 @@ +package app.b; + +public class EmailVerificationService { + public boolean verify(String token) { return token != null; } +} diff --git a/crates/codegraph-bench/fixtures/java/src/main/java/app/b/Field.java b/crates/codegraph-bench/fixtures/java/src/main/java/app/b/Field.java new file mode 100644 index 00000000..f22e1a64 --- /dev/null +++ b/crates/codegraph-bench/fixtures/java/src/main/java/app/b/Field.java @@ -0,0 +1,5 @@ +package app.b; + +public class Field { + public String name() { return "b"; } +} diff --git a/crates/codegraph-bench/fixtures/java/src/main/java/app/shapes/Main.java b/crates/codegraph-bench/fixtures/java/src/main/java/app/shapes/Main.java new file mode 100644 index 00000000..73252388 --- /dev/null +++ b/crates/codegraph-bench/fixtures/java/src/main/java/app/shapes/Main.java @@ -0,0 +1,35 @@ +package app.shapes; + +import java.util.ArrayList; +import java.util.List; + +public class Main { + enum Op { + ADD { + int apply(int a, int b) { return combine(a, b); } + }, + SUB { + int apply(int a, int b) { return a - b; } + }; + + abstract int apply(int a, int b); + + static int combine(int a, int b) { return a + b; } + } + + Square build(Square p) { + Square s = new Square(2); + List all = new ArrayList<>(); + all.add(s); + Runnable r = new Runnable() { + @Override + public void run() { helper(); } + }; + r.run(); + return s; + } + + int picks() { return Square.pick(1) + Square.pick("x"); } + + void helper() { } +} diff --git a/crates/codegraph-bench/fixtures/java/src/main/java/app/shapes/Shape.java b/crates/codegraph-bench/fixtures/java/src/main/java/app/shapes/Shape.java new file mode 100644 index 00000000..ad1d372a --- /dev/null +++ b/crates/codegraph-bench/fixtures/java/src/main/java/app/shapes/Shape.java @@ -0,0 +1,5 @@ +package app.shapes; + +public interface Shape { + int area(); +} diff --git a/crates/codegraph-bench/fixtures/java/src/main/java/app/shapes/Square.java b/crates/codegraph-bench/fixtures/java/src/main/java/app/shapes/Square.java new file mode 100644 index 00000000..a976e2c6 --- /dev/null +++ b/crates/codegraph-bench/fixtures/java/src/main/java/app/shapes/Square.java @@ -0,0 +1,19 @@ +package app.shapes; + +import static app.util.MathUtil.square; + +public class Square extends Base implements Shape { + private final int side; + + public Square(int side) { this.side = side; } + + @Override + public int area() { return square(side); } + + public static int pick(int x) { return x; } + public static int pick(String s) { return s.length(); } +} + +class Base { + void start() { } +} diff --git a/crates/codegraph-bench/fixtures/java/src/main/java/app/util/MathUtil.java b/crates/codegraph-bench/fixtures/java/src/main/java/app/util/MathUtil.java new file mode 100644 index 00000000..cf84f96c --- /dev/null +++ b/crates/codegraph-bench/fixtures/java/src/main/java/app/util/MathUtil.java @@ -0,0 +1,7 @@ +package app.util; + +public final class MathUtil { + private MathUtil() { } + + public static int square(int x) { return x * x; } +} diff --git a/crates/codegraph-bench/fixtures/java/src/test/java/app/MainTest.java b/crates/codegraph-bench/fixtures/java/src/test/java/app/MainTest.java new file mode 100644 index 00000000..1e614898 --- /dev/null +++ b/crates/codegraph-bench/fixtures/java/src/test/java/app/MainTest.java @@ -0,0 +1,21 @@ +package app; + +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; + +import app.a.Field; +import java.lang.reflect.Method; + +public class MainTest { + static class Field { + String id() { return "nested"; } + } + + void checks() throws Exception { + Object service = mock(Object.class); + verify(service); + app.a.Field real = new app.a.Field(); + real.name(); + Method m = Object.class.getMethod("toString"); + } +} diff --git a/crates/codegraph-bench/fixtures/objc/Sources/Base.h b/crates/codegraph-bench/fixtures/objc/Sources/Base.h new file mode 100644 index 00000000..003089bb --- /dev/null +++ b/crates/codegraph-bench/fixtures/objc/Sources/Base.h @@ -0,0 +1,9 @@ +#import + +@protocol Doer +- (void)work; +@end + +@interface Base : NSObject +- (void)ping; +@end diff --git a/crates/codegraph-bench/fixtures/objc/Sources/Base.m b/crates/codegraph-bench/fixtures/objc/Sources/Base.m new file mode 100644 index 00000000..6e18f595 --- /dev/null +++ b/crates/codegraph-bench/fixtures/objc/Sources/Base.m @@ -0,0 +1,7 @@ +#import "Base.h" + +@implementation Base +- (void)ping { + NSLog(@"ping"); +} +@end diff --git a/crates/codegraph-bench/fixtures/objc/Sources/Sub.m b/crates/codegraph-bench/fixtures/objc/Sources/Sub.m new file mode 100644 index 00000000..56341e94 --- /dev/null +++ b/crates/codegraph-bench/fixtures/objc/Sources/Sub.m @@ -0,0 +1,16 @@ +#import "Base.h" + +static void cancel(void) { } + +@interface Sub : Base +- (void)work; +@end + +@implementation Sub +- (void)work { + [self ping]; + [super ping]; + [Base new]; + cancel(); +} +@end diff --git a/crates/codegraph-bench/fixtures/php/src/Controller.php b/crates/codegraph-bench/fixtures/php/src/Controller.php new file mode 100644 index 00000000..f7fd4e5c --- /dev/null +++ b/crates/codegraph-bench/fixtures/php/src/Controller.php @@ -0,0 +1,41 @@ + trans('title'), + 'price' => formatprice(300), + 'name' => $name->render() . $other->render(), + ]; + } + + public function parentCall(): string + { + return parent::describe(); + } +} + +class Base +{ + public function describe(): string + { + return 'base'; + } +} diff --git a/crates/codegraph-bench/fixtures/php/src/Field/Base.php b/crates/codegraph-bench/fixtures/php/src/Field/Base.php new file mode 100644 index 00000000..265e308f --- /dev/null +++ b/crates/codegraph-bench/fixtures/php/src/Field/Base.php @@ -0,0 +1,11 @@ +label() . self::suffix(); + } + + private static function suffix(): string + { + return '!'; + } +} diff --git a/crates/codegraph-bench/fixtures/php/src/Util/helpers.php b/crates/codegraph-bench/fixtures/php/src/Util/helpers.php new file mode 100644 index 00000000..0989aa6c --- /dev/null +++ b/crates/codegraph-bench/fixtures/php/src/Util/helpers.php @@ -0,0 +1,11 @@ +assertEquals(3, count($controller->show())); + } +} diff --git a/crates/codegraph-bench/fixtures/python_bases/pkg/__init__.py b/crates/codegraph-bench/fixtures/python_bases/pkg/__init__.py new file mode 100644 index 00000000..56e3adaf --- /dev/null +++ b/crates/codegraph-bench/fixtures/python_bases/pkg/__init__.py @@ -0,0 +1,3 @@ +from .models import User + +__all__ = ["User"] diff --git a/crates/codegraph-bench/fixtures/python_bases/pkg/models/__init__.py b/crates/codegraph-bench/fixtures/python_bases/pkg/models/__init__.py new file mode 100644 index 00000000..f4a2da08 --- /dev/null +++ b/crates/codegraph-bench/fixtures/python_bases/pkg/models/__init__.py @@ -0,0 +1 @@ +from .user import * diff --git a/crates/codegraph-bench/fixtures/python_bases/pkg/models/base.py b/crates/codegraph-bench/fixtures/python_bases/pkg/models/base.py new file mode 100644 index 00000000..66fe1827 --- /dev/null +++ b/crates/codegraph-bench/fixtures/python_bases/pkg/models/base.py @@ -0,0 +1,16 @@ +class Mixin: + def describe(self): + return type(self).__name__ + + +class Base: + @classmethod + def create(cls): + return cls() + + def ping(self): + return 1 + + +class Meta(type): + pass diff --git a/crates/codegraph-bench/fixtures/python_bases/pkg/models/user.py b/crates/codegraph-bench/fixtures/python_bases/pkg/models/user.py new file mode 100644 index 00000000..da79a545 --- /dev/null +++ b/crates/codegraph-bench/fixtures/python_bases/pkg/models/user.py @@ -0,0 +1,12 @@ +from .base import Base, Meta, Mixin + + +class User(Base, Mixin, metaclass=Meta): + def run(self): + super().ping() + return self.describe() + + +class Admin(User): + def run(self): + return super().run() diff --git a/crates/codegraph-bench/fixtures/python_bases/pkg/service.py b/crates/codegraph-bench/fixtures/python_bases/pkg/service.py new file mode 100644 index 00000000..97ce395f --- /dev/null +++ b/crates/codegraph-bench/fixtures/python_bases/pkg/service.py @@ -0,0 +1,8 @@ +from pkg import models +from pkg.models import User + + +def make_user(): + user = User.create() + other = models.User() + return user.run(), other.run() diff --git a/crates/codegraph-bench/fixtures/python_bases/tests/conftest.py b/crates/codegraph-bench/fixtures/python_bases/tests/conftest.py new file mode 100644 index 00000000..0f79d83f --- /dev/null +++ b/crates/codegraph-bench/fixtures/python_bases/tests/conftest.py @@ -0,0 +1,8 @@ +import pytest + +from pkg.models.user import Admin + + +@pytest.fixture +def admin(): + return Admin() diff --git a/crates/codegraph-bench/fixtures/python_bases/tests/test_service.py b/crates/codegraph-bench/fixtures/python_bases/tests/test_service.py new file mode 100644 index 00000000..a7753b5b --- /dev/null +++ b/crates/codegraph-bench/fixtures/python_bases/tests/test_service.py @@ -0,0 +1,9 @@ +from pkg.service import make_user + + +def test_admin(admin): + assert admin.run() + + +def test_make_user(): + assert make_user() diff --git a/crates/codegraph-bench/fixtures/svelte/package.json b/crates/codegraph-bench/fixtures/svelte/package.json new file mode 100644 index 00000000..bd6c854e --- /dev/null +++ b/crates/codegraph-bench/fixtures/svelte/package.json @@ -0,0 +1,7 @@ +{ + "name": "svelte-fixture", + "private": true, + "devDependencies": { + "svelte": "4.2.0" + } +} diff --git a/crates/codegraph-bench/fixtures/svelte/src/lib/Widget.svelte b/crates/codegraph-bench/fixtures/svelte/src/lib/Widget.svelte new file mode 100644 index 00000000..e2a50690 --- /dev/null +++ b/crates/codegraph-bench/fixtures/svelte/src/lib/Widget.svelte @@ -0,0 +1,18 @@ + + + + + diff --git a/crates/codegraph-bench/fixtures/svelte/src/lib/util.ts b/crates/codegraph-bench/fixtures/svelte/src/lib/util.ts new file mode 100644 index 00000000..cbe338c0 --- /dev/null +++ b/crates/codegraph-bench/fixtures/svelte/src/lib/util.ts @@ -0,0 +1,7 @@ +export function doSetup(): number { + return 1; +} + +export function formatLabel(value: string): string { + return value.trim(); +} diff --git a/crates/codegraph-bench/fixtures/swift/Sources/App/Client.swift b/crates/codegraph-bench/fixtures/swift/Sources/App/Client.swift new file mode 100644 index 00000000..ede4b2a2 --- /dev/null +++ b/crates/codegraph-bench/fixtures/swift/Sources/App/Client.swift @@ -0,0 +1,18 @@ +import Foundation + +final class Client { + let cache = Cache() + + func run() -> String { + let view = HomeView(title: "home") + cache.store(key: "a") + cache.store(key: "b", value: 2) + let smallest = min(1, 2) + _ = smallest + return API.DependencyController.GetRoute.query() + view.subtitle() + } +} + +#Preview { + HomeView(title: "preview") +} diff --git a/crates/codegraph-bench/fixtures/swift/Sources/App/Models.swift b/crates/codegraph-bench/fixtures/swift/Sources/App/Models.swift new file mode 100644 index 00000000..2e2f7dcd --- /dev/null +++ b/crates/codegraph-bench/fixtures/swift/Sources/App/Models.swift @@ -0,0 +1,38 @@ +import Foundation + +protocol Renderable { + func render() -> String +} + +struct HomeView: Renderable { + let title: String + + func render() -> String { return format(title) } + + private func format(_ value: String) -> String { return value.uppercased() } +} + +extension HomeView { + func subtitle() -> String { return render() + "!" } +} + +enum API { + enum DependencyController { + struct GetRoute { + static func query() -> String { return "deps" } + } + } +} + +enum PackageController { + struct GetRoute { + static func query() -> String { return "packages" } + } +} + +final class Cache { + var imageCachedType: Int = 0 + + func store(key: String, value: Int) { imageCachedType = value } + func store(key: String) { store(key: key, value: 0) } +} diff --git a/crates/codegraph-bench/fixtures/vue/package.json b/crates/codegraph-bench/fixtures/vue/package.json new file mode 100644 index 00000000..1ed57b43 --- /dev/null +++ b/crates/codegraph-bench/fixtures/vue/package.json @@ -0,0 +1,7 @@ +{ + "name": "vue-fixture", + "private": true, + "dependencies": { + "vue": "3.4.0" + } +} diff --git a/crates/codegraph-bench/fixtures/vue/src/components/App.vue b/crates/codegraph-bench/fixtures/vue/src/components/App.vue new file mode 100644 index 00000000..ab5ef98d --- /dev/null +++ b/crates/codegraph-bench/fixtures/vue/src/components/App.vue @@ -0,0 +1,18 @@ + + + diff --git a/crates/codegraph-bench/fixtures/vue/src/components/Counter.vue b/crates/codegraph-bench/fixtures/vue/src/components/Counter.vue new file mode 100644 index 00000000..ab747775 --- /dev/null +++ b/crates/codegraph-bench/fixtures/vue/src/components/Counter.vue @@ -0,0 +1,27 @@ + + + diff --git a/crates/codegraph-bench/fixtures/vue/src/composables/useCounter.ts b/crates/codegraph-bench/fixtures/vue/src/composables/useCounter.ts new file mode 100644 index 00000000..01eb464a --- /dev/null +++ b/crates/codegraph-bench/fixtures/vue/src/composables/useCounter.ts @@ -0,0 +1,13 @@ +import { ref } from 'vue'; + +export function useCounter(start: number) { + const count = ref(start); + function increment() { + count.value += 1; + } + return { count, increment }; +} + +export function helper(): number { + return 1; +} diff --git a/crates/codegraph-bench/tests/equivalence.rs b/crates/codegraph-bench/tests/equivalence.rs index cd8d783d..649a9ac0 100644 --- a/crates/codegraph-bench/tests/equivalence.rs +++ b/crates/codegraph-bench/tests/equivalence.rs @@ -87,6 +87,15 @@ golden_corpora! { "terraform" => generated_golden_matches_committed_terraform_fixture, terraform_db_is_self_equivalent_to_terraform_golden; "erlang" => generated_golden_matches_committed_erlang_fixture, erlang_db_is_self_equivalent_to_erlang_golden; "cfml" => generated_golden_matches_committed_cfml_fixture, cfml_db_is_self_equivalent_to_cfml_golden; + "csharp" => generated_golden_matches_committed_csharp_fixture, csharp_db_is_self_equivalent_to_csharp_golden; + "java" => generated_golden_matches_committed_java_fixture, java_db_is_self_equivalent_to_java_golden; + "swift" => generated_golden_matches_committed_swift_fixture, swift_db_is_self_equivalent_to_swift_golden; + "objc" => generated_golden_matches_committed_objc_fixture, objc_db_is_self_equivalent_to_objc_golden; + "php" => generated_golden_matches_committed_php_fixture, php_db_is_self_equivalent_to_php_golden; + "vue" => generated_golden_matches_committed_vue_fixture, vue_db_is_self_equivalent_to_vue_golden; + "svelte" => generated_golden_matches_committed_svelte_fixture, svelte_db_is_self_equivalent_to_svelte_golden; + "python_bases" => generated_golden_matches_committed_python_bases_fixture, python_bases_db_is_self_equivalent_to_python_bases_golden; + "commonjs" => generated_golden_matches_committed_commonjs_fixture, commonjs_db_is_self_equivalent_to_commonjs_golden; } #[test] diff --git a/crates/codegraph-cli/tests/golden_reextract.rs b/crates/codegraph-cli/tests/golden_reextract.rs index ef51a208..84fefa46 100644 --- a/crates/codegraph-cli/tests/golden_reextract.rs +++ b/crates/codegraph-cli/tests/golden_reextract.rs @@ -44,8 +44,34 @@ macro_rules! reextract_corpora { } reextract_corpora!( - arkts, cfml, cpp, cuda, dart, erlang, go, godot, kotlin, lua, metal, nix, python, ruby, rust, - scala, solidity, terraform, typescript, + arkts, + cfml, + commonjs, + cpp, + csharp, + cuda, + dart, + erlang, + go, + godot, + java, + kotlin, + lua, + metal, + nix, + objc, + php, + python, + python_bases, + ruby, + rust, + scala, + solidity, + svelte, + swift, + terraform, + typescript, + vue, ); #[test] diff --git a/docs/equivalence.md b/docs/equivalence.md index aed9c5a1..e8e19223 100644 --- a/docs/equivalence.md +++ b/docs/equivalence.md @@ -184,8 +184,9 @@ Every intentional golden change is recorded here with the commit, the corpora and artifacts it changed, why the graph change is intended, and the command that regenerated it. Older regenerations are described in each corpus section. -| Commit | Corpus | Artifacts | Intent | Command | -| ------ | ------ | --------- | ------ | ------- | +| Commit | Corpus | Artifacts | Intent | Command | +| --------------------------------------------------------------------------------------------- | ------------------------------------------------------------------- | ----------------- | ----------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------- | +| `test(bench): add C#, Java, Swift, ObjC, PHP, Vue, Svelte, Python-bases and CommonJS corpora` | csharp, java, swift, objc, php, vue, svelte, python_bases, commonjs | all (new corpora) | baseline capture of the current extraction and resolution, including the misses later commits correct | `scripts/regen-goldens.sh --write csharp java swift objc php vue svelte python_bases commonjs` | The canonicalizer strips inherently unstable timestamp columns (`nodes.updated_at`, `files.modified_at`, `files.indexed_at`), parses JSON text @@ -1092,6 +1093,50 @@ self-equivalence. As for every fixture below the Godot caveats, do not compare `colby.db` bytes; compare the four JSON artifacts byte-for-byte and compare `schema.sql` as a normalized statement set when statement order differs. +### Baseline corpora for C#, Java, Swift, Objective-C, PHP, Vue, Svelte, Python bases and CommonJS + +These nine corpora pin languages and shapes the older corpora never exercised. +Each was captured with the extraction and resolution as they stood when the +corpus was added, deliberately including what that code got wrong, so every later +correction shows up as a diff attributable to one change: + +- **`csharp`**: a `base_list` with a class base and an interface, target-typed + `new()`, property accessor bodies and expression-bodied members, field + initializers, nested and sibling block namespaces, `using static`, a partial + class split across declarations, and an overload that delegates to its + sibling. +- **`java`**: `extends`/`implements`, object creation and an anonymous class, + enum constants with their own bodies, a static import, two same-named `Field` + classes in different packages, test-only imports of Mockito helpers, and the + `pick(int)`/`pick(String)` overload pair. +- **`swift`**: a protocol and its conformance, an extension of a struct, nested + types reached through a type path (`API.DependencyController.GetRoute.query()`) + beside a same-named path elsewhere, an overload family distinguished by + argument labels, and a `#Preview` block. +- **`objc`**: `@interface Sub : Base `, message sends to `self`, `super` + and a class, and a plain C call inside a method. +- **`php`**: namespaces with `use` imports, `extends`/`implements`, + `$this`/`self`/`parent` calls, calls written after `=>` in an array literal, a + case-variant call to a global function, and a namespace-qualified class name + (`Field\FirstName::make()`). +- **`vue`**: a ` diff --git a/crates/codegraph-bench/fixtures/routers/apps/nuxt/pages/users/[id].vue b/crates/codegraph-bench/fixtures/routers/apps/nuxt/pages/users/[id].vue new file mode 100644 index 00000000..897a2f60 --- /dev/null +++ b/crates/codegraph-bench/fixtures/routers/apps/nuxt/pages/users/[id].vue @@ -0,0 +1,5 @@ + + diff --git a/crates/codegraph-bench/fixtures/routers/apps/react-router/package.json b/crates/codegraph-bench/fixtures/routers/apps/react-router/package.json new file mode 100644 index 00000000..1a9a657c --- /dev/null +++ b/crates/codegraph-bench/fixtures/routers/apps/react-router/package.json @@ -0,0 +1 @@ +{ "name": "react-router-app", "private": true, "dependencies": { "react": "18.2.0", "react-router-dom": "6.22.0" } } diff --git a/crates/codegraph-bench/fixtures/routers/apps/react-router/src/main.tsx b/crates/codegraph-bench/fixtures/routers/apps/react-router/src/main.tsx new file mode 100644 index 00000000..cbb3a8b3 --- /dev/null +++ b/crates/codegraph-bench/fixtures/routers/apps/react-router/src/main.tsx @@ -0,0 +1,24 @@ +import { createBrowserRouter, Link, useNavigate } from 'react-router-dom'; +import { Home } from './pages/Home'; +import { Layout } from './pages/Layout'; + +export const router = createBrowserRouter([ + { + path: '/', + element: , + children: [ + { index: true, element: }, + { path: 'team', lazy: () => import('./pages/Team') }, + ], + }, +]); + +export function Nav() { + const navigate = useNavigate(); + return ( + + ); +} diff --git a/crates/codegraph-bench/fixtures/routers/apps/react-router/src/pages/Home.tsx b/crates/codegraph-bench/fixtures/routers/apps/react-router/src/pages/Home.tsx new file mode 100644 index 00000000..028daa22 --- /dev/null +++ b/crates/codegraph-bench/fixtures/routers/apps/react-router/src/pages/Home.tsx @@ -0,0 +1,3 @@ +export function Home() { + return

Home

; +} diff --git a/crates/codegraph-bench/fixtures/routers/apps/react-router/src/pages/Layout.tsx b/crates/codegraph-bench/fixtures/routers/apps/react-router/src/pages/Layout.tsx new file mode 100644 index 00000000..3bbb8f35 --- /dev/null +++ b/crates/codegraph-bench/fixtures/routers/apps/react-router/src/pages/Layout.tsx @@ -0,0 +1,5 @@ +import { Outlet } from 'react-router-dom'; + +export function Layout() { + return
; +} diff --git a/crates/codegraph-bench/fixtures/routers/apps/react-router/src/pages/Team.tsx b/crates/codegraph-bench/fixtures/routers/apps/react-router/src/pages/Team.tsx new file mode 100644 index 00000000..525cdebc --- /dev/null +++ b/crates/codegraph-bench/fixtures/routers/apps/react-router/src/pages/Team.tsx @@ -0,0 +1,3 @@ +export function Component() { + return

Team

; +} diff --git a/crates/codegraph-bench/fixtures/routers/apps/sveltekit/package.json b/crates/codegraph-bench/fixtures/routers/apps/sveltekit/package.json new file mode 100644 index 00000000..085a2d67 --- /dev/null +++ b/crates/codegraph-bench/fixtures/routers/apps/sveltekit/package.json @@ -0,0 +1 @@ +{ "name": "sveltekit-app", "private": true, "devDependencies": { "@sveltejs/kit": "2.5.0", "svelte": "4.2.0" } } diff --git a/crates/codegraph-bench/fixtures/routers/apps/sveltekit/src/routes/(marketing)/about/+page.svelte b/crates/codegraph-bench/fixtures/routers/apps/sveltekit/src/routes/(marketing)/about/+page.svelte new file mode 100644 index 00000000..ae068f61 --- /dev/null +++ b/crates/codegraph-bench/fixtures/routers/apps/sveltekit/src/routes/(marketing)/about/+page.svelte @@ -0,0 +1 @@ +

About

diff --git a/crates/codegraph-bench/fixtures/routers/apps/sveltekit/src/routes/+page.svelte b/crates/codegraph-bench/fixtures/routers/apps/sveltekit/src/routes/+page.svelte new file mode 100644 index 00000000..9d1354f7 --- /dev/null +++ b/crates/codegraph-bench/fixtures/routers/apps/sveltekit/src/routes/+page.svelte @@ -0,0 +1,7 @@ + + diff --git a/crates/codegraph-bench/fixtures/routers/apps/sveltekit/src/routes/blog/[slug=slug]/+page.ts b/crates/codegraph-bench/fixtures/routers/apps/sveltekit/src/routes/blog/[slug=slug]/+page.ts new file mode 100644 index 00000000..34a9bafe --- /dev/null +++ b/crates/codegraph-bench/fixtures/routers/apps/sveltekit/src/routes/blog/[slug=slug]/+page.ts @@ -0,0 +1,8 @@ +import { redirect } from '@sveltejs/kit'; + +export function load({ params }) { + if (!params.slug) { + redirect(307, '/'); + } + return { slug: params.slug }; +} diff --git a/crates/codegraph-bench/fixtures/routers/apps/tanstack/package.json b/crates/codegraph-bench/fixtures/routers/apps/tanstack/package.json new file mode 100644 index 00000000..68484280 --- /dev/null +++ b/crates/codegraph-bench/fixtures/routers/apps/tanstack/package.json @@ -0,0 +1 @@ +{ "name": "tanstack-app", "private": true, "dependencies": { "@tanstack/react-router": "1.15.0", "react": "18.2.0" } } diff --git a/crates/codegraph-bench/fixtures/routers/apps/tanstack/src/routes/index.tsx b/crates/codegraph-bench/fixtures/routers/apps/tanstack/src/routes/index.tsx new file mode 100644 index 00000000..d2853772 --- /dev/null +++ b/crates/codegraph-bench/fixtures/routers/apps/tanstack/src/routes/index.tsx @@ -0,0 +1,9 @@ +import { createFileRoute, Link } from '@tanstack/react-router'; + +export const Route = createFileRoute('/')({ + component: Index, +}); + +function Index() { + return Posts; +} diff --git a/crates/codegraph-bench/fixtures/routers/apps/tanstack/src/routes/posts.tsx b/crates/codegraph-bench/fixtures/routers/apps/tanstack/src/routes/posts.tsx new file mode 100644 index 00000000..dbd4e64e --- /dev/null +++ b/crates/codegraph-bench/fixtures/routers/apps/tanstack/src/routes/posts.tsx @@ -0,0 +1,9 @@ +import { createFileRoute } from '@tanstack/react-router'; + +export const Route = createFileRoute('/posts')({ + component: Posts, +}); + +function Posts() { + return