Unverified Commit 6de42a70 authored by Michael Goderbauer's avatar Michael Goderbauer Committed by GitHub

Fix how tests count open SemanticsHandles (#121571)

Fix how tests count open SemanticsHandles
parent f97a5153
...@@ -67,6 +67,11 @@ mixin SemanticsBinding on BindingBase { ...@@ -67,6 +67,11 @@ mixin SemanticsBinding on BindingBase {
_semanticsEnabled.removeListener(listener); _semanticsEnabled.removeListener(listener);
} }
/// The number of clients registered to listen for semantics.
///
/// The number is increased whenever [ensureSemantics] is called and decreased
/// when [SemanticsHandle.dispose] is called.
int get debugOutstandingSemanticsHandles => _outstandingHandles;
int _outstandingHandles = 0; int _outstandingHandles = 0;
/// Creates a new [SemanticsHandle] and requests the collection of semantics /// Creates a new [SemanticsHandle] and requests the collection of semantics
......
...@@ -1247,6 +1247,7 @@ void main() { ...@@ -1247,6 +1247,7 @@ void main() {
label: 'Custom label', label: 'Custom label',
flags: <SemanticsFlag>[SemanticsFlag.namesRoute], flags: <SemanticsFlag>[SemanticsFlag.namesRoute],
))); )));
semantics.dispose();
}); });
testWidgets('CupertinoDialogRoute is state restorable', (WidgetTester tester) async { testWidgets('CupertinoDialogRoute is state restorable', (WidgetTester tester) async {
......
...@@ -1484,6 +1484,7 @@ void main() { ...@@ -1484,6 +1484,7 @@ void main() {
label: 'Dismiss', label: 'Dismiss',
))); )));
debugDefaultTargetPlatformOverride = null; debugDefaultTargetPlatformOverride = null;
semantics.dispose();
}); });
testWidgets('showCupertinoModalPopup allows for semantics dismiss when set', (WidgetTester tester) async { testWidgets('showCupertinoModalPopup allows for semantics dismiss when set', (WidgetTester tester) async {
...@@ -1519,6 +1520,7 @@ void main() { ...@@ -1519,6 +1520,7 @@ void main() {
label: 'Dismiss', label: 'Dismiss',
)); ));
debugDefaultTargetPlatformOverride = null; debugDefaultTargetPlatformOverride = null;
semantics.dispose();
}); });
testWidgets('showCupertinoModalPopup passes RouteSettings to PopupRoute', (WidgetTester tester) async { testWidgets('showCupertinoModalPopup passes RouteSettings to PopupRoute', (WidgetTester tester) async {
......
...@@ -657,7 +657,6 @@ void main() { ...@@ -657,7 +657,6 @@ void main() {
group('Semantics', () { group('Semantics', () {
testWidgets('day mode', (WidgetTester tester) async { testWidgets('day mode', (WidgetTester tester) async {
final SemanticsHandle semantics = tester.ensureSemantics(); final SemanticsHandle semantics = tester.ensureSemantics();
addTearDown(semantics.dispose);
await tester.pumpWidget(calendarDatePicker()); await tester.pumpWidget(calendarDatePicker());
...@@ -837,11 +836,11 @@ void main() { ...@@ -837,11 +836,11 @@ void main() {
hasTapAction: true, hasTapAction: true,
isFocusable: true, isFocusable: true,
)); ));
semantics.dispose();
}); });
testWidgets('calendar year mode', (WidgetTester tester) async { testWidgets('calendar year mode', (WidgetTester tester) async {
final SemanticsHandle semantics = tester.ensureSemantics(); final SemanticsHandle semantics = tester.ensureSemantics();
addTearDown(semantics.dispose);
await tester.pumpWidget(calendarDatePicker( await tester.pumpWidget(calendarDatePicker(
initialCalendarMode: DatePickerMode.year, initialCalendarMode: DatePickerMode.year,
...@@ -863,8 +862,8 @@ void main() { ...@@ -863,8 +862,8 @@ void main() {
isButton: true, isButton: true,
)); ));
} }
semantics.dispose();
}); });
}); });
}); });
......
...@@ -814,7 +814,6 @@ void main() { ...@@ -814,7 +814,6 @@ void main() {
group('Semantics', () { group('Semantics', () {
testWidgets('calendar mode', (WidgetTester tester) async { testWidgets('calendar mode', (WidgetTester tester) async {
final SemanticsHandle semantics = tester.ensureSemantics(); final SemanticsHandle semantics = tester.ensureSemantics();
addTearDown(semantics.dispose);
await prepareDatePicker(tester, (Future<DateTime?> date) async { await prepareDatePicker(tester, (Future<DateTime?> date) async {
// Header // Header
...@@ -858,11 +857,11 @@ void main() { ...@@ -858,11 +857,11 @@ void main() {
isFocusable: true, isFocusable: true,
)); ));
}); });
semantics.dispose();
}); });
testWidgets('input mode', (WidgetTester tester) async { testWidgets('input mode', (WidgetTester tester) async {
final SemanticsHandle semantics = tester.ensureSemantics(); final SemanticsHandle semantics = tester.ensureSemantics();
addTearDown(semantics.dispose);
initialEntryMode = DatePickerEntryMode.input; initialEntryMode = DatePickerEntryMode.input;
await prepareDatePicker(tester, (Future<DateTime?> date) async { await prepareDatePicker(tester, (Future<DateTime?> date) async {
...@@ -901,6 +900,7 @@ void main() { ...@@ -901,6 +900,7 @@ void main() {
isFocusable: true, isFocusable: true,
)); ));
}); });
semantics.dispose();
}); });
}); });
......
...@@ -1086,21 +1086,21 @@ void main() { ...@@ -1086,21 +1086,21 @@ void main() {
}); });
}); });
group('Semantics', () { group('Semantics', () {
testWidgets('calendar mode', (WidgetTester tester) async { testWidgets('calendar mode', (WidgetTester tester) async {
final SemanticsHandle semantics = tester.ensureSemantics(); final SemanticsHandle semantics = tester.ensureSemantics();
currentDate = DateTime(2016, DateTime.january, 30); currentDate = DateTime(2016, DateTime.january, 30);
addTearDown(semantics.dispose); await preparePicker(tester, (Future<DateTimeRange?> range) async {
await preparePicker(tester, (Future<DateTimeRange?> range) async { expect(
expect( tester.getSemantics(find.text('30')),
tester.getSemantics(find.text('30')), matchesSemantics(
matchesSemantics( label: '30, Saturday, January 30, 2016, Today',
label: '30, Saturday, January 30, 2016, Today', hasTapAction: true,
hasTapAction: true, isFocusable: true,
isFocusable: true, ),
), );
); });
}); semantics.dispose();
}); });
}); });
} }
......
...@@ -2505,6 +2505,7 @@ void main() { ...@@ -2505,6 +2505,7 @@ void main() {
label: 'Custom label', label: 'Custom label',
flags: <SemanticsFlag>[SemanticsFlag.namesRoute], flags: <SemanticsFlag>[SemanticsFlag.namesRoute],
))); )));
semantics.dispose();
}); });
testWidgets('DialogRoute is state restorable', (WidgetTester tester) async { testWidgets('DialogRoute is state restorable', (WidgetTester tester) async {
......
...@@ -262,7 +262,6 @@ void main() { ...@@ -262,7 +262,6 @@ void main() {
testWidgets('Semantics', (WidgetTester tester) async { testWidgets('Semantics', (WidgetTester tester) async {
final SemanticsHandle semantics = tester.ensureSemantics(); final SemanticsHandle semantics = tester.ensureSemantics();
addTearDown(semantics.dispose);
// Fill the clipboard so that the Paste option is available in the text // Fill the clipboard so that the Paste option is available in the text
// selection menu. // selection menu.
...@@ -287,6 +286,7 @@ void main() { ...@@ -287,6 +286,7 @@ void main() {
hasMoveCursorBackwardByCharacterAction: true, hasMoveCursorBackwardByCharacterAction: true,
hasMoveCursorBackwardByWordAction: true, hasMoveCursorBackwardByWordAction: true,
)); ));
semantics.dispose();
}); });
testWidgets('InputDecorationTheme is honored', (WidgetTester tester) async { testWidgets('InputDecorationTheme is honored', (WidgetTester tester) async {
......
...@@ -4514,6 +4514,7 @@ void main() { ...@@ -4514,6 +4514,7 @@ void main() {
), ),
], ],
), ignoreTransform: true, ignoreRect: true)); ), ignoreTransform: true, ignoreRect: true));
semantics.dispose();
}); });
testWidgets('TextField with specified suffixStyle', (WidgetTester tester) async { testWidgets('TextField with specified suffixStyle', (WidgetTester tester) async {
......
...@@ -13,7 +13,6 @@ void main() { ...@@ -13,7 +13,6 @@ void main() {
// Regression test for https://github.com/flutter/flutter/issues/100358. // Regression test for https://github.com/flutter/flutter/issues/100358.
final SemanticsTester semantics = SemanticsTester(tester); final SemanticsTester semantics = SemanticsTester(tester);
addTearDown(semantics.dispose);
await tester.pumpWidget( await tester.pumpWidget(
Directionality( Directionality(
...@@ -44,5 +43,6 @@ void main() { ...@@ -44,5 +43,6 @@ void main() {
type: SemanticsAction.showOnScreen, type: SemanticsAction.showOnScreen,
nodeId: nodeId, nodeId: nodeId,
)); ));
semantics.dispose();
}); });
} }
...@@ -273,7 +273,8 @@ void main() { ...@@ -273,7 +273,8 @@ void main() {
}); });
test('after markNeedsSemanticsUpdate() all render objects between two semantic boundaries are asked for annotations', () { test('after markNeedsSemanticsUpdate() all render objects between two semantic boundaries are asked for annotations', () {
TestRenderingFlutterBinding.instance.pipelineOwner.ensureSemantics(); final SemanticsHandle handle = TestRenderingFlutterBinding.instance.ensureSemantics();
addTearDown(handle.dispose);
TestRender middle; TestRender middle;
final TestRender root = TestRender( final TestRender root = TestRender(
......
...@@ -4510,7 +4510,6 @@ void main() { ...@@ -4510,7 +4510,6 @@ void main() {
testWidgets('are exposed', (WidgetTester tester) async { testWidgets('are exposed', (WidgetTester tester) async {
final SemanticsTester semantics = SemanticsTester(tester); final SemanticsTester semantics = SemanticsTester(tester);
addTearDown(semantics.dispose);
controls.testCanCopy = false; controls.testCanCopy = false;
controls.testCanCut = false; controls.testCanCut = false;
...@@ -4603,6 +4602,7 @@ void main() { ...@@ -4603,6 +4602,7 @@ void main() {
], ],
), ),
); );
semantics.dispose();
}); });
testWidgets('can copy/cut/paste with a11y', (WidgetTester tester) async { testWidgets('can copy/cut/paste with a11y', (WidgetTester tester) async {
......
...@@ -1751,6 +1751,7 @@ void main() { ...@@ -1751,6 +1751,7 @@ void main() {
await tester.pumpWidget(Focus(includeSemantics: false, child: Container())); await tester.pumpWidget(Focus(includeSemantics: false, child: Container()));
final TestSemantics expectedSemantics = TestSemantics.root(); final TestSemantics expectedSemantics = TestSemantics.root();
expect(semantics, hasSemantics(expectedSemantics)); expect(semantics, hasSemantics(expectedSemantics));
semantics.dispose();
}); });
testWidgets('Focus updates the onKey handler when the widget updates', (WidgetTester tester) async { testWidgets('Focus updates the onKey handler when the widget updates', (WidgetTester tester) async {
...@@ -2043,6 +2044,7 @@ void main() { ...@@ -2043,6 +2044,7 @@ void main() {
await tester.pumpWidget(ExcludeFocus(child: Container())); await tester.pumpWidget(ExcludeFocus(child: Container()));
final TestSemantics expectedSemantics = TestSemantics.root(); final TestSemantics expectedSemantics = TestSemantics.root();
expect(semantics, hasSemantics(expectedSemantics)); expect(semantics, hasSemantics(expectedSemantics));
semantics.dispose();
}); });
// Regression test for https://github.com/flutter/flutter/issues/92693 // Regression test for https://github.com/flutter/flutter/issues/92693
......
...@@ -2160,6 +2160,7 @@ void main() { ...@@ -2160,6 +2160,7 @@ void main() {
await tester.pumpWidget(FocusTraversalGroup(child: Container())); await tester.pumpWidget(FocusTraversalGroup(child: Container()));
final TestSemantics expectedSemantics = TestSemantics.root(); final TestSemantics expectedSemantics = TestSemantics.root();
expect(semantics, hasSemantics(expectedSemantics)); expect(semantics, hasSemantics(expectedSemantics));
semantics.dispose();
}); });
testWidgets("Descendants of FocusTraversalGroup aren't focusable if descendantsAreFocusable is false.", (WidgetTester tester) async { testWidgets("Descendants of FocusTraversalGroup aren't focusable if descendantsAreFocusable is false.", (WidgetTester tester) async {
...@@ -2418,6 +2419,7 @@ void main() { ...@@ -2418,6 +2419,7 @@ void main() {
ignoreRect: true, ignoreRect: true,
ignoreTransform: true, ignoreTransform: true,
)); ));
semantics.dispose();
}); });
testWidgets("Raw keyboard listener doesn't introduce a Semantics node when specified", (WidgetTester tester) async { testWidgets("Raw keyboard listener doesn't introduce a Semantics node when specified", (WidgetTester tester) async {
...@@ -2432,6 +2434,7 @@ void main() { ...@@ -2432,6 +2434,7 @@ void main() {
); );
final TestSemantics expectedSemantics = TestSemantics.root(); final TestSemantics expectedSemantics = TestSemantics.root();
expect(semantics, hasSemantics(expectedSemantics)); expect(semantics, hasSemantics(expectedSemantics));
semantics.dispose();
}); });
}); });
...@@ -2489,6 +2492,7 @@ void main() { ...@@ -2489,6 +2492,7 @@ void main() {
await tester.pumpWidget(ExcludeFocusTraversal(child: Container())); await tester.pumpWidget(ExcludeFocusTraversal(child: Container()));
final TestSemantics expectedSemantics = TestSemantics.root(); final TestSemantics expectedSemantics = TestSemantics.root();
expect(semantics, hasSemantics(expectedSemantics)); expect(semantics, hasSemantics(expectedSemantics));
semantics.dispose();
}); });
}); });
......
...@@ -2297,6 +2297,7 @@ void main() { ...@@ -2297,6 +2297,7 @@ void main() {
), ),
], ],
), ignoreTransform: true, ignoreRect: true)); ), ignoreTransform: true, ignoreRect: true));
semantics.dispose();
}); });
testWidgets('SelectableText semantics, enableInteractiveSelection = false', (WidgetTester tester) async { testWidgets('SelectableText semantics, enableInteractiveSelection = false', (WidgetTester tester) async {
......
...@@ -71,6 +71,7 @@ void main() { ...@@ -71,6 +71,7 @@ void main() {
), ),
], ],
), ignoreId: true, ignoreRect: true, ignoreTransform: true)); ), ignoreId: true, ignoreRect: true, ignoreTransform: true));
semantics.dispose();
}); });
testWidgets('Semantics can drop semantics config', (WidgetTester tester) async { testWidgets('Semantics can drop semantics config', (WidgetTester tester) async {
...@@ -128,6 +129,7 @@ void main() { ...@@ -128,6 +129,7 @@ void main() {
), ),
], ],
), ignoreId: true, ignoreRect: true, ignoreTransform: true)); ), ignoreId: true, ignoreRect: true, ignoreTransform: true));
semantics.dispose();
}); });
testWidgets('Semantics throws when mark the same config twice case 1', (WidgetTester tester) async { testWidgets('Semantics throws when mark the same config twice case 1', (WidgetTester tester) async {
......
...@@ -421,7 +421,7 @@ class SemanticsTester { ...@@ -421,7 +421,7 @@ class SemanticsTester {
/// You should call [dispose] at the end of a test that creates a semantics /// You should call [dispose] at the end of a test that creates a semantics
/// tester. /// tester.
SemanticsTester(this.tester) { SemanticsTester(this.tester) {
_semanticsHandle = tester.binding.pipelineOwner.ensureSemantics(); _semanticsHandle = tester.ensureSemantics();
// This _extra_ clean-up is needed for the case when a test fails and // This _extra_ clean-up is needed for the case when a test fails and
// therefore fails to call dispose() explicitly. The test is still required // therefore fails to call dispose() explicitly. The test is still required
......
...@@ -238,6 +238,7 @@ void main() { ...@@ -238,6 +238,7 @@ void main() {
], ],
); );
expect(semantics, hasSemantics(expectedSemantics, ignoreTransform: true, ignoreId: true, ignoreRect: true)); expect(semantics, hasSemantics(expectedSemantics, ignoreTransform: true, ignoreId: true, ignoreRect: true));
semantics.dispose();
}); });
testWidgets('Sliver appbars - floating and pinned - second app bar stacks below', (WidgetTester tester) async { testWidgets('Sliver appbars - floating and pinned - second app bar stacks below', (WidgetTester tester) async {
......
...@@ -677,6 +677,7 @@ void main() { ...@@ -677,6 +677,7 @@ void main() {
final RenderSliver renderSliver = renderViewport.lastChild!; final RenderSliver renderSliver = renderViewport.lastChild!;
expect(renderSliver.geometry!.scrollExtent, 0.0); expect(renderSliver.geometry!.scrollExtent, 0.0);
expect(find.byType(SliverOffstage), findsNothing); expect(find.byType(SliverOffstage), findsNothing);
semantics.dispose();
}); });
testWidgets('offstage false', (WidgetTester tester) async { testWidgets('offstage false', (WidgetTester tester) async {
...@@ -696,6 +697,7 @@ void main() { ...@@ -696,6 +697,7 @@ void main() {
final RenderSliver renderSliver = renderViewport.lastChild!; final RenderSliver renderSliver = renderViewport.lastChild!;
expect(renderSliver.geometry!.scrollExtent, 14.0); expect(renderSliver.geometry!.scrollExtent, 14.0);
expect(find.byType(SliverOffstage), paints..paragraph()); expect(find.byType(SliverOffstage), paints..paragraph());
semantics.dispose();
}); });
}); });
...@@ -841,6 +843,7 @@ void main() { ...@@ -841,6 +843,7 @@ void main() {
expect(semantics.nodesWith(label: 'a'), hasLength(1)); expect(semantics.nodesWith(label: 'a'), hasLength(1));
await tester.tap(find.byType(GestureDetector), warnIfMissed: false); await tester.tap(find.byType(GestureDetector), warnIfMissed: false);
expect(events, equals(<String>[])); expect(events, equals(<String>[]));
semantics.dispose();
}); });
testWidgets('ignores semantics', (WidgetTester tester) async { testWidgets('ignores semantics', (WidgetTester tester) async {
...@@ -863,6 +866,7 @@ void main() { ...@@ -863,6 +866,7 @@ void main() {
expect(semantics.nodesWith(label: 'a'), hasLength(0)); expect(semantics.nodesWith(label: 'a'), hasLength(0));
await tester.tap(find.byType(GestureDetector)); await tester.tap(find.byType(GestureDetector));
expect(events, equals(<String>['tap'])); expect(events, equals(<String>['tap']));
semantics.dispose();
}); });
testWidgets('ignores pointer events & semantics', (WidgetTester tester) async { testWidgets('ignores pointer events & semantics', (WidgetTester tester) async {
...@@ -884,6 +888,7 @@ void main() { ...@@ -884,6 +888,7 @@ void main() {
expect(semantics.nodesWith(label: 'a'), hasLength(0)); expect(semantics.nodesWith(label: 'a'), hasLength(0));
await tester.tap(find.byType(GestureDetector), warnIfMissed: false); await tester.tap(find.byType(GestureDetector), warnIfMissed: false);
expect(events, equals(<String>[])); expect(events, equals(<String>[]));
semantics.dispose();
}); });
testWidgets('ignores nothing', (WidgetTester tester) async { testWidgets('ignores nothing', (WidgetTester tester) async {
...@@ -906,6 +911,7 @@ void main() { ...@@ -906,6 +911,7 @@ void main() {
expect(semantics.nodesWith(label: 'a'), hasLength(1)); expect(semantics.nodesWith(label: 'a'), hasLength(1));
await tester.tap(find.byType(GestureDetector)); await tester.tap(find.byType(GestureDetector));
expect(events, equals(<String>['tap'])); expect(events, equals(<String>['tap']));
semantics.dispose();
}); });
}); });
......
...@@ -1151,6 +1151,7 @@ void main() { ...@@ -1151,6 +1151,7 @@ void main() {
), ),
], ],
))); )));
semantics.dispose();
}, skip: isBrowser); // https://github.com/flutter/flutter/issues/87877 }, skip: isBrowser); // https://github.com/flutter/flutter/issues/87877
// Regression test for https://github.com/flutter/flutter/issues/69787 // Regression test for https://github.com/flutter/flutter/issues/69787
...@@ -1174,29 +1175,34 @@ void main() { ...@@ -1174,29 +1175,34 @@ void main() {
), ),
); );
expect(semantics, hasSemantics(TestSemantics.root( expect(
children: <TestSemantics>[ semantics,
TestSemantics( hasSemantics(
TestSemantics.root(
children: <TestSemantics>[ children: <TestSemantics>[
TestSemantics(label: 'included'),
TestSemantics( TestSemantics(
label: 'HELLO', children: <TestSemantics>[
actions: <SemanticsAction>[ TestSemantics(label: 'included'),
SemanticsAction.tap, TestSemantics(
], label: 'HELLO',
flags: <SemanticsFlag>[ actions: <SemanticsAction>[
SemanticsFlag.isLink, SemanticsAction.tap,
],
flags: <SemanticsFlag>[
SemanticsFlag.isLink,
],
),
TestSemantics(label: 'included2'),
], ],
), ),
TestSemantics(label: 'included2'),
], ],
), ),
], ignoreId: true,
), ignoreRect: true,
ignoreId: true, ignoreTransform: true,
ignoreRect: true, ),
ignoreTransform: true, );
)); semantics.dispose();
}, skip: isBrowser); // https://github.com/flutter/flutter/issues/87877 }, skip: isBrowser); // https://github.com/flutter/flutter/issues/87877
// Regression test for https://github.com/flutter/flutter/issues/69787 // Regression test for https://github.com/flutter/flutter/issues/69787
...@@ -1232,29 +1238,34 @@ void main() { ...@@ -1232,29 +1238,34 @@ void main() {
), ),
); );
expect(semantics, hasSemantics(TestSemantics.root( expect(
children: <TestSemantics>[ semantics,
TestSemantics( hasSemantics(
TestSemantics.root(
children: <TestSemantics>[ children: <TestSemantics>[
TestSemantics(label: 'foo'),
TestSemantics(label: 'bar'),
TestSemantics( TestSemantics(
label: 'HELLO', children: <TestSemantics>[
actions: <SemanticsAction>[ TestSemantics(label: 'foo'),
SemanticsAction.tap, TestSemantics(label: 'bar'),
], TestSemantics(
flags: <SemanticsFlag>[ label: 'HELLO',
SemanticsFlag.isLink, actions: <SemanticsAction>[
SemanticsAction.tap,
],
flags: <SemanticsFlag>[
SemanticsFlag.isLink,
],
),
], ],
), ),
], ],
), ),
], ignoreId: true,
), ignoreRect: true,
ignoreId: true, ignoreTransform: true,
ignoreRect: true, ),
ignoreTransform: true, );
)); semantics.dispose();
}, skip: isBrowser); // https://github.com/flutter/flutter/issues/87877 }, skip: isBrowser); // https://github.com/flutter/flutter/issues/87877
// Regression test for https://github.com/flutter/flutter/issues/69787 // Regression test for https://github.com/flutter/flutter/issues/69787
...@@ -1303,24 +1314,29 @@ void main() { ...@@ -1303,24 +1314,29 @@ void main() {
), ),
); );
expect(semantics, hasSemantics(TestSemantics.root( expect(
children: <TestSemantics>[ semantics,
TestSemantics( hasSemantics(
TestSemantics.root(
children: <TestSemantics>[ children: <TestSemantics>[
TestSemantics(label: 'not clipped'),
TestSemantics( TestSemantics(
label: 'next WS is clipped', children: <TestSemantics>[
flags: <SemanticsFlag>[SemanticsFlag.isLink], TestSemantics(label: 'not clipped'),
actions: <SemanticsAction>[SemanticsAction.tap], TestSemantics(
label: 'next WS is clipped',
flags: <SemanticsFlag>[SemanticsFlag.isLink],
actions: <SemanticsAction>[SemanticsAction.tap],
),
],
), ),
], ],
), ),
], ignoreId: true,
), ignoreRect: true,
ignoreId: true, ignoreTransform: true,
ignoreRect: true, ),
ignoreTransform: true, );
)); semantics.dispose();
}, skip: isBrowser); // https://github.com/flutter/flutter/issues/87877 }, skip: isBrowser); // https://github.com/flutter/flutter/issues/87877
testWidgets('RenderParagraph intrinsic width', (WidgetTester tester) async { testWidgets('RenderParagraph intrinsic width', (WidgetTester tester) async {
......
...@@ -473,7 +473,7 @@ void main() { ...@@ -473,7 +473,7 @@ void main() {
}); });
testWidgets('works when semantics are enabled', (WidgetTester tester) async { testWidgets('works when semantics are enabled', (WidgetTester tester) async {
final SemanticsHandle semantics = RendererBinding.instance.pipelineOwner.ensureSemantics(); final SemanticsHandle semantics = tester.ensureSemantics();
await tester.pumpWidget( await tester.pumpWidget(
const Text('hello', textDirection: TextDirection.ltr)); const Text('hello', textDirection: TextDirection.ltr));
...@@ -497,7 +497,7 @@ void main() { ...@@ -497,7 +497,7 @@ void main() {
}, semanticsEnabled: false); }, semanticsEnabled: false);
testWidgets('throws state error multiple matches are found', (WidgetTester tester) async { testWidgets('throws state error multiple matches are found', (WidgetTester tester) async {
final SemanticsHandle semantics = RendererBinding.instance.pipelineOwner.ensureSemantics(); final SemanticsHandle semantics = tester.ensureSemantics();
await tester.pumpWidget( await tester.pumpWidget(
Directionality( Directionality(
textDirection: TextDirection.ltr, textDirection: TextDirection.ltr,
......
...@@ -155,10 +155,10 @@ void testWidgets( ...@@ -155,10 +155,10 @@ void testWidgets(
() { () {
tester._testDescription = combinedDescription; tester._testDescription = combinedDescription;
SemanticsHandle? semanticsHandle; SemanticsHandle? semanticsHandle;
tester._recordNumberOfSemanticsHandles();
if (semanticsEnabled == true) { if (semanticsEnabled == true) {
semanticsHandle = tester.ensureSemantics(); semanticsHandle = tester.ensureSemantics();
} }
tester._recordNumberOfSemanticsHandles();
test_package.addTearDown(binding.postTest); test_package.addTearDown(binding.postTest);
return binding.runTest( return binding.runTest(
() async { () async {
...@@ -1044,18 +1044,16 @@ class WidgetTester extends WidgetController implements HitTestDispatcher, Ticker ...@@ -1044,18 +1044,16 @@ class WidgetTester extends WidgetController implements HitTestDispatcher, Ticker
void _verifySemanticsHandlesWereDisposed() { void _verifySemanticsHandlesWereDisposed() {
assert(_lastRecordedSemanticsHandles != null); assert(_lastRecordedSemanticsHandles != null);
if (binding.pipelineOwner.debugOutstandingSemanticsHandles > _lastRecordedSemanticsHandles!) { // TODO(goderbauer): Fix known leak in web engine when running integration tests and remove this "correction", https://github.com/flutter/flutter/issues/121640.
final int knownWebEngineLeakForLiveTestsCorrection = kIsWeb && binding is LiveTestWidgetsFlutterBinding ? 2 : 0;
if (_currentSemanticsHandles - knownWebEngineLeakForLiveTestsCorrection > _lastRecordedSemanticsHandles!) {
throw FlutterError.fromParts(<DiagnosticsNode>[ throw FlutterError.fromParts(<DiagnosticsNode>[
ErrorSummary('A SemanticsHandle was active at the end of the test.'), ErrorSummary('A SemanticsHandle was active at the end of the test.'),
ErrorDescription( ErrorDescription(
'All SemanticsHandle instances must be disposed by calling dispose() on ' 'All SemanticsHandle instances must be disposed by calling dispose() on '
'the SemanticsHandle.' 'the SemanticsHandle.'
), ),
ErrorHint(
'If your test uses SemanticsTester, it is '
'sufficient to call dispose() on SemanticsTester. Otherwise, the '
'existing handle will leak into another test and alter its behavior.'
),
]); ]);
} }
_lastRecordedSemanticsHandles = null; _lastRecordedSemanticsHandles = null;
...@@ -1063,8 +1061,10 @@ class WidgetTester extends WidgetController implements HitTestDispatcher, Ticker ...@@ -1063,8 +1061,10 @@ class WidgetTester extends WidgetController implements HitTestDispatcher, Ticker
int? _lastRecordedSemanticsHandles; int? _lastRecordedSemanticsHandles;
int get _currentSemanticsHandles => binding.debugOutstandingSemanticsHandles + binding.pipelineOwner.debugOutstandingSemanticsHandles;
void _recordNumberOfSemanticsHandles() { void _recordNumberOfSemanticsHandles() {
_lastRecordedSemanticsHandles = binding.pipelineOwner.debugOutstandingSemanticsHandles; _lastRecordedSemanticsHandles = _currentSemanticsHandles;
} }
/// Returns the TestTextInput singleton. /// Returns the TestTextInput singleton.
......
...@@ -737,7 +737,6 @@ void main() { ...@@ -737,7 +737,6 @@ void main() {
testWidgets('failure does not throw unexpected errors', (WidgetTester tester) async { testWidgets('failure does not throw unexpected errors', (WidgetTester tester) async {
final SemanticsHandle handle = tester.ensureSemantics(); final SemanticsHandle handle = tester.ensureSemantics();
addTearDown(() => handle.dispose());
const Key key = Key('semantics'); const Key key = Key('semantics');
await tester.pumpWidget(Semantics( await tester.pumpWidget(Semantics(
...@@ -789,13 +788,13 @@ void main() { ...@@ -789,13 +788,13 @@ void main() {
); );
expect(failedExpectation, throwsA(isA<TestFailure>())); expect(failedExpectation, throwsA(isA<TestFailure>()));
handle.dispose();
}); });
}); });
group('containsSemantics', () { group('containsSemantics', () {
testWidgets('matches SemanticsData', (WidgetTester tester) async { testWidgets('matches SemanticsData', (WidgetTester tester) async {
final SemanticsHandle handle = tester.ensureSemantics(); final SemanticsHandle handle = tester.ensureSemantics();
addTearDown(() => handle.dispose());
const Key key = Key('semantics'); const Key key = Key('semantics');
await tester.pumpWidget(Semantics( await tester.pumpWidget(Semantics(
...@@ -889,6 +888,7 @@ void main() { ...@@ -889,6 +888,7 @@ void main() {
)), )),
reason: 'onTapHint "scans" should not have matched "scan".', reason: 'onTapHint "scans" should not have matched "scan".',
); );
handle.dispose();
}); });
testWidgets('can match all semantics flags and actions enabled', (WidgetTester tester) async { testWidgets('can match all semantics flags and actions enabled', (WidgetTester tester) async {
...@@ -1233,7 +1233,6 @@ void main() { ...@@ -1233,7 +1233,6 @@ void main() {
testWidgets('failure does not throw unexpected errors', (WidgetTester tester) async { testWidgets('failure does not throw unexpected errors', (WidgetTester tester) async {
final SemanticsHandle handle = tester.ensureSemantics(); final SemanticsHandle handle = tester.ensureSemantics();
addTearDown(() => handle.dispose());
const Key key = Key('semantics'); const Key key = Key('semantics');
await tester.pumpWidget(Semantics( await tester.pumpWidget(Semantics(
...@@ -1283,6 +1282,7 @@ void main() { ...@@ -1283,6 +1282,7 @@ void main() {
); );
expect(failedExpectation, throwsA(isA<TestFailure>())); expect(failedExpectation, throwsA(isA<TestFailure>()));
handle.dispose();
}); });
}); });
......
// Copyright 2014 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'dart:async';
import 'package:flutter/foundation.dart';
import 'package:flutter_test/flutter_test.dart';
final List<FlutterErrorDetails> errors = <FlutterErrorDetails>[];
Future<void> testExecutable(FutureOr<void> Function() testMain) async {
reportTestException = (FlutterErrorDetails details, String testDescription) {
errors.add(details);
};
// The error that the test throws in their run methods below will be forwarded
// to our exception handler above and do not cause the test to fail. The
// tearDown method then checks that the test threw the expected exception.
await testMain();
}
void pipelineOwnerTestRun() {
testWidgets('open SemanticsHandle from PipelineOwner fails test', (WidgetTester tester) async {
final int outstandingHandles = tester.binding.pipelineOwner.debugOutstandingSemanticsHandles;
tester.binding.pipelineOwner.ensureSemantics();
expect(tester.binding.pipelineOwner.debugOutstandingSemanticsHandles, outstandingHandles + 1);
// SemanticsHandle is not disposed on purpose to verify in tearDown that
// the test failed due to an active SemanticsHandle.
});
tearDown(() {
expect(errors, hasLength(1));
expect(errors.single.toString(), contains('SemanticsHandle was active at the end of the test'));
});
}
void semanticsBindingTestRun() {
testWidgets('open SemanticsHandle from SemanticsBinding fails test', (WidgetTester tester) async {
final int outstandingHandles = tester.binding.debugOutstandingSemanticsHandles;
tester.binding.ensureSemantics();
expect(tester.binding.debugOutstandingSemanticsHandles, outstandingHandles + 1);
// SemanticsHandle is not disposed on purpose to verify in tearDown that
// the test failed due to an active SemanticsHandle.
});
tearDown(() {
expect(errors, hasLength(1));
expect(errors.single.toString(), contains('SemanticsHandle was active at the end of the test'));
});
}
void failingTestTestRun() {
testWidgets('open SemanticsHandle from SemanticsBinding fails test', (WidgetTester tester) async {
final int outstandingHandles = tester.binding.debugOutstandingSemanticsHandles;
tester.binding.ensureSemantics();
expect(tester.binding.debugOutstandingSemanticsHandles, outstandingHandles + 1);
// Failing expectation to verify that an open semantics handle doesn't
// cause any cascading failures and only the failing expectation is
// reported.
expect(1, equals(2));
fail('The test should never have gotten this far.');
});
tearDown(() {
expect(errors, hasLength(1));
expect(errors.single.toString(), contains('Expected: <2>'));
expect(errors.single.toString(), contains('Actual: <1>'));
});
}
// Copyright 2014 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'flutter_test_config.dart' as config;
void main() => config.failingTestTestRun();
// Copyright 2014 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'flutter_test_config.dart' as config;
void main() => config.pipelineOwnerTestRun();
// Copyright 2014 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'flutter_test_config.dart' as config;
void main() => config.semanticsBindingTestRun();
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment