Unverified Commit e0b2138b authored by Polina Cherkasova's avatar Polina Cherkasova Committed by GitHub

Dispose OverlayEntry in TooltipState. (#117291)

parent fc8ea562
...@@ -630,6 +630,7 @@ class TooltipState extends State<Tooltip> with SingleTickerProviderStateMixin { ...@@ -630,6 +630,7 @@ class TooltipState extends State<Tooltip> with SingleTickerProviderStateMixin {
_entry?.remove(); _entry?.remove();
} }
_isConcealed = false; _isConcealed = false;
_entry?.dispose();
_entry = null; _entry = null;
if (_mouseIsConnected) { if (_mouseIsConnected) {
Tooltip._revealLastTooltip(); Tooltip._revealLastTooltip();
......
// 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);
}
});
}
...@@ -4,12 +4,14 @@ ...@@ -4,12 +4,14 @@
import 'dart:ui'; import 'dart:ui';
import 'package:flutter/foundation.dart';
import 'package:flutter/gestures.dart'; import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart'; import 'package:flutter/rendering.dart';
import 'package:flutter/services.dart'; import 'package:flutter/services.dart';
import 'package:flutter_test/flutter_test.dart'; import 'package:flutter_test/flutter_test.dart';
import '../foundation/leak_tracking.dart';
import '../rendering/mock_canvas.dart'; import '../rendering/mock_canvas.dart';
import '../widgets/semantics_tester.dart'; import '../widgets/semantics_tester.dart';
import 'feedback_tester.dart'; import 'feedback_tester.dart';
...@@ -649,38 +651,43 @@ void main() { ...@@ -649,38 +651,43 @@ void main() {
}); });
testWidgets('Custom tooltip message textAlign', (WidgetTester tester) async { testWidgets('Custom tooltip message textAlign', (WidgetTester tester) async {
Future<void> pumpTooltipWithTextAlign({TextAlign? textAlign}) async { await withFlutterLeakTracking(
final GlobalKey<TooltipState> tooltipKey = GlobalKey<TooltipState>(); () async {
await tester.pumpWidget( Future<void> pumpTooltipWithTextAlign({TextAlign? textAlign}) async {
MaterialApp( final GlobalKey<TooltipState> tooltipKey = GlobalKey<TooltipState>();
home: Tooltip( await tester.pumpWidget(
key: tooltipKey, MaterialApp(
textAlign: textAlign, home: Tooltip(
message: tooltipText, key: tooltipKey,
child: Container( textAlign: textAlign,
width: 100.0, message: tooltipText,
height: 100.0, child: Container(
color: Colors.green[500], width: 100.0,
height: 100.0,
color: Colors.green[500],
),
),
), ),
), );
), tooltipKey.currentState?.ensureTooltipVisible();
); await tester.pump(const Duration(seconds: 2)); // faded in, show timer started (and at 0.0)
tooltipKey.currentState?.ensureTooltipVisible(); }
await tester.pump(const Duration(seconds: 2)); // faded in, show timer started (and at 0.0)
} // Default value should be TextAlign.start
await pumpTooltipWithTextAlign();
// Default value should be TextAlign.start TextAlign textAlign = tester.widget<Text>(find.text(tooltipText)).textAlign!;
await pumpTooltipWithTextAlign(); expect(textAlign, TextAlign.start);
TextAlign textAlign = tester.widget<Text>(find.text(tooltipText)).textAlign!;
expect(textAlign, TextAlign.start); await pumpTooltipWithTextAlign(textAlign: TextAlign.center);
textAlign = tester.widget<Text>(find.text(tooltipText)).textAlign!;
await pumpTooltipWithTextAlign(textAlign: TextAlign.center); expect(textAlign, TextAlign.center);
textAlign = tester.widget<Text>(find.text(tooltipText)).textAlign!;
expect(textAlign, TextAlign.center); await pumpTooltipWithTextAlign(textAlign: TextAlign.end);
textAlign = tester.widget<Text>(find.text(tooltipText)).textAlign!;
await pumpTooltipWithTextAlign(textAlign: TextAlign.end); expect(textAlign, TextAlign.end);
textAlign = tester.widget<Text>(find.text(tooltipText)).textAlign!; },
expect(textAlign, TextAlign.end); tester: tester,
);
}); });
testWidgets('Tooltip overlay respects ambient Directionality', (WidgetTester tester) async { testWidgets('Tooltip overlay respects ambient Directionality', (WidgetTester tester) async {
...@@ -922,7 +929,7 @@ void main() { ...@@ -922,7 +929,7 @@ void main() {
final Finder tooltip = find.byType(Tooltip); final Finder tooltip = find.byType(Tooltip);
expect(find.text(tooltipText), findsNothing); expect(find.text(tooltipText), findsNothing);
await testGestureTap(tester, tooltip); await _testGestureTap(tester, tooltip);
expect(find.text(tooltipText), findsOneWidget); expect(find.text(tooltipText), findsOneWidget);
// Tooltip is dismissed after showDuration expired // Tooltip is dismissed after showDuration expired
...@@ -1697,7 +1704,7 @@ void main() { ...@@ -1697,7 +1704,7 @@ void main() {
expect(semanticEvents, unorderedEquals(<dynamic>[ expect(semanticEvents, unorderedEquals(<dynamic>[
<String, dynamic>{ <String, dynamic>{
'type': 'longPress', 'type': 'longPress',
'nodeId': findDebugSemantics(object).id, 'nodeId': _findDebugSemantics(object).id,
'data': <String, dynamic>{}, 'data': <String, dynamic>{},
}, },
<String, dynamic>{ <String, dynamic>{
...@@ -1790,7 +1797,7 @@ void main() { ...@@ -1790,7 +1797,7 @@ void main() {
final Finder tooltip = find.byType(Tooltip); final Finder tooltip = find.byType(Tooltip);
expect(find.text(tooltipText), findsNothing); expect(find.text(tooltipText), findsNothing);
await testGestureTap(tester, tooltip); await _testGestureTap(tester, tooltip);
expect(find.text(tooltipText), findsOneWidget); expect(find.text(tooltipText), findsOneWidget);
}); });
...@@ -1800,10 +1807,10 @@ void main() { ...@@ -1800,10 +1807,10 @@ void main() {
final Finder tooltip = find.byType(Tooltip); final Finder tooltip = find.byType(Tooltip);
expect(find.text(tooltipText), findsNothing); expect(find.text(tooltipText), findsNothing);
await testGestureTap(tester, tooltip); await _testGestureTap(tester, tooltip);
expect(find.text(tooltipText), findsNothing); expect(find.text(tooltipText), findsNothing);
await testGestureLongPress(tester, tooltip); await _testGestureLongPress(tester, tooltip);
expect(find.text(tooltipText), findsOneWidget); expect(find.text(tooltipText), findsOneWidget);
}); });
...@@ -1813,7 +1820,7 @@ void main() { ...@@ -1813,7 +1820,7 @@ void main() {
final Finder tooltip = find.byType(Tooltip); final Finder tooltip = find.byType(Tooltip);
expect(find.text(tooltipText), findsNothing); expect(find.text(tooltipText), findsNothing);
await testGestureTap(tester, tooltip); await _testGestureTap(tester, tooltip);
expect(find.text(tooltipText), findsNothing); expect(find.text(tooltipText), findsNothing);
}); });
...@@ -1823,10 +1830,10 @@ void main() { ...@@ -1823,10 +1830,10 @@ void main() {
final Finder tooltip = find.byType(Tooltip); final Finder tooltip = find.byType(Tooltip);
expect(find.text(tooltipText), findsNothing); expect(find.text(tooltipText), findsNothing);
await testGestureTap(tester, tooltip); await _testGestureTap(tester, tooltip);
expect(find.text(tooltipText), findsNothing); expect(find.text(tooltipText), findsNothing);
await testGestureLongPress(tester, tooltip); await _testGestureLongPress(tester, tooltip);
expect(find.text(tooltipText), findsNothing); expect(find.text(tooltipText), findsNothing);
}); });
...@@ -1836,13 +1843,13 @@ void main() { ...@@ -1836,13 +1843,13 @@ void main() {
await setWidgetForTooltipMode(tester, TooltipTriggerMode.longPress, onTriggered: onTriggered); await setWidgetForTooltipMode(tester, TooltipTriggerMode.longPress, onTriggered: onTriggered);
Finder tooltip = find.byType(Tooltip); Finder tooltip = find.byType(Tooltip);
await testGestureLongPress(tester, tooltip); await _testGestureLongPress(tester, tooltip);
expect(onTriggeredCalled, true); expect(onTriggeredCalled, true);
onTriggeredCalled = false; onTriggeredCalled = false;
await setWidgetForTooltipMode(tester, TooltipTriggerMode.tap, onTriggered: onTriggered); await setWidgetForTooltipMode(tester, TooltipTriggerMode.tap, onTriggered: onTriggered);
tooltip = find.byType(Tooltip); tooltip = find.byType(Tooltip);
await testGestureTap(tester, tooltip); await _testGestureTap(tester, tooltip);
expect(onTriggeredCalled, true); expect(onTriggeredCalled, true);
}); });
...@@ -1925,7 +1932,7 @@ Future<void> setWidgetForTooltipMode( ...@@ -1925,7 +1932,7 @@ Future<void> setWidgetForTooltipMode(
); );
} }
Future<void> testGestureLongPress(WidgetTester tester, Finder tooltip) async { Future<void> _testGestureLongPress(WidgetTester tester, Finder tooltip) async {
final TestGesture gestureLongPress = await tester.startGesture(tester.getCenter(tooltip)); final TestGesture gestureLongPress = await tester.startGesture(tester.getCenter(tooltip));
await tester.pump(); await tester.pump();
await tester.pump(kLongPressTimeout); await tester.pump(kLongPressTimeout);
...@@ -1933,14 +1940,14 @@ Future<void> testGestureLongPress(WidgetTester tester, Finder tooltip) async { ...@@ -1933,14 +1940,14 @@ Future<void> testGestureLongPress(WidgetTester tester, Finder tooltip) async {
await tester.pump(); await tester.pump();
} }
Future<void> testGestureTap(WidgetTester tester, Finder tooltip) async { Future<void> _testGestureTap(WidgetTester tester, Finder tooltip) async {
await tester.tap(tooltip); await tester.tap(tooltip);
await tester.pump(const Duration(milliseconds: 10)); await tester.pump(const Duration(milliseconds: 10));
} }
SemanticsNode findDebugSemantics(RenderObject object) { SemanticsNode _findDebugSemantics(RenderObject object) {
if (object.debugSemantics != null) { if (object.debugSemantics != null) {
return object.debugSemantics!; return object.debugSemantics!;
} }
return findDebugSemantics(object.parent! as RenderObject); return _findDebugSemantics(object.parent! as RenderObject);
} }
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment