Unverified Commit 92125ed3 authored by Matt Carroll's avatar Matt Carroll Committed by GitHub

Enable dependency injection of Window instead of using static property (#27389)

parent 3ba97da9
......@@ -32,8 +32,14 @@ Future<void> main() async {
await tester.pump(); // Start drawer animation
await tester.pump(const Duration(seconds: 1)); // Complete drawer animation
final TestViewConfiguration big = TestViewConfiguration(size: const Size(360.0, 640.0));
final TestViewConfiguration small = TestViewConfiguration(size: const Size(355.0, 635.0));
final TestViewConfiguration big = TestViewConfiguration(
size: const Size(360.0, 640.0),
window: RendererBinding.instance.window,
);
final TestViewConfiguration small = TestViewConfiguration(
size: const Size(355.0, 635.0),
window: RendererBinding.instance.window,
);
final RenderView renderView = WidgetsBinding.instance.renderView;
binding.framePolicy = LiveTestWidgetsFlutterBindingFramePolicy.benchmark;
......
......@@ -6,7 +6,8 @@ import 'dart:async';
import 'dart:convert' show json;
import 'dart:developer' as developer;
import 'dart:io' show exit;
import 'dart:ui' show saveCompilationTrace;
// Before adding any more dart:ui imports, pleaes read the README.
import 'dart:ui' as ui show saveCompilationTrace, Window, window;
import 'package:meta/meta.dart';
......@@ -66,6 +67,24 @@ abstract class BindingBase {
static bool _debugInitialized = false;
static bool _debugServiceExtensionsRegistered = false;
/// The window to which this binding is bound.
///
/// A number of additional bindings are defined as extensions of [BindingBase],
/// e.g., [ServicesBinding], [RendererBinding], and [WidgetsBinding]. Each of
/// these bindings define behaviors that interact with a [ui.Window], e.g.,
/// [ServicesBinding] registers a [ui.Window.onPlatformMessage] handler, and
/// [RendererBinding] registers [ui.Window.onMetricsChanged],
/// [ui.Window.onTextScaleFactorChanged], [ui.Window.onSemanticsEnabledChanged],
/// and [ui.Window.onSemanticsAction] handlers.
///
/// Each of these other bindings could individually access a [Window] statically,
/// but that would preclude the ability to test these behaviors with a fake
/// window for verification purposes. Therefore, [BindingBase] exposes this
/// [Window] for use by other bindings. A subclass of [BindingBase], such as
/// [TestWidgetsFlutterBinding], can override this accessor to return a
/// different [Window] implementation, such as a [TestWindow].
ui.Window get window => ui.window;
/// The initialization method. Subclasses override this method to hook into
/// the platform and otherwise configure their services. Subclasses must call
/// "super.initInstances()".
......@@ -122,7 +141,7 @@ abstract class BindingBase {
name: 'saveCompilationTrace',
callback: (Map<String, String> parameters) async {
return <String, dynamic> {
'value': saveCompilationTrace(),
'value': ui.saveCompilationTrace(),
};
}
);
......
......@@ -4,7 +4,7 @@
import 'dart:async';
import 'dart:collection';
import 'dart:ui' as ui show window, PointerDataPacket;
import 'dart:ui' as ui show PointerDataPacket;
import 'package:flutter/foundation.dart';
......@@ -62,7 +62,7 @@ mixin GestureBinding on BindingBase implements HitTestable, HitTestDispatcher, H
void initInstances() {
super.initInstances();
_instance = this;
ui.window.onPointerDataPacket = _handlePointerDataPacket;
window.onPointerDataPacket = _handlePointerDataPacket;
}
@override
......@@ -80,7 +80,7 @@ mixin GestureBinding on BindingBase implements HitTestable, HitTestDispatcher, H
void _handlePointerDataPacket(ui.PointerDataPacket packet) {
// We convert pointer data to logical pixels so that e.g. the touch slop can be
// defined in a device-independent manner.
_pendingPointerEvents.addAll(PointerEventConverter.expand(packet.data, ui.window.devicePixelRatio));
_pendingPointerEvents.addAll(PointerEventConverter.expand(packet.data, window.devicePixelRatio));
if (!locked)
_flushPointerEventQueue();
}
......
......@@ -5,7 +5,6 @@
import 'dart:async';
import 'dart:developer';
import 'dart:typed_data';
import 'dart:ui' as ui show window;
import 'package:flutter/foundation.dart';
import 'package:flutter/gestures.dart';
......@@ -31,7 +30,7 @@ mixin RendererBinding on BindingBase, ServicesBinding, SchedulerBinding, Gesture
onSemanticsOwnerCreated: _handleSemanticsOwnerCreated,
onSemanticsOwnerDisposed: _handleSemanticsOwnerDisposed,
);
ui.window
window
..onMetricsChanged = handleMetricsChanged
..onTextScaleFactorChanged = handleTextScaleFactorChanged
..onSemanticsEnabledChanged = _handleSemanticsEnabledChanged
......@@ -131,7 +130,7 @@ mixin RendererBinding on BindingBase, ServicesBinding, SchedulerBinding, Gesture
/// Called automatically when the binding is created.
void initRenderView() {
assert(renderView == null);
renderView = RenderView(configuration: createViewConfiguration());
renderView = RenderView(configuration: createViewConfiguration(), window: window);
renderView.scheduleInitialFrame();
}
......@@ -181,9 +180,9 @@ mixin RendererBinding on BindingBase, ServicesBinding, SchedulerBinding, Gesture
/// this to force the display into 800x600 when a test is run on the device
/// using `flutter run`.
ViewConfiguration createViewConfiguration() {
final double devicePixelRatio = ui.window.devicePixelRatio;
final double devicePixelRatio = window.devicePixelRatio;
return ViewConfiguration(
size: ui.window.physicalSize / devicePixelRatio,
size: window.physicalSize / devicePixelRatio,
devicePixelRatio: devicePixelRatio,
);
}
......@@ -198,12 +197,12 @@ mixin RendererBinding on BindingBase, ServicesBinding, SchedulerBinding, Gesture
// the logical coordinates of the event location back to device pixels
// here.
return renderView.layer
.find<MouseTrackerAnnotation>(offset * ui.window.devicePixelRatio);
.find<MouseTrackerAnnotation>(offset * window.devicePixelRatio);
});
}
void _handleSemanticsEnabledChanged() {
setSemanticsEnabled(ui.window.semanticsEnabled);
setSemanticsEnabled(window.semanticsEnabled);
}
/// Whether the render tree associated with this binding should produce a tree
......
......@@ -4,7 +4,7 @@
import 'dart:developer';
import 'dart:io' show Platform;
import 'dart:ui' as ui show Scene, SceneBuilder, window;
import 'dart:ui' as ui show Scene, SceneBuilder, Window;
import 'package:flutter/foundation.dart';
import 'package:flutter/services.dart';
......@@ -56,8 +56,10 @@ class RenderView extends RenderObject with RenderObjectWithChildMixin<RenderBox>
RenderView({
RenderBox child,
@required ViewConfiguration configuration,
@required ui.Window window,
}) : assert(configuration != null),
_configuration = configuration {
_configuration = configuration,
_window = window {
this.child = child;
}
......@@ -82,6 +84,8 @@ class RenderView extends RenderObject with RenderObjectWithChildMixin<RenderBox>
markNeedsLayout();
}
ui.Window _window;
/// Whether Flutter should automatically compute the desired system UI.
///
/// When this setting is enabled, Flutter will hit-test the layer tree at the
......@@ -195,7 +199,7 @@ class RenderView extends RenderObject with RenderObjectWithChildMixin<RenderBox>
final ui.Scene scene = layer.buildScene(builder);
if (automaticSystemUiAdjustment)
_updateSystemChrome();
ui.window.render(scene);
_window.render(scene);
scene.dispose();
assert(() {
if (debugRepaintRainbowEnabled || debugRepaintTextRainbowEnabled)
......@@ -209,8 +213,8 @@ class RenderView extends RenderObject with RenderObjectWithChildMixin<RenderBox>
void _updateSystemChrome() {
final Rect bounds = paintBounds;
final Offset top = Offset(bounds.center.dx, ui.window.padding.top / ui.window.devicePixelRatio);
final Offset bottom = Offset(bounds.center.dx, bounds.center.dy - ui.window.padding.bottom / ui.window.devicePixelRatio);
final Offset top = Offset(bounds.center.dx, _window.padding.top / _window.devicePixelRatio);
final Offset bottom = Offset(bounds.center.dx, bounds.center.dy - _window.padding.bottom / _window.devicePixelRatio);
final SystemUiOverlayStyle upperOverlayStyle = layer.find<SystemUiOverlayStyle>(top);
// Only android has a customizable system navigation bar.
SystemUiOverlayStyle lowerOverlayStyle;
......@@ -254,10 +258,10 @@ class RenderView extends RenderObject with RenderObjectWithChildMixin<RenderBox>
properties.add(DiagnosticsNode.message('debug mode enabled - ${Platform.operatingSystem}'));
return true;
}());
properties.add(DiagnosticsProperty<Size>('window size', ui.window.physicalSize, tooltip: 'in physical pixels'));
properties.add(DoubleProperty('device pixel ratio', ui.window.devicePixelRatio, tooltip: 'physical pixels per logical pixel'));
properties.add(DiagnosticsProperty<Size>('window size', _window.physicalSize, tooltip: 'in physical pixels'));
properties.add(DoubleProperty('device pixel ratio', _window.devicePixelRatio, tooltip: 'physical pixels per logical pixel'));
properties.add(DiagnosticsProperty<ViewConfiguration>('configuration', configuration, tooltip: 'in logical pixels'));
if (ui.window.semanticsEnabled)
if (_window.semanticsEnabled)
properties.add(DiagnosticsNode.message('semantics enabled'));
}
}
......@@ -5,7 +5,6 @@
import 'dart:async';
import 'dart:collection';
import 'dart:developer';
import 'dart:ui' as ui show window;
import 'dart:ui' show AppLifecycleState;
import 'package:collection/collection.dart' show PriorityQueue, HeapPriorityQueue;
......@@ -191,8 +190,8 @@ mixin SchedulerBinding on BindingBase, ServicesBinding {
void initInstances() {
super.initInstances();
_instance = this;
ui.window.onBeginFrame = _handleBeginFrame;
ui.window.onDrawFrame = _handleDrawFrame;
window.onBeginFrame = _handleBeginFrame;
window.onDrawFrame = _handleDrawFrame;
SystemChannels.lifecycle.setMessageHandler(_handleLifecycleMessage);
}
......@@ -682,7 +681,7 @@ mixin SchedulerBinding on BindingBase, ServicesBinding {
debugPrintStack(label: 'scheduleFrame() called. Current phase is $schedulerPhase.');
return true;
}());
ui.window.scheduleFrame();
window.scheduleFrame();
_hasScheduledFrame = true;
}
......@@ -713,7 +712,7 @@ mixin SchedulerBinding on BindingBase, ServicesBinding {
debugPrintStack(label: 'scheduleForcedFrame() called. Current phase is $schedulerPhase.');
return true;
}());
ui.window.scheduleFrame();
window.scheduleFrame();
_hasScheduledFrame = true;
}
......
......@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'dart:ui' as ui show AccessibilityFeatures, window;
import 'dart:ui' as ui show AccessibilityFeatures;
import 'package:flutter/foundation.dart';
......@@ -21,7 +21,7 @@ mixin SemanticsBinding on BindingBase {
void initInstances() {
super.initInstances();
_instance = this;
_accessibilityFeatures = ui.window.accessibilityFeatures;
_accessibilityFeatures = window.accessibilityFeatures;
}
/// Called when the platform accessibility features change.
......@@ -29,7 +29,7 @@ mixin SemanticsBinding on BindingBase {
/// See [Window.onAccessibilityFeaturesChanged].
@protected
void handleAccessibilityFeaturesChanged() {
_accessibilityFeatures = ui.window.accessibilityFeatures;
_accessibilityFeatures = window.accessibilityFeatures;
}
/// The currently active set of [AccessibilityFeatures].
......
......@@ -13,6 +13,7 @@ import 'package:flutter/painting.dart' show MatrixUtils, TransformProperty;
import 'package:flutter/services.dart';
import 'package:vector_math/vector_math_64.dart';
import 'binding.dart' show SemanticsBinding;
import 'semantics_event.dart';
export 'dart:ui' show SemanticsAction;
......@@ -2473,7 +2474,7 @@ class SemanticsOwner extends ChangeNotifier {
final CustomSemanticsAction action = CustomSemanticsAction.getAction(actionId);
builder.updateCustomAction(id: actionId, label: action.label, hint: action.hint, overrideId: action.action?.index ?? -1);
}
ui.window.updateSemantics(builder.build());
SemanticsBinding.instance.window.updateSemantics(builder.build());
notifyListeners();
}
......
......@@ -3,7 +3,6 @@
// found in the LICENSE file.
import 'dart:async';
import 'dart:ui' as ui;
import 'package:flutter/foundation.dart';
......@@ -20,11 +19,16 @@ mixin ServicesBinding on BindingBase {
@override
void initInstances() {
super.initInstances();
ui.window
_instance = this;
window
..onPlatformMessage = BinaryMessages.handlePlatformMessage;
initLicenses();
}
/// The current [ServicesBinding], if one has been created.
static ServicesBinding get instance => _instance;
static ServicesBinding _instance;
/// Adds relevant licenses to the [LicenseRegistry].
///
/// By default, the [ServicesBinding]'s implementation of [initLicenses] adds
......
......@@ -8,6 +8,7 @@ import 'dart:ui' as ui;
import 'package:flutter/foundation.dart';
import 'binding.dart' show ServicesBinding;
import 'platform_channel.dart';
typedef _MessageHandler = Future<ByteData> Function(ByteData message);
......@@ -36,7 +37,7 @@ class BinaryMessages {
static Future<ByteData> _sendPlatformMessage(String channel, ByteData message) {
final Completer<ByteData> completer = Completer<ByteData>();
ui.window.sendPlatformMessage(channel, message, (ByteData reply) {
ServicesBinding.instance.window.sendPlatformMessage(channel, message, (ByteData reply) {
try {
completer.complete(reply);
} catch (exception, stack) {
......
......@@ -4,7 +4,6 @@
import 'dart:async';
import 'dart:collection' show HashMap;
import 'dart:ui' as ui show window;
import 'package:flutter/foundation.dart';
import 'package:flutter/rendering.dart';
......@@ -710,7 +709,7 @@ class _WidgetsAppState extends State<WidgetsApp> implements WidgetsBindingObserv
void initState() {
super.initState();
_updateNavigator();
_locale = _resolveLocales(ui.window.locales, widget.supportedLocales);
_locale = _resolveLocales(WidgetsBinding.instance.window.locales, widget.supportedLocales);
WidgetsBinding.instance.addObserver(this);
}
......@@ -996,7 +995,7 @@ class _WidgetsAppState extends State<WidgetsApp> implements WidgetsBindingObserv
@override
void didChangeAccessibilityFeatures() {
setState(() {
// The properties of ui.window have changed. We use them in our build
// The properties of window have changed. We use them in our build
// function, so we need setState(), but we don't cache anything locally.
});
}
......@@ -1007,7 +1006,7 @@ class _WidgetsAppState extends State<WidgetsApp> implements WidgetsBindingObserv
@override
void didChangeMetrics() {
setState(() {
// The properties of ui.window have changed. We use them in our build
// The properties of window have changed. We use them in our build
// function, so we need setState(), but we don't cache anything locally.
});
}
......@@ -1015,8 +1014,8 @@ class _WidgetsAppState extends State<WidgetsApp> implements WidgetsBindingObserv
@override
void didChangeTextScaleFactor() {
setState(() {
// The textScaleFactor property of ui.window has changed. We reference
// ui.window in our build function, so we need to call setState(), but
// The textScaleFactor property of window has changed. We reference
// window in our build function, so we need to call setState(), but
// we don't need to cache anything locally.
});
}
......@@ -1077,12 +1076,12 @@ class _WidgetsAppState extends State<WidgetsApp> implements WidgetsBindingObserv
if (_navigator != null) {
navigator = Navigator(
key: _navigator,
// If ui.window.defaultRouteName isn't '/', we should assume it was set
// If window.defaultRouteName isn't '/', we should assume it was set
// intentionally via `setInitialRoute`, and should override whatever
// is in [widget.initialRoute].
initialRoute: ui.window.defaultRouteName != Navigator.defaultRouteName
? ui.window.defaultRouteName
: widget.initialRoute ?? ui.window.defaultRouteName,
initialRoute: WidgetsBinding.instance.window.defaultRouteName != Navigator.defaultRouteName
? WidgetsBinding.instance.window.defaultRouteName
: widget.initialRoute ?? WidgetsBinding.instance.window.defaultRouteName,
onGenerateRoute: _onGenerateRoute,
onUnknownRoute: _onUnknownRoute,
observers: widget.navigatorObservers,
......@@ -1183,7 +1182,7 @@ class _WidgetsAppState extends State<WidgetsApp> implements WidgetsBindingObserv
assert(_debugCheckLocalizations(appLocale));
return MediaQuery(
data: MediaQueryData.fromWindow(ui.window),
data: MediaQueryData.fromWindow(WidgetsBinding.instance.window),
child: Localizations(
locale: appLocale,
delegates: _localizationsDelegates.toList(),
......
......@@ -5,7 +5,6 @@
import 'dart:async';
import 'dart:developer' as developer;
import 'dart:ui' show AppLifecycleState, Locale, AccessibilityFeatures;
import 'dart:ui' as ui show window;
import 'package:flutter/foundation.dart';
import 'package:flutter/gestures.dart';
......@@ -141,7 +140,7 @@ abstract class WidgetsBindingObserver {
///
/// @override
/// void didChangeMetrics() {
/// setState(() { _lastSize = ui.window.physicalSize; });
/// setState(() { _lastSize = WidgetsBinding.instance.window.physicalSize; });
/// }
///
/// @override
......@@ -197,7 +196,7 @@ abstract class WidgetsBindingObserver {
///
/// @override
/// void didChangeTextScaleFactor() {
/// setState(() { _lastTextScaleFactor = ui.window.textScaleFactor; });
/// setState(() { _lastTextScaleFactor = WidgetsBinding.instance.window.textScaleFactor; });
/// }
///
/// @override
......@@ -250,8 +249,8 @@ mixin WidgetsBinding on BindingBase, SchedulerBinding, GestureBinding, RendererB
super.initInstances();
_instance = this;
buildOwner.onBuildScheduled = _handleBuildScheduled;
ui.window.onLocaleChanged = handleLocaleChanged;
ui.window.onAccessibilityFeaturesChanged = handleAccessibilityFeaturesChanged;
window.onLocaleChanged = handleLocaleChanged;
window.onAccessibilityFeaturesChanged = handleAccessibilityFeaturesChanged;
SystemChannels.navigation.setMethodCallHandler(_handleNavigationInvocation);
SystemChannels.system.setMessageHandler(_handleSystemMessage);
}
......@@ -424,7 +423,7 @@ mixin WidgetsBinding on BindingBase, SchedulerBinding, GestureBinding, RendererB
@protected
@mustCallSuper
void handleLocaleChanged() {
dispatchLocalesChanged(ui.window.locales);
dispatchLocalesChanged(window.locales);
}
/// Notify all the observers that the locale has changed (using
......
......@@ -980,10 +980,10 @@ class EditableTextState extends State<EditableText> with AutomaticKeepAliveClien
@override
void didChangeMetrics() {
if (_lastBottomViewInset < ui.window.viewInsets.bottom) {
if (_lastBottomViewInset < WidgetsBinding.instance.window.viewInsets.bottom) {
_showCaretOnScreen();
}
_lastBottomViewInset = ui.window.viewInsets.bottom;
_lastBottomViewInset = WidgetsBinding.instance.window.viewInsets.bottom;
}
void _formatAndSetValue(TextEditingValue value) {
......@@ -1102,7 +1102,7 @@ class EditableTextState extends State<EditableText> with AutomaticKeepAliveClien
if (_hasFocus) {
// Listen for changing viewInsets, which indicates keyboard showing up.
WidgetsBinding.instance.addObserver(this);
_lastBottomViewInset = ui.window.viewInsets.bottom;
_lastBottomViewInset = WidgetsBinding.instance.window.viewInsets.bottom;
_showCaretOnScreen();
if (!_value.selection.isValid) {
// Place cursor at the end if the selection is invalid when we receive focus.
......
......@@ -3,12 +3,12 @@
// found in the LICENSE file.
import 'dart:math' as math;
import 'dart:ui' as ui;
import 'package:flutter/foundation.dart';
import 'package:flutter/gestures.dart';
import 'package:flutter/physics.dart';
import 'binding.dart' show WidgetsBinding;
import 'overscroll_indicator.dart';
import 'scroll_metrics.dart';
import 'scroll_simulation.dart';
......@@ -174,8 +174,8 @@ class ScrollPhysics {
static final Tolerance _kDefaultTolerance = Tolerance(
// TODO(ianh): Handle the case of the device pixel ratio changing.
// TODO(ianh): Get this from the local MediaQuery not dart:ui's window object.
velocity: 1.0 / (0.050 * ui.window.devicePixelRatio), // logical pixels per second
distance: 1.0 / ui.window.devicePixelRatio // logical pixels
velocity: 1.0 / (0.050 * WidgetsBinding.instance.window.devicePixelRatio), // logical pixels per second
distance: 1.0 / WidgetsBinding.instance.window.devicePixelRatio // logical pixels
);
/// The tolerance to use for ballistic simulations.
......
......@@ -4,7 +4,6 @@
import 'dart:math' as math;
import 'dart:ui' show SemanticsFlag;
import 'dart:ui' as ui show window;
import 'package:flutter/foundation.dart';
import 'package:flutter/scheduler.dart';
......@@ -89,7 +88,7 @@ class _SemanticsDebuggerState extends State<SemanticsDebugger> with WidgetsBindi
Offset _lastPointerDownLocation;
void _handlePointerDown(PointerDownEvent event) {
setState(() {
_lastPointerDownLocation = event.position * ui.window.devicePixelRatio;
_lastPointerDownLocation = event.position * WidgetsBinding.instance.window.devicePixelRatio;
});
// TODO(ianh): Use a gesture recognizer so that we can reset the
// _lastPointerDownLocation when none of the other gesture recognizers win.
......@@ -150,7 +149,7 @@ class _SemanticsDebuggerState extends State<SemanticsDebugger> with WidgetsBindi
_pipelineOwner,
_client.generation,
_lastPointerDownLocation, // in physical pixels
ui.window.devicePixelRatio,
WidgetsBinding.instance.window.devicePixelRatio,
),
child: GestureDetector(
behavior: HitTestBehavior.opaque,
......
......@@ -10,7 +10,6 @@ import 'dart:math' as math;
import 'dart:typed_data';
import 'dart:ui' as ui
show
window,
ClipOp,
EngineLayer,
Image,
......@@ -2294,7 +2293,7 @@ class _WidgetInspectorState extends State<WidgetInspector>
// on the edge of the display. If the pointer is being dragged off the edge
// of the display we do not want to select anything. A user can still select
// a widget that is only at the exact screen margin by tapping.
final Rect bounds = (Offset.zero & (ui.window.physicalSize / ui.window.devicePixelRatio)).deflate(_kOffScreenMargin);
final Rect bounds = (Offset.zero & (WidgetsBinding.instance.window.physicalSize / WidgetsBinding.instance.window.devicePixelRatio)).deflate(_kOffScreenMargin);
if (!bounds.contains(_lastPointerLocation)) {
setState(() {
selection.clear();
......
......@@ -44,7 +44,7 @@ void main() {
expect(offscreen.child.hasSize, isFalse);
expect(offscreen.painted, isFalse);
// Attach the offscreen to a custom render view and owner
final RenderView renderView = RenderView(configuration: testConfiguration);
final RenderView renderView = RenderView(configuration: testConfiguration, window: null);
final PipelineOwner pipelineOwner = PipelineOwner();
renderView.attach(pipelineOwner);
renderView.child = offscreen.root;
......@@ -73,7 +73,7 @@ void main() {
expect(offscreen.child.hasSize, isFalse);
expect(offscreen.painted, isFalse);
// Attach the offscreen to a custom render view and owner
final RenderView renderView = RenderView(configuration: testConfiguration);
final RenderView renderView = RenderView(configuration: testConfiguration, window: null);
final PipelineOwner pipelineOwner = PipelineOwner();
renderView.attach(pipelineOwner);
renderView.child = offscreen.root;
......
......@@ -4,10 +4,14 @@
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
import '../flutter_test_alternative.dart';
import 'package:flutter_test/src/binding.dart' show TestWidgetsFlutterBinding;
import 'package:flutter_test/flutter_test.dart';
void main() {
test('ensure frame is scheduled for markNeedsSemanticsUpdate', () {
// Initialize all bindings because owner.flushSemantics() requires a window
TestWidgetsFlutterBinding.ensureInitialized();
final TestRenderObject renderObject = TestRenderObject();
int onNeedVisualUpdateCallCount = 0;
final PipelineOwner owner = PipelineOwner(onNeedVisualUpdate: () {
......
......@@ -5,10 +5,13 @@
import 'dart:typed_data';
import 'package:flutter/services.dart';
import '../flutter_test_alternative.dart';
import 'package:flutter_test/flutter_test.dart';
void main() {
test('Mock binary message handler control test', () async {
// Initialize all bindings because BinaryMessages.send() needs a window.
TestWidgetsFlutterBinding.ensureInitialized();
final List<ByteData> log = <ByteData>[];
BinaryMessages.setMockMessageHandler('test1', (ByteData message) async {
......
......@@ -9,7 +9,10 @@ import 'package:flutter/rendering.dart';
const Size _kTestViewSize = Size(800.0, 600.0);
class OffscreenRenderView extends RenderView {
OffscreenRenderView() : super(configuration: const ViewConfiguration(size: _kTestViewSize));
OffscreenRenderView() : super(
configuration: const ViewConfiguration(size: _kTestViewSize),
window: WidgetsBinding.instance.window,
);
@override
void compositeFrame() {
......
......@@ -2,8 +2,6 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'dart:ui' as ui;
import 'package:flutter_test/flutter_test.dart';
import 'package:flutter/widgets.dart';
......@@ -39,10 +37,10 @@ void main() {
});
testWidgets('MediaQueryData is sane', (WidgetTester tester) async {
final MediaQueryData data = MediaQueryData.fromWindow(ui.window);
final MediaQueryData data = MediaQueryData.fromWindow(WidgetsBinding.instance.window);
expect(data, hasOneLineDescription);
expect(data.hashCode, equals(data.copyWith().hashCode));
expect(data.size, equals(ui.window.physicalSize / ui.window.devicePixelRatio));
expect(data.size, equals(WidgetsBinding.instance.window.physicalSize / WidgetsBinding.instance.window.devicePixelRatio));
expect(data.accessibleNavigation, false);
expect(data.invertColors, false);
expect(data.disableAnimations, false);
......@@ -50,7 +48,7 @@ void main() {
});
testWidgets('MediaQueryData.copyWith defaults to source', (WidgetTester tester) async {
final MediaQueryData data = MediaQueryData.fromWindow(ui.window);
final MediaQueryData data = MediaQueryData.fromWindow(WidgetsBinding.instance.window);
final MediaQueryData copied = data.copyWith();
expect(copied.size, data.size);
expect(copied.devicePixelRatio, data.devicePixelRatio);
......@@ -65,7 +63,7 @@ void main() {
});
testWidgets('MediaQuery.copyWith copies specified values', (WidgetTester tester) async {
final MediaQueryData data = MediaQueryData.fromWindow(ui.window);
final MediaQueryData data = MediaQueryData.fromWindow(WidgetsBinding.instance.window);
final MediaQueryData copied = data.copyWith(
size: const Size(3.14, 2.72),
devicePixelRatio: 1.41,
......
......@@ -62,3 +62,4 @@ export 'src/test_pointer.dart';
export 'src/test_text_input.dart';
export 'src/test_vsync.dart';
export 'src/widget_tester.dart';
export 'src/window.dart';
......@@ -108,11 +108,11 @@ class MinimumTapTargetGuideline extends AccessibilityGuideline {
const double delta = 0.001;
if (paintBounds.left <= delta
|| paintBounds.top <= delta
|| (paintBounds.bottom - ui.window.physicalSize.height).abs() <= delta
|| (paintBounds.right - ui.window.physicalSize.width).abs() <= delta)
|| (paintBounds.bottom - tester.binding.window.physicalSize.height).abs() <= delta
|| (paintBounds.right - tester.binding.window.physicalSize.width).abs() <= delta)
return result;
// shrink by device pixel ratio.
final Size candidateSize = paintBounds.size / ui.window.devicePixelRatio;
final Size candidateSize = paintBounds.size / tester.binding.window.devicePixelRatio;
if (candidateSize.width < size.width || candidateSize.height < size.height)
result += Evaluation.fail(
'$node: expected tap target size of at least $size, but found $candidateSize\n'
......
......@@ -13,6 +13,7 @@ import 'package:flutter/rendering.dart';
import 'package:flutter/scheduler.dart';
import 'package:flutter/services.dart';
import 'package:flutter/widgets.dart';
import 'package:flutter_test/flutter_test.dart' show TestWindow;
import 'package:quiver/testing/async.dart';
import 'package:quiver/time.dart';
import 'package:test_api/test_api.dart' as test_package;
......@@ -96,12 +97,16 @@ abstract class TestWidgetsFlutterBinding extends BindingBase
///
/// This constructor overrides the [debugPrint] global hook to point to
/// [debugPrintOverride], which can be overridden by subclasses.
TestWidgetsFlutterBinding() {
TestWidgetsFlutterBinding() : _window = TestWindow(window: ui.window) {
debugPrint = debugPrintOverride;
debugDisableShadows = disableShadows;
debugCheckIntrinsicSizes = checkIntrinsicSizes;
}
@override
TestWindow get window => _window;
final TestWindow _window;
/// The value to set [debugPrint] to while tests are running.
///
/// This can be used to redirect console output from the framework, or to
......@@ -265,8 +270,8 @@ abstract class TestWidgetsFlutterBinding extends BindingBase
@override
ViewConfiguration createViewConfiguration() {
final double devicePixelRatio = ui.window.devicePixelRatio;
final Size size = _surfaceSize ?? ui.window.physicalSize / devicePixelRatio;
final double devicePixelRatio = window.devicePixelRatio;
final Size size = _surfaceSize ?? window.physicalSize / devicePixelRatio;
return ViewConfiguration(
size: size,
devicePixelRatio: devicePixelRatio,
......@@ -665,8 +670,8 @@ class AutomatedTestWidgetsFlutterBinding extends TestWidgetsFlutterBinding {
@override
void initInstances() {
super.initInstances();
ui.window.onBeginFrame = null;
ui.window.onDrawFrame = null;
window.onBeginFrame = null;
window.onDrawFrame = null;
}
FakeAsync _currentFakeAsync; // set in runTest; cleared in postTest
......@@ -1145,7 +1150,7 @@ class LiveTestWidgetsFlutterBinding extends TestWidgetsFlutterBinding {
_pendingFrame = null;
_expectingFrame = false;
} else if (framePolicy != LiveTestWidgetsFlutterBindingFramePolicy.benchmark) {
ui.window.scheduleFrame();
window.scheduleFrame();
}
}
......@@ -1155,6 +1160,7 @@ class LiveTestWidgetsFlutterBinding extends TestWidgetsFlutterBinding {
renderView = _LiveTestRenderView(
configuration: createViewConfiguration(),
onNeedPaint: _handleViewNeedsPaint,
window: window,
);
renderView.scheduleInitialFrame();
}
......@@ -1286,7 +1292,10 @@ class LiveTestWidgetsFlutterBinding extends TestWidgetsFlutterBinding {
@override
ViewConfiguration createViewConfiguration() {
return TestViewConfiguration(size: _surfaceSize ?? _kDefaultTestViewportSize);
return TestViewConfiguration(
size: _surfaceSize ?? _kDefaultTestViewportSize,
window: window,
);
}
@override
......@@ -1310,15 +1319,18 @@ class LiveTestWidgetsFlutterBinding extends TestWidgetsFlutterBinding {
/// size onto the actual display using the [BoxFit.contain] algorithm.
class TestViewConfiguration extends ViewConfiguration {
/// Creates a [TestViewConfiguration] with the given size. Defaults to 800x600.
TestViewConfiguration({ Size size = _kDefaultTestViewportSize })
: _paintMatrix = _getMatrix(size, ui.window.devicePixelRatio),
_hitTestMatrix = _getMatrix(size, 1.0),
TestViewConfiguration({
Size size = _kDefaultTestViewportSize,
ui.Window window,
})
: _paintMatrix = _getMatrix(size, window.devicePixelRatio, window),
_hitTestMatrix = _getMatrix(size, 1.0, window),
super(size: size);
static Matrix4 _getMatrix(Size size, double devicePixelRatio) {
final double inverseRatio = devicePixelRatio / ui.window.devicePixelRatio;
final double actualWidth = ui.window.physicalSize.width * inverseRatio;
final double actualHeight = ui.window.physicalSize.height * inverseRatio;
static Matrix4 _getMatrix(Size size, double devicePixelRatio, ui.Window window) {
final double inverseRatio = devicePixelRatio / window.devicePixelRatio;
final double actualWidth = window.physicalSize.width * inverseRatio;
final double actualHeight = window.physicalSize.height * inverseRatio;
final double desiredWidth = size.width;
final double desiredHeight = size.height;
double scale, shiftX, shiftY;
......@@ -1377,7 +1389,8 @@ class _LiveTestRenderView extends RenderView {
_LiveTestRenderView({
ViewConfiguration configuration,
this.onNeedPaint,
}) : super(configuration: configuration);
@required ui.Window window,
}) : super(configuration: configuration, window: window);
@override
TestViewConfiguration get configuration => super.configuration;
......@@ -1714,4 +1727,4 @@ class _MockHttpHeaders extends HttpHeaders {
@override
String value(String name) => null;
}
}
\ No newline at end of file
This diff is collapsed.
// Copyright 2018 The Chromium 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' as ui show window;
import 'dart:ui' show Size, Locale, WindowPadding, AccessibilityFeatures;
import 'package:flutter/widgets.dart' show WidgetsBinding;
import 'package:flutter_test/flutter_test.dart';
import 'package:meta/meta.dart';
void main() {
testWidgets('TestWindow can fake device pixel ratio', (WidgetTester tester) async {
verifyThatTestWindowCanFakeProperty<double>(
tester: tester,
realValue: ui.window.devicePixelRatio,
fakeValue: 2.5,
propertyRetriever: () {
return WidgetsBinding.instance.window.devicePixelRatio;
},
propertyFaker: (TestWidgetsFlutterBinding binding, double fakeValue) {
binding.window.devicePixelRatioTestValue = fakeValue;
}
);
});
testWidgets('TestWindow can fake physical size', (WidgetTester tester) async {
verifyThatTestWindowCanFakeProperty<Size>(
tester: tester,
realValue: ui.window.physicalSize,
fakeValue: const Size(50, 50),
propertyRetriever: () {
return WidgetsBinding.instance.window.physicalSize;
},
propertyFaker: (TestWidgetsFlutterBinding binding, Size fakeValue) {
binding.window.physicalSizeTestValue = fakeValue;
}
);
});
testWidgets('TestWindow can fake view insets', (WidgetTester tester) async {
verifyThatTestWindowCanFakeProperty<WindowPadding>(
tester: tester,
realValue: ui.window.viewInsets,
fakeValue: const FakeWindowPadding(),
propertyRetriever: () {
return WidgetsBinding.instance.window.viewInsets;
},
propertyFaker: (TestWidgetsFlutterBinding binding, WindowPadding fakeValue) {
binding.window.viewInsetsTestValue = fakeValue;
}
);
});
testWidgets('TestWindow can fake padding', (WidgetTester tester) async {
verifyThatTestWindowCanFakeProperty<WindowPadding>(
tester: tester,
realValue: ui.window.padding,
fakeValue: const FakeWindowPadding(),
propertyRetriever: () {
return WidgetsBinding.instance.window.padding;
},
propertyFaker: (TestWidgetsFlutterBinding binding, WindowPadding fakeValue) {
binding.window.paddingTestValue = fakeValue;
}
);
});
testWidgets('TestWindow can fake locale', (WidgetTester tester) async {
verifyThatTestWindowCanFakeProperty<Locale>(
tester: tester,
realValue: ui.window.locale,
fakeValue: const Locale('fake_language_code'),
propertyRetriever: () {
return WidgetsBinding.instance.window.locale;
},
propertyFaker: (TestWidgetsFlutterBinding binding, Locale fakeValue) {
binding.window.localeTestValue = fakeValue;
}
);
});
testWidgets('TestWindow can fake locales', (WidgetTester tester) async {
verifyThatTestWindowCanFakeProperty<List<Locale>>(
tester: tester,
realValue: ui.window.locales,
fakeValue: <Locale>[const Locale('fake_language_code')],
propertyRetriever: () {
return WidgetsBinding.instance.window.locales;
},
propertyFaker: (TestWidgetsFlutterBinding binding, List<Locale> fakeValue) {
binding.window.localesTestValue = fakeValue;
}
);
});
testWidgets('TestWindow can fake text scale factor', (WidgetTester tester) async {
verifyThatTestWindowCanFakeProperty<double>(
tester: tester,
realValue: ui.window.textScaleFactor,
fakeValue: 2.5,
propertyRetriever: () {
return WidgetsBinding.instance.window.textScaleFactor;
},
propertyFaker: (TestWidgetsFlutterBinding binding, double fakeValue) {
binding.window.textScaleFactorTestValue = fakeValue;
}
);
});
testWidgets('TestWindow can fake clock format', (WidgetTester tester) async {
verifyThatTestWindowCanFakeProperty<bool>(
tester: tester,
realValue: ui.window.alwaysUse24HourFormat,
fakeValue: !ui.window.alwaysUse24HourFormat,
propertyRetriever: () {
return WidgetsBinding.instance.window.alwaysUse24HourFormat;
},
propertyFaker: (TestWidgetsFlutterBinding binding, bool fakeValue) {
binding.window.alwaysUse24HourFormatTestValue = fakeValue;
}
);
});
testWidgets('TestWindow can fake default route name', (WidgetTester tester) async {
verifyThatTestWindowCanFakeProperty<String>(
tester: tester,
realValue: ui.window.defaultRouteName,
fakeValue: 'fake_route',
propertyRetriever: () {
return WidgetsBinding.instance.window.defaultRouteName;
},
propertyFaker: (TestWidgetsFlutterBinding binding, String fakeValue) {
binding.window.defaultRouteNameTestValue = fakeValue;
}
);
});
testWidgets('TestWindow can fake semantics enabled', (WidgetTester tester) async {
verifyThatTestWindowCanFakeProperty<bool>(
tester: tester,
realValue: ui.window.semanticsEnabled,
fakeValue: !ui.window.semanticsEnabled,
propertyRetriever: () {
return WidgetsBinding.instance.window.semanticsEnabled;
},
propertyFaker: (TestWidgetsFlutterBinding binding, bool fakeValue) {
binding.window.semanticsEnabledTestValue = fakeValue;
}
);
});
testWidgets('TestWindow can fake accessibility features', (WidgetTester tester) async {
verifyThatTestWindowCanFakeProperty<AccessibilityFeatures>(
tester: tester,
realValue: ui.window.accessibilityFeatures,
fakeValue: const FakeAccessibilityFeatures(),
propertyRetriever: () {
return WidgetsBinding.instance.window.accessibilityFeatures;
},
propertyFaker: (TestWidgetsFlutterBinding binding, AccessibilityFeatures fakeValue) {
binding.window.accessibilityFeaturesTestValue = fakeValue;
}
);
});
testWidgets('TestWindow can clear out fake properties all at once', (WidgetTester tester) {
final double originalDevicePixelRatio = ui.window.devicePixelRatio;
final double originalTextScaleFactor = ui.window.textScaleFactor;
final TestWindow testWindow = retrieveTestBinding(tester).window;
// Set fake values for window properties.
testWindow.devicePixelRatioTestValue = 2.5;
testWindow.textScaleFactorTestValue = 3.0;
// Erase fake window property values.
testWindow.clearAllTestValues();
// Verify that the window once again reports real property values.
expect(WidgetsBinding.instance.window.devicePixelRatio, originalDevicePixelRatio);
expect(WidgetsBinding.instance.window.textScaleFactor, originalTextScaleFactor);
});
}
void verifyThatTestWindowCanFakeProperty<WindowPropertyType>({
@required WidgetTester tester,
@required WindowPropertyType realValue,
@required WindowPropertyType fakeValue,
@required WindowPropertyType Function() propertyRetriever,
@required Function(TestWidgetsFlutterBinding, WindowPropertyType fakeValue) propertyFaker,
}) {
WindowPropertyType propertyBeforeFaking;
WindowPropertyType propertyAfterFaking;
propertyBeforeFaking = propertyRetriever();
propertyFaker(retrieveTestBinding(tester), fakeValue);
propertyAfterFaking = propertyRetriever();
expect(propertyBeforeFaking, realValue);
expect(propertyAfterFaking, fakeValue);
}
TestWidgetsFlutterBinding retrieveTestBinding(WidgetTester tester) {
final WidgetsBinding binding = tester.binding;
assert(binding is TestWidgetsFlutterBinding);
final TestWidgetsFlutterBinding testBinding = binding;
return testBinding;
}
class FakeWindowPadding implements WindowPadding {
const FakeWindowPadding({
this.left = 0.0,
this.top = 0.0,
this.right = 0.0,
this.bottom = 0.0,
});
@override
final double left;
@override
final double top;
@override
final double right;
@override
final double bottom;
}
class FakeAccessibilityFeatures implements AccessibilityFeatures {
const FakeAccessibilityFeatures({
this.accessibleNavigation = false,
this.invertColors = false,
this.disableAnimations = false,
this.boldText = false,
this.reduceMotion = false,
});
@override
final bool accessibleNavigation;
@override
final bool invertColors;
@override
final bool disableAnimations;
@override
final bool boldText;
@override
final bool reduceMotion;
}
\ No newline at end of file
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