flutter_flutter/testing/dart/window_hooks_integration_test.dart
liyuqian 9f088c65ee
Add onReportTimings and FrameRasterizedCallback API (#8983)
Using it, a Flutter app can monitor missing frames in the release mode, and a custom Flutter runner (e.g., Fuchsia) can add a custom FrameRasterizedCallback.

Related issues:
https://github.com/flutter/flutter/issues/26154
https://github.com/flutter/flutter/issues/31444
https://github.com/flutter/flutter/issues/32447

Need review as soon as possible so we can merge this before the end of May to catch the milestone.

Tests added:
* NoNeedToReportTimingsByDefault
* NeedsReportTimingsIsSetWithCallback
* ReportTimingsIsCalled
* FrameRasterizedCallbackIsCalled
* FrameTimingSetsAndGetsProperly
* onReportTimings preserves callback zone
* FrameTiming.toString has the correct format

This will need a manual engine roll as the TestWindow defined in the framework needs to implement onReportTimings.
2019-06-06 10:42:48 -07:00

348 lines
10 KiB
Dart

// Copyright 2013 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.
// HACK: pretend to be dart.ui in order to access its internals
library dart.ui;
import 'dart:async';
// this needs to be imported because painting.dart expects it this way
import 'dart:collection' as collection;
import 'dart:convert';
import 'dart:developer' as developer;
import 'dart:math' as math;
import 'dart:nativewrappers'; // ignore: unused_import
import 'dart:typed_data';
import 'package:test/test.dart';
// HACK: these parts are to get access to private functions tested here.
part '../../lib/ui/compositing.dart';
part '../../lib/ui/geometry.dart';
part '../../lib/ui/hash_codes.dart';
part '../../lib/ui/hooks.dart';
part '../../lib/ui/lerp.dart';
part '../../lib/ui/natives.dart';
part '../../lib/ui/painting.dart';
part '../../lib/ui/pointer.dart';
part '../../lib/ui/semantics.dart';
part '../../lib/ui/text.dart';
part '../../lib/ui/window.dart';
void main() {
group('window callback zones', () {
VoidCallback originalOnMetricsChanged;
VoidCallback originalOnLocaleChanged;
FrameCallback originalOnBeginFrame;
VoidCallback originalOnDrawFrame;
TimingsCallback originalOnReportTimings;
PointerDataPacketCallback originalOnPointerDataPacket;
VoidCallback originalOnSemanticsEnabledChanged;
SemanticsActionCallback originalOnSemanticsAction;
PlatformMessageCallback originalOnPlatformMessage;
VoidCallback originalOnTextScaleFactorChanged;
setUp(() {
originalOnMetricsChanged = window.onMetricsChanged;
originalOnLocaleChanged = window.onLocaleChanged;
originalOnBeginFrame = window.onBeginFrame;
originalOnDrawFrame = window.onDrawFrame;
originalOnReportTimings = window.onReportTimings;
originalOnPointerDataPacket = window.onPointerDataPacket;
originalOnSemanticsEnabledChanged = window.onSemanticsEnabledChanged;
originalOnSemanticsAction = window.onSemanticsAction;
originalOnPlatformMessage = window.onPlatformMessage;
originalOnTextScaleFactorChanged = window.onTextScaleFactorChanged;
});
tearDown(() {
window.onMetricsChanged = originalOnMetricsChanged;
window.onLocaleChanged = originalOnLocaleChanged;
window.onBeginFrame = originalOnBeginFrame;
window.onDrawFrame = originalOnDrawFrame;
window.onReportTimings = originalOnReportTimings;
window.onPointerDataPacket = originalOnPointerDataPacket;
window.onSemanticsEnabledChanged = originalOnSemanticsEnabledChanged;
window.onSemanticsAction = originalOnSemanticsAction;
window.onPlatformMessage = originalOnPlatformMessage;
window.onTextScaleFactorChanged = originalOnTextScaleFactorChanged;
});
test('updateUserSettings can handle an empty object', () {
// this should now throw.
_updateUserSettingsData('{}');
expect(true, equals(true));
});
test('onMetricsChanged preserves callback zone', () {
Zone innerZone;
Zone runZone;
double devicePixelRatio;
runZoned(() {
innerZone = Zone.current;
window.onMetricsChanged = () {
runZone = Zone.current;
devicePixelRatio = window.devicePixelRatio;
};
});
window.onMetricsChanged();
_updateWindowMetrics(0.1234, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0);
expect(runZone, isNotNull);
expect(runZone, same(innerZone));
expect(devicePixelRatio, equals(0.1234));
});
test('onLocaleChanged preserves callback zone', () {
Zone innerZone;
Zone runZone;
Locale locale;
runZoned(() {
innerZone = Zone.current;
window.onLocaleChanged = () {
runZone = Zone.current;
locale = window.locale;
};
});
_updateLocales(<String>['en', 'US', '', '']);
expect(runZone, isNotNull);
expect(runZone, same(innerZone));
expect(locale, equals(const Locale('en', 'US')));
});
test('onBeginFrame preserves callback zone', () {
Zone innerZone;
Zone runZone;
Duration start;
runZoned(() {
innerZone = Zone.current;
window.onBeginFrame = (Duration value) {
runZone = Zone.current;
start = value;
};
});
_beginFrame(1234);
expect(runZone, isNotNull);
expect(runZone, same(innerZone));
expect(start, equals(const Duration(microseconds: 1234)));
});
test('onDrawFrame preserves callback zone', () {
Zone innerZone;
Zone runZone;
runZoned(() {
innerZone = Zone.current;
window.onDrawFrame = () {
runZone = Zone.current;
};
});
_drawFrame();
expect(runZone, isNotNull);
expect(runZone, same(innerZone));
});
test('onReportTimings preserves callback zone', () {
Zone innerZone;
Zone runZone;
runZoned(() {
innerZone = Zone.current;
window.onReportTimings = (List<FrameTiming> timings) {
runZone = Zone.current;
};
});
_reportTimings(<int>[]);
expect(runZone, isNotNull);
expect(runZone, same(innerZone));
});
test('onPointerDataPacket preserves callback zone', () {
Zone innerZone;
Zone runZone;
PointerDataPacket data;
runZoned(() {
innerZone = Zone.current;
window.onPointerDataPacket = (PointerDataPacket value) {
runZone = Zone.current;
data = value;
};
});
final ByteData testData = ByteData.view(Uint8List(0).buffer);
_dispatchPointerDataPacket(testData);
expect(runZone, isNotNull);
expect(runZone, same(innerZone));
expect(data.data, equals(_unpackPointerDataPacket(testData).data));
});
test('onSemanticsEnabledChanged preserves callback zone', () {
Zone innerZone;
Zone runZone;
bool enabled;
runZoned(() {
innerZone = Zone.current;
window.onSemanticsEnabledChanged = () {
runZone = Zone.current;
enabled = window.semanticsEnabled;
};
});
_updateSemanticsEnabled(window._semanticsEnabled);
expect(runZone, isNotNull);
expect(runZone, same(innerZone));
expect(enabled, isNotNull);
expect(enabled, equals(window._semanticsEnabled));
});
test('onSemanticsAction preserves callback zone', () {
Zone innerZone;
Zone runZone;
int id;
int action;
runZoned(() {
innerZone = Zone.current;
window.onSemanticsAction = (int i, SemanticsAction a, ByteData _) {
runZone = Zone.current;
action = a.index;
id = i;
};
});
_dispatchSemanticsAction(1234, 4, null);
expect(runZone, isNotNull);
expect(runZone, same(innerZone));
expect(id, equals(1234));
expect(action, equals(4));
});
test('onPlatformMessage preserves callback zone', () {
Zone innerZone;
Zone runZone;
String name;
runZoned(() {
innerZone = Zone.current;
window.onPlatformMessage = (String value, _, __) {
runZone = Zone.current;
name = value;
};
});
_dispatchPlatformMessage('testName', null, null);
expect(runZone, isNotNull);
expect(runZone, same(innerZone));
expect(name, equals('testName'));
});
test('onTextScaleFactorChanged preserves callback zone', () {
Zone innerZone;
Zone runZone;
double textScaleFactor;
runZoned(() {
innerZone = Zone.current;
window.onTextScaleFactorChanged = () {
runZone = Zone.current;
textScaleFactor = window.textScaleFactor;
};
});
window.onTextScaleFactorChanged();
_updateTextScaleFactor(0.5);
expect(runZone, isNotNull);
expect(runZone, same(innerZone));
expect(textScaleFactor, equals(0.5));
});
test('onThemeBrightnessMode preserves callback zone', () {
Zone innerZone;
Zone runZone;
Brightness platformBrightness;
runZoned(() {
innerZone = Zone.current;
window.onPlatformBrightnessChanged = () {
runZone = Zone.current;
platformBrightness = window.platformBrightness;
};
});
window.onPlatformBrightnessChanged();
_updatePlatformBrightness('dark');
expect(runZone, isNotNull);
expect(runZone, same(innerZone));
expect(platformBrightness, equals(Brightness.dark));
});
test('Window padding/insets/viewPadding', () {
final double oldDPR = window.devicePixelRatio;
final Size oldSize = window.physicalSize;
final WindowPadding oldPadding = window.viewPadding;
final WindowPadding oldInsets = window.viewInsets;
_updateWindowMetrics(
1.0, // DPR
800.0, // width
600.0, // height
50.0, // padding top
0.0, // padding right
40.0, // padding bottom
0.0, // padding left
0.0, // inset top
0.0, // inset right
0.0, // inset bottom
0.0, // inset left
);
expect(window.viewInsets.bottom, 0.0);
expect(window.viewPadding.bottom, 40.0);
expect(window.padding.bottom, 40.0);
_updateWindowMetrics(
1.0, // DPR
800.0, // width
600.0, // height
50.0, // padding top
0.0, // padding right
40.0, // padding bottom
0.0, // padding left
0.0, // inset top
0.0, // inset right
400.0, // inset bottom
0.0, // inset left
);
expect(window.viewInsets.bottom, 400.0);
expect(window.viewPadding.bottom, 40.0);
expect(window.padding.bottom, 0.0);
_updateWindowMetrics(
oldDPR, // DPR
oldSize.width, // width
oldSize.height, // height
oldPadding.top, // padding top
oldPadding.right, // padding right
oldPadding.bottom, // padding bottom
oldPadding.left, // padding left
oldInsets.top, // inset top
oldInsets.right, // inset right
oldInsets.bottom, // inset bottom
oldInsets.left, // inset left
);
});
});
}