Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add API to integrate with testWidgets. #188

Merged
merged 17 commits into from
Dec 13, 2023
4 changes: 4 additions & 0 deletions pkgs/leak_tracker/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -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.
Expand Down
1 change: 0 additions & 1 deletion pkgs/leak_tracker/lib/leak_tracker.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down
2 changes: 1 addition & 1 deletion pkgs/leak_tracker/pubspec.yaml
Original file line number Diff line number Diff line change
@@ -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

Expand Down
5 changes: 3 additions & 2 deletions pkgs/leak_tracker_flutter_testing/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -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

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
3 changes: 3 additions & 0 deletions pkgs/leak_tracker_flutter_testing/lib/src/test_widgets.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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());
}
Expand Down
100 changes: 100 additions & 0 deletions pkgs/leak_tracker_flutter_testing/lib/src/testing.dart
Original file line number Diff line number Diff line change
@@ -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<void> 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;
polina-c marked this conversation as resolved.
Show resolved Hide resolved

/// 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);
}
3 changes: 2 additions & 1 deletion pkgs/leak_tracker_flutter_testing/pubspec.yaml
Original file line number Diff line number Diff line change
@@ -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

Expand All @@ -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:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down
83 changes: 83 additions & 0 deletions pkgs/leak_tracker_flutter_testing/test/tests/testing_test.dart
Original file line number Diff line number Diff line change
@@ -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);
});
});
}
3 changes: 2 additions & 1 deletion pkgs/leak_tracker_testing/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -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

Expand Down
1 change: 1 addition & 0 deletions pkgs/leak_tracker_testing/lib/leak_tracker_testing.dart
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,4 @@
// BSD-style license that can be found in the LICENSE file.

export 'src/matchers.dart';
export 'src/leak_testing.dart';
Original file line number Diff line number Diff line change
Expand Up @@ -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) {}

Expand Down Expand Up @@ -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.
Expand Down
3 changes: 2 additions & 1 deletion pkgs/leak_tracker_testing/pubspec.yaml
Original file line number Diff line number Diff line change
@@ -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

Expand All @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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() {
Expand Down