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);
}
}
} 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>[];
......
......@@ -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));
......
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