| // 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. | |
| // @dart = 2.10 | |
| // 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'; | |
| // HACK: these parts are to get access to private functions tested here. | |
| part '../../lib/ui/annotations.dart'; | |
| part '../../lib/ui/channel_buffers.dart'; | |
| 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() { | |
| VoidCallback? originalOnMetricsChanged; | |
| VoidCallback? originalOnLocaleChanged; | |
| FrameCallback? originalOnBeginFrame; | |
| VoidCallback? originalOnDrawFrame; | |
| TimingsCallback? originalOnReportTimings; | |
| PointerDataPacketCallback? originalOnPointerDataPacket; | |
| VoidCallback? originalOnSemanticsEnabledChanged; | |
| SemanticsActionCallback? originalOnSemanticsAction; | |
| PlatformMessageCallback? originalOnPlatformMessage; | |
| VoidCallback? originalOnTextScaleFactorChanged; | |
| double? oldDPR; | |
| Size? oldSize; | |
| WindowPadding? oldPadding; | |
| WindowPadding? oldInsets; | |
| WindowPadding? oldSystemGestureInsets; | |
| void setUp() { | |
| oldDPR = window.devicePixelRatio; | |
| oldSize = window.physicalSize; | |
| oldPadding = window.viewPadding; | |
| oldInsets = window.viewInsets; | |
| oldSystemGestureInsets = window.systemGestureInsets; | |
| 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; | |
| } | |
| void tearDown() { | |
| _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 | |
| oldSystemGestureInsets!.top, // system gesture inset top | |
| oldSystemGestureInsets!.right, // system gesture inset right | |
| oldSystemGestureInsets!.bottom, // system gesture inset bottom | |
| oldSystemGestureInsets!.left, // system gesture inset left | |
| ); | |
| 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; | |
| } | |
| void test(String description, void Function() testFunction) { | |
| print(description); | |
| setUp(); | |
| testFunction(); | |
| tearDown(); | |
| } | |
| void expectEquals(dynamic actual, dynamic expected) { | |
| if (actual != expected) { | |
| throw Exception('Equality check failed:\n Expected: $expected\n Actual: $actual'); | |
| } | |
| } | |
| void expectIterablesEqual(Iterable<dynamic> actual, Iterable<dynamic> expected) { | |
| expectEquals(actual.length, expected.length); | |
| final Iterator<dynamic> actualIter = actual.iterator; | |
| final Iterator<dynamic> expectedIter = expected.iterator; | |
| while (expectedIter.moveNext()) { | |
| expectEquals(actualIter.moveNext(), true); | |
| expectEquals(actualIter.current, expectedIter.current); | |
| } | |
| expectEquals(actualIter.moveNext(), false); | |
| } | |
| void expectNotEquals(dynamic actual, dynamic expected) { | |
| if (actual == expected) { | |
| throw Exception('Inequality check failed:\n Expected: $expected\n Actual: $actual'); | |
| } | |
| } | |
| void expectIdentical(dynamic actual, dynamic expected) { | |
| if (!identical(actual, expected)) { | |
| throw Exception('Identity check failed:\n Expected: $expected\n Actual: $actual'); | |
| } | |
| } | |
| test('updateUserSettings can handle an empty object', () { | |
| // this should not throw. | |
| _updateUserSettingsData('{}'); | |
| }); | |
| test('onMetricsChanged preserves callback zone', () { | |
| late Zone innerZone; | |
| late Zone runZone; | |
| late double devicePixelRatio; | |
| runZoned(() { | |
| innerZone = Zone.current; | |
| window.onMetricsChanged = () { | |
| runZone = Zone.current; | |
| devicePixelRatio = window.devicePixelRatio; | |
| }; | |
| }); | |
| window.onMetricsChanged!(); | |
| _updateWindowMetrics( | |
| 0.1234, // DPR | |
| 0.0, // width | |
| 0.0, // height | |
| 0.0, // padding top | |
| 0.0, // padding right | |
| 0.0, // padding bottom | |
| 0.0, // padding left | |
| 0.0, // inset top | |
| 0.0, // inset right | |
| 0.0, // inset bottom | |
| 0.0, // inset left | |
| 0.0, // system gesture inset top | |
| 0.0, // system gesture inset right | |
| 0.0, // system gesture inset bottom | |
| 0.0, // system gesture inset left | |
| ); | |
| expectNotEquals(runZone, null); | |
| expectIdentical(runZone, innerZone); | |
| expectEquals(devicePixelRatio, 0.1234); | |
| }); | |
| test('onLocaleChanged preserves callback zone', () { | |
| late Zone innerZone; | |
| late Zone runZone; | |
| Locale? locale; | |
| runZoned(() { | |
| innerZone = Zone.current; | |
| window.onLocaleChanged = () { | |
| runZone = Zone.current; | |
| locale = window.locale; | |
| }; | |
| }); | |
| _updateLocales(<String>['en', 'US', '', '']); | |
| expectNotEquals(runZone, null); | |
| expectIdentical(runZone, innerZone); | |
| expectEquals(locale, const Locale('en', 'US')); | |
| }); | |
| test('onBeginFrame preserves callback zone', () { | |
| late Zone innerZone; | |
| late Zone runZone; | |
| late Duration start; | |
| runZoned(() { | |
| innerZone = Zone.current; | |
| window.onBeginFrame = (Duration value) { | |
| runZone = Zone.current; | |
| start = value; | |
| }; | |
| }); | |
| _beginFrame(1234); | |
| expectNotEquals(runZone, null); | |
| expectIdentical(runZone, innerZone); | |
| expectEquals(start, const Duration(microseconds: 1234)); | |
| }); | |
| test('onDrawFrame preserves callback zone', () { | |
| late Zone innerZone; | |
| late Zone runZone; | |
| runZoned(() { | |
| innerZone = Zone.current; | |
| window.onDrawFrame = () { | |
| runZone = Zone.current; | |
| }; | |
| }); | |
| _drawFrame(); | |
| expectNotEquals(runZone, null); | |
| expectIdentical(runZone, innerZone); | |
| }); | |
| test('onReportTimings preserves callback zone', () { | |
| late Zone innerZone; | |
| late Zone runZone; | |
| window._setNeedsReportTimings = (bool _) {}; | |
| runZoned(() { | |
| innerZone = Zone.current; | |
| window.onReportTimings = (List<FrameTiming> timings) { | |
| runZone = Zone.current; | |
| }; | |
| }); | |
| _reportTimings(<int>[]); | |
| expectNotEquals(runZone, null); | |
| expectIdentical(runZone, innerZone); | |
| }); | |
| test('onPointerDataPacket preserves callback zone', () { | |
| late Zone innerZone; | |
| late Zone runZone; | |
| late 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); | |
| expectNotEquals(runZone, null); | |
| expectIdentical(runZone, innerZone); | |
| expectIterablesEqual(data.data, _unpackPointerDataPacket(testData).data); | |
| }); | |
| test('onSemanticsEnabledChanged preserves callback zone', () { | |
| late Zone innerZone; | |
| late Zone runZone; | |
| late bool enabled; | |
| runZoned(() { | |
| innerZone = Zone.current; | |
| window.onSemanticsEnabledChanged = () { | |
| runZone = Zone.current; | |
| enabled = window.semanticsEnabled; | |
| }; | |
| }); | |
| _updateSemanticsEnabled(window._semanticsEnabled); | |
| expectNotEquals(runZone, null); | |
| expectIdentical(runZone, innerZone); | |
| expectNotEquals(enabled, null); | |
| expectEquals(enabled, window._semanticsEnabled); | |
| }); | |
| test('onSemanticsAction preserves callback zone', () { | |
| late Zone innerZone; | |
| late Zone runZone; | |
| late int id; | |
| late 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); | |
| expectNotEquals(runZone, null); | |
| expectIdentical(runZone, innerZone); | |
| expectEquals(id, 1234); | |
| expectEquals(action, 4); | |
| }); | |
| test('onPlatformMessage preserves callback zone', () { | |
| late Zone innerZone; | |
| late Zone runZone; | |
| late String name; | |
| runZoned(() { | |
| innerZone = Zone.current; | |
| window.onPlatformMessage = (String value, _, __) { | |
| runZone = Zone.current; | |
| name = value; | |
| }; | |
| }); | |
| _dispatchPlatformMessage('testName', null, 123456789); | |
| expectNotEquals(runZone, null); | |
| expectIdentical(runZone, innerZone); | |
| expectEquals(name, 'testName'); | |
| }); | |
| test('onTextScaleFactorChanged preserves callback zone', () { | |
| late Zone innerZone; | |
| late Zone runZone; | |
| late double textScaleFactor; | |
| runZoned(() { | |
| innerZone = Zone.current; | |
| window.onTextScaleFactorChanged = () { | |
| runZone = Zone.current; | |
| textScaleFactor = window.textScaleFactor; | |
| }; | |
| }); | |
| window.onTextScaleFactorChanged!(); | |
| _updateTextScaleFactor(0.5); | |
| expectNotEquals(runZone, null); | |
| expectIdentical(runZone, innerZone); | |
| expectEquals(textScaleFactor, 0.5); | |
| }); | |
| test('onThemeBrightnessMode preserves callback zone', () { | |
| late Zone innerZone; | |
| late Zone runZone; | |
| late Brightness platformBrightness; | |
| runZoned(() { | |
| innerZone = Zone.current; | |
| window.onPlatformBrightnessChanged = () { | |
| runZone = Zone.current; | |
| platformBrightness = window.platformBrightness; | |
| }; | |
| }); | |
| window.onPlatformBrightnessChanged!(); | |
| _updatePlatformBrightness('dark'); | |
| expectNotEquals(runZone, null); | |
| expectIdentical(runZone, innerZone); | |
| expectEquals(platformBrightness, Brightness.dark); | |
| }); | |
| test('Window padding/insets/viewPadding/systemGestureInsets', () { | |
| _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 | |
| 0.0, // system gesture inset top | |
| 0.0, // system gesture inset right | |
| 0.0, // system gesture inset bottom | |
| 0.0, // system gesture inset left | |
| ); | |
| expectEquals(window.viewInsets.bottom, 0.0); | |
| expectEquals(window.viewPadding.bottom, 40.0); | |
| expectEquals(window.padding.bottom, 40.0); | |
| expectEquals(window.systemGestureInsets.bottom, 0.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 | |
| 0.0, // system gesture insets top | |
| 0.0, // system gesture insets right | |
| 44.0, // system gesture insets bottom | |
| 0.0, // system gesture insets left | |
| ); | |
| expectEquals(window.viewInsets.bottom, 400.0); | |
| expectEquals(window.viewPadding.bottom, 40.0); | |
| expectEquals(window.padding.bottom, 0.0); | |
| expectEquals(window.systemGestureInsets.bottom, 44.0); | |
| }); | |
| } |