Skip to content

Fix flaky ink_sparkle shader load on master channel - #144

Closed
passsy wants to merge 1 commit into
mainfrom
fix-ink-sparkle-shader-flake
Closed

Fix flaky ink_sparkle shader load on master channel#144
passsy wants to merge 1 commit into
mainfrom
fix-ink-sparkle-shader-flake

Conversation

@passsy

@passsy passsy commented Jun 8, 2026

Copy link
Copy Markdown
Owner

CI on main is red on test_channel (master) (and nightly), failing intermittently with:

Exception: Asset 'shaders/ink_sparkle.frag' not found

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 loads shaders/ink_sparkle.frag via a fire-and-forget Future with no error handler:

ui.FragmentProgram.fromAsset('shaders/ink_sparkle.frag').then((p) => _program = p);
// no .catchError — a rejection becomes an unhandled async error

On the master channel the flutter test asset 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 a filter_test / timeline tap or drag test).

This is a recent regression in flutter_tools: flutter/flutter#186902 ("unify asset processing", May 27 2026) reworked devfs.dart (the asset-serving layer). It's a recurrence of the class of flutter/flutter#104084. stable, beta and the pinned 3.10 job 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 concurrent FragmentProgram.fromAsset requests.

Fix

flutter test runs every test file in its own OS process (verified: 47 files → 47 PIDs). A test/flutter_test_config.dart pre-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, and FragmentProgram.fromAsset caches 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

  • Reduce test concurrency — works but a blunt slowdown that hides rather than fixes the issue.
  • Override the platform to avoid InkSparkle — debugDefaultTargetPlatformOverride must be null at each test body's end (debugAssertAllFoundationVarsUnset), and package:test tearDown runs after that invariant check, so a global override is impossible.
  • Mock the shader assetFragmentProgram/ImmutableBuffer.fromAsset are @Native engine calls, not routed through rootBundle, so they can't be mocked from Dart.

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
passsy force-pushed the fix-ink-sparkle-shader-flake branch from 765385a to 09f83df Compare June 8, 2026 18:01
@passsy passsy changed the title Run master channel tests serially to fix shader flake Fix flaky ink_sparkle shader load on master channel Jun 8, 2026
@passsy

passsy commented Jun 9, 2026

Copy link
Copy Markdown
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.

@passsy passsy closed this Jun 9, 2026
@passsy
passsy deleted the fix-ink-sparkle-shader-flake branch June 14, 2026 01:32
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