Skip to content

merge-batch-graphs.py silently deletes all Swift (and Rust/Ruby/PHP) test coverage: _TEST_NAME_PATTERNS has no .swift key #646

Description

@SlyWombat

Version: plugin 2.9.4 · Skill: /understand · File: skills/understand/merge-batch-graphs.py

Summary

_TEST_NAME_PATTERNS (line 113) has no .swift entry, and is_test_path returns False when the extension is unknown:

_TEST_NAME_PATTERNS: dict[str, tuple[tuple[str, ...], tuple[str, ...]]] = {
    ".go":    ((), ("_test",)),
    ".py":    (("test_",), ("_test",)),
    ".java":  ((), ("Test", "Tests", "IT")),
    ".kt":    ((), ("Test", "Tests")),
    ".scala": ((), ("Spec", "Suite", "Test", "Tests")),
    ".cs":    ((), ("Test", "Tests")),
    ".c" / ".cpp" / ".cc": …
}                                  # no .swift, .rs, .rb, .php, .m

# is_test_path (~line 342)
patterns = _TEST_NAME_PATTERNS.get(ext)
if patterns is None:
    return False

So every Swift file classifies as production. The tested_by linker then sees production → production for each iOS pair and drops it as semantically broken — 100% of iOS test coverage is deleted at merge time, with no error and only an aggregate line in the report.

Impact

On a repo with Windows/Android/iOS ports, the merge printed:

Fixed (33 corrections):
    33 × tested_by edges dropped (orphan endpoint or test↔test / prod↔prod pair)

Android and C# passed clean because .kt and .cs are in the table. Of those 33, six were genuine iOS pairs (PdfEngine+Search.swiftSearchTests.swift, etc.) that the analyzers had correctly emitted production → test. They were only recovered because the assemble-reviewer sampled the batch files and noticed; a normal run would ship a graph asserting the iOS layer has no tests at all. Any dashboard filter on the tested tag under-reports in the same way — the linker's tagging pass never fires for those nodes either.

The failure is silent and asymmetric, which is what makes it dangerous: a polyglot repo looks fine in aggregate while one language's coverage vanishes.

Repro

  1. Any project with Foo.swift and FooTests.swift where an analyzer emits tested_by: file:Foo.swift → file:FooTests.swift.
  2. Run /understand.
  3. The edge is dropped as a prod↔prod pair; assembled-graph.json has no iOS tested_by edges.

Fix

One line, edge-preserving:

".swift": ((), ("Tests", "Test", "Spec")),

Worth adding the other missing ecosystems at the same time — .rs (test_ / _test), .rb (_spec, _test, test_), .php (Test), .m/.mm (Tests) — since generate-ignore.mjs already ships suggested test-file patterns for Swift, Rust and Ruby, so the pipeline clearly intends to support them.

A defensive follow-up: when is_test_path gets an extension it has no patterns for, that is worth counting and printing (e.g. note: 21 .swift files had no test-name patterns), so the next missing language surfaces as a line in the report rather than as vanished edges.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions