The PR is breaking the `flutter_test_performance` test and making the tree red.
This commit is contained in:
parent
d746007fcd
commit
8407dd2d0e
@ -4,7 +4,6 @@
|
|||||||
|
|
||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
|
|
||||||
import 'package:leak_tracker_flutter_testing/leak_tracker_flutter_testing.dart';
|
|
||||||
import 'package:meta/meta.dart';
|
import 'package:meta/meta.dart';
|
||||||
import 'package:test_api/scaffolding.dart' show Timeout;
|
import 'package:test_api/scaffolding.dart' show Timeout;
|
||||||
import 'package:test_api/src/backend/declarer.dart'; // ignore: implementation_imports
|
import 'package:test_api/src/backend/declarer.dart'; // ignore: implementation_imports
|
||||||
@ -164,7 +163,6 @@ void test(
|
|||||||
Map<String, dynamic>? onPlatform,
|
Map<String, dynamic>? onPlatform,
|
||||||
int? retry,
|
int? retry,
|
||||||
}) {
|
}) {
|
||||||
_configureTearDownForTestFile();
|
|
||||||
_declarer.test(
|
_declarer.test(
|
||||||
description.toString(),
|
description.toString(),
|
||||||
body,
|
body,
|
||||||
@ -188,7 +186,6 @@ void test(
|
|||||||
/// of running the group's tests.
|
/// of running the group's tests.
|
||||||
@isTestGroup
|
@isTestGroup
|
||||||
void group(Object description, void Function() body, { dynamic skip, int? retry }) {
|
void group(Object description, void Function() body, { dynamic skip, int? retry }) {
|
||||||
_configureTearDownForTestFile();
|
|
||||||
_declarer.group(description.toString(), body, skip: skip, retry: retry);
|
_declarer.group(description.toString(), body, skip: skip, retry: retry);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -204,7 +201,6 @@ void group(Object description, void Function() body, { dynamic skip, int? retry
|
|||||||
/// Each callback at the top level or in a given group will be run in the order
|
/// Each callback at the top level or in a given group will be run in the order
|
||||||
/// they were declared.
|
/// they were declared.
|
||||||
void setUp(dynamic Function() body) {
|
void setUp(dynamic Function() body) {
|
||||||
_configureTearDownForTestFile();
|
|
||||||
_declarer.setUp(body);
|
_declarer.setUp(body);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -222,7 +218,6 @@ void setUp(dynamic Function() body) {
|
|||||||
///
|
///
|
||||||
/// See also [addTearDown], which adds tear-downs to a running test.
|
/// See also [addTearDown], which adds tear-downs to a running test.
|
||||||
void tearDown(dynamic Function() body) {
|
void tearDown(dynamic Function() body) {
|
||||||
_configureTearDownForTestFile();
|
|
||||||
_declarer.tearDown(body);
|
_declarer.tearDown(body);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -240,7 +235,6 @@ void tearDown(dynamic Function() body) {
|
|||||||
/// prefer [setUp], and only use [setUpAll] if the callback is prohibitively
|
/// prefer [setUp], and only use [setUpAll] if the callback is prohibitively
|
||||||
/// slow.
|
/// slow.
|
||||||
void setUpAll(dynamic Function() body) {
|
void setUpAll(dynamic Function() body) {
|
||||||
_configureTearDownForTestFile();
|
|
||||||
_declarer.setUpAll(body);
|
_declarer.setUpAll(body);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -256,27 +250,9 @@ void setUpAll(dynamic Function() body) {
|
|||||||
/// prefer [tearDown], and only use [tearDownAll] if the callback is
|
/// prefer [tearDown], and only use [tearDownAll] if the callback is
|
||||||
/// prohibitively slow.
|
/// prohibitively slow.
|
||||||
void tearDownAll(dynamic Function() body) {
|
void tearDownAll(dynamic Function() body) {
|
||||||
_configureTearDownForTestFile();
|
|
||||||
_declarer.tearDownAll(body);
|
_declarer.tearDownAll(body);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool _isTearDownForTestFileConfigured = false;
|
|
||||||
/// Configures `tearDownAll` after all user defined `tearDownAll` in the test file.
|
|
||||||
///
|
|
||||||
/// This function should be invoked in all functions, that may be invoked by user in the test file,
|
|
||||||
/// to be invoked before any other `tearDownAll`.
|
|
||||||
void _configureTearDownForTestFile() {
|
|
||||||
if (_isTearDownForTestFileConfigured) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
_declarer.tearDownAll(_tearDownForTestFile);
|
|
||||||
_isTearDownForTestFileConfigured = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Tear down that should happen after all user defined tear down.
|
|
||||||
Future<void> _tearDownForTestFile() async {
|
|
||||||
await maybeTearDownLeakTrackingForAll();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A reporter that prints each test on its own line.
|
/// A reporter that prints each test on its own line.
|
||||||
///
|
///
|
||||||
|
@ -9,7 +9,6 @@ import 'package:flutter/material.dart' show Tooltip;
|
|||||||
import 'package:flutter/rendering.dart';
|
import 'package:flutter/rendering.dart';
|
||||||
import 'package:flutter/scheduler.dart';
|
import 'package:flutter/scheduler.dart';
|
||||||
import 'package:flutter/services.dart';
|
import 'package:flutter/services.dart';
|
||||||
import 'package:leak_tracker_flutter_testing/leak_tracker_flutter_testing.dart';
|
|
||||||
import 'package:matcher/expect.dart' as matcher_expect;
|
import 'package:matcher/expect.dart' as matcher_expect;
|
||||||
import 'package:meta/meta.dart';
|
import 'package:meta/meta.dart';
|
||||||
import 'package:test_api/scaffolding.dart' as test_package;
|
import 'package:test_api/scaffolding.dart' as test_package;
|
||||||
@ -117,18 +116,6 @@ E? _lastWhereOrNull<E>(Iterable<E> list, bool Function(E) test) {
|
|||||||
/// If the [tags] are passed, they declare user-defined tags that are implemented by
|
/// If the [tags] are passed, they declare user-defined tags that are implemented by
|
||||||
/// the `test` package.
|
/// the `test` package.
|
||||||
///
|
///
|
||||||
/// The argument [experimentalLeakTesting] is experimental and is not recommended
|
|
||||||
/// for use outside of the Flutter framework.
|
|
||||||
/// When [experimentalLeakTesting] is set, it is used to leak track objects created
|
|
||||||
/// during test execution.
|
|
||||||
/// Otherwise [LeakTesting.settings] is used.
|
|
||||||
/// Adjust [LeakTesting.settings] in flutter_test_config.dart
|
|
||||||
/// (see https://github.com/flutter/flutter/blob/master/packages/flutter_test/lib/flutter_test.dart)
|
|
||||||
/// for the entire package or folder, or in the test's main for a test file
|
|
||||||
/// (don't use [setUp] or [setUpAll]).
|
|
||||||
/// To turn off leak tracking just for one test, set [experimentalLeakTesting] to
|
|
||||||
/// `LeakTrackingForTests.ignore()`.
|
|
||||||
///
|
|
||||||
/// ## Sample code
|
/// ## Sample code
|
||||||
///
|
///
|
||||||
/// ```dart
|
/// ```dart
|
||||||
@ -148,7 +135,6 @@ void testWidgets(
|
|||||||
TestVariant<Object?> variant = const DefaultTestVariant(),
|
TestVariant<Object?> variant = const DefaultTestVariant(),
|
||||||
dynamic tags,
|
dynamic tags,
|
||||||
int? retry,
|
int? retry,
|
||||||
LeakTesting? experimentalLeakTesting,
|
|
||||||
}) {
|
}) {
|
||||||
assert(variant.values.isNotEmpty, 'There must be at least one value to test in the testing variant.');
|
assert(variant.values.isNotEmpty, 'There must be at least one value to test in the testing variant.');
|
||||||
final TestWidgetsFlutterBinding binding = TestWidgetsFlutterBinding.ensureInitialized();
|
final TestWidgetsFlutterBinding binding = TestWidgetsFlutterBinding.ensureInitialized();
|
||||||
@ -179,11 +165,9 @@ void testWidgets(
|
|||||||
Object? memento;
|
Object? memento;
|
||||||
try {
|
try {
|
||||||
memento = await variant.setUp(value);
|
memento = await variant.setUp(value);
|
||||||
maybeSetupLeakTrackingForTest(experimentalLeakTesting, combinedDescription);
|
|
||||||
await callback(tester);
|
await callback(tester);
|
||||||
} finally {
|
} finally {
|
||||||
await variant.tearDown(value, memento);
|
await variant.tearDown(value, memento);
|
||||||
maybeTearDownLeakTrackingForTest();
|
|
||||||
}
|
}
|
||||||
semanticsHandle?.dispose();
|
semanticsHandle?.dispose();
|
||||||
},
|
},
|
||||||
|
@ -1,54 +0,0 @@
|
|||||||
// 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:leak_tracker_flutter_testing/leak_tracker_flutter_testing.dart';
|
|
||||||
|
|
||||||
class LeakTrackedClass {
|
|
||||||
LeakTrackedClass() {
|
|
||||||
LeakTracking.dispatchObjectCreated(
|
|
||||||
library: library,
|
|
||||||
className: '$LeakTrackedClass',
|
|
||||||
object: this,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
static const String library = 'package:my_package/lib/src/my_lib.dart';
|
|
||||||
|
|
||||||
void dispose() {
|
|
||||||
LeakTracking.dispatchObjectDisposed(object: this);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
final List<LeakTrackedClass> _notGCedObjects = <LeakTrackedClass>[];
|
|
||||||
|
|
||||||
class LeakingClass {
|
|
||||||
LeakingClass() {
|
|
||||||
_notGCedObjects.add(LeakTrackedClass()..dispose());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class StatelessLeakingWidget extends StatelessWidget {
|
|
||||||
StatelessLeakingWidget({
|
|
||||||
super.key,
|
|
||||||
this.notGCed = true,
|
|
||||||
this.notDisposed = true,
|
|
||||||
}) {
|
|
||||||
if (notGCed) {
|
|
||||||
_notGCedObjects.add(LeakTrackedClass()..dispose());
|
|
||||||
}
|
|
||||||
if (notDisposed) {
|
|
||||||
// ignore: unused_local_variable, it is unused intentionally, to illustrate not disposed object.
|
|
||||||
final LeakTrackedClass notDisposedObject = LeakTrackedClass();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
final bool notGCed;
|
|
||||||
final bool notDisposed;
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
return const Placeholder();
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,204 +0,0 @@
|
|||||||
// 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/cupertino.dart';
|
|
||||||
import 'package:flutter/material.dart';
|
|
||||||
import 'package:flutter_test/flutter_test.dart';
|
|
||||||
import 'package:leak_tracker_flutter_testing/leak_tracker_flutter_testing.dart';
|
|
||||||
|
|
||||||
import 'utils/leaking_classes.dart';
|
|
||||||
|
|
||||||
late final String _test1TrackingOnNoLeaks;
|
|
||||||
late final String _test2TrackingOffLeaks;
|
|
||||||
late final String _test3TrackingOnLeaks;
|
|
||||||
late final String _test4TrackingOnWithCreationStackTrace;
|
|
||||||
late final String _test5TrackingOnWithDisposalStackTrace;
|
|
||||||
late final String _test6TrackingOnNoLeaks;
|
|
||||||
late final String _test7TrackingOnNoLeaks;
|
|
||||||
late final String _test8TrackingOnNotDisposed;
|
|
||||||
|
|
||||||
void main() {
|
|
||||||
LeakTesting.collectedLeaksReporter = (Leaks leaks) => verifyLeaks(leaks);
|
|
||||||
LeakTesting.settings = LeakTesting.settings.copyWith(ignore: false);
|
|
||||||
|
|
||||||
// It is important that the test file starts with group, to test that leaks are collected for all tests after group too.
|
|
||||||
group('Group', () {
|
|
||||||
testWidgets('test', (_) async {
|
|
||||||
StatelessLeakingWidget();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
testWidgets(_test1TrackingOnNoLeaks = 'test1, tracking-on, no leaks', (WidgetTester widgetTester) async {
|
|
||||||
expect(LeakTracking.isStarted, true);
|
|
||||||
expect(LeakTracking.phase.name, _test1TrackingOnNoLeaks);
|
|
||||||
expect(LeakTracking.phase.ignoreLeaks, false);
|
|
||||||
await widgetTester.pumpWidget(Container());
|
|
||||||
});
|
|
||||||
|
|
||||||
testWidgets(
|
|
||||||
_test2TrackingOffLeaks = 'test2, tracking-off, leaks',
|
|
||||||
experimentalLeakTesting: LeakTesting.settings.withIgnoredAll(),
|
|
||||||
(WidgetTester widgetTester) async {
|
|
||||||
expect(LeakTracking.isStarted, true);
|
|
||||||
expect(LeakTracking.phase.name, null);
|
|
||||||
expect(LeakTracking.phase.ignoreLeaks, true);
|
|
||||||
await widgetTester.pumpWidget(StatelessLeakingWidget());
|
|
||||||
});
|
|
||||||
|
|
||||||
testWidgets(_test3TrackingOnLeaks = 'test3, tracking-on, leaks', (WidgetTester widgetTester) async {
|
|
||||||
expect(LeakTracking.isStarted, true);
|
|
||||||
expect(LeakTracking.phase.name, _test3TrackingOnLeaks);
|
|
||||||
expect(LeakTracking.phase.ignoreLeaks, false);
|
|
||||||
await widgetTester.pumpWidget(StatelessLeakingWidget());
|
|
||||||
});
|
|
||||||
|
|
||||||
testWidgets(
|
|
||||||
_test4TrackingOnWithCreationStackTrace = 'test4, tracking-on, with creation stack trace',
|
|
||||||
experimentalLeakTesting: LeakTesting.settings.withCreationStackTrace(),
|
|
||||||
(WidgetTester widgetTester) async {
|
|
||||||
expect(LeakTracking.isStarted, true);
|
|
||||||
expect(LeakTracking.phase.name, _test4TrackingOnWithCreationStackTrace);
|
|
||||||
expect(LeakTracking.phase.ignoreLeaks, false);
|
|
||||||
await widgetTester.pumpWidget(StatelessLeakingWidget());
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
testWidgets(
|
|
||||||
_test5TrackingOnWithDisposalStackTrace = 'test5, tracking-on, with disposal stack trace',
|
|
||||||
experimentalLeakTesting: LeakTesting.settings.withDisposalStackTrace(),
|
|
||||||
(WidgetTester widgetTester) async {
|
|
||||||
expect(LeakTracking.isStarted, true);
|
|
||||||
expect(LeakTracking.phase.name, _test5TrackingOnWithDisposalStackTrace);
|
|
||||||
expect(LeakTracking.phase.ignoreLeaks, false);
|
|
||||||
await widgetTester.pumpWidget(StatelessLeakingWidget());
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
testWidgets(_test6TrackingOnNoLeaks = 'test6, tracking-on, no leaks', (_) async {
|
|
||||||
LeakTrackedClass().dispose();
|
|
||||||
});
|
|
||||||
|
|
||||||
testWidgets(_test7TrackingOnNoLeaks = 'test7, tracking-on, tear down, no leaks', (_) async {
|
|
||||||
final LeakTrackedClass myClass = LeakTrackedClass();
|
|
||||||
addTearDown(myClass.dispose);
|
|
||||||
});
|
|
||||||
|
|
||||||
testWidgets(_test8TrackingOnNotDisposed = 'test8, tracking-on, not disposed leak', (_) async {
|
|
||||||
expect(LeakTracking.isStarted, true);
|
|
||||||
expect(LeakTracking.phase.name, _test8TrackingOnNotDisposed);
|
|
||||||
expect(LeakTracking.phase.ignoreLeaks, false);
|
|
||||||
LeakTrackedClass();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
int _leakReporterInvocationCount = 0;
|
|
||||||
|
|
||||||
void verifyLeaks(Leaks leaks) {
|
|
||||||
_leakReporterInvocationCount += 1;
|
|
||||||
expect(_leakReporterInvocationCount, 1);
|
|
||||||
|
|
||||||
try {
|
|
||||||
expect(leaks, isLeakFree);
|
|
||||||
} on TestFailure catch (e) {
|
|
||||||
expect(e.message, contains('https://github.com/dart-lang/leak_tracker'));
|
|
||||||
|
|
||||||
expect(e.message, isNot(contains(_test1TrackingOnNoLeaks)));
|
|
||||||
expect(e.message, isNot(contains(_test2TrackingOffLeaks)));
|
|
||||||
expect(e.message, contains('test: $_test3TrackingOnLeaks'));
|
|
||||||
expect(e.message, contains('test: $_test4TrackingOnWithCreationStackTrace'));
|
|
||||||
expect(e.message, contains('test: $_test5TrackingOnWithDisposalStackTrace'));
|
|
||||||
expect(e.message, isNot(contains(_test6TrackingOnNoLeaks)));
|
|
||||||
expect(e.message, isNot(contains(_test7TrackingOnNoLeaks)));
|
|
||||||
expect(e.message, contains('test: $_test8TrackingOnNotDisposed'));
|
|
||||||
}
|
|
||||||
|
|
||||||
_verifyLeaks(
|
|
||||||
leaks,
|
|
||||||
_test3TrackingOnLeaks,
|
|
||||||
notDisposed: 1,
|
|
||||||
notGCed: 1,
|
|
||||||
expectedContextKeys: <LeakType, List<String>>{
|
|
||||||
LeakType.notGCed: <String>[],
|
|
||||||
LeakType.notDisposed: <String>[],
|
|
||||||
},
|
|
||||||
);
|
|
||||||
_verifyLeaks(
|
|
||||||
leaks,
|
|
||||||
_test4TrackingOnWithCreationStackTrace,
|
|
||||||
notDisposed: 1,
|
|
||||||
notGCed: 1,
|
|
||||||
expectedContextKeys: <LeakType, List<String>>{
|
|
||||||
LeakType.notGCed: <String>['start'],
|
|
||||||
LeakType.notDisposed: <String>['start'],
|
|
||||||
},
|
|
||||||
);
|
|
||||||
_verifyLeaks(
|
|
||||||
leaks,
|
|
||||||
_test5TrackingOnWithDisposalStackTrace,
|
|
||||||
notDisposed: 1,
|
|
||||||
notGCed: 1,
|
|
||||||
expectedContextKeys: <LeakType, List<String>>{
|
|
||||||
LeakType.notGCed: <String>['disposal'],
|
|
||||||
LeakType.notDisposed: <String>[],
|
|
||||||
},
|
|
||||||
);
|
|
||||||
_verifyLeaks(
|
|
||||||
leaks,
|
|
||||||
_test8TrackingOnNotDisposed,
|
|
||||||
notDisposed: 1,
|
|
||||||
expectedContextKeys: <LeakType, List<String>>{},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Verifies [allLeaks] contain expected number of leaks for the test [testDescription].
|
|
||||||
///
|
|
||||||
/// [notDisposed] and [notGCed] set number for expected leaks by leak type.
|
|
||||||
/// The method will fail if the leaks context does not contain [expectedContextKeys].
|
|
||||||
void _verifyLeaks(
|
|
||||||
Leaks allLeaks,
|
|
||||||
String testDescription, {
|
|
||||||
int notDisposed = 0,
|
|
||||||
int notGCed = 0,
|
|
||||||
Map<LeakType, List<String>> expectedContextKeys = const <LeakType, List<String>>{},
|
|
||||||
}) {
|
|
||||||
final Leaks testLeaks = Leaks(
|
|
||||||
allLeaks.byType.map(
|
|
||||||
(LeakType key, List<LeakReport> value) =>
|
|
||||||
MapEntry<LeakType, List<LeakReport>>(key, value.where((LeakReport leak) => leak.phase == testDescription).toList()),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
|
|
||||||
for (final LeakType type in expectedContextKeys.keys) {
|
|
||||||
final List<LeakReport> leaks = testLeaks.byType[type]!;
|
|
||||||
final List<String> expectedKeys = expectedContextKeys[type]!..sort();
|
|
||||||
for (final LeakReport leak in leaks) {
|
|
||||||
final List<String> actualKeys = leak.context?.keys.toList() ?? <String>[];
|
|
||||||
expect(actualKeys..sort(), equals(expectedKeys), reason: '$testDescription, $type');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
_verifyLeakList(
|
|
||||||
testLeaks.notDisposed,
|
|
||||||
notDisposed,
|
|
||||||
testDescription,
|
|
||||||
);
|
|
||||||
_verifyLeakList(
|
|
||||||
testLeaks.notGCed,
|
|
||||||
notGCed,
|
|
||||||
testDescription,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
void _verifyLeakList(
|
|
||||||
List<LeakReport> list,
|
|
||||||
int expectedCount,
|
|
||||||
String testDescription,
|
|
||||||
) {
|
|
||||||
expect(list.length, expectedCount, reason: testDescription);
|
|
||||||
|
|
||||||
for (final LeakReport leak in list) {
|
|
||||||
expect(leak.trackedClass, contains(LeakTrackedClass.library));
|
|
||||||
expect(leak.trackedClass, contains('$LeakTrackedClass'));
|
|
||||||
}
|
|
||||||
}
|
|
Loading…
x
Reference in New Issue
Block a user