// 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 _waitUntilWindowVisible() async { while (!await isWindowVisible()) { await Future.delayed(const Duration(milliseconds: 100)); } } void _expectVisible(bool current, bool expect, Completer 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 visibilityCompleter = Completer(); 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(); }