diff --git a/pkgs/leak_tracker/CHANGELOG.md b/pkgs/leak_tracker/CHANGELOG.md index e571d441..34519737 100644 --- a/pkgs/leak_tracker/CHANGELOG.md +++ b/pkgs/leak_tracker/CHANGELOG.md @@ -1,3 +1,7 @@ +## 9.0.17 + +* Move LeakTesting to leak_tracker_testing. + ## 9.0.16 * Stub web implementation for retaining path to serve G3. diff --git a/pkgs/leak_tracker/lib/leak_tracker.dart b/pkgs/leak_tracker/lib/leak_tracker.dart index c84268b9..1ba13753 100644 --- a/pkgs/leak_tracker/lib/leak_tracker.dart +++ b/pkgs/leak_tracker/lib/leak_tracker.dart @@ -3,7 +3,6 @@ // BSD-style license that can be found in the LICENSE file. export 'src/leak_tracking/helpers.dart'; -export 'src/leak_tracking/leak_testing.dart'; export 'src/leak_tracking/leak_tracking.dart'; export 'src/leak_tracking/primitives/model.dart'; export 'src/shared/shared_model.dart'; diff --git a/pkgs/leak_tracker/pubspec.yaml b/pkgs/leak_tracker/pubspec.yaml index 97bba935..3b79e3ab 100644 --- a/pkgs/leak_tracker/pubspec.yaml +++ b/pkgs/leak_tracker/pubspec.yaml @@ -1,5 +1,5 @@ name: leak_tracker -version: 9.0.16 +version: 9.0.17 description: A framework for memory leak tracking for Dart and Flutter applications. repository: https://github.com/dart-lang/leak_tracker/tree/main/pkgs/leak_tracker diff --git a/pkgs/leak_tracker_flutter_testing/CHANGELOG.md b/pkgs/leak_tracker_flutter_testing/CHANGELOG.md index 900b7738..d996cba0 100644 --- a/pkgs/leak_tracker_flutter_testing/CHANGELOG.md +++ b/pkgs/leak_tracker_flutter_testing/CHANGELOG.md @@ -1,6 +1,7 @@ -## 1.0.11-wip +## 1.0.12 -* Updated to use `package:lints/recommended.yaml` for analysis. +* Update to use `package:lints/recommended.yaml` for analysis. +* Add API to integrate with testWidgets. ## 1.0.10 diff --git a/pkgs/leak_tracker_flutter_testing/lib/leak_tracker_flutter_testing.dart b/pkgs/leak_tracker_flutter_testing/lib/leak_tracker_flutter_testing.dart index 7ee10e67..3bf816ab 100644 --- a/pkgs/leak_tracker_flutter_testing/lib/leak_tracker_flutter_testing.dart +++ b/pkgs/leak_tracker_flutter_testing/lib/leak_tracker_flutter_testing.dart @@ -5,3 +5,8 @@ export 'src/matchers.dart'; export 'src/model.dart'; export 'src/test_widgets.dart'; +export 'src/testing.dart'; +export 'package:leak_tracker/leak_tracker.dart' + show Leaks, LeakTracking, IgnoredLeaks, LeakType, LeakReport; +export 'package:leak_tracker_testing/leak_tracker_testing.dart' + show isLeakFree, LeakTesting; diff --git a/pkgs/leak_tracker_flutter_testing/lib/src/test_widgets.dart b/pkgs/leak_tracker_flutter_testing/lib/src/test_widgets.dart index e4cbae6d..523aaac2 100644 --- a/pkgs/leak_tracker_flutter_testing/lib/src/test_widgets.dart +++ b/pkgs/leak_tracker_flutter_testing/lib/src/test_widgets.dart @@ -8,6 +8,9 @@ import 'package:leak_tracker/leak_tracker.dart'; import 'package:leak_tracker_testing/leak_tracker_testing.dart'; import 'package:meta/meta.dart'; +// TODO: remove this library +// https://github.com/flutter/flutter/issues/135856 + void _flutterEventToLeakTracker(ObjectEvent event) { return LeakTracking.dispatchObjectEvent(event.toMap()); } diff --git a/pkgs/leak_tracker_flutter_testing/lib/src/testing.dart b/pkgs/leak_tracker_flutter_testing/lib/src/testing.dart new file mode 100644 index 00000000..7d661fdb --- /dev/null +++ b/pkgs/leak_tracker_flutter_testing/lib/src/testing.dart @@ -0,0 +1,100 @@ +// Copyright (c) 2023, the Dart project authors. Please see the AUTHORS file +// for details. 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/foundation.dart'; +import 'package:leak_tracker/leak_tracker.dart'; +import 'package:leak_tracker_testing/leak_tracker_testing.dart'; + +/// Makes sure leak tracking is set up for a test. +/// +/// If `settings.ignore` is true, the method is noop. +/// If leak tracking is not started, starts it. +/// Configures `LeakTracking.phase` to match [settings]. +void maybeSetupLeakTrackingForTest( + LeakTesting? settings, + String testDescription, +) { + final leakTesting = settings ?? LeakTesting.settings; + if (leakTesting.ignore) return; + + if (!_checkPlatformAndMayBePrintWarning( + platformName: defaultTargetPlatform.name, isBrowser: kIsWeb)) { + return; + } + + _maybeStartLeakTracking(); + + final PhaseSettings phase = PhaseSettings( + name: testDescription, + leakDiagnosticConfig: leakTesting.leakDiagnosticConfig, + ignoredLeaks: leakTesting.ignoredLeaks, + baselining: leakTesting.baselining, + ignoreLeaks: leakTesting.ignore, + ); + + LeakTracking.phase = phase; +} + +/// If leak tracking is enabled, stops it and declares notDisposed objects as leaks. +void maybeTearDownLeakTrackingForTest() { + if (!LeakTracking.isStarted || LeakTracking.phase.ignoreLeaks) return; + LeakTracking.phase = const PhaseSettings.ignored(); +} + +/// Should be invoked after execution of all tests to report found leaks. +/// +/// Is noop if leak tracking is not started. +Future maybeTearDownLeakTrackingForAll() async { + if (!LeakTracking.isStarted) { + return; + } + + // The listener is not added/removed for each test, + // because GC may happen after test is complete. + MemoryAllocations.instance.removeListener(_dispatchFlutterEventToLeakTracker); + await forceGC(fullGcCycles: defaultNumberOfGcCycles); + LeakTracking.declareNotDisposedObjectsAsLeaks(); + final Leaks leaks = await LeakTracking.collectLeaks(); + LeakTracking.stop(); + + LeakTesting.collectedLeaksReporter(leaks); +} + +void _dispatchFlutterEventToLeakTracker(ObjectEvent event) { + return LeakTracking.dispatchObjectEvent(event.toMap()); +} + +bool _notSupportedWarningPrinted = false; + +/// Checks if platform supported and, if no, prints warning if the warning is needed. +/// +/// Warning is printed one time if `LeakTracking.warnForNotSupportedPlatforms` is true. +bool _checkPlatformAndMayBePrintWarning( + {required String platformName, required bool isBrowser}) { + final isSupported = !isBrowser; + + if (isSupported) return true; + + final shouldPrintWarning = + LeakTracking.warnForUnsupportedPlatforms && !_notSupportedWarningPrinted; + + if (!shouldPrintWarning) return false; + + _notSupportedWarningPrinted = true; + debugPrint( + "Leak tracking is not supported on the platform '$platformName'.\n" + 'To turn off this message, set `LeakTracking.warnForNotSupportedPlatforms` to false.', + ); + + return false; +} + +/// Starts leak tracking with all leaks ignored. +void _maybeStartLeakTracking() { + if (LeakTracking.isStarted) return; + + LeakTracking.phase = const PhaseSettings.ignored(); + LeakTracking.start(config: LeakTrackingConfig.passive()); + MemoryAllocations.instance.addListener(_dispatchFlutterEventToLeakTracker); +} diff --git a/pkgs/leak_tracker_flutter_testing/pubspec.yaml b/pkgs/leak_tracker_flutter_testing/pubspec.yaml index c92ad730..45f088f7 100644 --- a/pkgs/leak_tracker_flutter_testing/pubspec.yaml +++ b/pkgs/leak_tracker_flutter_testing/pubspec.yaml @@ -1,5 +1,5 @@ name: leak_tracker_flutter_testing -version: 1.0.12-wip +version: 1.0.12 description: An internal package to test leak tracking with Flutter. repository: https://github.com/dart-lang/leak_tracker/tree/main/pkgs/leak_tracker_flutter_testing @@ -13,6 +13,7 @@ dependencies: sdk: flutter leak_tracker: ^9.0.10 leak_tracker_testing: ^1.0.5 + matcher: ^0.12.16 meta: ^1.8.0 dev_dependencies: diff --git a/pkgs/leak_tracker_flutter_testing/test/tests/end_to_end/failing_tests/flutter_test_config.dart b/pkgs/leak_tracker_flutter_testing/test/tests/end_to_end/failing_tests/flutter_test_config.dart index f0025dd4..8b3db857 100644 --- a/pkgs/leak_tracker_flutter_testing/test/tests/end_to_end/failing_tests/flutter_test_config.dart +++ b/pkgs/leak_tracker_flutter_testing/test/tests/end_to_end/failing_tests/flutter_test_config.dart @@ -5,7 +5,6 @@ import 'dart:async'; import 'package:flutter_test/flutter_test.dart'; -import 'package:leak_tracker/leak_tracker.dart'; import 'package:leak_tracker_flutter_testing/leak_tracker_flutter_testing.dart'; import 'failure_test.dart'; diff --git a/pkgs/leak_tracker_flutter_testing/test/tests/end_to_end/no_config_test.dart b/pkgs/leak_tracker_flutter_testing/test/tests/end_to_end/no_config_test.dart index 9c26b949..22bb04ac 100644 --- a/pkgs/leak_tracker_flutter_testing/test/tests/end_to_end/no_config_test.dart +++ b/pkgs/leak_tracker_flutter_testing/test/tests/end_to_end/no_config_test.dart @@ -3,7 +3,6 @@ // BSD-style license that can be found in the LICENSE file. import 'package:flutter_test/flutter_test.dart'; -import 'package:leak_tracker/leak_tracker.dart'; import 'package:leak_tracker_flutter_testing/leak_tracker_flutter_testing.dart'; import '../../test_infra/flutter_classes.dart'; diff --git a/pkgs/leak_tracker_flutter_testing/test/tests/end_to_end/per_test_config_test/flutter_test_config.dart b/pkgs/leak_tracker_flutter_testing/test/tests/end_to_end/per_test_config_test/flutter_test_config.dart index aaebf9f0..8bfca8b7 100644 --- a/pkgs/leak_tracker_flutter_testing/test/tests/end_to_end/per_test_config_test/flutter_test_config.dart +++ b/pkgs/leak_tracker_flutter_testing/test/tests/end_to_end/per_test_config_test/flutter_test_config.dart @@ -5,9 +5,7 @@ import 'dart:async'; import 'package:flutter_test/flutter_test.dart'; -import 'package:leak_tracker/leak_tracker.dart'; import 'package:leak_tracker_flutter_testing/leak_tracker_flutter_testing.dart'; -import 'package:leak_tracker_testing/leak_tracker_testing.dart'; import '../../../test_infra/dart_classes.dart'; import 'per_test_config_test.dart'; diff --git a/pkgs/leak_tracker_flutter_testing/test/tests/end_to_end/per_test_config_test/per_test_config_test.dart b/pkgs/leak_tracker_flutter_testing/test/tests/end_to_end/per_test_config_test/per_test_config_test.dart index e232b0e7..855b6cf2 100644 --- a/pkgs/leak_tracker_flutter_testing/test/tests/end_to_end/per_test_config_test/per_test_config_test.dart +++ b/pkgs/leak_tracker_flutter_testing/test/tests/end_to_end/per_test_config_test/per_test_config_test.dart @@ -4,7 +4,6 @@ import 'package:flutter/widgets.dart'; import 'package:flutter_test/flutter_test.dart'; -import 'package:leak_tracker/leak_tracker.dart'; import 'package:leak_tracker_flutter_testing/leak_tracker_flutter_testing.dart'; import '../../../test_infra/flutter_classes.dart'; diff --git a/pkgs/leak_tracker_flutter_testing/test/tests/end_to_end/settings_not_disposed_off_test/flutter_test_config.dart b/pkgs/leak_tracker_flutter_testing/test/tests/end_to_end/settings_not_disposed_off_test/flutter_test_config.dart index a87f04c3..0815e3b4 100644 --- a/pkgs/leak_tracker_flutter_testing/test/tests/end_to_end/settings_not_disposed_off_test/flutter_test_config.dart +++ b/pkgs/leak_tracker_flutter_testing/test/tests/end_to_end/settings_not_disposed_off_test/flutter_test_config.dart @@ -5,7 +5,6 @@ import 'dart:async'; import 'package:flutter_test/flutter_test.dart'; -import 'package:leak_tracker/leak_tracker.dart'; import 'package:leak_tracker_flutter_testing/leak_tracker_flutter_testing.dart'; /// Test configuration for each test library in this directory. diff --git a/pkgs/leak_tracker_flutter_testing/test/tests/end_to_end/settings_not_gced_off_test/flutter_test_config.dart b/pkgs/leak_tracker_flutter_testing/test/tests/end_to_end/settings_not_gced_off_test/flutter_test_config.dart index 3867e80a..bb1e25ba 100644 --- a/pkgs/leak_tracker_flutter_testing/test/tests/end_to_end/settings_not_gced_off_test/flutter_test_config.dart +++ b/pkgs/leak_tracker_flutter_testing/test/tests/end_to_end/settings_not_gced_off_test/flutter_test_config.dart @@ -5,7 +5,6 @@ import 'dart:async'; import 'package:flutter_test/flutter_test.dart'; -import 'package:leak_tracker/leak_tracker.dart'; import 'package:leak_tracker_flutter_testing/leak_tracker_flutter_testing.dart'; /// Test configuration for each test library in this directory. diff --git a/pkgs/leak_tracker_flutter_testing/test/tests/leak_tracking_for_tests_test.dart b/pkgs/leak_tracker_flutter_testing/test/tests/leak_tracking_for_tests_test.dart index e51c7103..2220eb37 100644 --- a/pkgs/leak_tracker_flutter_testing/test/tests/leak_tracking_for_tests_test.dart +++ b/pkgs/leak_tracker_flutter_testing/test/tests/leak_tracking_for_tests_test.dart @@ -4,6 +4,7 @@ import 'package:flutter_test/flutter_test.dart'; import 'package:leak_tracker/leak_tracker.dart'; +import 'package:leak_tracker_testing/leak_tracker_testing.dart'; class _Classes { static const anyLeak1 = 'anyLeak1'; diff --git a/pkgs/leak_tracker_flutter_testing/test/tests/testing_test.dart b/pkgs/leak_tracker_flutter_testing/test/tests/testing_test.dart new file mode 100644 index 00000000..44c69d79 --- /dev/null +++ b/pkgs/leak_tracker_flutter_testing/test/tests/testing_test.dart @@ -0,0 +1,83 @@ +// Copyright (c) 2023, the Dart project authors. Please see the AUTHORS file +// for details. 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_test/flutter_test.dart'; +import 'package:leak_tracker_flutter_testing/leak_tracker_flutter_testing.dart'; + +final LeakTesting settings = + LeakTesting.settings.withIgnored(allNotDisposed: true, allNotGCed: true); + +void main() { + group('maybeSetupLeakTrackingForTest', () { + setUp(() { + LeakTesting.settings = LeakTesting.settings.withTrackedAll(); + }); + + tearDown(() { + LeakTracking.stop(); + }); + + test('If settings is null, respects globals', () { + maybeSetupLeakTrackingForTest(null, 'myTest1'); + expect(LeakTracking.isStarted, true); + expect(LeakTracking.phase.name, 'myTest1'); + expect(LeakTracking.phase.ignoreLeaks, LeakTesting.settings.ignore); + expect( + LeakTracking.phase.ignoredLeaks, + LeakTesting.settings.ignoredLeaks, + ); + }); + + test('If settings are provided, respects them', () { + maybeSetupLeakTrackingForTest(settings, 'myTest2'); + expect(LeakTracking.isStarted, true); + expect(LeakTracking.phase.name, 'myTest2'); + expect(LeakTracking.phase.ignoreLeaks, settings.ignore); + expect( + LeakTracking.phase.ignoredLeaks, + settings.ignoredLeaks, + ); + }); + }); + + group('maybeTearDownLeakTrackingForTest', () { + setUp(() { + LeakTesting.settings = LeakTesting.settings.withTrackedAll(); + maybeSetupLeakTrackingForTest(null, 'myTest1'); + }); + + tearDown(() { + LeakTracking.stop(); + }); + + test('Pauses leak tracking and can be invoiked twice', () { + maybeTearDownLeakTrackingForTest(); + expect(LeakTracking.phase.name, null); + expect(LeakTracking.isStarted, true); + expect(LeakTracking.phase.ignoreLeaks, true); + + maybeTearDownLeakTrackingForTest(); + expect(LeakTracking.phase.name, null); + expect(LeakTracking.isStarted, true); + expect(LeakTracking.phase.ignoreLeaks, true); + }); + }); + + group('maybeTearDownLeakTrackingForAll', () { + setUp(() { + LeakTesting.settings = LeakTesting.settings.withTrackedAll(); + maybeSetupLeakTrackingForTest(null, 'myTest1'); + maybeTearDownLeakTrackingForTest(); + }); + + tearDown(() { + LeakTracking.stop(); + }); + + test('Stops leak tracking', () async { + await maybeTearDownLeakTrackingForAll(); + expect(LeakTracking.isStarted, false); + }); + }); +} diff --git a/pkgs/leak_tracker_testing/CHANGELOG.md b/pkgs/leak_tracker_testing/CHANGELOG.md index 16240a8a..9ce6acfa 100644 --- a/pkgs/leak_tracker_testing/CHANGELOG.md +++ b/pkgs/leak_tracker_testing/CHANGELOG.md @@ -1,6 +1,7 @@ -## 1.0.6-wip +## 1.0.6 * Updated to use `package:lints/recommended.yaml` for analysis. +* Move LeakTesting from leak_tracker to this library. ## 1.0.5 diff --git a/pkgs/leak_tracker_testing/lib/leak_tracker_testing.dart b/pkgs/leak_tracker_testing/lib/leak_tracker_testing.dart index 8dd06554..77e69558 100644 --- a/pkgs/leak_tracker_testing/lib/leak_tracker_testing.dart +++ b/pkgs/leak_tracker_testing/lib/leak_tracker_testing.dart @@ -3,3 +3,4 @@ // BSD-style license that can be found in the LICENSE file. export 'src/matchers.dart'; +export 'src/leak_testing.dart'; diff --git a/pkgs/leak_tracker/lib/src/leak_tracking/leak_testing.dart b/pkgs/leak_tracker_testing/lib/src/leak_testing.dart similarity index 93% rename from pkgs/leak_tracker/lib/src/leak_tracking/leak_testing.dart rename to pkgs/leak_tracker_testing/lib/src/leak_testing.dart index 9e5393db..f19ce334 100644 --- a/pkgs/leak_tracker/lib/src/leak_tracking/leak_testing.dart +++ b/pkgs/leak_tracker_testing/lib/src/leak_testing.dart @@ -2,10 +2,11 @@ // for details. 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:leak_tracker/leak_tracker.dart'; +import 'package:matcher/expect.dart'; import 'package:meta/meta.dart'; -import '../shared/shared_model.dart'; -import 'primitives/model.dart'; +import 'matchers.dart'; void _emptyLeakHandler(Leaks leaks) {} @@ -43,6 +44,16 @@ class LeakTesting { this.baselining = const MemoryBaselining.none(), }); + /// Handler for memory leaks found in tests. + /// + /// Set it to analyse the leaks programmatically. + /// The handler is invoked on tear down of the test run. + /// The default reporter fails in case of found leaks. + /// + /// Used to test leak tracking functionality. + static LeaksCallback collectedLeaksReporter = + (Leaks leaks) => expect(leaks, isLeakFree); + /// Current configuration for leak tracking. /// /// Is used by `testWidgets` if configuration is not provided for a test. diff --git a/pkgs/leak_tracker_testing/pubspec.yaml b/pkgs/leak_tracker_testing/pubspec.yaml index 1f736b47..7c7adc12 100644 --- a/pkgs/leak_tracker_testing/pubspec.yaml +++ b/pkgs/leak_tracker_testing/pubspec.yaml @@ -1,5 +1,5 @@ name: leak_tracker_testing -version: 1.0.6-wip +version: 1.0.6 description: Leak tracking code intended for usage in tests. repository: https://github.com/dart-lang/leak_tracker/tree/main/pkgs/leak_tracker_testing @@ -12,6 +12,7 @@ environment: dependencies: leak_tracker: '>=9.0.0 <11.0.0' matcher: ^0.12.16 + meta: ^1.11.0 dev_dependencies: layerlens: ^1.0.0 diff --git a/pkgs/leak_tracker/test/tests/leak_tracking/leak_testing_test.dart b/pkgs/leak_tracker_testing/test/leak_testing_test.dart similarity index 98% rename from pkgs/leak_tracker/test/tests/leak_tracking/leak_testing_test.dart rename to pkgs/leak_tracker_testing/test/leak_testing_test.dart index 261e7ed8..24704324 100644 --- a/pkgs/leak_tracker/test/tests/leak_tracking/leak_testing_test.dart +++ b/pkgs/leak_tracker_testing/test/leak_testing_test.dart @@ -2,8 +2,8 @@ // for details. 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:leak_tracker/src/leak_tracking/leak_testing.dart'; import 'package:leak_tracker/src/leak_tracking/primitives/model.dart'; +import 'package:leak_tracker_testing/src/leak_testing.dart'; import 'package:test/test.dart'; void main() {