Cover more test/widgets tests with leak tracking #12. (#135385)

This commit is contained in:
Kostia Sokolovskyi 2023-09-30 02:47:04 +02:00 committed by GitHub
parent fdde24195f
commit 95eae5f967
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
9 changed files with 456 additions and 283 deletions

View file

@ -435,7 +435,7 @@ void main() {
);
});
testWidgets('disposes animation and controller', (WidgetTester tester) async {
testWidgetsWithLeakTracking('disposes animation and controller', (WidgetTester tester) async {
await tester.pumpWidget(
const Center(
child: AnimatedSize(

View file

@ -1062,13 +1062,14 @@ void main() {
element.createChild(0, after: null);
});
testWidgets('GlobalKey - re-attach child to new parents, and the old parent is deactivated(unmounted)', (WidgetTester tester) async {
testWidgetsWithLeakTracking('GlobalKey - re-attach child to new parents, and the old parent is deactivated(unmounted)', (WidgetTester tester) async {
// This is a regression test for https://github.com/flutter/flutter/issues/62055
const Key key1 = GlobalObjectKey('key1');
const Key key2 = GlobalObjectKey('key2');
late StateSetter setState;
int tabBarViewCnt = 2;
TabController tabController = TabController(length: tabBarViewCnt, vsync: const TestVSync());
addTearDown(tabController.dispose);
await tester.pumpWidget(Directionality(
textDirection: TextDirection.ltr,
@ -1101,6 +1102,7 @@ void main() {
setState(() {
tabBarViewCnt = 1;
tabController = TabController(length: tabBarViewCnt, vsync: const TestVSync());
addTearDown(tabController.dispose);
});
await tester.pump(const Duration(seconds: 1)); // finish the animation

View file

@ -15,6 +15,7 @@ import 'package:flutter/src/widgets/_html_element_view_web.dart'
show debugOverridePlatformViewRegistry;
import 'package:flutter/widgets.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:leak_tracker_flutter_testing/leak_tracker_flutter_testing.dart';
import 'package:web/web.dart' as web;
final Object _mockHtmlElement = Object();
@ -42,7 +43,7 @@ void main() {
});
group('HtmlElementView', () {
testWidgets('Create HTML view', (WidgetTester tester) async {
testWidgetsWithLeakTracking('Create HTML view', (WidgetTester tester) async {
final int currentViewId = platformViewsRegistry.getNextPlatformViewId();
fakePlatformViewRegistry.registerViewFactory('webview', _mockViewFactory);
@ -64,7 +65,7 @@ void main() {
);
});
testWidgets('Create HTML view with PlatformViewCreatedCallback', (WidgetTester tester) async {
testWidgetsWithLeakTracking('Create HTML view with PlatformViewCreatedCallback', (WidgetTester tester) async {
final int currentViewId = platformViewsRegistry.getNextPlatformViewId();
fakePlatformViewRegistry.registerViewFactory('webview', _mockViewFactory);
@ -97,7 +98,7 @@ void main() {
);
});
testWidgets('Create HTML view with creation params', (WidgetTester tester) async {
testWidgetsWithLeakTracking('Create HTML view with creation params', (WidgetTester tester) async {
final int currentViewId = platformViewsRegistry.getNextPlatformViewId();
fakePlatformViewRegistry.registerViewFactory('webview', _mockViewFactory);
await tester.pumpWidget(
@ -132,7 +133,7 @@ void main() {
);
});
testWidgets('Resize HTML view', (WidgetTester tester) async {
testWidgetsWithLeakTracking('Resize HTML view', (WidgetTester tester) async {
final int currentViewId = platformViewsRegistry.getNextPlatformViewId();
fakePlatformViewRegistry.registerViewFactory('webview', _mockViewFactory);
await tester.pumpWidget(
@ -168,7 +169,7 @@ void main() {
);
});
testWidgets('Change HTML view type', (WidgetTester tester) async {
testWidgetsWithLeakTracking('Change HTML view type', (WidgetTester tester) async {
final int currentViewId = platformViewsRegistry.getNextPlatformViewId();
fakePlatformViewRegistry.registerViewFactory('webview', _mockViewFactory);
fakePlatformViewRegistry.registerViewFactory('maps', _mockViewFactory);
@ -200,7 +201,7 @@ void main() {
);
});
testWidgets('Dispose HTML view', (WidgetTester tester) async {
testWidgetsWithLeakTracking('Dispose HTML view', (WidgetTester tester) async {
fakePlatformViewRegistry.registerViewFactory('webview', _mockViewFactory);
await tester.pumpWidget(
const Center(
@ -227,7 +228,7 @@ void main() {
);
});
testWidgets('HTML view survives widget tree change', (WidgetTester tester) async {
testWidgetsWithLeakTracking('HTML view survives widget tree change', (WidgetTester tester) async {
final int currentViewId = platformViewsRegistry.getNextPlatformViewId();
fakePlatformViewRegistry.registerViewFactory('webview', _mockViewFactory);
final GlobalKey key = GlobalKey();
@ -259,7 +260,7 @@ void main() {
);
});
testWidgets('HtmlElementView has correct semantics', (WidgetTester tester) async {
testWidgetsWithLeakTracking('HtmlElementView has correct semantics', (WidgetTester tester) async {
final SemanticsHandle handle = tester.ensureSemantics();
final int currentViewId = platformViewsRegistry.getNextPlatformViewId();
expect(currentViewId, greaterThanOrEqualTo(0));
@ -306,7 +307,7 @@ void main() {
debugOverridePlatformViewRegistry = null;
});
testWidgets('Create platform view from tagName', (WidgetTester tester) async {
testWidgetsWithLeakTracking('Create platform view from tagName', (WidgetTester tester) async {
final int currentViewId = platformViewsRegistry.getNextPlatformViewId();
await tester.pumpWidget(
@ -331,7 +332,7 @@ void main() {
expect(htmlElement.tagName, equalsIgnoringCase('div'));
});
testWidgets('Create invisible platform view', (WidgetTester tester) async {
testWidgetsWithLeakTracking('Create invisible platform view', (WidgetTester tester) async {
final int currentViewId = platformViewsRegistry.getNextPlatformViewId();
await tester.pumpWidget(
@ -357,7 +358,7 @@ void main() {
expect(htmlElement.tagName, equalsIgnoringCase('script'));
});
testWidgets('onElementCreated', (WidgetTester tester) async {
testWidgetsWithLeakTracking('onElementCreated', (WidgetTester tester) async {
final List<Object> createdElements = <Object>[];
void onElementCreated(Object element) {
createdElements.add(element);

View file

@ -55,7 +55,7 @@ void main() {
});
group('construction check', () {
testWidgets('ListWheelScrollView needs positive diameter ratio', (WidgetTester tester) async {
testWidgetsWithLeakTracking('ListWheelScrollView needs positive diameter ratio', (WidgetTester tester) async {
expect(
() => ListWheelScrollView(
diameterRatio: nonconst(-2.0),
@ -70,7 +70,7 @@ void main() {
);
});
testWidgets('ListWheelScrollView can have zero child', (WidgetTester tester) async {
testWidgetsWithLeakTracking('ListWheelScrollView can have zero child', (WidgetTester tester) async {
await tester.pumpWidget(
Directionality(
textDirection: TextDirection.ltr,
@ -114,7 +114,7 @@ void main() {
expect(detach, 1);
});
testWidgets('ListWheelScrollView needs positive magnification', (WidgetTester tester) async {
testWidgetsWithLeakTracking('ListWheelScrollView needs positive magnification', (WidgetTester tester) async {
expect(
() {
ListWheelScrollView(
@ -128,7 +128,7 @@ void main() {
);
});
testWidgets('ListWheelScrollView needs valid overAndUnderCenterOpacity', (WidgetTester tester) async {
testWidgetsWithLeakTracking('ListWheelScrollView needs valid overAndUnderCenterOpacity', (WidgetTester tester) async {
expect(
() {
ListWheelScrollView(
@ -175,9 +175,9 @@ void main() {
});
group('infinite scrolling', () {
testWidgets('infinite looping list', (WidgetTester tester) async {
final FixedExtentScrollController controller =
FixedExtentScrollController();
testWidgetsWithLeakTracking('infinite looping list', (WidgetTester tester) async {
final FixedExtentScrollController controller = FixedExtentScrollController();
addTearDown(controller.dispose);
await tester.pumpWidget(
Directionality(
@ -221,9 +221,9 @@ void main() {
);
});
testWidgets('infinite child builder', (WidgetTester tester) async {
final FixedExtentScrollController controller =
FixedExtentScrollController();
testWidgetsWithLeakTracking('infinite child builder', (WidgetTester tester) async {
final FixedExtentScrollController controller = FixedExtentScrollController();
addTearDown(controller.dispose);
await tester.pumpWidget(
Directionality(
@ -262,12 +262,12 @@ void main() {
);
});
testWidgets('child builder with lower and upper limits', (WidgetTester tester) async {
testWidgetsWithLeakTracking('child builder with lower and upper limits', (WidgetTester tester) async {
// Adjust the content dimensions at the end of `RenderListWheelViewport.performLayout()`
final List<int> paintedChildren = <int>[];
final FixedExtentScrollController controller =
FixedExtentScrollController(initialItem: -10);
final FixedExtentScrollController controller = FixedExtentScrollController(initialItem: -10);
addTearDown(controller.dispose);
await tester.pumpWidget(
Directionality(
@ -320,9 +320,11 @@ void main() {
});
group('layout', () {
testWidgets('Flings with high velocity should not break the children lower and upper limits', (WidgetTester tester) async {
testWidgetsWithLeakTracking('Flings with high velocity should not break the children lower and upper limits', (WidgetTester tester) async {
// Regression test for https://github.com/flutter/flutter/issues/112526
final FixedExtentScrollController controller = FixedExtentScrollController();
addTearDown(controller.dispose);
Widget buildFrame() {
return Directionality(
textDirection: TextDirection.ltr,
@ -359,8 +361,10 @@ void main() {
}, variant: TargetPlatformVariant(TargetPlatform.values.toSet()));
// Regression test for https://github.com/flutter/flutter/issues/90953
testWidgets('ListWheelScrollView childDelegate update test 2', (WidgetTester tester) async {
final FixedExtentScrollController controller = FixedExtentScrollController( initialItem: 2 );
testWidgetsWithLeakTracking('ListWheelScrollView childDelegate update test 2', (WidgetTester tester) async {
final FixedExtentScrollController controller = FixedExtentScrollController(initialItem: 2);
addTearDown(controller.dispose);
Widget buildFrame(int childCount) {
return Directionality(
textDirection: TextDirection.ltr,
@ -422,8 +426,10 @@ void main() {
});
// Regression test for https://github.com/flutter/flutter/issues/58144
testWidgets('ListWheelScrollView childDelegate update test', (WidgetTester tester) async {
testWidgetsWithLeakTracking('ListWheelScrollView childDelegate update test', (WidgetTester tester) async {
final FixedExtentScrollController controller = FixedExtentScrollController();
addTearDown(controller.dispose);
Widget buildFrame(int childCount) {
return Directionality(
textDirection: TextDirection.ltr,
@ -453,7 +459,7 @@ void main() {
expect(tester.renderObject(find.text('1')).attached, true);
});
testWidgets("ListWheelScrollView takes parent's size with small children", (WidgetTester tester) async {
testWidgetsWithLeakTracking("ListWheelScrollView takes parent's size with small children", (WidgetTester tester) async {
await tester.pumpWidget(
Directionality(
textDirection: TextDirection.ltr,
@ -474,7 +480,7 @@ void main() {
expect(tester.getBottomRight(find.byType(ListWheelScrollView)), const Offset(800.0, 600.0));
});
testWidgets("ListWheelScrollView takes parent's size with large children", (WidgetTester tester) async {
testWidgetsWithLeakTracking("ListWheelScrollView takes parent's size with large children", (WidgetTester tester) async {
await tester.pumpWidget(
Directionality(
textDirection: TextDirection.ltr,
@ -495,7 +501,7 @@ void main() {
expect(tester.getBottomRight(find.byType(ListWheelScrollView)), const Offset(800.0, 600.0));
});
testWidgets("ListWheelScrollView children can't be bigger than itemExtent", (WidgetTester tester) async {
testWidgetsWithLeakTracking("ListWheelScrollView children can't be bigger than itemExtent", (WidgetTester tester) async {
await tester.pumpWidget(
Directionality(
textDirection: TextDirection.ltr,
@ -517,10 +523,10 @@ void main() {
expect(find.text('blah'), findsOneWidget);
});
testWidgets('builder is never called twice for same index', (WidgetTester tester) async {
testWidgetsWithLeakTracking('builder is never called twice for same index', (WidgetTester tester) async {
final Set<int> builtChildren = <int>{};
final FixedExtentScrollController controller =
FixedExtentScrollController();
final FixedExtentScrollController controller = FixedExtentScrollController();
addTearDown(controller.dispose);
await tester.pumpWidget(
Directionality(
@ -554,9 +560,9 @@ void main() {
await tester.pump();
});
testWidgets('only visible children are maintained as children of the rendered viewport', (WidgetTester tester) async {
final FixedExtentScrollController controller =
FixedExtentScrollController();
testWidgetsWithLeakTracking('only visible children are maintained as children of the rendered viewport', (WidgetTester tester) async {
final FixedExtentScrollController controller = FixedExtentScrollController();
addTearDown(controller.dispose);
await tester.pumpWidget(
Directionality(
@ -591,9 +597,9 @@ void main() {
expect(viewport.childCount, 4);
});
testWidgets('a tighter squeeze lays out more children', (WidgetTester tester) async {
final FixedExtentScrollController controller =
FixedExtentScrollController(initialItem: 10);
testWidgetsWithLeakTracking('a tighter squeeze lays out more children', (WidgetTester tester) async {
final FixedExtentScrollController controller = FixedExtentScrollController(initialItem: 10);
addTearDown(controller.dispose);
await tester.pumpWidget(
Directionality(
@ -636,7 +642,7 @@ void main() {
expect(viewport.childCount, 13);
});
testWidgets('Active children are laid out with correct offset', (WidgetTester tester) async {
testWidgetsWithLeakTracking('Active children are laid out with correct offset', (WidgetTester tester) async {
// Regression test for https://github.com/flutter/flutter/issues/123497
Future<void> buildWidget(double width) async {
return tester.pumpWidget(
@ -673,8 +679,9 @@ void main() {
});
group('pre-transform viewport', () {
testWidgets('ListWheelScrollView starts and ends from the middle', (WidgetTester tester) async {
testWidgetsWithLeakTracking('ListWheelScrollView starts and ends from the middle', (WidgetTester tester) async {
final ScrollController controller = ScrollController();
addTearDown(controller.dispose);
final List<int> paintedChildren = <int>[];
await tester.pumpWidget(
@ -715,8 +722,9 @@ void main() {
expect(paintedChildren, <int>[96, 97, 98, 99]);
});
testWidgets('A child gets painted as soon as its first pixel is in the viewport', (WidgetTester tester) async {
testWidgetsWithLeakTracking('A child gets painted as soon as its first pixel is in the viewport', (WidgetTester tester) async {
final ScrollController controller = ScrollController(initialScrollOffset: 50.0);
addTearDown(controller.dispose);
final List<int> paintedChildren = <int>[];
await tester.pumpWidget(
@ -749,8 +757,9 @@ void main() {
expect(paintedChildren, <int>[0, 1, 2, 3, 4]);
});
testWidgets('A child is no longer painted after its last pixel leaves the viewport', (WidgetTester tester) async {
testWidgetsWithLeakTracking('A child is no longer painted after its last pixel leaves the viewport', (WidgetTester tester) async {
final ScrollController controller = ScrollController(initialScrollOffset: 250.0);
addTearDown(controller.dispose);
final List<int> paintedChildren = <int>[];
await tester.pumpWidget(
@ -793,7 +802,7 @@ void main() {
});
group('viewport transformation', () {
testWidgets('Center child is magnified', (WidgetTester tester) async {
testWidgetsWithLeakTracking('Center child is magnified', (WidgetTester tester) async {
await tester.pumpWidget(
Directionality(
textDirection: TextDirection.ltr,
@ -817,7 +826,7 @@ void main() {
);
});
testWidgets('Default middle transform', (WidgetTester tester) async {
testWidgetsWithLeakTracking('Default middle transform', (WidgetTester tester) async {
await tester.pumpWidget(
Directionality(
textDirection: TextDirection.ltr,
@ -846,8 +855,10 @@ void main() {
));
});
testWidgets('Curve the wheel to the left', (WidgetTester tester) async {
testWidgetsWithLeakTracking('Curve the wheel to the left', (WidgetTester tester) async {
final ScrollController controller = ScrollController(initialScrollOffset: 300.0);
addTearDown(controller.dispose);
await tester.pumpWidget(
Directionality(
textDirection: TextDirection.ltr,
@ -871,8 +882,9 @@ void main() {
);
});
testWidgets('Scrolling, diameterRatio, perspective all changes matrix', (WidgetTester tester) async {
testWidgetsWithLeakTracking('Scrolling, diameterRatio, perspective all changes matrix', (WidgetTester tester) async {
final ScrollController controller = ScrollController(initialScrollOffset: 200.0);
addTearDown(controller.dispose);
await tester.pumpWidget(
Directionality(
@ -990,8 +1002,9 @@ void main() {
));
});
testWidgets('offAxisFraction, magnification changes matrix', (WidgetTester tester) async {
testWidgetsWithLeakTracking('offAxisFraction, magnification changes matrix', (WidgetTester tester) async {
final ScrollController controller = ScrollController(initialScrollOffset: 200.0);
addTearDown(controller.dispose);
await tester.pumpWidget(
Directionality(
@ -1089,7 +1102,7 @@ void main() {
});
group('scroll notifications', () {
testWidgets('no onSelectedItemChanged callback on first build', (WidgetTester tester) async {
testWidgetsWithLeakTracking('no onSelectedItemChanged callback on first build', (WidgetTester tester) async {
bool itemChangeCalled = false;
void onItemChange(int _) { itemChangeCalled = true; }
@ -1114,7 +1127,7 @@ void main() {
expect(itemChangeCalled, false);
});
testWidgets('onSelectedItemChanged when a new item is closest to center', (WidgetTester tester) async {
testWidgetsWithLeakTracking('onSelectedItemChanged when a new item is closest to center', (WidgetTester tester) async {
final List<int> selectedItems = <int>[];
await tester.pumpWidget(
@ -1151,7 +1164,7 @@ void main() {
expect(selectedItems, <int>[1, 2, 1]);
});
testWidgets('onSelectedItemChanged reports only in valid range', (WidgetTester tester) async {
testWidgetsWithLeakTracking('onSelectedItemChanged reports only in valid range', (WidgetTester tester) async {
final List<int> selectedItems = <int>[];
await tester.pumpWidget(
@ -1187,8 +1200,9 @@ void main() {
});
group('scroll controller', () {
testWidgets('initialItem', (WidgetTester tester) async {
testWidgetsWithLeakTracking('initialItem', (WidgetTester tester) async {
final FixedExtentScrollController controller = FixedExtentScrollController(initialItem: 10);
addTearDown(controller.dispose);
final List<int> paintedChildren = <int>[];
await tester.pumpWidget(
@ -1213,8 +1227,9 @@ void main() {
expect(controller.selectedItem, 10);
});
testWidgets('controller jump', (WidgetTester tester) async {
testWidgetsWithLeakTracking('controller jump', (WidgetTester tester) async {
final FixedExtentScrollController controller = FixedExtentScrollController(initialItem: 10);
addTearDown(controller.dispose);
final List<int> paintedChildren = <int>[];
await tester.pumpWidget(
@ -1245,8 +1260,9 @@ void main() {
expect(controller.selectedItem, 0);
});
testWidgets('controller animateToItem', (WidgetTester tester) async {
testWidgetsWithLeakTracking('controller animateToItem', (WidgetTester tester) async {
final FixedExtentScrollController controller = FixedExtentScrollController(initialItem: 10);
addTearDown(controller.dispose);
final List<int> paintedChildren = <int>[];
await tester.pumpWidget(
@ -1282,9 +1298,10 @@ void main() {
expect(controller.selectedItem, 0);
});
testWidgets('onSelectedItemChanged and controller are in sync', (WidgetTester tester) async {
testWidgetsWithLeakTracking('onSelectedItemChanged and controller are in sync', (WidgetTester tester) async {
final List<int> selectedItems = <int>[];
final FixedExtentScrollController controller = FixedExtentScrollController(initialItem: 10);
addTearDown(controller.dispose);
await tester.pumpWidget(
Directionality(
@ -1334,8 +1351,7 @@ void main() {
await tester.drag(find.byType(ListWheelScrollView), const Offset(0.0, -500.0));
await tester.pump();
final FixedExtentScrollController controller1 =
FixedExtentScrollController(initialItem: 30);
final FixedExtentScrollController controller1 = FixedExtentScrollController(initialItem: 30);
addTearDown(controller1.dispose);
// Attaching first controller.
@ -1360,8 +1376,7 @@ void main() {
expect(controller1.selectedItem, 50);
expect(controller1.position.pixels, 5000.0);
final FixedExtentScrollController controller2 =
FixedExtentScrollController(initialItem: 33);
final FixedExtentScrollController controller2 = FixedExtentScrollController(initialItem: 33);
addTearDown(controller2.dispose);
// Attaching the second controller.
@ -1407,8 +1422,7 @@ void main() {
});
testWidgetsWithLeakTracking('controller can be reused', (WidgetTester tester) async {
final FixedExtentScrollController controller =
FixedExtentScrollController(initialItem: 3);
final FixedExtentScrollController controller = FixedExtentScrollController(initialItem: 3);
addTearDown(controller.dispose);
await tester.pumpWidget(
@ -1458,8 +1472,9 @@ void main() {
});
group('physics', () {
testWidgets('fling velocities too low snaps back to the same item', (WidgetTester tester) async {
testWidgetsWithLeakTracking('fling velocities too low snaps back to the same item', (WidgetTester tester) async {
final FixedExtentScrollController controller = FixedExtentScrollController(initialItem: 40);
addTearDown(controller.dispose);
final List<double> scrolledPositions = <double>[];
await tester.pumpWidget(
@ -1508,8 +1523,9 @@ void main() {
expect(scrolledPositions.last, moreOrLessEquals(40 * 1000.0, epsilon: 0.2));
});
testWidgets('high fling velocities lands exactly on items', (WidgetTester tester) async {
testWidgetsWithLeakTracking('high fling velocities lands exactly on items', (WidgetTester tester) async {
final FixedExtentScrollController controller = FixedExtentScrollController(initialItem: 40);
addTearDown(controller.dispose);
final List<double> scrolledPositions = <double>[];
await tester.pumpWidget(
@ -1561,9 +1577,11 @@ void main() {
}, variant: const TargetPlatformVariant(<TargetPlatform>{ TargetPlatform.iOS, TargetPlatform.macOS }));
});
testWidgets('ListWheelScrollView getOffsetToReveal', (WidgetTester tester) async {
testWidgetsWithLeakTracking('ListWheelScrollView getOffsetToReveal', (WidgetTester tester) async {
List<Widget> outerChildren;
final List<Widget> innerChildren = List<Widget>.generate(10, (int index) => Container());
final ScrollController controller = ScrollController(initialScrollOffset: 300.0);
addTearDown(controller.dispose);
await tester.pumpWidget(
Directionality(
@ -1573,7 +1591,7 @@ void main() {
height: 500.0,
width: 300.0,
child: ListWheelScrollView(
controller: ScrollController(initialScrollOffset: 300.0),
controller: controller,
itemExtent: 100.0,
children: outerChildren = List<Widget>.generate(10, (int i) {
return Center(
@ -1629,7 +1647,10 @@ void main() {
expect(revealed.rect, const Rect.fromLTWH(165.0, 265.0, 10.0, 10.0));
});
testWidgets('will not assert on getOffsetToReveal Axis', (WidgetTester tester) async {
testWidgetsWithLeakTracking('will not assert on getOffsetToReveal Axis', (WidgetTester tester) async {
final ScrollController controller = ScrollController(initialScrollOffset: 300.0);
addTearDown(controller.dispose);
await tester.pumpWidget(
Directionality(
textDirection: TextDirection.ltr,
@ -1638,7 +1659,7 @@ void main() {
height: 500.0,
width: 300.0,
child: ListWheelScrollView(
controller: ScrollController(initialScrollOffset: 300.0),
controller: controller,
itemExtent: 100.0,
children: List<Widget>.generate(10, (int i) {
return Center(
@ -1660,10 +1681,11 @@ void main() {
viewport.getOffsetToReveal(target, 0.0, axis: Axis.horizontal);
});
testWidgets('ListWheelScrollView showOnScreen', (WidgetTester tester) async {
testWidgetsWithLeakTracking('ListWheelScrollView showOnScreen', (WidgetTester tester) async {
List<Widget> outerChildren;
final List<Widget> innerChildren = List<Widget>.generate(10, (int index) => Container());
ScrollController controller;
final ScrollController controller = ScrollController(initialScrollOffset: 300.0);
addTearDown(controller.dispose);
await tester.pumpWidget(
Directionality(
@ -1673,7 +1695,7 @@ void main() {
height: 500.0,
width: 300.0,
child: ListWheelScrollView(
controller: controller = ScrollController(initialScrollOffset: 300.0),
controller: controller,
itemExtent: 100.0,
children:
outerChildren = List<Widget>.generate(10, (int i) {
@ -1716,8 +1738,9 @@ void main() {
});
group('gestures', () {
testWidgets('ListWheelScrollView allows taps for on its children', (WidgetTester tester) async {
testWidgetsWithLeakTracking('ListWheelScrollView allows taps for on its children', (WidgetTester tester) async {
final FixedExtentScrollController controller = FixedExtentScrollController(initialItem: 10);
addTearDown(controller.dispose);
final List<int> children = List<int>.generate(100, (int index) => index);
final List<int> paintedChildren = <int>[];
final Set<int> tappedChildren = <int>{};
@ -1758,8 +1781,9 @@ void main() {
expect(tappedChildren, paintedChildren);
});
testWidgets('ListWheelScrollView allows for horizontal drags on its children', (WidgetTester tester) async {
testWidgetsWithLeakTracking('ListWheelScrollView allows for horizontal drags on its children', (WidgetTester tester) async {
final PageController pageController = PageController();
addTearDown(pageController.dispose);
await tester.pumpWidget(
Directionality(
@ -1785,10 +1809,11 @@ void main() {
expect(pageController.page, 1.0);
});
testWidgets('ListWheelScrollView does not crash and does not allow taps on children that were laid out, but not painted', (WidgetTester tester) async {
testWidgetsWithLeakTracking('ListWheelScrollView does not crash and does not allow taps on children that were laid out, but not painted', (WidgetTester tester) async {
// Regression test for https://github.com/flutter/flutter/issues/126491
final FixedExtentScrollController controller = FixedExtentScrollController();
addTearDown(controller.dispose);
final List<int> children = List<int>.generate(100, (int index) => index);
final List<int> paintedChildren = <int>[];
final Set<int> tappedChildren = <int>{};
@ -1845,7 +1870,7 @@ void main() {
});
});
testWidgets('ListWheelScrollView creates only one opacity layer for all children', (WidgetTester tester) async {
testWidgetsWithLeakTracking('ListWheelScrollView creates only one opacity layer for all children', (WidgetTester tester) async {
await tester.pumpWidget(
ListWheelScrollView(
overAndUnderCenterOpacity: 0.5,

View file

@ -121,7 +121,7 @@ class SlideInOutPageRoute<T> extends PageRouteBuilder<T> {
}
void main() {
testWidgets('Can navigator navigate to and from a stateful widget', (WidgetTester tester) async {
testWidgetsWithLeakTracking('Can navigator navigate to and from a stateful widget', (WidgetTester tester) async {
final Map<String, WidgetBuilder> routes = <String, WidgetBuilder>{
'/': (BuildContext context) => const FirstWidget(), // X
'/second': (BuildContext context) => const SecondWidget(), // Y
@ -171,7 +171,7 @@ void main() {
expect(find.text('Y', skipOffstage: false), findsNothing);
});
testWidgets('Navigator.of fails gracefully when not found in context', (WidgetTester tester) async {
testWidgetsWithLeakTracking('Navigator.of fails gracefully when not found in context', (WidgetTester tester) async {
const Key targetKey = Key('foo');
dynamic exception;
final Widget widget = ThirdWidget(
@ -186,7 +186,7 @@ void main() {
expect('$exception', startsWith('Navigator operation requested with a context'));
});
testWidgets('Navigator can push Route created through page class as Pageless route', (WidgetTester tester) async {
testWidgetsWithLeakTracking('Navigator can push Route created through page class as Pageless route', (WidgetTester tester) async {
final GlobalKey<NavigatorState> nav = GlobalKey<NavigatorState>();
await tester.pumpWidget(
MaterialApp(
@ -206,7 +206,7 @@ void main() {
expect(find.text('home'), findsOneWidget);
});
testWidgets('Navigator can set clip behavior', (WidgetTester tester) async {
testWidgetsWithLeakTracking('Navigator can set clip behavior', (WidgetTester tester) async {
const MaterialPage<void> page = MaterialPage<void>(child: Text('page'));
await tester.pumpWidget(
MediaQuery(
@ -239,7 +239,7 @@ void main() {
expect(tester.widget<Overlay>(find.byType(Overlay)).clipBehavior, Clip.none);
});
testWidgets('Zero transition page-based route correctly notifies observers when it is popped', (WidgetTester tester) async {
testWidgetsWithLeakTracking('Zero transition page-based route correctly notifies observers when it is popped', (WidgetTester tester) async {
final List<Page<void>> pages = <Page<void>>[
const ZeroTransitionPage(name: 'Page 1'),
const ZeroTransitionPage(name: 'Page 2'),
@ -279,7 +279,7 @@ void main() {
expect(observations[0].previous, 'Page 1');
});
testWidgets('Navigator.of rootNavigator finds root Navigator', (WidgetTester tester) async {
testWidgetsWithLeakTracking('Navigator.of rootNavigator finds root Navigator', (WidgetTester tester) async {
await tester.pumpWidget(MaterialApp(
home: Material(
child: Column(
@ -347,7 +347,7 @@ void main() {
expect(tester.getTopLeft(find.text('Dialog')).dy, 0.0);
});
testWidgets('Gestures between push and build are ignored', (WidgetTester tester) async {
testWidgetsWithLeakTracking('Gestures between push and build are ignored', (WidgetTester tester) async {
final List<String> log = <String>[];
final Map<String, WidgetBuilder> routes = <String, WidgetBuilder>{
'/': (BuildContext context) {
@ -377,7 +377,7 @@ void main() {
expect(log, equals(<String>['left']));
});
testWidgets('pushnamed can handle Object as type', (WidgetTester tester) async {
testWidgetsWithLeakTracking('pushnamed can handle Object as type', (WidgetTester tester) async {
final GlobalKey<NavigatorState> nav = GlobalKey<NavigatorState>();
final Map<String, WidgetBuilder> routes = <String, WidgetBuilder>{
'/': (BuildContext context) => const Text('/'),
@ -397,7 +397,7 @@ void main() {
expect(find.text('/second'), findsOneWidget);
});
testWidgets('Pending gestures are rejected', (WidgetTester tester) async {
testWidgetsWithLeakTracking('Pending gestures are rejected', (WidgetTester tester) async {
final List<String> log = <String>[];
final Map<String, WidgetBuilder> routes = <String, WidgetBuilder>{
'/': (BuildContext context) {
@ -428,7 +428,7 @@ void main() {
expect(log, equals(<String>['left']));
});
testWidgets('popAndPushNamed', (WidgetTester tester) async {
testWidgetsWithLeakTracking('popAndPushNamed', (WidgetTester tester) async {
final Map<String, WidgetBuilder> routes = <String, WidgetBuilder>{
'/' : (BuildContext context) => OnTapPage(id: '/', onTap: () { Navigator.pushNamed(context, '/A'); }),
'/A': (BuildContext context) => OnTapPage(id: 'A', onTap: () { Navigator.popAndPushNamed(context, '/B'); }),
@ -455,7 +455,7 @@ void main() {
expect(find.text('B'), findsOneWidget);
});
testWidgets('popAndPushNamed with explicit void type parameter', (WidgetTester tester) async {
testWidgetsWithLeakTracking('popAndPushNamed with explicit void type parameter', (WidgetTester tester) async {
final Map<String, WidgetBuilder> routes = <String, WidgetBuilder>{
'/' : (BuildContext context) => OnTapPage(id: '/', onTap: () { Navigator.pushNamed<void>(context, '/A'); }),
'/A': (BuildContext context) => OnTapPage(id: 'A', onTap: () { Navigator.popAndPushNamed<void, void>(context, '/B'); }),
@ -482,7 +482,7 @@ void main() {
expect(find.text('B'), findsOneWidget);
});
testWidgets('Push and pop should trigger the observers', (WidgetTester tester) async {
testWidgetsWithLeakTracking('Push and pop should trigger the observers', (WidgetTester tester) async {
final Map<String, WidgetBuilder> routes = <String, WidgetBuilder>{
'/' : (BuildContext context) => OnTapPage(id: '/', onTap: () { Navigator.pushNamed(context, '/A'); }),
'/A': (BuildContext context) => OnTapPage(id: 'A', onTap: () { Navigator.pop(context); }),
@ -542,7 +542,7 @@ void main() {
expect(isPopped, isTrue);
});
testWidgets('Add and remove an observer should work', (WidgetTester tester) async {
testWidgetsWithLeakTracking('Add and remove an observer should work', (WidgetTester tester) async {
final Map<String, WidgetBuilder> routes = <String, WidgetBuilder>{
'/' : (BuildContext context) => OnTapPage(id: '/', onTap: () { Navigator.pushNamed(context, '/A'); }),
'/A': (BuildContext context) => OnTapPage(id: 'A', onTap: () { Navigator.pop(context); }),
@ -589,7 +589,7 @@ void main() {
expect(isPopped, isFalse);
});
testWidgets('initial route trigger observer in the right order', (WidgetTester tester) async {
testWidgetsWithLeakTracking('initial route trigger observer in the right order', (WidgetTester tester) async {
final Map<String, WidgetBuilder> routes = <String, WidgetBuilder>{
'/' : (BuildContext context) => const Text('/'),
'/A': (BuildContext context) => const Text('A'),
@ -663,7 +663,7 @@ void main() {
MemoryAllocations.instance.removeListener(listener);
});
testWidgets('Route didAdd and dispose in same frame work', (WidgetTester tester) async {
testWidgetsWithLeakTracking('Route didAdd and dispose in same frame work', (WidgetTester tester) async {
// Regression Test for https://github.com/flutter/flutter/issues/61346.
Widget buildNavigator() {
return Navigator(
@ -676,6 +676,8 @@ void main() {
);
}
final TabController controller = TabController(length: 3, vsync: tester);
addTearDown(controller.dispose);
await tester.pumpWidget(
TestDependencies(
child: TabBarView(
@ -694,7 +696,7 @@ void main() {
await tester.pumpAndSettle();
});
testWidgets('Page-based route pop before push finishes', (WidgetTester tester) async {
testWidgetsWithLeakTracking('Page-based route pop before push finishes', (WidgetTester tester) async {
List<Page<void>> pages = <Page<void>>[const MaterialPage<void>(child: Text('Page 1'))];
final GlobalKey<NavigatorState> navigator = GlobalKey<NavigatorState>();
Widget buildNavigator() {
@ -730,7 +732,7 @@ void main() {
expect(find.text('Page 1'), findsOneWidget);
});
testWidgets('Pages update does update overlay correctly', (WidgetTester tester) async {
testWidgetsWithLeakTracking('Pages update does update overlay correctly', (WidgetTester tester) async {
// Regression Test for https://github.com/flutter/flutter/issues/64941.
List<Page<void>> pages = const <Page<void>>[
MaterialPage<void>(
@ -779,7 +781,7 @@ void main() {
expect(find.text('page 0'), findsNothing);
});
testWidgets('replaceNamed replaces', (WidgetTester tester) async {
testWidgetsWithLeakTracking('replaceNamed replaces', (WidgetTester tester) async {
final Map<String, WidgetBuilder> routes = <String, WidgetBuilder>{
'/' : (BuildContext context) => OnTapPage(id: '/', onTap: () { Navigator.pushReplacementNamed(context, '/A'); }),
'/A': (BuildContext context) => OnTapPage(id: 'A', onTap: () { Navigator.pushReplacementNamed(context, '/B'); }),
@ -801,7 +803,7 @@ void main() {
expect(find.text('B'), findsOneWidget);
});
testWidgets('pushReplacement sets secondaryAnimation after transition, with history change during transition', (WidgetTester tester) async {
testWidgetsWithLeakTracking('pushReplacement sets secondaryAnimation after transition, with history change during transition', (WidgetTester tester) async {
final Map<String, SlideInOutPageRoute<dynamic>> routes = <String, SlideInOutPageRoute<dynamic>>{};
final Map<String, WidgetBuilder> builders = <String, WidgetBuilder>{
'/' : (BuildContext context) => OnTapPage(
@ -858,7 +860,7 @@ void main() {
expect(routes['/A']!.secondaryAnimation!.value, equals(routes['/C']!.animation!.value));
});
testWidgets('new route removed from navigator history during pushReplacement transition', (WidgetTester tester) async {
testWidgetsWithLeakTracking('new route removed from navigator history during pushReplacement transition', (WidgetTester tester) async {
final Map<String, SlideInOutPageRoute<dynamic>> routes = <String, SlideInOutPageRoute<dynamic>>{};
final Map<String, WidgetBuilder> builders = <String, WidgetBuilder>{
'/' : (BuildContext context) => OnTapPage(
@ -904,7 +906,7 @@ void main() {
expect(routes['/']!.animation!.value, equals(1.0));
});
testWidgets('pushReplacement triggers secondaryAnimation', (WidgetTester tester) async {
testWidgetsWithLeakTracking('pushReplacement triggers secondaryAnimation', (WidgetTester tester) async {
final Map<String, WidgetBuilder> routes = <String, WidgetBuilder>{
'/' : (BuildContext context) => OnTapPage(
id: '/',
@ -953,7 +955,7 @@ void main() {
expect(aOffset.dx, lessThan(aOffsetOriginal.dx));
});
testWidgets('pushReplacement correctly reports didReplace to the observer', (WidgetTester tester) async {
testWidgetsWithLeakTracking('pushReplacement correctly reports didReplace to the observer', (WidgetTester tester) async {
// Regression test for https://github.com/flutter/flutter/issues/56892.
final Map<String, WidgetBuilder> routes = <String, WidgetBuilder>{
'/' : (BuildContext context) => const OnTapPage(
@ -1021,7 +1023,7 @@ void main() {
expect(find.text('C'), isOnstage);
});
testWidgets('Able to pop all routes', (WidgetTester tester) async {
testWidgetsWithLeakTracking('Able to pop all routes', (WidgetTester tester) async {
final Map<String, WidgetBuilder> routes = <String, WidgetBuilder>{
'/' : (BuildContext context) => const OnTapPage(
id: '/',
@ -1048,7 +1050,7 @@ void main() {
expect(tester.takeException(), isNull);
});
testWidgets('pushAndRemoveUntil triggers secondaryAnimation', (WidgetTester tester) async {
testWidgetsWithLeakTracking('pushAndRemoveUntil triggers secondaryAnimation', (WidgetTester tester) async {
final Map<String, WidgetBuilder> routes = <String, WidgetBuilder>{
'/' : (BuildContext context) => OnTapPage(
id: '/',
@ -1102,7 +1104,7 @@ void main() {
expect(find.text('B'), isOnstage);
});
testWidgets('pushAndRemoveUntil does not remove routes below the first route that pass the predicate', (WidgetTester tester) async {
testWidgetsWithLeakTracking('pushAndRemoveUntil does not remove routes below the first route that pass the predicate', (WidgetTester tester) async {
// Regression https://github.com/flutter/flutter/issues/56688
final GlobalKey<NavigatorState> navigator = GlobalKey<NavigatorState>();
final Map<String, WidgetBuilder> routes = <String, WidgetBuilder>{
@ -1138,7 +1140,7 @@ void main() {
expect(find.text('home'), isOnstage);
});
testWidgets('replaceNamed returned value', (WidgetTester tester) async {
testWidgetsWithLeakTracking('replaceNamed returned value', (WidgetTester tester) async {
late Future<String?> value;
final Map<String, WidgetBuilder> routes = <String, WidgetBuilder>{
@ -1187,7 +1189,7 @@ void main() {
expect(replaceNamedValue, 'B');
});
testWidgets('removeRoute', (WidgetTester tester) async {
testWidgetsWithLeakTracking('removeRoute', (WidgetTester tester) async {
final Map<String, WidgetBuilder> pageBuilders = <String, WidgetBuilder>{
'/' : (BuildContext context) => OnTapPage(id: '/', onTap: () { Navigator.pushNamed(context, '/A'); }),
'/A': (BuildContext context) => OnTapPage(id: 'A', onTap: () { Navigator.pushNamed(context, '/B'); }),
@ -1273,7 +1275,7 @@ void main() {
expect(previousRoute, routes['/']);
});
testWidgets('remove a route whose value is awaited', (WidgetTester tester) async {
testWidgetsWithLeakTracking('remove a route whose value is awaited', (WidgetTester tester) async {
late Future<String?> pageValue;
final Map<String, WidgetBuilder> pageBuilders = <String, WidgetBuilder>{
'/': (BuildContext context) => OnTapPage(id: '/', onTap: () { pageValue = Navigator.pushNamed(context, '/A'); }),
@ -1301,7 +1303,7 @@ void main() {
navigator.removeRoute(routes['/A']!); // stack becomes /, pageValue will not complete
});
testWidgets('replacing route can be observed', (WidgetTester tester) async {
testWidgetsWithLeakTracking('replacing route can be observed', (WidgetTester tester) async {
final GlobalKey<NavigatorState> key = GlobalKey<NavigatorState>();
final List<String> log = <String>[];
final TestObserver observer = TestObserver()
@ -1371,7 +1373,7 @@ void main() {
expect(log, <String>['pushed / (previous is <none>)', 'pushed B (previous is /)', 'pushed C (previous is B)', 'replaced B with D']);
});
testWidgets('didStartUserGesture observable', (WidgetTester tester) async {
testWidgetsWithLeakTracking('didStartUserGesture observable', (WidgetTester tester) async {
final Map<String, WidgetBuilder> routes = <String, WidgetBuilder>{
'/' : (BuildContext context) => OnTapPage(id: '/', onTap: () { Navigator.pushNamed(context, '/A'); }),
'/A': (BuildContext context) => OnTapPage(id: 'A', onTap: () { Navigator.pop(context); }),
@ -1402,7 +1404,7 @@ void main() {
expect(observedPreviousRoute.settings.name, '/');
});
testWidgets('ModalRoute.of sets up a route to rebuild if its state changes', (WidgetTester tester) async {
testWidgetsWithLeakTracking('ModalRoute.of sets up a route to rebuild if its state changes', (WidgetTester tester) async {
final GlobalKey<NavigatorState> key = GlobalKey<NavigatorState>();
final List<String> log = <String>[];
late Route<void> routeB;
@ -1469,7 +1471,7 @@ void main() {
expect(log, <String>['building B', 'building C', 'found C', 'building D']);
});
testWidgets("Routes don't rebuild just because their animations ended", (WidgetTester tester) async {
testWidgetsWithLeakTracking("Routes don't rebuild just because their animations ended", (WidgetTester tester) async {
final GlobalKey<NavigatorState> key = GlobalKey<NavigatorState>();
final List<String> log = <String>[];
Route<dynamic>? nextRoute = PageRouteBuilder<int>(
@ -1512,7 +1514,7 @@ void main() {
expect(log, <String>['building page 1 - false', 'building page 2 - false', 'building page 3 - false']);
});
testWidgets('route semantics', (WidgetTester tester) async {
testWidgetsWithLeakTracking('route semantics', (WidgetTester tester) async {
final SemanticsTester semantics = SemanticsTester(tester);
final Map<String, WidgetBuilder> routes = <String, WidgetBuilder>{
'/': (BuildContext context) => OnTapPage(id: '1', onTap: () { Navigator.pushNamed(context, '/A'); }),
@ -1569,7 +1571,7 @@ void main() {
semantics.dispose();
});
testWidgets('arguments for named routes on Navigator', (WidgetTester tester) async {
testWidgetsWithLeakTracking('arguments for named routes on Navigator', (WidgetTester tester) async {
late GlobalKey currentRouteKey;
final List<Object?> arguments = <Object?>[];
@ -1643,7 +1645,7 @@ void main() {
arguments.clear();
});
testWidgets('arguments for named routes on NavigatorState', (WidgetTester tester) async {
testWidgetsWithLeakTracking('arguments for named routes on NavigatorState', (WidgetTester tester) async {
final GlobalKey<NavigatorState> navigatorKey = GlobalKey<NavigatorState>();
final List<Object?> arguments = <Object?>[];
@ -1714,7 +1716,7 @@ void main() {
arguments.clear();
});
testWidgets('Initial route can have gaps', (WidgetTester tester) async {
testWidgetsWithLeakTracking('Initial route can have gaps', (WidgetTester tester) async {
final GlobalKey<NavigatorState> keyNav = GlobalKey<NavigatorState>();
const Key keyRoot = Key('Root');
const Key keyA = Key('A');
@ -1745,7 +1747,7 @@ void main() {
expect(find.byKey(keyABC, skipOffstage: false), findsNothing);
});
testWidgets('The full initial route has to be matched', (WidgetTester tester) async {
testWidgetsWithLeakTracking('The full initial route has to be matched', (WidgetTester tester) async {
final GlobalKey<NavigatorState> keyNav = GlobalKey<NavigatorState>();
const Key keyRoot = Key('Root');
const Key keyA = Key('A');
@ -1775,7 +1777,7 @@ void main() {
expect(find.byKey(keyAB), findsNothing);
});
testWidgets("Popping immediately after pushing doesn't crash", (WidgetTester tester) async {
testWidgetsWithLeakTracking("Popping immediately after pushing doesn't crash", (WidgetTester tester) async {
// Added this test to protect against regression of https://github.com/flutter/flutter/issues/45539
final Map<String, WidgetBuilder> routes = <String, WidgetBuilder>{
'/' : (BuildContext context) => OnTapPage(id: '/', onTap: () {
@ -1824,7 +1826,7 @@ void main() {
});
group('error control test', () {
testWidgets('onUnknownRoute null and onGenerateRoute returns null', (WidgetTester tester) async {
testWidgetsWithLeakTracking('onUnknownRoute null and onGenerateRoute returns null', (WidgetTester tester) async {
final GlobalKey<NavigatorState> navigatorKey = GlobalKey<NavigatorState>();
await tester.pumpWidget(Navigator(
key: navigatorKey,
@ -1850,7 +1852,7 @@ void main() {
);
});
testWidgets('onUnknownRoute null and onGenerateRoute returns null', (WidgetTester tester) async {
testWidgetsWithLeakTracking('onUnknownRoute null and onGenerateRoute returns null', (WidgetTester tester) async {
final GlobalKey<NavigatorState> navigatorKey = GlobalKey<NavigatorState>();
await tester.pumpWidget(Navigator(
key: navigatorKey,
@ -1877,7 +1879,7 @@ void main() {
});
});
testWidgets('OverlayEntry of topmost initial route is marked as opaque', (WidgetTester tester) async {
testWidgetsWithLeakTracking('OverlayEntry of topmost initial route is marked as opaque', (WidgetTester tester) async {
// Regression test for https://github.com/flutter/flutter/issues/38038.
final Key root = UniqueKey();
@ -1901,7 +1903,7 @@ void main() {
expect(find.byKey(topmost), findsOneWidget);
});
testWidgets('OverlayEntry of topmost route is set to opaque after Push', (WidgetTester tester) async {
testWidgetsWithLeakTracking('OverlayEntry of topmost route is set to opaque after Push', (WidgetTester tester) async {
// Regression test for https://github.com/flutter/flutter/issues/38038.
final GlobalKey<NavigatorState> navigator = GlobalKey<NavigatorState>();
@ -1928,7 +1930,7 @@ void main() {
expect(find.byKey(const ValueKey<String>('/A')), findsOneWidget);
});
testWidgets('OverlayEntry of topmost route is set to opaque after Replace', (WidgetTester tester) async {
testWidgetsWithLeakTracking('OverlayEntry of topmost route is set to opaque after Replace', (WidgetTester tester) async {
// Regression test for https://github.com/flutter/flutter/issues/38038.
final GlobalKey<NavigatorState> navigator = GlobalKey<NavigatorState>();
@ -1973,7 +1975,7 @@ void main() {
expect(find.byKey(const ValueKey<String>('/C')), findsOneWidget);
});
testWidgets('Pushing opaque Route does not rebuild routes below', (WidgetTester tester) async {
testWidgetsWithLeakTracking('Pushing opaque Route does not rebuild routes below', (WidgetTester tester) async {
// Regression test for https://github.com/flutter/flutter/issues/45797.
final GlobalKey<NavigatorState> navigator = GlobalKey<NavigatorState>();
@ -2007,7 +2009,7 @@ void main() {
expect(tester.state<StatefulTestState>(find.byKey(topRoute)).rebuildCount, 1);
});
testWidgets('initial routes below opaque route are offstage', (WidgetTester tester) async {
testWidgetsWithLeakTracking('initial routes below opaque route are offstage', (WidgetTester tester) async {
final GlobalKey<NavigatorState> testKey = GlobalKey<NavigatorState>();
await tester.pumpWidget(
TestDependencies(
@ -2048,7 +2050,7 @@ void main() {
expect(find.text('+/a/b+'), findsNothing);
});
testWidgets('Can provide custom onGenerateInitialRoutes', (WidgetTester tester) async {
testWidgetsWithLeakTracking('Can provide custom onGenerateInitialRoutes', (WidgetTester tester) async {
bool onGenerateInitialRoutesCalled = false;
final GlobalKey<NavigatorState> testKey = GlobalKey<NavigatorState>();
await tester.pumpWidget(
@ -2081,7 +2083,7 @@ void main() {
expect(find.text('World'), findsNothing);
});
testWidgets('Navigator.of able to handle input context is a navigator context', (WidgetTester tester) async {
testWidgetsWithLeakTracking('Navigator.of able to handle input context is a navigator context', (WidgetTester tester) async {
final GlobalKey<NavigatorState> testKey = GlobalKey<NavigatorState>();
await tester.pumpWidget(
MaterialApp(
@ -2094,7 +2096,7 @@ void main() {
expect(state, testKey.currentState);
});
testWidgets('Navigator.of able to handle input context is a navigator context - root navigator', (WidgetTester tester) async {
testWidgetsWithLeakTracking('Navigator.of able to handle input context is a navigator context - root navigator', (WidgetTester tester) async {
final GlobalKey<NavigatorState> root = GlobalKey<NavigatorState>();
final GlobalKey<NavigatorState> sub = GlobalKey<NavigatorState>();
await tester.pumpWidget(
@ -2116,7 +2118,7 @@ void main() {
expect(state, root.currentState);
});
testWidgets('Navigator.maybeOf throws when there is no navigator', (WidgetTester tester) async {
testWidgetsWithLeakTracking('Navigator.maybeOf throws when there is no navigator', (WidgetTester tester) async {
final GlobalKey<NavigatorState> testKey = GlobalKey<NavigatorState>();
await tester.pumpWidget(SizedBox(key: testKey));
@ -2125,7 +2127,7 @@ void main() {
}, throwsFlutterError);
});
testWidgets('Navigator.maybeOf works when there is no navigator', (WidgetTester tester) async {
testWidgetsWithLeakTracking('Navigator.maybeOf works when there is no navigator', (WidgetTester tester) async {
final GlobalKey<NavigatorState> testKey = GlobalKey<NavigatorState>();
await tester.pumpWidget(SizedBox(key: testKey));
@ -2133,7 +2135,7 @@ void main() {
expect(state, isNull);
});
testWidgets('Navigator.maybeOf able to handle input context is a navigator context', (WidgetTester tester) async {
testWidgetsWithLeakTracking('Navigator.maybeOf able to handle input context is a navigator context', (WidgetTester tester) async {
final GlobalKey<NavigatorState> testKey = GlobalKey<NavigatorState>();
await tester.pumpWidget(
MaterialApp(
@ -2147,7 +2149,7 @@ void main() {
expect(state, testKey.currentState);
});
testWidgets('Navigator.maybeOf able to handle input context is a navigator context - root navigator', (WidgetTester tester) async {
testWidgetsWithLeakTracking('Navigator.maybeOf able to handle input context is a navigator context - root navigator', (WidgetTester tester) async {
final GlobalKey<NavigatorState> root = GlobalKey<NavigatorState>();
final GlobalKey<NavigatorState> sub = GlobalKey<NavigatorState>();
await tester.pumpWidget(
@ -2170,7 +2172,7 @@ void main() {
expect(state, root.currentState);
});
testWidgets('pushAndRemove until animates the push', (WidgetTester tester) async {
testWidgetsWithLeakTracking('pushAndRemove until animates the push', (WidgetTester tester) async {
// Regression test for https://github.com/flutter/flutter/issues/25080.
const Duration kFourTenthsOfTheTransitionDuration = Duration(milliseconds: 120);
@ -2252,7 +2254,7 @@ void main() {
expect(find.text('Route: 4', skipOffstage: false), findsNothing);
});
testWidgets('Wrapping TickerMode can turn off ticking in routes', (WidgetTester tester) async {
testWidgetsWithLeakTracking('Wrapping TickerMode can turn off ticking in routes', (WidgetTester tester) async {
int tickCount = 0;
Widget widgetUnderTest({required bool enabled}) {
return TickerMode(
@ -2345,7 +2347,7 @@ void main() {
expect(popNextOfFirst, secondRoute);
});
testWidgets('hero controller scope works', (WidgetTester tester) async {
testWidgetsWithLeakTracking('hero controller scope works', (WidgetTester tester) async {
final GlobalKey<NavigatorState> top = GlobalKey<NavigatorState>();
final GlobalKey<NavigatorState> sub = GlobalKey<NavigatorState>();
@ -2415,7 +2417,7 @@ void main() {
expect(observations[1].previous, 'top1');
});
testWidgets('hero controller can correctly transfer subscription - replacing navigator', (WidgetTester tester) async {
testWidgetsWithLeakTracking('hero controller can correctly transfer subscription - replacing navigator', (WidgetTester tester) async {
final GlobalKey<NavigatorState> key1 = GlobalKey<NavigatorState>();
final GlobalKey<NavigatorState> key2 = GlobalKey<NavigatorState>();
@ -2484,7 +2486,7 @@ void main() {
expect(observations[0].previous, 'navigator2');
});
testWidgets('hero controller can correctly transfer subscription - swapping navigator', (WidgetTester tester) async {
testWidgetsWithLeakTracking('hero controller can correctly transfer subscription - swapping navigator', (WidgetTester tester) async {
final GlobalKey<NavigatorState> key1 = GlobalKey<NavigatorState>();
final GlobalKey<NavigatorState> key2 = GlobalKey<NavigatorState>();
@ -2624,7 +2626,7 @@ void main() {
expect(observations2[1].previous, 'navigator1');
});
testWidgets('hero controller subscribes to multiple navigators does throw', (WidgetTester tester) async {
testWidgetsWithLeakTracking('hero controller subscribes to multiple navigators does throw', (WidgetTester tester) async {
final HeroControllerSpy spy = HeroControllerSpy();
await tester.pumpWidget(
HeroControllerScope(
@ -2662,7 +2664,7 @@ void main() {
expect(tester.takeException(), isAssertionError);
});
testWidgets('hero controller throws has correct error message', (WidgetTester tester) async {
testWidgetsWithLeakTracking('hero controller throws has correct error message', (WidgetTester tester) async {
final HeroControllerSpy spy = HeroControllerSpy();
await tester.pumpWidget(
HeroControllerScope(
@ -2746,7 +2748,7 @@ void main() {
);
}
testWidgets('can initialize with pages list', (WidgetTester tester) async {
testWidgetsWithLeakTracking('can initialize with pages list', (WidgetTester tester) async {
final GlobalKey<NavigatorState> navigator = GlobalKey<NavigatorState>();
final List<TestPage> myPages = <TestPage>[
const TestPage(key: ValueKey<String>('1'), name:'initial'),
@ -2784,7 +2786,7 @@ void main() {
expect(find.text('initial'), findsOneWidget);
});
testWidgets('can handle duplicate page key if update before transition finishes', (WidgetTester tester) async {
testWidgetsWithLeakTracking('can handle duplicate page key if update before transition finishes', (WidgetTester tester) async {
// Regression test for https://github.com/flutter/flutter/issues/97363.
final GlobalKey<NavigatorState> navigator = GlobalKey<NavigatorState>();
final List<TestPage> myPages1 = <TestPage>[
@ -2840,7 +2842,7 @@ void main() {
expect(tester.takeException(), isNull);
});
testWidgets('throw if onPopPage callback is not provided', (WidgetTester tester) async {
testWidgetsWithLeakTracking('throw if onPopPage callback is not provided', (WidgetTester tester) async {
final List<TestPage> myPages = <TestPage>[
const TestPage(key: ValueKey<String>('1'), name:'initial'),
const TestPage(key: ValueKey<String>('2'), name:'second'),
@ -2910,7 +2912,7 @@ void main() {
);
});
testWidgets('Can pop route with local history entries using page api', (WidgetTester tester) async {
testWidgetsWithLeakTracking('Can pop route with local history entries using page api', (WidgetTester tester) async {
List<Page<void>> myPages = const <Page<void>>[
MaterialPage<void>(child: Text('page1')),
MaterialPage<void>(child: Text('page2')),
@ -2961,7 +2963,7 @@ void main() {
expect(entryRemoved, isTrue);
});
testWidgets('ModalRoute must comply with willHandlePopInternally when there is a PopScope', (WidgetTester tester) async {
testWidgetsWithLeakTracking('ModalRoute must comply with willHandlePopInternally when there is a PopScope', (WidgetTester tester) async {
const List<Page<void>> myPages = <Page<void>>[
MaterialPage<void>(child: Text('page1')),
MaterialPage<void>(
@ -2994,7 +2996,7 @@ void main() {
expect(route.didPop(null), true);
});
testWidgets('can push and pop pages using page api', (WidgetTester tester) async {
testWidgetsWithLeakTracking('can push and pop pages using page api', (WidgetTester tester) async {
late Animation<double> secondaryAnimationOfRouteOne;
late Animation<double> primaryAnimationOfRouteOne;
late Animation<double> secondaryAnimationOfRouteTwo;
@ -3143,7 +3145,7 @@ void main() {
expect(primaryAnimationOfRouteThree.status, AnimationStatus.dismissed);
});
testWidgets('can modify routes history and secondary animation still works', (WidgetTester tester) async {
testWidgetsWithLeakTracking('can modify routes history and secondary animation still works', (WidgetTester tester) async {
final GlobalKey<NavigatorState> navigator = GlobalKey<NavigatorState>();
late Animation<double> secondaryAnimationOfRouteOne;
late Animation<double> primaryAnimationOfRouteOne;
@ -3254,7 +3256,7 @@ void main() {
expect(primaryAnimationOfRouteOne.status, AnimationStatus.dismissed);
});
testWidgets('Pop no animation page does not crash', (WidgetTester tester) async {
testWidgetsWithLeakTracking('Pop no animation page does not crash', (WidgetTester tester) async {
// Regression Test for https://github.com/flutter/flutter/issues/86604.
Widget buildNavigator(bool secondPage) {
return TestDependencies(
@ -3279,7 +3281,7 @@ void main() {
expect(find.text('page1'), findsOneWidget);
});
testWidgets('can work with pageless route', (WidgetTester tester) async {
testWidgetsWithLeakTracking('can work with pageless route', (WidgetTester tester) async {
final GlobalKey<NavigatorState> navigator = GlobalKey<NavigatorState>();
List<TestPage> myPages = <TestPage>[
const TestPage(key: ValueKey<String>('1'), name:'initial'),
@ -3427,7 +3429,7 @@ void main() {
expect(myPages.length, 1);
});
testWidgets('complex case 1', (WidgetTester tester) async {
testWidgetsWithLeakTracking('complex case 1', (WidgetTester tester) async {
final GlobalKey<NavigatorState> navigator = GlobalKey<NavigatorState>();
List<TestPage> myPages = <TestPage>[
const TestPage(key: ValueKey<String>('1'), name: 'initial'),
@ -3564,7 +3566,7 @@ void main() {
});
//Regression test for https://github.com/flutter/flutter/issues/115887
testWidgets('Complex case 2', (WidgetTester tester) async {
testWidgetsWithLeakTracking('Complex case 2', (WidgetTester tester) async {
final GlobalKey<NavigatorState> navigator = GlobalKey<NavigatorState>();
List<TestPage> myPages = <TestPage>[
const TestPage(key: ValueKey<String>('1'), name:'initial'),
@ -3628,7 +3630,7 @@ void main() {
expect(find.text('second-pageless1'), findsNothing);
});
testWidgets('complex case 1 - with always remove transition delegate', (WidgetTester tester) async {
testWidgetsWithLeakTracking('complex case 1 - with always remove transition delegate', (WidgetTester tester) async {
final GlobalKey<NavigatorState> navigator = GlobalKey<NavigatorState>();
final AlwaysRemoveTransitionDelegate transitionDelegate = AlwaysRemoveTransitionDelegate();
List<TestPage> myPages = <TestPage>[
@ -3773,7 +3775,7 @@ void main() {
expect(find.text('forth'), findsOneWidget);
});
testWidgets('can repush a page that was previously popped before it has finished popping', (WidgetTester tester) async {
testWidgetsWithLeakTracking('can repush a page that was previously popped before it has finished popping', (WidgetTester tester) async {
final GlobalKey<NavigatorState> navigator = GlobalKey<NavigatorState>();
List<Page<dynamic>> myPages = <TestPage>[
const TestPage(key: ValueKey<String>('1'), name: 'initial'),
@ -3825,7 +3827,7 @@ void main() {
expect(find.text('second'), findsOneWidget);
});
testWidgets('can update pages before a route has finished popping', (WidgetTester tester) async {
testWidgetsWithLeakTracking('can update pages before a route has finished popping', (WidgetTester tester) async {
final GlobalKey<NavigatorState> navigator = GlobalKey<NavigatorState>();
List<Page<dynamic>> myPages = <TestPage>[
const TestPage(key: ValueKey<String>('1'), name: 'initial'),
@ -3876,7 +3878,7 @@ void main() {
expect(find.text('initial'), findsOneWidget);
});
testWidgets('can update pages before a pageless route has finished popping', (WidgetTester tester) async {
testWidgetsWithLeakTracking('can update pages before a pageless route has finished popping', (WidgetTester tester) async {
// Regression test for https://github.com/flutter/flutter/issues/68162.
final GlobalKey<NavigatorState> navigator = GlobalKey<NavigatorState>();
List<Page<dynamic>> myPages = <TestPage>[
@ -3923,7 +3925,7 @@ void main() {
expect(find.text('initial'), findsOneWidget);
});
testWidgets('pages remove and add trigger observer in the right order', (WidgetTester tester) async {
testWidgetsWithLeakTracking('pages remove and add trigger observer in the right order', (WidgetTester tester) async {
final GlobalKey<NavigatorState> navigator = GlobalKey<NavigatorState>();
List<TestPage> myPages = <TestPage>[
const TestPage(key: ValueKey<String>('1'), name:'first'),
@ -4016,7 +4018,7 @@ void main() {
});
});
testWidgets('Can reuse NavigatorObserver in rebuilt tree', (WidgetTester tester) async {
testWidgetsWithLeakTracking('Can reuse NavigatorObserver in rebuilt tree', (WidgetTester tester) async {
final NavigatorObserver observer = NavigatorObserver();
Widget build([Key? key]) {
return TestDependencies(
@ -4051,7 +4053,7 @@ void main() {
expect(observer.navigator, tester.state<NavigatorState>(find.byType(Navigator)));
});
testWidgets('Navigator requests focus if requestFocus is true', (WidgetTester tester) async {
testWidgetsWithLeakTracking('Navigator requests focus if requestFocus is true', (WidgetTester tester) async {
final GlobalKey navigatorKey = GlobalKey();
final GlobalKey innerKey = GlobalKey();
final Map<String, Widget> routes = <String, Widget>{
@ -4060,6 +4062,7 @@ void main() {
};
late final NavigatorState navigator = navigatorKey.currentState! as NavigatorState;
final FocusScopeNode focusNode = FocusScopeNode();
addTearDown(focusNode.dispose);
await tester.pumpWidget(Column(
children: <Widget>[
@ -4126,7 +4129,7 @@ void main() {
expect(focusNode.hasFocus, true);
});
testWidgets('Navigator does not request focus if requestFocus is false', (WidgetTester tester) async {
testWidgetsWithLeakTracking('Navigator does not request focus if requestFocus is false', (WidgetTester tester) async {
final GlobalKey navigatorKey = GlobalKey();
final GlobalKey innerKey = GlobalKey();
final Map<String, Widget> routes = <String, Widget>{
@ -4136,6 +4139,7 @@ void main() {
late final NavigatorState navigator =
navigatorKey.currentState! as NavigatorState;
final FocusScopeNode focusNode = FocusScopeNode();
addTearDown(focusNode.dispose);
await tester.pumpWidget(Column(
children: <Widget>[
@ -4192,7 +4196,7 @@ void main() {
expect(focusNode.hasFocus, true);
});
testWidgets('class implementing NavigatorObserver can be used without problems', (WidgetTester tester) async {
testWidgetsWithLeakTracking('class implementing NavigatorObserver can be used without problems', (WidgetTester tester) async {
final _MockNavigatorObserver observer = _MockNavigatorObserver();
Widget build([Key? key]) {
return TestDependencies(
@ -4224,7 +4228,7 @@ void main() {
observer._checkInvocations(<Symbol>[#navigator, #navigator]);
});
testWidgets("Navigator doesn't override FocusTraversalPolicy of ancestors", (WidgetTester tester) async {
testWidgetsWithLeakTracking("Navigator doesn't override FocusTraversalPolicy of ancestors", (WidgetTester tester) async {
FocusTraversalPolicy? policy;
await tester.pumpWidget(
TestDependencies(
@ -4247,7 +4251,7 @@ void main() {
expect(policy, isA<WidgetOrderTraversalPolicy>());
});
testWidgets('Navigator inserts ReadingOrderTraversalPolicy if no ancestor has a policy', (WidgetTester tester) async {
testWidgetsWithLeakTracking('Navigator inserts ReadingOrderTraversalPolicy if no ancestor has a policy', (WidgetTester tester) async {
FocusTraversalPolicy? policy;
await tester.pumpWidget(
TestDependencies(
@ -4302,7 +4306,7 @@ void main() {
.setMockMethodCallHandler(SystemChannels.platform, null);
});
testWidgets('a single route is already defaulted to false', (WidgetTester tester) async {
testWidgetsWithLeakTracking('a single route is already defaulted to false', (WidgetTester tester) async {
await tester.pumpWidget(
const MaterialApp(
home: Scaffold(
@ -4317,7 +4321,7 @@ void main() {
skip: isBrowser, // [intended] only non-web Android supports predictive back.
);
testWidgets('navigating around a single Navigator with .pop', (WidgetTester tester) async {
testWidgetsWithLeakTracking('navigating around a single Navigator with .pop', (WidgetTester tester) async {
await tester.pumpWidget(
MaterialApp(
initialRoute: '/',
@ -4394,7 +4398,7 @@ void main() {
skip: isBrowser, // [intended] only non-web Android supports predictive back.
);
testWidgets('navigating around a single Navigator with system back', (WidgetTester tester) async {
testWidgetsWithLeakTracking('navigating around a single Navigator with system back', (WidgetTester tester) async {
await tester.pumpWidget(
MaterialApp(
initialRoute: '/',
@ -4471,7 +4475,7 @@ void main() {
skip: isBrowser, // [intended] only non-web Android supports predictive back.
);
testWidgets('a single Navigator with a PopScope that defaults to enabled', (WidgetTester tester) async {
testWidgetsWithLeakTracking('a single Navigator with a PopScope that defaults to enabled', (WidgetTester tester) async {
bool canPop = true;
late StateSetter setState;
await tester.pumpWidget(
@ -4511,7 +4515,7 @@ void main() {
skip: isBrowser, // [intended] only non-web Android supports predictive back.
);
testWidgets('a single Navigator with a PopScope that defaults to disabled', (WidgetTester tester) async {
testWidgetsWithLeakTracking('a single Navigator with a PopScope that defaults to disabled', (WidgetTester tester) async {
bool canPop = false;
late StateSetter setState;
await tester.pumpWidget(
@ -4553,7 +4557,7 @@ void main() {
// Test both system back gestures and Navigator.pop.
for (final _BackType backType in _BackType.values) {
testWidgets('navigating around nested Navigators', (WidgetTester tester) async {
testWidgetsWithLeakTracking('navigating around nested Navigators', (WidgetTester tester) async {
final GlobalKey<NavigatorState> nav = GlobalKey<NavigatorState>();
final GlobalKey<NavigatorState> nestedNav = GlobalKey<NavigatorState>();
Future<void> goBack() async {
@ -4653,7 +4657,7 @@ void main() {
);
}
testWidgets('nested Navigators with a nested PopScope', (WidgetTester tester) async {
testWidgetsWithLeakTracking('nested Navigators with a nested PopScope', (WidgetTester tester) async {
bool canPop = true;
late StateSetter setState;
await tester.pumpWidget(
@ -4780,7 +4784,7 @@ void main() {
);
group('Navigator page API', () {
testWidgets('starting with one route as usual', (WidgetTester tester) async {
testWidgetsWithLeakTracking('starting with one route as usual', (WidgetTester tester) async {
late StateSetter builderSetState;
final List<_Page> pages = <_Page>[_Page.home];
bool canPop() => pages.length <= 1;
@ -4898,7 +4902,7 @@ void main() {
skip: isBrowser, // [intended] only non-web Android supports predictive back.
);
testWidgets('starting with existing route history', (WidgetTester tester) async {
testWidgetsWithLeakTracking('starting with existing route history', (WidgetTester tester) async {
final List<_Page> pages = <_Page>[_Page.home, _Page.one];
bool canPop() => pages.length <= 1;

View file

@ -5,20 +5,26 @@
import 'package:flutter/foundation.dart';
import 'package:flutter/widgets.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:leak_tracker_flutter_testing/leak_tracker_flutter_testing.dart';
import 'semantics_tester.dart';
void main() {
testWidgets('OverflowEntries context contains Overlay', (WidgetTester tester) async {
testWidgetsWithLeakTracking('OverflowEntries context contains Overlay', (WidgetTester tester) async {
final GlobalKey overlayKey = GlobalKey();
bool didBuild = false;
late final OverlayEntry overlayEntry1;
addTearDown(() => overlayEntry1..remove()..dispose());
late final OverlayEntry overlayEntry2;
addTearDown(() => overlayEntry2..remove()..dispose());
await tester.pumpWidget(
Directionality(
textDirection: TextDirection.ltr,
child: Overlay(
key: overlayKey,
initialEntries: <OverlayEntry>[
OverlayEntry(
overlayEntry1 = OverlayEntry(
builder: (BuildContext context) {
didBuild = true;
final Overlay overlay = context.findAncestorWidgetOfExactType<Overlay>()!;
@ -26,7 +32,7 @@ void main() {
return Container();
},
),
OverlayEntry(
overlayEntry2 = OverlayEntry(
builder: (BuildContext context) => Container(),
),
],
@ -80,25 +86,32 @@ void main() {
);
});
testWidgets('Offstage overlay', (WidgetTester tester) async {
testWidgetsWithLeakTracking('Offstage overlay', (WidgetTester tester) async {
final GlobalKey overlayKey = GlobalKey();
late final OverlayEntry overlayEntry1;
addTearDown(() => overlayEntry1..remove()..dispose());
late final OverlayEntry overlayEntry2;
addTearDown(() => overlayEntry2..remove()..dispose());
late final OverlayEntry overlayEntry3;
addTearDown(() => overlayEntry3..remove()..dispose());
await tester.pumpWidget(
Directionality(
textDirection: TextDirection.ltr,
child: Overlay(
key: overlayKey,
initialEntries: <OverlayEntry>[
OverlayEntry(
overlayEntry1 = OverlayEntry(
opaque: true,
maintainState: true,
builder: (BuildContext context) => Container(),
),
OverlayEntry(
overlayEntry2 = OverlayEntry(
opaque: true,
maintainState: true,
builder: (BuildContext context) => Container(),
),
OverlayEntry(
overlayEntry3 = OverlayEntry(
opaque: true,
maintainState: true,
builder: (BuildContext context) => Container(),
@ -163,16 +176,19 @@ void main() {
);
});
testWidgets('insert top', (WidgetTester tester) async {
testWidgetsWithLeakTracking('insert top', (WidgetTester tester) async {
final GlobalKey overlayKey = GlobalKey();
final List<String> buildOrder = <String>[];
late final OverlayEntry baseEntry;
addTearDown(() => baseEntry..remove()..dispose());
await tester.pumpWidget(
Directionality(
textDirection: TextDirection.ltr,
child: Overlay(
key: overlayKey,
initialEntries: <OverlayEntry>[
OverlayEntry(
baseEntry = OverlayEntry(
builder: (BuildContext context) {
buildOrder.add('Base');
return Container();
@ -187,8 +203,10 @@ void main() {
buildOrder.clear();
final OverlayState overlay = overlayKey.currentState! as OverlayState;
late final OverlayEntry newEntry;
addTearDown(() => newEntry..remove()..dispose());
overlay.insert(
OverlayEntry(
newEntry = OverlayEntry(
builder: (BuildContext context) {
buildOrder.add('New');
return Container();
@ -200,17 +218,19 @@ void main() {
expect(buildOrder, <String>['Base', 'New']);
});
testWidgets('insert below', (WidgetTester tester) async {
testWidgetsWithLeakTracking('insert below', (WidgetTester tester) async {
final GlobalKey overlayKey = GlobalKey();
OverlayEntry base;
late final OverlayEntry baseEntry;
addTearDown(() => baseEntry..remove()..dispose());
final List<String> buildOrder = <String>[];
await tester.pumpWidget(
Directionality(
textDirection: TextDirection.ltr,
child: Overlay(
key: overlayKey,
initialEntries: <OverlayEntry>[
base = OverlayEntry(
baseEntry = OverlayEntry(
builder: (BuildContext context) {
buildOrder.add('Base');
return Container();
@ -225,37 +245,43 @@ void main() {
buildOrder.clear();
final OverlayState overlay = overlayKey.currentState! as OverlayState;
late final OverlayEntry newEntry;
addTearDown(() => newEntry..remove()..dispose());
overlay.insert(
OverlayEntry(
newEntry = OverlayEntry(
builder: (BuildContext context) {
buildOrder.add('New');
return Container();
},
),
below: base,
below: baseEntry,
);
await tester.pump();
expect(buildOrder, <String>['New', 'Base']);
});
testWidgets('insert above', (WidgetTester tester) async {
testWidgetsWithLeakTracking('insert above', (WidgetTester tester) async {
final GlobalKey overlayKey = GlobalKey();
OverlayEntry base;
late final OverlayEntry baseEntry;
addTearDown(() => baseEntry..remove()..dispose());
late final OverlayEntry topEntry;
addTearDown(() => topEntry..remove()..dispose());
final List<String> buildOrder = <String>[];
await tester.pumpWidget(
Directionality(
textDirection: TextDirection.ltr,
child: Overlay(
key: overlayKey,
initialEntries: <OverlayEntry>[
base = OverlayEntry(
baseEntry = OverlayEntry(
builder: (BuildContext context) {
buildOrder.add('Base');
return Container();
},
),
OverlayEntry(
topEntry = OverlayEntry(
builder: (BuildContext context) {
buildOrder.add('Top');
return Container();
@ -270,30 +296,35 @@ void main() {
buildOrder.clear();
final OverlayState overlay = overlayKey.currentState! as OverlayState;
late final OverlayEntry newEntry;
addTearDown(() => newEntry..remove()..dispose());
overlay.insert(
OverlayEntry(
newEntry = OverlayEntry(
builder: (BuildContext context) {
buildOrder.add('New');
return Container();
},
),
above: base,
above: baseEntry,
);
await tester.pump();
expect(buildOrder, <String>['Base', 'New', 'Top']);
});
testWidgets('insertAll top', (WidgetTester tester) async {
testWidgetsWithLeakTracking('insertAll top', (WidgetTester tester) async {
final GlobalKey overlayKey = GlobalKey();
final List<String> buildOrder = <String>[];
late final OverlayEntry baseEntry;
addTearDown(() => baseEntry..remove()..dispose());
await tester.pumpWidget(
Directionality(
textDirection: TextDirection.ltr,
child: Overlay(
key: overlayKey,
initialEntries: <OverlayEntry>[
OverlayEntry(
baseEntry = OverlayEntry(
builder: (BuildContext context) {
buildOrder.add('Base');
return Container();
@ -320,6 +351,11 @@ void main() {
},
),
];
addTearDown(() {
for (final OverlayEntry entry in entries) {
entry..remove()..dispose();
}
});
buildOrder.clear();
final OverlayState overlay = overlayKey.currentState! as OverlayState;
@ -329,17 +365,19 @@ void main() {
expect(buildOrder, <String>['Base', 'New1', 'New2']);
});
testWidgets('insertAll below', (WidgetTester tester) async {
testWidgetsWithLeakTracking('insertAll below', (WidgetTester tester) async {
final GlobalKey overlayKey = GlobalKey();
OverlayEntry base;
late final OverlayEntry baseEntry;
addTearDown(() => baseEntry..remove()..dispose());
final List<String> buildOrder = <String>[];
await tester.pumpWidget(
Directionality(
textDirection: TextDirection.ltr,
child: Overlay(
key: overlayKey,
initialEntries: <OverlayEntry>[
base = OverlayEntry(
baseEntry = OverlayEntry(
builder: (BuildContext context) {
buildOrder.add('Base');
return Container();
@ -366,32 +404,41 @@ void main() {
},
),
];
addTearDown(() {
for (final OverlayEntry entry in entries) {
entry..remove()..dispose();
}
});
buildOrder.clear();
final OverlayState overlay = overlayKey.currentState! as OverlayState;
overlay.insertAll(entries, below: base);
overlay.insertAll(entries, below: baseEntry);
await tester.pump();
expect(buildOrder, <String>['New1', 'New2','Base']);
});
testWidgets('insertAll above', (WidgetTester tester) async {
testWidgetsWithLeakTracking('insertAll above', (WidgetTester tester) async {
final GlobalKey overlayKey = GlobalKey();
final List<String> buildOrder = <String>[];
OverlayEntry base;
late final OverlayEntry baseEntry;
addTearDown(() => baseEntry..remove()..dispose());
late final OverlayEntry topEntry;
addTearDown(() => topEntry..remove()..dispose());
await tester.pumpWidget(
Directionality(
textDirection: TextDirection.ltr,
child: Overlay(
key: overlayKey,
initialEntries: <OverlayEntry>[
base = OverlayEntry(
baseEntry = OverlayEntry(
builder: (BuildContext context) {
buildOrder.add('Base');
return Container();
},
),
OverlayEntry(
topEntry = OverlayEntry(
builder: (BuildContext context) {
buildOrder.add('Top');
return Container();
@ -418,16 +465,21 @@ void main() {
},
),
];
addTearDown(() {
for (final OverlayEntry entry in entries) {
entry..remove()..dispose();
}
});
buildOrder.clear();
final OverlayState overlay = overlayKey.currentState! as OverlayState;
overlay.insertAll(entries, above: base);
overlay.insertAll(entries, above: baseEntry);
await tester.pump();
expect(buildOrder, <String>['Base', 'New1', 'New2', 'Top']);
});
testWidgets('rearrange', (WidgetTester tester) async {
testWidgetsWithLeakTracking('rearrange', (WidgetTester tester) async {
final GlobalKey overlayKey = GlobalKey();
final List<int> buildOrder = <int>[];
final List<OverlayEntry> initialEntries = <OverlayEntry>[
@ -456,6 +508,11 @@ void main() {
},
),
];
addTearDown(() {
for (final OverlayEntry entry in initialEntries) {
entry..remove()..dispose();
}
});
await tester.pumpWidget(
Directionality(
@ -469,9 +526,11 @@ void main() {
expect(buildOrder, <int>[0, 1, 2, 3]);
late final OverlayEntry newEntry;
addTearDown(() => newEntry..remove()..dispose());
final List<OverlayEntry> rearranged = <OverlayEntry>[
initialEntries[3],
OverlayEntry(
newEntry = OverlayEntry(
builder: (BuildContext context) {
buildOrder.add(4);
return Container();
@ -490,9 +549,10 @@ void main() {
expect(buildOrder, <int>[3, 4, 2, 0, 1]);
});
testWidgets('rearrange above', (WidgetTester tester) async {
testWidgetsWithLeakTracking('rearrange above', (WidgetTester tester) async {
final GlobalKey overlayKey = GlobalKey();
final List<int> buildOrder = <int>[];
final List<OverlayEntry> initialEntries = <OverlayEntry>[
OverlayEntry(
builder: (BuildContext context) {
@ -519,6 +579,11 @@ void main() {
},
),
];
addTearDown(() {
for (final OverlayEntry entry in initialEntries) {
entry..remove()..dispose();
}
});
await tester.pumpWidget(
Directionality(
@ -532,9 +597,11 @@ void main() {
expect(buildOrder, <int>[0, 1, 2, 3]);
late final OverlayEntry newEntry;
addTearDown(() => newEntry..remove()..dispose());
final List<OverlayEntry> rearranged = <OverlayEntry>[
initialEntries[3],
OverlayEntry(
newEntry = OverlayEntry(
builder: (BuildContext context) {
buildOrder.add(4);
return Container();
@ -553,7 +620,7 @@ void main() {
expect(buildOrder, <int>[3, 4, 2, 1, 0]);
});
testWidgets('rearrange below', (WidgetTester tester) async {
testWidgetsWithLeakTracking('rearrange below', (WidgetTester tester) async {
final GlobalKey overlayKey = GlobalKey();
final List<int> buildOrder = <int>[];
final List<OverlayEntry> initialEntries = <OverlayEntry>[
@ -582,6 +649,11 @@ void main() {
},
),
];
addTearDown(() {
for (final OverlayEntry entry in initialEntries) {
entry..remove()..dispose();
}
});
await tester.pumpWidget(
Directionality(
@ -595,9 +667,11 @@ void main() {
expect(buildOrder, <int>[0, 1, 2, 3]);
late final OverlayEntry newEntry;
addTearDown(() => newEntry..remove()..dispose());
final List<OverlayEntry> rearranged = <OverlayEntry>[
initialEntries[3],
OverlayEntry(
newEntry = OverlayEntry(
builder: (BuildContext context) {
buildOrder.add(4);
return Container();
@ -694,7 +768,7 @@ void main() {
await tester.pump();
});
testWidgets('OverlayState.of() throws when called if an Overlay does not exist', (WidgetTester tester) async {
testWidgetsWithLeakTracking('OverlayState.of() throws when called if an Overlay does not exist', (WidgetTester tester) async {
await tester.pumpWidget(
Directionality(
textDirection: TextDirection.ltr,
@ -740,9 +814,11 @@ void main() {
);
});
testWidgets("OverlayState.maybeOf() works when an Overlay does and doesn't exist", (WidgetTester tester) async {
testWidgetsWithLeakTracking("OverlayState.maybeOf() works when an Overlay does and doesn't exist", (WidgetTester tester) async {
final GlobalKey overlayKey = GlobalKey();
OverlayState? foundState;
late final OverlayEntry baseEntry;
addTearDown(() => baseEntry..remove()..dispose());
await tester.pumpWidget(
Directionality(
@ -750,7 +826,7 @@ void main() {
child: Overlay(
key: overlayKey,
initialEntries: <OverlayEntry>[
OverlayEntry(
baseEntry = OverlayEntry(
builder: (BuildContext context) {
foundState = Overlay.maybeOf(context);
return Container();
@ -781,7 +857,7 @@ void main() {
expect(foundState, isNull);
});
testWidgets('OverlayEntry.opaque can be changed when OverlayEntry is not part of an Overlay (yet)', (WidgetTester tester) async {
testWidgetsWithLeakTracking('OverlayEntry.opaque can be changed when OverlayEntry is not part of an Overlay (yet)', (WidgetTester tester) async {
final GlobalKey<OverlayState> overlayKey = GlobalKey<OverlayState>();
final Key root = UniqueKey();
final Key top = UniqueKey();
@ -790,6 +866,7 @@ void main() {
return Container(key: root);
},
);
addTearDown(() => rootEntry..remove()..dispose());
await tester.pumpWidget(
Directionality(
@ -810,6 +887,7 @@ void main() {
return Container(key: top);
},
);
addTearDown(() => newEntry..remove()..dispose());
expect(newEntry.opaque, isFalse);
newEntry.opaque = true; // Does neither trigger an assert nor throw.
expect(newEntry.opaque, isTrue);
@ -822,7 +900,7 @@ void main() {
expect(find.byKey(top), findsOneWidget);
});
testWidgets('OverlayEntries do not rebuild when opaqueness changes', (WidgetTester tester) async {
testWidgetsWithLeakTracking('OverlayEntries do not rebuild when opaqueness changes', (WidgetTester tester) async {
// Regression test for https://github.com/flutter/flutter/issues/45797.
final GlobalKey<OverlayState> overlayKey = GlobalKey<OverlayState>();
@ -839,18 +917,21 @@ void main() {
return bottomWidget;
},
);
addTearDown(() => bottomEntry..remove()..dispose());
final OverlayEntry middleEntry = OverlayEntry(
maintainState: true,
builder: (BuildContext context) {
return middleWidget;
},
);
addTearDown(() => middleEntry..remove()..dispose());
final OverlayEntry topEntry = OverlayEntry(
maintainState: true,
builder: (BuildContext context) {
return topWidget;
},
);
addTearDown(() => topEntry..remove()..dispose());
await tester.pumpWidget(
Directionality(
@ -881,7 +962,7 @@ void main() {
expect(tester.state<StatefulTestState>(find.byKey(top)).rebuildCount, 1);
});
testWidgets('OverlayEntries do not rebuild when opaque entry is added', (WidgetTester tester) async {
testWidgetsWithLeakTracking('OverlayEntries do not rebuild when opaque entry is added', (WidgetTester tester) async {
// Regression test for https://github.com/flutter/flutter/issues/45797.
final GlobalKey<OverlayState> overlayKey = GlobalKey<OverlayState>();
@ -898,6 +979,7 @@ void main() {
return bottomWidget;
},
);
addTearDown(() => bottomEntry..remove()..dispose());
final OverlayEntry middleEntry = OverlayEntry(
opaque: true,
maintainState: true,
@ -905,12 +987,14 @@ void main() {
return middleWidget;
},
);
addTearDown(() => middleEntry..remove()..dispose());
final OverlayEntry topEntry = OverlayEntry(
maintainState: true,
builder: (BuildContext context) {
return topWidget;
},
);
addTearDown(() => topEntry..remove()..dispose());
await tester.pumpWidget(
Directionality(
@ -941,16 +1025,19 @@ void main() {
expect(tester.state<StatefulTestState>(find.byKey(top)).rebuildCount, 1);
});
testWidgets('entries below opaque entries are ignored for hit testing', (WidgetTester tester) async {
testWidgetsWithLeakTracking('entries below opaque entries are ignored for hit testing', (WidgetTester tester) async {
final GlobalKey<OverlayState> overlayKey = GlobalKey<OverlayState>();
int bottomTapCount = 0;
late final OverlayEntry baseEntry;
addTearDown(() => baseEntry..remove()..dispose());
await tester.pumpWidget(
Directionality(
textDirection: TextDirection.ltr,
child: Overlay(
key: overlayKey,
initialEntries: <OverlayEntry>[
OverlayEntry(
baseEntry = OverlayEntry(
maintainState: true,
builder: (BuildContext context) {
return GestureDetector(
@ -969,13 +1056,17 @@ void main() {
await tester.tap(find.byKey(overlayKey), warnIfMissed: false); // gesture detector is translucent; no hit is registered between it and the render view
expect(bottomTapCount, 1);
overlayKey.currentState!.insert(OverlayEntry(
maintainState: true,
opaque: true,
builder: (BuildContext context) {
return Container();
},
));
late final OverlayEntry newEntry1;
addTearDown(() => newEntry1..remove()..dispose());
overlayKey.currentState!.insert(
newEntry1 = OverlayEntry(
maintainState: true,
opaque: true,
builder: (BuildContext context) {
return Container();
},
),
);
await tester.pump();
// Bottom is offstage and does not receive tap events.
@ -985,17 +1076,21 @@ void main() {
expect(bottomTapCount, 1);
int topTapCount = 0;
overlayKey.currentState!.insert(OverlayEntry(
maintainState: true,
opaque: true,
builder: (BuildContext context) {
return GestureDetector(
onTap: () {
topTapCount++;
},
);
},
));
late final OverlayEntry newEntry2;
addTearDown(() => newEntry2..remove()..dispose());
overlayKey.currentState!.insert(
newEntry2 = OverlayEntry(
maintainState: true,
opaque: true,
builder: (BuildContext context) {
return GestureDetector(
onTap: () {
topTapCount++;
},
);
},
),
);
await tester.pump();
expect(topTapCount, 0);
@ -1004,22 +1099,27 @@ void main() {
expect(bottomTapCount, 1);
});
testWidgets('Semantics of entries below opaque entries are ignored', (WidgetTester tester) async {
testWidgetsWithLeakTracking('Semantics of entries below opaque entries are ignored', (WidgetTester tester) async {
final SemanticsTester semantics = SemanticsTester(tester);
final GlobalKey<OverlayState> overlayKey = GlobalKey<OverlayState>();
late final OverlayEntry bottomEntry;
addTearDown(() => bottomEntry..remove()..dispose());
late final OverlayEntry topEntry;
addTearDown(() => topEntry..remove()..dispose());
await tester.pumpWidget(
Directionality(
textDirection: TextDirection.ltr,
child: Overlay(
key: overlayKey,
initialEntries: <OverlayEntry>[
OverlayEntry(
bottomEntry = OverlayEntry(
maintainState: true,
builder: (BuildContext context) {
return const Text('bottom');
},
),
OverlayEntry(
topEntry = OverlayEntry(
maintainState: true,
opaque: true,
builder: (BuildContext context) {
@ -1039,13 +1139,16 @@ void main() {
semantics.dispose();
});
testWidgets('Can use Positioned within OverlayEntry', (WidgetTester tester) async {
testWidgetsWithLeakTracking('Can use Positioned within OverlayEntry', (WidgetTester tester) async {
late final OverlayEntry baseEntry;
addTearDown(() => baseEntry..remove()..dispose());
await tester.pumpWidget(
Directionality(
textDirection: TextDirection.ltr,
child: Overlay(
initialEntries: <OverlayEntry>[
OverlayEntry(
baseEntry = OverlayEntry(
builder: (BuildContext context) {
return const Positioned(
left: 145,
@ -1117,13 +1220,16 @@ void main() {
}
});
testWidgets('Overlay always applies clip', (WidgetTester tester) async {
testWidgetsWithLeakTracking('Overlay always applies clip', (WidgetTester tester) async {
late final OverlayEntry baseEntry;
addTearDown(() => baseEntry..remove()..dispose());
await tester.pumpWidget(
Directionality(
textDirection: TextDirection.ltr,
child: Overlay(
initialEntries: <OverlayEntry>[
OverlayEntry(
baseEntry = OverlayEntry(
builder: (BuildContext context) => Positioned(left: 10, right: 10, child: Container()),
),
],
@ -1139,7 +1245,7 @@ void main() {
);
});
testWidgets('OverlayEntry throws if inserted to an invalid Overlay', (WidgetTester tester) async {
testWidgetsWithLeakTracking('OverlayEntry throws if inserted to an invalid Overlay', (WidgetTester tester) async {
await tester.pumpWidget(
const Directionality(
textDirection: TextDirection.ltr,
@ -1148,6 +1254,7 @@ void main() {
);
final OverlayState overlay = tester.state(find.byType(Overlay));
final OverlayEntry entry = OverlayEntry(builder: (BuildContext context) => const SizedBox());
addTearDown(() => entry..remove()..dispose());
expect(
() => overlay.insert(entry),
returnsNormally,
@ -1212,13 +1319,14 @@ void main() {
child: Overlay(key: overlayKey),
);
testWidgets('mounted state can be listened', (WidgetTester tester) async {
testWidgetsWithLeakTracking('mounted state can be listened', (WidgetTester tester) async {
await tester.pumpWidget(emptyOverlay);
final OverlayState overlay = overlayKey.currentState! as OverlayState;
final List<bool> mountedLog = <bool>[];
final OverlayEntry entry = OverlayEntry(
builder: (BuildContext context) => Container(),
);
addTearDown(entry.dispose);
entry.addListener(() {
mountedLog.add(entry.mounted);
@ -1245,12 +1353,13 @@ void main() {
expect(mountedLog, <bool>[true, false, true, false]);
});
testWidgets('throw if disposed before removal', (WidgetTester tester) async {
testWidgetsWithLeakTracking('throw if disposed before removal', (WidgetTester tester) async {
await tester.pumpWidget(emptyOverlay);
final OverlayState overlay = overlayKey.currentState! as OverlayState;
final OverlayEntry entry = OverlayEntry(
builder: (BuildContext context) => Container(),
);
addTearDown(() => entry..remove()..dispose());
overlay.insert(entry);
Object? error;
@ -1279,7 +1388,7 @@ void main() {
expect(error, isAssertionError);
});
testWidgets('delayed dispose', (WidgetTester tester) async {
testWidgetsWithLeakTracking('delayed dispose', (WidgetTester tester) async {
await tester.pumpWidget(emptyOverlay);
final OverlayState overlay = overlayKey.currentState! as OverlayState;
final List<bool> mountedLog = <bool>[];
@ -1315,15 +1424,17 @@ void main() {
});
group('LookupBoundary', () {
testWidgets('hides Overlay from Overlay.maybeOf', (WidgetTester tester) async {
testWidgetsWithLeakTracking('hides Overlay from Overlay.maybeOf', (WidgetTester tester) async {
OverlayState? overlay;
late final OverlayEntry baseEntry;
addTearDown(() => baseEntry..remove()..dispose());
await tester.pumpWidget(
Directionality(
textDirection: TextDirection.ltr,
child: Overlay(
initialEntries: <OverlayEntry>[
OverlayEntry(
baseEntry = OverlayEntry(
builder: (BuildContext context) {
return LookupBoundary(
child: Builder(
@ -1343,13 +1454,16 @@ void main() {
expect(overlay, isNull);
});
testWidgets('hides Overlay from Overlay.of', (WidgetTester tester) async {
testWidgetsWithLeakTracking('hides Overlay from Overlay.of', (WidgetTester tester) async {
late final OverlayEntry baseEntry;
addTearDown(() => baseEntry..remove()..dispose());
await tester.pumpWidget(
Directionality(
textDirection: TextDirection.ltr,
child: Overlay(
initialEntries: <OverlayEntry>[
OverlayEntry(
baseEntry = OverlayEntry(
builder: (BuildContext context) {
return LookupBoundary(
child: Builder(
@ -1386,13 +1500,16 @@ void main() {
);
});
testWidgets('hides Overlay from debugCheckHasOverlay', (WidgetTester tester) async {
testWidgetsWithLeakTracking('hides Overlay from debugCheckHasOverlay', (WidgetTester tester) async {
late final OverlayEntry baseEntry;
addTearDown(() => baseEntry..remove()..dispose());
await tester.pumpWidget(
Directionality(
textDirection: TextDirection.ltr,
child: Overlay(
initialEntries: <OverlayEntry>[
OverlayEntry(
baseEntry = OverlayEntry(
builder: (BuildContext context) {
return LookupBoundary(
child: Builder(

View file

@ -5,9 +5,10 @@
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:leak_tracker_flutter_testing/leak_tracker_flutter_testing.dart';
void main() {
testWidgets('Router state restoration without RouteInformationProvider', (WidgetTester tester) async {
testWidgetsWithLeakTracking('Router state restoration without RouteInformationProvider', (WidgetTester tester) async {
final UniqueKey router = UniqueKey();
_TestRouterDelegate delegate() => tester.widget<Router<Object?>>(find.byKey(router)).routerDelegate as _TestRouterDelegate;
@ -39,7 +40,12 @@ void main() {
expect(find.text('Current config: /foo'), findsOneWidget);
expect(delegate().newRoutePaths, isEmpty);
expect(delegate().restoredRoutePaths, <String>['/foo', '/foo']);
});
},
leakTrackingTestConfig: const LeakTrackingTestConfig(
// TODO(ksokolovskyi): remove after fixing
// https://github.com/flutter/flutter/issues/134205
notDisposedAllowList: <String, int?> {'_RestorableRouteInformation': 2},
));
testWidgets('Router state restoration with RouteInformationProvider', (WidgetTester tester) async {
final UniqueKey router = UniqueKey();
@ -152,22 +158,37 @@ class _TestRouteInformationProvider extends RouteInformationProvider with Change
}
}
class _TestWidget extends StatelessWidget {
class _TestWidget extends StatefulWidget {
const _TestWidget({this.withInformationProvider = false, this.routerKey});
final bool withInformationProvider;
final Key? routerKey;
@override
State<_TestWidget> createState() => _TestWidgetState();
}
class _TestWidgetState extends State<_TestWidget> {
final _TestRouterDelegate _delegate = _TestRouterDelegate();
final _TestRouteInformationProvider _routeInformationProvider = _TestRouteInformationProvider();
@override
void dispose() {
_delegate.dispose();
_routeInformationProvider.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return RootRestorationScope(
restorationId: 'root',
child: Router<String>(
key: routerKey,
key: widget.routerKey,
restorationScopeId: 'router',
routerDelegate: _TestRouterDelegate(),
routerDelegate: _delegate,
routeInformationParser: _TestRouteInformationParser(),
routeInformationProvider: withInformationProvider ? _TestRouteInformationProvider() : null,
routeInformationProvider: widget.withInformationProvider ? _routeInformationProvider : null,
),
);
}

View file

@ -861,12 +861,13 @@ void main() {
expect(targetMidLeftPage1, findsOneWidget);
});
testWidgets('ensureVisible does not move TabViews', (WidgetTester tester) async {
testWidgetsWithLeakTracking('ensureVisible does not move TabViews', (WidgetTester tester) async {
final TickerProvider vsync = TestTickerProvider();
final TabController controller = TabController(
length: 3,
vsync: vsync,
);
addTearDown(controller.dispose);
await tester.pumpWidget(
Directionality(

View file

@ -13,10 +13,11 @@ import 'dart:ui' as ui;
import 'package:flutter/rendering.dart';
import 'package:flutter/widgets.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:leak_tracker_flutter_testing/leak_tracker_flutter_testing.dart';
import 'package:vector_math/vector_math_64.dart';
void main() {
testWidgets('Transform origin', (WidgetTester tester) async {
testWidgetsWithLeakTracking('Transform origin', (WidgetTester tester) async {
bool didReceiveTap = false;
await tester.pumpWidget(
Directionality(
@ -64,7 +65,7 @@ void main() {
expect(didReceiveTap, isTrue);
});
testWidgets('Transform alignment', (WidgetTester tester) async {
testWidgetsWithLeakTracking('Transform alignment', (WidgetTester tester) async {
bool didReceiveTap = false;
await tester.pumpWidget(
Directionality(
@ -112,7 +113,7 @@ void main() {
expect(didReceiveTap, isTrue);
});
testWidgets('Transform AlignmentDirectional alignment', (WidgetTester tester) async {
testWidgetsWithLeakTracking('Transform AlignmentDirectional alignment', (WidgetTester tester) async {
bool didReceiveTap = false;
Widget buildFrame(TextDirection textDirection, AlignmentGeometry alignment) {
@ -183,7 +184,7 @@ void main() {
expect(didReceiveTap, isTrue);
});
testWidgets('Transform offset + alignment', (WidgetTester tester) async {
testWidgetsWithLeakTracking('Transform offset + alignment', (WidgetTester tester) async {
bool didReceiveTap = false;
await tester.pumpWidget(
Directionality(
@ -232,7 +233,7 @@ void main() {
expect(didReceiveTap, isTrue);
});
testWidgets('Composited transform offset', (WidgetTester tester) async {
testWidgetsWithLeakTracking('Composited transform offset', (WidgetTester tester) async {
await tester.pumpWidget(
Center(
child: SizedBox(
@ -261,7 +262,7 @@ void main() {
expect(transform.getTranslation(), equals(Vector3(100.0, 75.0, 0.0)));
});
testWidgets('Transform.rotate', (WidgetTester tester) async {
testWidgetsWithLeakTracking('Transform.rotate', (WidgetTester tester) async {
await tester.pumpWidget(
Transform.rotate(
angle: math.pi / 2.0,
@ -283,7 +284,7 @@ void main() {
]);
});
testWidgets('applyPaintTransform of Transform in Padding', (WidgetTester tester) async {
testWidgetsWithLeakTracking('applyPaintTransform of Transform in Padding', (WidgetTester tester) async {
await tester.pumpWidget(
Padding(
padding: const EdgeInsets.only(
@ -301,7 +302,7 @@ void main() {
expect(tester.getTopLeft(find.byType(Placeholder)), const Offset(30.0, 20.0));
});
testWidgets('Transform.translate', (WidgetTester tester) async {
testWidgetsWithLeakTracking('Transform.translate', (WidgetTester tester) async {
await tester.pumpWidget(
Transform.translate(
offset: const Offset(100.0, 50.0),
@ -316,7 +317,7 @@ void main() {
expect(tester.getTopLeft(find.byType(Container)), const Offset(100.0, 50.0));
});
testWidgets('Transform.scale', (WidgetTester tester) async {
testWidgetsWithLeakTracking('Transform.scale', (WidgetTester tester) async {
await tester.pumpWidget(
Transform.scale(
scale: 2.0,
@ -339,7 +340,7 @@ void main() {
]);
});
testWidgets('Transform with nan value short-circuits rendering', (WidgetTester tester) async {
testWidgetsWithLeakTracking('Transform with nan value short-circuits rendering', (WidgetTester tester) async {
await tester.pumpWidget(
Transform(
transform: Matrix4.identity()
@ -351,7 +352,7 @@ void main() {
expect(tester.layers, hasLength(1));
});
testWidgets('Transform with inf value short-circuits rendering', (WidgetTester tester) async {
testWidgetsWithLeakTracking('Transform with inf value short-circuits rendering', (WidgetTester tester) async {
await tester.pumpWidget(
Transform(
transform: Matrix4.identity()
@ -363,7 +364,7 @@ void main() {
expect(tester.layers, hasLength(1));
});
testWidgets('Transform with -inf value short-circuits rendering', (WidgetTester tester) async {
testWidgetsWithLeakTracking('Transform with -inf value short-circuits rendering', (WidgetTester tester) async {
await tester.pumpWidget(
Transform(
transform: Matrix4.identity()
@ -375,7 +376,7 @@ void main() {
expect(tester.layers, hasLength(1));
});
testWidgets('Transform.rotate does not remove layers due to singular short-circuit', (WidgetTester tester) async {
testWidgetsWithLeakTracking('Transform.rotate does not remove layers due to singular short-circuit', (WidgetTester tester) async {
await tester.pumpWidget(
Transform.rotate(
angle: math.pi / 2,
@ -386,7 +387,7 @@ void main() {
expect(tester.layers, hasLength(3));
});
testWidgets('Transform.rotate creates nice rotation matrices for 0, 90, 180, 270 degrees', (WidgetTester tester) async {
testWidgetsWithLeakTracking('Transform.rotate creates nice rotation matrices for 0, 90, 180, 270 degrees', (WidgetTester tester) async {
await tester.pumpWidget(
Transform.rotate(
angle: math.pi / 2,
@ -447,7 +448,7 @@ void main() {
expect(tester.layers, hasLength(2));
});
testWidgets('Transform.scale with 0.0 does not paint child layers', (WidgetTester tester) async {
testWidgetsWithLeakTracking('Transform.scale with 0.0 does not paint child layers', (WidgetTester tester) async {
await tester.pumpWidget(
Transform.scale(
scale: 0.0,
@ -486,7 +487,7 @@ void main() {
});
testWidgets('Translated child into translated box - hit test', (WidgetTester tester) async {
testWidgetsWithLeakTracking('Translated child into translated box - hit test', (WidgetTester tester) async {
final GlobalKey key1 = GlobalKey();
bool pointerDown = false;
await tester.pumpWidget(
@ -524,7 +525,7 @@ void main() {
);
}
testWidgets(
testWidgetsWithLeakTracking(
'3D transform renders the same with or without needsCompositing',
(WidgetTester tester) async {
for (double angle = 0; angle <= math.pi/4; angle += 0.01) {
@ -532,6 +533,7 @@ void main() {
final RenderBox renderBox = tester.binding.renderView.child!;
final OffsetLayer layer = renderBox.debugLayer! as OffsetLayer;
final ui.Image imageWithCompositing = await layer.toImage(renderBox.paintBounds);
addTearDown(imageWithCompositing.dispose);
await tester.pumpWidget(RepaintBoundary(child: generateTransform(false, angle)));
await expectLater(find.byType(RepaintBoundary).first, matchesReferenceImage(imageWithCompositing));
@ -545,7 +547,7 @@ void main() {
return numbers.map<double>((String str) => double.parse(str.trim())).toList();
}
testWidgets('Transform.translate with FilterQuality produces filter layer', (WidgetTester tester) async {
testWidgetsWithLeakTracking('Transform.translate with FilterQuality produces filter layer', (WidgetTester tester) async {
await tester.pumpWidget(
Transform.translate(
offset: const Offset(25.0, 25.0),
@ -563,7 +565,7 @@ void main() {
]);
});
testWidgets('Transform.scale with FilterQuality produces filter layer', (WidgetTester tester) async {
testWidgetsWithLeakTracking('Transform.scale with FilterQuality produces filter layer', (WidgetTester tester) async {
await tester.pumpWidget(
Transform.scale(
scale: 3.14159,
@ -581,7 +583,7 @@ void main() {
]);
});
testWidgets('Transform.rotate with FilterQuality produces filter layer', (WidgetTester tester) async {
testWidgetsWithLeakTracking('Transform.rotate with FilterQuality produces filter layer', (WidgetTester tester) async {
await tester.pumpWidget(
Transform.rotate(
angle: math.pi / 4,
@ -599,7 +601,7 @@ void main() {
]);
});
testWidgets('Offset Transform.rotate with FilterQuality produces filter layer', (WidgetTester tester) async {
testWidgetsWithLeakTracking('Offset Transform.rotate with FilterQuality produces filter layer', (WidgetTester tester) async {
await tester.pumpWidget(
SizedBox(width: 400, height: 400,
child: Center(
@ -621,7 +623,7 @@ void main() {
]);
});
testWidgets('Transform layers update to match child and filterQuality', (WidgetTester tester) async {
testWidgetsWithLeakTracking('Transform layers update to match child and filterQuality', (WidgetTester tester) async {
await tester.pumpWidget(
Transform.rotate(
angle: math.pi / 4,
@ -657,7 +659,7 @@ void main() {
expect(tester.layers.whereType<ImageFilterLayer>(), hasLength(1));
});
testWidgets('Transform layers with filterQuality golden', (WidgetTester tester) async {
testWidgetsWithLeakTracking('Transform layers with filterQuality golden', (WidgetTester tester) async {
await tester.pumpWidget(
Directionality(
textDirection: TextDirection.ltr,
@ -701,7 +703,7 @@ void main() {
);
});
testWidgets("Transform.scale() does not accept all three 'scale', 'scaleX' and 'scaleY' parameters to be non-null", (WidgetTester tester) async {
testWidgetsWithLeakTracking("Transform.scale() does not accept all three 'scale', 'scaleX' and 'scaleY' parameters to be non-null", (WidgetTester tester) async {
await expectLater(() {
tester.pumpWidget(Directionality(
textDirection: TextDirection.ltr,
@ -719,7 +721,7 @@ void main() {
}, throwsAssertionError);
});
testWidgets("Transform.scale() needs at least one of 'scale', 'scaleX' and 'scaleY' to be non-null, otherwise throws AssertionError", (WidgetTester tester) async {
testWidgetsWithLeakTracking("Transform.scale() needs at least one of 'scale', 'scaleX' and 'scaleY' to be non-null, otherwise throws AssertionError", (WidgetTester tester) async {
await expectLater(() {
tester.pumpWidget(Directionality(
textDirection: TextDirection.ltr,
@ -734,7 +736,7 @@ void main() {
}, throwsAssertionError);
});
testWidgets("Transform.scale() scales widget uniformly with 'scale' parameter", (WidgetTester tester) async {
testWidgetsWithLeakTracking("Transform.scale() scales widget uniformly with 'scale' parameter", (WidgetTester tester) async {
const double scale = 1.5;
const double height = 100;
const double width = 150;
@ -760,7 +762,7 @@ void main() {
expect(tester.getBottomRight(find.byType(Container)), target.bottomRight(tester.getTopLeft(find.byType(Container))));
});
testWidgets("Transform.scale() scales widget according to 'scaleX' and 'scaleY'", (WidgetTester tester) async {
testWidgetsWithLeakTracking("Transform.scale() scales widget according to 'scaleX' and 'scaleY'", (WidgetTester tester) async {
const double scaleX = 1.5;
const double scaleY = 1.2;
const double height = 100;
@ -788,7 +790,7 @@ void main() {
expect(tester.getBottomRight(find.byType(Container)), target.bottomRight(tester.getTopLeft(find.byType(Container))));
});
testWidgets(
testWidgetsWithLeakTracking(
'Transform.flip does flip child correctly',
(WidgetTester tester) async {
const Offset topRight = Offset(60, 20);