diff --git a/packages/flutter/test/widgets/form_test.dart b/packages/flutter/test/widgets/form_test.dart index 6fa4bbd42ac..90f5fe727bf 100644 --- a/packages/flutter/test/widgets/form_test.dart +++ b/packages/flutter/test/widgets/form_test.dart @@ -6,9 +6,10 @@ import 'package:flutter/material.dart'; import 'package:flutter/rendering.dart'; import 'package:flutter/services.dart'; import 'package:flutter_test/flutter_test.dart'; +import 'package:leak_tracker_flutter_testing/leak_tracker_flutter_testing.dart'; void main() { - testWidgets('onSaved callback is called', (WidgetTester tester) async { + testWidgetsWithLeakTracking('onSaved callback is called', (WidgetTester tester) async { final GlobalKey formKey = GlobalKey(); String? fieldValue; @@ -48,7 +49,7 @@ void main() { await checkText(''); }); - testWidgets('onChanged callback is called', (WidgetTester tester) async { + testWidgetsWithLeakTracking('onChanged callback is called', (WidgetTester tester) async { String? fieldValue; Widget builder() { @@ -85,7 +86,7 @@ void main() { await checkText(''); }); - testWidgets('Validator sets the error text only when validate is called', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Validator sets the error text only when validate is called', (WidgetTester tester) async { final GlobalKey formKey = GlobalKey(); String? errorText(String? value) => '${value ?? ''}/error'; @@ -139,7 +140,7 @@ void main() { await checkErrorText(''); }); - testWidgets('Should announce error text when validate returns error', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Should announce error text when validate returns error', (WidgetTester tester) async { final GlobalKey formKey = GlobalKey(); await tester.pumpWidget( MaterialApp( @@ -178,7 +179,7 @@ void main() { }); - testWidgets('isValid returns true when a field is valid', (WidgetTester tester) async { + testWidgetsWithLeakTracking('isValid returns true when a field is valid', (WidgetTester tester) async { final GlobalKey> fieldKey1 = GlobalKey>(); final GlobalKey> fieldKey2 = GlobalKey>(); const String validString = 'Valid string'; @@ -223,7 +224,7 @@ void main() { expect(fieldKey2.currentState!.isValid, isTrue); }); - testWidgets( + testWidgetsWithLeakTracking( 'isValid returns false when the field is invalid and does not change error display', (WidgetTester tester) async { final GlobalKey> fieldKey1 = GlobalKey>(); @@ -272,7 +273,7 @@ void main() { }, ); - testWidgets('Multiple TextFormFields communicate', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Multiple TextFormFields communicate', (WidgetTester tester) async { final GlobalKey formKey = GlobalKey(); final GlobalKey> fieldKey = GlobalKey>(); // Input 2's validator depends on a input 1's value. @@ -322,7 +323,7 @@ void main() { await checkErrorText(''); }); - testWidgets('Provide initial value to input when no controller is specified', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Provide initial value to input when no controller is specified', (WidgetTester tester) async { const String initialValue = 'hello'; final GlobalKey> inputKey = GlobalKey>(); @@ -366,8 +367,9 @@ void main() { expect(editableText.widget.controller.text, equals('world')); }); - testWidgets('Controller defines initial value', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Controller defines initial value', (WidgetTester tester) async { final TextEditingController controller = TextEditingController(text: 'hello'); + addTearDown(controller.dispose); const String initialValue = 'hello'; final GlobalKey> inputKey = GlobalKey>(); @@ -413,10 +415,11 @@ void main() { expect(controller.text, equals('world')); }); - testWidgets('TextFormField resets to its initial value', (WidgetTester tester) async { + testWidgetsWithLeakTracking('TextFormField resets to its initial value', (WidgetTester tester) async { final GlobalKey formKey = GlobalKey(); final GlobalKey> inputKey = GlobalKey>(); final TextEditingController controller = TextEditingController(text: 'Plover'); + addTearDown(controller.dispose); Widget builder() { return MaterialApp( @@ -459,9 +462,11 @@ void main() { expect(controller.text, equals('Plover')); }); - testWidgets('TextEditingController updates to/from form field value', (WidgetTester tester) async { + testWidgetsWithLeakTracking('TextEditingController updates to/from form field value', (WidgetTester tester) async { final TextEditingController controller1 = TextEditingController(text: 'Foo'); + addTearDown(controller1.dispose); final TextEditingController controller2 = TextEditingController(text: 'Bar'); + addTearDown(controller2.dispose); final GlobalKey> inputKey = GlobalKey>(); TextEditingController? currentController; @@ -566,7 +571,7 @@ void main() { expect(controller2.text, equals('Xyzzy')); }); - testWidgets('No crash when a TextFormField is removed from the tree', (WidgetTester tester) async { + testWidgetsWithLeakTracking('No crash when a TextFormField is removed from the tree', (WidgetTester tester) async { final GlobalKey formKey = GlobalKey(); String? fieldValue; @@ -620,7 +625,7 @@ void main() { expect(formKey.currentState!.validate(), isTrue); }); - testWidgets('Does not auto-validate before value changes when autovalidateMode is set to onUserInteraction', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Does not auto-validate before value changes when autovalidateMode is set to onUserInteraction', (WidgetTester tester) async { late FormFieldState formFieldState; String? errorText(String? value) => '$value/error'; @@ -656,7 +661,7 @@ void main() { expect(find.text(errorText('foo')!), findsNothing); }); - testWidgets('auto-validate before value changes if autovalidateMode was set to always', (WidgetTester tester) async { + testWidgetsWithLeakTracking('auto-validate before value changes if autovalidateMode was set to always', (WidgetTester tester) async { late FormFieldState formFieldState; String? errorText(String? value) => '$value/error'; @@ -689,7 +694,7 @@ void main() { expect(formFieldState.hasError, isTrue); }); - testWidgets('Form auto-validates form fields only after one of them changes if autovalidateMode is onUserInteraction', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Form auto-validates form fields only after one of them changes if autovalidateMode is onUserInteraction', (WidgetTester tester) async { const String initialValue = 'foo'; String? errorText(String? value) => 'error/$value'; @@ -743,7 +748,7 @@ void main() { expect(find.text(errorText(initialValue)!), findsNWidgets(2)); }); - testWidgets('Form auto-validates form fields even before any have changed if autovalidateMode is set to always', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Form auto-validates form fields even before any have changed if autovalidateMode is set to always', (WidgetTester tester) async { String? errorText(String? value) => 'error/$value'; Widget builder() { @@ -773,7 +778,7 @@ void main() { expect(find.text(errorText('')!), findsOneWidget); }); - testWidgets('Form.reset() resets form fields, and auto validation will only happen on the next user interaction if autovalidateMode is onUserInteraction', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Form.reset() resets form fields, and auto validation will only happen on the next user interaction if autovalidateMode is onUserInteraction', (WidgetTester tester) async { final GlobalKey formState = GlobalKey(); String? errorText(String? value) => '$value/error'; @@ -818,7 +823,7 @@ void main() { }); // Regression test for https://github.com/flutter/flutter/issues/63753. - testWidgets('Validate form should return correct validation if the value is composing', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Validate form should return correct validation if the value is composing', (WidgetTester tester) async { final GlobalKey formKey = GlobalKey(); String? fieldValue; @@ -855,7 +860,7 @@ void main() { expect(formKey.currentState!.validate(), isFalse); }); - testWidgets('hasInteractedByUser returns false when the input has not changed', (WidgetTester tester) async { + testWidgetsWithLeakTracking('hasInteractedByUser returns false when the input has not changed', (WidgetTester tester) async { final GlobalKey> fieldKey = GlobalKey>(); final Widget widget = MaterialApp( @@ -879,7 +884,7 @@ void main() { expect(fieldKey.currentState!.hasInteractedByUser, isFalse); }); - testWidgets('hasInteractedByUser returns true after the input has changed', (WidgetTester tester) async { + testWidgetsWithLeakTracking('hasInteractedByUser returns true after the input has changed', (WidgetTester tester) async { final GlobalKey> fieldKey = GlobalKey>(); final Widget widget = MaterialApp( @@ -908,7 +913,7 @@ void main() { expect(fieldKey.currentState!.hasInteractedByUser, isTrue); }); - testWidgets('hasInteractedByUser returns false after the field is reset', (WidgetTester tester) async { + testWidgetsWithLeakTracking('hasInteractedByUser returns false after the field is reset', (WidgetTester tester) async { final GlobalKey> fieldKey = GlobalKey>(); final Widget widget = MaterialApp( diff --git a/packages/flutter/test/widgets/fractionally_sized_box_test.dart b/packages/flutter/test/widgets/fractionally_sized_box_test.dart index ddebf069199..1a9f7ef6757 100644 --- a/packages/flutter/test/widgets/fractionally_sized_box_test.dart +++ b/packages/flutter/test/widgets/fractionally_sized_box_test.dart @@ -4,9 +4,10 @@ import 'package:flutter/widgets.dart'; import 'package:flutter_test/flutter_test.dart'; +import 'package:leak_tracker_flutter_testing/leak_tracker_flutter_testing.dart'; void main() { - testWidgets('FractionallySizedBox', (WidgetTester tester) async { + testWidgetsWithLeakTracking('FractionallySizedBox', (WidgetTester tester) async { final GlobalKey inner = GlobalKey(); await tester.pumpWidget(OverflowBox( minWidth: 0.0, @@ -29,7 +30,7 @@ void main() { expect(box.localToGlobal(Offset.zero), equals(const Offset(25.0, 37.5))); }); - testWidgets('FractionallySizedBox alignment', (WidgetTester tester) async { + testWidgetsWithLeakTracking('FractionallySizedBox alignment', (WidgetTester tester) async { final GlobalKey inner = GlobalKey(); await tester.pumpWidget(Directionality( textDirection: TextDirection.rtl, @@ -45,7 +46,7 @@ void main() { expect(box.localToGlobal(box.size.center(Offset.zero)), equals(const Offset(800.0 - 400.0 / 2.0, 0.0 + 300.0 / 2.0))); }); - testWidgets('FractionallySizedBox alignment (direction-sensitive)', (WidgetTester tester) async { + testWidgetsWithLeakTracking('FractionallySizedBox alignment (direction-sensitive)', (WidgetTester tester) async { final GlobalKey inner = GlobalKey(); await tester.pumpWidget(Directionality( textDirection: TextDirection.rtl, @@ -61,7 +62,7 @@ void main() { expect(box.localToGlobal(box.size.center(Offset.zero)), equals(const Offset(0.0 + 400.0 / 2.0, 0.0 + 300.0 / 2.0))); }); - testWidgets('OverflowBox alignment with FractionallySizedBox', (WidgetTester tester) async { + testWidgetsWithLeakTracking('OverflowBox alignment with FractionallySizedBox', (WidgetTester tester) async { final GlobalKey inner = GlobalKey(); await tester.pumpWidget(Directionality( textDirection: TextDirection.rtl, diff --git a/packages/flutter/test/widgets/framework_test.dart b/packages/flutter/test/widgets/framework_test.dart index 4d82aa85308..bfa44287a29 100644 --- a/packages/flutter/test/widgets/framework_test.dart +++ b/packages/flutter/test/widgets/framework_test.dart @@ -8,6 +8,7 @@ import 'package:flutter/material.dart'; import 'package:flutter/rendering.dart'; import 'package:flutter/services.dart'; import 'package:flutter_test/flutter_test.dart'; +import 'package:leak_tracker_flutter_testing/leak_tracker_flutter_testing.dart'; typedef ElementRebuildCallback = void Function(StatefulElement element); @@ -22,13 +23,13 @@ class _MyGlobalObjectKey> extends GlobalObjectKe } void main() { - testWidgets('UniqueKey control test', (WidgetTester tester) async { + testWidgetsWithLeakTracking('UniqueKey control test', (WidgetTester tester) async { final Key key = UniqueKey(); expect(key, hasOneLineDescription); expect(key, isNot(equals(UniqueKey()))); }); - testWidgets('ObjectKey control test', (WidgetTester tester) async { + testWidgetsWithLeakTracking('ObjectKey control test', (WidgetTester tester) async { final Object a = Object(); final Object b = Object(); final Key keyA = ObjectKey(a); @@ -41,7 +42,7 @@ void main() { expect(keyA, isNot(equals(keyB))); }); - testWidgets('GlobalObjectKey toString test', (WidgetTester tester) async { + testWidgetsWithLeakTracking('GlobalObjectKey toString test', (WidgetTester tester) async { const GlobalObjectKey one = GlobalObjectKey(1); const GlobalObjectKey two = GlobalObjectKey(2); const GlobalObjectKey three = _MyGlobalObjectKey(3); @@ -53,7 +54,7 @@ void main() { expect(four.toString(), equals('[_MyGlobalObjectKey ${describeIdentity(4)}]')); }); - testWidgets('GlobalObjectKey control test', (WidgetTester tester) async { + testWidgetsWithLeakTracking('GlobalObjectKey control test', (WidgetTester tester) async { final Object a = Object(); final Object b = Object(); final Key keyA = GlobalObjectKey(a); @@ -66,7 +67,7 @@ void main() { expect(keyA, isNot(equals(keyB))); }); - testWidgets('GlobalKey correct case 1 - can move global key from container widget to layoutbuilder', (WidgetTester tester) async { + testWidgetsWithLeakTracking('GlobalKey correct case 1 - can move global key from container widget to layoutbuilder', (WidgetTester tester) async { final Key key = GlobalKey(debugLabel: 'correct'); await tester.pumpWidget(Stack( textDirection: TextDirection.ltr, @@ -101,7 +102,7 @@ void main() { )); }); - testWidgets('GlobalKey correct case 2 - can move global key from layoutbuilder to container widget', (WidgetTester tester) async { + testWidgetsWithLeakTracking('GlobalKey correct case 2 - can move global key from layoutbuilder to container widget', (WidgetTester tester) async { final Key key = GlobalKey(debugLabel: 'correct'); await tester.pumpWidget(Stack( textDirection: TextDirection.ltr, @@ -135,7 +136,7 @@ void main() { )); }); - testWidgets('GlobalKey correct case 3 - can deal with early rebuild in layoutbuilder - move backward', (WidgetTester tester) async { + testWidgetsWithLeakTracking('GlobalKey correct case 3 - can deal with early rebuild in layoutbuilder - move backward', (WidgetTester tester) async { const Key key1 = GlobalObjectKey('Text1'); const Key key2 = GlobalObjectKey('Text2'); Key? rebuiltKeyOfSecondChildBeforeLayout; @@ -224,7 +225,7 @@ void main() { expect(rebuiltKeyOfSecondChildAfterLayout, key1); }); - testWidgets('GlobalKey correct case 4 - can deal with early rebuild in layoutbuilder - move forward', (WidgetTester tester) async { + testWidgetsWithLeakTracking('GlobalKey correct case 4 - can deal with early rebuild in layoutbuilder - move forward', (WidgetTester tester) async { const Key key1 = GlobalObjectKey('Text1'); const Key key2 = GlobalObjectKey('Text2'); const Key key3 = GlobalObjectKey('Text3'); @@ -327,7 +328,7 @@ void main() { expect(rebuiltKeyOfThirdChildAfterLayout, key2); }); - testWidgets('GlobalKey correct case 5 - can deal with early rebuild in layoutbuilder - only one global key', (WidgetTester tester) async { + testWidgetsWithLeakTracking('GlobalKey correct case 5 - can deal with early rebuild in layoutbuilder - only one global key', (WidgetTester tester) async { const Key key1 = GlobalObjectKey('Text1'); Key? rebuiltKeyOfSecondChildBeforeLayout; Key? rebuiltKeyOfThirdChildAfterLayout; @@ -418,7 +419,7 @@ void main() { expect(rebuiltKeyOfThirdChildAfterLayout, key1); }); - testWidgets('GlobalKey duplication 1 - double appearance', (WidgetTester tester) async { + testWidgetsWithLeakTracking('GlobalKey duplication 1 - double appearance', (WidgetTester tester) async { final Key key = GlobalKey(debugLabel: 'problematic'); await tester.pumpWidget(Stack( textDirection: TextDirection.ltr, @@ -447,7 +448,7 @@ void main() { ); }); - testWidgets('GlobalKey duplication 2 - splitting and changing type', (WidgetTester tester) async { + testWidgetsWithLeakTracking('GlobalKey duplication 2 - splitting and changing type', (WidgetTester tester) async { final Key key = GlobalKey(debugLabel: 'problematic'); await tester.pumpWidget(Stack( @@ -493,7 +494,7 @@ void main() { ); }); - testWidgets('GlobalKey duplication 3 - splitting and changing type', (WidgetTester tester) async { + testWidgetsWithLeakTracking('GlobalKey duplication 3 - splitting and changing type', (WidgetTester tester) async { final Key key = GlobalKey(debugLabel: 'problematic'); await tester.pumpWidget(Stack( textDirection: TextDirection.ltr, @@ -520,7 +521,7 @@ void main() { ); }); - testWidgets('GlobalKey duplication 4 - splitting and half changing type', (WidgetTester tester) async { + testWidgetsWithLeakTracking('GlobalKey duplication 4 - splitting and half changing type', (WidgetTester tester) async { final Key key = GlobalKey(debugLabel: 'problematic'); await tester.pumpWidget(Stack( textDirection: TextDirection.ltr, @@ -547,7 +548,7 @@ void main() { ); }); - testWidgets('GlobalKey duplication 5 - splitting and half changing type', (WidgetTester tester) async { + testWidgetsWithLeakTracking('GlobalKey duplication 5 - splitting and half changing type', (WidgetTester tester) async { final Key key = GlobalKey(debugLabel: 'problematic'); await tester.pumpWidget(Stack( textDirection: TextDirection.ltr, @@ -565,7 +566,7 @@ void main() { expect(tester.takeException(), isFlutterError); }); - testWidgets('GlobalKey duplication 6 - splitting and not changing type', (WidgetTester tester) async { + testWidgetsWithLeakTracking('GlobalKey duplication 6 - splitting and not changing type', (WidgetTester tester) async { final Key key = GlobalKey(debugLabel: 'problematic'); await tester.pumpWidget(Stack( textDirection: TextDirection.ltr, @@ -583,7 +584,7 @@ void main() { expect(tester.takeException(), isFlutterError); }); - testWidgets('GlobalKey duplication 7 - appearing later', (WidgetTester tester) async { + testWidgetsWithLeakTracking('GlobalKey duplication 7 - appearing later', (WidgetTester tester) async { final Key key = GlobalKey(debugLabel: 'problematic'); await tester.pumpWidget(Stack( textDirection: TextDirection.ltr, @@ -602,7 +603,7 @@ void main() { expect(tester.takeException(), isFlutterError); }); - testWidgets('GlobalKey duplication 8 - appearing earlier', (WidgetTester tester) async { + testWidgetsWithLeakTracking('GlobalKey duplication 8 - appearing earlier', (WidgetTester tester) async { final Key key = GlobalKey(debugLabel: 'problematic'); await tester.pumpWidget(Stack( textDirection: TextDirection.ltr, @@ -621,7 +622,7 @@ void main() { expect(tester.takeException(), isFlutterError); }); - testWidgets('GlobalKey duplication 9 - moving and appearing later', (WidgetTester tester) async { + testWidgetsWithLeakTracking('GlobalKey duplication 9 - moving and appearing later', (WidgetTester tester) async { final Key key = GlobalKey(debugLabel: 'problematic'); await tester.pumpWidget(Stack( textDirection: TextDirection.ltr, @@ -642,7 +643,7 @@ void main() { expect(tester.takeException(), isFlutterError); }); - testWidgets('GlobalKey duplication 10 - moving and appearing earlier', (WidgetTester tester) async { + testWidgetsWithLeakTracking('GlobalKey duplication 10 - moving and appearing earlier', (WidgetTester tester) async { final Key key = GlobalKey(debugLabel: 'problematic'); await tester.pumpWidget(Stack( textDirection: TextDirection.ltr, @@ -663,7 +664,7 @@ void main() { expect(tester.takeException(), isFlutterError); }); - testWidgets('GlobalKey duplication 11 - double sibling appearance', (WidgetTester tester) async { + testWidgetsWithLeakTracking('GlobalKey duplication 11 - double sibling appearance', (WidgetTester tester) async { final Key key = GlobalKey(debugLabel: 'problematic'); await tester.pumpWidget(Stack( textDirection: TextDirection.ltr, @@ -675,7 +676,7 @@ void main() { expect(tester.takeException(), isFlutterError); }); - testWidgets('GlobalKey duplication 12 - all kinds of badness at once', (WidgetTester tester) async { + testWidgetsWithLeakTracking('GlobalKey duplication 12 - all kinds of badness at once', (WidgetTester tester) async { final Key key1 = GlobalKey(debugLabel: 'problematic'); final Key key2 = GlobalKey(debugLabel: 'problematic'); // intentionally the same label final Key key3 = GlobalKey(debugLabel: 'also problematic'); @@ -723,7 +724,7 @@ void main() { ); }); - testWidgets('GlobalKey duplication 13 - all kinds of badness at once', (WidgetTester tester) async { + testWidgetsWithLeakTracking('GlobalKey duplication 13 - all kinds of badness at once', (WidgetTester tester) async { final Key key1 = GlobalKey(debugLabel: 'problematic'); final Key key2 = GlobalKey(debugLabel: 'problematic'); // intentionally the same label final Key key3 = GlobalKey(debugLabel: 'also problematic'); @@ -770,7 +771,7 @@ void main() { expect(tester.takeException(), isFlutterError); }); - testWidgets('GlobalKey duplication 14 - moving during build - before', (WidgetTester tester) async { + testWidgetsWithLeakTracking('GlobalKey duplication 14 - moving during build - before', (WidgetTester tester) async { final Key key = GlobalKey(debugLabel: 'problematic'); await tester.pumpWidget(Stack( textDirection: TextDirection.ltr, @@ -789,7 +790,7 @@ void main() { )); }); - testWidgets('GlobalKey duplication 15 - duplicating during build - before', (WidgetTester tester) async { + testWidgetsWithLeakTracking('GlobalKey duplication 15 - duplicating during build - before', (WidgetTester tester) async { final Key key = GlobalKey(debugLabel: 'problematic'); await tester.pumpWidget(Stack( textDirection: TextDirection.ltr, @@ -810,7 +811,7 @@ void main() { expect(tester.takeException(), isFlutterError); }); - testWidgets('GlobalKey duplication 16 - moving during build - after', (WidgetTester tester) async { + testWidgetsWithLeakTracking('GlobalKey duplication 16 - moving during build - after', (WidgetTester tester) async { final Key key = GlobalKey(debugLabel: 'problematic'); await tester.pumpWidget(Stack( textDirection: TextDirection.ltr, @@ -829,7 +830,7 @@ void main() { )); }); - testWidgets('GlobalKey duplication 17 - duplicating during build - after', (WidgetTester tester) async { + testWidgetsWithLeakTracking('GlobalKey duplication 17 - duplicating during build - after', (WidgetTester tester) async { final Key key = GlobalKey(debugLabel: 'problematic'); await tester.pumpWidget(Stack( textDirection: TextDirection.ltr, @@ -857,7 +858,7 @@ void main() { expect(count, 1); }); - testWidgets('GlobalKey duplication 18 - subtree build duplicate key with same type', (WidgetTester tester) async { + testWidgetsWithLeakTracking('GlobalKey duplication 18 - subtree build duplicate key with same type', (WidgetTester tester) async { final Key key = GlobalKey(debugLabel: 'problematic'); final Stack stack = Stack( textDirection: TextDirection.ltr, @@ -894,7 +895,7 @@ void main() { ); }); - testWidgets('GlobalKey duplication 19 - subtree build duplicate key with different types', (WidgetTester tester) async { + testWidgetsWithLeakTracking('GlobalKey duplication 19 - subtree build duplicate key with different types', (WidgetTester tester) async { final Key key = GlobalKey(debugLabel: 'problematic'); final Stack stack = Stack( textDirection: TextDirection.ltr, @@ -922,7 +923,7 @@ void main() { ); }); - testWidgets('GlobalKey duplication 20 - real duplication with early rebuild in layoutbuilder will throw', (WidgetTester tester) async { + testWidgetsWithLeakTracking('GlobalKey duplication 20 - real duplication with early rebuild in layoutbuilder will throw', (WidgetTester tester) async { const Key key1 = GlobalObjectKey('Text1'); const Key key2 = GlobalObjectKey('Text2'); Key? rebuiltKeyOfSecondChildBeforeLayout; @@ -1023,14 +1024,17 @@ void main() { ); }); - testWidgets('GlobalKey - detach and re-attach child to different parents', (WidgetTester tester) async { + testWidgetsWithLeakTracking('GlobalKey - detach and re-attach child to different parents', (WidgetTester tester) async { + final ScrollController scrollController = ScrollController(); + addTearDown(scrollController.dispose); + await tester.pumpWidget(Directionality( textDirection: TextDirection.ltr, child: Center( child: SizedBox( height: 100, child: CustomScrollView( - controller: ScrollController(), + controller: scrollController, slivers: [ SliverList( delegate: SliverChildListDelegate([ @@ -1058,7 +1062,7 @@ 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'); @@ -1104,7 +1108,7 @@ void main() { expect(tabController.index, 0); }); - testWidgets('Defunct setState throws exception', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Defunct setState throws exception', (WidgetTester tester) async { late StateSetter setState; await tester.pumpWidget(StatefulBuilder( @@ -1122,12 +1126,12 @@ void main() { expect(() { setState(() { }); }, throwsFlutterError); }); - testWidgets('State toString', (WidgetTester tester) async { + testWidgetsWithLeakTracking('State toString', (WidgetTester tester) async { final TestState state = TestState(); expect(state.toString(), contains('no widget')); }); - testWidgets('debugPrintGlobalKeyedWidgetLifecycle control test', (WidgetTester tester) async { + testWidgetsWithLeakTracking('debugPrintGlobalKeyedWidgetLifecycle control test', (WidgetTester tester) async { expect(debugPrintGlobalKeyedWidgetLifecycle, isFalse); final DebugPrintCallback oldCallback = debugPrint; @@ -1150,7 +1154,7 @@ void main() { expect(log[1], matches('Discarding .+ from inactive elements list.')); }); - testWidgets('MultiChildRenderObjectElement.children', (WidgetTester tester) async { + testWidgetsWithLeakTracking('MultiChildRenderObjectElement.children', (WidgetTester tester) async { GlobalKey key0, key1, key2; await tester.pumpWidget(Column( key: key0 = GlobalKey(), @@ -1169,7 +1173,7 @@ void main() { ); }); - testWidgets('Can not attach a non-RenderObjectElement to the MultiChildRenderObjectElement - mount', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Can not attach a non-RenderObjectElement to the MultiChildRenderObjectElement - mount', (WidgetTester tester) async { await tester.pumpWidget( Column( children: [ @@ -1194,7 +1198,7 @@ void main() { ); }); - testWidgets('Can not attach a non-RenderObjectElement to the MultiChildRenderObjectElement - update', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Can not attach a non-RenderObjectElement to the MultiChildRenderObjectElement - update', (WidgetTester tester) async { await tester.pumpWidget( Column( children: [ @@ -1227,7 +1231,7 @@ void main() { ); }); - testWidgets('Element diagnostics', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Element diagnostics', (WidgetTester tester) async { GlobalKey key0; await tester.pumpWidget(Column( key: key0 = GlobalKey(), @@ -1319,7 +1323,7 @@ void main() { } }); - testWidgets('didUpdateDependencies is not called on a State that never rebuilds', (WidgetTester tester) async { + testWidgetsWithLeakTracking('didUpdateDependencies is not called on a State that never rebuilds', (WidgetTester tester) async { final GlobalKey key = GlobalKey(); /// Initial build - should call didChangeDependencies, not deactivate @@ -1348,7 +1352,7 @@ void main() { expect(state.deactivatedCount, 2); }); - testWidgets('StatefulElement subclass can decorate State.build', (WidgetTester tester) async { + testWidgetsWithLeakTracking('StatefulElement subclass can decorate State.build', (WidgetTester tester) async { late bool isDidChangeDependenciesDecorated; late bool isBuildDecorated; @@ -1372,7 +1376,7 @@ void main() { expect(isDidChangeDependenciesDecorated, isFalse); }); group('BuildContext.debugDoingbuild', () { - testWidgets('StatelessWidget', (WidgetTester tester) async { + testWidgetsWithLeakTracking('StatelessWidget', (WidgetTester tester) async { late bool debugDoingBuildOnBuild; await tester.pumpWidget( StatelessWidgetSpy( @@ -1387,7 +1391,7 @@ void main() { expect(context.debugDoingBuild, isFalse); expect(debugDoingBuildOnBuild, isTrue); }); - testWidgets('StatefulWidget', (WidgetTester tester) async { + testWidgetsWithLeakTracking('StatefulWidget', (WidgetTester tester) async { late bool debugDoingBuildOnBuild; late bool debugDoingBuildOnInitState; late bool debugDoingBuildOnDidChangeDependencies; @@ -1456,11 +1460,12 @@ void main() { expect(debugDoingBuildOnDispose, isFalse); expect(debugDoingBuildOnDeactivate, isFalse); }); - testWidgets('RenderObjectWidget', (WidgetTester tester) async { + testWidgetsWithLeakTracking('RenderObjectWidget', (WidgetTester tester) async { late bool debugDoingBuildOnCreateRenderObject; bool? debugDoingBuildOnUpdateRenderObject; bool? debugDoingBuildOnDidUnmountRenderObject; final ValueNotifier notifier = ValueNotifier(0); + addTearDown(notifier.dispose); late BuildContext spyContext; @@ -1516,7 +1521,7 @@ void main() { }); }); - testWidgets('A widget whose element has an invalid visitChildren implementation triggers a useful error message', (WidgetTester tester) async { + testWidgetsWithLeakTracking('A widget whose element has an invalid visitChildren implementation triggers a useful error message', (WidgetTester tester) async { final GlobalKey key = GlobalKey(); await tester.pumpWidget(_WidgetWithNoVisitChildren(_StatefulLeaf(key: key))); (key.currentState! as _StatefulLeafState).markNeedsBuild(); @@ -1542,11 +1547,13 @@ void main() { ); }); - testWidgets('Can create BuildOwner that does not interfere with pointer router or raw key event handler', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Can create BuildOwner that does not interfere with pointer router or raw key event handler', (WidgetTester tester) async { final int pointerRouterCount = GestureBinding.instance.pointerRouter.debugGlobalRouteCount; final RawKeyEventHandler? rawKeyEventHandler = RawKeyboard.instance.keyEventHandler; expect(rawKeyEventHandler, isNotNull); - BuildOwner(focusManager: FocusManager()); + final FocusManager focusManager = FocusManager(); + addTearDown(focusManager.dispose); + BuildOwner(focusManager: focusManager); expect(GestureBinding.instance.pointerRouter.debugGlobalRouteCount, pointerRouterCount); expect(RawKeyboard.instance.keyEventHandler, same(rawKeyEventHandler)); }); @@ -1601,7 +1608,7 @@ void main() { expect(dependenciesProperty.toDescription(), '[ButtonBarTheme, Directionality, FocusTraversalOrder]'); }); - testWidgets('BuildOwner.globalKeyCount keeps track of in-use global keys', (WidgetTester tester) async { + testWidgetsWithLeakTracking('BuildOwner.globalKeyCount keeps track of in-use global keys', (WidgetTester tester) async { final int initialCount = tester.binding.buildOwner!.globalKeyCount; final GlobalKey key1 = GlobalKey(); final GlobalKey key2 = GlobalKey(); @@ -1615,7 +1622,7 @@ void main() { expect(tester.binding.buildOwner!.globalKeyCount, initialCount + 0); }); - testWidgets('Widget and State properties are nulled out when unmounted', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Widget and State properties are nulled out when unmounted', (WidgetTester tester) async { await tester.pumpWidget(const _StatefulLeaf()); final StatefulElement element = tester.element(find.byType(_StatefulLeaf)); expect(element.state, isA>()); @@ -1631,7 +1638,7 @@ void main() { expect(() => element.widget, throwsA(isA())); }); - testWidgets('LayerLink can be swapped between parent and child container layers', (WidgetTester tester) async { + testWidgetsWithLeakTracking('LayerLink can be swapped between parent and child container layers', (WidgetTester tester) async { // Regression test for https://github.com/flutter/flutter/issues/96959. final LayerLink link = LayerLink(); await tester.pumpWidget(_TestLeaderLayerWidget( @@ -1653,7 +1660,7 @@ void main() { }); - testWidgets('Deactivate and activate are called correctly', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Deactivate and activate are called correctly', (WidgetTester tester) async { final List states = []; Widget build([Key? key]) { return StatefulWidgetSpy( @@ -1687,7 +1694,7 @@ void main() { expect(states, ['deactivate', 'dispose']); }); - testWidgets('RenderObjectElement.unmount disposes of its renderObject', (WidgetTester tester) async { + testWidgetsWithLeakTracking('RenderObjectElement.unmount disposes of its renderObject', (WidgetTester tester) async { await tester.pumpWidget(const Placeholder()); final RenderObjectElement element = tester.allElements.whereType().last; final RenderObject renderObject = element.renderObject; @@ -1699,7 +1706,7 @@ void main() { expect(renderObject.debugDisposed, true); }); - testWidgets('Getting the render object of an unmounted element throws', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Getting the render object of an unmounted element throws', (WidgetTester tester) async { await tester.pumpWidget(const _StatefulLeaf()); final StatefulElement element = tester.element(find.byType(_StatefulLeaf)); expect(element.state, isA>()); @@ -1754,7 +1761,7 @@ The findRenderObject() method was called for the following element: expect(child.doesDependOnInheritedElement(ancestor), isTrue); }); - testWidgets( + testWidgetsWithLeakTracking( 'MultiChildRenderObjectElement.updateChildren test', (WidgetTester tester) async { // Regression test for https://github.com/flutter/flutter/issues/120762. diff --git a/packages/flutter/test/widgets/gesture_detector_semantics_test.dart b/packages/flutter/test/widgets/gesture_detector_semantics_test.dart index 4632cf53bc5..fc43260892e 100644 --- a/packages/flutter/test/widgets/gesture_detector_semantics_test.dart +++ b/packages/flutter/test/widgets/gesture_detector_semantics_test.dart @@ -6,11 +6,12 @@ import 'package:flutter/gestures.dart'; 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 'semantics_tester.dart'; void main() { - testWidgets('Vertical gesture detector has up/down actions', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Vertical gesture detector has up/down actions', (WidgetTester tester) async { final SemanticsTester semantics = SemanticsTester(tester); int callCount = 0; @@ -44,7 +45,7 @@ void main() { semantics.dispose(); }); - testWidgets('Horizontal gesture detector has up/down actions', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Horizontal gesture detector has up/down actions', (WidgetTester tester) async { final SemanticsTester semantics = SemanticsTester(tester); int callCount = 0; @@ -78,7 +79,7 @@ void main() { semantics.dispose(); }); - testWidgets('All registered handlers for the gesture kind are called', (WidgetTester tester) async { + testWidgetsWithLeakTracking('All registered handlers for the gesture kind are called', (WidgetTester tester) async { final SemanticsTester semantics = SemanticsTester(tester); final Set logs = {}; @@ -102,7 +103,7 @@ void main() { semantics.dispose(); }); - testWidgets('Replacing recognizers should update semantic handlers', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Replacing recognizers should update semantic handlers', (WidgetTester tester) async { final SemanticsTester semantics = SemanticsTester(tester); // How the test is set up: @@ -173,7 +174,7 @@ void main() { }); group("RawGestureDetector's custom semantics delegate", () { - testWidgets('should update semantics notations when switching from the default delegate', (WidgetTester tester) async { + testWidgetsWithLeakTracking('should update semantics notations when switching from the default delegate', (WidgetTester tester) async { final SemanticsTester semantics = SemanticsTester(tester); final Map gestures = _buildGestureMap(() => LongPressGestureRecognizer(), null) @@ -208,7 +209,7 @@ void main() { semantics.dispose(); }); - testWidgets('should update semantics notations when switching to the default delegate', (WidgetTester tester) async { + testWidgetsWithLeakTracking('should update semantics notations when switching to the default delegate', (WidgetTester tester) async { final SemanticsTester semantics = SemanticsTester(tester); final Map gestures = _buildGestureMap(() => LongPressGestureRecognizer(), null) @@ -243,7 +244,7 @@ void main() { semantics.dispose(); }); - testWidgets('should update semantics notations when switching from a different custom delegate', (WidgetTester tester) async { + testWidgetsWithLeakTracking('should update semantics notations when switching from a different custom delegate', (WidgetTester tester) async { final SemanticsTester semantics = SemanticsTester(tester); final Map gestures = _buildGestureMap(() => LongPressGestureRecognizer(), null) @@ -279,7 +280,7 @@ void main() { semantics.dispose(); }); - testWidgets('should correctly call callbacks', (WidgetTester tester) async { + testWidgetsWithLeakTracking('should correctly call callbacks', (WidgetTester tester) async { final SemanticsTester semantics = SemanticsTester(tester); final List logs = []; final GlobalKey detectorKey = GlobalKey(); @@ -321,7 +322,7 @@ void main() { group("RawGestureDetector's default semantics delegate", () { group('should map onTap to', () { - testWidgets('null when there is no TapGR', (WidgetTester tester) async { + testWidgetsWithLeakTracking('null when there is no TapGR', (WidgetTester tester) async { final SemanticsTester semantics = SemanticsTester(tester); await tester.pumpWidget( Center( @@ -339,7 +340,7 @@ void main() { semantics.dispose(); }); - testWidgets('non-null when there is TapGR with no callbacks', (WidgetTester tester) async { + testWidgetsWithLeakTracking('non-null when there is TapGR with no callbacks', (WidgetTester tester) async { final SemanticsTester semantics = SemanticsTester(tester); await tester.pumpWidget( Center( @@ -360,7 +361,7 @@ void main() { semantics.dispose(); }); - testWidgets('a callback that correctly calls callbacks', (WidgetTester tester) async { + testWidgetsWithLeakTracking('a callback that correctly calls callbacks', (WidgetTester tester) async { final SemanticsTester semantics = SemanticsTester(tester); final GlobalKey detectorKey = GlobalKey(); final List logs = []; @@ -394,7 +395,7 @@ void main() { }); group('should map onLongPress to', () { - testWidgets('null when there is no LongPressGR ', (WidgetTester tester) async { + testWidgetsWithLeakTracking('null when there is no LongPressGR ', (WidgetTester tester) async { final SemanticsTester semantics = SemanticsTester(tester); await tester.pumpWidget( Center( @@ -412,7 +413,7 @@ void main() { semantics.dispose(); }); - testWidgets('non-null when there is LongPressGR with no callbacks', (WidgetTester tester) async { + testWidgetsWithLeakTracking('non-null when there is LongPressGR with no callbacks', (WidgetTester tester) async { final SemanticsTester semantics = SemanticsTester(tester); await tester.pumpWidget( Center( @@ -433,7 +434,7 @@ void main() { semantics.dispose(); }); - testWidgets('a callback that correctly calls callbacks', (WidgetTester tester) async { + testWidgetsWithLeakTracking('a callback that correctly calls callbacks', (WidgetTester tester) async { final SemanticsTester semantics = SemanticsTester(tester); final GlobalKey detectorKey = GlobalKey(); final List logs = []; @@ -466,7 +467,7 @@ void main() { }); group('should map onHorizontalDragUpdate to', () { - testWidgets('null when there is no matching recognizers ', (WidgetTester tester) async { + testWidgetsWithLeakTracking('null when there is no matching recognizers ', (WidgetTester tester) async { final SemanticsTester semantics = SemanticsTester(tester); await tester.pumpWidget( Center( @@ -484,7 +485,7 @@ void main() { semantics.dispose(); }); - testWidgets('non-null when there is either matching recognizer with no callbacks', (WidgetTester tester) async { + testWidgetsWithLeakTracking('non-null when there is either matching recognizer with no callbacks', (WidgetTester tester) async { final SemanticsTester semantics = SemanticsTester(tester); await tester.pumpWidget( Center( @@ -526,7 +527,7 @@ void main() { semantics.dispose(); }); - testWidgets('a callback that correctly calls callbacks', (WidgetTester tester) async { + testWidgetsWithLeakTracking('a callback that correctly calls callbacks', (WidgetTester tester) async { final SemanticsTester semantics = SemanticsTester(tester); final GlobalKey detectorKey = GlobalKey(); final List logs = []; @@ -576,7 +577,7 @@ void main() { }); group('should map onVerticalDragUpdate to', () { - testWidgets('null when there is no matching recognizers ', (WidgetTester tester) async { + testWidgetsWithLeakTracking('null when there is no matching recognizers ', (WidgetTester tester) async { final SemanticsTester semantics = SemanticsTester(tester); await tester.pumpWidget( Center( @@ -594,7 +595,7 @@ void main() { semantics.dispose(); }); - testWidgets('non-null when there is either matching recognizer with no callbacks', (WidgetTester tester) async { + testWidgetsWithLeakTracking('non-null when there is either matching recognizer with no callbacks', (WidgetTester tester) async { final SemanticsTester semantics = SemanticsTester(tester); await tester.pumpWidget( Center( @@ -617,7 +618,7 @@ void main() { semantics.dispose(); }); - testWidgets('a callback that correctly calls callbacks', (WidgetTester tester) async { + testWidgetsWithLeakTracking('a callback that correctly calls callbacks', (WidgetTester tester) async { final SemanticsTester semantics = SemanticsTester(tester); final GlobalKey detectorKey = GlobalKey(); final List logs = []; @@ -666,7 +667,7 @@ void main() { }); }); - testWidgets('should update semantics notations when receiving new gestures', (WidgetTester tester) async { + testWidgetsWithLeakTracking('should update semantics notations when receiving new gestures', (WidgetTester tester) async { final SemanticsTester semantics = SemanticsTester(tester); await tester.pumpWidget( Center( diff --git a/packages/flutter/test/widgets/gesture_detector_test.dart b/packages/flutter/test/widgets/gesture_detector_test.dart index 40d3569d117..3d30a8f32ff 100644 --- a/packages/flutter/test/widgets/gesture_detector_test.dart +++ b/packages/flutter/test/widgets/gesture_detector_test.dart @@ -6,11 +6,12 @@ import 'package:flutter/gestures.dart'; 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'; void main() { const Offset forcePressOffset = Offset(400.0, 50.0); - testWidgets('Uncontested scrolls start immediately', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Uncontested scrolls start immediately', (WidgetTester tester) async { bool didStartDrag = false; double? updatedDragDelta; bool didEndDrag = false; @@ -58,7 +59,7 @@ void main() { await tester.pumpWidget(Container()); }); - testWidgets('Match two scroll gestures in succession', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Match two scroll gestures in succession', (WidgetTester tester) async { int gestureCount = 0; double dragDistance = 0.0; @@ -91,7 +92,7 @@ void main() { await tester.pumpWidget(Container()); }); - testWidgets("Pan doesn't crash", (WidgetTester tester) async { + testWidgetsWithLeakTracking("Pan doesn't crash", (WidgetTester tester) async { bool didStartPan = false; Offset? panDelta; bool didEndPan = false; @@ -135,7 +136,7 @@ void main() { }, ); - testWidgets('Translucent', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Translucent', (WidgetTester tester) async { bool didReceivePointerDown; bool didTap; @@ -206,7 +207,7 @@ void main() { expect(didTap, isTrue); }, variant: buttonVariant); - testWidgets('Empty', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Empty', (WidgetTester tester) async { bool didTap = false; await tester.pumpWidget( Center( @@ -228,7 +229,7 @@ void main() { expect(didTap, isTrue); }, variant: buttonVariant); - testWidgets('Only container', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Only container', (WidgetTester tester) async { bool didTap = false; await tester.pumpWidget( Center( @@ -251,7 +252,7 @@ void main() { expect(didTap, isFalse); }, variant: buttonVariant); - testWidgets('cache render object', (WidgetTester tester) async { + testWidgetsWithLeakTracking('cache render object', (WidgetTester tester) async { void inputCallback() { } await tester.pumpWidget( @@ -283,7 +284,7 @@ void main() { expect(renderObj1, same(renderObj2)); }, variant: buttonVariant); - testWidgets('Tap down occurs after kPressTimeout', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Tap down occurs after kPressTimeout', (WidgetTester tester) async { int tapDown = 0; int tap = 0; int tapCancel = 0; @@ -391,7 +392,7 @@ void main() { expect(longPress, 1); }, variant: buttonVariant); - testWidgets('Long Press Up Callback called after long press', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Long Press Up Callback called after long press', (WidgetTester tester) async { int longPressUp = 0; await tester.pumpWidget( @@ -441,7 +442,7 @@ void main() { }, variant: buttonVariant); }); - testWidgets('Primary and secondary long press callbacks should work together in GestureDetector', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Primary and secondary long press callbacks should work together in GestureDetector', (WidgetTester tester) async { bool primaryLongPress = false, secondaryLongPress = false; await tester.pumpWidget( @@ -477,7 +478,7 @@ void main() { expect(secondaryLongPress, isTrue); }); - testWidgets('Force Press Callback called after force press', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Force Press Callback called after force press', (WidgetTester tester) async { int forcePressStart = 0; int forcePressPeaked = 0; int forcePressUpdate = 0; @@ -580,7 +581,7 @@ void main() { expect(forcePressEnded, 1); }); - testWidgets('Force Press Callback not called if long press triggered before force press', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Force Press Callback not called if long press triggered before force press', (WidgetTester tester) async { int forcePressStart = 0; int longPressTimes = 0; @@ -645,7 +646,7 @@ void main() { expect(forcePressStart, 0); }); - testWidgets('Force Press Callback not called if drag triggered before force press', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Force Press Callback not called if drag triggered before force press', (WidgetTester tester) async { int forcePressStart = 0; int horizontalDragStart = 0; @@ -706,7 +707,7 @@ void main() { }); group("RawGestureDetectorState's debugFillProperties", () { - testWidgets('when default', (WidgetTester tester) async { + testWidgetsWithLeakTracking('when default', (WidgetTester tester) async { final DiagnosticPropertiesBuilder builder = DiagnosticPropertiesBuilder(); final GlobalKey key = GlobalKey(); await tester.pumpWidget(RawGestureDetector( @@ -724,7 +725,7 @@ void main() { ]); }); - testWidgets('should show gestures, custom semantics and behavior', (WidgetTester tester) async { + testWidgetsWithLeakTracking('should show gestures, custom semantics and behavior', (WidgetTester tester) async { final DiagnosticPropertiesBuilder builder = DiagnosticPropertiesBuilder(); final GlobalKey key = GlobalKey(); await tester.pumpWidget(RawGestureDetector( @@ -761,7 +762,7 @@ void main() { ]); }); - testWidgets('should not show semantics when excludeFromSemantics is true', (WidgetTester tester) async { + testWidgetsWithLeakTracking('should not show semantics when excludeFromSemantics is true', (WidgetTester tester) async { final DiagnosticPropertiesBuilder builder = DiagnosticPropertiesBuilder(); final GlobalKey key = GlobalKey(); await tester.pumpWidget(RawGestureDetector( @@ -832,7 +833,7 @@ void main() { } }); - testWidgets('replaceGestureRecognizers not during layout', (WidgetTester tester) async { + testWidgetsWithLeakTracking('replaceGestureRecognizers not during layout', (WidgetTester tester) async { final GlobalKey key = GlobalKey(); await tester.pumpWidget( Directionality( @@ -876,7 +877,7 @@ void main() { }); }); - testWidgets('supportedDevices update test', (WidgetTester tester) async { + testWidgetsWithLeakTracking('supportedDevices update test', (WidgetTester tester) async { // Regression test for https://github.com/flutter/flutter/issues/111716 bool didStartPan = false; Offset? panDelta; @@ -946,7 +947,7 @@ void main() { expect(didEndPan, isTrue); }); - testWidgets('supportedDevices is respected', (WidgetTester tester) async { + testWidgetsWithLeakTracking('supportedDevices is respected', (WidgetTester tester) async { bool didStartPan = false; Offset? panDelta; bool didEndPan = false; @@ -994,7 +995,7 @@ void main() { }); group('DoubleTap', () { - testWidgets('onDoubleTap is called even if onDoubleTapDown has not been not provided', (WidgetTester tester) async { + testWidgetsWithLeakTracking('onDoubleTap is called even if onDoubleTapDown has not been not provided', (WidgetTester tester) async { final List log = []; await tester.pumpWidget( Directionality( @@ -1017,7 +1018,7 @@ void main() { expect(log, ['double-tap']); }); - testWidgets('onDoubleTapDown is called even if onDoubleTap has not been not provided', (WidgetTester tester) async { + testWidgetsWithLeakTracking('onDoubleTapDown is called even if onDoubleTap has not been not provided', (WidgetTester tester) async { final List log = []; await tester.pumpWidget( Directionality( diff --git a/packages/flutter/test/widgets/gesture_disambiguation_test.dart b/packages/flutter/test/widgets/gesture_disambiguation_test.dart index 0788d5a5c16..617e1046deb 100644 --- a/packages/flutter/test/widgets/gesture_disambiguation_test.dart +++ b/packages/flutter/test/widgets/gesture_disambiguation_test.dart @@ -4,9 +4,10 @@ import 'package:flutter/widgets.dart'; import 'package:flutter_test/flutter_test.dart'; +import 'package:leak_tracker_flutter_testing/leak_tracker_flutter_testing.dart'; void main() { - testWidgets('onTap detection with canceled pointer and a drag listener', (WidgetTester tester) async { + testWidgetsWithLeakTracking('onTap detection with canceled pointer and a drag listener', (WidgetTester tester) async { int detector1TapCount = 0; int detector2TapCount = 0; diff --git a/packages/flutter/test/widgets/global_keys_duplicated_test.dart b/packages/flutter/test/widgets/global_keys_duplicated_test.dart index ec37b19be24..f2e2430cf8f 100644 --- a/packages/flutter/test/widgets/global_keys_duplicated_test.dart +++ b/packages/flutter/test/widgets/global_keys_duplicated_test.dart @@ -5,11 +5,12 @@ 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'; // There's also some duplicate GlobalKey tests in the framework_test.dart file. void main() { - testWidgets('GlobalKey children of one node', (WidgetTester tester) async { + testWidgetsWithLeakTracking('GlobalKey children of one node', (WidgetTester tester) async { // This is actually a test of the regular duplicate key logic, which // happens before the duplicate GlobalKey logic. await tester.pumpWidget(const Stack(children: [ @@ -23,7 +24,7 @@ void main() { expect(error.toString(), contains('[GlobalObjectKey ${describeIdentity(0)}]')); }); - testWidgets('GlobalKey children of two nodes - A', (WidgetTester tester) async { + testWidgetsWithLeakTracking('GlobalKey children of two nodes - A', (WidgetTester tester) async { await tester.pumpWidget(const Stack( textDirection: TextDirection.ltr, children: [ @@ -43,7 +44,7 @@ void main() { expect(error.toString(), endsWith('\nA GlobalKey can only be specified on one widget at a time in the widget tree.')); }); - testWidgets('GlobalKey children of two different nodes - B', (WidgetTester tester) async { + testWidgetsWithLeakTracking('GlobalKey children of two different nodes - B', (WidgetTester tester) async { await tester.pumpWidget(const Stack( textDirection: TextDirection.ltr, children: [ @@ -61,7 +62,7 @@ void main() { expect(error.toString(), endsWith('\nA GlobalKey can only be specified on one widget at a time in the widget tree.')); }); - testWidgets('GlobalKey children of two nodes - C', (WidgetTester tester) async { + testWidgetsWithLeakTracking('GlobalKey children of two nodes - C', (WidgetTester tester) async { late StateSetter nestedSetState; bool flag = false; await tester.pumpWidget(Stack( diff --git a/packages/flutter/test/widgets/global_keys_moving_test.dart b/packages/flutter/test/widgets/global_keys_moving_test.dart index 4984289950a..1fd8eaf1be1 100644 --- a/packages/flutter/test/widgets/global_keys_moving_test.dart +++ b/packages/flutter/test/widgets/global_keys_moving_test.dart @@ -4,6 +4,7 @@ import 'package:flutter/widgets.dart'; import 'package:flutter_test/flutter_test.dart'; +import 'package:leak_tracker_flutter_testing/leak_tracker_flutter_testing.dart'; class Item { GlobalKey key1 = GlobalKey(); @@ -55,7 +56,7 @@ Widget builder() { } void main() { - testWidgets('moving subtrees with global keys - smoketest', (WidgetTester tester) async { + testWidgetsWithLeakTracking('moving subtrees with global keys - smoketest', (WidgetTester tester) async { await tester.pumpWidget(builder()); final StatefulLeafState leaf = tester.firstState(find.byType(StatefulLeaf)); leaf.markNeedsBuild(); diff --git a/packages/flutter/test/widgets/grid_paper_test.dart b/packages/flutter/test/widgets/grid_paper_test.dart index d64a14cfc74..cd0c8d93bb8 100644 --- a/packages/flutter/test/widgets/grid_paper_test.dart +++ b/packages/flutter/test/widgets/grid_paper_test.dart @@ -5,9 +5,10 @@ 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'; void main() { - testWidgets('GridPaper control test', (WidgetTester tester) async { + testWidgetsWithLeakTracking('GridPaper control test', (WidgetTester tester) async { await tester.pumpWidget(const GridPaper()); final List layers1 = tester.layers; await tester.pumpWidget(const GridPaper()); diff --git a/packages/flutter/test/widgets/grid_view_layout_test.dart b/packages/flutter/test/widgets/grid_view_layout_test.dart index 221b96080b1..ea1e43e725d 100644 --- a/packages/flutter/test/widgets/grid_view_layout_test.dart +++ b/packages/flutter/test/widgets/grid_view_layout_test.dart @@ -4,9 +4,10 @@ import 'package:flutter/widgets.dart'; import 'package:flutter_test/flutter_test.dart'; +import 'package:leak_tracker_flutter_testing/leak_tracker_flutter_testing.dart'; void main() { - testWidgets('Empty GridView', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Empty GridView', (WidgetTester tester) async { final List children = [ const DecoratedBox(decoration: BoxDecoration()), const DecoratedBox(decoration: BoxDecoration()), diff --git a/packages/flutter/test/widgets/grid_view_test.dart b/packages/flutter/test/widgets/grid_view_test.dart index f1abdebe6fc..03014dbd298 100644 --- a/packages/flutter/test/widgets/grid_view_test.dart +++ b/packages/flutter/test/widgets/grid_view_test.dart @@ -6,13 +6,14 @@ import 'package:flutter/gestures.dart' show DragStartBehavior; 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 '../rendering/rendering_tester.dart' show TestClipPaintingContext; import 'states.dart'; void main() { // Regression test for https://github.com/flutter/flutter/issues/100451 - testWidgets('GridView.builder respects findChildIndexCallback', (WidgetTester tester) async { + testWidgetsWithLeakTracking('GridView.builder respects findChildIndexCallback', (WidgetTester tester) async { bool finderCalled = false; int itemCount = 7; late StateSetter stateSetter; @@ -50,7 +51,7 @@ void main() { expect(finderCalled, true); }); - testWidgets('Empty GridView', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Empty GridView', (WidgetTester tester) async { await tester.pumpWidget( Directionality( textDirection: TextDirection.ltr, @@ -62,7 +63,7 @@ void main() { ); }); - testWidgets('GridView.count control test', (WidgetTester tester) async { + testWidgetsWithLeakTracking('GridView.count control test', (WidgetTester tester) async { final List log = []; await tester.pumpWidget( @@ -135,7 +136,7 @@ void main() { log.clear(); }); - testWidgets('GridView.extent control test', (WidgetTester tester) async { + testWidgetsWithLeakTracking('GridView.extent control test', (WidgetTester tester) async { final List log = []; await tester.pumpWidget( @@ -182,7 +183,7 @@ void main() { log.clear(); }); - testWidgets('GridView large scroll jump', (WidgetTester tester) async { + testWidgetsWithLeakTracking('GridView large scroll jump', (WidgetTester tester) async { final List log = []; await tester.pumpWidget( @@ -274,7 +275,7 @@ void main() { } }); - testWidgets('GridView - change crossAxisCount', (WidgetTester tester) async { + testWidgetsWithLeakTracking('GridView - change crossAxisCount', (WidgetTester tester) async { final List log = []; await tester.pumpWidget( @@ -345,7 +346,7 @@ void main() { expect(find.text('4'), findsNothing); }); - testWidgets('SliverGridRegularTileLayout - can handle close to zero mainAxisStride', (WidgetTester tester) async { + testWidgetsWithLeakTracking('SliverGridRegularTileLayout - can handle close to zero mainAxisStride', (WidgetTester tester) async { const SliverGridDelegateWithMaxCrossAxisExtent delegate = SliverGridDelegateWithMaxCrossAxisExtent( childAspectRatio: 1e300, maxCrossAxisExtent: 500.0, @@ -369,7 +370,7 @@ void main() { expect(layout.getMinChildIndexForScrollOffset(1000.0), 0.0); }); - testWidgets('GridView - change maxChildCrossAxisExtent', (WidgetTester tester) async { + testWidgetsWithLeakTracking('GridView - change maxChildCrossAxisExtent', (WidgetTester tester) async { final List log = []; await tester.pumpWidget( @@ -440,7 +441,7 @@ void main() { expect(find.text('4'), findsNothing); }); - testWidgets('One-line GridView paints', (WidgetTester tester) async { + testWidgetsWithLeakTracking('One-line GridView paints', (WidgetTester tester) async { const Color green = Color(0xFF00FF00); final Container container = Container( @@ -469,7 +470,7 @@ void main() { expect(find.byType(GridView), isNot(paints..rect(color: green)..rect(color: green)..rect(color: green))); }); - testWidgets('GridView in zero context', (WidgetTester tester) async { + testWidgetsWithLeakTracking('GridView in zero context', (WidgetTester tester) async { await tester.pumpWidget( Directionality( textDirection: TextDirection.ltr, @@ -490,7 +491,7 @@ void main() { expect(find.text('1'), findsNothing); }); - testWidgets('GridView in unbounded context', (WidgetTester tester) async { + testWidgetsWithLeakTracking('GridView in unbounded context', (WidgetTester tester) async { await tester.pumpWidget( Directionality( textDirection: TextDirection.ltr, @@ -510,7 +511,7 @@ void main() { expect(find.text('19'), findsOneWidget); }); - testWidgets('GridView.builder control test', (WidgetTester tester) async { + testWidgetsWithLeakTracking('GridView.builder control test', (WidgetTester tester) async { await tester.pumpWidget( Directionality( textDirection: TextDirection.ltr, @@ -531,7 +532,7 @@ void main() { expect(find.text('12'), findsNothing); }); - testWidgets('GridView.builder with undefined itemCount', (WidgetTester tester) async { + testWidgetsWithLeakTracking('GridView.builder with undefined itemCount', (WidgetTester tester) async { await tester.pumpWidget( Directionality( textDirection: TextDirection.ltr, @@ -553,7 +554,7 @@ void main() { expect(find.text('13'), findsOneWidget); }); - testWidgets('GridView cross axis layout', (WidgetTester tester) async { + testWidgetsWithLeakTracking('GridView cross axis layout', (WidgetTester tester) async { final Key target = UniqueKey(); Widget build(TextDirection textDirection) { @@ -579,7 +580,7 @@ void main() { expect(tester.getBottomRight(find.byKey(target)), const Offset(800.0, 200.0)); }); - testWidgets('GridView crossAxisSpacing', (WidgetTester tester) async { + testWidgetsWithLeakTracking('GridView crossAxisSpacing', (WidgetTester tester) async { // Regression test for https://github.com/flutter/flutter/issues/27151. final Key target = UniqueKey(); @@ -607,7 +608,7 @@ void main() { expect(tester.getBottomRight(find.byKey(target)), const Offset(800.0, 194.0)); }); - testWidgets('GridView does not cache itemBuilder calls', (WidgetTester tester) async { + testWidgetsWithLeakTracking('GridView does not cache itemBuilder calls', (WidgetTester tester) async { final Map counters = {}; await tester.pumpWidget(Directionality( @@ -642,7 +643,7 @@ void main() { expect(counters[4], 2); }); - testWidgets('GridView does not report visual overflow unnecessarily', (WidgetTester tester) async { + testWidgetsWithLeakTracking('GridView does not report visual overflow unnecessarily', (WidgetTester tester) async { await tester.pumpWidget( Directionality( textDirection: TextDirection.ltr, @@ -663,9 +664,18 @@ void main() { final TestClipPaintingContext context = TestClipPaintingContext(); renderObject.paint(context, Offset.zero); expect(context.clipBehavior, equals(Clip.none)); - }); + }, + leakTrackingTestConfig: const LeakTrackingTestConfig( + // TODO(ksokolovskyi): remove after fixing + notDisposedAllowList: { + // https://github.com/flutter/flutter/issues/134575 + 'OffsetLayer': 1, + // https://github.com/flutter/flutter/issues/134572 + 'ContainerLayer': 1, + }, + )); - testWidgets('GridView respects clipBehavior', (WidgetTester tester) async { + testWidgetsWithLeakTracking('GridView respects clipBehavior', (WidgetTester tester) async { await tester.pumpWidget( Directionality( textDirection: TextDirection.ltr, @@ -729,9 +739,14 @@ void main() { // 4th, check that a non-default clip behavior can be sent to the painting context. renderObject.paint(context, Offset.zero); expect(context.clipBehavior, equals(Clip.antiAlias)); - }); + }, + leakTrackingTestConfig: const LeakTrackingTestConfig( + // TODO(ksokolovskyi): remove after fixing + // https://github.com/flutter/flutter/issues/134572 + notDisposedAllowList: {'ContainerLayer': 1}, + )); - testWidgets('GridView.builder respects clipBehavior', (WidgetTester tester) async { + testWidgetsWithLeakTracking('GridView.builder respects clipBehavior', (WidgetTester tester) async { await tester.pumpWidget( Directionality( textDirection: TextDirection.ltr, @@ -747,7 +762,7 @@ void main() { expect(renderObject.clipBehavior, equals(Clip.antiAlias)); }); - testWidgets('GridView.custom respects clipBehavior', (WidgetTester tester) async { + testWidgetsWithLeakTracking('GridView.custom respects clipBehavior', (WidgetTester tester) async { await tester.pumpWidget( Directionality( textDirection: TextDirection.ltr, @@ -765,7 +780,7 @@ void main() { expect(renderObject.clipBehavior, equals(Clip.antiAlias)); }); - testWidgets('GridView.count respects clipBehavior', (WidgetTester tester) async { + testWidgetsWithLeakTracking('GridView.count respects clipBehavior', (WidgetTester tester) async { await tester.pumpWidget( Directionality( textDirection: TextDirection.ltr, @@ -780,7 +795,7 @@ void main() { expect(renderObject.clipBehavior, equals(Clip.antiAlias)); }); - testWidgets('GridView.extent respects clipBehavior', (WidgetTester tester) async { + testWidgetsWithLeakTracking('GridView.extent respects clipBehavior', (WidgetTester tester) async { await tester.pumpWidget( Directionality( textDirection: TextDirection.ltr, @@ -795,7 +810,7 @@ void main() { expect(renderObject.clipBehavior, equals(Clip.antiAlias)); }); - testWidgets('SliverGridDelegateWithFixedCrossAxisCount mainAxisExtent works as expected', (WidgetTester tester) async { + testWidgetsWithLeakTracking('SliverGridDelegateWithFixedCrossAxisCount mainAxisExtent works as expected', (WidgetTester tester) async { const int crossAxisCount = 4; const double mainAxisExtent = 100.0; @@ -821,7 +836,7 @@ void main() { expect(tester.getSize(find.text('4')), equals(const Size(200.0, mainAxisExtent))); }); - testWidgets('SliverGridDelegateWithMaxCrossAxisExtent mainAxisExtent works as expected', (WidgetTester tester) async { + testWidgetsWithLeakTracking('SliverGridDelegateWithMaxCrossAxisExtent mainAxisExtent works as expected', (WidgetTester tester) async { const double maxCrossAxisExtent = 200.0; const double mainAxisExtent = 100.0; @@ -847,7 +862,7 @@ void main() { expect(tester.getSize(find.text('4')), equals(const Size(200.0, mainAxisExtent))); }); - testWidgets('SliverGridDelegateWithMaxCrossAxisExtent throws assertion error when maxCrossAxisExtent is 0', (WidgetTester tester) async { + testWidgetsWithLeakTracking('SliverGridDelegateWithMaxCrossAxisExtent throws assertion error when maxCrossAxisExtent is 0', (WidgetTester tester) async { const double maxCrossAxisExtent = 0; expect(() => Directionality( @@ -858,9 +873,10 @@ void main() { ), throwsAssertionError); }); - testWidgets('SliverGrid sets correct extent for null returning builder delegate', (WidgetTester tester) async { + testWidgetsWithLeakTracking('SliverGrid sets correct extent for null returning builder delegate', (WidgetTester tester) async { // Regression test for https://github.com/flutter/flutter/issues/130685 final ScrollController controller = ScrollController(); + addTearDown(controller.dispose); await tester.pumpWidget(Directionality( textDirection: TextDirection.ltr, child: GridView.builder( diff --git a/packages/flutter/test/widgets/heroes_test.dart b/packages/flutter/test/widgets/heroes_test.dart index 463f5ba99c8..46d26c1ea10 100644 --- a/packages/flutter/test/widgets/heroes_test.dart +++ b/packages/flutter/test/widgets/heroes_test.dart @@ -9,6 +9,7 @@ import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter/rendering.dart'; import 'package:flutter_test/flutter_test.dart'; +import 'package:leak_tracker_flutter_testing/leak_tracker_flutter_testing.dart'; import '../painting/image_test_utils.dart' show TestImageProvider; @@ -190,7 +191,7 @@ Future main() async { transitionFromUserGestures = false; }); - testWidgets('Heroes animate', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Heroes animate', (WidgetTester tester) async { await tester.pumpWidget(MaterialApp(routes: routes)); @@ -300,7 +301,7 @@ Future main() async { expect(find.byKey(thirdKey), isInCard); }); - testWidgets('Heroes still animate after hero controller is swapped.', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Heroes still animate after hero controller is swapped.', (WidgetTester tester) async { final GlobalKey key = GlobalKey(); final UniqueKey heroKey = UniqueKey(); await tester.pumpWidget( @@ -395,7 +396,7 @@ Future main() async { expect(find.byKey(heroKey), findsNothing); }); - testWidgets('Heroes animate should hide original hero', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Heroes animate should hide original hero', (WidgetTester tester) async { await tester.pumpWidget(MaterialApp(routes: routes)); // Checks initial state. expect(find.byKey(firstKey), isOnstage); @@ -418,7 +419,7 @@ Future main() async { expect(find.byKey(secondKey), isInCard); }); - testWidgets('Destination hero is rebuilt midflight', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Destination hero is rebuilt midflight', (WidgetTester tester) async { final MutatingRoute route = MutatingRoute(); await tester.pumpWidget(MaterialApp( @@ -443,7 +444,7 @@ Future main() async { await tester.pump(const Duration(seconds: 1)); }); - testWidgets('Heroes animation is fastOutSlowIn', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Heroes animation is fastOutSlowIn', (WidgetTester tester) async { await tester.pumpWidget(MaterialApp(routes: routes)); await tester.tap(find.text('two')); await tester.pump(); // begin navigation @@ -483,7 +484,7 @@ Future main() async { ); }); - testWidgets('Heroes are not interactive', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Heroes are not interactive', (WidgetTester tester) async { final List log = []; await tester.pumpWidget(MaterialApp( @@ -553,7 +554,7 @@ Future main() async { expect(log, equals(['bar'])); }); - testWidgets('Popping on first frame does not cause hero observer to crash', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Popping on first frame does not cause hero observer to crash', (WidgetTester tester) async { await tester.pumpWidget(MaterialApp( onGenerateRoute: (RouteSettings settings) { return MaterialPageRoute( @@ -574,7 +575,7 @@ Future main() async { await tester.pump(); // ...and removes it straight away (since it's already at 0.0) }); - testWidgets('Overlapping starting and ending a hero transition works ok', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Overlapping starting and ending a hero transition works ok', (WidgetTester tester) async { await tester.pumpWidget(MaterialApp( onGenerateRoute: (RouteSettings settings) { return MaterialPageRoute( @@ -603,7 +604,7 @@ Future main() async { await tester.pump(); }); - testWidgets('One route, two heroes, same tag, throws', (WidgetTester tester) async { + testWidgetsWithLeakTracking('One route, two heroes, same tag, throws', (WidgetTester tester) async { await tester.pumpWidget(MaterialApp( home: Material( child: ListView( @@ -658,7 +659,7 @@ Future main() async { ); }); - testWidgets('Hero push transition interrupted by a pop', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Hero push transition interrupted by a pop', (WidgetTester tester) async { await tester.pumpWidget(MaterialApp( routes: routes, )); @@ -723,7 +724,7 @@ Future main() async { expect(find.byKey(secondKey), findsNothing); }); - testWidgets('Hero pop transition interrupted by a push', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Hero pop transition interrupted by a push', (WidgetTester tester) async { await tester.pumpWidget( MaterialApp( routes: routes, @@ -800,7 +801,7 @@ Future main() async { expect(find.byKey(firstKey), findsNothing); }); - testWidgets('Destination hero disappears mid-flight', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Destination hero disappears mid-flight', (WidgetTester tester) async { const Key homeHeroKey = Key('home hero'); const Key routeHeroKey = Key('route hero'); bool routeIncludesHero = true; @@ -903,7 +904,7 @@ Future main() async { }); - testWidgets('Destination hero scrolls mid-flight', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Destination hero scrolls mid-flight', (WidgetTester tester) async { const Key homeHeroKey = Key('home hero'); const Key routeHeroKey = Key('route hero'); const Key routeContainerKey = Key('route hero container'); @@ -990,7 +991,7 @@ Future main() async { expect(finalHeroY, 75.0); // 100 less 25 for the scroll }); - testWidgets('Destination hero scrolls out of view mid-flight', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Destination hero scrolls out of view mid-flight', (WidgetTester tester) async { const Key homeHeroKey = Key('home hero'); const Key routeHeroKey = Key('route hero'); const Key routeContainerKey = Key('route hero container'); @@ -1067,7 +1068,7 @@ Future main() async { expect(find.byKey(routeHeroKey), findsNothing); }); - testWidgets('Aborted flight', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Aborted flight', (WidgetTester tester) async { // See https://github.com/flutter/flutter/issues/5798 const Key heroABKey = Key('AB hero'); const Key heroBCKey = Key('BC hero'); @@ -1202,7 +1203,7 @@ Future main() async { expect(tester.getTopLeft(find.byKey(heroBCKey)).dy, 0.0); }); - testWidgets('Stateful hero child state survives flight', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Stateful hero child state survives flight', (WidgetTester tester) async { final MaterialPageRoute route = MaterialPageRoute( builder: (BuildContext context) { return Material( @@ -1286,7 +1287,7 @@ Future main() async { }); - testWidgets('Hero createRectTween', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Hero createRectTween', (WidgetTester tester) async { RectTween createRectTween(Rect? begin, Rect? end) { return MaterialRectCenterArcTween(begin: begin, end: end); } @@ -1398,7 +1399,7 @@ Future main() async { expect(tester.getCenter(find.byKey(firstKey)), const Offset(50.0, 50.0)); }); - testWidgets('Hero createRectTween for Navigator that is not full screen', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Hero createRectTween for Navigator that is not full screen', (WidgetTester tester) async { // Regression test for https://github.com/flutter/flutter/issues/25272 RectTween createRectTween(Rect? begin, Rect? end) { @@ -1519,7 +1520,7 @@ Future main() async { }); - testWidgets('Pop interrupts push, reverses flight', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Pop interrupts push, reverses flight', (WidgetTester tester) async { await tester.pumpWidget(MaterialApp(routes: routes)); await tester.tap(find.text('twoInset')); await tester.pump(); // begin navigation from / to /twoInset. @@ -1612,7 +1613,7 @@ Future main() async { expect(tester.getTopLeft(find.byKey(firstKey)).dx, x0); }); - testWidgets('Can override flight shuttle in to hero', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Can override flight shuttle in to hero', (WidgetTester tester) async { await tester.pumpWidget(MaterialApp( home: Material( child: ListView( @@ -1656,7 +1657,7 @@ Future main() async { expect(find.text('baz'), findsOneWidget); }); - testWidgets('Can override flight shuttle in from hero', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Can override flight shuttle in from hero', (WidgetTester tester) async { await tester.pumpWidget(MaterialApp( home: Material( child: ListView( @@ -1699,7 +1700,7 @@ Future main() async { }); // Regression test for https://github.com/flutter/flutter/issues/77720. - testWidgets("toHero's shuttle builder over fromHero's shuttle builder", (WidgetTester tester) async { + testWidgetsWithLeakTracking("toHero's shuttle builder over fromHero's shuttle builder", (WidgetTester tester) async { await tester.pumpWidget(MaterialApp( home: Material( child: ListView( @@ -1752,7 +1753,7 @@ Future main() async { expect(find.text('toHero text'), findsOneWidget); }); - testWidgets('Can override flight launch pads', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Can override flight launch pads', (WidgetTester tester) async { await tester.pumpWidget(MaterialApp( home: Material( child: ListView( @@ -1799,7 +1800,7 @@ Future main() async { expect(find.text('Joker'), findsOneWidget); }); - testWidgets('Heroes do not transition on back gestures by default', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Heroes do not transition on back gestures by default', (WidgetTester tester) async { await tester.pumpWidget(MaterialApp( routes: routes, )); @@ -1838,7 +1839,7 @@ Future main() async { expect(find.byKey(secondKey), isInCard); }, variant: const TargetPlatformVariant({ TargetPlatform.iOS, TargetPlatform.macOS })); - testWidgets('Heroes can transition on gesture in one frame', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Heroes can transition on gesture in one frame', (WidgetTester tester) async { transitionFromUserGestures = true; await tester.pumpWidget(MaterialApp( routes: routes, @@ -1881,7 +1882,7 @@ Future main() async { expect(find.byKey(secondKey), findsNothing); }, variant: const TargetPlatformVariant({ TargetPlatform.iOS, TargetPlatform.macOS })); - testWidgets('Heroes animate should hide destination hero and display original hero in case of dismissed', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Heroes animate should hide destination hero and display original hero in case of dismissed', (WidgetTester tester) async { transitionFromUserGestures = true; await tester.pumpWidget(MaterialApp( routes: routes, @@ -1917,7 +1918,7 @@ Future main() async { expect(find.byKey(secondKey), isInCard); }, variant: const TargetPlatformVariant({ TargetPlatform.iOS, TargetPlatform.macOS })); - testWidgets('Handles transitions when a non-default initial route is set', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Handles transitions when a non-default initial route is set', (WidgetTester tester) async { await tester.pumpWidget(MaterialApp( routes: routes, initialRoute: '/two', @@ -1927,7 +1928,7 @@ Future main() async { expect(find.text('three'), findsOneWidget); }); - testWidgets('Can push/pop on outer Navigator if nested Navigator contains Heroes', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Can push/pop on outer Navigator if nested Navigator contains Heroes', (WidgetTester tester) async { // Regression test for https://github.com/flutter/flutter/issues/28042. const String heroTag = 'You are my hero!'; @@ -2001,7 +2002,7 @@ Future main() async { expect(find.byKey(nestedRouteHeroBottom, skipOffstage: false), findsOneWidget); }); - testWidgets('Can hero from route in root Navigator to route in nested Navigator', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Can hero from route in root Navigator to route in nested Navigator', (WidgetTester tester) async { const String heroTag = 'foo'; final GlobalKey rootNavigator = GlobalKey(); final Key smallContainer = UniqueKey(); @@ -2087,7 +2088,7 @@ Future main() async { expect(tester.getSize(find.byKey(smallContainer)), const Size(100,100)); }); - testWidgets('Hero within a Hero, throws', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Hero within a Hero, throws', (WidgetTester tester) async { await tester.pumpWidget( const MaterialApp( home: Material( @@ -2105,7 +2106,7 @@ Future main() async { expect(tester.takeException(), isAssertionError); }); - testWidgets('Can push/pop on outer Navigator if nested Navigators contains same Heroes', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Can push/pop on outer Navigator if nested Navigators contains same Heroes', (WidgetTester tester) async { const String heroTag = 'foo'; final GlobalKey rootNavigator = GlobalKey(); final Key rootRouteHero = UniqueKey(); @@ -2189,7 +2190,7 @@ Future main() async { expect(find.byKey(nestedRouteHeroOne, skipOffstage: false), findsOneWidget); }); - testWidgets('Hero within a Hero subtree, throws', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Hero within a Hero subtree, throws', (WidgetTester tester) async { await tester.pumpWidget( const MaterialApp( home: Material( @@ -2207,7 +2208,7 @@ Future main() async { expect(tester.takeException(), isAssertionError); }); - testWidgets('Hero within a Hero subtree with Builder, throws', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Hero within a Hero subtree with Builder, throws', (WidgetTester tester) async { await tester.pumpWidget( MaterialApp( home: Material( @@ -2229,7 +2230,7 @@ Future main() async { expect(tester.takeException(),isAssertionError); }); - testWidgets('Hero within a Hero subtree with LayoutBuilder, throws', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Hero within a Hero subtree with LayoutBuilder, throws', (WidgetTester tester) async { await tester.pumpWidget( MaterialApp( home: Material( @@ -2251,7 +2252,7 @@ Future main() async { expect(tester.takeException(), isAssertionError); }); - testWidgets('Heroes fly on pushReplacement', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Heroes fly on pushReplacement', (WidgetTester tester) async { // Regression test for https://github.com/flutter/flutter/issues/28041. const String heroTag = 'foo'; @@ -2338,7 +2339,7 @@ Future main() async { expect(tester.getSize(find.byKey(smallContainer)), const Size(100,100)); }); - testWidgets('On an iOS back swipe and snap, only a single flight should take place', (WidgetTester tester) async { + testWidgetsWithLeakTracking('On an iOS back swipe and snap, only a single flight should take place', (WidgetTester tester) async { int shuttlesBuilt = 0; Widget shuttleBuilder( BuildContext flightContext, @@ -2401,7 +2402,7 @@ Future main() async { expect(shuttlesBuilt, 2); }); - testWidgets( + testWidgetsWithLeakTracking( "From hero's state should be preserved, " 'heroes work well with child widgets that has global keys', (WidgetTester tester) async { @@ -2468,7 +2469,7 @@ Future main() async { }, ); - testWidgets( + testWidgetsWithLeakTracking( "Hero works with images that don't have both width and height specified", // Regression test for https://github.com/flutter/flutter/issues/32356 // and https://github.com/flutter/flutter/issues/31503 @@ -2556,7 +2557,7 @@ Future main() async { ); // Regression test for https://github.com/flutter/flutter/issues/38183. - testWidgets('Remove user gesture driven flights when the gesture is invalid', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Remove user gesture driven flights when the gesture is invalid', (WidgetTester tester) async { transitionFromUserGestures = true; await tester.pumpWidget(MaterialApp( routes: routes, @@ -2585,7 +2586,7 @@ Future main() async { }, variant: const TargetPlatformVariant({ TargetPlatform.iOS, TargetPlatform.macOS })); // Regression test for https://github.com/flutter/flutter/issues/40239. - testWidgets( + testWidgetsWithLeakTracking( 'In a pop transition, when fromHero is null, the to hero should eventually become visible', (WidgetTester tester) async { final GlobalKey navigatorKey = GlobalKey(); @@ -2634,7 +2635,7 @@ Future main() async { }, ); - testWidgets('popped hero uses fastOutSlowIn curve', (WidgetTester tester) async { + testWidgetsWithLeakTracking('popped hero uses fastOutSlowIn curve', (WidgetTester tester) async { final Key container1 = UniqueKey(); final Key container2 = UniqueKey(); final GlobalKey navigator = GlobalKey(); @@ -2712,7 +2713,7 @@ Future main() async { expect(heroSize, tween.transform(1.0)); }); - testWidgets('Heroes in enabled HeroMode do transition', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Heroes in enabled HeroMode do transition', (WidgetTester tester) async { await tester.pumpWidget(MaterialApp( home: Material( child: Column( @@ -2783,7 +2784,7 @@ Future main() async { expect(find.byKey(secondKey), isInCard); }); - testWidgets('Heroes in disabled HeroMode do not transition', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Heroes in disabled HeroMode do not transition', (WidgetTester tester) async { await tester.pumpWidget(MaterialApp( home: Material( child: Column( @@ -2861,7 +2862,7 @@ Future main() async { expect(find.byKey(secondKey), isOnstage); }); - testWidgets('kept alive Hero does not throw when the transition begins', (WidgetTester tester) async { + testWidgetsWithLeakTracking('kept alive Hero does not throw when the transition begins', (WidgetTester tester) async { final GlobalKey navigatorKey = GlobalKey(); await tester.pumpWidget( @@ -2914,9 +2915,10 @@ Future main() async { expect(find.byType(Placeholder), findsOneWidget); }); - testWidgets('toHero becomes unpaintable after the transition begins', (WidgetTester tester) async { + testWidgetsWithLeakTracking('toHero becomes unpaintable after the transition begins', (WidgetTester tester) async { final GlobalKey navigatorKey = GlobalKey(); final ScrollController controller = ScrollController(); + addTearDown(controller.dispose); RenderAnimatedOpacity? findRenderAnimatedOpacity() { RenderObject? parent = tester.renderObject(find.byType(Placeholder)); @@ -2989,7 +2991,7 @@ Future main() async { expect(find.byType(Placeholder), findsNothing); }); - testWidgets('diverting to a keepalive but unpaintable hero', (WidgetTester tester) async { + testWidgetsWithLeakTracking('diverting to a keepalive but unpaintable hero', (WidgetTester tester) async { final GlobalKey navigatorKey = GlobalKey(); await tester.pumpWidget( @@ -3069,7 +3071,7 @@ Future main() async { expect(tester.takeException(), isNull); }); - testWidgets('smooth transition between different incoming data', (WidgetTester tester) async { + testWidgetsWithLeakTracking('smooth transition between different incoming data', (WidgetTester tester) async { addTearDown(tester.view.reset); final GlobalKey navigatorKey = GlobalKey(); diff --git a/packages/flutter/test/widgets/hit_testing_test.dart b/packages/flutter/test/widgets/hit_testing_test.dart index bd4cd9377a9..39ff67cca89 100644 --- a/packages/flutter/test/widgets/hit_testing_test.dart +++ b/packages/flutter/test/widgets/hit_testing_test.dart @@ -6,16 +6,17 @@ import 'package:flutter/gestures.dart'; 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'; void main() { - testWidgets('toString control test', (WidgetTester tester) async { + testWidgetsWithLeakTracking('toString control test', (WidgetTester tester) async { await tester.pumpWidget(const Center(child: Text('Hello', textDirection: TextDirection.ltr))); final HitTestResult result = tester.hitTestOnBinding(Offset.zero); expect(result, hasOneLineDescription); expect(result.path.first, hasOneLineDescription); }); - testWidgets('A mouse click should only cause one hit test', (WidgetTester tester) async { + testWidgetsWithLeakTracking('A mouse click should only cause one hit test', (WidgetTester tester) async { int hitCount = 0; await tester.pumpWidget( _HitTestCounter( @@ -31,7 +32,7 @@ void main() { expect(hitCount, 1); }); - testWidgets('Non-mouse events should not cause movement hit tests', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Non-mouse events should not cause movement hit tests', (WidgetTester tester) async { int hitCount = 0; await tester.pumpWidget( _HitTestCounter( diff --git a/packages/flutter/test/widgets/hyperlink_test.dart b/packages/flutter/test/widgets/hyperlink_test.dart index 4e303befd1d..d35d45ed5b6 100644 --- a/packages/flutter/test/widgets/hyperlink_test.dart +++ b/packages/flutter/test/widgets/hyperlink_test.dart @@ -5,9 +5,10 @@ import 'package:flutter/gestures.dart'; import 'package:flutter/widgets.dart'; import 'package:flutter_test/flutter_test.dart'; +import 'package:leak_tracker_flutter_testing/leak_tracker_flutter_testing.dart'; void main() { - testWidgets('Can tap a hyperlink', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Can tap a hyperlink', (WidgetTester tester) async { bool didTapLeft = false; final TapGestureRecognizer tapLeft = TapGestureRecognizer() ..onTap = () { diff --git a/packages/flutter/test/widgets/icon_test.dart b/packages/flutter/test/widgets/icon_test.dart index 4b53d670a33..bdb690995ae 100644 --- a/packages/flutter/test/widgets/icon_test.dart +++ b/packages/flutter/test/widgets/icon_test.dart @@ -6,11 +6,12 @@ import 'dart:ui'; import 'package:flutter/material.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('Can set opacity for an Icon', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Can set opacity for an Icon', (WidgetTester tester) async { await tester.pumpWidget( const Directionality( textDirection: TextDirection.ltr, @@ -27,7 +28,7 @@ void main() { expect(text.text.style!.color, const Color(0xFF666666).withOpacity(0.5)); }); - testWidgets('Icon sizing - no theme, default size', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Icon sizing - no theme, default size', (WidgetTester tester) async { await tester.pumpWidget( const Directionality( textDirection: TextDirection.ltr, @@ -41,7 +42,7 @@ void main() { expect(renderObject.size, equals(const Size.square(24.0))); }); - testWidgets('Icon sizing - no theme, explicit size', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Icon sizing - no theme, explicit size', (WidgetTester tester) async { await tester.pumpWidget( const Directionality( textDirection: TextDirection.ltr, @@ -58,7 +59,7 @@ void main() { expect(renderObject.size, equals(const Size.square(96.0))); }); - testWidgets('Icon sizing - sized theme', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Icon sizing - sized theme', (WidgetTester tester) async { await tester.pumpWidget( const Directionality( textDirection: TextDirection.ltr, @@ -75,7 +76,7 @@ void main() { expect(renderObject.size, equals(const Size.square(36.0))); }); - testWidgets('Icon sizing - sized theme, explicit size', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Icon sizing - sized theme, explicit size', (WidgetTester tester) async { await tester.pumpWidget( const Directionality( textDirection: TextDirection.ltr, @@ -95,7 +96,7 @@ void main() { expect(renderObject.size, equals(const Size.square(48.0))); }); - testWidgets('Icon sizing - sizeless theme, default size', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Icon sizing - sizeless theme, default size', (WidgetTester tester) async { await tester.pumpWidget( const Directionality( textDirection: TextDirection.ltr, @@ -113,7 +114,7 @@ void main() { }); - testWidgets('Icon with custom font', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Icon with custom font', (WidgetTester tester) async { await tester.pumpWidget( const Directionality( textDirection: TextDirection.ltr, @@ -127,7 +128,7 @@ void main() { expect(richText.text.style!.fontFamily, equals('Roboto')); }); - testWidgets('Icon with custom fontFamilyFallback', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Icon with custom fontFamilyFallback', (WidgetTester tester) async { await tester.pumpWidget( const Directionality( textDirection: TextDirection.ltr, @@ -141,7 +142,7 @@ void main() { expect(richText.text.style!.fontFamilyFallback, equals(['FallbackFont'])); }); - testWidgets('Icon with semantic label', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Icon with semantic label', (WidgetTester tester) async { final SemanticsTester semantics = SemanticsTester(tester); await tester.pumpWidget( @@ -161,7 +162,7 @@ void main() { semantics.dispose(); }); - testWidgets('Null icon with semantic label', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Null icon with semantic label', (WidgetTester tester) async { final SemanticsTester semantics = SemanticsTester(tester); await tester.pumpWidget( @@ -181,7 +182,7 @@ void main() { semantics.dispose(); }); - testWidgets("Changing semantic label from null doesn't rebuild tree ", (WidgetTester tester) async { + testWidgetsWithLeakTracking("Changing semantic label from null doesn't rebuild tree ", (WidgetTester tester) async { await tester.pumpWidget( const Directionality( textDirection: TextDirection.ltr, @@ -212,7 +213,7 @@ void main() { expect(richText2, same(richText1)); }); - testWidgets('IconData comparison', (WidgetTester tester) async { + testWidgetsWithLeakTracking('IconData comparison', (WidgetTester tester) async { expect(const IconData(123), const IconData(123)); expect(const IconData(123), isNot(const IconData(123, matchTextDirection: true))); expect(const IconData(123), isNot(const IconData(123, fontFamily: 'f'))); @@ -225,7 +226,7 @@ void main() { }); - testWidgets('Fill, weight, grade, and optical size variations are passed', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Fill, weight, grade, and optical size variations are passed', (WidgetTester tester) async { await tester.pumpWidget( const Directionality( textDirection: TextDirection.ltr, @@ -258,7 +259,7 @@ void main() { ]); }); - testWidgets('Fill, weight, grade, and optical size can be set at the theme-level', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Fill, weight, grade, and optical size can be set at the theme-level', (WidgetTester tester) async { await tester.pumpWidget( const Directionality( textDirection: TextDirection.ltr, @@ -283,7 +284,7 @@ void main() { ]); }); - testWidgets('Theme-level fill, weight, grade, and optical size can be overridden', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Theme-level fill, weight, grade, and optical size can be overridden', (WidgetTester tester) async { await tester.pumpWidget( const Directionality( textDirection: TextDirection.ltr, diff --git a/packages/flutter/test/widgets/image_filter_quality_test.dart b/packages/flutter/test/widgets/image_filter_quality_test.dart index dcbeadfd438..5025b25e186 100644 --- a/packages/flutter/test/widgets/image_filter_quality_test.dart +++ b/packages/flutter/test/widgets/image_filter_quality_test.dart @@ -13,17 +13,18 @@ import 'dart:ui' as ui; 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'; void main() { - testWidgets('Image at default filterQuality', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Image at default filterQuality', (WidgetTester tester) async { await testImageQuality(tester, null); }); - testWidgets('Image at high filterQuality', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Image at high filterQuality', (WidgetTester tester) async { await testImageQuality(tester, ui.FilterQuality.high); }); - testWidgets('Image at none filterQuality', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Image at none filterQuality', (WidgetTester tester) async { await testImageQuality(tester, ui.FilterQuality.none); }); } diff --git a/packages/flutter/test/widgets/image_filter_test.dart b/packages/flutter/test/widgets/image_filter_test.dart index 29da67bae4d..0cad97b4c46 100644 --- a/packages/flutter/test/widgets/image_filter_test.dart +++ b/packages/flutter/test/widgets/image_filter_test.dart @@ -14,9 +14,10 @@ import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter/rendering.dart'; import 'package:flutter_test/flutter_test.dart'; +import 'package:leak_tracker_flutter_testing/leak_tracker_flutter_testing.dart'; void main() { - testWidgets('Image filter - blur', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Image filter - blur', (WidgetTester tester) async { await tester.pumpWidget( RepaintBoundary( child: ImageFiltered( @@ -31,7 +32,7 @@ void main() { ); }); - testWidgets('Image filter - blur with offset', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Image filter - blur with offset', (WidgetTester tester) async { final Key key = GlobalKey(); await tester.pumpWidget( RepaintBoundary( @@ -51,7 +52,7 @@ void main() { ); }); - testWidgets('Image filter - dilate', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Image filter - dilate', (WidgetTester tester) async { await tester.pumpWidget( RepaintBoundary( child: ImageFiltered( @@ -66,7 +67,7 @@ void main() { ); }, skip: kIsWeb); // https://github.com/flutter/flutter/issues/101874 - testWidgets('Image filter - erode', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Image filter - erode', (WidgetTester tester) async { await tester.pumpWidget( RepaintBoundary( child: ImageFiltered( @@ -82,7 +83,7 @@ void main() { ); }, skip: kIsWeb); // https://github.com/flutter/flutter/issues/101874 - testWidgets('Image filter - matrix', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Image filter - matrix', (WidgetTester tester) async { final ImageFilter matrix = ImageFilter.matrix(Float64List.fromList([ 0.5, 0.0, 0.0, 0.0, // 0.0, 0.5, 0.0, 0.0, // @@ -119,7 +120,7 @@ void main() { ); }); - testWidgets('Image filter - matrix with offset', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Image filter - matrix with offset', (WidgetTester tester) async { final Matrix4 matrix = Matrix4.rotationZ(pi / 18); final ImageFilter matrixFilter = ImageFilter.matrix(matrix.storage); final Key key = GlobalKey(); @@ -157,7 +158,7 @@ void main() { ); }); - testWidgets('Image filter - reuses its layer', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Image filter - reuses its layer', (WidgetTester tester) async { Future pumpWithSigma(double sigma) async { await tester.pumpWidget( RepaintBoundary( @@ -178,7 +179,7 @@ void main() { expect(renderObject.debugLayer, same(originalLayer)); }); - testWidgets('Image filter - enabled and disabled', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Image filter - enabled and disabled', (WidgetTester tester) async { Future pumpWithEnabledState(bool enabled) async { await tester.pumpWidget( RepaintBoundary( diff --git a/packages/flutter/test/widgets/image_headers_test.dart b/packages/flutter/test/widgets/image_headers_test.dart index f00f00fcdae..2d833f49e02 100644 --- a/packages/flutter/test/widgets/image_headers_test.dart +++ b/packages/flutter/test/widgets/image_headers_test.dart @@ -7,13 +7,14 @@ import 'dart:io'; import 'package:flutter/widgets.dart'; import 'package:flutter_test/flutter_test.dart'; +import 'package:leak_tracker_flutter_testing/leak_tracker_flutter_testing.dart'; import '../image_data.dart'; void main() { final MockHttpClient client = MockHttpClient(); - testWidgets('Headers', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Headers', (WidgetTester tester) async { HttpOverrides.runZoned>(() async { await tester.pumpWidget(Image.network( 'https://www.example.com/images/frame.png', diff --git a/packages/flutter/test/widgets/image_icon_test.dart b/packages/flutter/test/widgets/image_icon_test.dart index 0d4709406a9..2aa1d732903 100644 --- a/packages/flutter/test/widgets/image_icon_test.dart +++ b/packages/flutter/test/widgets/image_icon_test.dart @@ -5,6 +5,7 @@ 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 '../painting/mocks_for_image_cache.dart'; @@ -20,7 +21,7 @@ void main() { ); }); - testWidgets('ImageIcon sizing - no theme, default size', (WidgetTester tester) async { + testWidgetsWithLeakTracking('ImageIcon sizing - no theme, default size', (WidgetTester tester) async { await tester.pumpWidget( Center( child: ImageIcon(image), @@ -32,7 +33,7 @@ void main() { expect(find.byType(Image), findsOneWidget); }); - testWidgets('Icon opacity', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Icon opacity', (WidgetTester tester) async { await tester.pumpWidget( Center( child: IconTheme( @@ -45,7 +46,7 @@ void main() { expect(tester.widget(find.byType(Image)).color!.alpha, equals(128)); }); - testWidgets('ImageIcon sizing - no theme, explicit size', (WidgetTester tester) async { + testWidgetsWithLeakTracking('ImageIcon sizing - no theme, explicit size', (WidgetTester tester) async { await tester.pumpWidget( const Center( child: ImageIcon( @@ -59,7 +60,7 @@ void main() { expect(renderObject.size, equals(const Size.square(96.0))); }); - testWidgets('ImageIcon sizing - sized theme', (WidgetTester tester) async { + testWidgetsWithLeakTracking('ImageIcon sizing - sized theme', (WidgetTester tester) async { await tester.pumpWidget( const Center( child: IconTheme( @@ -73,7 +74,7 @@ void main() { expect(renderObject.size, equals(const Size.square(36.0))); }); - testWidgets('ImageIcon sizing - sized theme, explicit size', (WidgetTester tester) async { + testWidgetsWithLeakTracking('ImageIcon sizing - sized theme, explicit size', (WidgetTester tester) async { await tester.pumpWidget( const Center( child: IconTheme( @@ -90,7 +91,7 @@ void main() { expect(renderObject.size, equals(const Size.square(48.0))); }); - testWidgets('ImageIcon sizing - sizeless theme, default size', (WidgetTester tester) async { + testWidgetsWithLeakTracking('ImageIcon sizing - sizeless theme, default size', (WidgetTester tester) async { await tester.pumpWidget( const Center( child: IconTheme( @@ -104,7 +105,7 @@ void main() { expect(renderObject.size, equals(const Size.square(24.0))); }); - testWidgets('ImageIcon has semantics data', (WidgetTester tester) async { + testWidgetsWithLeakTracking('ImageIcon has semantics data', (WidgetTester tester) async { final SemanticsHandle handle = tester.ensureSemantics(); await tester.pumpWidget( const Directionality( diff --git a/packages/flutter/test/widgets/image_resolution_test.dart b/packages/flutter/test/widgets/image_resolution_test.dart index 46f0478058d..afacf9ae922 100644 --- a/packages/flutter/test/widgets/image_resolution_test.dart +++ b/packages/flutter/test/widgets/image_resolution_test.dart @@ -13,6 +13,7 @@ import 'package:flutter/rendering.dart'; import 'package:flutter/services.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 '../image_data.dart'; @@ -155,6 +156,7 @@ void main() { const String image = 'assets/image.png'; final Map images = {}; + setUpAll(() async { for (final double scale in const [0.5, 1.0, 1.5, 2.0, 4.0, 10.0]) { final int dimension = (48 * scale).floor(); @@ -162,7 +164,13 @@ void main() { } }); - testWidgets('Image for device pixel ratio 1.0', (WidgetTester tester) async { + tearDownAll(() { + for (final ui.Image image in images.values) { + image.dispose(); + } + }); + + testWidgetsWithLeakTracking('Image for device pixel ratio 1.0', (WidgetTester tester) async { const double ratio = 1.0; Key key = GlobalKey(); await pumpTreeToLayout(tester, buildImageAtRatio(image, key, ratio, false, images)); @@ -174,7 +182,7 @@ void main() { expect(getRenderImage(tester, key).scale, 1.0); }); - testWidgets('Image for device pixel ratio 0.5', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Image for device pixel ratio 0.5', (WidgetTester tester) async { const double ratio = 0.5; Key key = GlobalKey(); await pumpTreeToLayout(tester, buildImageAtRatio(image, key, ratio, false, images)); @@ -186,7 +194,7 @@ void main() { expect(getRenderImage(tester, key).scale, 1.0); }); - testWidgets('Image for device pixel ratio 1.5', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Image for device pixel ratio 1.5', (WidgetTester tester) async { const double ratio = 1.5; Key key = GlobalKey(); await pumpTreeToLayout(tester, buildImageAtRatio(image, key, ratio, false, images)); @@ -201,7 +209,7 @@ void main() { // A 1.75 DPR screen is typically a low-resolution screen, such that physical // pixels are visible to the user. For such screens we prefer to pick the // higher resolution image, if available. - testWidgets('Image for device pixel ratio 1.75', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Image for device pixel ratio 1.75', (WidgetTester tester) async { const double ratio = 1.75; Key key = GlobalKey(); await pumpTreeToLayout(tester, buildImageAtRatio(image, key, ratio, false, images)); @@ -213,7 +221,7 @@ void main() { expect(getRenderImage(tester, key).scale, 2.0); }); - testWidgets('Image for device pixel ratio 2.3', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Image for device pixel ratio 2.3', (WidgetTester tester) async { const double ratio = 2.3; Key key = GlobalKey(); await pumpTreeToLayout(tester, buildImageAtRatio(image, key, ratio, false, images)); @@ -225,7 +233,7 @@ void main() { expect(getRenderImage(tester, key).scale, 2.0); }); - testWidgets('Image for device pixel ratio 3.7', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Image for device pixel ratio 3.7', (WidgetTester tester) async { const double ratio = 3.7; Key key = GlobalKey(); await pumpTreeToLayout(tester, buildImageAtRatio(image, key, ratio, false, images)); @@ -237,7 +245,7 @@ void main() { expect(getRenderImage(tester, key).scale, 4.0); }); - testWidgets('Image for device pixel ratio 5.1', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Image for device pixel ratio 5.1', (WidgetTester tester) async { const double ratio = 5.1; Key key = GlobalKey(); await pumpTreeToLayout(tester, buildImageAtRatio(image, key, ratio, false, images)); @@ -249,7 +257,7 @@ void main() { expect(getRenderImage(tester, key).scale, 4.0); }); - testWidgets('Image for device pixel ratio 1.0, with a main asset and a 1.0x asset', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Image for device pixel ratio 1.0, with a main asset and a 1.0x asset', (WidgetTester tester) async { // If both a main asset and a 1.0x asset are specified, then prefer // the 1.0x asset. @@ -279,19 +287,19 @@ void main() { expect(getRenderImage(tester, key).image!.height, 480); }); - testWidgets('Image cache resize upscale display 5', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Image cache resize upscale display 5', (WidgetTester tester) async { final Key key = GlobalKey(); await pumpTreeToLayout(tester, buildImageCacheResized(image, key, 5, 5, 20, 20)); expect(getRenderImage(tester, key).size, const Size(5.0, 5.0)); }); - testWidgets('Image cache resize upscale display 50', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Image cache resize upscale display 50', (WidgetTester tester) async { final Key key = GlobalKey(); await pumpTreeToLayout(tester, buildImageCacheResized(image, key, 50, 50, 20, 20)); expect(getRenderImage(tester, key).size, const Size(50.0, 50.0)); }); - testWidgets('Image cache resize downscale display 5', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Image cache resize downscale display 5', (WidgetTester tester) async { final Key key = GlobalKey(); await pumpTreeToLayout(tester, buildImageCacheResized(image, key, 5, 5, 1, 1)); expect(getRenderImage(tester, key).size, const Size(5.0, 5.0)); @@ -301,7 +309,7 @@ void main() { // visible physical pixel size (see the test for 1.75 DPR above). However, // if higher resolution assets are not available we will pick the best // available. - testWidgets('Low-resolution assets', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Low-resolution assets', (WidgetTester tester) async { const Map manifest = { 'assets/image.png': >[ {'asset': 'assets/image.png'}, diff --git a/packages/flutter/test/widgets/image_rtl_test.dart b/packages/flutter/test/widgets/image_rtl_test.dart index 6f2c075a00e..905ba88d4aa 100644 --- a/packages/flutter/test/widgets/image_rtl_test.dart +++ b/packages/flutter/test/widgets/image_rtl_test.dart @@ -7,6 +7,7 @@ import 'dart:ui' as ui show Image; 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'; class TestImageProvider extends ImageProvider { const TestImageProvider(this.image); @@ -28,10 +29,16 @@ class TestImageProvider extends ImageProvider { void main() { late ui.Image testImage; + setUpAll(() async { testImage = await createTestImage(width: 16, height: 9); }); - testWidgets('DecorationImage RTL with alignment topEnd and match', (WidgetTester tester) async { + + tearDownAll(() { + testImage.dispose(); + }); + + testWidgetsWithLeakTracking('DecorationImage RTL with alignment topEnd and match', (WidgetTester tester) async { await tester.pumpWidget( Directionality( textDirection: TextDirection.rtl, @@ -70,7 +77,7 @@ void main() { expect(find.byType(Container), isNot(paints..scale()..scale())); }); - testWidgets('DecorationImage LTR with alignment topEnd (and pointless match)', (WidgetTester tester) async { + testWidgetsWithLeakTracking('DecorationImage LTR with alignment topEnd (and pointless match)', (WidgetTester tester) async { await tester.pumpWidget( Directionality( textDirection: TextDirection.ltr, @@ -106,7 +113,7 @@ void main() { expect(find.byType(Container), isNot(paints..scale())); }); - testWidgets('DecorationImage RTL with alignment topEnd', (WidgetTester tester) async { + testWidgetsWithLeakTracking('DecorationImage RTL with alignment topEnd', (WidgetTester tester) async { await tester.pumpWidget( Directionality( textDirection: TextDirection.rtl, @@ -141,7 +148,7 @@ void main() { expect(find.byType(Container), isNot(paints..scale())); }); - testWidgets('DecorationImage LTR with alignment topEnd', (WidgetTester tester) async { + testWidgetsWithLeakTracking('DecorationImage LTR with alignment topEnd', (WidgetTester tester) async { await tester.pumpWidget( Directionality( textDirection: TextDirection.ltr, @@ -176,7 +183,7 @@ void main() { expect(find.byType(Container), isNot(paints..scale())); }); - testWidgets('DecorationImage RTL with alignment center-right and match', (WidgetTester tester) async { + testWidgetsWithLeakTracking('DecorationImage RTL with alignment center-right and match', (WidgetTester tester) async { await tester.pumpWidget( Directionality( textDirection: TextDirection.rtl, @@ -208,7 +215,7 @@ void main() { expect(find.byType(Container), isNot(paints..drawImageRect()..drawImageRect())); }); - testWidgets('DecorationImage RTL with alignment center-right and no match', (WidgetTester tester) async { + testWidgetsWithLeakTracking('DecorationImage RTL with alignment center-right and no match', (WidgetTester tester) async { await tester.pumpWidget( Directionality( textDirection: TextDirection.rtl, @@ -235,7 +242,7 @@ void main() { expect(find.byType(Container), isNot(paints..drawImageRect()..drawImageRect())); }); - testWidgets('DecorationImage LTR with alignment center-right and match', (WidgetTester tester) async { + testWidgetsWithLeakTracking('DecorationImage LTR with alignment center-right and match', (WidgetTester tester) async { await tester.pumpWidget( Directionality( textDirection: TextDirection.ltr, @@ -263,7 +270,7 @@ void main() { expect(find.byType(Container), isNot(paints..drawImageRect()..drawImageRect())); }); - testWidgets('DecorationImage LTR with alignment center-right and no match', (WidgetTester tester) async { + testWidgetsWithLeakTracking('DecorationImage LTR with alignment center-right and no match', (WidgetTester tester) async { await tester.pumpWidget( Directionality( textDirection: TextDirection.ltr, @@ -291,7 +298,7 @@ void main() { expect(find.byType(Container), isNot(paints..drawImageRect()..drawImageRect())); }); - testWidgets('Image RTL with alignment topEnd and match', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Image RTL with alignment topEnd and match', (WidgetTester tester) async { await tester.pumpWidget( Directionality( textDirection: TextDirection.rtl, @@ -328,7 +335,7 @@ void main() { expect(find.byType(SizedBox), isNot(paints..scale()..scale())); }); - testWidgets('Image LTR with alignment topEnd (and pointless match)', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Image LTR with alignment topEnd (and pointless match)', (WidgetTester tester) async { await tester.pumpWidget( Directionality( textDirection: TextDirection.ltr, @@ -362,7 +369,7 @@ void main() { expect(find.byType(SizedBox), isNot(paints..scale())); }); - testWidgets('Image RTL with alignment topEnd', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Image RTL with alignment topEnd', (WidgetTester tester) async { await tester.pumpWidget( Directionality( textDirection: TextDirection.rtl, @@ -395,7 +402,7 @@ void main() { expect(find.byType(SizedBox), isNot(paints..scale())); }); - testWidgets('Image LTR with alignment topEnd', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Image LTR with alignment topEnd', (WidgetTester tester) async { await tester.pumpWidget( Directionality( textDirection: TextDirection.ltr, @@ -428,7 +435,7 @@ void main() { expect(find.byType(SizedBox), isNot(paints..scale())); }); - testWidgets('Image RTL with alignment center-right and match', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Image RTL with alignment center-right and match', (WidgetTester tester) async { await tester.pumpWidget( Directionality( textDirection: TextDirection.rtl, @@ -456,7 +463,7 @@ void main() { expect(find.byType(SizedBox), isNot(paints..drawImageRect()..drawImageRect())); }); - testWidgets('Image RTL with alignment center-right and no match', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Image RTL with alignment center-right and no match', (WidgetTester tester) async { await tester.pumpWidget( Directionality( textDirection: TextDirection.rtl, @@ -481,7 +488,7 @@ void main() { expect(find.byType(SizedBox), isNot(paints..drawImageRect()..drawImageRect())); }); - testWidgets('Image LTR with alignment center-right and match', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Image LTR with alignment center-right and match', (WidgetTester tester) async { await tester.pumpWidget( Directionality( textDirection: TextDirection.ltr, @@ -507,7 +514,7 @@ void main() { expect(find.byType(SizedBox), isNot(paints..drawImageRect()..drawImageRect())); }); - testWidgets('Image LTR with alignment center-right and no match', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Image LTR with alignment center-right and no match', (WidgetTester tester) async { await tester.pumpWidget( Directionality( textDirection: TextDirection.ltr, @@ -533,7 +540,7 @@ void main() { expect(find.byType(SizedBox), isNot(paints..drawImageRect()..drawImageRect())); }); - testWidgets('Image - Switch needing direction', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Image - Switch needing direction', (WidgetTester tester) async { await tester.pumpWidget( Directionality( textDirection: TextDirection.ltr,