Fix flaky ink_sparkle shader load on master channel - #144
Closed
passsy wants to merge 1 commit into
Closed
Conversation
On the Flutter master channel the `flutter test` asset server intermittently fails to serve shaders/ink_sparkle.frag while several test processes request assets at once (regressed by the devfs "unify asset processing" rework, flutter/flutter#186902). Widget tests run as TargetPlatform.android, where Material uses InkSparkle. The first Material tap loads the shader via a fire-and-forget Future with no error handler, so a failed load surfaces as an unhandled exception that fails an unrelated test. Each test file runs in its own process, so a shared exclusive file lock serializes the shader load across them. The asset server serves it reliably one request at a time, and FragmentProgram.fromAsset caches the result for the later InkSparkle load. Only the shader load is serialized; tests still run concurrently.
passsy
force-pushed
the
fix-ink-sparkle-shader-flake
branch
from
June 8, 2026 18:01
765385a to
09f83df
Compare
Owner
Author
|
Closing: the file-lock pre-warm does not actually fix this. Investigation showed the failure is a probabilistic devfs corruption at concurrent process startup (flutter/flutter#186902), which no in-process pre-warm can repair. See follow-up analysis. |
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.
CI on
mainis red ontest_channel (master)(and nightly), failing intermittently with:Root cause
Widget tests run as
TargetPlatform.android, so Material uses the InkSparkle ink splash. The first tap on a Material widget triggers_InkSparkleFactory.initializeShader(), which loadsshaders/ink_sparkle.fragvia a fire-and-forgetFuturewith no error handler:On the master channel the
flutter testasset server intermittently fails to serve that shader when several test processes request assets at the same time. The rejected future becomes an unhandled exception that fails whichever test is running — so it looks flaky (a different test each run, usually afilter_test/ timeline tap or drag test).This is a recent regression in
flutter_tools: flutter/flutter#186902 ("unify asset processing", May 27 2026) reworkeddevfs.dart(the asset-serving layer). It's a recurrence of the class of flutter/flutter#104084.stable,betaand the pinned3.10job are unaffected.Measured against master (
3.45.0-1.0.pre): default/-j 2→ 1–2 failures per run; the asset server can't serve even two concurrentFragmentProgram.fromAssetrequests.Fix
flutter testruns every test file in its own OS process (verified: 47 files → 47 PIDs). Atest/flutter_test_config.dartpre-warms the shader behind a shared exclusive file lock, so the load is serialized across all test processes. The asset server serves it reliably one request at a time, andFragmentProgram.fromAssetcaches the result, so the later InkSparkle load reuses the cached program. Only the (fast) shader load is serialized — tests still run concurrently.No platform or theme changes, so test behavior is unchanged. Verified locally on master: 5/5 full runs green (394 tests, ~25s each, still concurrent), and green on stable.
Alternatives rejected
debugDefaultTargetPlatformOverridemust benullat each test body's end (debugAssertAllFoundationVarsUnset), and package:testtearDownruns after that invariant check, so a global override is impossible.FragmentProgram/ImmutableBuffer.fromAssetare@Nativeengine calls, not routed throughrootBundle, so they can't be mocked from Dart.