// Copyright 2014 The Flutter Authors. 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:flutter_test/flutter_test.dart'; import 'package:leak_tracker/leak_tracker.dart'; typedef LeaksObtainer = void Function(Leaks foundLeaks); /// Wrapper for [withLeakTracking] with Flutter specific functionality. /// /// The method will fail if wrapped code contains memory leaks. /// /// See details in documentation for `withLeakTracking` at /// https://github.com/dart-lang/leak_tracker/blob/main/lib/src/orchestration.dart#withLeakTracking /// /// The Flutter related enhancements are: /// 1. Listens to [MemoryAllocations] events. /// 2. Uses `tester.runAsync` for leak detection if [tester] is provided. /// /// If you use [testWidgets], pass [tester] to avoid async issues in leak processing. /// Pass null otherwise. /// /// Pass [leaksObtainer] if you want to get leak information before /// the method failure. Future<void> withFlutterLeakTracking( DartAsyncCallback callback, { required WidgetTester? tester, StackTraceCollectionConfig stackTraceCollectionConfig = const StackTraceCollectionConfig(), Duration? timeoutForFinalGarbageCollection, LeaksObtainer? leaksObtainer, }) async { // The method is copied (with improvements) from // `package:leak_tracker/test/test_infra/flutter_helpers.dart`. // The method is not combined with [testWidgets], because the combining will // impact VSCode's ability to recognize tests. // Leak tracker does not work for web platform. if (kIsWeb) { await callback(); return; } void flutterEventToLeakTracker(ObjectEvent event) { return dispatchObjectEvent(event.toMap()); } return TestAsyncUtils.guard<void>(() async { MemoryAllocations.instance.addListener(flutterEventToLeakTracker); final AsyncCodeRunner asyncCodeRunner = tester == null ? (DartAsyncCallback action) async => action() : (DartAsyncCallback action) async => tester.runAsync(action); try { final Leaks leaks = await withLeakTracking( callback, asyncCodeRunner: asyncCodeRunner, stackTraceCollectionConfig: stackTraceCollectionConfig, shouldThrowOnLeaks: false, timeoutForFinalGarbageCollection: timeoutForFinalGarbageCollection, ); if (leaksObtainer != null) { leaksObtainer(leaks); } expect(leaks, isLeakFree); } finally { MemoryAllocations.instance.removeListener(flutterEventToLeakTracker); } }); }