Unverified Commit 5a80f8d6 authored by Polina Cherkasova's avatar Polina Cherkasova Committed by GitHub

Define testWidgetsWithLeakTracking. (#125063)

parent fde717da
......@@ -16,6 +16,8 @@ void main() {
* because [matchesGoldenFile] does not use Skia Gold in its native package.
*/
// TODO(polina-c): fix ValueNotifier not disposed and switch to testWidgetsWithLeakTracking.
// https://github.com/flutter/devtools/issues/3951
testWidgets('correctly records frames using display', (WidgetTester tester) async {
final AnimationSheetBuilder builder = AnimationSheetBuilder(frameSize: _DecuplePixels.size);
......@@ -52,6 +54,8 @@ void main() {
await expectLater(find.byWidget(display), matchesGoldenFile('test.animation_sheet_builder.records.png'));
}, skip: isBrowser); // https://github.com/flutter/flutter/issues/56001
// TODO(polina-c): fix ValueNotifier not disposed and switch to testWidgetsWithLeakTracking.
// https://github.com/flutter/devtools/issues/3951
testWidgets('correctly wraps a row', (WidgetTester tester) async {
final AnimationSheetBuilder builder = AnimationSheetBuilder(frameSize: _DecuplePixels.size);
......@@ -70,6 +74,8 @@ void main() {
await expectLater(find.byWidget(display), matchesGoldenFile('test.animation_sheet_builder.wraps.png'));
}, skip: isBrowser); // https://github.com/flutter/flutter/issues/56001
// TODO(polina-c): fix Picture and Image not disposed and and switch to testWidgetsWithLeakTracking.
// https://github.com/flutter/devtools/issues/3951
testWidgets('correctly records frames using collate', (WidgetTester tester) async {
final AnimationSheetBuilder builder = AnimationSheetBuilder(frameSize: _DecuplePixels.size);
......@@ -104,6 +110,8 @@ void main() {
);
}, skip: isBrowser); // https://github.com/flutter/flutter/issues/56001
// TODO(polina-c): fix Picture and Image not disposed and switch to testWidgetsWithLeakTracking.
// https://github.com/flutter/devtools/issues/3951
testWidgets('use allLayers to record out-of-subtree contents', (WidgetTester tester) async {
final AnimationSheetBuilder builder = AnimationSheetBuilder(
frameSize: const Size(8, 2),
......
......@@ -6,8 +6,10 @@ import 'package:flutter/animation.dart';
import 'package:flutter/widgets.dart';
import 'package:flutter_test/flutter_test.dart';
import '../foundation/leak_tracking.dart';
void main() {
testWidgets('awaiting animation controllers - using direct future', (WidgetTester tester) async {
testWidgetsWithLeakTracking('awaiting animation controllers - using direct future', (WidgetTester tester) async {
final AnimationController controller1 = AnimationController(
duration: const Duration(milliseconds: 100),
vsync: const TestVSync(),
......@@ -56,7 +58,7 @@ void main() {
expect(log, <String>['start', 'a', 'b', 'c', 'd', 'end']);
});
testWidgets('awaiting animation controllers - using orCancel', (WidgetTester tester) async {
testWidgetsWithLeakTracking('awaiting animation controllers - using orCancel', (WidgetTester tester) async {
final AnimationController controller1 = AnimationController(
duration: const Duration(milliseconds: 100),
vsync: const TestVSync(),
......@@ -105,7 +107,7 @@ void main() {
expect(log, <String>['start', 'a', 'b', 'c', 'd', 'end']);
});
testWidgets('awaiting animation controllers and failing', (WidgetTester tester) async {
testWidgetsWithLeakTracking('awaiting animation controllers and failing', (WidgetTester tester) async {
final AnimationController controller1 = AnimationController(
duration: const Duration(milliseconds: 100),
vsync: const TestVSync(),
......@@ -133,7 +135,7 @@ void main() {
expect(log, <String>['start', 'caught', 'end']);
});
testWidgets('creating orCancel future later', (WidgetTester tester) async {
testWidgetsWithLeakTracking('creating orCancel future later', (WidgetTester tester) async {
final AnimationController controller1 = AnimationController(
duration: const Duration(milliseconds: 100),
vsync: const TestVSync(),
......@@ -146,7 +148,7 @@ void main() {
expect(true, isTrue); // should reach here
});
testWidgets('creating orCancel future later', (WidgetTester tester) async {
testWidgetsWithLeakTracking('creating orCancel future later', (WidgetTester tester) async {
final AnimationController controller1 = AnimationController(
duration: const Duration(milliseconds: 100),
vsync: const TestVSync(),
......@@ -163,7 +165,7 @@ void main() {
expect(ok, isTrue); // should reach here
});
testWidgets('TickerFuture is a Future', (WidgetTester tester) async {
testWidgetsWithLeakTracking('TickerFuture is a Future', (WidgetTester tester) async {
final AnimationController controller1 = AnimationController(
duration: const Duration(milliseconds: 100),
vsync: const TestVSync(),
......
......@@ -5,6 +5,8 @@
import 'package:flutter/widgets.dart';
import 'package:flutter_test/flutter_test.dart';
import '../foundation/leak_tracking.dart';
void main() {
setUp(() {
WidgetsFlutterBinding.ensureInitialized();
......@@ -79,7 +81,7 @@ void main() {
controller.dispose();
});
testWidgets('AnimationController with throwing listener', (WidgetTester tester) async {
testWidgetsWithLeakTracking('AnimationController with throwing listener', (WidgetTester tester) async {
final AnimationController controller = AnimationController(
duration: const Duration(milliseconds: 100),
vsync: const TestVSync(),
......@@ -102,7 +104,7 @@ void main() {
log.clear();
});
testWidgets('AnimationController with throwing status listener', (WidgetTester tester) async {
testWidgetsWithLeakTracking('AnimationController with throwing status listener', (WidgetTester tester) async {
final AnimationController controller = AnimationController(
duration: const Duration(milliseconds: 100),
vsync: const TestVSync(),
......
......@@ -10,6 +10,8 @@ library;
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import '../foundation/leak_tracking.dart';
void main() {
/*
* Here lies golden tests for packages/flutter_test/lib/src/binding.dart
......@@ -18,7 +20,7 @@ void main() {
LiveTestWidgetsFlutterBinding().framePolicy = LiveTestWidgetsFlutterBindingFramePolicy.onlyPumps;
testWidgets('Should show event indicator for pointer events', (WidgetTester tester) async {
testWidgetsWithLeakTracking('Should show event indicator for pointer events', (WidgetTester tester) async {
final AnimationSheetBuilder animationSheet = AnimationSheetBuilder(frameSize: const Size(200, 200), allLayers: true);
final List<Offset> taps = <Offset>[];
Widget target({bool recording = true}) => Container(
......@@ -76,6 +78,8 @@ void main() {
// Currently skipped due to daily flake: https://github.com/flutter/flutter/issues/87588
}, skip: true); // Typically skip: isBrowser https://github.com/flutter/flutter/issues/42767
// TODO(polina-c): fix ValueNotifier not disposed and switch to testWidgetsWithLeakTracking.
// https://github.com/flutter/devtools/issues/3951
testWidgets('Should show event indicator for pointer events with setSurfaceSize', (WidgetTester tester) async {
final AnimationSheetBuilder animationSheet = AnimationSheetBuilder(frameSize: const Size(200, 200), allLayers: true);
final List<Offset> taps = <Offset>[];
......
......@@ -6,10 +6,14 @@ import 'dart:async';
import 'package:flutter/rendering.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:leak_tracker/leak_tracker.dart';
import '_goldens_io.dart'
if (dart.library.html) '_goldens_web.dart' as flutter_goldens;
/// Test configuration for each test library in this directory.
///
/// See https://api.flutter.dev/flutter/flutter_test/flutter_test-library.html.
Future<void> testExecutable(FutureOr<void> Function() testMain) {
// Enable checks because there are many implementations of [RenderBox] in this
// package can benefit from the additional validations.
......@@ -19,6 +23,8 @@ Future<void> testExecutable(FutureOr<void> Function() testMain) {
// receive the event.
WidgetController.hitTestWarningShouldBeFatal = true;
LeakTrackingTestConfig.warnForNonSupportedPlatforms = false;
// Enable golden file testing using Skia Gold.
return flutter_goldens.testExecutable(testMain);
}
......@@ -5,6 +5,8 @@
import 'package:flutter/foundation.dart';
import 'package:flutter_test/flutter_test.dart';
import 'leak_tracking.dart';
class TestNotifier extends ChangeNotifier {
void notify() {
notifyListeners();
......@@ -49,23 +51,25 @@ class Counter with ChangeNotifier {
}
void main() {
testWidgets('ChangeNotifier can not dispose in callback', (WidgetTester tester) async {
testWidgetsWithLeakTracking('ChangeNotifier can not dispose in callback', (WidgetTester tester) async {
final TestNotifier test = TestNotifier();
bool callbackDidFinish = false;
void foo() {
test.dispose();
callbackDidFinish = true;
}
test.addListener(foo);
test.notify();
final AssertionError error = tester.takeException() as AssertionError;
expect(error.toString().contains('dispose()'), isTrue);
// Make sure it crashes during dispose call.
expect(callbackDidFinish, isFalse);
test.dispose();
});
testWidgets('ChangeNotifier', (WidgetTester tester) async {
testWidgetsWithLeakTracking('ChangeNotifier', (WidgetTester tester) async {
final List<String> log = <String>[];
void listener() {
log.add('listener');
......@@ -147,6 +151,7 @@ void main() {
expect(log, <String>['badListener', 'listener1', 'listener2']);
expect(tester.takeException(), isArgumentError);
log.clear();
test.dispose();
});
test('ChangeNotifier with mutating listener', () {
......
......@@ -5,9 +5,75 @@
import 'package:flutter/foundation.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:leak_tracker/leak_tracker.dart';
import 'package:meta/meta.dart';
typedef LeaksObtainer = void Function(Leaks foundLeaks);
/// Set of objects, that does not hold the objects from garbage collection.
///
/// The objects are referenced by hash codes and can duplicate with low probability.
@visibleForTesting
class WeakSet {
final Set<String> _objectCodes = <String>{};
String _toCode(int hashCode, String type) => '$type-$hashCode';
void add(Object object) {
_objectCodes.add(_toCode(identityHashCode(object), object.runtimeType.toString()));
}
void addByCode(int hashCode, String type) {
_objectCodes.add(_toCode(hashCode, type));
}
bool contains(int hashCode, String type) {
final bool result = _objectCodes.contains(_toCode(hashCode, type));
return result;
}
}
/// Wrapper for [testWidgets] with memory leak tracking.
///
/// The method will fail if instrumented objects in [callback] are
/// garbage collected without being disposed.
///
/// More about leak tracking:
/// https://github.com/dart-lang/leak_tracker.
///
/// See https://github.com/flutter/devtools/issues/3951 for plans
/// on leak tracking.
@isTest
void testWidgetsWithLeakTracking(
String description,
WidgetTesterCallback callback, {
bool? skip,
Timeout? timeout,
bool semanticsEnabled = true,
TestVariant<Object?> variant = const DefaultTestVariant(),
dynamic tags,
LeakTrackingTestConfig leakTrackingConfig = const LeakTrackingTestConfig(),
}) {
Future<void> wrappedCallback(WidgetTester tester) async {
await _withFlutterLeakTracking(
() async => callback(tester),
tester,
leakTrackingConfig,
);
}
testWidgets(
description,
wrappedCallback,
skip: skip,
timeout: timeout,
semanticsEnabled: semanticsEnabled,
variant: variant,
tags: tags,
);
}
bool _webWarningPrinted = false;
/// Runs [callback] with leak tracking.
///
/// Wrapper for [withLeakTracking] with Flutter specific functionality.
///
/// The method will fail if wrapped code contains memory leaks.
......@@ -19,27 +85,20 @@ typedef LeaksObtainer = void Function(Leaks foundLeaks);
/// 1. Listens to [MemoryAllocations] events.
/// 2. Uses `tester.runAsync` for leak detection if [tester] is provided.
///
/// If you use [testWidgets], pass [tester] to avoid async issues in leak processing.
/// Pass null otherwise.
///
/// Pass [leaksObtainer] if you want to get leak information before
/// the method failure.
Future<void> withFlutterLeakTracking(
DartAsyncCallback callback, {
required WidgetTester? tester,
StackTraceCollectionConfig stackTraceCollectionConfig =
const StackTraceCollectionConfig(),
Duration? timeoutForFinalGarbageCollection,
LeaksObtainer? leaksObtainer,
}) async {
// The method is copied (with improvements) from
// `package:leak_tracker/test/test_infra/flutter_helpers.dart`.
// The method is not combined with [testWidgets], because the combining will
// impact VSCode's ability to recognize tests.
/// Pass [config] to troubleshoot or exempt leaks. See [LeakTrackingTestConfig]
/// for details.
Future<void> _withFlutterLeakTracking(
DartAsyncCallback callback,
WidgetTester tester,
LeakTrackingTestConfig config,
) async {
// Leak tracker does not work for web platform.
if (kIsWeb) {
final bool shouldPrintWarning = !_webWarningPrinted && LeakTrackingTestConfig.warnForNonSupportedPlatforms;
if (shouldPrintWarning) {
_webWarningPrinted = true;
debugPrint('Leak tracking is not supported on web platform.\nTo turn off this message, set `LeakTrackingTestConfig.warnForNonSupportedPlatforms` to false.');
}
await callback();
return;
}
......@@ -50,23 +109,59 @@ Future<void> withFlutterLeakTracking(
return TestAsyncUtils.guard<void>(() async {
MemoryAllocations.instance.addListener(flutterEventToLeakTracker);
final AsyncCodeRunner asyncCodeRunner = tester == null
? (DartAsyncCallback action) async => action()
: (DartAsyncCallback action) async => tester.runAsync(action);
Future<void> asyncCodeRunner(DartAsyncCallback action) async => tester.runAsync(action);
try {
final Leaks leaks = await withLeakTracking(
Leaks leaks = await withLeakTracking(
callback,
asyncCodeRunner: asyncCodeRunner,
stackTraceCollectionConfig: stackTraceCollectionConfig,
stackTraceCollectionConfig: config.stackTraceCollectionConfig,
shouldThrowOnLeaks: false,
timeoutForFinalGarbageCollection: timeoutForFinalGarbageCollection,
);
if (leaksObtainer != null) {
leaksObtainer(leaks);
leaks = LeakCleaner(config).clean(leaks);
if (leaks.total > 0) {
config.onLeaks?.call(leaks);
if (config.failTestOnLeaks) {
expect(leaks, isLeakFree);
}
}
expect(leaks, isLeakFree);
} finally {
MemoryAllocations.instance.removeListener(flutterEventToLeakTracker);
}
});
}
/// Cleans leaks that are allowed by [config].
@visibleForTesting
class LeakCleaner {
LeakCleaner(this.config);
final LeakTrackingTestConfig config;
Leaks clean(Leaks leaks) {
final Leaks result = Leaks(<LeakType, List<LeakReport>>{
for (LeakType leakType in leaks.byType.keys)
leakType: leaks.byType[leakType]!.where((LeakReport leak) => _shouldReportLeak(leakType, leak)).toList()
});
return result;
}
/// Returns true if [leak] should be reported as failure.
bool _shouldReportLeak(LeakType leakType, LeakReport leak) {
// Tracking for non-GCed is temporarily disabled.
// TODO(polina-c): turn on tracking for non-GCed after investigating existing leaks.
if (leakType != LeakType.notDisposed) {
return false;
}
switch (leakType) {
case LeakType.notDisposed:
return !config.notDisposedAllowList.contains(leak.type);
case LeakType.notGCed:
case LeakType.gcedLate:
return !config.notGCedAllowList.contains(leak.type);
}
}
}
// Copyright 2014 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'package:flutter/widgets.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:leak_tracker/leak_tracker.dart';
import 'leak_tracking.dart';
final String _leakTrackedClassName = '$_LeakTrackedClass';
Leaks _leaksOfAllTypes() => Leaks(<LeakType, List<LeakReport>> {
LeakType.notDisposed: <LeakReport>[LeakReport(code: 1, context: <String, dynamic>{}, type:'myNotDisposedClass', trackedClass: 'myTrackedClass')],
LeakType.notGCed: <LeakReport>[LeakReport(code: 2, context: <String, dynamic>{}, type:'myNotGCedClass', trackedClass: 'myTrackedClass')],
LeakType.gcedLate: <LeakReport>[LeakReport(code: 3, context: <String, dynamic>{}, type:'myGCedLateClass', trackedClass: 'myTrackedClass')],
});
Future<void> main() async {
test('Trivial $LeakCleaner returns only non-disposed leaks.', () {
final LeakCleaner leakCleaner = LeakCleaner(const LeakTrackingTestConfig());
final Leaks leaks = _leaksOfAllTypes();
final int leakTotal = leaks.total;
final Leaks cleanedLeaks = leakCleaner.clean(leaks);
expect(leaks.total, leakTotal);
expect(cleanedLeaks.total, 1);
});
group('Leak tracking works for non-web', () {
testWidgetsWithLeakTracking(
'Leak tracker respects all allow lists',
(WidgetTester tester) async {
await tester.pumpWidget(_StatelessLeakingWidget());
},
leakTrackingConfig: LeakTrackingTestConfig(
notDisposedAllowList: <String>{_leakTrackedClassName},
notGCedAllowList: <String>{_leakTrackedClassName},
),
);
group('Leak tracker respects notGCed allow lists', () {
// These tests cannot run inside other tests because test nesting is forbidden.
// So, `expect` happens outside the tests, in `tearDown`.
late Leaks leaks;
testWidgetsWithLeakTracking(
'when $_StatelessLeakingWidget leaks',
(WidgetTester tester) async {
await tester.pumpWidget(_StatelessLeakingWidget());
},
leakTrackingConfig: LeakTrackingTestConfig(
onLeaks: (Leaks theLeaks) {
leaks = theLeaks;
},
failTestOnLeaks: false,
notGCedAllowList: <String>{_leakTrackedClassName},
),
);
tearDown(() => _verifyLeaks(leaks, expectedNotDisposed: 1));
});
group('Leak tracker catches that', () {
// These tests cannot run inside other tests because test nesting is forbidden.
// So, `expect` happens outside the tests, in `tearDown`.
late Leaks leaks;
testWidgetsWithLeakTracking(
'$_StatelessLeakingWidget leaks',
(WidgetTester tester) async {
await tester.pumpWidget(_StatelessLeakingWidget());
},
leakTrackingConfig: LeakTrackingTestConfig(
onLeaks: (Leaks theLeaks) {
leaks = theLeaks;
},
failTestOnLeaks: false,
),
);
tearDown(() => _verifyLeaks(leaks, expectedNotDisposed: 1));
});
},
skip: isBrowser); // [intended] Leak detection is off for web.
testWidgetsWithLeakTracking('Leak tracking is no-op for web', (WidgetTester tester) async {
await tester.pumpWidget(_StatelessLeakingWidget());
},
skip: !isBrowser); // [intended] Leaks detection is off for web.
}
/// Verifies [leaks] contains expected number of leaks for [_LeakTrackedClass].
void _verifyLeaks(Leaks leaks, { int expectedNotDisposed = 0, int expectedNotGCed = 0 }) {
const String linkToLeakTracker = 'https://github.com/dart-lang/leak_tracker';
expect(
() => expect(leaks, isLeakFree),
throwsA(
predicate((Object? e) {
return e is TestFailure && e.toString().contains(linkToLeakTracker);
}),
),
);
_verifyLeakList(leaks.notDisposed, expectedNotDisposed);
_verifyLeakList(leaks.notGCed, expectedNotGCed);
}
void _verifyLeakList(List<LeakReport> list, int expectedCount){
expect(list.length, expectedCount);
for (final LeakReport leak in list) {
expect(leak.trackedClass, contains(_LeakTrackedClass.library));
expect(leak.trackedClass, contains(_leakTrackedClassName));
}
}
/// Storage to keep disposed objects, to generate not-gced leaks.
final List<_LeakTrackedClass> _notGcedStorage = <_LeakTrackedClass>[];
class _StatelessLeakingWidget extends StatelessWidget {
_StatelessLeakingWidget() {
// ignore: unused_local_variable, the variable is used to create non disposed leak
final _LeakTrackedClass notDisposed = _LeakTrackedClass();
_notGcedStorage.add(_LeakTrackedClass()..dispose());
}
@override
Widget build(BuildContext context) {
return const Placeholder();
}
}
class _LeakTrackedClass {
_LeakTrackedClass() {
dispatchObjectCreated(
library: library,
className: '$_LeakTrackedClass',
object: this,
);
}
static const String library = 'package:my_package/lib/src/my_lib.dart';
void dispose() {
dispatchObjectDisposed(object: this);
}
}
......@@ -6,8 +6,10 @@ import 'package:flutter/foundation.dart';
import 'package:flutter/gestures.dart';
import 'package:flutter_test/flutter_test.dart';
import '../foundation/leak_tracking.dart';
void main() {
testWidgets('debugPrintGestureArenaDiagnostics', (WidgetTester tester) async {
testWidgetsWithLeakTracking('debugPrintGestureArenaDiagnostics', (WidgetTester tester) async {
PointerEvent event;
debugPrintGestureArenaDiagnostics = true;
final DebugPrintCallback oldCallback = debugPrint;
......@@ -53,7 +55,7 @@ void main() {
debugPrint = oldCallback;
});
testWidgets('debugPrintRecognizerCallbacksTrace', (WidgetTester tester) async {
testWidgetsWithLeakTracking('debugPrintRecognizerCallbacksTrace', (WidgetTester tester) async {
PointerEvent event;
debugPrintRecognizerCallbacksTrace = true;
final DebugPrintCallback oldCallback = debugPrint;
......@@ -95,7 +97,7 @@ void main() {
debugPrint = oldCallback;
});
testWidgets('debugPrintGestureArenaDiagnostics and debugPrintRecognizerCallbacksTrace', (WidgetTester tester) async {
testWidgetsWithLeakTracking('debugPrintGestureArenaDiagnostics and debugPrintRecognizerCallbacksTrace', (WidgetTester tester) async {
PointerEvent event;
debugPrintGestureArenaDiagnostics = true;
debugPrintRecognizerCallbacksTrace = true;
......
......@@ -10,6 +10,8 @@ import 'package:flutter/scheduler.dart';
import 'package:flutter/widgets.dart';
import 'package:flutter_test/flutter_test.dart';
import '../foundation/leak_tracking.dart';
class TestResampleEventFlutterBinding extends AutomatedTestWidgetsFlutterBinding {
@override
SamplingClock? get debugSamplingClock => TestSamplingClock(this.clock);
......@@ -29,7 +31,7 @@ class TestSamplingClock implements SamplingClock {
void main() {
final TestWidgetsFlutterBinding binding = TestResampleEventFlutterBinding();
testWidgets('PointerEvent resampling on a widget', (WidgetTester tester) async {
testWidgetsWithLeakTracking('PointerEvent resampling on a widget', (WidgetTester tester) async {
assert(WidgetsBinding.instance == binding);
Duration currentTestFrameTime() => Duration(milliseconds: binding.clock.now().millisecondsSinceEpoch);
void requestFrame() => SchedulerBinding.instance.scheduleFrameCallback((_) {});
......@@ -124,7 +126,7 @@ void main() {
expect(events[3], isA<PointerUpEvent>());
});
testWidgets('Timer should be canceled when resampling stopped', (WidgetTester tester) async {
testWidgetsWithLeakTracking('Timer should be canceled when resampling stopped', (WidgetTester tester) async {
// A timer will be started when event's timeStamp is larger than sampleTime.
final ui.PointerDataPacket packet = ui.PointerDataPacket(
data: <ui.PointerData>[
......
......@@ -7,6 +7,8 @@ import 'dart:ui' as ui;
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import '../foundation/leak_tracking.dart';
class TestResult {
bool dragStarted = false;
bool dragUpdate = false;
......@@ -89,7 +91,7 @@ class NestedDraggableCase extends StatelessWidget {
}
void main() {
testWidgets('Scroll Views get the same ScrollConfiguration as GestureDetectors', (WidgetTester tester) async {
testWidgetsWithLeakTracking('Scroll Views get the same ScrollConfiguration as GestureDetectors', (WidgetTester tester) async {
tester.view.gestureSettings = const ui.GestureSettings(physicalTouchSlop: 4);
addTearDown(tester.view.reset);
......@@ -112,6 +114,8 @@ void main() {
expect(result.dragUpdate, true);
});
// TODO(polina-c): fix ValueNotifier not disposed and switch to testWidgetsWithLeakTracking.
// https://github.com/flutter/devtools/issues/3951
testWidgets('Scroll Views get the same ScrollConfiguration as Draggables', (WidgetTester tester) async {
tester.view.gestureSettings = const ui.GestureSettings(physicalTouchSlop: 4);
addTearDown(tester.view.reset);
......
......@@ -6,8 +6,10 @@ import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import '../foundation/leak_tracking.dart';
void main() {
testWidgets('kTouchSlop is evaluated in the global coordinate space when scaled up', (WidgetTester tester) async {
testWidgetsWithLeakTracking('kTouchSlop is evaluated in the global coordinate space when scaled up', (WidgetTester tester) async {
int doubleTapCount = 0;
final Key redContainer = UniqueKey();
......@@ -51,7 +53,7 @@ void main() {
expect(doubleTapCount, 0);
});
testWidgets('kTouchSlop is evaluated in the global coordinate space when scaled down', (WidgetTester tester) async {
testWidgetsWithLeakTracking('kTouchSlop is evaluated in the global coordinate space when scaled down', (WidgetTester tester) async {
int doubleTapCount = 0;
final Key redContainer = UniqueKey();
......
......@@ -6,8 +6,10 @@ import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import '../foundation/leak_tracking.dart';
void main() {
testWidgets('gets local coordinates', (WidgetTester tester) async {
testWidgetsWithLeakTracking('gets local coordinates', (WidgetTester tester) async {
int longPressCount = 0;
int longPressUpCount = 0;
final List<LongPressEndDetails> endDetails = <LongPressEndDetails>[];
......@@ -53,7 +55,7 @@ void main() {
expect(endDetails.single.globalPosition, const Offset(400, 300));
});
testWidgets('scaled up', (WidgetTester tester) async {
testWidgetsWithLeakTracking('scaled up', (WidgetTester tester) async {
int longPressCount = 0;
int longPressUpCount = 0;
final List<LongPressEndDetails> endDetails = <LongPressEndDetails>[];
......@@ -128,7 +130,7 @@ void main() {
expect(moveDetails.single.localOffsetFromOrigin, const Offset(0, 100.0 / 2.0));
});
testWidgets('scaled down', (WidgetTester tester) async {
testWidgetsWithLeakTracking('scaled down', (WidgetTester tester) async {
int longPressCount = 0;
int longPressUpCount = 0;
final List<LongPressEndDetails> endDetails = <LongPressEndDetails>[];
......
......@@ -8,9 +8,11 @@ import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import '../foundation/leak_tracking.dart';
void main() {
group('Horizontal', () {
testWidgets('gets local coordinates', (WidgetTester tester) async {
testWidgetsWithLeakTracking('gets local coordinates', (WidgetTester tester) async {
int dragCancelCount = 0;
final List<DragDownDetails> downDetails = <DragDownDetails>[];
final List<DragEndDetails> endDetails = <DragEndDetails>[];
......@@ -65,7 +67,7 @@ void main() {
);
});
testWidgets('kTouchSlop is evaluated in the global coordinate space when scaled up', (WidgetTester tester) async {
testWidgetsWithLeakTracking('kTouchSlop is evaluated in the global coordinate space when scaled up', (WidgetTester tester) async {
int dragCancelCount = 0;
final List<DragDownDetails> downDetails = <DragDownDetails>[];
final List<DragEndDetails> endDetails = <DragEndDetails>[];
......@@ -163,7 +165,7 @@ void main() {
updateDetails.clear();
});
testWidgets('kTouchSlop is evaluated in the global coordinate space when scaled down', (WidgetTester tester) async {
testWidgetsWithLeakTracking('kTouchSlop is evaluated in the global coordinate space when scaled down', (WidgetTester tester) async {
int dragCancelCount = 0;
final List<DragDownDetails> downDetails = <DragDownDetails>[];
final List<DragEndDetails> endDetails = <DragEndDetails>[];
......@@ -261,7 +263,7 @@ void main() {
updateDetails.clear();
});
testWidgets('kTouchSlop is evaluated in the global coordinate space when rotated 45 degrees', (WidgetTester tester) async {
testWidgetsWithLeakTracking('kTouchSlop is evaluated in the global coordinate space when rotated 45 degrees', (WidgetTester tester) async {
int dragCancelCount = 0;
final List<DragDownDetails> downDetails = <DragDownDetails>[];
final List<DragEndDetails> endDetails = <DragEndDetails>[];
......@@ -338,7 +340,7 @@ void main() {
});
group('Vertical', () {
testWidgets('gets local coordinates', (WidgetTester tester) async {
testWidgetsWithLeakTracking('gets local coordinates', (WidgetTester tester) async {
int dragCancelCount = 0;
final List<DragDownDetails> downDetails = <DragDownDetails>[];
final List<DragEndDetails> endDetails = <DragEndDetails>[];
......@@ -393,7 +395,7 @@ void main() {
);
});
testWidgets('kTouchSlop is evaluated in the global coordinate space when scaled up', (WidgetTester tester) async {
testWidgetsWithLeakTracking('kTouchSlop is evaluated in the global coordinate space when scaled up', (WidgetTester tester) async {
int dragCancelCount = 0;
final List<DragDownDetails> downDetails = <DragDownDetails>[];
final List<DragEndDetails> endDetails = <DragEndDetails>[];
......@@ -491,7 +493,7 @@ void main() {
updateDetails.clear();
});
testWidgets('kTouchSlop is evaluated in the global coordinate space when scaled down', (WidgetTester tester) async {
testWidgetsWithLeakTracking('kTouchSlop is evaluated in the global coordinate space when scaled down', (WidgetTester tester) async {
int dragCancelCount = 0;
final List<DragDownDetails> downDetails = <DragDownDetails>[];
final List<DragEndDetails> endDetails = <DragEndDetails>[];
......@@ -589,7 +591,7 @@ void main() {
updateDetails.clear();
});
testWidgets('kTouchSlop is evaluated in the global coordinate space when rotated 45 degrees', (WidgetTester tester) async {
testWidgetsWithLeakTracking('kTouchSlop is evaluated in the global coordinate space when rotated 45 degrees', (WidgetTester tester) async {
int dragCancelCount = 0;
final List<DragDownDetails> downDetails = <DragDownDetails>[];
final List<DragEndDetails> endDetails = <DragEndDetails>[];
......
......@@ -5,8 +5,10 @@
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import '../foundation/leak_tracking.dart';
void main() {
testWidgets('gets local coordinates', (WidgetTester tester) async {
testWidgetsWithLeakTracking('gets local coordinates', (WidgetTester tester) async {
final List<ScaleStartDetails> startDetails = <ScaleStartDetails>[];
final List<ScaleUpdateDetails> updateDetails = <ScaleUpdateDetails>[];
......
......@@ -6,8 +6,10 @@ import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import '../foundation/leak_tracking.dart';
void main() {
testWidgets('gets local coordinates', (WidgetTester tester) async {
testWidgetsWithLeakTracking('gets local coordinates', (WidgetTester tester) async {
int tapCount = 0;
int tapCancelCount = 0;
final List<TapDownDetails> downDetails = <TapDownDetails>[];
......@@ -48,7 +50,7 @@ void main() {
expect(upDetails.single.globalPosition, const Offset(400, 300));
});
testWidgets('kTouchSlop is evaluated in the global coordinate space when scaled up', (WidgetTester tester) async {
testWidgetsWithLeakTracking('kTouchSlop is evaluated in the global coordinate space when scaled up', (WidgetTester tester) async {
int tapCount = 0;
int tapCancelCount = 0;
final List<TapDownDetails> downDetails = <TapDownDetails>[];
......@@ -111,7 +113,7 @@ void main() {
expect(upDetails, isEmpty);
});
testWidgets('kTouchSlop is evaluated in the global coordinate space when scaled down', (WidgetTester tester) async {
testWidgetsWithLeakTracking('kTouchSlop is evaluated in the global coordinate space when scaled down', (WidgetTester tester) async {
int tapCount = 0;
int tapCancelCount = 0;
final List<TapDownDetails> downDetails = <TapDownDetails>[];
......
......@@ -11,12 +11,14 @@ import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
import 'package:flutter_test/flutter_test.dart';
import '../foundation/leak_tracking.dart';
void main() {
tearDown(() {
LicenseRegistry.reset();
});
testWidgets('Material3 has sentence case labels', (WidgetTester tester) async {
testWidgetsWithLeakTracking('Material3 has sentence case labels', (WidgetTester tester) async {
await tester.pumpWidget(MaterialApp(
theme: ThemeData(useMaterial3: true),
builder: (BuildContext context, Widget? child) {
......@@ -56,7 +58,7 @@ void main() {
expect(find.text('View licenses'), findsOneWidget);
});
testWidgets('AboutListTile control test', (WidgetTester tester) async {
testWidgetsWithLeakTracking('AboutListTile control test', (WidgetTester tester) async {
const FlutterLogo logo = FlutterLogo();
await tester.pumpWidget(
......@@ -138,7 +140,7 @@ void main() {
expect(find.text('Pirate license'), findsOneWidget);
});
testWidgets('About box logic defaults to executable name for app name', (WidgetTester tester) async {
testWidgetsWithLeakTracking('About box logic defaults to executable name for app name', (WidgetTester tester) async {
await tester.pumpWidget(
const MaterialApp(
title: 'flutter_tester',
......@@ -148,6 +150,8 @@ void main() {
expect(find.text('About flutter_tester'), findsOneWidget);
});
// TODO(polina-c): fix SnapshotController not disposed and switch to testWidgetsWithLeakTracking.
// https://github.com/flutter/devtools/issues/3951
testWidgets('LicensePage control test', (WidgetTester tester) async {
LicenseRegistry.addLicense(() {
return Stream<LicenseEntry>.fromIterable(<LicenseEntry>[
......@@ -199,6 +203,8 @@ void main() {
expect(find.text('Another license'), findsOneWidget);
});
// TODO(polina-c): fix SnapshotController not disposed and switch to testWidgetsWithLeakTracking.
// https://github.com/flutter/devtools/issues/3951
testWidgets('LicensePage control test with all properties', (WidgetTester tester) async {
const FlutterLogo logo = FlutterLogo();
......@@ -275,7 +281,7 @@ void main() {
expect(find.text('Another license'), findsOneWidget);
});
testWidgets('_PackageLicensePage title style without AppBarTheme', (WidgetTester tester) async {
testWidgetsWithLeakTracking('_PackageLicensePage title style without AppBarTheme', (WidgetTester tester) async {
LicenseRegistry.addLicense(() {
return Stream<LicenseEntry>.fromIterable(<LicenseEntry>[
const LicenseEntryWithLineBreaks(<String>['AAA'], 'BBB'),
......@@ -322,7 +328,7 @@ void main() {
expect(subtitle.style, subtitleTextStyle);
});
testWidgets('_PackageLicensePage title style with AppBarTheme', (WidgetTester tester) async {
testWidgetsWithLeakTracking('_PackageLicensePage title style with AppBarTheme', (WidgetTester tester) async {
LicenseRegistry.addLicense(() {
return Stream<LicenseEntry>.fromIterable(<LicenseEntry>[
const LicenseEntryWithLineBreaks(<String>['AAA'], 'BBB'),
......@@ -372,7 +378,7 @@ void main() {
expect(title.style, titleTextStyle);
});
testWidgets('LicensePage respects the notch', (WidgetTester tester) async {
testWidgetsWithLeakTracking('LicensePage respects the notch', (WidgetTester tester) async {
const double safeareaPadding = 27.0;
LicenseRegistry.addLicense(() {
......@@ -402,6 +408,8 @@ void main() {
);
});
// TODO(polina-c): fix SnapshotController not disposed and switch to testWidgetsWithLeakTracking.
// https://github.com/flutter/devtools/issues/3951
testWidgets('LicensePage returns early if unmounted', (WidgetTester tester) async {
final Completer<LicenseEntry> licenseCompleter = Completer<LicenseEntry>();
LicenseRegistry.addLicense(() {
......@@ -427,6 +435,8 @@ void main() {
expect(licenseEntry.packagesCalled, false);
});
// TODO(polina-c): fix SnapshotController not disposed and switch to testWidgetsWithLeakTracking.
// https://github.com/flutter/devtools/issues/3951
testWidgets('LicensePage returns late if unmounted', (WidgetTester tester) async {
final Completer<LicenseEntry> licenseCompleter = Completer<LicenseEntry>();
LicenseRegistry.addLicense(() {
......@@ -452,7 +462,7 @@ void main() {
expect(licenseEntry.packagesCalled, true);
});
testWidgets('LicensePage logic defaults to executable name for app name', (WidgetTester tester) async {
testWidgetsWithLeakTracking('LicensePage logic defaults to executable name for app name', (WidgetTester tester) async {
await tester.pumpWidget(
const MaterialApp(
title: 'flutter_tester',
......@@ -462,7 +472,7 @@ void main() {
expect(find.text('flutter_tester'), findsOneWidget);
});
testWidgets('AboutListTile dense property is applied', (WidgetTester tester) async {
testWidgetsWithLeakTracking('AboutListTile dense property is applied', (WidgetTester tester) async {
await tester.pumpWidget(const MaterialApp(
home: Material(child: Center(child: AboutListTile())),
));
......@@ -482,7 +492,7 @@ void main() {
expect(tileRect.height, 48.0);
});
testWidgets('showLicensePage uses nested navigator by default', (WidgetTester tester) async {
testWidgetsWithLeakTracking('showLicensePage uses nested navigator by default', (WidgetTester tester) async {
final LicensePageObserver rootObserver = LicensePageObserver();
final LicensePageObserver nestedObserver = LicensePageObserver();
......@@ -520,7 +530,7 @@ void main() {
expect(nestedObserver.licensePageCount, 1);
});
testWidgets('showLicensePage uses root navigator if useRootNavigator is true', (WidgetTester tester) async {
testWidgetsWithLeakTracking('showLicensePage uses root navigator if useRootNavigator is true', (WidgetTester tester) async {
final LicensePageObserver rootObserver = LicensePageObserver();
final LicensePageObserver nestedObserver = LicensePageObserver();
......@@ -559,7 +569,7 @@ void main() {
expect(nestedObserver.licensePageCount, 0);
});
testWidgets('showAboutDialog uses root navigator by default', (WidgetTester tester) async {
testWidgetsWithLeakTracking('showAboutDialog uses root navigator by default', (WidgetTester tester) async {
final AboutDialogObserver rootObserver = AboutDialogObserver();
final AboutDialogObserver nestedObserver = AboutDialogObserver();
......@@ -592,7 +602,7 @@ void main() {
expect(nestedObserver.dialogCount, 0);
});
testWidgets('showAboutDialog uses nested navigator if useRootNavigator is false', (WidgetTester tester) async {
testWidgetsWithLeakTracking('showAboutDialog uses nested navigator if useRootNavigator is false', (WidgetTester tester) async {
final AboutDialogObserver rootObserver = AboutDialogObserver();
final AboutDialogObserver nestedObserver = AboutDialogObserver();
......@@ -627,7 +637,7 @@ void main() {
});
group('showAboutDialog avoids overlapping display features', () {
testWidgets('default positioning', (WidgetTester tester) async {
testWidgetsWithLeakTracking('default positioning', (WidgetTester tester) async {
await tester.pumpWidget(MaterialApp(
builder: (BuildContext context, Widget? child) {
return MediaQuery(
......@@ -668,7 +678,7 @@ void main() {
expect(tester.getBottomRight(find.byType(AboutDialog)), const Offset(390.0, 600.0));
});
testWidgets('positioning using anchorPoint', (WidgetTester tester) async {
testWidgetsWithLeakTracking('positioning using anchorPoint', (WidgetTester tester) async {
await tester.pumpWidget(MaterialApp(
builder: (BuildContext context, Widget? child) {
return MediaQuery(
......@@ -710,7 +720,7 @@ void main() {
expect(tester.getBottomRight(find.byType(AboutDialog)), const Offset(800.0, 600.0));
});
testWidgets('positioning using Directionality', (WidgetTester tester) async {
testWidgetsWithLeakTracking('positioning using Directionality', (WidgetTester tester) async {
await tester.pumpWidget(MaterialApp(
builder: (BuildContext context, Widget? child) {
return MediaQuery(
......@@ -755,7 +765,7 @@ void main() {
});
});
testWidgets("AboutListTile's child should not be offset when the icon is not specified.", (WidgetTester tester) async {
testWidgetsWithLeakTracking("AboutListTile's child should not be offset when the icon is not specified.", (WidgetTester tester) async {
await tester.pumpWidget(
const MaterialApp(
home: Scaffold(
......@@ -775,7 +785,7 @@ void main() {
);
});
testWidgets("AboutDialog's contents are scrollable", (WidgetTester tester) async {
testWidgetsWithLeakTracking("AboutDialog's contents are scrollable", (WidgetTester tester) async {
final Key contentKey = UniqueKey();
await tester.pumpWidget(MaterialApp(
home: Navigator(
......@@ -823,7 +833,7 @@ void main() {
expect(box.localToGlobal(Offset.zero), equals(originalOffset.translate(0.0, -20.0)));
});
testWidgets("LicensePage's color must be same whether loading or done", (WidgetTester tester) async {
testWidgetsWithLeakTracking("LicensePage's color must be same whether loading or done", (WidgetTester tester) async {
const Color scaffoldColor = Color(0xFF123456);
const Color cardColor = Color(0xFF654321);
......@@ -870,7 +880,7 @@ void main() {
expect(materialDones[1].color, cardColor);
});
testWidgets('Conflicting scrollbars are not applied by ScrollBehavior to _PackageLicensePage', (WidgetTester tester) async {
testWidgetsWithLeakTracking('Conflicting scrollbars are not applied by ScrollBehavior to _PackageLicensePage', (WidgetTester tester) async {
// Regression test for https://github.com/flutter/flutter/issues/83819
LicenseRegistry.addLicense(() {
return Stream<LicenseEntry>.fromIterable(<LicenseEntry>[
......@@ -912,7 +922,7 @@ void main() {
}, variant: TargetPlatformVariant.all());
testWidgets('ListView of license entries is primary', (WidgetTester tester) async {
testWidgetsWithLeakTracking('ListView of license entries is primary', (WidgetTester tester) async {
// Regression test for https://github.com/flutter/flutter/issues/120710
LicenseRegistry.addLicense(() {
return Stream<LicenseEntry>.fromIterable(<LicenseEntry>[
......@@ -971,7 +981,7 @@ void main() {
await tester.pumpAndSettle(); // No exception triggered.
}, variant: TargetPlatformVariant.all());
testWidgets('LicensePage padding', (WidgetTester tester) async {
testWidgetsWithLeakTracking('LicensePage padding', (WidgetTester tester) async {
const FlutterLogo logo = FlutterLogo();
await tester.pumpWidget(
......@@ -1014,7 +1024,7 @@ void main() {
expect(appLegaleseBottomPadding, 18.0);
});
testWidgets('LicensePage has no extra padding between app icon and app powered text', (WidgetTester tester) async {
testWidgetsWithLeakTracking('LicensePage has no extra padding between app icon and app powered text', (WidgetTester tester) async {
// This is a regression test for https://github.com/flutter/flutter/issues/99559
const FlutterLogo logo = FlutterLogo();
......@@ -1047,7 +1057,7 @@ void main() {
expect(appIconBottomPadding, 18.0);
});
testWidgets('Error handling test', (WidgetTester tester) async {
testWidgetsWithLeakTracking('Error handling test', (WidgetTester tester) async {
LicenseRegistry.addLicense(() => Stream<LicenseEntry>.error(Exception('Injected failure')));
await tester.pumpWidget(const MaterialApp(home: Material(child: AboutListTile())));
await tester.tap(find.byType(ListTile));
......@@ -1065,6 +1075,8 @@ void main() {
expect(find.text('Exception: Injected failure'), findsOneWidget);
});
// TODO(polina-c): fix SnapshotController not disposed and switch to testWidgetsWithLeakTracking.
// https://github.com/flutter/devtools/issues/3951
testWidgets('LicensePage master view layout position - ltr', (WidgetTester tester) async {
const TextDirection textDirection = TextDirection.ltr;
const Size defaultSize = Size(800.0, 600.0);
......@@ -1128,6 +1140,8 @@ void main() {
await tester.binding.setSurfaceSize(defaultSize);
});
// TODO(polina-c): fix SnapshotController not disposed and switch to testWidgetsWithLeakTracking.
// https://github.com/flutter/devtools/issues/3951
testWidgets('LicensePage master view layout position - rtl', (WidgetTester tester) async {
const TextDirection textDirection = TextDirection.rtl;
const Size defaultSize = Size(800.0, 600.0);
......@@ -1191,7 +1205,7 @@ void main() {
await tester.binding.setSurfaceSize(defaultSize);
});
testWidgets('License page title in lateral UI does not use AppBarTheme.foregroundColor', (WidgetTester tester) async {
testWidgetsWithLeakTracking('License page title in lateral UI does not use AppBarTheme.foregroundColor', (WidgetTester tester) async {
// This is a regression test for https://github.com/flutter/flutter/issues/108991
final ThemeData theme = ThemeData(
appBarTheme: const AppBarTheme(foregroundColor: Color(0xFFFFFFFF)),
......@@ -1231,7 +1245,7 @@ void main() {
await tester.binding.setSurfaceSize(const Size(800.0, 600.0));
});
testWidgets('License page default title text color in the nested UI', (WidgetTester tester) async {
testWidgetsWithLeakTracking('License page default title text color in the nested UI', (WidgetTester tester) async {
// This is a regression test for https://github.com/flutter/flutter/issues/108991
final ThemeData theme = ThemeData(useMaterial3: true);
const String title = 'License ABC';
......@@ -1271,7 +1285,7 @@ void main() {
// Tests that are only relevant for Material 2. Once ThemeData.useMaterial3
// is turned on by default, these tests can be removed.
testWidgets('License page default title text color in the nested UI', (WidgetTester tester) async {
testWidgetsWithLeakTracking('License page default title text color in the nested UI', (WidgetTester tester) async {
// This is a regression test for https://github.com/flutter/flutter/issues/108991
final ThemeData theme = ThemeData(useMaterial3: false);
const String title = 'License ABC';
......
......@@ -5,6 +5,8 @@
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import '../foundation/leak_tracking.dart';
/// Adds the basic requirements for a Chip.
Widget wrapForChip({
required Widget child,
......@@ -34,7 +36,7 @@ void checkChipMaterialClipBehavior(WidgetTester tester, Clip clipBehavior) {
}
void main() {
testWidgets('ActionChip can be tapped', (WidgetTester tester) async {
testWidgetsWithLeakTracking('ActionChip can be tapped', (WidgetTester tester) async {
await tester.pumpWidget(
MaterialApp(
home: Material(
......@@ -50,7 +52,7 @@ void main() {
expect(tester.takeException(), null);
});
testWidgets('ActionChip clipBehavior properly passes through to the Material', (WidgetTester tester) async {
testWidgetsWithLeakTracking('ActionChip clipBehavior properly passes through to the Material', (WidgetTester tester) async {
const Text label = Text('label');
await tester.pumpWidget(wrapForChip(child: ActionChip(label: label, onPressed: () { })));
checkChipMaterialClipBehavior(tester, Clip.none);
......
......@@ -6,6 +6,8 @@ import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
import 'package:flutter_test/flutter_test.dart';
import '../foundation/leak_tracking.dart';
void main() {
test('ActionIconThemeData copyWith, ==, hashCode basics', () {
expect(const ActionIconThemeData(), const ActionIconThemeData().copyWith());
......@@ -21,7 +23,7 @@ void main() {
expect(themeData.endDrawerButtonIconBuilder, null);
});
testWidgets('Default ActionIconThemeData debugFillProperties',
testWidgetsWithLeakTracking('Default ActionIconThemeData debugFillProperties',
(WidgetTester tester) async {
final DiagnosticPropertiesBuilder builder = DiagnosticPropertiesBuilder();
const ActionIconThemeData().debugFillProperties(builder);
......@@ -34,7 +36,7 @@ void main() {
expect(description, <String>[]);
});
testWidgets('ActionIconThemeData implements debugFillProperties',
testWidgetsWithLeakTracking('ActionIconThemeData implements debugFillProperties',
(WidgetTester tester) async {
Widget actionButtonIconBuilder(BuildContext context) {
return const Icon(IconData(0));
......@@ -62,7 +64,7 @@ void main() {
]);
});
testWidgets('Action buttons use ThemeData action icon theme', (WidgetTester tester) async {
testWidgetsWithLeakTracking('Action buttons use ThemeData action icon theme', (WidgetTester tester) async {
const Color green = Color(0xff00ff00);
const IconData icon = IconData(0);
......@@ -123,7 +125,7 @@ void main() {
// This test is essentially the same as 'Action buttons use ThemeData action icon theme'. In
// this case the theme is introduced with the ActionIconTheme widget instead of
// ThemeData.actionIconTheme.
testWidgets('Action buttons use ActionIconTheme', (WidgetTester tester) async {
testWidgetsWithLeakTracking('Action buttons use ActionIconTheme', (WidgetTester tester) async {
const Color green = Color(0xff00ff00);
const IconData icon = IconData(0);
......
......@@ -8,6 +8,7 @@ import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_test/flutter_test.dart';
import '../foundation/leak_tracking.dart';
import '../widgets/clipboard_utils.dart';
import '../widgets/editable_text_utils.dart';
......@@ -25,7 +26,7 @@ void main() {
await Clipboard.setData(const ClipboardData(text: 'Clipboard data'));
});
testWidgets('Builds the right toolbar on each platform, including web, and shows buttonItems', (WidgetTester tester) async {
testWidgetsWithLeakTracking('Builds the right toolbar on each platform, including web, and shows buttonItems', (WidgetTester tester) async {
const String buttonText = 'Click me';
await tester.pumpWidget(
......@@ -80,7 +81,7 @@ void main() {
skip: isBrowser, // [intended] see https://github.com/flutter/flutter/issues/108382
);
testWidgets('Can build children directly as well', (WidgetTester tester) async {
testWidgetsWithLeakTracking('Can build children directly as well', (WidgetTester tester) async {
final GlobalKey key = GlobalKey();
await tester.pumpWidget(
......@@ -103,7 +104,7 @@ void main() {
expect(find.byKey(key), findsOneWidget);
});
testWidgets('Can build from EditableTextState', (WidgetTester tester) async {
testWidgetsWithLeakTracking('Can build from EditableTextState', (WidgetTester tester) async {
final GlobalKey key = GlobalKey();
await tester.pumpWidget(
MaterialApp(
......@@ -167,7 +168,7 @@ void main() {
variant: TargetPlatformVariant.all(),
);
testWidgets('Can build for editable text from raw parameters', (WidgetTester tester) async {
testWidgetsWithLeakTracking('Can build for editable text from raw parameters', (WidgetTester tester) async {
final GlobalKey key = GlobalKey();
await tester.pumpWidget(
MaterialApp(
......@@ -216,7 +217,7 @@ void main() {
);
group('buttonItems', () {
testWidgets('getEditableTextButtonItems builds the correct button items per-platform', (WidgetTester tester) async {
testWidgetsWithLeakTracking('getEditableTextButtonItems builds the correct button items per-platform', (WidgetTester tester) async {
// Fill the clipboard so that the Paste option is available in the text
// selection menu.
await Clipboard.setData(const ClipboardData(text: 'Clipboard data'));
......@@ -311,7 +312,7 @@ void main() {
skip: kIsWeb, // [intended]
);
testWidgets('getAdaptiveButtons builds the correct button widgets per-platform', (WidgetTester tester) async {
testWidgetsWithLeakTracking('getAdaptiveButtons builds the correct button widgets per-platform', (WidgetTester tester) async {
const String buttonText = 'Click me';
await tester.pumpWidget(
......
......@@ -11,6 +11,8 @@ import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
void main() {
// TODO(polina-c): fix Image not disposed and switch to testWidgetsWithLeakTracking.
// https://github.com/flutter/devtools/issues/3951
testWidgets('Flutter Logo golden test', (WidgetTester tester) async {
final Key logo = UniqueKey();
await tester.pumpWidget(FlutterLogo(key: logo));
......
......@@ -26,7 +26,7 @@ Finder _findTooltipContainer(String tooltipText) {
}
void main() {
testWidgets('Does tooltip end up in the right place - center', (WidgetTester tester) async {
testWidgetsWithLeakTracking('Does tooltip end up in the right place - center', (WidgetTester tester) async {
final GlobalKey<TooltipState> tooltipKey = GlobalKey<TooltipState>();
await tester.pumpWidget(
Directionality(
......@@ -80,7 +80,7 @@ void main() {
expect(tipInGlobal.dy, 20.0);
});
testWidgets('Does tooltip end up in the right place - center with padding outside overlay', (WidgetTester tester) async {
testWidgetsWithLeakTracking('Does tooltip end up in the right place - center with padding outside overlay', (WidgetTester tester) async {
final GlobalKey<TooltipState> tooltipKey = GlobalKey<TooltipState>();
await tester.pumpWidget(
Directionality(
......@@ -139,7 +139,7 @@ void main() {
expect(tipInGlobal.dy, 40.0);
});
testWidgets('Does tooltip end up in the right place - top left', (WidgetTester tester) async {
testWidgetsWithLeakTracking('Does tooltip end up in the right place - top left', (WidgetTester tester) async {
final GlobalKey<TooltipState> tooltipKey = GlobalKey<TooltipState>();
await tester.pumpWidget(
Directionality(
......@@ -190,7 +190,7 @@ void main() {
expect(tip.localToGlobal(tip.size.topLeft(Offset.zero)), equals(const Offset(10.0, 20.0)));
});
testWidgets('Does tooltip end up in the right place - center prefer above fits', (WidgetTester tester) async {
testWidgetsWithLeakTracking('Does tooltip end up in the right place - center prefer above fits', (WidgetTester tester) async {
final GlobalKey<TooltipState> tooltipKey = GlobalKey<TooltipState>();
await tester.pumpWidget(
Directionality(
......@@ -243,7 +243,7 @@ void main() {
expect(tip.localToGlobal(tip.size.bottomRight(Offset.zero)).dy, equals(200.0));
});
testWidgets('Does tooltip end up in the right place - center prefer above does not fit', (WidgetTester tester) async {
testWidgetsWithLeakTracking('Does tooltip end up in the right place - center prefer above does not fit', (WidgetTester tester) async {
final GlobalKey<TooltipState> tooltipKey = GlobalKey<TooltipState>();
await tester.pumpWidget(
Directionality(
......@@ -307,7 +307,7 @@ void main() {
expect(tip.localToGlobal(tip.size.bottomRight(Offset.zero)).dy, equals(589.0));
});
testWidgets('Does tooltip end up in the right place - center prefer below fits', (WidgetTester tester) async {
testWidgetsWithLeakTracking('Does tooltip end up in the right place - center prefer below fits', (WidgetTester tester) async {
final GlobalKey<TooltipState> tooltipKey = GlobalKey<TooltipState>();
await tester.pumpWidget(
Directionality(
......@@ -359,7 +359,7 @@ void main() {
expect(tip.localToGlobal(tip.size.bottomRight(Offset.zero)).dy, equals(590.0));
});
testWidgets('Does tooltip end up in the right place - way off to the right', (WidgetTester tester) async {
testWidgetsWithLeakTracking('Does tooltip end up in the right place - way off to the right', (WidgetTester tester) async {
final GlobalKey<TooltipState> tooltipKey = GlobalKey<TooltipState>();
await tester.pumpWidget(
Directionality(
......@@ -413,7 +413,7 @@ void main() {
expect(tip.localToGlobal(tip.size.bottomRight(Offset.zero)).dy, equals(324.0));
});
testWidgets('Does tooltip end up in the right place - near the edge', (WidgetTester tester) async {
testWidgetsWithLeakTracking('Does tooltip end up in the right place - near the edge', (WidgetTester tester) async {
final GlobalKey<TooltipState> tooltipKey = GlobalKey<TooltipState>();
await tester.pumpWidget(
Directionality(
......@@ -467,7 +467,7 @@ void main() {
expect(tip.localToGlobal(tip.size.bottomRight(Offset.zero)).dy, equals(324.0));
});
testWidgets('Tooltip should be fully visible when MediaQuery.viewInsets > 0', (WidgetTester tester) async {
testWidgetsWithLeakTracking('Tooltip should be fully visible when MediaQuery.viewInsets > 0', (WidgetTester tester) async {
// Regression test for https://github.com/flutter/flutter/issues/23666
Widget materialAppWithViewInsets(double viewInsetsHeight) {
final Widget scaffold = Scaffold(
......@@ -520,7 +520,7 @@ void main() {
expect(tooltipTopRight.dy < fabTopRight.dy, true);
});
testWidgets('Custom tooltip margin', (WidgetTester tester) async {
testWidgetsWithLeakTracking('Custom tooltip margin', (WidgetTester tester) async {
const double customMarginValue = 10.0;
final GlobalKey<TooltipState> tooltipKey = GlobalKey<TooltipState>();
await tester.pumpWidget(
......@@ -575,7 +575,7 @@ void main() {
expect(bottomRightTooltipContentInGlobal.dy, bottomRightTipInGlobal.dy - customMarginValue);
});
testWidgets('Default tooltip message textStyle - light', (WidgetTester tester) async {
testWidgetsWithLeakTracking('Default tooltip message textStyle - light', (WidgetTester tester) async {
final GlobalKey<TooltipState> tooltipKey = GlobalKey<TooltipState>();
await tester.pumpWidget(MaterialApp(
home: Tooltip(
......@@ -598,7 +598,7 @@ void main() {
expect(textStyle.debugLabel, '((englishLike bodyMedium 2014).merge(blackMountainView bodyMedium)).copyWith');
});
testWidgets('Default tooltip message textStyle - dark', (WidgetTester tester) async {
testWidgetsWithLeakTracking('Default tooltip message textStyle - dark', (WidgetTester tester) async {
final GlobalKey<TooltipState> tooltipKey = GlobalKey<TooltipState>();
await tester.pumpWidget(MaterialApp(
theme: ThemeData(
......@@ -624,7 +624,7 @@ void main() {
expect(textStyle.debugLabel, '((englishLike bodyMedium 2014).merge(whiteMountainView bodyMedium)).copyWith');
});
testWidgets('Custom tooltip message textStyle', (WidgetTester tester) async {
testWidgetsWithLeakTracking('Custom tooltip message textStyle', (WidgetTester tester) async {
final GlobalKey<TooltipState> tooltipKey = GlobalKey<TooltipState>();
await tester.pumpWidget(MaterialApp(
home: Tooltip(
......@@ -650,47 +650,42 @@ void main() {
expect(textStyle.decoration, TextDecoration.underline);
});
testWidgets('Custom tooltip message textAlign', (WidgetTester tester) async {
await withFlutterLeakTracking(
() async {
Future<void> pumpTooltipWithTextAlign({TextAlign? textAlign}) async {
final GlobalKey<TooltipState> tooltipKey = GlobalKey<TooltipState>();
await tester.pumpWidget(
MaterialApp(
home: Tooltip(
key: tooltipKey,
textAlign: textAlign,
message: tooltipText,
child: Container(
width: 100.0,
height: 100.0,
color: Colors.green[500],
),
),
testWidgetsWithLeakTracking('Custom tooltip message textAlign', (WidgetTester tester) async {
Future<void> pumpTooltipWithTextAlign({TextAlign? textAlign}) async {
final GlobalKey<TooltipState> tooltipKey = GlobalKey<TooltipState>();
await tester.pumpWidget(
MaterialApp(
home: Tooltip(
key: tooltipKey,
textAlign: textAlign,
message: tooltipText,
child: Container(
width: 100.0,
height: 100.0,
color: Colors.green[500],
),
);
tooltipKey.currentState?.ensureTooltipVisible();
await tester.pump(const Duration(seconds: 2)); // faded in, show timer started (and at 0.0)
}
// Default value should be TextAlign.start
await pumpTooltipWithTextAlign();
TextAlign textAlign = tester.widget<Text>(find.text(tooltipText)).textAlign!;
expect(textAlign, TextAlign.start);
await pumpTooltipWithTextAlign(textAlign: TextAlign.center);
textAlign = tester.widget<Text>(find.text(tooltipText)).textAlign!;
expect(textAlign, TextAlign.center);
await pumpTooltipWithTextAlign(textAlign: TextAlign.end);
textAlign = tester.widget<Text>(find.text(tooltipText)).textAlign!;
expect(textAlign, TextAlign.end);
},
tester: tester,
);
),
),
);
tooltipKey.currentState?.ensureTooltipVisible();
await tester.pump(const Duration(seconds: 2)); // faded in, show timer started (and at 0.0)
}
// Default value should be TextAlign.start
await pumpTooltipWithTextAlign();
TextAlign textAlign = tester.widget<Text>(find.text(tooltipText)).textAlign!;
expect(textAlign, TextAlign.start);
await pumpTooltipWithTextAlign(textAlign: TextAlign.center);
textAlign = tester.widget<Text>(find.text(tooltipText)).textAlign!;
expect(textAlign, TextAlign.center);
await pumpTooltipWithTextAlign(textAlign: TextAlign.end);
textAlign = tester.widget<Text>(find.text(tooltipText)).textAlign!;
expect(textAlign, TextAlign.end);
});
testWidgets('Tooltip overlay respects ambient Directionality', (WidgetTester tester) async {
testWidgetsWithLeakTracking('Tooltip overlay respects ambient Directionality', (WidgetTester tester) async {
// Regression test for https://github.com/flutter/flutter/issues/40702.
Widget buildApp(String text, TextDirection textDirection) {
return MaterialApp(
......@@ -723,7 +718,7 @@ void main() {
expect(tooltipRenderParagraph.textDirection, TextDirection.ltr);
});
testWidgets('Tooltip overlay wrapped with a non-fallback DefaultTextStyle widget', (WidgetTester tester) async {
testWidgetsWithLeakTracking('Tooltip overlay wrapped with a non-fallback DefaultTextStyle widget', (WidgetTester tester) async {
// A Material widget is needed as an ancestor of the Text widget.
// It is invalid to have text in a Material application that
// does not have a Material ancestor.
......@@ -756,7 +751,7 @@ void main() {
expect(textStyle.decorationStyle, isNot(TextDecorationStyle.double));
});
testWidgets('Does tooltip end up with the right default size, shape, and color', (WidgetTester tester) async {
testWidgetsWithLeakTracking('Does tooltip end up with the right default size, shape, and color', (WidgetTester tester) async {
final GlobalKey<TooltipState> tooltipKey = GlobalKey<TooltipState>();
await tester.pumpWidget(
Directionality(
......@@ -791,7 +786,7 @@ void main() {
expect(tooltipContainer.padding, const EdgeInsets.symmetric(horizontal: 16.0, vertical: 4.0));
});
testWidgets('Tooltip default size, shape, and color test for Desktop', (WidgetTester tester) async {
testWidgetsWithLeakTracking('Tooltip default size, shape, and color test for Desktop', (WidgetTester tester) async {
// Regressing test for https://github.com/flutter/flutter/issues/68601
final GlobalKey<TooltipState> tooltipKey = GlobalKey<TooltipState>();
await tester.pumpWidget(
......@@ -820,7 +815,7 @@ void main() {
expect(tooltipContainer.padding, const EdgeInsets.symmetric(horizontal: 8.0, vertical: 4.0));
}, variant: const TargetPlatformVariant(<TargetPlatform>{TargetPlatform.macOS, TargetPlatform.linux, TargetPlatform.windows}));
testWidgets('Can tooltip decoration be customized', (WidgetTester tester) async {
testWidgetsWithLeakTracking('Can tooltip decoration be customized', (WidgetTester tester) async {
final GlobalKey<TooltipState> tooltipKey = GlobalKey<TooltipState>();
const Decoration customDecoration = ShapeDecoration(
shape: StadiumBorder(),
......@@ -856,7 +851,7 @@ void main() {
expect(tip, paints..rrect(color: const Color(0x80800000)));
});
testWidgets('Tooltip stays after long press', (WidgetTester tester) async {
testWidgetsWithLeakTracking('Tooltip stays after long press', (WidgetTester tester) async {
await tester.pumpWidget(
MaterialApp(
home: Center(
......@@ -903,7 +898,7 @@ void main() {
await gesture.up();
});
testWidgets('Tooltip is dismissed after a long press and showDuration expired', (WidgetTester tester) async {
testWidgetsWithLeakTracking('Tooltip is dismissed after a long press and showDuration expired', (WidgetTester tester) async {
const Duration showDuration = Duration(seconds: 3);
await setWidgetForTooltipMode(tester, TooltipTriggerMode.longPress, showDuration: showDuration);
......@@ -922,7 +917,7 @@ void main() {
expect(find.text(tooltipText), findsNothing);
});
testWidgets('Tooltip is dismissed after a tap and showDuration expired', (WidgetTester tester) async {
testWidgetsWithLeakTracking('Tooltip is dismissed after a tap and showDuration expired', (WidgetTester tester) async {
const Duration showDuration = Duration(seconds: 3);
await setWidgetForTooltipMode(tester, TooltipTriggerMode.tap, showDuration: showDuration);
......@@ -938,7 +933,7 @@ void main() {
expect(find.text(tooltipText), findsNothing);
});
testWidgets('Tooltip is dismissed after a tap and showDuration expired when competing with a GestureDetector', (WidgetTester tester) async {
testWidgetsWithLeakTracking('Tooltip is dismissed after a tap and showDuration expired when competing with a GestureDetector', (WidgetTester tester) async {
// Regression test for https://github.com/flutter/flutter/issues/98854
const Duration showDuration = Duration(seconds: 3);
await tester.pumpWidget(
......@@ -969,7 +964,7 @@ void main() {
expect(find.text(tooltipText), findsNothing);
});
testWidgets('Dispatch the mouse events before tip overlay detached', (WidgetTester tester) async {
testWidgetsWithLeakTracking('Dispatch the mouse events before tip overlay detached', (WidgetTester tester) async {
// Regression test for https://github.com/flutter/flutter/issues/96890
const Duration waitDuration = Duration.zero;
TestGesture? gesture = await tester.createGesture(kind: PointerDeviceKind.mouse);
......@@ -1026,7 +1021,7 @@ void main() {
gesture = null;
});
testWidgets('Calling ensureTooltipVisible on an unmounted TooltipState returns false', (WidgetTester tester) async {
testWidgetsWithLeakTracking('Calling ensureTooltipVisible on an unmounted TooltipState returns false', (WidgetTester tester) async {
// Regression test for https://github.com/flutter/flutter/issues/95851
await tester.pumpWidget(
const MaterialApp(
......@@ -1057,7 +1052,7 @@ void main() {
expect(tooltipState.ensureTooltipVisible(), false);
});
testWidgets('Tooltip shows/hides when hovered', (WidgetTester tester) async {
testWidgetsWithLeakTracking('Tooltip shows/hides when hovered', (WidgetTester tester) async {
const Duration waitDuration = Duration.zero;
TestGesture? gesture = await tester.createGesture(kind: PointerDeviceKind.mouse);
addTearDown(() async {
......@@ -1110,7 +1105,7 @@ void main() {
expect(find.text(tooltipText), findsNothing);
});
testWidgets('Tooltip text is also hoverable', (WidgetTester tester) async {
testWidgetsWithLeakTracking('Tooltip text is also hoverable', (WidgetTester tester) async {
const Duration waitDuration = Duration.zero;
TestGesture? gesture = await tester.createGesture(kind: PointerDeviceKind.mouse);
addTearDown(() async {
......@@ -1164,7 +1159,7 @@ void main() {
expect(find.text(tooltipText), findsNothing);
});
testWidgets('Tooltip should not show more than one tooltip when hovered', (WidgetTester tester) async {
testWidgetsWithLeakTracking('Tooltip should not show more than one tooltip when hovered', (WidgetTester tester) async {
const Duration waitDuration = Duration(milliseconds: 500);
final UniqueKey innerKey = UniqueKey();
final UniqueKey outerKey = UniqueKey();
......@@ -1228,7 +1223,7 @@ void main() {
expect(find.text('Inner'), findsNothing);
});
testWidgets('Tooltip can be dismissed by escape key', (WidgetTester tester) async {
testWidgetsWithLeakTracking('Tooltip can be dismissed by escape key', (WidgetTester tester) async {
const Duration waitDuration = Duration.zero;
TestGesture? gesture = await tester.createGesture(kind: PointerDeviceKind.mouse);
addTearDown(() async {
......@@ -1273,7 +1268,7 @@ void main() {
gesture = null;
});
testWidgets('Multiple Tooltips are dismissed by escape key', (WidgetTester tester) async {
testWidgetsWithLeakTracking('Multiple Tooltips are dismissed by escape key', (WidgetTester tester) async {
const Duration waitDuration = Duration.zero;
TestGesture? gesture = await tester.createGesture(kind: PointerDeviceKind.mouse);
addTearDown(() async {
......@@ -1340,7 +1335,7 @@ void main() {
gesture = null;
});
testWidgets('Tooltip does not attempt to show after unmount', (WidgetTester tester) async {
testWidgetsWithLeakTracking('Tooltip does not attempt to show after unmount', (WidgetTester tester) async {
// Regression test for https://github.com/flutter/flutter/issues/54096.
const Duration waitDuration = Duration(seconds: 1);
final TestGesture gesture = await tester.createGesture(kind: PointerDeviceKind.mouse);
......@@ -1386,7 +1381,7 @@ void main() {
await tester.pump(waitDuration);
});
testWidgets('Does tooltip contribute semantics', (WidgetTester tester) async {
testWidgetsWithLeakTracking('Does tooltip contribute semantics', (WidgetTester tester) async {
final SemanticsTester semantics = SemanticsTester(tester);
final GlobalKey<TooltipState> tooltipKey = GlobalKey<TooltipState>();
......@@ -1439,7 +1434,7 @@ void main() {
semantics.dispose();
});
testWidgets('Tooltip overlay does not update', (WidgetTester tester) async {
testWidgetsWithLeakTracking('Tooltip overlay does not update', (WidgetTester tester) async {
Widget buildApp(String text) {
return MaterialApp(
home: Center(
......@@ -1468,7 +1463,7 @@ void main() {
expect(find.text(tooltipText), findsNothing);
});
testWidgets('Tooltip text scales with textScaleFactor', (WidgetTester tester) async {
testWidgetsWithLeakTracking('Tooltip text scales with textScaleFactor', (WidgetTester tester) async {
Widget buildApp(String text, { required double textScaleFactor }) {
return MediaQuery(
data: MediaQueryData(textScaleFactor: textScaleFactor),
......@@ -1515,7 +1510,7 @@ void main() {
expect(tip.size.height, equals(64.0));
});
testWidgets('Tooltip text displays with richMessage', (WidgetTester tester) async {
testWidgetsWithLeakTracking('Tooltip text displays with richMessage', (WidgetTester tester) async {
final GlobalKey<TooltipState> tooltipKey = GlobalKey<TooltipState>();
const String textSpan1Text = 'I am a rich tooltip message. ';
const String textSpan2Text = 'I am another span of a rich tooltip message';
......@@ -1546,7 +1541,7 @@ void main() {
expect(richText.text.toPlainText(), equals('$textSpan1Text$textSpan2Text'));
});
testWidgets('Tooltip throws assertion error when both message and richMessage are specified', (WidgetTester tester) async {
testWidgetsWithLeakTracking('Tooltip throws assertion error when both message and richMessage are specified', (WidgetTester tester) async {
expect(
() {
MaterialApp(
......@@ -1572,7 +1567,7 @@ void main() {
);
});
testWidgets('Haptic feedback', (WidgetTester tester) async {
testWidgetsWithLeakTracking('Haptic feedback', (WidgetTester tester) async {
final FeedbackTester feedback = FeedbackTester();
await tester.pumpWidget(
MaterialApp(
......@@ -1596,7 +1591,7 @@ void main() {
feedback.dispose();
});
testWidgets('Semantics included', (WidgetTester tester) async {
testWidgetsWithLeakTracking('Semantics included', (WidgetTester tester) async {
final SemanticsTester semantics = SemanticsTester(tester);
await tester.pumpWidget(
......@@ -1636,7 +1631,7 @@ void main() {
semantics.dispose();
});
testWidgets('Semantics excluded', (WidgetTester tester) async {
testWidgetsWithLeakTracking('Semantics excluded', (WidgetTester tester) async {
final SemanticsTester semantics = SemanticsTester(tester);
await tester.pumpWidget(
......@@ -1676,7 +1671,7 @@ void main() {
semantics.dispose();
});
testWidgets('has semantic events', (WidgetTester tester) async {
testWidgetsWithLeakTracking('has semantic events', (WidgetTester tester) async {
final List<dynamic> semanticEvents = <dynamic>[];
tester.binding.defaultBinaryMessenger.setMockDecodedMessageHandler<dynamic>(SystemChannels.accessibility, (dynamic message) async {
semanticEvents.add(message);
......@@ -1717,7 +1712,7 @@ void main() {
semantics.dispose();
tester.binding.defaultBinaryMessenger.setMockDecodedMessageHandler<dynamic>(SystemChannels.accessibility, null);
});
testWidgets('default Tooltip debugFillProperties', (WidgetTester tester) async {
testWidgetsWithLeakTracking('default Tooltip debugFillProperties', (WidgetTester tester) async {
final DiagnosticPropertiesBuilder builder = DiagnosticPropertiesBuilder();
const Tooltip(message: 'message').debugFillProperties(builder);
......@@ -1730,7 +1725,7 @@ void main() {
'"message"',
]);
});
testWidgets('default Tooltip debugFillProperties with richMessage', (WidgetTester tester) async {
testWidgetsWithLeakTracking('default Tooltip debugFillProperties with richMessage', (WidgetTester tester) async {
final DiagnosticPropertiesBuilder builder = DiagnosticPropertiesBuilder();
const Tooltip(
......@@ -1752,7 +1747,7 @@ void main() {
'"This is a richMessage"',
]);
});
testWidgets('Tooltip implements debugFillProperties', (WidgetTester tester) async {
testWidgetsWithLeakTracking('Tooltip implements debugFillProperties', (WidgetTester tester) async {
final DiagnosticPropertiesBuilder builder = DiagnosticPropertiesBuilder();
// Not checking controller, inputFormatters, focusNode
......@@ -1791,7 +1786,7 @@ void main() {
]);
});
testWidgets('Tooltip triggers on tap when trigger mode is tap', (WidgetTester tester) async {
testWidgetsWithLeakTracking('Tooltip triggers on tap when trigger mode is tap', (WidgetTester tester) async {
await setWidgetForTooltipMode(tester, TooltipTriggerMode.tap);
final Finder tooltip = find.byType(Tooltip);
......@@ -1801,7 +1796,7 @@ void main() {
expect(find.text(tooltipText), findsOneWidget);
});
testWidgets('Tooltip triggers on long press when mode is long press', (WidgetTester tester) async {
testWidgetsWithLeakTracking('Tooltip triggers on long press when mode is long press', (WidgetTester tester) async {
await setWidgetForTooltipMode(tester, TooltipTriggerMode.longPress);
final Finder tooltip = find.byType(Tooltip);
......@@ -1814,7 +1809,7 @@ void main() {
expect(find.text(tooltipText), findsOneWidget);
});
testWidgets('Tooltip does not trigger on tap when trigger mode is longPress', (WidgetTester tester) async {
testWidgetsWithLeakTracking('Tooltip does not trigger on tap when trigger mode is longPress', (WidgetTester tester) async {
await setWidgetForTooltipMode(tester, TooltipTriggerMode.longPress);
final Finder tooltip = find.byType(Tooltip);
......@@ -1824,7 +1819,7 @@ void main() {
expect(find.text(tooltipText), findsNothing);
});
testWidgets('Tooltip does not trigger when trigger mode is manual', (WidgetTester tester) async {
testWidgetsWithLeakTracking('Tooltip does not trigger when trigger mode is manual', (WidgetTester tester) async {
await setWidgetForTooltipMode(tester, TooltipTriggerMode.manual);
final Finder tooltip = find.byType(Tooltip);
......@@ -1837,7 +1832,7 @@ void main() {
expect(find.text(tooltipText), findsNothing);
});
testWidgets('Tooltip onTriggered is called when Tooltip triggers', (WidgetTester tester) async {
testWidgetsWithLeakTracking('Tooltip onTriggered is called when Tooltip triggers', (WidgetTester tester) async {
bool onTriggeredCalled = false;
void onTriggered() => onTriggeredCalled = true;
......@@ -1853,7 +1848,7 @@ void main() {
expect(onTriggeredCalled, true);
});
testWidgets('Tooltip onTriggered is not called when Tooltip is hovered', (WidgetTester tester) async {
testWidgetsWithLeakTracking('Tooltip onTriggered is not called when Tooltip is hovered', (WidgetTester tester) async {
bool onTriggeredCalled = false;
void onTriggered() => onTriggeredCalled = true;
......@@ -1886,7 +1881,7 @@ void main() {
expect(onTriggeredCalled, false);
});
testWidgets('Tooltip should not be shown with empty message (with child)', (WidgetTester tester) async {
testWidgetsWithLeakTracking('Tooltip should not be shown with empty message (with child)', (WidgetTester tester) async {
await tester.pumpWidget(
const MaterialApp(
home: Tooltip(
......@@ -1898,7 +1893,7 @@ void main() {
expect(find.text(tooltipText), findsOneWidget);
});
testWidgets('Tooltip should not be shown with empty message (without child)', (WidgetTester tester) async {
testWidgetsWithLeakTracking('Tooltip should not be shown with empty message (without child)', (WidgetTester tester) async {
await tester.pumpWidget(
const MaterialApp(
home: Tooltip(
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment