blob: 3a52362b6befea83a513c6abd22ada6192ed30b9 [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,
) {
if (!LeakTesting.enabled) return;
final leakTesting = settings ?? LeakTesting.settings;
if (leakTesting.ignore) 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 (!LeakTesting.enabled ||
!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 (!LeakTesting.enabled || !LeakTracking.isStarted) {
// Reporter is invoked so that tests can verify the number of
// collected leaks is as expected.
LeakTesting.collectedLeaksReporter(Leaks({}));
return;
}
// The listener is not added/removed for each test,
// because GC may happen after test is complete.
FlutterMemoryAllocations.instance
.removeListener(_dispatchFlutterEventToLeakTracker);
final notGCedTracked =
!LeakTesting.settings.ignoredLeaks.experimentalNotGCed.ignoreAll;
if (notGCedTracked) {
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());
}
/// Starts leak tracking with all leaks ignored.
void _maybeStartLeakTracking() {
if (LeakTracking.isStarted) return;
LeakTracking.phase = const PhaseSettings.ignored();
LeakTracking.start(config: LeakTrackingConfig.passive());
FlutterMemoryAllocations.instance
.addListener(_dispatchFlutterEventToLeakTracker);
}