Skip to content

Maintain the setup cache, and fix two id-related defects - #43

Open
davidanthoff wants to merge 4 commits into
mainfrom
tic-followups
Open

Maintain the setup cache, and fix two id-related defects#43
davidanthoff wants to merge 4 commits into
mainfrom
tic-followups

Conversation

@davidanthoff

Copy link
Copy Markdown
Member

Four changes that share a branch because they all touch run bookkeeping. Rebased onto the test speedup on main.

loaded_setups was written and never maintained

It recorded what a @testmodule/@testsnippet cost and printed, and nothing ever invalidated it. A stale cost then mis-ranked the scheduler's setup-affinity model.

Now invalidated from _setup_testrun_on_process! when a run ships different code for a setup, or drops it. That one hook covers the Revise case too: a pooled process is set up for every run, so revised source arrives as changed setup code through the same path.

Deliberately conservative — a setup cached under an earlier run that this one does not mention is dropped rather than assumed intact. Losing a cost hint costs a scheduling opportunity; keeping a wrong one produces a worse schedule and hides why.

tr.test_items was keyed by test item id alone

Ids are scoped to their package (julia-vscode/JuliaWorkspaces.jl#257), so the same package checked out into two folders of one workspace mints the same id from both. The entries collapsed, and the surviving item's source then ran twice while the other never ran at all — silently.

Keyed by (id, package_uri) now, matching remaining_work. execute_testrun also asserts every item is individually addressable. That assertion should never fire — discovery already suffixes duplicate labels with #N — which is exactly why it is worth having: a future mistake in the id scheme surfaces as an error instead of a test that quietly stopped running.

TestrunResult now carries the real id

The JUnit writer reconstructed it from (uri, root) and the name. That was wrong twice over: it assumed root was the item's package root when callers pass the run root — different things in a multi-package run — and the id now carries a package qualifier that cannot be recovered from a path at all. The reconstruction survives only as a fallback for result files written before the field existed.

_relative_path did not absolutise its root

A relative root produced a ..-heavy path, failed the guard, and silently emitted absolute machine paths as JUnit classnames. TestItemApp was working around it caller-side.

Tests

25/25 across test_state.jl, test_junit.jl and test_results.jl, including new coverage for setup invalidation, the relative root, two checkouts keeping their own source, and the guard rejecting genuinely indistinguishable items. Full-suite verification is still in progress and I will report it here.

Depends on

julia-vscode/JuliaWorkspaces.jl#257 for the id format itself. Companion: julia-vscode/JuliaMCP.jl#15.

🤖 Generated with Claude Code

davidanthoff and others added 3 commits August 14, 2026 21:56
Four changes that share a branch because they all touch run bookkeeping.

`loaded_setups` was written and never maintained. It is now invalidated when a
run ships different code for a setup, or drops it entirely, via
`_setup_testrun_on_process!` — which runs for every run on a pooled process, so
one hook covers the Revise case too: revised source reaches a pooled process as
changed setup code through that same path. Deliberately conservative: a setup
cached under an earlier run that this one does not mention is dropped rather
than assumed intact, since losing a cost hint only costs a scheduling
opportunity while keeping a wrong one produces a worse schedule and hides why.

`tr.test_items` was keyed by test item id alone. Ids are scoped to their
package, so the same package checked out into two folders of one workspace
mints the same id from both — the entries collapsed, and the surviving item's
source then ran twice while the other never ran at all. Keyed by
`(id, package_uri)` now, matching `remaining_work`, with an assertion when a run
is assembled so any future scheme mistake surfaces as an error rather than a
silently dropped test.

`TestrunResult` now carries the real test item id. The JUnit writer used to
reconstruct it from `(uri, root)` and the name, which was wrong twice over: it
assumed `root` was the item's package root when callers pass the run root, and
the id now carries a package qualifier that cannot be recovered from a path at
all. The reconstruction survives only as a fallback for result files written
before the field existed.

`_relative_path` did not absolutise its root, so a relative root produced a
`..`-heavy path, failed the guard, and silently emitted absolute machine paths
as JUnit classnames.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Two checkouts of one package keep their own source, and a run whose items are
not individually addressable is rejected rather than silently losing one.

Also updates a construction test that asserted the old key shape.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Widening `tr.test_items` to `(id, package_uri)` left nine reads still keyed by
the id alone. Three shapes, and my first sweep only caught one of them:

  * `get(tr.test_items, id, ...)` in six handlers,
  * `keys(tr.test_items)` in the scheduling-cache prune and the
    every-item-reported assertion, where the keys are now tuples,
  * `for (id, _) in tr.test_items` in `_get_unchunked_items`, which silently
    destructured the pair and produced tuples where strings were expected.

The last one also needed scoping to the env's package: with one package checked
out twice both checkouts' items share an id, and a process may only be handed
the items belonging to its own checkout.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@davidanthoff

Copy link
Copy Markdown
Member Author

Full suite: 189/189, rebased onto the test speedup on main.

That closes the one open claim in the description. Worth recording how the last two fixes were found, since the first sweep looked complete and was not:

Widening tr.test_items to (id, package_uri) left nine reads still keyed by the id alone, in three shapes:

  • get(tr.test_items, id, ...) — six handlers
  • keys(tr.test_items) — the scheduling-cache prune and the every-item-reported assertion, where keys are now tuples
  • for (id, _) in tr.test_items_get_unchunked_items, which silently destructured each pair and produced tuples where strings were expected

My first pass grepped for test_items[ and caught one of the nine; two runtime failures later I audited every reference to the identifier instead of the indexing syntax. The _get_unchunked_items one also needed scoping to the env's package: with a package checked out twice, both checkouts' items share an id, and a process may only be handed items from its own checkout.

@davidanthoff
davidanthoff marked this pull request as ready for review August 15, 2026 16:46
Two hazards introduced when `tr.test_items` was keyed by `(id, package_uri)`,
both on the process-termination path.

The crash branch indexed the dict directly. An exception thrown out of a reactor
handler does not surface as a failed test item — it kills the reactor loop, so
the run never completes and the caller waits until its timeout. A missing detail
now degrades to the item's id in the message; the item is reported as errored
either way, which is the part that matters.

The user-termination branch dereferenced `terminated_env` before the guard that
establishes it is not `nothing`, so a process the run no longer tracks would
have thrown there instead.

Both now go through `_item_for_env`, which tolerates an unknown environment and
a missing item.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant