blob: 537061ad72583a2ba27b320ff3f001f08183831e [file] [log] [blame]
// 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 'dart:ui';
import 'package:flutter/foundation.dart';
import 'package:flutter_test/flutter_test.dart';
class PrintOverrideTestBinding extends AutomatedTestWidgetsFlutterBinding {
@override
DebugPrintCallback get debugPrintOverride => _enablePrint ? debugPrint : _emptyPrint;
static void _emptyPrint(String? message, { int? wrapWidth }) {}
static bool _enablePrint = true;
static void runWithDebugPrintDisabled(void Function() f) {
try {
_enablePrint = false;
f();
} finally {
_enablePrint = true;
}
}
}
void main() {
final MemoryAllocations ma = MemoryAllocations.instance;
PrintOverrideTestBinding();
setUp(() {
assert(!ma.hasListeners);
_checkSdkHandlersNotSet();
});
test('addListener and removeListener add and remove listeners.', () {
final ObjectEvent event = ObjectDisposed(object: 'object');
ObjectEvent? receivedEvent;
void listener(ObjectEvent event) => receivedEvent = event;
expect(ma.hasListeners, isFalse);
ma.addListener(listener);
_checkSdkHandlersSet();
ma.dispatchObjectEvent(event);
expect(receivedEvent, equals(event));
expect(ma.hasListeners, isTrue);
receivedEvent = null;
ma.removeListener(listener);
ma.dispatchObjectEvent(event);
expect(receivedEvent, isNull);
expect(ma.hasListeners, isFalse);
_checkSdkHandlersNotSet();
});
testWidgets('dispatchObjectEvent handles bad listeners', (WidgetTester tester) async {
final ObjectEvent event = ObjectDisposed(object: 'object');
final List<String> log = <String>[];
void badListener1(ObjectEvent event) {
log.add('badListener1');
throw ArgumentError();
}
void listener1(ObjectEvent event) => log.add('listener1');
void badListener2(ObjectEvent event) {
log.add('badListener2');
throw ArgumentError();
}
void listener2(ObjectEvent event) => log.add('listener2');
ma.addListener(badListener1);
_checkSdkHandlersSet();
ma.addListener(listener1);
ma.addListener(badListener2);
ma.addListener(listener2);
PrintOverrideTestBinding.runWithDebugPrintDisabled(
() => ma.dispatchObjectEvent(event)
);
expect(log, <String>['badListener1', 'listener1', 'badListener2','listener2']);
expect(tester.takeException(), contains('Multiple exceptions (2)'));
ma.removeListener(badListener1);
_checkSdkHandlersSet();
ma.removeListener(listener1);
ma.removeListener(badListener2);
ma.removeListener(listener2);
_checkSdkHandlersNotSet();
log.clear();
expect(ma.hasListeners, isFalse);
ma.dispatchObjectEvent(event);
expect(log, <String>[]);
});
test('dispatchObjectEvent does not invoke concurrently added listeners', () {
final ObjectEvent event = ObjectDisposed(object: 'object');
final List<String> log = <String>[];
void listener2(ObjectEvent event) => log.add('listener2');
void listener1(ObjectEvent event) {
log.add('listener1');
ma.addListener(listener2);
}
ma.addListener(listener1);
_checkSdkHandlersSet();
ma.dispatchObjectEvent(event);
expect(log, <String>['listener1']);
log.clear();
ma.dispatchObjectEvent(event);
expect(log, <String>['listener1','listener2']);
log.clear();
ma.removeListener(listener1);
ma.removeListener(listener2);
_checkSdkHandlersNotSet();
expect(ma.hasListeners, isFalse);
ma.dispatchObjectEvent(event);
expect(log, <String>[]);
});
test('dispatchObjectEvent does not invoke concurrently removed listeners', () {
final ObjectEvent event = ObjectDisposed(object: 'object');
final List<String> log = <String>[];
void listener2(ObjectEvent event) => log.add('listener2');
void listener1(ObjectEvent event) {
log.add('listener1');
ma.removeListener(listener2);
expect(ma.hasListeners, isFalse);
}
ma.addListener(listener1);
ma.addListener(listener2);
ma.dispatchObjectEvent(event);
expect(log, <String>['listener1']);
log.clear();
ma.removeListener(listener1);
_checkSdkHandlersNotSet();
expect(ma.hasListeners, isFalse);
});
test('last removeListener unsubscribes from Flutter SDK events', () {
void listener1(ObjectEvent event) {}
void listener2(ObjectEvent event) {}
ma.addListener(listener1);
_checkSdkHandlersSet();
ma.addListener(listener2);
_checkSdkHandlersSet();
ma.removeListener(listener1);
_checkSdkHandlersSet();
ma.removeListener(listener2);
_checkSdkHandlersNotSet();
});
test('kFlutterMemoryAllocationsEnabled is true in debug mode.', () {
expect(kFlutterMemoryAllocationsEnabled, isTrue);
});
test('publishers in Flutter dispatch events in debug mode', () async {
int eventCount = 0;
void listener(ObjectEvent event) => eventCount++;
ma.addListener(listener);
final int expectedEventCount = await _activateFlutterObjectsAndReturnCountOfEvents();
expect(eventCount, expectedEventCount);
ma.removeListener(listener);
_checkSdkHandlersNotSet();
expect(ma.hasListeners, isFalse);
});
}
void _checkSdkHandlersSet() {
expect(Image.onCreate, isNotNull);
expect(Picture.onCreate, isNotNull);
expect(Image.onDispose, isNotNull);
expect(Picture.onDispose, isNotNull);
}
void _checkSdkHandlersNotSet() {
expect(Image.onCreate, isNull);
expect(Picture.onCreate, isNull);
expect(Image.onDispose, isNull);
expect(Picture.onDispose, isNull);
}
/// Create and dispose Flutter objects to fire memory allocation events.
Future<int> _activateFlutterObjectsAndReturnCountOfEvents() async {
int count = 0;
final ValueNotifier<bool> valueNotifier = ValueNotifier<bool>(true); count++;
final ChangeNotifier changeNotifier = ChangeNotifier()..addListener(() {}); count++;
final Picture picture = _createPicture(); count++;
valueNotifier.dispose(); count++;
changeNotifier.dispose(); count++;
picture.dispose(); count++;
final Image image = await _createImage(); count++; count++; count++;
image.dispose(); count++;
return count;
}
Future<Image> _createImage() async {
final Picture picture = _createPicture();
final Image result = await picture.toImage(10, 10);
picture.dispose();
return result;
}
Picture _createPicture() {
final PictureRecorder recorder = PictureRecorder();
final Canvas canvas = Canvas(recorder);
const Rect rect = Rect.fromLTWH(0.0, 0.0, 100.0, 100.0);
canvas.clipRect(rect);
return recorder.endRecording();
}