// 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/widgets.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:leak_tracker_flutter_testing/leak_tracker_flutter_testing.dart'; int _creations = 0; int _disposals = 0; void main() { // LeakTesting is turned off because it adds subscriptions to // [FlutterMemoryAllocations], that may interfere with the tests. LeakTesting.settings = LeakTesting.settings.withIgnoredAll(); final FlutterMemoryAllocations ma = FlutterMemoryAllocations.instance; test('Publishers dispatch events in debug mode', () async { void listener(ObjectEvent event) { if (event is ObjectDisposed) { _disposals++; } if (event is ObjectCreated) { _creations++; } } ma.addListener(listener); final _EventStats actual = await _activateFlutterObjectsAndReturnCountOfEvents(); expect(actual.creations, _creations); expect(actual.disposals, _disposals); ma.removeListener(listener); expect(ma.hasListeners, isFalse); }); testWidgets('State dispatches events in debug mode', (WidgetTester tester) async { var stateCreated = false; var stateDisposed = false; expect(ma.hasListeners, false); void listener(ObjectEvent event) { if (event is ObjectCreated && event.object is State) { stateCreated = true; } if (event is ObjectDisposed && event.object is State) { stateDisposed = true; } } ma.addListener(listener); await tester.pumpWidget(const _TestStatefulWidget()); expect(stateCreated, isTrue); expect(stateDisposed, isFalse); await tester.pumpWidget(const SizedBox.shrink()); expect(stateCreated, isTrue); expect(stateDisposed, isTrue); ma.removeListener(listener); expect(ma.hasListeners, false); }); } class _TestLeafRenderObjectWidget extends LeafRenderObjectWidget { @override RenderObject createRenderObject(BuildContext context) { return _TestRenderObject(); } } class _TestElement extends RenderObjectElement with RootElementMixin { _TestElement() : super(_TestLeafRenderObjectWidget()); void makeInactive() { final newFocusManager = FocusManager(); assignOwner(BuildOwner(focusManager: newFocusManager)); mount(null, null); // ignore: invalid_use_of_visible_for_overriding_member deactivate(); } @override void insertRenderObjectChild(covariant RenderObject child, covariant Object? slot) {} @override void moveRenderObjectChild( covariant RenderObject child, covariant Object? oldSlot, covariant Object? newSlot, ) {} @override void removeRenderObjectChild(covariant RenderObject child, covariant Object? slot) {} } class _TestRenderObject extends RenderObject { @override void debugAssertDoesMeetConstraints() {} @override Rect get paintBounds => throw UnimplementedError(); @override void performLayout() {} @override void performResize() {} @override Rect get semanticBounds => throw UnimplementedError(); } class _TestStatefulWidget extends StatefulWidget { const _TestStatefulWidget(); @override State<_TestStatefulWidget> createState() => _TestStatefulWidgetState(); } class _TestStatefulWidgetState extends State<_TestStatefulWidget> { @override Widget build(BuildContext context) { return Container(); } } class _EventStats { int creations = 0; int disposals = 0; } /// Create and dispose Flutter objects to fire memory allocation events. Future<_EventStats> _activateFlutterObjectsAndReturnCountOfEvents() async { final result = _EventStats(); final element = _TestElement(); result.creations++; final RenderObject renderObject = _TestRenderObject(); result.creations++; element.makeInactive(); result.creations += 4; // 1 for the new BuildOwner, 1 for the new FocusManager, 1 for the new FocusScopeNode, 1 for the new _HighlightModeManager // ignore: invalid_use_of_visible_for_overriding_member element.unmount(); result.disposals += 2; // 1 for the old BuildOwner, 1 for the element renderObject.dispose(); result.disposals += 1; return result; }