Repository navigation
⚡ Async file access, concurrent discovery, and coverage gaps - #23
Merged
Merged
Conversation
… directory File access no longer blocks the extension host thread. EngineFileSystem is fully async, and projectsFromSettings() and configurationFiles() take and return promises. Discovery overlaps the Ceedling starts it can: - Projects load concurrently across directories and one at a time within a directory (mapGrouped). Outcomes apply in settings order, so errors read the same however loads interleave. - Listings run concurrently across directories, and test sources are read concurrently. The engine-wide mutex becomes a lock per project directory (KeyedLock). Every Ceedling command that loads a project rewrites the vendored Unity and CMock files in its build directory in place. Discovery's dumpconfig ran outside the old mutex, so a rediscovery during a build in the same directory could leave gcc a half-written unity.h. A real Ceedling stress run reproduced that as TEST_CASE compile errors. dumpconfig, listings, runs, debug builds, and clean/clobber now hold their directory's lock. Different directories proceed independently, so builds in separate projects may overlap. async-mutex is gone. Every source read shares one limit (Limiter, 16). A stress run with 600 simultaneous reparses alongside two rediscoveries hit EMFILE under a 64-descriptor limit with per-call bounds; it runs clean with the shared limit. Stress-tested before commit, all against the final code: - S1: the compiled engine against a simulated Ceedling with random latencies, failures, and cancellation; 60 seeds x 300 overlapping operations; invariants on locking, project args, reporter results, live processes, and temp files. - S2: real Ceedling 1.0.0, 1.1.10, and 1.2.0, overlapping discovery, runs, debug builds, clean, and reparses with random cancellation; checks results, leftover processes, and temp files. - S3: a 3,000-file project under a 64-descriptor limit. All ran with 0 violations and 0 unhandled rejections. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…thout a range Unit tests now reach the error and edge paths that only real use exercised before: - Engine: unparseable and missing dumpconfig output, a failed test listing, discovery warnings, an invalid file prefix for pretty labels, ignored and unmapped results, development-mode logging, a cancelled debug build, undeletable reports and temp files, project lookups, cancel and dispose, the shell and command settings, and the real filesystem and runner defaults. - Process: killProcessTree against a real process tree on every OS, and its fallback when tree-kill fails. The fallback is reached through an injected platform and tree-kill. - Problem matching, run plans, and Ruby float ranges: their remaining branches. Unity's generator fails on a TEST_RANGE with no bracketed range, just as on a TEST_MATRIX with no lists. Discovery now warns about both. Previously the range case passed silently. Statement coverage of the pure modules is now 99-100%. What's left is unreachable `??` fallbacks, a race guard, and the engine's Windows path selection. The fake-Ceedling integration tests on the Windows CI leg are the place to cover that last one. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
mkarlesky
marked this pull request as ready for review
October 23, 2026 18:11
Development.md: the module table gains concurrency.ts. Discovery describes what overlaps and the shared read limit, and dumpconfig's stdout and temporary-file forms. A new paragraph explains why Ceedling commands in one project directory run one at a time, and the running step names the directory lock in place of the old mutex. CHANGELOG: faster discovery, commands in separate project directories overlapping, and the warning for a TEST_RANGE or TEST_MATRIX that Ceedling can't build. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Third PR in the lint-and-coverage series, stacked on #22. It covers two parts of the triage report:
Adapter extraction (PR D) and fake-Ceedling integration tests (PR E) follow separately.
Async file access
EngineFileSystemis fully async.projectsFromSettings()andconfigurationFiles()return promises.no-restricted-propertieswarnings on syncfsinsrc/drop to zero, apart from the documented/procreads inceedlingProcess.ts.Concurrent discovery
mapGroupedin the newsrc/concurrency.ts).A lock per project directory (fixes a real race)
The race: Every Ceedling command that loads a project rewrites the vendored Unity and CMock files under
build/vendor/in place,dumpconfigincluded. Discovery'sdumpconfigran outside the old engine-wide mutex. So a rediscovery during a build in the same directory could leave gcc reading a half-writtenunity.h. The S2 stress run below reproduced this against real Ceedling as spuriousTEST_CASEcompile errors.The fix:
KeyedLock, one lock per project directory.dumpconfig, listings, runs, debug builds, and clean/clobber all hold their directory's lock.async-mutexdependency is removed.One shared read limit (fixes EMFILE)
All source reads share one
Limitercapped at 16. Per-call bounds weren't enough: 600 simultaneous reparses plus two rediscoveries exhausted a 64-descriptor limit (S3).Stress gate (one-off scripts, not committed)
All three ran against the final code with 0 violations and 0 unhandled rejections:
/proc); no leftover temp files.ulimit -n 64. Two concurrent discoveries plus 600 reparses.Tests
New unit tests:
concurrency.test.tscoversmapGrouped,KeyedLock, andLimiter.ceedlingEngine.test.tscovers directory grouping, error ordering, lock interplay between rediscovery and builds, the shared read limit, and read order.Results: 311 unit tests and 4 integration tests pass, and lint reports 0 findings.
Coverage of the pure modules
¹ The new injection point adds branches.
What the new tests reach:
cancelanddispose, the shell and command settings, and the real filesystem and runner defaults.killProcessTreeagainst a real process tree on every OS, and its fallback when tree-kill fails. The fallback is reached through an injected platform andtreeKill.One behavior change: a
TEST_RANGEwith no bracketed range now gives a discovery warning, as aTEST_MATRIXwith no lists already did. Unity's generator fails on both (undefined method 'map' for nil). This was checked in the Ceedling 1.2.0 image.What stays uncovered:
??fallbacks on captures that always participate;/procis being read;path.win32selection. Unit tests always injectplatform: 'linux', so this only runs on a real Windows host. The fake-Ceedling integration tests (PR E) run on the Windows CI leg and will cover it.Docs
docs/Development.md: the module table gainsconcurrency.ts. Discovery describes what overlaps, the shared read limit, anddumpconfig's two forms. A new paragraph explains why commands in one project directory run one at a time.CHANGELOG.md:TEST_RANGE()orTEST_MATRIX()that Ceedling can't build.files:test, so it only ever existed onnext_version.Upstream
Ceedling's non-atomic, in-place re-vendoring on every project-loading command is reported as ThrowTheSwitch/Ceedling#1361. This PR works around it.
🤖 Generated with Claude Code