blob: 7d661fdb6989f6912a28e8cf783969de878118c5 [file]
// 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;
/// 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);
}