Fix how tests count open SemanticsHandles (#121571)
Fix how tests count open SemanticsHandles
This commit is contained in:
parent
f97a51533b
commit
6de42a70f1
@ -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() {
|
|||||||
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() {
|
|||||||
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() {
|
|||||||
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() {
|
|||||||
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() {
|
|||||||
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() {
|
|||||||
isButton: true,
|
isButton: true,
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
semantics.dispose();
|
||||||
});
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -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() {
|
|||||||
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() {
|
|||||||
isFocusable: true,
|
isFocusable: true,
|
||||||
));
|
));
|
||||||
});
|
});
|
||||||
|
semantics.dispose();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -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() {
|
|||||||
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() {
|
|||||||
|
|
||||||
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() {
|
|||||||
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() {
|
|||||||
),
|
),
|
||||||
],
|
],
|
||||||
), 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() {
|
|||||||
// 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() {
|
|||||||
type: SemanticsAction.showOnScreen,
|
type: SemanticsAction.showOnScreen,
|
||||||
nodeId: nodeId,
|
nodeId: nodeId,
|
||||||
));
|
));
|
||||||
|
semantics.dispose();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -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() {
|
|||||||
|
|
||||||
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() {
|
|||||||
],
|
],
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
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() {
|
|||||||
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() {
|
|||||||
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() {
|
|||||||
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() {
|
|||||||
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() {
|
|||||||
);
|
);
|
||||||
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() {
|
|||||||
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() {
|
|||||||
),
|
),
|
||||||
],
|
],
|
||||||
), 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() {
|
|||||||
),
|
),
|
||||||
],
|
],
|
||||||
), 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() {
|
|||||||
),
|
),
|
||||||
],
|
],
|
||||||
), 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 {
|
|||||||
/// 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() {
|
|||||||
],
|
],
|
||||||
);
|
);
|
||||||
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() {
|
|||||||
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() {
|
|||||||
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() {
|
|||||||
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() {
|
|||||||
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() {
|
|||||||
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() {
|
|||||||
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() {
|
|||||||
),
|
),
|
||||||
],
|
],
|
||||||
)));
|
)));
|
||||||
|
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() {
|
|||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
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() {
|
|||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
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() {
|
|||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
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() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
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() {
|
|||||||
}, 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(
|
|||||||
() {
|
() {
|
||||||
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
|
|||||||
|
|
||||||
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
|
|||||||
|
|
||||||
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() {
|
|||||||
|
|
||||||
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() {
|
|||||||
);
|
);
|
||||||
|
|
||||||
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() {
|
|||||||
)),
|
)),
|
||||||
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() {
|
|||||||
|
|
||||||
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() {
|
|||||||
);
|
);
|
||||||
|
|
||||||
expect(failedExpectation, throwsA(isA<TestFailure>()));
|
expect(failedExpectation, throwsA(isA<TestFailure>()));
|
||||||
|
handle.dispose();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -0,0 +1,71 @@
|
|||||||
|
// 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>'));
|
||||||
|
});
|
||||||
|
}
|
@ -0,0 +1,7 @@
|
|||||||
|
// 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();
|
@ -0,0 +1,7 @@
|
|||||||
|
// 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();
|
@ -0,0 +1,7 @@
|
|||||||
|
// 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();
|
Loading…
x
Reference in New Issue
Block a user