Repository navigation
Refactor TestPlatformDispatcher - #192747
Conversation
70dd8a4 to
dfcdd60
Compare
…n tests Remove the per-view TestPlatformDispatcher indirection (_forView, _testValuesOwner, _testValues, _sharingTestValues). With only this PR applied there is one dispatcher per binding, so _testValues resolved to "this" everywhere. The indirection exists to serve a later PR and belongs there. Keep a custom view that shadows an engine view from rebuilding its TestFlutterView on every refresh, which discarded the test values set on it. Route the teardown unfocus through notifyViewFocusChanged rather than the wrapped dispatcher's callback field. Tests: the custom-view test now refreshes the registry through notifyMetricsChanged instead of calling back the callback it just registered, which refreshed nothing. The chaining tests read the previous callback back through the getter, which is what the symmetry is for. New tests cover the focus bookkeeping, the refresh-before-notify ordering, and shadowing. Docs: drop a dangling [debugViewMetricsOverrides] reference and correct resetWindowingOwner, which does not destroy the previous owner's windows.
BEHAVIOUR CHANGE: fixes a reproduced view leak in removeTestView. Window controllers hold the TestFlutterView wrapper that addTestView returns, and _destroyTestWindowController passes that wrapper to removeTestView. The staleness check guarding the fallback refresh only compared against the backing view, so it was always false for that caller. Normally harmless, because the metrics change refreshes the registry anyway. But destroying a controller from inside a metrics-changed notification makes that refresh reentrant, and the guard drops it by design, so the fallback was the only thing left and it never fired. The destroyed view then stayed in views and view(id:) permanently. Reproduced with a throwaway test before fixing: after a reentrant destroy the registry still reported the destroyed view. removeTestView now accepts the wrapper as well as the backing view. A regression test covers it, mutation-verified in both directions. The fix is in removeTestView rather than at the call site because the backing view is not reachable from the controller and _view is private to window.dart. Boundary correction, per the repository owner's decision: TestFlutterView.render and the physicalConstraints getter are reverted to master's form. Both carried fallbacks that belong to the piece that introduces debug view metric overrides: - render used `size ?? _physicalSize`. Provably inert here: the only caller, RenderView.compositeFrame at rendering/view.dart:360, always passes a non-null size, and no other caller omits it. Its comment also forward-referenced debug overrides, which do not exist in this piece. - physicalConstraints fell back through _physicalSize. Reachable only by calling resetPhysicalConstraints() without resetPhysicalSize(), where it changed behaviour versus master and left resetPhysicalConstraints not fully resetting. No test covered it. Test coverage, both gaps confirmed by mutation first: - clearAllTestValues dropping custom views was unpinned. Deleting _customViews.clear() and the refresh left all 714 tests green. - The notify: false argument in _TestFlutterView.register was unpinned. Removing it left all 94 windowing tests green. Test hygiene: three tests replaced the shared dispatcher's onMetricsChanged or onViewFocusChange without restoring it. One never restored it at all; two restored it mid-test, which an earlier failing expect would skip. All three now restore through addTearDown. Gate: dart analyze --fatal-infos clean, dart format 0 changed, flutter_test 715 passing, flutter test/widgets 7327 passing. Findings rejected with evidence, for the record: - "removeTestView leaves a dangling _currentlyFocusedViewId, and clearAllTestValues does not clear focus state." Reproduced with a probe: nothing throws and the state converges to null. It matches the documented contract that currentlyFocusedViewIdTestValue records the last focus event rather than querying a live view. The clearAllTestValues half is pre-existing master behaviour, untouched by this PR. - "Add @VisibleForTesting to createWindowingOwner." Rejected: the method is invoked by WidgetsBinding.initInstances on every production app start, so it is not test-only. Adding it was also tried and does not produce the diagnostic that was claimed to justify it. - "The constraints != null fallback in _TestFlutterView is inert scaffolding." It is verbatim master code, untouched by this PR. Noted but deliberately NOT fixed here, as unrelated to this split: TestPlatformDispatcher's `set onError` is a no-op. It evaluates _platformDispatcher.onError as a statement and discards it, never assigning value. That is pre-existing on master and untouched by this PR; it wants its own change.
There was a problem hiding this comment.
Code Review
This pull request refactors the initialization and destruction of test window controllers and views within the Flutter test framework. It introduces a createWindowingOwner method to safely initialize windowing owners, and updates TestPlatformDispatcher to better manage custom views, handle reentrant metrics, and track view focus changes. Additionally, a compile-time error was identified in packages/flutter_test/lib/src/binding.dart where _TestFlutterView is instantiated with an incorrect parameter name platformDispatcher instead of _platformDispatcher.
…stWidgetsFlutterBinding
…est-platform-dispatcher
mattkae
left a comment
There was a problem hiding this comment.
A few things, but this looks largely ok!
…eWindowingOwner - Mark WidgetsBinding.createWindowingOwner @internal and use the shared windowing experimental macro, like the rest of the windowing surface. @Protected stays: it is an override hook. - Doc nit: "Only state that a constructor established". - Move the six tests that exercise flutter_test's own window controller doubles into a 'test window controllers' group whose setUp installs a test windowing owner and restores the previous one, instead of each test repeating that. The owner has to be installed there because the binding is constructed (and picks its owner) when the first testWidgets is declared, before this file's setUp enables windowing; the stub controller tests never notice because they bypass the owner. - Document that on resetWindowingOwner, and that it is part of the experimental windowing support.
…patcher # Conflicts: # packages/flutter/test/widgets/windowing_test.dart
…ists The binding chooses its windowing owner when it is constructed, which happens when the first testWidgets in a file is declared, before any setUp runs. windowing_test.dart enabled windowing in setUp, so the binding had installed the unsupported owner and the test window controller tests needed resetWindowingOwner() to swap a test owner in. Instead, enable windowing at the top of main(), so the binding installs its test owner itself and the whole file shares it. Remove resetWindowingOwner() from flutter_test. Because the owner is shared, every test in the group destroys the windows it creates at teardown (destroy is idempotent). Document the timing on registerTestWindowingOwner.
mattkae
left a comment
There was a problem hiding this comment.
Cool by me now, thanks for fixing this!
|
Google testing failures are text goldens failing because of a stacktrace that has different line numbers as a result of the diff in |
Roll Flutter from 4249ccbc18a3 to 426be968b5eb (44 revisions) flutter/flutter@4249ccb...426be96 2026-10-14 [email protected] Allow color delta tolerance in in-memory fuzzy image comparator (flutter/flutter#193937) 2026-10-14 [email protected] [web] Drop stale composing range after text is replaced mid-composition (flutter/flutter#194096) 2026-10-14 [email protected] Roll Skia from 2a2bbea66844 to acde3755d2e2 (1 revision) (flutter/flutter#194376) 2026-10-14 [email protected] Roll Skia from 128c983b12de to 2a2bbea66844 (1 revision) (flutter/flutter#194371) 2026-10-14 [email protected] Roll Skia from 48a294ed4955 to 128c983b12de (3 revisions) (flutter/flutter#194353) 2026-10-14 [email protected] feat(flutter_tools): extract WebDebugConfig from DebuggingOptions (flutter/flutter#194312) 2026-10-14 [email protected] Roll Skia from ea6781c5b6d1 to 48a294ed4955 (2 revisions) (flutter/flutter#194349) 2026-10-14 [email protected] Include <vector> in Android proc_table.h. (flutter/flutter#194325) 2026-10-14 [email protected] Roll Dart SDK from 5933cb486b4a to 6b50116630d0 (28 revisions) (flutter/flutter#194341) 2026-10-14 [email protected] Move GLES3 instancing API entry points to FOR_EACH_IMPELLER_GLES3_PROC (flutter/flutter#194319) 2026-10-14 [email protected] feat(flutter_tools): extract AndroidGradleConfig and move toGradleConfig to AndroidBuildInfo (flutter/flutter#194016) 2026-10-14 [email protected] Roll Skia from ae297bec6012 to ea6781c5b6d1 (8 revisions) (flutter/flutter#194331) 2026-10-14 [email protected] [flutter_tools] Migrate gradle_errors and deferred_components_prebuild_validator away from globals (flutter/flutter#193649) 2026-10-14 [email protected] [Impeller] Cache GLES sampler configuration in RenderPassStateCache (flutter/flutter#193922) 2026-10-14 [email protected] Roll pub packages (flutter/flutter#194332) 2026-10-13 [email protected] [tool] Migrate RunCommand to modular dependency injection (flutter/flutter#190791) 2026-10-13 [email protected] Include `engine/src/flutter/.ci.yaml` in quarterly `goldctl` checklist (flutter/flutter#194316) 2026-10-13 [email protected] [flutter_tools] Remove the last AMD module system support (flutter/flutter#193607) 2026-10-13 [email protected] Roll Skia from 8b3394a60654 to ae297bec6012 (5 revisions) (flutter/flutter#194310) 2026-10-13 [email protected] [web] Remove the a11y placeholder only after the gesture ends (flutter/flutter#194109) 2026-10-13 [email protected] Upgrade goldctl to the latest version (flutter/flutter#193932) 2026-10-13 [email protected] [flutter_tools] Lazily initialize AndroidStudio validation and optimize candidate selection (flutter/flutter#191968) 2026-10-13 [email protected] [flutter_tools] Clean up outdated widget preview TODOs and refactor static state (flutter/flutter#189437) 2026-10-13 [email protected] Roll Skia from e5b5db6d0d6f to 8b3394a60654 (2 revisions) (flutter/flutter#194306) 2026-10-13 [email protected] Redistribute TESTOWNERS for Android team (flutter/flutter#193861) 2026-10-13 [email protected] feat(flutter_tools): introduce Test and Drive specification objects (flutter/flutter#194017) 2026-10-13 [email protected] [Impeller] Keep the aspect ratio when shrinking images to the max texture size (flutter/flutter#194010) 2026-10-13 [email protected] [flutter tool] preserve inherited `DASH__SUPPRESS_ANALYTICS=true` for sub-tools (flutter/flutter#194261) 2026-10-13 [email protected] Roll Packages from d8d9e97 to a1ed23c (9 revisions) (flutter/flutter#194302) 2026-10-13 [email protected] Use PE/COFF AOT snapshots for Windows x64 (flutter/flutter#187594) 2026-10-13 [email protected] Fix Expansible crash when PageStorage holds a non-bool value (flutter/flutter#192195) 2026-10-13 [email protected] Roll Skia from ffeccb3ca58a to e5b5db6d0d6f (1 revision) (flutter/flutter#194297) 2026-10-13 [email protected] Explain why initState() is too early to depend on an inherited widget (flutter/flutter#192727) 2026-10-13 [email protected] Refactor TestPlatformDispatcher (flutter/flutter#192747) 2026-10-13 [email protected] Roll Skia from 3de0984208ec to ffeccb3ca58a (2 revisions) (flutter/flutter#194291) 2026-10-13 [email protected] Roll Skia from 0580c2b780f6 to 3de0984208ec (2 revisions) (flutter/flutter#194281) 2026-10-13 [email protected] Roll Skia from f641d55e07a8 to 0580c2b780f6 (3 revisions) (flutter/flutter#194275) 2026-10-13 [email protected] Roll Skia from 0e5d8d1be81b to f641d55e07a8 (1 revision) (flutter/flutter#194266) 2026-10-13 [email protected] Scaffold view metrics overrides (flutter/flutter#192764) 2026-10-13 [email protected] [web] Add SemanticsRole.listBox and SemanticsRole.option (flutter/flutter#192971) 2026-10-13 [email protected] Pin mac_unopt to macOS 15 (flutter/flutter#194254) 2026-10-13 [email protected] Roll pub packages (flutter/flutter#194257) 2026-10-12 [email protected] Roll Skia from 17f6986c226c to 0e5d8d1be81b (4 revisions) (flutter/flutter#194249) 2026-10-12 [email protected] Make DisplayLists' use of DlPaint, SkPaint, and impeller::Paint agree on the default isAntiAlias (flutter/flutter#193939) If this roll has caused a breakage, revert this CL and set the roller ...
Refactor `TestPlatformDispatcher` to properly manage custom view lifecycles. Fixes a longstanding getter/setter asymmetry bug on `TestPlatformDispatcher.onMetricsChanged` and `onViewFocusChange` (where saving/restoring callbacks caused infinite recursion because the getter returned `_platformDispatcher`'s internal handler rather than `_onMetricsChanged`).
Refactor
TestPlatformDispatcherto properly manage custom view lifecycles.Fixes a longstanding getter/setter asymmetry bug on
TestPlatformDispatcher.onMetricsChangedandonViewFocusChange(where saving/restoring callbacks caused infinite recursion because the getter returned_platformDispatcher's internal handler rather than_onMetricsChanged).