blob: c3e4fc01e98e1c3a50e92a436dc295c059a1f7f8 [file] [log] [blame]
// 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 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 = 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);
}