mirror of
https://github.com/flutter/flutter.git
synced 2025-06-03 00:51:18 +00:00

This PR fixes yet another case in the windows startup test that violates the render rule, which caused https://github.com/flutter/engine/pull/45300 to be reverted. Although the `FlutterView.render` call is within `onBeginFrame`, there is an `await` before the call, causing the call to fall out of the synchronous scope. I've added this problem to the documentation of `FlutterView.render` in https://github.com/flutter/engine/pull/45555.
128 lines
4.7 KiB
Dart
128 lines
4.7 KiB
Dart
// 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:async';
|
|
import 'dart:typed_data';
|
|
import 'dart:ui' as ui;
|
|
|
|
import 'package:flutter_driver/driver_extension.dart';
|
|
|
|
import 'windows.dart';
|
|
|
|
void drawHelloWorld(ui.FlutterView view) {
|
|
final ui.ParagraphStyle style = ui.ParagraphStyle();
|
|
final ui.ParagraphBuilder paragraphBuilder = ui.ParagraphBuilder(style)
|
|
..addText('Hello world');
|
|
final ui.Paragraph paragraph = paragraphBuilder.build();
|
|
|
|
paragraph.layout(const ui.ParagraphConstraints(width: 100.0));
|
|
|
|
final ui.PictureRecorder recorder = ui.PictureRecorder();
|
|
final ui.Canvas canvas = ui.Canvas(recorder);
|
|
|
|
canvas.drawParagraph(paragraph, ui.Offset.zero);
|
|
|
|
final ui.Picture picture = recorder.endRecording();
|
|
final ui.SceneBuilder sceneBuilder = ui.SceneBuilder()
|
|
..addPicture(ui.Offset.zero, picture)
|
|
..pop();
|
|
|
|
view.render(sceneBuilder.build());
|
|
}
|
|
|
|
Future<void> _waitUntilWindowVisible() async {
|
|
while (!await isWindowVisible()) {
|
|
await Future<void>.delayed(const Duration(milliseconds: 100));
|
|
}
|
|
}
|
|
|
|
void _expectVisible(bool current, bool expect, Completer<String> completer, int frameCount) {
|
|
if (current != expect) {
|
|
try {
|
|
throw 'Window should be ${expect ? 'visible' : 'hidden'} on frame $frameCount';
|
|
} catch (e) {
|
|
if (!completer.isCompleted) {
|
|
completer.completeError(e);
|
|
}
|
|
rethrow;
|
|
}
|
|
}
|
|
}
|
|
|
|
void main() async {
|
|
// TODO(goderbauer): Create a window if embedder doesn't provide an implicit view to draw into.
|
|
assert(ui.PlatformDispatcher.instance.implicitView != null);
|
|
final ui.FlutterView view = ui.PlatformDispatcher.instance.implicitView!;
|
|
|
|
// Create a completer to send the window visibility result back to the
|
|
// integration test.
|
|
final Completer<String> visibilityCompleter = Completer<String>();
|
|
enableFlutterDriverExtension(handler: (String? message) async {
|
|
if (message == 'verifyWindowVisibility') {
|
|
return visibilityCompleter.future;
|
|
} else if (message == 'verifyTheme') {
|
|
final bool app = await isAppDarkModeEnabled();
|
|
final bool system = await isSystemDarkModeEnabled();
|
|
|
|
return (app == system)
|
|
? 'success'
|
|
: 'error: app dark mode ($app) does not match system dark mode ($system)';
|
|
} else if (message == 'verifyStringConversion') {
|
|
// Use a test string that contains code points that fit in both 8 and 16 bits.
|
|
// The code points are passed a list of integers through the method channel,
|
|
// which will use the UTF16 to UTF8 utility function to convert them to a
|
|
// std::string, which should equate to the original expected string.
|
|
const String expected = 'ABCℵ';
|
|
final Int32List codePoints = Int32List.fromList(expected.codeUnits);
|
|
final String converted = await testStringConversion(codePoints);
|
|
return (converted == expected)
|
|
? 'success'
|
|
: 'error: conversion of UTF16 string to UTF8 failed, expected "${expected.codeUnits}" but got "${converted.codeUnits}"';
|
|
}
|
|
|
|
throw 'Unrecognized message: $message';
|
|
});
|
|
|
|
try {
|
|
if (await isWindowVisible()) {
|
|
throw 'Window should be hidden at startup';
|
|
}
|
|
|
|
int frameCount = 0;
|
|
ui.PlatformDispatcher.instance.onBeginFrame = (Duration duration) {
|
|
// Our goal is to verify that it's `drawHelloWorld` that makes the window
|
|
// appear, not anything else. This requires checking the visibility right
|
|
// before drawing, but since `isWindowVisible` has to be async, and
|
|
// `FlutterView.render` (in `drawHelloWorld`) forbids async before it,
|
|
// this can not be done during a single onBeginFrame. However, we can
|
|
// verify in separate frames to indirectly prove it, by ensuring that
|
|
// no other mechanism can affect isWindowVisible in the first frame at all.
|
|
frameCount += 1;
|
|
switch (frameCount) {
|
|
// The 1st frame: render nothing, just verify that the window is hidden.
|
|
case 1:
|
|
isWindowVisible().then((bool visible) {
|
|
_expectVisible(visible, false, visibilityCompleter, frameCount);
|
|
ui.PlatformDispatcher.instance.scheduleFrame();
|
|
});
|
|
// The 2nd frame: render, which makes the window appear.
|
|
case 2:
|
|
drawHelloWorld(view);
|
|
_waitUntilWindowVisible().then((_) {
|
|
if (!visibilityCompleter.isCompleted) {
|
|
visibilityCompleter.complete('success');
|
|
}
|
|
});
|
|
// Others, in case requested to render.
|
|
default:
|
|
drawHelloWorld(view);
|
|
}
|
|
};
|
|
} catch (e) {
|
|
visibilityCompleter.completeError(e);
|
|
rethrow;
|
|
}
|
|
ui.PlatformDispatcher.instance.scheduleFrame();
|
|
}
|