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 {
_entry?.remove();
}
_isConcealed = false;
_entry?.dispose();
_entry = null;
if (_mouseIsConnected) {
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 @@
import 'dart:ui';
import 'package:flutter/foundation.dart';
import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
import 'package:flutter/services.dart';
import 'package:flutter_test/flutter_test.dart';
import '../foundation/leak_tracking.dart';
import '../rendering/mock_canvas.dart';
import '../widgets/semantics_tester.dart';
import 'feedback_tester.dart';
......@@ -649,38 +651,43 @@ void main() {
});
testWidgets('Custom tooltip message textAlign', (WidgetTester tester) async {
Future<void> pumpTooltipWithTextAlign({TextAlign? textAlign}) async {
final GlobalKey<TooltipState> tooltipKey = GlobalKey<TooltipState>();
await tester.pumpWidget(
MaterialApp(
home: Tooltip(
key: tooltipKey,
textAlign: textAlign,
message: tooltipText,
child: Container(
width: 100.0,
height: 100.0,
color: Colors.green[500],
await withFlutterLeakTracking(
() async {
Future<void> pumpTooltipWithTextAlign({TextAlign? textAlign}) async {
final GlobalKey<TooltipState> tooltipKey = GlobalKey<TooltipState>();
await tester.pumpWidget(
MaterialApp(
home: Tooltip(
key: tooltipKey,
textAlign: textAlign,
message: tooltipText,
child: Container(
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)
}
// Default value should be TextAlign.start
await pumpTooltipWithTextAlign();
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!;
expect(textAlign, TextAlign.center);
await pumpTooltipWithTextAlign(textAlign: TextAlign.end);
textAlign = tester.widget<Text>(find.text(tooltipText)).textAlign!;
expect(textAlign, TextAlign.end);
);
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();
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!;
expect(textAlign, TextAlign.center);
await pumpTooltipWithTextAlign(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 {
......@@ -922,7 +929,7 @@ void main() {
final Finder tooltip = find.byType(Tooltip);
expect(find.text(tooltipText), findsNothing);
await testGestureTap(tester, tooltip);
await _testGestureTap(tester, tooltip);
expect(find.text(tooltipText), findsOneWidget);
// Tooltip is dismissed after showDuration expired
......@@ -1697,7 +1704,7 @@ void main() {
expect(semanticEvents, unorderedEquals(<dynamic>[
<String, dynamic>{
'type': 'longPress',
'nodeId': findDebugSemantics(object).id,
'nodeId': _findDebugSemantics(object).id,
'data': <String, dynamic>{},
},
<String, dynamic>{
......@@ -1790,7 +1797,7 @@ void main() {
final Finder tooltip = find.byType(Tooltip);
expect(find.text(tooltipText), findsNothing);
await testGestureTap(tester, tooltip);
await _testGestureTap(tester, tooltip);
expect(find.text(tooltipText), findsOneWidget);
});
......@@ -1800,10 +1807,10 @@ void main() {
final Finder tooltip = find.byType(Tooltip);
expect(find.text(tooltipText), findsNothing);
await testGestureTap(tester, tooltip);
await _testGestureTap(tester, tooltip);
expect(find.text(tooltipText), findsNothing);
await testGestureLongPress(tester, tooltip);
await _testGestureLongPress(tester, tooltip);
expect(find.text(tooltipText), findsOneWidget);
});
......@@ -1813,7 +1820,7 @@ void main() {
final Finder tooltip = find.byType(Tooltip);
expect(find.text(tooltipText), findsNothing);
await testGestureTap(tester, tooltip);
await _testGestureTap(tester, tooltip);
expect(find.text(tooltipText), findsNothing);
});
......@@ -1823,10 +1830,10 @@ void main() {
final Finder tooltip = find.byType(Tooltip);
expect(find.text(tooltipText), findsNothing);
await testGestureTap(tester, tooltip);
await _testGestureTap(tester, tooltip);
expect(find.text(tooltipText), findsNothing);
await testGestureLongPress(tester, tooltip);
await _testGestureLongPress(tester, tooltip);
expect(find.text(tooltipText), findsNothing);
});
......@@ -1836,13 +1843,13 @@ void main() {
await setWidgetForTooltipMode(tester, TooltipTriggerMode.longPress, onTriggered: onTriggered);
Finder tooltip = find.byType(Tooltip);
await testGestureLongPress(tester, tooltip);
await _testGestureLongPress(tester, tooltip);
expect(onTriggeredCalled, true);
onTriggeredCalled = false;
await setWidgetForTooltipMode(tester, TooltipTriggerMode.tap, onTriggered: onTriggered);
tooltip = find.byType(Tooltip);
await testGestureTap(tester, tooltip);
await _testGestureTap(tester, tooltip);
expect(onTriggeredCalled, true);
});
......@@ -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));
await tester.pump();
await tester.pump(kLongPressTimeout);
......@@ -1933,14 +1940,14 @@ Future<void> testGestureLongPress(WidgetTester tester, Finder tooltip) async {
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.pump(const Duration(milliseconds: 10));
}
SemanticsNode findDebugSemantics(RenderObject object) {
SemanticsNode _findDebugSemantics(RenderObject object) {
if (object.debugSemantics != null) {
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