From 2987e259bdf123978be811c70ddb47fbbe4ca211 Mon Sep 17 00:00:00 2001 From: skia-flutter-autoroll Date: Thu, 20 Oct 2022 12:33:55 -0400 Subject: [PATCH 1/3] Roll Dart SDK from 78eca41a105a to e06db8e1b620 (1 revision) (#36895) --- DEPS | 2 +- ci/licenses_golden/licenses_third_party | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/DEPS b/DEPS index 97bbcf95a9dae..d0a434dcfe2f4 100644 --- a/DEPS +++ b/DEPS @@ -43,7 +43,7 @@ vars = { # Dart is: https://github.com/dart-lang/sdk/blob/main/DEPS. # You can use //tools/dart/create_updated_flutter_deps.py to produce # updated revision list of existing dependencies. - 'dart_revision': '78eca41a105a7e2733dd5095c5f77337312298b8', + 'dart_revision': 'e06db8e1b6202bd11f5bc9d3f6fd257e4233087f', # WARNING: DO NOT EDIT MANUALLY # The lines between blank lines above and below are generated by a script. See create_updated_flutter_deps.py diff --git a/ci/licenses_golden/licenses_third_party b/ci/licenses_golden/licenses_third_party index 371f4ebbee9c0..53ca90fefa627 100644 --- a/ci/licenses_golden/licenses_third_party +++ b/ci/licenses_golden/licenses_third_party @@ -1,4 +1,4 @@ -Signature: a8494264f44c4e0f471e40ff50f54f25 +Signature: 6f3ec8b8dc4c2da5834e224e6bbc43ec UNUSED LICENSES: From 791221c97b2fa11fa21872235220041e489f523c Mon Sep 17 00:00:00 2001 From: Erik Date: Thu, 20 Oct 2022 13:26:41 -0400 Subject: [PATCH 2/3] Add test view for touch-input-test (#36879) This PR adds a touch-input-view for use in the touch-input-test to validate touch input. The view will be attached to the test, and a touch event will be injected. The test asserts that a correct response is sent back from the view (the location of the touch as well as the name of the view). --- .../tests/integration/touch-input/BUILD.gn | 4 +- .../tests/integration/touch-input/README.md | 76 ++++++++ .../touch-input/meta/touch-input-test.cml | 23 ++- .../one-flutter/lib/one-flutter.dart | 18 -- .../one-flutter/meta/one-flutter-realm.cml | 56 ------ .../touch-input/touch-input-test.cc | 177 ++++++------------ .../BUILD.gn | 29 ++- .../lib/touch-input-view.dart | 115 ++++++++++++ .../meta/touch-input-view.cml} | 25 ++- .../touch-input/touch-input-view/pubspec.yaml | 8 + .../flutter/tests/integration/utils/BUILD.gn | 6 + .../integration/utils/portable_ui_test.cc | 50 ++++- .../integration/utils/portable_ui_test.h | 22 ++- .../fuchsia/devshell/run_integration_test.sh | 5 + 14 files changed, 375 insertions(+), 239 deletions(-) create mode 100644 shell/platform/fuchsia/flutter/tests/integration/touch-input/README.md delete mode 100644 shell/platform/fuchsia/flutter/tests/integration/touch-input/one-flutter/lib/one-flutter.dart delete mode 100644 shell/platform/fuchsia/flutter/tests/integration/touch-input/one-flutter/meta/one-flutter-realm.cml rename shell/platform/fuchsia/flutter/tests/integration/touch-input/{one-flutter => touch-input-view}/BUILD.gn (65%) create mode 100644 shell/platform/fuchsia/flutter/tests/integration/touch-input/touch-input-view/lib/touch-input-view.dart rename shell/platform/fuchsia/flutter/tests/integration/touch-input/{one-flutter/meta/one-flutter-view.cml => touch-input-view/meta/touch-input-view.cml} (83%) create mode 100644 shell/platform/fuchsia/flutter/tests/integration/touch-input/touch-input-view/pubspec.yaml diff --git a/shell/platform/fuchsia/flutter/tests/integration/touch-input/BUILD.gn b/shell/platform/fuchsia/flutter/tests/integration/touch-input/BUILD.gn index 27f422029fc0d..83075b1a5cd54 100644 --- a/shell/platform/fuchsia/flutter/tests/integration/touch-input/BUILD.gn +++ b/shell/platform/fuchsia/flutter/tests/integration/touch-input/BUILD.gn @@ -34,11 +34,11 @@ executable("touch-input-test-bin") { "$fuchsia_sdk_root/fidl:fuchsia.tracing.provider", "$fuchsia_sdk_root/fidl:fuchsia.ui.app", "$fuchsia_sdk_root/fidl:fuchsia.ui.input", + "$fuchsia_sdk_root/fidl:fuchsia.ui.pointerinjector", "$fuchsia_sdk_root/fidl:fuchsia.ui.policy", "$fuchsia_sdk_root/fidl:fuchsia.ui.scenic", "$fuchsia_sdk_root/fidl:fuchsia.ui.test.input", "$fuchsia_sdk_root/fidl:fuchsia.ui.test.scene", - "$fuchsia_sdk_root/fidl:fuchsia.ui.test.scene", "$fuchsia_sdk_root/fidl:fuchsia.web", "$fuchsia_sdk_root/pkg:async", "$fuchsia_sdk_root/pkg:async-loop-testing", @@ -46,6 +46,7 @@ executable("touch-input-test-bin") { "$fuchsia_sdk_root/pkg:scenic_cpp", "$fuchsia_sdk_root/pkg:sys_component_cpp_testing", "$fuchsia_sdk_root/pkg:zx", + "touch-input-view:package", "//build/fuchsia/fidl:fuchsia.ui.gfx", "//flutter/fml", "//flutter/shell/platform/fuchsia/flutter/tests/integration/utils:portable_ui_test", @@ -58,7 +59,6 @@ fuchsia_test_archive("touch-input-test") { testonly = true deps = [ ":touch-input-test-bin", - "one-flutter:package", # "OOT" copies of the runners used by tests, to avoid conflicting with the # runners in the base fuchsia image. diff --git a/shell/platform/fuchsia/flutter/tests/integration/touch-input/README.md b/shell/platform/fuchsia/flutter/tests/integration/touch-input/README.md new file mode 100644 index 0000000000000..1d37bde6dc7b2 --- /dev/null +++ b/shell/platform/fuchsia/flutter/tests/integration/touch-input/README.md @@ -0,0 +1,76 @@ +# touch-input + +`touch-input-test` exercises touch through a child view (in this case, the `touch-input-view` Dart component) and asserting +the precise location of the touch event. We do this by attaching the child view, injecting touch, and validating that the view +reports the touch event back with the correct coordinates. + +```shell +Injecting the tap event +[touch-input-test.cm] INFO: [portable_ui_test.cc(193)] Injecting tap at (-500, -500) + +View receives the event +[flutter_jit_runner] INFO: touch-input-view.cm(flutter): touch-input-view received tap: PointerData(embedderId: 0, timeStamp: 0:01:03.623259, +change: PointerChange.add, kind: PointerDeviceKind.touch, signalKind: PointerSignalKind.none, device: -4294967295, pointerIdentifier: 0, +physicalX: 319.99998331069946, physicalY: 199.99999284744263, physicalDeltaX: 0.0, physicalDeltaY: 0.0, buttons: 0, synthesized: false, +pressure: 0.0, pressureMin: 0.0, pressureMax: 0.0, distance: 0.0, distanceMax: 0.0, size: 0.0, radiusMajor: 0.0, radiusMinor: 0.0, +radiusMin: 0.0, radiusMax: 0.0, orientation: 0.0, tilt: 0.0, platformData: 0, scrollDeltaX: 0.0, scrollDeltaY: 0.0, panX: 0.0, panY: 0.0, +panDeltaX: 0.0, panDeltaY: 0.0, scale: 0.0, rotation: 0.0) + +Successfully received response from view +[touch-input-test.cm] INFO: [touch-input-test.cc(162)] Received ReportTouchInput event +[touch-input-test.cm] INFO: [touch-input-test.cc(255)] Expecting event for component touch-input-view at (320, 200) +[touch-input-test.cm] INFO: [touch-input-test.cc(257)] Received event for component touch-input-view at (320, 200), accounting for pixel scale of 1 +``` + +Some interesting details (thanks to abrusher@): + +There exists two coordinate spaces within our testing realm. The first is `touch-input-view`'s "logical" coordinate space. This +is determined based on `touch-input-view`'s size and is the space in which it sees incoming events. The second is the "injector" +coordinate space, which spans [-1000, 1000] on both axes. + +The size/position of a view doesn't always match the bounds of a display exactly. As a result, Scenic has a separate coordinate space +to specify the location at which to inject a touch event. This is always fixed to the display bounds. Scenic knows how to map this +coordinate space onto the client view's space. + +For example, if we inject at (-500, -500) `touch-input-view` will see a touch event at the middle of the upper-left quadrant of the screen. + +## Running the Test + +Reference the Flutter integration test [documentation](https://github.com/flutter/engine/blob/main/shell/platform/fuchsia/flutter/tests/integration/README.md) at //flutter/shell/platform/fuchsia/flutter/tests/integration/README.md + +## Playing around with `touch-input-view` + +Build Fuchsia with `workstation_eng.qemu-x64` +```shell +fx set workstation_eng.qemu-x64 --with-base=//src/session/bin/session_manager && fx build +``` + +Build flutter/engine +```shell +$ENGINE_DIR/flutter/tools/gn --fuchsia --no-lto && ninja -C $ENGINE_DIR/out/fuchsia_debug_x64 flutter/shell/platform/fuchsia/flutter/tests/ +integration/touch_input:tests +``` + +Start a Fuchsia package server +```shell +cd "$FUCHSIA_DIR" +fx serve +``` + +Publish `touch-input-view` +```shell +$FUCHSIA_DIR/.jiri_root/bin/fx pm publish -a -repo $FUCHSIA_DIR/$(cat $FUCHSIA_DIR/.fx-build-dir)/amber-files -f $ENGINE_DIR/out/ +fuchsia_debug_x64/gen/flutter/shell/platform/fuchsia/flutter/tests/integration/touch-input/touch-input-view/touch-input-view/touch-input-view.far +``` + +Launch Fuchsia emulator in a graphical environment +```shell +ffx emu start +``` + +**Before proceeding, make sure you have successfully completed the "Set a Password" screen** + +Add `touch-input-view` +```shell +ffx session add fuchsia-pkg://fuchsia.com/touch-input-view#meta/touch-input-view.cm +``` diff --git a/shell/platform/fuchsia/flutter/tests/integration/touch-input/meta/touch-input-test.cml b/shell/platform/fuchsia/flutter/tests/integration/touch-input/meta/touch-input-test.cml index 937e9c3604cd3..ce02849484a52 100644 --- a/shell/platform/fuchsia/flutter/tests/integration/touch-input/meta/touch-input-test.cml +++ b/shell/platform/fuchsia/flutter/tests/integration/touch-input/meta/touch-input-test.cml @@ -6,9 +6,6 @@ "gtest_runner.shard.cml", "sys/component/realm_builder_absolute.shard.cml", - "syslog/client.shard.cml", - "vulkan/client.shard.cml", - // This test needs both the vulkan facet and the hermetic-tier-2 facet, // so we are forced to make it a system test. "sys/testing/system-test.shard.cml", @@ -16,11 +13,17 @@ program: { binary: "bin/app", }, + use: [ + { + protocol: [ + "fuchsia.ui.test.input.TouchInputListener", + ] + } + ], offer: [ { // Offer capabilities needed by components in this test realm. // Keep it minimal, describe only what's actually needed. - // TODO(fxbug.dev/81446): Remove this list. protocol: [ "fuchsia.kernel.RootJobForInspect", "fuchsia.kernel.Stats", @@ -30,9 +33,21 @@ "fuchsia.tracing.provider.Registry", "fuchsia.ui.input.ImeService", "fuchsia.vulkan.loader.Loader", + "fuchsia.ui.scenic.Scenic", + "fuchsia.ui.test.input.TouchInputListener", + "fuchsia.intl.PropertyProvider", + "fuchsia.posix.socket.Provider", + "fuchsia.ui.pointerinjector.Registry", ], from: "parent", to: "#realm_builder", }, + { + directory: "pkg", + subdir: "config", + as: "config-data", + from: "framework", + to: "#realm_builder", + }, ], } diff --git a/shell/platform/fuchsia/flutter/tests/integration/touch-input/one-flutter/lib/one-flutter.dart b/shell/platform/fuchsia/flutter/tests/integration/touch-input/one-flutter/lib/one-flutter.dart deleted file mode 100644 index 34aca414f09e8..0000000000000 --- a/shell/platform/fuchsia/flutter/tests/integration/touch-input/one-flutter/lib/one-flutter.dart +++ /dev/null @@ -1,18 +0,0 @@ -// 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. - -// TODO(https://fxbug.dev/84961): Fix null safety and remove this language version. -// @dart=2.9 - -import 'dart:convert'; -import 'dart:typed_data'; -import 'dart:io'; -import 'dart:ui'; - -import 'package:fidl_fuchsia_ui_test_input/fidl_async.dart'; -import 'package:fuchsia_services/services.dart'; - -int main() { - print('touch-input-view: starting'); -} diff --git a/shell/platform/fuchsia/flutter/tests/integration/touch-input/one-flutter/meta/one-flutter-realm.cml b/shell/platform/fuchsia/flutter/tests/integration/touch-input/one-flutter/meta/one-flutter-realm.cml deleted file mode 100644 index ccc7f991a475d..0000000000000 --- a/shell/platform/fuchsia/flutter/tests/integration/touch-input/one-flutter/meta/one-flutter-realm.cml +++ /dev/null @@ -1,56 +0,0 @@ -// 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. -{ - children: [ - { - name: "flutter_jit_runner", - url: "fuchsia-pkg://fuchsia.com/flutter_jit_runner#meta/flutter_jit_runner.cm", - }, - { - name: "one_flutter_view", - url: "#meta/one-flutter-view.cm", - environment: "#one_flutter_view_env", - }, - ], - offer: [ - { - protocol: [ - "fuchsia.logger.LogSink", - "fuchsia.sysmem.Allocator", - "fuchsia.tracing.provider.Registry", - "fuchsia.ui.scenic.Scenic", - "fuchsia.vulkan.loader.Loader", - ], - from: "parent", - to: [ - "#flutter_jit_runner", - "#one_flutter_view", - ], - }, - { - protocol: [ "fuchsia.ui.test.input.TouchInputListener" ], - from: "parent", - to: "#one_flutter_view", - }, - ], - expose: [ - { - protocol: [ "fuchsia.ui.app.ViewProvider" ], - from: "#one_flutter_view", - to: "parent", - }, - ], - environments: [ - { - name: "one_flutter_view_env", - extends: "realm", - runners: [ - { - runner: "flutter_jit_runner", - from: "#flutter_jit_runner", - }, - ], - }, - ], -} diff --git a/shell/platform/fuchsia/flutter/tests/integration/touch-input/touch-input-test.cc b/shell/platform/fuchsia/flutter/tests/integration/touch-input/touch-input-test.cc index e8eede23d151d..78137ac0f49fd 100644 --- a/shell/platform/fuchsia/flutter/tests/integration/touch-input/touch-input-test.cc +++ b/shell/platform/fuchsia/flutter/tests/integration/touch-input/touch-input-test.cc @@ -123,52 +123,43 @@ using component_testing::Route; using fuchsia_test_utils::PortableUITest; using RealmBuilder = component_testing::RealmBuilder; -// Alias for Component child name as provided to Realm Builder. -using ChildName = std::string; -// Alias for Component Legacy URL as provided to Realm Builder. -using LegacyUrl = std::string; // Max timeout in failure cases. // Set this as low as you can that still works across all test platforms. constexpr zx::duration kTimeout = zx::min(5); -constexpr auto kMockResponseListener = "response_listener"; - -enum class TapLocation { kTopLeft, kTopRight }; - -// Combines all vectors in `vecs` into one. -template -std::vector merge(std::initializer_list> vecs) { - std::vector result; - for (auto v : vecs) { - result.insert(result.end(), v.begin(), v.end()); - } - return result; -} +constexpr auto kMockTouchInputListener = "touch_input_listener"; +constexpr auto kMockTouchInputListenerRef = ChildRef{kMockTouchInputListener}; +constexpr auto kTouchInputView = "touch-input-view"; +constexpr auto kTouchInputViewRef = ChildRef{kTouchInputView}; +constexpr auto kTouchInputViewUrl = + "fuchsia-pkg://fuchsia.com/touch-input-view#meta/touch-input-view.cm"; bool CompareDouble(double f0, double f1, double epsilon) { return std::abs(f0 - f1) <= epsilon; } -// // This component implements the test.touch.ResponseListener protocol -// // and the interface for a RealmBuilder LocalComponent. A LocalComponent -// // is a component that is implemented here in the test, as opposed to -// elsewhere -// // in the system. When it's inserted to the realm, it will act like a proper -// // component. This is accomplished, in part, because the realm_builder -// // library creates the necessary plumbing. It creates a manifest for the -// // component and routes all capabilities to and from it. -class ResponseListenerServer +// This component implements the TouchInput protocol +// and the interface for a RealmBuilder LocalComponent. A LocalComponent +// is a component that is implemented here in the test, as opposed to +// elsewhere in the system. When it's inserted to the realm, it will act +// like a proper component. This is accomplished, in part, because the +// realm_builder library creates the necessary plumbing. It creates a manifest +// for the component and routes all capabilities to and from it. +// LocalComponent: +// https://fuchsia.dev/fuchsia-src/development/testing/components/realm_builder#mock-components +class TouchInputListenerServer : public fuchsia::ui::test::input::TouchInputListener, public LocalComponent { public: - explicit ResponseListenerServer(async_dispatcher_t* dispatcher) + explicit TouchInputListenerServer(async_dispatcher_t* dispatcher) : dispatcher_(dispatcher) {} // |fuchsia::ui::test::input::TouchInputListener| void ReportTouchInput( fuchsia::ui::test::input::TouchInputListenerReportTouchInputRequest request) override { + FML_LOG(INFO) << "Received ReportTouchInput event"; events_received_.push_back(std::move(request)); } @@ -176,9 +167,9 @@ class ResponseListenerServer // When the component framework requests for this component to start, this // method will be invoked by the realm_builder library. void Start(std::unique_ptr local_handles) override { + FML_LOG(INFO) << "Starting TouchInputListenerServer"; // When this component starts, add a binding to the - // test.touch.ResponseListener protocol to this component's outgoing - // directory. + // protocol to this component's outgoing directory. ASSERT_EQ(ZX_OK, local_handles->outgoing()->AddPublicService( fidl::InterfaceRequestHandler< fuchsia::ui::test::input::TouchInputListener>( @@ -242,45 +233,12 @@ class FlutterTapTest : public PortableUITest, RegisterTouchScreen(); } - // Routes needed to setup Flutter client. - static std::vector GetFlutterRoutes(ChildRef target) { - return { - {.capabilities = {Protocol{ - fuchsia::ui::test::input::TouchInputListener::Name_}}, - .source = ChildRef{kMockResponseListener}, - .targets = {target}}, - {.capabilities = {Protocol{fuchsia::logger::LogSink::Name_}, - Protocol{fuchsia::sysmem::Allocator::Name_}, - Protocol{ - fuchsia::tracing::provider::Registry::Name_}}, - .source = ParentRef(), - .targets = {target}}, - {.capabilities = {Protocol{fuchsia::ui::scenic::Scenic::Name_}}, - .source = kTestUIStackRef, - .targets = {target}}, - }; - } - - std::vector GetTestRoutes() { - return merge( - {GetFlutterRoutes(ChildRef{kFlutterRealm}), - { - {.capabilities = {Protocol{fuchsia::ui::app::ViewProvider::Name_}}, - .source = ChildRef{kFlutterRealm}, - .targets = {ParentRef()}}, - }}); - } - - std::vector> GetTestV2Components() { - return { - std::make_pair(kFlutterRealm, kFlutterRealmUrl), - }; - }; - bool LastEventReceivedMatches(float expected_x, float expected_y, std::string component_name) { - const auto& events_received = response_listener_server_->events_received(); + const auto& events_received = + touch_input_listener_server_->events_received(); + if (events_received.empty()) { return false; } @@ -305,75 +263,50 @@ class FlutterTapTest : public PortableUITest, last_event.component_name() == component_name; } - void InjectInput(TapLocation tap_location) { - // The /config/data/display_rotation (90) specifies how many degrees to - // rotate the presentation child view, counter-clockwise, in a - // right-handed coordinate system. Thus, the user observes the child - // view to rotate *clockwise* by that amount (90). - // - // Hence, a tap in the center of the display's top-right quadrant is - // observed by the child view as a tap in the center of its top-left - // quadrant. - auto touch = std::make_unique(); - switch (tap_location) { - case TapLocation::kTopLeft: - // center of top right quadrant -> ends up as center of top left - // quadrant - InjectTap(/* x = */ 500, /* y = */ -500); - break; - case TapLocation::kTopRight: - // center of bottom right quadrant -> ends up as center of top right - // quadrant - InjectTap(/* x = */ 500, /* y = */ 500); - break; - default: - FML_CHECK(false) << "Received invalid TapLocation"; - } - } - // Guaranteed to be initialized after SetUp(). uint32_t display_width() const { return display_width_; } uint32_t display_height() const { return display_height_; } - static constexpr auto kFlutterRealm = "flutter-realm"; - static constexpr auto kFlutterRealmUrl = - "fuchsia-pkg://fuchsia.com/one-flutter#meta/one-flutter-realm.cm"; - private: void ExtendRealm() override { // Key part of service setup: have this test component vend the - // |ResponseListener| service in the constructed realm. - response_listener_server_ = - std::make_unique(dispatcher()); - realm_builder()->AddLocalChild(kMockResponseListener, - response_listener_server_.get()); - + // |TouchInputListener| service in the constructed realm. + touch_input_listener_server_ = + std::make_unique(dispatcher()); + realm_builder()->AddLocalChild(kMockTouchInputListener, + touch_input_listener_server_.get()); + + realm_builder()->AddChild(kTouchInputView, kTouchInputViewUrl, + component_testing::ChildOptions{ + .environment = kFlutterRunnerEnvironment, + }); + + // Route the TouchInput protocol capability to the Dart component realm_builder()->AddRoute( - {.capabilities = {Protocol{fuchsia::ui::scenic::Scenic::Name_}}, - .source = kTestUIStackRef, - .targets = {ParentRef()}}); + Route{.capabilities = {Protocol{ + fuchsia::ui::test::input::TouchInputListener::Name_}}, + .source = kMockTouchInputListenerRef, + .targets = {kFlutterJitRunnerRef, kTouchInputViewRef}}); - // Add components specific for this test case to the realm. - for (const auto& [name, component] : GetTestV2Components()) { - realm_builder()->AddChild(name, component); - } - - // Add the necessary routing for each of the extra components added - // above. - for (const auto& route : GetTestRoutes()) { - realm_builder()->AddRoute(route); - } + realm_builder()->AddRoute( + Route{.capabilities = {Protocol{fuchsia::ui::app::ViewProvider::Name_}}, + .source = kTouchInputViewRef, + .targets = {ParentRef()}}); } ParamType GetTestUIStackUrl() override { return GetParam(); }; - std::unique_ptr response_listener_server_; + std::unique_ptr touch_input_listener_server_; fuchsia::ui::scenic::ScenicPtr scenic_; uint32_t display_width_ = 0; uint32_t display_height_ = 0; }; +// Makes use of gtest's parameterized testing, allowing us +// to test different combinations of test-ui-stack + runners. Currently, there +// is just one combination. Documentation: +// http://go/gunitadvanced#value-parameterized-tests INSTANTIATE_TEST_SUITE_P( FlutterTapTestParameterized, FlutterTapTest, @@ -387,12 +320,18 @@ TEST_P(FlutterTapTest, FlutterTap) { LaunchClient(); FML_LOG(INFO) << "Client launched"; - InjectInput(TapLocation::kTopLeft); + // touch-input-view logical coordinate space doesn't match the fake touch + // screen injector's coordinate space, which spans [-1000, 1000] on both axes. + // Scenic handles figuring out where in the coordinate space + // to inject a touch event (this is fixed to a display's bounds). + InjectTap(-500, -500); + // For a (-500 [x], -500 [y]) tap, we expect a touch event in the middle of + // the upper-left quadrant of the screen. RunLoopUntil([this] { return LastEventReceivedMatches( - /*expected_x=*/static_cast(display_height()) / 4.f, - /*expected_y=*/static_cast(display_width()) / 4.f, - /*component_name=*/"one-flutter"); + /*expected_x=*/static_cast(display_width() / 4.0f), + /*expected_y=*/static_cast(display_height() / 4.0f), + /*component_name=*/"touch-input-view"); }); } diff --git a/shell/platform/fuchsia/flutter/tests/integration/touch-input/one-flutter/BUILD.gn b/shell/platform/fuchsia/flutter/tests/integration/touch-input/touch-input-view/BUILD.gn similarity index 65% rename from shell/platform/fuchsia/flutter/tests/integration/touch-input/one-flutter/BUILD.gn rename to shell/platform/fuchsia/flutter/tests/integration/touch-input/touch-input-view/BUILD.gn index 06983c0d684ec..bbaf6ca65fc5a 100644 --- a/shell/platform/fuchsia/flutter/tests/integration/touch-input/one-flutter/BUILD.gn +++ b/shell/platform/fuchsia/flutter/tests/integration/touch-input/touch-input-view/BUILD.gn @@ -9,9 +9,11 @@ import("//flutter/tools/fuchsia/gn-sdk/component.gni") import("//flutter/tools/fuchsia/gn-sdk/package.gni") dart_library("lib") { - package_name = "one-flutter" - sources = [ "one-flutter.dart" ] + package_name = "touch-input-view" + sources = [ "touch-input-view.dart" ] + deps = [ + "//flutter/shell/platform/fuchsia/dart:args", "//flutter/tools/fuchsia/dart:fuchsia_services", "//flutter/tools/fuchsia/dart:zircon", "//flutter/tools/fuchsia/fidl:fuchsia.ui.test.input", @@ -20,24 +22,17 @@ dart_library("lib") { flutter_component("component") { testonly = true - component_name = "one-flutter-view" - manifest = rebase_path("meta/one-flutter-view.cml") - main_package = "one-flutter" - main_dart = "one-flutter.dart" - deps = [ ":lib" ] -} + component_name = "touch-input-view" + manifest = rebase_path("meta/touch-input-view.cml") + main_package = "touch-input-view" + main_dart = "touch-input-view.dart" -fuchsia_component("realm") { - testonly = true - manifest = "meta/one-flutter-realm.cml" - manifest_output_name = "one-flutter-realm.cml" - deps = [ ":component" ] + deps = [ ":lib" ] } fuchsia_package("package") { testonly = true - deps = [ - ":component", - ":realm", - ] + package_name = "touch-input-view" + + deps = [ ":component" ] } diff --git a/shell/platform/fuchsia/flutter/tests/integration/touch-input/touch-input-view/lib/touch-input-view.dart b/shell/platform/fuchsia/flutter/tests/integration/touch-input/touch-input-view/lib/touch-input-view.dart new file mode 100644 index 0000000000000..54b9093a790b0 --- /dev/null +++ b/shell/platform/fuchsia/flutter/tests/integration/touch-input/touch-input-view/lib/touch-input-view.dart @@ -0,0 +1,115 @@ +// Copyright 2020 The Fuchsia Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// TODO(https://fxbug.dev/84961): Fix null safety and remove this language version. +// @dart=2.9 + +import 'dart:convert'; +import 'dart:typed_data'; +import 'dart:ui'; + +import 'package:args/args.dart'; +import 'package:fidl_fuchsia_ui_test_input/fidl_async.dart' as test_touch; +import 'package:fuchsia_services/services.dart'; +import 'package:zircon/zircon.dart'; + +void main() { + print('Launching two-flutter view'); + MyApp app = MyApp(); + app.run(); +} + +class MyApp { + static const _red = Color.fromARGB(255, 244, 67, 54); + static const _orange = Color.fromARGB(255, 255, 152, 0); + static const _yellow = Color.fromARGB(255, 255, 235, 59); + static const _green = Color.fromARGB(255, 76, 175, 80); + static const _blue = Color.fromARGB(255, 33, 150, 143); + static const _purple = Color.fromARGB(255, 156, 39, 176); + + final List _colors = [ + _red, + _orange, + _yellow, + _green, + _blue, + _purple, + ]; + + // Each tap will increment the counter, we then determine what color to choose + int _touchCounter = 0; + final _responseListener = test_touch.TouchInputListenerProxy(); + + void run() { + // Set up window callbacks. + window.onPointerDataPacket = (PointerDataPacket packet) { + this.pointerDataPacket(packet); + }; + window.onMetricsChanged = () { + window.scheduleFrame(); + }; + window.onBeginFrame = (Duration duration) { + this.beginFrame(duration); + }; + + // The child view should be attached to Scenic now. + // Ready to build the scene. + window.scheduleFrame(); + } + + void beginFrame(Duration duration) { + // Convert physical screen size of device to values + final pixelRatio = window.devicePixelRatio; + final size = window.physicalSize / pixelRatio; + final physicalBounds = Offset.zero & size * pixelRatio; + // Set up Canvas that uses the screen size + final recorder = PictureRecorder(); + final canvas = Canvas(recorder, physicalBounds); + canvas.scale(pixelRatio, pixelRatio); + // Draw something + // Color of the screen is set initially to the first value in _colors + // Incrementing _touchCounter will change screen color + final paint = Paint()..color = _colors[_touchCounter % _colors.length]; + canvas.drawRect(Rect.fromLTWH(0, 0, size.width, size.height), paint); + // Build the scene + final picture = recorder.endRecording(); + final sceneBuilder = SceneBuilder() + ..pushClipRect(physicalBounds) + ..addPicture(Offset.zero, picture) + ..pop(); + window.render(sceneBuilder.build()); + } + + void pointerDataPacket(PointerDataPacket packet) async { + int nowNanos = System.clockGetMonotonic(); + + for (PointerData data in packet.data) { + print('touch-input-view received tap: ${data.toStringFull()}'); + + if (data.change == PointerChange.down) { + _touchCounter++; + } + + if (data.change == PointerChange.down || data.change == PointerChange.move) { + Incoming.fromSvcPath() + ..connectToService(_responseListener) + ..close(); + + _respond(test_touch.TouchInputListenerReportTouchInputRequest( + localX: data.physicalX, + localY: data.physicalY, + timeReceived: nowNanos, + componentName: 'touch-input-view', + )); + } + } + + window.scheduleFrame(); + } + + void _respond(test_touch.TouchInputListenerReportTouchInputRequest request) async { + print('touch-input-view reporting touch input to TouchInputListener'); + await _responseListener.reportTouchInput(request); + } +} diff --git a/shell/platform/fuchsia/flutter/tests/integration/touch-input/one-flutter/meta/one-flutter-view.cml b/shell/platform/fuchsia/flutter/tests/integration/touch-input/touch-input-view/meta/touch-input-view.cml similarity index 83% rename from shell/platform/fuchsia/flutter/tests/integration/touch-input/one-flutter/meta/one-flutter-view.cml rename to shell/platform/fuchsia/flutter/tests/integration/touch-input/touch-input-view/meta/touch-input-view.cml index 8fb7dd9e25976..bdd76ea1b30ee 100644 --- a/shell/platform/fuchsia/flutter/tests/integration/touch-input/one-flutter/meta/one-flutter-view.cml +++ b/shell/platform/fuchsia/flutter/tests/integration/touch-input/touch-input-view/meta/touch-input-view.cml @@ -2,12 +2,9 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. { - include: [ - "sys/component/realm_builder_absolute.shard.cml", - "syslog/client.shard.cml", - ], + include: [ "syslog/client.shard.cml" ], program: { - data: "data/one-flutter", + data: "data/touch-input-view", // Always use the jit runner for now. // TODO(fxbug.dev/106577): Implement manifest merging build rules for V2 components. @@ -18,6 +15,12 @@ protocol: [ "fuchsia.ui.app.ViewProvider" ], }, ], + expose: [ + { + protocol: [ "fuchsia.ui.app.ViewProvider" ], + from: "self", + }, + ], use: [ { protocol: [ @@ -26,13 +29,7 @@ "fuchsia.ui.scenic.Scenic", "fuchsia.ui.test.input.TouchInputListener", "fuchsia.vulkan.loader.Loader", - ], - }, - ], - expose: [ - { - protocol: [ "fuchsia.ui.app.ViewProvider" ], - from: "self", - }, - ], + ] + } + ] } diff --git a/shell/platform/fuchsia/flutter/tests/integration/touch-input/touch-input-view/pubspec.yaml b/shell/platform/fuchsia/flutter/tests/integration/touch-input/touch-input-view/pubspec.yaml new file mode 100644 index 0000000000000..c572e05d0160a --- /dev/null +++ b/shell/platform/fuchsia/flutter/tests/integration/touch-input/touch-input-view/pubspec.yaml @@ -0,0 +1,8 @@ +# 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. + +name: one-flutter + +environment: + sdk: '>=2.12.0 <3.0.0' diff --git a/shell/platform/fuchsia/flutter/tests/integration/utils/BUILD.gn b/shell/platform/fuchsia/flutter/tests/integration/utils/BUILD.gn index b41ffae3a3907..dd4ae203bfd5c 100644 --- a/shell/platform/fuchsia/flutter/tests/integration/utils/BUILD.gn +++ b/shell/platform/fuchsia/flutter/tests/integration/utils/BUILD.gn @@ -50,7 +50,13 @@ source_set("portable_ui_test") { deps = [ ":check_view", + "$fuchsia_sdk_root/fidl:fuchsia.ui.app", + "$fuchsia_sdk_root/fidl:fuchsia.ui.input", "$fuchsia_sdk_root/fidl:fuchsia.ui.observation.geometry", + "$fuchsia_sdk_root/fidl:fuchsia.ui.policy", + "$fuchsia_sdk_root/fidl:fuchsia.ui.scenic", + "$fuchsia_sdk_root/fidl:fuchsia.ui.test.input", + "$fuchsia_sdk_root/fidl:fuchsia.ui.test.scene", "$fuchsia_sdk_root/pkg:async-loop-testing", "$fuchsia_sdk_root/pkg:sys_component_cpp_testing", "//flutter/fml", diff --git a/shell/platform/fuchsia/flutter/tests/integration/utils/portable_ui_test.cc b/shell/platform/fuchsia/flutter/tests/integration/utils/portable_ui_test.cc index 9d5cc3d094eb5..86a3147f9f63c 100644 --- a/shell/platform/fuchsia/flutter/tests/integration/utils/portable_ui_test.cc +++ b/shell/platform/fuchsia/flutter/tests/integration/utils/portable_ui_test.cc @@ -18,6 +18,7 @@ namespace fuchsia_test_utils { namespace { // Types imported for the realm_builder library. +using component_testing::ChildOptions; using component_testing::ChildRef; using component_testing::ParentRef; using component_testing::Protocol; @@ -39,10 +40,36 @@ void PortableUITest::SetUp() { void PortableUITest::SetUpRealmBase() { FML_LOG(INFO) << "Setting up realm base"; + // Add Flutter JIT runner as a child of the RealmBuilder + realm_builder_.AddChild(kFlutterJitRunner, kFlutterJitRunnerUrl); + + // Add environment providing the Flutter JIT runner + fuchsia::component::decl::Environment flutter_runner_environment; + flutter_runner_environment.set_name(kFlutterRunnerEnvironment); + flutter_runner_environment.set_extends( + fuchsia::component::decl::EnvironmentExtends::REALM); + flutter_runner_environment.set_runners({}); + auto environment_runners = flutter_runner_environment.mutable_runners(); + + // Add Flutter JIT runner to the environment + fuchsia::component::decl::RunnerRegistration flutter_jit_runner_reg; + flutter_jit_runner_reg.set_source(fuchsia::component::decl::Ref::WithChild( + fuchsia::component::decl::ChildRef{.name = kFlutterJitRunner})); + flutter_jit_runner_reg.set_source_name(kFlutterJitRunner); + flutter_jit_runner_reg.set_target_name(kFlutterJitRunner); + environment_runners->push_back(std::move(flutter_jit_runner_reg)); + auto realm_decl = realm_builder_.GetRealmDecl(); + if (!realm_decl.has_environments()) { + realm_decl.set_environments({}); + } + auto realm_environments = realm_decl.mutable_environments(); + realm_environments->push_back(std::move(flutter_runner_environment)); + realm_builder_.ReplaceRealmDecl(std::move(realm_decl)); + // Add test UI stack component. realm_builder_.AddChild(kTestUIStack, GetTestUIStackUrl()); - // Route base system services to flutter and the test UI stack. + // // Route base system services to flutter and the test UI stack. realm_builder_.AddRoute( Route{.capabilities = { @@ -50,18 +77,29 @@ void PortableUITest::SetUpRealmBase() { Protocol{fuchsia::sys::Environment::Name_}, Protocol{fuchsia::sysmem::Allocator::Name_}, Protocol{fuchsia::tracing::provider::Registry::Name_}, + Protocol{fuchsia::ui::input::ImeService::Name_}, + Protocol{kPointerInjectorRegistryName}, + Protocol{kPosixSocketProviderName}, Protocol{kVulkanLoaderServiceName}, - Protocol{kProfileProviderServiceName}, + component_testing::Directory{"config-data"}, }, - .source = ParentRef{}, - .targets = {kTestUIStackRef}}); + .source = ParentRef(), + .targets = {kFlutterJitRunnerRef, kTestUIStackRef}}); // Capabilities routed to test driver. realm_builder_.AddRoute(Route{ .capabilities = {Protocol{fuchsia::ui::test::input::Registry::Name_}, - Protocol{fuchsia::ui::test::scene::Controller::Name_}}, + Protocol{fuchsia::ui::test::scene::Controller::Name_}, + Protocol{fuchsia::ui::scenic::Scenic::Name_}}, + .source = kTestUIStackRef, + .targets = {ParentRef()}}); + + // Route UI capabilities from test UI stack to flutter runners. + realm_builder_.AddRoute(Route{ + .capabilities = {Protocol{fuchsia::ui::composition::Flatland::Name_}, + Protocol{fuchsia::ui::scenic::Scenic::Name_}}, .source = kTestUIStackRef, - .targets = {ParentRef{}}}); + .targets = {kFlutterJitRunnerRef}}); } void PortableUITest::ProcessViewGeometryResponse( diff --git a/shell/platform/fuchsia/flutter/tests/integration/utils/portable_ui_test.h b/shell/platform/fuchsia/flutter/tests/integration/utils/portable_ui_test.h index f246846d44c19..55afc990dd0bf 100644 --- a/shell/platform/fuchsia/flutter/tests/integration/utils/portable_ui_test.h +++ b/shell/platform/fuchsia/flutter/tests/integration/utils/portable_ui_test.h @@ -6,6 +6,10 @@ #define FLUTTER_SHELL_PLATFORM_FUCHSIA_FLUTTER_TESTS_INTEGRATION_UTILS_PORTABLE_UI_TEST_H_ #include +#include +#include +#include +#include #include #include #include @@ -23,11 +27,22 @@ class PortableUITest : public ::loop_fixture::RealLoop { // we must encode the names manually here. static constexpr auto kVulkanLoaderServiceName = "fuchsia.vulkan.loader.Loader"; - static constexpr auto kProfileProviderServiceName = - "fuchsia.sheduler.ProfileProvider"; + static constexpr auto kPosixSocketProviderName = + "fuchsia.posix.socket.Provider"; + static constexpr auto kPointerInjectorRegistryName = + "fuchsia.ui.pointerinjector.Registry"; + + // The naming and references used by Realm Builder static constexpr auto kTestUIStack = "ui"; static constexpr auto kTestUIStackRef = component_testing::ChildRef{kTestUIStack}; + static constexpr auto kFlutterJitRunner = "flutter_jit_runner"; + static constexpr auto kFlutterJitRunnerRef = + component_testing::ChildRef{kFlutterJitRunner}; + static constexpr auto kFlutterJitRunnerUrl = + "fuchsia-pkg://fuchsia.com/oot_flutter_jit_runner#meta/" + "flutter_jit_runner.cm"; + static constexpr auto kFlutterRunnerEnvironment = "flutter_runner_env"; void SetUp(); @@ -59,7 +74,8 @@ class PortableUITest : public ::loop_fixture::RealLoop { // Configures the test-specific component topology. virtual void ExtendRealm() = 0; - // Returns the test-ui-stack component url to use in this test. + // Returns the test-specific test-ui-stack component url to use. + // Usually overriden to return a value from gtest GetParam() virtual std::string GetTestUIStackUrl() = 0; // Helper method to watch watch for view geometry updates. diff --git a/tools/fuchsia/devshell/run_integration_test.sh b/tools/fuchsia/devshell/run_integration_test.sh index 7b059c6a61557..f3b86ad9130e7 100755 --- a/tools/fuchsia/devshell/run_integration_test.sh +++ b/tools/fuchsia/devshell/run_integration_test.sh @@ -47,6 +47,8 @@ shift # past argument # Ensure we know about the test and look up its packages. # The first package listed here should be the main package for the test # (the package that gets passed to `ffx test run`). +# Note: You do not need to include oot_flutter_jit_runner-0.far, the script +# automatically publishes it. test_packages= case $test_name in embedder) @@ -57,6 +59,9 @@ case $test_name in engine-warning "This test currently hangs because the Dart view hasn't been implemented yet. https://fxbug.dev/107917" test_packages=("text-input-test-0.far" "text-input-view.far") ;; + touch-input) + test_packages=("touch-input-test-0.far" "touch-input-view.far") + ;; *) engine-error "Unknown test name $test_name. You may need to add it to $0" exit 1 From 69f2bc881f466871605289571f913a75f700a4c4 Mon Sep 17 00:00:00 2001 From: Jenn Magder Date: Thu, 20 Oct 2022 10:34:22 -0700 Subject: [PATCH 3/3] Use iOS 16 APIs to rotate orientation (#36874) --- .../framework/Source/FlutterViewController.mm | 66 +++++++++++++------ .../Source/FlutterViewControllerTest.mm | 43 +++++++++--- 2 files changed, 80 insertions(+), 29 deletions(-) diff --git a/shell/platform/darwin/ios/framework/Source/FlutterViewController.mm b/shell/platform/darwin/ios/framework/Source/FlutterViewController.mm index 78195cf1800c8..45e90e0c76544 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterViewController.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterViewController.mm @@ -6,6 +6,7 @@ #import "flutter/shell/platform/darwin/ios/framework/Source/FlutterViewController_Internal.h" +#import #include #include "flutter/fml/memory/weak_ptr.h" @@ -1536,26 +1537,51 @@ - (void)onOrientationPreferencesUpdated:(NSNotification*)notification { - (void)performOrientationUpdate:(UIInterfaceOrientationMask)new_preferences { if (new_preferences != _orientationPreferences) { _orientationPreferences = new_preferences; - [UIViewController attemptRotationToDeviceOrientation]; - - UIInterfaceOrientationMask currentInterfaceOrientation = - 1 << [[UIApplication sharedApplication] statusBarOrientation]; - if (!(_orientationPreferences & currentInterfaceOrientation)) { - // Force orientation switch if the current orientation is not allowed - if (_orientationPreferences & UIInterfaceOrientationMaskPortrait) { - // This is no official API but more like a workaround / hack (using - // key-value coding on a read-only property). This might break in - // the future, but currently it´s the only way to force an orientation change - [[UIDevice currentDevice] setValue:@(UIInterfaceOrientationPortrait) forKey:@"orientation"]; - } else if (_orientationPreferences & UIInterfaceOrientationMaskPortraitUpsideDown) { - [[UIDevice currentDevice] setValue:@(UIInterfaceOrientationPortraitUpsideDown) - forKey:@"orientation"]; - } else if (_orientationPreferences & UIInterfaceOrientationMaskLandscapeLeft) { - [[UIDevice currentDevice] setValue:@(UIInterfaceOrientationLandscapeLeft) - forKey:@"orientation"]; - } else if (_orientationPreferences & UIInterfaceOrientationMaskLandscapeRight) { - [[UIDevice currentDevice] setValue:@(UIInterfaceOrientationLandscapeRight) - forKey:@"orientation"]; + + if (@available(iOS 16.0, *)) { + for (UIScene* scene in UIApplication.sharedApplication.connectedScenes) { + if (![scene isKindOfClass:[UIWindowScene class]]) { + continue; + } + UIWindowScene* windowScene = (UIWindowScene*)scene; + UIInterfaceOrientationMask currentInterfaceOrientation = + 1 << windowScene.interfaceOrientation; + if (!(_orientationPreferences & currentInterfaceOrientation)) { + [self setNeedsUpdateOfSupportedInterfaceOrientations]; + UIWindowSceneGeometryPreferencesIOS* preference = + [[UIWindowSceneGeometryPreferencesIOS alloc] + initWithInterfaceOrientations:_orientationPreferences]; + [windowScene + requestGeometryUpdateWithPreferences:preference + errorHandler:^(NSError* error) { + os_log_error(OS_LOG_DEFAULT, + "Failed to change device orientation: %@", + error); + }]; + } + } + } else { + UIInterfaceOrientationMask currentInterfaceOrientation = + 1 << [[UIApplication sharedApplication] statusBarOrientation]; + if (!(_orientationPreferences & currentInterfaceOrientation)) { + [UIViewController attemptRotationToDeviceOrientation]; + // Force orientation switch if the current orientation is not allowed + if (_orientationPreferences & UIInterfaceOrientationMaskPortrait) { + // This is no official API but more like a workaround / hack (using + // key-value coding on a read-only property). This might break in + // the future, but currently it´s the only way to force an orientation change + [[UIDevice currentDevice] setValue:@(UIInterfaceOrientationPortrait) + forKey:@"orientation"]; + } else if (_orientationPreferences & UIInterfaceOrientationMaskPortraitUpsideDown) { + [[UIDevice currentDevice] setValue:@(UIInterfaceOrientationPortraitUpsideDown) + forKey:@"orientation"]; + } else if (_orientationPreferences & UIInterfaceOrientationMaskLandscapeLeft) { + [[UIDevice currentDevice] setValue:@(UIInterfaceOrientationLandscapeLeft) + forKey:@"orientation"]; + } else if (_orientationPreferences & UIInterfaceOrientationMaskLandscapeRight) { + [[UIDevice currentDevice] setValue:@(UIInterfaceOrientationLandscapeRight) + forKey:@"orientation"]; + } } } } diff --git a/shell/platform/darwin/ios/framework/Source/FlutterViewControllerTest.mm b/shell/platform/darwin/ios/framework/Source/FlutterViewControllerTest.mm index cbdcf3cf6974e..7ed4c0592e0d3 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterViewControllerTest.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterViewControllerTest.mm @@ -888,22 +888,47 @@ - (void)orientationTestWithOrientationUpdate:(UIInterfaceOrientationMask)mask currentOrientation:(UIInterfaceOrientation)currentOrientation didChangeOrientation:(BOOL)didChange resultingOrientation:(UIInterfaceOrientation)resultingOrientation { - id deviceMock = OCMPartialMock([UIDevice currentDevice]); - if (!didChange) { - OCMReject([deviceMock setValue:[OCMArg any] forKey:@"orientation"]); + id mockApplication = OCMClassMock([UIApplication class]); + id mockWindowScene; + id deviceMock; + if (@available(iOS 16.0, *)) { + mockWindowScene = OCMClassMock([UIWindowScene class]); + OCMStub([mockWindowScene interfaceOrientation]).andReturn(currentOrientation); + if (!didChange) { + OCMReject([mockWindowScene requestGeometryUpdateWithPreferences:[OCMArg any] + errorHandler:[OCMArg any]]); + } else { + OCMExpect([mockWindowScene + requestGeometryUpdateWithPreferences:[OCMArg checkWithBlock:^BOOL( + UIWindowSceneGeometryPreferencesIOS* + preferences) { + return preferences.interfaceOrientations == mask; + }] + errorHandler:[OCMArg any]]); + } + OCMStub([mockApplication sharedApplication]).andReturn(mockApplication); + OCMStub([mockApplication connectedScenes]).andReturn([NSSet setWithObject:mockWindowScene]); } else { - OCMExpect([deviceMock setValue:@(resultingOrientation) forKey:@"orientation"]); - } + deviceMock = OCMPartialMock([UIDevice currentDevice]); + if (!didChange) { + OCMReject([deviceMock setValue:[OCMArg any] forKey:@"orientation"]); + } else { + OCMExpect([deviceMock setValue:@(resultingOrientation) forKey:@"orientation"]); + } + OCMStub([mockApplication sharedApplication]).andReturn(mockApplication); + OCMStub([mockApplication statusBarOrientation]).andReturn(currentOrientation); + } FlutterViewController* realVC = [[FlutterViewController alloc] initWithEngine:self.mockEngine nibName:nil bundle:nil]; - id mockApplication = OCMClassMock([UIApplication class]); - OCMStub([mockApplication sharedApplication]).andReturn(mockApplication); - OCMStub([mockApplication statusBarOrientation]).andReturn(currentOrientation); [realVC performOrientationUpdate:mask]; - OCMVerifyAll(deviceMock); + if (@available(iOS 16.0, *)) { + OCMVerifyAll(mockWindowScene); + } else { + OCMVerifyAll(deviceMock); + } [deviceMock stopMocking]; [mockApplication stopMocking]; }