mirror of
https://github.com/flutter/flutter.git
synced 2026-02-20 02:29:02 +08:00
[fuchsia] Migrate integration test to CFv2 (flutter/engine#35318)
This commit is contained in:
parent
08440907ca
commit
b0d80cc091
@ -2129,18 +2129,6 @@ FILE: ../../../flutter/shell/platform/fuchsia/flutter/gfx_platform_view.cc
|
||||
FILE: ../../../flutter/shell/platform/fuchsia/flutter/gfx_platform_view.h
|
||||
FILE: ../../../flutter/shell/platform/fuchsia/flutter/gfx_session_connection.cc
|
||||
FILE: ../../../flutter/shell/platform/fuchsia/flutter/gfx_session_connection.h
|
||||
FILE: ../../../flutter/shell/platform/fuchsia/flutter/integration_flutter_tests/embedder/child-view2/child_view2.dart
|
||||
FILE: ../../../flutter/shell/platform/fuchsia/flutter/integration_flutter_tests/embedder/child-view2/meta/child-view2.cmx
|
||||
FILE: ../../../flutter/shell/platform/fuchsia/flutter/integration_flutter_tests/embedder/flutter-embedder-test2.cc
|
||||
FILE: ../../../flutter/shell/platform/fuchsia/flutter/integration_flutter_tests/embedder/flutter-embedder-test2.h
|
||||
FILE: ../../../flutter/shell/platform/fuchsia/flutter/integration_flutter_tests/embedder/meta/flutter-embedder-test2.cmx
|
||||
FILE: ../../../flutter/shell/platform/fuchsia/flutter/integration_flutter_tests/embedder/parent-view2/meta/parent-view2.cmx
|
||||
FILE: ../../../flutter/shell/platform/fuchsia/flutter/integration_flutter_tests/embedder/parent-view2/parent_view2.dart
|
||||
FILE: ../../../flutter/shell/platform/fuchsia/flutter/integration_flutter_tests/fuchsia_testing/src/lib/ui/base_view/base_view.cc
|
||||
FILE: ../../../flutter/shell/platform/fuchsia/flutter/integration_flutter_tests/fuchsia_testing/src/lib/ui/base_view/base_view.h
|
||||
FILE: ../../../flutter/shell/platform/fuchsia/flutter/integration_flutter_tests/fuchsia_testing/src/lib/ui/base_view/embedded_view_utils.cc
|
||||
FILE: ../../../flutter/shell/platform/fuchsia/flutter/integration_flutter_tests/fuchsia_testing/src/lib/ui/base_view/embedded_view_utils.h
|
||||
FILE: ../../../flutter/shell/platform/fuchsia/flutter/integration_flutter_tests/fuchsia_testing/src/lib/ui/base_view/math.h
|
||||
FILE: ../../../flutter/shell/platform/fuchsia/flutter/isolate_configurator.cc
|
||||
FILE: ../../../flutter/shell/platform/fuchsia/flutter/isolate_configurator.h
|
||||
FILE: ../../../flutter/shell/platform/fuchsia/flutter/kernel/extract_far.dart
|
||||
|
||||
@ -457,6 +457,34 @@ jit_runner("flutter_jit_product_runner") {
|
||||
product = true
|
||||
}
|
||||
|
||||
# "OOT" copy of the runner used by tests, to avoid conflicting with the runner
|
||||
# in the base fuchsia image.
|
||||
# TODO(fxbug.dev/106575): Fix this with subpackages.
|
||||
aot_runner("oot_flutter_aot_runner") {
|
||||
product = false
|
||||
}
|
||||
|
||||
# "OOT" copy of the runner used by tests, to avoid conflicting with the runner
|
||||
# in the base fuchsia image.
|
||||
# TODO(fxbug.dev/106575): Fix this with subpackages.
|
||||
aot_runner("oot_flutter_aot_product_runner") {
|
||||
product = true
|
||||
}
|
||||
|
||||
# "OOT" copy of the runner used by tests, to avoid conflicting with the runner
|
||||
# in the base fuchsia image.
|
||||
# TODO(fxbug.dev/106575): Fix this with subpackages.
|
||||
jit_runner("oot_flutter_jit_runner") {
|
||||
product = false
|
||||
}
|
||||
|
||||
# "OOT" copy of the runner used by tests, to avoid conflicting with the runner
|
||||
# in the base fuchsia image.
|
||||
# TODO(fxbug.dev/106575): Fix this with subpackages.
|
||||
jit_runner("oot_flutter_jit_product_runner") {
|
||||
product = true
|
||||
}
|
||||
|
||||
test_fixtures("flutter_runner_fixtures") {
|
||||
fixtures = []
|
||||
}
|
||||
@ -885,7 +913,7 @@ if (enable_unittests) {
|
||||
":testing_tests",
|
||||
":txt_tests",
|
||||
":ui_tests",
|
||||
"integration_flutter_tests",
|
||||
"tests/integration",
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,5 +0,0 @@
|
||||
dworsham@google.com
|
||||
sanjayc@google.com
|
||||
richkadel@google.com
|
||||
|
||||
# COMPONENT: FlutteronFuchsia
|
||||
@ -1,67 +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.
|
||||
|
||||
assert(is_fuchsia)
|
||||
|
||||
import("//build/fuchsia/sdk.gni")
|
||||
import("//flutter/tools/fuchsia/fuchsia_archive.gni")
|
||||
|
||||
group("tests") {
|
||||
testonly = true
|
||||
deps = [
|
||||
":flutter-embedder-test2",
|
||||
"//flutter/shell/platform/fuchsia/flutter/integration_flutter_tests/embedder/child-view2",
|
||||
"//flutter/shell/platform/fuchsia/flutter/integration_flutter_tests/embedder/parent-view2",
|
||||
]
|
||||
}
|
||||
|
||||
executable("flutter-embedder-test2-bin") {
|
||||
testonly = true
|
||||
|
||||
output_name = "flutter-embedder-test2"
|
||||
|
||||
sources = [
|
||||
"flutter-embedder-test2.cc",
|
||||
"flutter-embedder-test2.h",
|
||||
]
|
||||
|
||||
# This is needed for //third_party/googletest for linking zircon symbols.
|
||||
libs = [ "$fuchsia_sdk_path/arch/$target_cpu/sysroot/lib/libzircon.so" ]
|
||||
|
||||
include_dirs = [ "//flutter/shell/platform/fuchsia/flutter/integration_flutter_tests/fuchsia_testing" ]
|
||||
|
||||
public_deps = [ "//third_party/googletest:gtest" ]
|
||||
|
||||
deps = [
|
||||
"$fuchsia_sdk_root/fidl:fuchsia.sys",
|
||||
"$fuchsia_sdk_root/fidl:fuchsia.ui.app",
|
||||
"$fuchsia_sdk_root/fidl:fuchsia.ui.input",
|
||||
"$fuchsia_sdk_root/fidl:fuchsia.ui.lifecycle",
|
||||
"$fuchsia_sdk_root/fidl:fuchsia.ui.policy",
|
||||
"$fuchsia_sdk_root/fidl:fuchsia.ui.scenic",
|
||||
"$fuchsia_sdk_root/pkg:async-loop-cpp",
|
||||
"$fuchsia_sdk_root/pkg:async-loop-default",
|
||||
"$fuchsia_sdk_root/pkg:fit",
|
||||
"$fuchsia_sdk_root/pkg:scenic_cpp",
|
||||
"$fuchsia_sdk_root/pkg:sys_cpp",
|
||||
"$fuchsia_sdk_root/pkg:sys_cpp_testing",
|
||||
"$fuchsia_sdk_root/pkg:zx",
|
||||
"//flutter/shell/platform/fuchsia/flutter/integration_flutter_tests/fuchsia_testing/src/lib/ui/base_view",
|
||||
"//flutter/shell/platform/fuchsia/flutter/integration_flutter_tests/fuchsia_testing/src/ui/testing/views",
|
||||
"//third_party/dart/runtime:libdart_jit",
|
||||
"//third_party/googletest:gtest_main",
|
||||
]
|
||||
}
|
||||
|
||||
fuchsia_test_archive("flutter-embedder-test2") {
|
||||
deps = [
|
||||
":flutter-embedder-test2-bin",
|
||||
"//flutter/shell/platform/fuchsia/flutter/integration_flutter_tests/embedder/child-view2:package",
|
||||
"//flutter/shell/platform/fuchsia/flutter/integration_flutter_tests/embedder/parent-view2:package",
|
||||
]
|
||||
|
||||
binary = "$target_name"
|
||||
|
||||
cmx_file = rebase_path("meta/$target_name.cmx")
|
||||
}
|
||||
@ -1,173 +0,0 @@
|
||||
# `flutter scenic embedder tests`
|
||||
|
||||
## Configure and build fuchsia
|
||||
|
||||
For tests that require scenic, for example, run `fx set` with the required
|
||||
targets; for example:
|
||||
|
||||
```shell
|
||||
$ cd "$FUCHSIA_DIR"
|
||||
$ fx set core.x64 \
|
||||
--with //src/ui/scenic \
|
||||
--with //src/ui/bin/root_presenter \
|
||||
--with //src/ui/bin/hardware_display_controller_provider
|
||||
$ fx build
|
||||
```
|
||||
|
||||
Note 1: You could use `--with-base` here, instead of `--with`, but if so, you
|
||||
would also need to add `--with-base //garnet/bin/run_test_component`. More on
|
||||
this below, under [Start the package servers](#start-the-package-servers).
|
||||
|
||||
Note 2: The `fx set` flags, above, offer a minimized fuchsia platform
|
||||
configuration to successfully execute the test, but some optional services may
|
||||
be missing. Be aware that the Fuchsia system logs may include multiple
|
||||
occurrences `WARNING: error resolving ...` messages, such as the following,
|
||||
which can be ignored:
|
||||
|
||||
```
|
||||
[pkg-resolver] WARNING: error resolving fuchsia-pkg://fuchsia.com/fonts/0 ...
|
||||
[pkg-resolver] WARNING: error resolving fuchsia-pkg://fuchsia.com/ime_service/0 ...
|
||||
[pkg-resolver] WARNING: error resolving fuchsia-pkg://fuchsia.com/intl_property_manager/0 ...
|
||||
```
|
||||
|
||||
## Restart and reboot your device
|
||||
|
||||
_(Optional)_ If developing with the emulator, launch (or shutdown and relaunch)
|
||||
the emulator.
|
||||
|
||||
```shell
|
||||
fx vdl start -N
|
||||
```
|
||||
|
||||
NOTE: Do _not_ run the default package server. The instructions below describe
|
||||
how to launch a flutter-specific package server.
|
||||
|
||||
Or if you've rebuilt fuchsia for a device that is already running a version of
|
||||
fuchsia, you may be able to reboot without restarting the device:
|
||||
|
||||
```shell
|
||||
$ fx reboot -r
|
||||
```
|
||||
|
||||
If you are building a device that launches the UI at startup, you will likely
|
||||
need to kill Scenic before running the test.
|
||||
|
||||
```shell
|
||||
$ fx shell killall scenic.cmx
|
||||
```
|
||||
|
||||
## Build the test
|
||||
|
||||
You can specify the test's package target to build only the test package, with
|
||||
its dependencies. This will also build the required runner.
|
||||
|
||||
```shell
|
||||
$ cd "$FLUTTER_ENGINE_DIR/src"
|
||||
$ ./flutter/tools/gn --fuchsia <flags> \
|
||||
# for example: --goma --fuchsia-cpu=x64 --runtime-mode=debug
|
||||
$ ninja -C out/fuchsia_debug_x64 \
|
||||
flutter/shell/platform/fuchsia/flutter/integration_flutter_tests
|
||||
```
|
||||
|
||||
## Publish the test packages to the Fuchsia package server
|
||||
|
||||
The tests currently specify the Fuchsia package server's standard domain,
|
||||
`fuchsia.com`, as the server to use to resolve (locate and load) the test
|
||||
packages. So, before running the test, the most recently built `.far` files
|
||||
need to be published to the Fuchsia package repo:
|
||||
|
||||
```shell
|
||||
$ fx pm publish -a -repo "$(cat $FUCHSIA_DIR/.fx-build-dir)/amber-files/" \
|
||||
-f "$FLUTTER_ENGINE_DIR"/src/out/fuchsia_*64/flutter-embedder-test2-0.far
|
||||
$ fx pm publish -a -repo "$(cat $FUCHSIA_DIR/.fx-build-dir)/amber-files/" \
|
||||
-f $(find "$FLUTTER_ENGINE_DIR"/src/out/fuchsia_*64 -name parent-view2.far)
|
||||
$ fx pm publish -a -repo "$(cat $FUCHSIA_DIR/.fx-build-dir)/amber-files/" \
|
||||
-f $(find "$FLUTTER_ENGINE_DIR"/src/out/fuchsia_*64 -name child-view2.far)
|
||||
```
|
||||
|
||||
## Run the test (using the package server at `fuchsia.com`)
|
||||
|
||||
```shell
|
||||
$ fx test flutter-embedder-test2
|
||||
```
|
||||
|
||||
## Make a change and re-run the test
|
||||
|
||||
If, for example, you only make a change to the Dart code in `parent-view2`, you
|
||||
can rebuild only the parent-view2 package target, republish it, and then re-run
|
||||
the test, with:
|
||||
|
||||
```shell
|
||||
$ ninja -C out/fuchsia_debug_x64 \
|
||||
flutter/shell/platform/fuchsia/flutter/integration_flutter_tests/embedder/parent-view2:package
|
||||
$ fx pm publish -a -repo "$(cat $FUCHSIA_DIR/.fx-build-dir)/amber-files/" \
|
||||
-f $(find "$FLUTTER_ENGINE_DIR"/src/out/fuchsia_*64 -name parent-view2.far)
|
||||
$ fx test flutter-embedder-test2
|
||||
```
|
||||
|
||||
From here, you can modify the Flutter test, rebuild flutter, and usually rerun
|
||||
the test without rebooting, by repeating the commands above.
|
||||
|
||||
The embedder tests must be run on a product without a graphical base shell,
|
||||
such as `core` because it starts and stops Scenic.
|
||||
|
||||
## (Alternative) Serving flutter packages from a custom package server
|
||||
|
||||
If you want to use a custom package server, you will need to edit these sources:
|
||||
|
||||
* `//flutter/shell/platform/fuchsia/flutter/integration_flutter_tests/embedder/flutter-embedder-test2.cc`
|
||||
* `//flutter/shell/platform/fuchsia/flutter/integration_flutter_tests/embedder/parent-view2/parent_view2.dart`
|
||||
|
||||
Search for the component URLs with `fuchsia.com`, and change it to `engine`,
|
||||
which is the domain currently registered with the custom package server in
|
||||
`//tools/fuchsia/devshell/serve.sh`.
|
||||
|
||||
WARNING: Be careful not to check in that change because CI requires using the
|
||||
`fuchsia.com` domain/package server.
|
||||
|
||||
The default fuchsia package server (launched via `fx serve`) is normally
|
||||
required, unless all of your test's package dependencies are included in the
|
||||
fuchsia system image. You can force additional packages into the system image
|
||||
with `fx set ... --with-base <package>` (instead of using `--with`). For
|
||||
example, in the `fx set` command above, using, `--with-base scenic`, and so on.
|
||||
Note, however, that the default `core.x64` configuration bundles the test
|
||||
runner as if it was included via
|
||||
`--with //garnet/bin/run_test_component`, so to include the test runner in the
|
||||
system image requires adding that package as well, via `--with-base`, instead.
|
||||
|
||||
In order to serve fuchsia package dependencies (like `scenic`, `root_presenter`,
|
||||
and `hardware-display-controller-provider`), without forcing them into the
|
||||
system image, you will need to run the fuchsia default package server, via `fx
|
||||
serve`.
|
||||
|
||||
The `flutter/engine` packages (tests and flutter runners, for dart-based tests)
|
||||
are served from a separate package server. The `flutter/engine` repo's
|
||||
`serve.sh` script launches this secondary package server, and configures
|
||||
package URL rewrite rules to redirect fuchsia's requests for flutter- and
|
||||
dart-runner packages from `fuchsia.com` to flutter's package server instead.
|
||||
|
||||
**IMPORTANT:** _The flutter package server must be launched **after** the
|
||||
default package server, because both `fx serve` and flutter's `serve.sh` set
|
||||
package URL rewrite rules, and only the last one wins._
|
||||
|
||||
Launch each package server in a separate window or shell:
|
||||
|
||||
```shell
|
||||
$ cd "${FUCHSIA_DIR}"
|
||||
$ fx serve
|
||||
```
|
||||
|
||||
From the flutter engine `src` directory, run the following script to launch the
|
||||
`engine` package server, to serve the flutter runner and test components.
|
||||
|
||||
```shell
|
||||
$ flutter/tools/fuchsia/devshell/serve.sh --out out/fuchsia_debug_x64 --only-serve-runners
|
||||
```
|
||||
|
||||
## Run the test, using `engine` as the package server domain
|
||||
|
||||
```shell
|
||||
$ fx test flutter-embedder-test2
|
||||
```
|
||||
|
||||
You can recompile and run the test without needing to re-publish the `.far`.
|
||||
@ -1,38 +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.
|
||||
|
||||
import("//build/fuchsia/sdk.gni")
|
||||
import("//flutter/tools/fuchsia/dart/dart_library.gni")
|
||||
import("//flutter/tools/fuchsia/flutter/flutter_component.gni")
|
||||
import("//flutter/tools/fuchsia/gn-sdk/package.gni")
|
||||
|
||||
dart_library("child-view2_dart_library") {
|
||||
package_name = "child-view2"
|
||||
|
||||
source_dir = "."
|
||||
sources = [ "child_view2.dart" ]
|
||||
|
||||
deps = []
|
||||
}
|
||||
|
||||
flutter_component("child-view2_flutter_component") {
|
||||
main_package = "child-view2"
|
||||
component_name = "child-view2"
|
||||
main_dart = "child_view2.dart"
|
||||
manifest = rebase_path("meta/child-view2.cmx")
|
||||
deps = [ ":child-view2_dart_library" ]
|
||||
}
|
||||
|
||||
# TODO(richkadel): The target name is set differently compared to fuchsia.git's flutter_app().
|
||||
# Unlike in fuchsia.git's version of fuchsia_component, the Fuchsia GN SDK
|
||||
# version passes the component name to fuchsia_component via it's target_name only.
|
||||
# GN SDK's fuchsia_component doesn't have a `component_name` argument! So I'm forced to set
|
||||
# the component name via "target_name". This is a problem in fuchsia_package, which uses
|
||||
# the target_name to name the fuchsia_pm_tool target, creating duplicate target IDs!
|
||||
# So I have to change the fuchsia_package name to something that is NOT the component name,
|
||||
# and then set the package_name (which fuchsia_package does support).
|
||||
fuchsia_package("package") {
|
||||
package_name = "child-view2"
|
||||
deps = [ ":child-view2_flutter_component" ]
|
||||
}
|
||||
@ -1,13 +0,0 @@
|
||||
{
|
||||
"program": {
|
||||
"data": "data/child-view2"
|
||||
},
|
||||
"sandbox": {
|
||||
"services": [
|
||||
"fuchsia.fonts.Provider",
|
||||
"fuchsia.sys.Environment",
|
||||
"fuchsia.ui.input.ImeService",
|
||||
"fuchsia.ui.scenic.Scenic"
|
||||
]
|
||||
}
|
||||
}
|
||||
@ -1,218 +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.
|
||||
|
||||
#include "flutter-embedder-test2.h"
|
||||
|
||||
namespace flutter_embedder_test2 {
|
||||
|
||||
// TODO(richkadel): To run the test serving the runner and test packages from
|
||||
// the flutter/engine package server (via
|
||||
// `//flutter/tools/fuchsia/devshell/serve.sh`), change `fuchsia.com` to
|
||||
// `engine`.
|
||||
constexpr char kParentViewUrl[] =
|
||||
"fuchsia-pkg://fuchsia.com/parent-view2#meta/parent-view2.cmx";
|
||||
|
||||
constexpr scenic::Color kParentBackgroundColor = {0x00, 0x00, 0xFF,
|
||||
0xFF}; // Blue
|
||||
constexpr scenic::Color kParentTappedColor = {0x00, 0x00, 0x00, 0xFF}; // Black
|
||||
constexpr scenic::Color kChildBackgroundColor = {0xFF, 0x00, 0xFF,
|
||||
0xFF}; // Pink
|
||||
constexpr scenic::Color kChildTappedColor = {0xFF, 0xFF, 0x00, 0xFF}; // Yellow
|
||||
|
||||
// TODO(fxb/94000): The new flutter renderer draws overlays as a single, large
|
||||
// layer. Some parts of this layer are fully transparent, so we want the
|
||||
// compositor to treat the layer as transparent and blend it with the contents
|
||||
// below.
|
||||
//
|
||||
// The gfx Scenic API only provides one way to mark this layer as transparent
|
||||
// which is to set an opacity < 1.0 for the entire layer. In practice, we use
|
||||
// 0.9961 (254 / 255) as an opacity value to force transparency. Unfortunately
|
||||
// this causes the overlay to blend very slightly and it looks wrong.
|
||||
//
|
||||
// Flatland allows marking a layer as transparent while still using a 1.0
|
||||
// opacity value when blending, so migrating flutter to Flatland will fix this
|
||||
// issue. For now we just hard-code the broken, blended values.
|
||||
constexpr scenic::Color kOverlayBackgroundColor1 = {
|
||||
0x00, 0xFF, 0x0E, 0xFF}; // Green, blended with blue (FEMU local)
|
||||
constexpr scenic::Color kOverlayBackgroundColor2 = {
|
||||
0x0E, 0xFF, 0x0E, 0xFF}; // Green, blended with pink (FEMU local)
|
||||
constexpr scenic::Color kOverlayBackgroundColor3 = {
|
||||
0x00, 0xFF, 0x0D, 0xFF}; // Green, blended with blue (AEMU infra)
|
||||
constexpr scenic::Color kOverlayBackgroundColor4 = {
|
||||
0x0D, 0xFF, 0x0D, 0xFF}; // Green, blended with pink (AEMU infra)
|
||||
constexpr scenic::Color kOverlayBackgroundColor5 = {
|
||||
0x00, 0xFE, 0x0D, 0xFF}; // Green, blended with blue (NUC)
|
||||
constexpr scenic::Color kOverlayBackgroundColor6 = {
|
||||
0x0D, 0xFF, 0x00, 0xFF}; // Green, blended with pink (NUC)
|
||||
|
||||
static size_t OverlayPixelCount(std::map<scenic::Color, size_t>& histogram) {
|
||||
return histogram[kOverlayBackgroundColor1] +
|
||||
histogram[kOverlayBackgroundColor2] +
|
||||
histogram[kOverlayBackgroundColor3] +
|
||||
histogram[kOverlayBackgroundColor4] +
|
||||
histogram[kOverlayBackgroundColor5] +
|
||||
histogram[kOverlayBackgroundColor6];
|
||||
}
|
||||
|
||||
/// Defines a list of services that are injected into the test environment.
|
||||
/// Unlike the injected-services in CMX which are injected per test package,
|
||||
/// these are injected per test and result in a more hermetic test environment.
|
||||
const std::vector<std::pair<const char*, const char*>> GetInjectedServices() {
|
||||
std::vector<std::pair<const char*, const char*>> injected_services = {{
|
||||
{"fuchsia.accessibility.semantics.SemanticsManager",
|
||||
"fuchsia-pkg://fuchsia.com/a11y-manager#meta/a11y-manager.cmx"},
|
||||
{"fuchsia.fonts.Provider",
|
||||
"fuchsia-pkg://fuchsia.com/fonts#meta/fonts.cmx"},
|
||||
{"fuchsia.hardware.display.Provider",
|
||||
"fuchsia-pkg://fuchsia.com/"
|
||||
"fake-hardware-display-controller-provider#meta/hdcp.cmx"},
|
||||
{"fuchsia.intl.PropertyProvider",
|
||||
"fuchsia-pkg://fuchsia.com/intl_property_manager#meta/"
|
||||
"intl_property_manager.cmx"},
|
||||
{"fuchsia.netstack.Netstack",
|
||||
"fuchsia-pkg://fuchsia.com/network-legacy-deprecated#meta/netstack.cmx"},
|
||||
{"fuchsia.posix.socket.Provider",
|
||||
"fuchsia-pkg://fuchsia.com/network-legacy-deprecated#meta/netstack.cmx"},
|
||||
{"fuchsia.tracing.provider.Registry",
|
||||
"fuchsia-pkg://fuchsia.com/trace_manager#meta/trace_manager.cmx"},
|
||||
{"fuchsia.ui.input.ImeService",
|
||||
"fuchsia-pkg://fuchsia.com/text_manager#meta/text_manager.cmx"},
|
||||
{"fuchsia.ui.input.ImeVisibilityService",
|
||||
"fuchsia-pkg://fuchsia.com/text_manager#meta/text_manager.cmx"},
|
||||
{"fuchsia.ui.scenic.Scenic",
|
||||
"fuchsia-pkg://fuchsia.com/scenic#meta/scenic.cmx"},
|
||||
{"fuchsia.ui.pointerinjector.Registry",
|
||||
"fuchsia-pkg://fuchsia.com/scenic#meta/scenic.cmx"}, // For
|
||||
// root_presenter
|
||||
// TODO(fxbug.dev/82655): Remove this after migrating to RealmBuilder.
|
||||
{"fuchsia.ui.lifecycle.LifecycleController",
|
||||
"fuchsia-pkg://fuchsia.com/scenic#meta/scenic.cmx"},
|
||||
{"fuchsia.ui.policy.Presenter",
|
||||
"fuchsia-pkg://fuchsia.com/root_presenter#meta/root_presenter.cmx"},
|
||||
{"fuchsia.ui.input.InputDeviceRegistry",
|
||||
"fuchsia-pkg://fuchsia.com/root_presenter#meta/root_presenter.cmx"},
|
||||
}};
|
||||
return injected_services;
|
||||
}
|
||||
|
||||
TEST_F(FlutterScenicEmbedderTests, Embedding) {
|
||||
RunAppWithArgs(kParentViewUrl);
|
||||
|
||||
// Take screenshot until we see the child-view2's embedded color.
|
||||
ASSERT_TRUE(TakeScreenshotUntil(
|
||||
kChildBackgroundColor, [](scenic::Screenshot screenshot,
|
||||
std::map<scenic::Color, size_t> histogram) {
|
||||
// Expect parent and child background colors, with parent color > child
|
||||
// color.
|
||||
EXPECT_GT(histogram[kParentBackgroundColor], 0u);
|
||||
EXPECT_GT(histogram[kChildBackgroundColor], 0u);
|
||||
EXPECT_GT(histogram[kParentBackgroundColor],
|
||||
histogram[kChildBackgroundColor]);
|
||||
|
||||
// Expect all corners to be the parent-view2 background color
|
||||
EXPECT_EQ(kParentBackgroundColor, screenshot.ColorAtPixelXY(10, 10));
|
||||
EXPECT_EQ(kParentBackgroundColor,
|
||||
screenshot.ColorAtPixelXY(screenshot.width() - 10, 0));
|
||||
EXPECT_EQ(kParentBackgroundColor,
|
||||
screenshot.ColorAtPixelXY(0, screenshot.height() - 10));
|
||||
EXPECT_EQ(kParentBackgroundColor,
|
||||
screenshot.ColorAtPixelXY(screenshot.width() - 10,
|
||||
screenshot.height() - 10));
|
||||
}));
|
||||
}
|
||||
|
||||
TEST_F(FlutterScenicEmbedderTests, HittestEmbedding) {
|
||||
RunAppWithArgs(kParentViewUrl);
|
||||
|
||||
// Take screenshot until we see the child-view2's embedded color.
|
||||
ASSERT_TRUE(TakeScreenshotUntil(kChildBackgroundColor));
|
||||
|
||||
// Tap the center of child view2.
|
||||
InjectInput();
|
||||
|
||||
// Take screenshot until we see the child-view2's tapped color.
|
||||
ASSERT_TRUE(TakeScreenshotUntil(
|
||||
kChildTappedColor, [](scenic::Screenshot screenshot,
|
||||
std::map<scenic::Color, size_t> histogram) {
|
||||
// Expect parent and child background colors, with parent color > child
|
||||
// color.
|
||||
EXPECT_GT(histogram[kParentBackgroundColor], 0u);
|
||||
EXPECT_EQ(histogram[kChildBackgroundColor], 0u);
|
||||
EXPECT_GT(histogram[kChildTappedColor], 0u);
|
||||
EXPECT_GT(histogram[kParentBackgroundColor],
|
||||
histogram[kChildTappedColor]);
|
||||
}));
|
||||
}
|
||||
|
||||
TEST_F(FlutterScenicEmbedderTests, HittestDisabledEmbedding) {
|
||||
RunAppWithArgs(kParentViewUrl, {"--no-hitTestable"});
|
||||
|
||||
// Take screenshots until we see the child-view2's embedded color.
|
||||
ASSERT_TRUE(TakeScreenshotUntil(kChildBackgroundColor));
|
||||
|
||||
// Tap the center of child view2. Since it's not hit-testable, the tap should
|
||||
// go to the parent.
|
||||
InjectInput();
|
||||
|
||||
// The parent-view2 should change color.
|
||||
ASSERT_TRUE(TakeScreenshotUntil(
|
||||
kParentTappedColor, [](scenic::Screenshot screenshot,
|
||||
std::map<scenic::Color, size_t> histogram) {
|
||||
// Expect parent and child background colors, with parent color > child
|
||||
// color.
|
||||
EXPECT_EQ(histogram[kParentBackgroundColor], 0u);
|
||||
EXPECT_GT(histogram[kParentTappedColor], 0u);
|
||||
EXPECT_GT(histogram[kChildBackgroundColor], 0u);
|
||||
EXPECT_EQ(histogram[kChildTappedColor], 0u);
|
||||
EXPECT_GT(histogram[kParentTappedColor],
|
||||
histogram[kChildBackgroundColor]);
|
||||
}));
|
||||
}
|
||||
|
||||
TEST_F(FlutterScenicEmbedderTests, EmbeddingWithOverlay) {
|
||||
RunAppWithArgs(kParentViewUrl, {"--showOverlay"});
|
||||
|
||||
// Take screenshot until we see the child-view2's embedded color.
|
||||
ASSERT_TRUE(TakeScreenshotUntil(
|
||||
kChildBackgroundColor, [](scenic::Screenshot screenshot,
|
||||
std::map<scenic::Color, size_t> histogram) {
|
||||
// Expect parent, overlay and child background colors.
|
||||
// With parent color > child color and overlay color > child color.
|
||||
const size_t overlay_pixel_count = OverlayPixelCount(histogram);
|
||||
EXPECT_GT(histogram[kParentBackgroundColor], 0u);
|
||||
EXPECT_GT(overlay_pixel_count, 0u);
|
||||
EXPECT_GT(histogram[kChildBackgroundColor], 0u);
|
||||
EXPECT_GT(histogram[kParentBackgroundColor],
|
||||
histogram[kChildBackgroundColor]);
|
||||
EXPECT_GT(overlay_pixel_count, histogram[kChildBackgroundColor]);
|
||||
}));
|
||||
}
|
||||
|
||||
TEST_F(FlutterScenicEmbedderTests, HittestEmbeddingWithOverlay) {
|
||||
RunAppWithArgs(kParentViewUrl, {"--showOverlay"});
|
||||
|
||||
// Take screenshot until we see the child-view2's embedded color.
|
||||
ASSERT_TRUE(TakeScreenshotUntil(kChildBackgroundColor));
|
||||
|
||||
// Tap the center of child view2.
|
||||
InjectInput();
|
||||
|
||||
// Take screenshot until we see the child-view2's tapped color.
|
||||
ASSERT_TRUE(TakeScreenshotUntil(
|
||||
kChildTappedColor, [](scenic::Screenshot screenshot,
|
||||
std::map<scenic::Color, size_t> histogram) {
|
||||
// Expect parent, overlay and child background colors.
|
||||
// With parent color > child color and overlay color > child color.
|
||||
const size_t overlay_pixel_count = OverlayPixelCount(histogram);
|
||||
EXPECT_GT(histogram[kParentBackgroundColor], 0u);
|
||||
EXPECT_GT(overlay_pixel_count, 0u);
|
||||
EXPECT_EQ(histogram[kChildBackgroundColor], 0u);
|
||||
EXPECT_GT(histogram[kChildTappedColor], 0u);
|
||||
EXPECT_GT(histogram[kParentBackgroundColor],
|
||||
histogram[kChildTappedColor]);
|
||||
EXPECT_GT(overlay_pixel_count, histogram[kChildTappedColor]);
|
||||
}));
|
||||
}
|
||||
|
||||
} // namespace flutter_embedder_test2
|
||||
@ -1,234 +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.
|
||||
|
||||
#ifndef SRC_UI_TESTS_INTEGRATION_FLUTTER_TESTS_EMBEDDER_flutter_embedder_test2_H_
|
||||
#define SRC_UI_TESTS_INTEGRATION_FLUTTER_TESTS_EMBEDDER_flutter_embedder_test2_H_
|
||||
|
||||
#include <fuchsia/ui/lifecycle/cpp/fidl.h>
|
||||
#include <fuchsia/ui/policy/cpp/fidl.h>
|
||||
#include <fuchsia/ui/scenic/cpp/fidl.h>
|
||||
#include <fuchsia/ui/views/cpp/fidl.h>
|
||||
#include <gtest/gtest.h>
|
||||
#include <lib/async/cpp/task.h>
|
||||
#include <lib/fidl/cpp/binding_set.h>
|
||||
#include <lib/sys/cpp/component_context.h>
|
||||
#include <lib/sys/cpp/testing/test_with_environment.h>
|
||||
#include <lib/ui/scenic/cpp/view_token_pair.h>
|
||||
#include <lib/zx/clock.h>
|
||||
#include <zircon/status.h>
|
||||
#include <zircon/time.h>
|
||||
#include "flutter/fml/logging.h"
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "src/lib/ui/base_view/embedded_view_utils.h"
|
||||
#include "src/ui/testing/views/color.h"
|
||||
#include "src/ui/testing/views/embedder_view.h"
|
||||
|
||||
namespace flutter_embedder_test2 {
|
||||
|
||||
/// Defines a list of services that are injected into the test environment.
|
||||
/// Unlike the injected-services in CMX which are injected per test package,
|
||||
/// these are injected per test and result in a more hermetic test environment.
|
||||
const std::vector<std::pair<const char*, const char*>> GetInjectedServices();
|
||||
|
||||
// Timeout when waiting on Scenic API calls like |GetDisplayInfo|.
|
||||
constexpr zx::duration kCallTimeout = zx::sec(5);
|
||||
// Timeout for Scenic's |TakeScreenshot| FIDL call.
|
||||
constexpr zx::duration kScreenshotTimeout = zx::sec(10);
|
||||
// Timeout to fail the test if it goes beyond this duration.
|
||||
constexpr zx::duration kTestTimeout = zx::min(1);
|
||||
|
||||
class FlutterScenicEmbedderTests : public sys::testing::TestWithEnvironment,
|
||||
public ::testing::Test {
|
||||
public:
|
||||
// |testing::Test|
|
||||
void SetUp() override {
|
||||
Test::SetUp();
|
||||
|
||||
// Create test-specific launchable services.
|
||||
auto services = TestWithEnvironment::CreateServices();
|
||||
for (const auto& service_info : GetInjectedServices()) {
|
||||
zx_status_t status = services->AddServiceWithLaunchInfo(
|
||||
{.url = service_info.second}, service_info.first);
|
||||
FML_CHECK(status == ZX_OK)
|
||||
<< "Failed to add service " << service_info.first;
|
||||
}
|
||||
|
||||
environment_ = CreateNewEnclosingEnvironment(
|
||||
"flutter-embedder-test2s", std::move(services),
|
||||
{.inherit_parent_services = true});
|
||||
WaitForEnclosingEnvToStart(environment());
|
||||
|
||||
FML_VLOG(fml::LOG_INFO) << "Created test environment.";
|
||||
|
||||
// Connects to scenic lifecycle controller in order to shutdown scenic at
|
||||
// the end of the test. This ensures the correct ordering of shutdown under
|
||||
// CFv1: first scenic, then the fake display controller.
|
||||
//
|
||||
// TODO(fxbug.dev/82655): Remove this after migrating to RealmBuilder.
|
||||
environment_->ConnectToService<fuchsia::ui::lifecycle::LifecycleController>(
|
||||
scenic_lifecycle_controller_.NewRequest());
|
||||
|
||||
environment_->ConnectToService(scenic_.NewRequest());
|
||||
scenic_.set_error_handler([](zx_status_t status) {
|
||||
FAIL() << "Lost connection to Scenic: " << zx_status_get_string(status);
|
||||
});
|
||||
|
||||
// Post a "just in case" quit task, if the test hangs.
|
||||
async::PostDelayedTask(
|
||||
dispatcher(),
|
||||
[] {
|
||||
FML_LOG(FATAL)
|
||||
<< "\n\n>> Test did not complete in time, terminating. <<\n\n";
|
||||
},
|
||||
kTestTimeout);
|
||||
}
|
||||
|
||||
// |testing::Test|
|
||||
void TearDown() override {
|
||||
// Avoid spurious errors since we are about to kill scenic.
|
||||
//
|
||||
// TODO(fxbug.dev/82655): Remove this after migrating to RealmBuilder.
|
||||
scenic_.set_error_handler(nullptr);
|
||||
|
||||
zx_status_t terminate_status = scenic_lifecycle_controller_->Terminate();
|
||||
FML_CHECK(terminate_status == ZX_OK)
|
||||
<< "Failed to terminate Scenic with status: "
|
||||
<< zx_status_get_string(terminate_status);
|
||||
}
|
||||
|
||||
sys::testing::EnclosingEnvironment* environment() {
|
||||
return environment_.get();
|
||||
}
|
||||
|
||||
fuchsia::ui::views::ViewToken CreatePresentationViewToken() {
|
||||
auto [view_token, view_holder_token] = scenic::ViewTokenPair::New();
|
||||
|
||||
auto presenter =
|
||||
environment()->ConnectToService<fuchsia::ui::policy::Presenter>();
|
||||
presenter.set_error_handler([](zx_status_t status) {
|
||||
FAIL() << "presenter: " << zx_status_get_string(status);
|
||||
});
|
||||
presenter->PresentView(std::move(view_holder_token), nullptr);
|
||||
|
||||
return std::move(view_token);
|
||||
}
|
||||
|
||||
void RunAppWithArgs(const std::string& component_url,
|
||||
const std::vector<std::string>& component_args = {}) {
|
||||
scenic::EmbeddedViewInfo flutter_runner =
|
||||
scenic::LaunchComponentAndCreateView(environment()->launcher_ptr(),
|
||||
component_url, component_args);
|
||||
flutter_runner.controller.events().OnTerminated = [](auto...) { FAIL(); };
|
||||
|
||||
// Present the view.
|
||||
embedder_view_.emplace(scenic::ViewContext{
|
||||
.session_and_listener_request =
|
||||
scenic::CreateScenicSessionPtrAndListenerRequest(scenic_.get()),
|
||||
.view_token = CreatePresentationViewToken(),
|
||||
});
|
||||
|
||||
// Embed the view.
|
||||
bool is_rendering = false;
|
||||
embedder_view_->EmbedView(
|
||||
std::move(flutter_runner),
|
||||
[&is_rendering](fuchsia::ui::gfx::ViewState view_state) {
|
||||
is_rendering = view_state.is_rendering;
|
||||
});
|
||||
RunLoopUntil([&is_rendering] { return is_rendering; });
|
||||
FML_LOG(INFO) << "Launched component: " << component_url;
|
||||
}
|
||||
|
||||
scenic::Screenshot TakeScreenshot() {
|
||||
FML_LOG(INFO) << "Taking screenshot... ";
|
||||
fuchsia::ui::scenic::ScreenshotData screenshot_out;
|
||||
scenic_->TakeScreenshot(
|
||||
[this, &screenshot_out](fuchsia::ui::scenic::ScreenshotData screenshot,
|
||||
bool status) {
|
||||
EXPECT_TRUE(status) << "Failed to take screenshot";
|
||||
screenshot_out = std::move(screenshot);
|
||||
QuitLoop();
|
||||
});
|
||||
EXPECT_FALSE(RunLoopWithTimeout(kScreenshotTimeout))
|
||||
<< "Timed out waiting for screenshot.";
|
||||
FML_LOG(INFO) << "Screenshot captured.";
|
||||
|
||||
return scenic::Screenshot(screenshot_out);
|
||||
}
|
||||
|
||||
bool TakeScreenshotUntil(
|
||||
scenic::Color color,
|
||||
fit::function<void(scenic::Screenshot, std::map<scenic::Color, size_t>)>
|
||||
callback = nullptr,
|
||||
zx::duration timeout = kTestTimeout) {
|
||||
return RunLoopWithTimeoutOrUntil(
|
||||
[this, &callback, &color] {
|
||||
auto screenshot = TakeScreenshot();
|
||||
auto histogram = screenshot.Histogram();
|
||||
|
||||
bool color_found = histogram[color] > 0;
|
||||
if (color_found && callback != nullptr) {
|
||||
callback(std::move(screenshot), std::move(histogram));
|
||||
}
|
||||
return color_found;
|
||||
},
|
||||
timeout);
|
||||
}
|
||||
|
||||
// Inject directly into Root Presenter, using fuchsia.ui.input FIDLs.
|
||||
void InjectInput() {
|
||||
using fuchsia::ui::input::InputReport;
|
||||
// Device parameters
|
||||
auto parameters = fuchsia::ui::input::TouchscreenDescriptor::New();
|
||||
*parameters = {.x = {.range = {.min = -1000, .max = 1000}},
|
||||
.y = {.range = {.min = -1000, .max = 1000}},
|
||||
.max_finger_id = 10};
|
||||
|
||||
FML_LOG(INFO) << "Injecting input... ";
|
||||
// Register it against Root Presenter.
|
||||
fuchsia::ui::input::DeviceDescriptor device{.touchscreen =
|
||||
std::move(parameters)};
|
||||
auto registry =
|
||||
environment()
|
||||
->ConnectToService<fuchsia::ui::input::InputDeviceRegistry>();
|
||||
fuchsia::ui::input::InputDevicePtr connection;
|
||||
registry->RegisterDevice(std::move(device), connection.NewRequest());
|
||||
|
||||
{
|
||||
// Inject one input report, then a conclusion (empty) report.
|
||||
auto touch = fuchsia::ui::input::TouchscreenReport::New();
|
||||
*touch = {
|
||||
.touches = {{.finger_id = 1, .x = 0, .y = 0}}}; // center of display
|
||||
InputReport report{
|
||||
.event_time = static_cast<uint64_t>(zx::clock::get_monotonic().get()),
|
||||
.touchscreen = std::move(touch)};
|
||||
connection->DispatchReport(std::move(report));
|
||||
}
|
||||
|
||||
{
|
||||
auto touch = fuchsia::ui::input::TouchscreenReport::New();
|
||||
InputReport report{
|
||||
.event_time = static_cast<uint64_t>(zx::clock::get_monotonic().get()),
|
||||
.touchscreen = std::move(touch)};
|
||||
connection->DispatchReport(std::move(report));
|
||||
}
|
||||
FML_LOG(INFO) << "Input dispatched.";
|
||||
}
|
||||
|
||||
private:
|
||||
const std::unique_ptr<sys::ComponentContext> component_context_;
|
||||
std::unique_ptr<sys::testing::EnclosingEnvironment> environment_;
|
||||
|
||||
fuchsia::ui::lifecycle::LifecycleControllerSyncPtr
|
||||
scenic_lifecycle_controller_;
|
||||
fuchsia::ui::scenic::ScenicPtr scenic_;
|
||||
|
||||
// Wrapped in optional since the view is not created until the middle of SetUp
|
||||
std::optional<scenic::EmbedderView> embedder_view_;
|
||||
};
|
||||
|
||||
} // namespace flutter_embedder_test2
|
||||
|
||||
#endif // SRC_UI_TESTS_INTEGRATION_FLUTTER_TESTS_EMBEDDER_flutter_embedder_test2_H_
|
||||
@ -1,25 +0,0 @@
|
||||
{
|
||||
"facets": {
|
||||
"fuchsia.test": {
|
||||
"system-services": [
|
||||
"fuchsia.scheduler.ProfileProvider",
|
||||
"fuchsia.sysmem.Allocator",
|
||||
"fuchsia.vulkan.loader.Loader"
|
||||
]
|
||||
}
|
||||
},
|
||||
"program": {
|
||||
"binary": "bin/app"
|
||||
},
|
||||
"sandbox": {
|
||||
"services": [
|
||||
"fuchsia.logger.LogSink",
|
||||
"fuchsia.sys.Environment",
|
||||
"fuchsia.sys.Launcher",
|
||||
"fuchsia.sys.Loader",
|
||||
"fuchsia.sysmem.Allocator",
|
||||
"fuchsia.tracing.provider.Registry",
|
||||
"fuchsia.vulkan.loader.Loader"
|
||||
]
|
||||
}
|
||||
}
|
||||
@ -1,46 +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.
|
||||
|
||||
import("//build/fuchsia/sdk.gni")
|
||||
import("//flutter/tools/fuchsia/dart/dart_library.gni")
|
||||
import("//flutter/tools/fuchsia/flutter/flutter_component.gni")
|
||||
import("//flutter/tools/fuchsia/gn-sdk/package.gni")
|
||||
|
||||
dart_library("parent-view2_dart_library") {
|
||||
package_name = "parent-view2"
|
||||
|
||||
source_dir = "."
|
||||
sources = [ "parent_view2.dart" ]
|
||||
|
||||
deps = [
|
||||
"//flutter/shell/platform/fuchsia/dart:args",
|
||||
"//flutter/shell/platform/fuchsia/dart:vector_math",
|
||||
"//flutter/tools/fuchsia/dart:fuchsia_services",
|
||||
"//flutter/tools/fuchsia/dart:zircon",
|
||||
"//flutter/tools/fuchsia/fidl:fuchsia.sys",
|
||||
"//flutter/tools/fuchsia/fidl:fuchsia.ui.app",
|
||||
"//flutter/tools/fuchsia/fidl:fuchsia.ui.views",
|
||||
]
|
||||
}
|
||||
|
||||
flutter_component("parent-view2_flutter_component") {
|
||||
main_package = "parent-view2"
|
||||
component_name = "parent-view2"
|
||||
main_dart = "parent_view2.dart"
|
||||
manifest = rebase_path("meta/parent-view2.cmx")
|
||||
deps = [ ":parent-view2_dart_library" ]
|
||||
}
|
||||
|
||||
# TODO(richkadel): The target name is set differently compared to fuchsia.git's flutter_app().
|
||||
# Unlike in fuchsia.git's version of fuchsia_component, the Fuchsia GN SDK
|
||||
# version passes the component name to fuchsia_component via it's target_name only.
|
||||
# GN SDK's fuchsia_component doesn't have a `component_name` argument! So I'm forced to set
|
||||
# the component name via "target_name". This is a problem in fuchsia_package, which uses
|
||||
# the target_name to name the fuchsia_pm_tool target, creating duplicate target IDs!
|
||||
# So I have to change the fuchsia_package name to something that is NOT the component name,
|
||||
# and then set the package_name (which fuchsia_package does support).
|
||||
fuchsia_package("package") {
|
||||
package_name = "parent-view2"
|
||||
deps = [ ":parent-view2_flutter_component" ]
|
||||
}
|
||||
@ -1,14 +0,0 @@
|
||||
{
|
||||
"program": {
|
||||
"data": "data/parent-view2"
|
||||
},
|
||||
"sandbox": {
|
||||
"services": [
|
||||
"fuchsia.fonts.Provider",
|
||||
"fuchsia.sys.Environment",
|
||||
"fuchsia.sys.Launcher",
|
||||
"fuchsia.ui.input.ImeService",
|
||||
"fuchsia.ui.scenic.Scenic"
|
||||
]
|
||||
}
|
||||
}
|
||||
@ -1,30 +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.
|
||||
|
||||
import("//build/fuchsia/sdk.gni")
|
||||
|
||||
source_set("base_view") {
|
||||
sources = [
|
||||
"base_view.cc",
|
||||
"base_view.h",
|
||||
"embedded_view_utils.cc",
|
||||
"embedded_view_utils.h",
|
||||
"math.h",
|
||||
]
|
||||
|
||||
include_dirs = [ "//flutter/shell/platform/fuchsia/flutter/integration_flutter_tests/fuchsia_testing" ]
|
||||
|
||||
public_deps = [
|
||||
"$fuchsia_sdk_root/fidl:fuchsia.sys",
|
||||
"$fuchsia_sdk_root/fidl:fuchsia.ui.app",
|
||||
"$fuchsia_sdk_root/fidl:fuchsia.ui.gfx",
|
||||
"$fuchsia_sdk_root/fidl:fuchsia.ui.input",
|
||||
"$fuchsia_sdk_root/fidl:fuchsia.ui.views",
|
||||
"$fuchsia_sdk_root/pkg:scenic_cpp",
|
||||
"$fuchsia_sdk_root/pkg:sys_cpp",
|
||||
"//flutter/fml",
|
||||
]
|
||||
|
||||
deps = [ "$fuchsia_sdk_root/pkg:trace" ]
|
||||
}
|
||||
@ -1,240 +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.
|
||||
|
||||
#include "src/lib/ui/base_view/base_view.h"
|
||||
|
||||
#include <lib/trace/event.h>
|
||||
#include <lib/ui/scenic/cpp/commands.h>
|
||||
#include <lib/ui/scenic/cpp/view_token_pair.h>
|
||||
#include <zircon/status.h>
|
||||
#include "flutter/fml/logging.h"
|
||||
|
||||
namespace scenic {
|
||||
|
||||
BaseView::BaseView(ViewContext context, const std::string& debug_name)
|
||||
: component_context_(context.component_context),
|
||||
listener_binding_(this,
|
||||
std::move(context.session_and_listener_request.second)),
|
||||
session_(std::move(context.session_and_listener_request.first)),
|
||||
root_node_(&session_),
|
||||
ime_client_(this),
|
||||
enable_ime_(context.enable_ime) {
|
||||
if (!context.view_ref_pair) {
|
||||
context.view_ref_pair = scenic::ViewRefPair::New();
|
||||
}
|
||||
view_.emplace(&session_, std::move(context.view_token),
|
||||
std::move(context.view_ref_pair->control_ref),
|
||||
std::move(context.view_ref_pair->view_ref), debug_name);
|
||||
FML_DCHECK(view_);
|
||||
|
||||
session_.SetDebugName(debug_name);
|
||||
|
||||
// Listen for metrics events on our top node.
|
||||
root_node_.SetEventMask(fuchsia::ui::gfx::kMetricsEventMask);
|
||||
view_->AddChild(root_node_);
|
||||
|
||||
if (enable_ime_) {
|
||||
ime_manager_ =
|
||||
component_context_->svc()->Connect<fuchsia::ui::input::ImeService>();
|
||||
|
||||
ime_.set_error_handler([](zx_status_t status) {
|
||||
FML_LOG(ERROR) << "Interface error on: Input Method Editor "
|
||||
<< zx_status_get_string(status);
|
||||
});
|
||||
ime_manager_.set_error_handler([](zx_status_t status) {
|
||||
FML_LOG(ERROR) << "Interface error on: Text Sync Service "
|
||||
<< zx_status_get_string(status);
|
||||
});
|
||||
}
|
||||
|
||||
// We must immediately invalidate the scene, otherwise we wouldn't ever hook
|
||||
// the View up to the ViewHolder. An alternative would be to require
|
||||
// subclasses to call an Init() method to set up the initial connection.
|
||||
InvalidateScene();
|
||||
}
|
||||
|
||||
void BaseView::SetReleaseHandler(fit::function<void(zx_status_t)> callback) {
|
||||
listener_binding_.set_error_handler(std::move(callback));
|
||||
}
|
||||
|
||||
void BaseView::InvalidateScene(PresentCallback present_callback) {
|
||||
TRACE_DURATION("view", "BaseView::InvalidateScene");
|
||||
if (present_callback) {
|
||||
callbacks_for_next_present_.push_back(std::move(present_callback));
|
||||
}
|
||||
if (invalidate_pending_)
|
||||
return;
|
||||
|
||||
invalidate_pending_ = true;
|
||||
|
||||
// Present the scene ASAP. Pass in the last presentation time; otherwise, if
|
||||
// presentation_time argument is less than the previous time passed to
|
||||
// PresentScene, the Session will be closed.
|
||||
// (We cannot use the current time because the last requested presentation
|
||||
// time, |last_presentation_time_|, could still be in the future. This is
|
||||
// because Session.Present() returns after it _begins_ preparing the given
|
||||
// frame, not after it is presented.)
|
||||
if (!present_pending_)
|
||||
PresentScene(last_presentation_time_);
|
||||
}
|
||||
|
||||
void BaseView::PresentScene() {
|
||||
PresentScene(last_presentation_time_);
|
||||
}
|
||||
|
||||
void BaseView::OnScenicEvent(std::vector<fuchsia::ui::scenic::Event> events) {
|
||||
TRACE_DURATION("view", "BaseView::OnScenicEvent");
|
||||
for (auto& event : events) {
|
||||
switch (event.Which()) {
|
||||
case ::fuchsia::ui::scenic::Event::Tag::kGfx:
|
||||
switch (event.gfx().Which()) {
|
||||
case ::fuchsia::ui::gfx::Event::Tag::kViewPropertiesChanged: {
|
||||
auto& evt = event.gfx().view_properties_changed();
|
||||
FML_DCHECK(view_->id() == evt.view_id);
|
||||
auto old_props = view_properties_;
|
||||
view_properties_ = evt.properties;
|
||||
|
||||
::fuchsia::ui::gfx::BoundingBox layout_box =
|
||||
ViewPropertiesLayoutBox(view_properties_);
|
||||
|
||||
logical_size_ = scenic::Max(layout_box.max - layout_box.min, 0.f);
|
||||
physical_size_.x = logical_size_.x * metrics_.scale_x;
|
||||
physical_size_.y = logical_size_.y * metrics_.scale_y;
|
||||
physical_size_.z = logical_size_.z * metrics_.scale_z;
|
||||
|
||||
OnPropertiesChanged(std::move(old_props));
|
||||
InvalidateScene();
|
||||
break;
|
||||
}
|
||||
case fuchsia::ui::gfx::Event::Tag::kMetrics: {
|
||||
auto& evt = event.gfx().metrics();
|
||||
if (evt.node_id == root_node_.id()) {
|
||||
auto old_metrics = metrics_;
|
||||
metrics_ = std::move(evt.metrics);
|
||||
physical_size_.x = logical_size_.x * metrics_.scale_x;
|
||||
physical_size_.y = logical_size_.y * metrics_.scale_y;
|
||||
physical_size_.z = logical_size_.z * metrics_.scale_z;
|
||||
OnMetricsChanged(std::move(old_metrics));
|
||||
InvalidateScene();
|
||||
}
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
OnScenicEvent(std::move(event));
|
||||
}
|
||||
}
|
||||
break;
|
||||
case ::fuchsia::ui::scenic::Event::Tag::kInput: {
|
||||
if (event.input().Which() ==
|
||||
fuchsia::ui::input::InputEvent::Tag::kFocus &&
|
||||
enable_ime_) {
|
||||
OnHandleFocusEvent(event.input().focus());
|
||||
}
|
||||
OnInputEvent(std::move(event.input()));
|
||||
break;
|
||||
}
|
||||
case ::fuchsia::ui::scenic::Event::Tag::kUnhandled: {
|
||||
OnUnhandledCommand(std::move(event.unhandled()));
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
OnScenicEvent(std::move(event));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void BaseView::PresentScene(zx_time_t presentation_time) {
|
||||
TRACE_DURATION("view", "BaseView::PresentScene");
|
||||
// TODO(fxbug.dev/24406): Remove this when BaseView::PresentScene() is
|
||||
// deprecated, see fxbug.dev/24573.
|
||||
if (present_pending_)
|
||||
return;
|
||||
|
||||
present_pending_ = true;
|
||||
|
||||
// Keep track of the most recent presentation time we've passed to
|
||||
// Session.Present(), for use in InvalidateScene().
|
||||
last_presentation_time_ = presentation_time;
|
||||
|
||||
TRACE_FLOW_BEGIN("gfx", "Session::Present", session_present_count_);
|
||||
++session_present_count_;
|
||||
|
||||
session()->Present(
|
||||
presentation_time,
|
||||
[this, present_callbacks = std::move(callbacks_for_next_present_)](
|
||||
fuchsia::images::PresentationInfo info) mutable {
|
||||
TRACE_DURATION("view", "BaseView::PresentationCallback");
|
||||
TRACE_FLOW_END("gfx", "present_callback", info.presentation_time);
|
||||
|
||||
FML_DCHECK(present_pending_);
|
||||
|
||||
zx_time_t next_presentation_time =
|
||||
info.presentation_time + info.presentation_interval;
|
||||
|
||||
bool present_needed = false;
|
||||
if (invalidate_pending_) {
|
||||
invalidate_pending_ = false;
|
||||
OnSceneInvalidated(std::move(info));
|
||||
present_needed = true;
|
||||
}
|
||||
|
||||
for (auto& callback : present_callbacks) {
|
||||
callback(info);
|
||||
}
|
||||
|
||||
present_pending_ = false;
|
||||
if (present_needed)
|
||||
PresentScene(next_presentation_time);
|
||||
});
|
||||
callbacks_for_next_present_.clear();
|
||||
}
|
||||
|
||||
// |fuchsia::ui::input::InputMethodEditorClient|
|
||||
void BaseView::DidUpdateState(
|
||||
fuchsia::ui::input::TextInputState state,
|
||||
std::unique_ptr<fuchsia::ui::input::InputEvent> input_event) {
|
||||
if (input_event) {
|
||||
const fuchsia::ui::input::InputEvent& input = *input_event;
|
||||
fuchsia::ui::input::InputEvent input_event_copy;
|
||||
fidl::Clone(input, &input_event_copy);
|
||||
OnInputEvent(std::move(input_event_copy));
|
||||
}
|
||||
}
|
||||
|
||||
// |fuchsia::ui::input::InputMethodEditorClient|
|
||||
void BaseView::OnAction(fuchsia::ui::input::InputMethodAction action) {}
|
||||
|
||||
bool BaseView::OnHandleFocusEvent(const fuchsia::ui::input::FocusEvent& focus) {
|
||||
if (focus.focused) {
|
||||
ActivateIme();
|
||||
return true;
|
||||
} else if (!focus.focused) {
|
||||
DeactivateIme();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void BaseView::ActivateIme() {
|
||||
ime_manager_->GetInputMethodEditor(
|
||||
fuchsia::ui::input::KeyboardType::TEXT, // keyboard type
|
||||
fuchsia::ui::input::InputMethodAction::DONE, // input method action
|
||||
fuchsia::ui::input::TextInputState{}, // initial state
|
||||
ime_client_.NewBinding(), // client
|
||||
ime_.NewRequest() // editor
|
||||
);
|
||||
}
|
||||
|
||||
void BaseView::DeactivateIme() {
|
||||
if (ime_) {
|
||||
ime_manager_->HideKeyboard();
|
||||
ime_ = nullptr;
|
||||
}
|
||||
if (ime_client_.is_bound()) {
|
||||
ime_client_.Unbind();
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace scenic
|
||||
@ -1,210 +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.
|
||||
|
||||
#ifndef SRC_LIB_UI_BASE_VIEW_BASE_VIEW_H_
|
||||
#define SRC_LIB_UI_BASE_VIEW_BASE_VIEW_H_
|
||||
|
||||
#include <fuchsia/ui/gfx/cpp/fidl.h>
|
||||
#include <fuchsia/ui/input/cpp/fidl.h>
|
||||
#include <fuchsia/ui/scenic/cpp/fidl.h>
|
||||
#include <lib/sys/cpp/component_context.h>
|
||||
#include <lib/ui/scenic/cpp/resources.h>
|
||||
#include <lib/ui/scenic/cpp/session.h>
|
||||
#include <lib/ui/scenic/cpp/view_ref_pair.h>
|
||||
|
||||
#include "src/lib/ui/base_view/embedded_view_utils.h"
|
||||
#include "src/lib/ui/base_view/math.h"
|
||||
|
||||
namespace scenic {
|
||||
|
||||
// Parameters for creating a BaseView.
|
||||
struct ViewContext {
|
||||
scenic::SessionPtrAndListenerRequest session_and_listener_request;
|
||||
fuchsia::ui::views::ViewToken view_token;
|
||||
std::optional<ViewRefPair> view_ref_pair;
|
||||
sys::ComponentContext* component_context;
|
||||
bool enable_ime = false;
|
||||
};
|
||||
|
||||
// Abstract base implementation of a view for simple applications.
|
||||
// Subclasses must handle layout and provide content for the scene by
|
||||
// overriding the virtual methods defined in this class.
|
||||
//
|
||||
// It is not necessary to use this class to implement all Views.
|
||||
// This class is merely intended to make the simple apps easier to write.
|
||||
class BaseView : private fuchsia::ui::scenic::SessionListener,
|
||||
private fuchsia::ui::input::InputMethodEditorClient {
|
||||
public:
|
||||
using PresentCallback =
|
||||
fit::function<void(const fuchsia::images::PresentationInfo& info)>;
|
||||
|
||||
// Subclasses are typically created by ViewProviderService::CreateView(),
|
||||
// which provides the necessary args to pass down to this base class.
|
||||
BaseView(ViewContext context, const std::string& debug_name);
|
||||
|
||||
BaseView(const BaseView&) = delete;
|
||||
|
||||
// |root_node| is the node directly under our View; i.e. it's the top-most
|
||||
// node within the tree under our View. Use it to attach any resources for
|
||||
// your UI.
|
||||
scenic::EntityNode& root_node() { return root_node_; }
|
||||
Session* session() { return &session_; }
|
||||
sys::ComponentContext* component_context() { return component_context_; }
|
||||
|
||||
fuchsia::ui::gfx::ViewProperties view_properties() const {
|
||||
return view_properties_;
|
||||
}
|
||||
|
||||
// Returns true if the view has a non-empty size in logical pixels.
|
||||
bool has_logical_size() const {
|
||||
auto& sz = logical_size();
|
||||
return sz.x > 0.f && sz.y > 0.f && sz.z > 0.f;
|
||||
}
|
||||
|
||||
// Gets the size of the view in logical pixels.
|
||||
// This value is zero until the view receives a layout from its parent.
|
||||
const fuchsia::ui::gfx::vec3& logical_size() const { return logical_size_; }
|
||||
|
||||
// Returns true if the view has a non-empty size in physical pixels.
|
||||
bool has_physical_size() const {
|
||||
auto& sz = physical_size();
|
||||
return sz.x > 0.f && sz.y > 0.f && sz.z > 0.f;
|
||||
}
|
||||
|
||||
// Gets the size of the view in physical pixels.
|
||||
// This value is zero until the view receives a layout from its parent
|
||||
// and metrics from its session.
|
||||
const fuchsia::ui::gfx::vec3& physical_size() const { return physical_size_; }
|
||||
|
||||
// Returns true if the view has received metrics from its session.
|
||||
bool has_metrics() const {
|
||||
return metrics_.scale_x > 0.f && metrics_.scale_y > 0.f &&
|
||||
metrics_.scale_z > 0.f;
|
||||
}
|
||||
|
||||
// Gets the view's metrics.
|
||||
// This value is zero until the view receives metrics from its session.
|
||||
const fuchsia::ui::gfx::Metrics& metrics() const { return metrics_; }
|
||||
|
||||
// Sets a callback which is invoked when the view's owner releases the
|
||||
// view causing the view manager to unregister it.
|
||||
//
|
||||
// This should be used to implement cleanup policies to release resources
|
||||
// associated with the view (including the object itself).
|
||||
void SetReleaseHandler(fit::function<void(zx_status_t)> callback);
|
||||
|
||||
// Invalidates the scene, causing |OnSceneInvalidated()| to be invoked
|
||||
// during the next frame. When the Present() callback corresponding to this
|
||||
// invalidate is invoked, the optional |present_callback| will also be
|
||||
// invoked.
|
||||
void InvalidateScene(PresentCallback present_callback = nullptr);
|
||||
|
||||
// Called when it's time for the view to update its scene contents due to
|
||||
// invalidation. The new contents are presented once this function returns.
|
||||
//
|
||||
// The default implementation does nothing.
|
||||
virtual void OnSceneInvalidated(
|
||||
fuchsia::images::PresentationInfo presentation_info) {}
|
||||
|
||||
// Called when the view's properties have changed.
|
||||
//
|
||||
// The subclass should compare the old and new properties and make note of
|
||||
// whether these property changes will affect the layout or content of
|
||||
// the view then update accordingly.
|
||||
//
|
||||
// The default implementation does nothing.
|
||||
virtual void OnPropertiesChanged(
|
||||
fuchsia::ui::gfx::ViewProperties old_properties) {}
|
||||
|
||||
// Called when the view's metrics have changed.
|
||||
//
|
||||
// The subclass should compare the old and new metrics and make note of
|
||||
// whether this change will affect the layout or content of the view then
|
||||
// update accordingly.
|
||||
//
|
||||
// The default implementation does nothing.
|
||||
virtual void OnMetricsChanged(fuchsia::ui::gfx::Metrics old_metrics){};
|
||||
|
||||
// Called to handle an input event.
|
||||
//
|
||||
// The default implementation does nothing.
|
||||
virtual void OnInputEvent(fuchsia::ui::input::InputEvent event) {}
|
||||
|
||||
// Called when a command sent by the client was not handled by Scenic.
|
||||
//
|
||||
// The default implementation does nothing.
|
||||
virtual void OnUnhandledCommand(fuchsia::ui::scenic::Command unhandled) {}
|
||||
|
||||
// Called when an event that is not handled directly by BaseView is received.
|
||||
// For example, BaseView handles fuchsia::ui::gfx::ViewPropertiesChangedEvent,
|
||||
// and notifies the subclass via OnPropertiesChanged(); not all events are
|
||||
// handled in this way.
|
||||
//
|
||||
// The default implementation does nothing.
|
||||
virtual void OnScenicEvent(fuchsia::ui::scenic::Event) {}
|
||||
|
||||
protected:
|
||||
// An alternative way to update the scene. Provide a faster way to cause a
|
||||
// present in comparison to InvalidateScene(). Caller should update the
|
||||
// scene contents before calling this method.
|
||||
void PresentScene();
|
||||
|
||||
private:
|
||||
// |scenic::SessionListener|
|
||||
//
|
||||
// Iterates over the received events and either handles them in a sensible way
|
||||
// (e.g. fuchsia::ui::gfx::ViewPropertiesChangedEvent is handled by invoking
|
||||
// the virtual method OnPropertiesChanged()), or delegates handling to the
|
||||
// subclass via the single-event version of OnEvent() above.
|
||||
//
|
||||
// Subclasses should not override this.
|
||||
void OnScenicEvent(std::vector<fuchsia::ui::scenic::Event> events) override;
|
||||
|
||||
// |fuchsia::ui::input::InputMethodEditorClient|
|
||||
void DidUpdateState(
|
||||
fuchsia::ui::input::TextInputState state,
|
||||
std::unique_ptr<fuchsia::ui::input::InputEvent> event) override;
|
||||
|
||||
// |fuchsia::ui::input::InputMethodEditorClient|
|
||||
void OnAction(fuchsia::ui::input::InputMethodAction action) override;
|
||||
|
||||
void PresentScene(zx_time_t presentation_time);
|
||||
|
||||
// Handles focus event when IME is enabled. This event is used to activate
|
||||
// or deactivate the IME client.
|
||||
bool OnHandleFocusEvent(const fuchsia::ui::input::FocusEvent& focus);
|
||||
|
||||
// Gets a new input method editor from the IME manager.
|
||||
void ActivateIme();
|
||||
|
||||
// Detaches the input method editor connection, ending the edit session and
|
||||
// closing the onscreen keyboard.
|
||||
void DeactivateIme();
|
||||
|
||||
sys::ComponentContext* const component_context_;
|
||||
fidl::Binding<fuchsia::ui::scenic::SessionListener> listener_binding_;
|
||||
Session session_;
|
||||
std::optional<scenic::View> view_;
|
||||
scenic::EntityNode root_node_;
|
||||
|
||||
fidl::Binding<fuchsia::ui::input::InputMethodEditorClient> ime_client_;
|
||||
fuchsia::ui::input::InputMethodEditorPtr ime_;
|
||||
fuchsia::ui::input::ImeServicePtr ime_manager_;
|
||||
|
||||
fuchsia::ui::gfx::vec3 logical_size_;
|
||||
fuchsia::ui::gfx::vec3 physical_size_;
|
||||
fuchsia::ui::gfx::ViewProperties view_properties_;
|
||||
fuchsia::ui::gfx::Metrics metrics_;
|
||||
|
||||
zx_time_t last_presentation_time_ = 0;
|
||||
size_t session_present_count_ = 0;
|
||||
bool invalidate_pending_ = false;
|
||||
std::vector<PresentCallback> callbacks_for_next_present_;
|
||||
bool present_pending_ = false;
|
||||
bool enable_ime_ = false;
|
||||
};
|
||||
|
||||
} // namespace scenic
|
||||
|
||||
#endif // SRC_LIB_UI_BASE_VIEW_BASE_VIEW_H_
|
||||
@ -1,48 +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.
|
||||
|
||||
#include "src/lib/ui/base_view/embedded_view_utils.h"
|
||||
|
||||
#include <lib/ui/scenic/cpp/view_ref_pair.h>
|
||||
#include <lib/ui/scenic/cpp/view_token_pair.h>
|
||||
#include "flutter/fml/logging.h"
|
||||
|
||||
namespace scenic {
|
||||
|
||||
EmbeddedViewInfo LaunchComponentAndCreateView(
|
||||
const fuchsia::sys::LauncherPtr& launcher,
|
||||
const std::string& component_url,
|
||||
const std::vector<std::string>& component_args) {
|
||||
FML_DCHECK(launcher);
|
||||
|
||||
EmbeddedViewInfo info;
|
||||
|
||||
// Configure the information to launch the component with.
|
||||
fuchsia::sys::LaunchInfo launch_info;
|
||||
info.app_services =
|
||||
sys::ServiceDirectory::CreateWithRequest(&launch_info.directory_request);
|
||||
launch_info.url = component_url;
|
||||
launch_info.arguments = fidl::VectorPtr(
|
||||
std::vector<std::string>(component_args.begin(), component_args.end()));
|
||||
|
||||
launcher->CreateComponent(std::move(launch_info),
|
||||
info.controller.NewRequest());
|
||||
|
||||
info.view_provider =
|
||||
info.app_services->Connect<fuchsia::ui::app::ViewProvider>();
|
||||
|
||||
auto [view_token, view_holder_token] = scenic::ViewTokenPair::New();
|
||||
info.view_holder_token = std::move(view_holder_token);
|
||||
|
||||
auto [view_ref_control, view_ref] = scenic::ViewRefPair::New();
|
||||
fidl::Clone(view_ref, &info.view_ref);
|
||||
|
||||
info.view_provider->CreateViewWithViewRef(std::move(view_token.value),
|
||||
std::move(view_ref_control),
|
||||
std::move(view_ref));
|
||||
|
||||
return info;
|
||||
}
|
||||
|
||||
} // namespace scenic
|
||||
@ -1,58 +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.
|
||||
|
||||
#ifndef SRC_LIB_UI_BASE_VIEW_EMBEDDED_VIEW_UTILS_H_
|
||||
#define SRC_LIB_UI_BASE_VIEW_EMBEDDED_VIEW_UTILS_H_
|
||||
|
||||
#include <fuchsia/ui/app/cpp/fidl.h>
|
||||
#include <fuchsia/ui/gfx/cpp/fidl.h>
|
||||
#include <fuchsia/ui/input/cpp/fidl.h>
|
||||
#include <fuchsia/ui/scenic/cpp/fidl.h>
|
||||
#include <fuchsia/ui/views/cpp/fidl.h>
|
||||
#include <lib/sys/cpp/service_directory.h>
|
||||
#include <lib/ui/scenic/cpp/resources.h>
|
||||
#include <lib/ui/scenic/cpp/session.h>
|
||||
|
||||
namespace scenic {
|
||||
|
||||
// Serves as the return value for LaunchAppAndCreateView(), below.
|
||||
struct EmbeddedViewInfo {
|
||||
// Controls the launched app. The app will be destroyed if this connection
|
||||
// is closed.
|
||||
fuchsia::sys::ComponentControllerPtr controller;
|
||||
|
||||
// Services provided by the launched app. Must not be destroyed
|
||||
// immediately, otherwise the |view_provider| connection may not be
|
||||
// established.
|
||||
std::shared_ptr<sys::ServiceDirectory> app_services;
|
||||
|
||||
// ViewProvider service obtained from the app via |app_services|. Must not
|
||||
// be destroyed immediately, otherwise the call to CreateView() might not be
|
||||
// processed.
|
||||
fuchsia::ui::app::ViewProviderPtr view_provider;
|
||||
|
||||
// A token that can be used to create a ViewHolder; the corresponding token
|
||||
// was provided to |view_provider| via ViewProvider.CreateView(). The
|
||||
// launched app is expected to create a View, which will be connected to the
|
||||
// ViewHolder created with this token.
|
||||
fuchsia::ui::views::ViewHolderToken view_holder_token;
|
||||
|
||||
// The ViewRef of the embedded View.
|
||||
fuchsia::ui::views::ViewRef view_ref;
|
||||
};
|
||||
|
||||
// Launch a component and connect to its ViewProvider service, passing it the
|
||||
// necessary information to attach itself as a child view2. Populates the
|
||||
// returned EmbeddedViewInfo, which the caller can use to embed the child.
|
||||
// For example, an interface to a ViewProvider is obtained, a pair of
|
||||
// zx::eventpairs is created, CreateView is called, etc. This encapsulates
|
||||
// the boilerplate the client would otherwise write themselves.
|
||||
EmbeddedViewInfo LaunchComponentAndCreateView(
|
||||
const fuchsia::sys::LauncherPtr& launcher,
|
||||
const std::string& component_url,
|
||||
const std::vector<std::string>& component_args = {});
|
||||
|
||||
} // namespace scenic
|
||||
|
||||
#endif // SRC_LIB_UI_BASE_VIEW_EMBEDDED_VIEW_UTILS_H_
|
||||
@ -1,92 +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.
|
||||
|
||||
#ifndef SRC_LIB_UI_BASE_VIEW_MATH_H_
|
||||
#define SRC_LIB_UI_BASE_VIEW_MATH_H_
|
||||
|
||||
#include <fuchsia/ui/gfx/cpp/fidl.h>
|
||||
|
||||
namespace scenic {
|
||||
|
||||
// Return a vec3 consisting of the component-wise sum of the two arguments.
|
||||
inline fuchsia::ui::gfx::vec3 operator+(const fuchsia::ui::gfx::vec3& a,
|
||||
const fuchsia::ui::gfx::vec3& b) {
|
||||
return {.x = a.x + b.x, .y = a.y + b.y, .z = a.z + b.z};
|
||||
}
|
||||
|
||||
// Return a vec3 consisting of the component-wise difference of the two args.
|
||||
inline fuchsia::ui::gfx::vec3 operator-(const fuchsia::ui::gfx::vec3& a,
|
||||
const fuchsia::ui::gfx::vec3& b) {
|
||||
return {.x = a.x - b.x, .y = a.y - b.y, .z = a.z - b.z};
|
||||
}
|
||||
|
||||
// Return true if |point| is contained by |box|, including when it is on the
|
||||
// box boundary, and false otherwise.
|
||||
inline bool ContainsPoint(const fuchsia::ui::gfx::BoundingBox& box,
|
||||
const fuchsia::ui::gfx::vec3& point) {
|
||||
return point.x >= box.min.x && point.y >= box.min.y && point.z >= box.min.z &&
|
||||
point.x <= box.max.x && point.y <= box.max.y && point.z <= box.max.z;
|
||||
}
|
||||
|
||||
// Similar to fuchsia::ui::gfx::ViewProperties: adds the inset to box.min, and
|
||||
// subtracts it from box.max.
|
||||
inline fuchsia::ui::gfx::BoundingBox InsetBy(
|
||||
const fuchsia::ui::gfx::BoundingBox& box,
|
||||
const fuchsia::ui::gfx::vec3& inset) {
|
||||
return {.min = box.min + inset, .max = box.max - inset};
|
||||
}
|
||||
|
||||
// Similar to fuchsia::ui::gfx::ViewProperties: adds the inset to box.min, and
|
||||
// subtracts it from box.max.
|
||||
inline fuchsia::ui::gfx::BoundingBox InsetBy(
|
||||
const fuchsia::ui::gfx::BoundingBox& box,
|
||||
const fuchsia::ui::gfx::vec3& inset_from_min,
|
||||
const fuchsia::ui::gfx::vec3& inset_from_max) {
|
||||
return {.min = box.min + inset_from_min, .max = box.max - inset_from_max};
|
||||
}
|
||||
|
||||
// Inset the view properties' outer box by its insets.
|
||||
inline fuchsia::ui::gfx::BoundingBox ViewPropertiesLayoutBox(
|
||||
const fuchsia::ui::gfx::ViewProperties& view_properties) {
|
||||
return InsetBy(view_properties.bounding_box, view_properties.inset_from_min,
|
||||
view_properties.inset_from_max);
|
||||
}
|
||||
|
||||
// Return a vec3 consisting of the maximum x/y/z from the two arguments.
|
||||
inline fuchsia::ui::gfx::vec3 Max(const fuchsia::ui::gfx::vec3& a,
|
||||
const fuchsia::ui::gfx::vec3& b) {
|
||||
return {.x = std::max(a.x, b.x),
|
||||
.y = std::max(a.y, b.y),
|
||||
.z = std::max(a.z, b.z)};
|
||||
}
|
||||
|
||||
// Return a vec3 consisting of the maximum of the x/y/z components of |v|,
|
||||
// compared with |min_val|.
|
||||
inline fuchsia::ui::gfx::vec3 Max(const fuchsia::ui::gfx::vec3& v,
|
||||
float min_val) {
|
||||
return {.x = std::max(v.x, min_val),
|
||||
.y = std::max(v.y, min_val),
|
||||
.z = std::max(v.z, min_val)};
|
||||
}
|
||||
|
||||
// Return a vec3 consisting of the minimum x/y/z from the two arguments.
|
||||
inline fuchsia::ui::gfx::vec3 Min(const fuchsia::ui::gfx::vec3& a,
|
||||
const fuchsia::ui::gfx::vec3& b) {
|
||||
return {.x = std::min(a.x, b.x),
|
||||
.y = std::min(a.y, b.y),
|
||||
.z = std::min(a.z, b.z)};
|
||||
}
|
||||
|
||||
// Return a vec3 consisting of the minimum of the x/y/z components of |v|,
|
||||
// compared with |max_val|.
|
||||
inline fuchsia::ui::gfx::vec3 Min(const fuchsia::ui::gfx::vec3& v,
|
||||
float max_val) {
|
||||
return {.x = std::min(v.x, max_val),
|
||||
.y = std::min(v.y, max_val),
|
||||
.z = std::min(v.z, max_val)};
|
||||
}
|
||||
|
||||
} // namespace scenic
|
||||
|
||||
#endif // SRC_LIB_UI_BASE_VIEW_MATH_H_
|
||||
@ -1,24 +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.
|
||||
|
||||
import("//build/fuchsia/sdk.gni")
|
||||
|
||||
source_set("views") {
|
||||
testonly = true
|
||||
|
||||
sources = [
|
||||
"color.cc",
|
||||
"color.h",
|
||||
"embedder_view.cc",
|
||||
"embedder_view.h",
|
||||
]
|
||||
|
||||
include_dirs = [ "//flutter/shell/platform/fuchsia/flutter/integration_flutter_tests/fuchsia_testing" ]
|
||||
|
||||
public_deps = [
|
||||
"$fuchsia_sdk_root/pkg:scenic_cpp",
|
||||
"//flutter/fml",
|
||||
"//flutter/shell/platform/fuchsia/flutter/integration_flutter_tests/fuchsia_testing/src/lib/ui/base_view",
|
||||
]
|
||||
}
|
||||
@ -1,88 +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.
|
||||
|
||||
#include <zircon/status.h>
|
||||
|
||||
#include "flutter/fml/logging.h"
|
||||
#include "src/ui/testing/views/embedder_view.h"
|
||||
|
||||
namespace scenic {
|
||||
|
||||
EmbedderView::EmbedderView(ViewContext context, const std::string& debug_name)
|
||||
: binding_(this, std::move(context.session_and_listener_request.second)),
|
||||
session_(std::move(context.session_and_listener_request.first)),
|
||||
view_(&session_, std::move(context.view_token), debug_name),
|
||||
top_node_(&session_) {
|
||||
binding_.set_error_handler([](zx_status_t status) {
|
||||
FML_LOG(FATAL) << "Session listener binding: "
|
||||
<< zx_status_get_string(status);
|
||||
});
|
||||
view_.AddChild(top_node_);
|
||||
// Call |Session::Present| in order to flush events having to do with
|
||||
// creation of |view_| and |top_node_|.
|
||||
session_.Present(0, [](auto) {});
|
||||
}
|
||||
|
||||
// Sets the EmbeddedViewInfo and attaches the embedded View to the scene. Any
|
||||
// callbacks for the embedded View's ViewState are delivered to the supplied
|
||||
// callback.
|
||||
void EmbedderView::EmbedView(EmbeddedViewInfo info,
|
||||
std::function<void(fuchsia::ui::gfx::ViewState)>
|
||||
view_state_changed_callback) {
|
||||
// Only one EmbeddedView is currently supported.
|
||||
FML_CHECK(!embedded_view_);
|
||||
embedded_view_ = std::make_unique<EmbeddedView>(
|
||||
std::move(info), &session_, std::move(view_state_changed_callback));
|
||||
|
||||
// Attach the embedded view to the scene.
|
||||
top_node_.Attach(embedded_view_->view_holder);
|
||||
|
||||
// Call |Session::Present| to apply the embedded view to the scene graph.
|
||||
session_.Present(0, [](auto) {});
|
||||
}
|
||||
|
||||
void EmbedderView::OnScenicEvent(
|
||||
std::vector<fuchsia::ui::scenic::Event> events) {
|
||||
for (const auto& event : events) {
|
||||
if (event.Which() == fuchsia::ui::scenic::Event::Tag::kGfx &&
|
||||
event.gfx().Which() ==
|
||||
fuchsia::ui::gfx::Event::Tag::kViewPropertiesChanged) {
|
||||
const auto& evt = event.gfx().view_properties_changed();
|
||||
// Naively apply the parent's ViewProperties to any EmbeddedViews.
|
||||
if (embedded_view_) {
|
||||
embedded_view_->view_holder.SetViewProperties(
|
||||
std::move(evt.properties));
|
||||
session_.Present(0, [](auto) {});
|
||||
}
|
||||
} else if (event.Which() == fuchsia::ui::scenic::Event::Tag::kGfx &&
|
||||
event.gfx().Which() ==
|
||||
fuchsia::ui::gfx::Event::Tag::kViewStateChanged) {
|
||||
const auto& evt = event.gfx().view_state_changed();
|
||||
if (embedded_view_ &&
|
||||
evt.view_holder_id == embedded_view_->view_holder.id()) {
|
||||
// Clients of |EmbedderView| *must* set a view state changed
|
||||
// callback. Failure to do so is a usage error.
|
||||
FML_CHECK(embedded_view_->view_state_changed_callback);
|
||||
embedded_view_->view_state_changed_callback(evt.state);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void EmbedderView::OnScenicError(std::string error) {
|
||||
FML_LOG(FATAL) << "OnScenicError: " << error;
|
||||
}
|
||||
|
||||
EmbedderView::EmbeddedView::EmbeddedView(
|
||||
EmbeddedViewInfo info,
|
||||
Session* session,
|
||||
std::function<void(fuchsia::ui::gfx::ViewState)> view_state_callback,
|
||||
const std::string& debug_name)
|
||||
: embedded_info(std::move(info)),
|
||||
view_holder(session,
|
||||
std::move(embedded_info.view_holder_token),
|
||||
debug_name + " ViewHolder"),
|
||||
view_state_changed_callback(std::move(view_state_callback)) {}
|
||||
|
||||
} // namespace scenic
|
||||
@ -1,59 +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.
|
||||
|
||||
#ifndef SRC_UI_TESTING_VIEWS_EMBEDDER_VIEW_H_
|
||||
#define SRC_UI_TESTING_VIEWS_EMBEDDER_VIEW_H_
|
||||
|
||||
#include <fuchsia/ui/scenic/cpp/fidl.h>
|
||||
#include <lib/ui/scenic/cpp/resources.h>
|
||||
#include <lib/ui/scenic/cpp/session.h>
|
||||
|
||||
#include "src/lib/ui/base_view/base_view.h"
|
||||
|
||||
namespace scenic {
|
||||
|
||||
// This is a simplified |BaseView| that exposes view state events.
|
||||
//
|
||||
// See also lib/ui/base_view.
|
||||
class EmbedderView : public fuchsia::ui::scenic::SessionListener {
|
||||
public:
|
||||
EmbedderView(ViewContext context,
|
||||
const std::string& debug_name = "EmbedderView");
|
||||
|
||||
// Sets the EmbeddedViewInfo and attaches the embedded View to the scene. Any
|
||||
// callbacks for the embedded View's ViewState are delivered to the supplied
|
||||
// callback.
|
||||
void EmbedView(EmbeddedViewInfo info,
|
||||
std::function<void(fuchsia::ui::gfx::ViewState)>
|
||||
view_state_changed_callback);
|
||||
|
||||
private:
|
||||
// |fuchsia::ui::scenic::SessionListener|
|
||||
void OnScenicEvent(std::vector<fuchsia::ui::scenic::Event> events) override;
|
||||
// |fuchsia::ui::scenic::SessionListener|
|
||||
void OnScenicError(std::string error) override;
|
||||
|
||||
struct EmbeddedView {
|
||||
EmbeddedView(
|
||||
EmbeddedViewInfo info,
|
||||
Session* session,
|
||||
std::function<void(fuchsia::ui::gfx::ViewState)> view_state_callback,
|
||||
const std::string& debug_name = "EmbedderView");
|
||||
|
||||
EmbeddedViewInfo embedded_info;
|
||||
ViewHolder view_holder;
|
||||
std::function<void(fuchsia::ui::gfx::ViewState)>
|
||||
view_state_changed_callback;
|
||||
};
|
||||
|
||||
fidl::Binding<fuchsia::ui::scenic::SessionListener> binding_;
|
||||
Session session_;
|
||||
View view_;
|
||||
EntityNode top_node_;
|
||||
std::optional<fuchsia::ui::gfx::ViewProperties> embedded_view_properties_;
|
||||
std::unique_ptr<EmbeddedView> embedded_view_;
|
||||
};
|
||||
|
||||
} // namespace scenic
|
||||
#endif // SRC_UI_TESTING_VIEWS_EMBEDDER_VIEW_H_
|
||||
@ -2,7 +2,11 @@
|
||||
# Use of this source code is governed by a BSD-style license that can be
|
||||
# found in the LICENSE file.
|
||||
|
||||
group("integration_flutter_tests") {
|
||||
assert(is_fuchsia)
|
||||
|
||||
import("//build/fuchsia/sdk.gni")
|
||||
|
||||
group("integration") {
|
||||
testonly = true
|
||||
deps = [ "embedder:tests" ]
|
||||
}
|
||||
@ -0,0 +1,84 @@
|
||||
# `flutter integration tests`
|
||||
|
||||
## Configure and build fuchsia
|
||||
|
||||
```shell
|
||||
$ cd "$FUCHSIA_DIR"
|
||||
$ fx set terminal.x64
|
||||
$ fx build
|
||||
```
|
||||
|
||||
## Build the test
|
||||
|
||||
You can specify the test's package target to build only the test package, with
|
||||
its dependencies. This will also build the required runner.
|
||||
|
||||
```shell
|
||||
$ cd "$ENGINE_DIR/src"
|
||||
$ ./flutter/tools/gn --fuchsia <flags> \
|
||||
# for example: --goma --fuchsia-cpu=x64 --runtime-mode=debug
|
||||
$ ninja -C out/fuchsia_debug_x64 \
|
||||
flutter/shell/platform/fuchsia/flutter/tests/integration
|
||||
```
|
||||
|
||||
|
||||
## Start an emulator
|
||||
|
||||
```shell
|
||||
ffx emu start --net tap
|
||||
```
|
||||
|
||||
NOTE: Do _not_ run the default package server. The instructions below describe
|
||||
how to launch a flutter-specific package server.
|
||||
|
||||
## Publish the test packages to the Fuchsia package server
|
||||
|
||||
The tests currently specify the Fuchsia package server's standard domain,
|
||||
`fuchsia.com`, as the server to use to resolve (locate and load) the test
|
||||
packages. So, before running the test, the most recently built `.far` files
|
||||
need to be published to the Fuchsia package repo:
|
||||
|
||||
```shell
|
||||
$ fx pm publish -a -repo "$(cat $FUCHSIA_DIR/.fx-build-dir)/amber-files/" \
|
||||
-f "$FLUTTER_ENGINE_DIR"/src/out/fuchsia_*64/oot_flutter_jit_runner-0.far
|
||||
$ fx pm publish -a -repo "$(cat $FUCHSIA_DIR/.fx-build-dir)/amber-files/" \
|
||||
-f "$FLUTTER_ENGINE_DIR"/src/out/fuchsia_*64/flutter-embedder-test-0.far
|
||||
$ fx pm publish -a -repo "$(cat $FUCHSIA_DIR/.fx-build-dir)/amber-files/" \
|
||||
-f $(find "$FLUTTER_ENGINE_DIR"/src/out/fuchsia_*64 -name parent-view.far)
|
||||
$ fx pm publish -a -repo "$(cat $FUCHSIA_DIR/.fx-build-dir)/amber-files/" \
|
||||
-f $(find "$FLUTTER_ENGINE_DIR"/src/out/fuchsia_*64 -name child-view.far)
|
||||
```
|
||||
|
||||
## Run the test
|
||||
|
||||
```shell
|
||||
$ ffx test run fuchsia-pkg:://fuchsia.com/flutter-embedder-test#meta/flutter-embedder-test.cm
|
||||
```
|
||||
|
||||
If, for example, you only make a change to the Dart code in `parent-view`, you
|
||||
can rebuild only the parent-view package target, and republish it.
|
||||
|
||||
```shell
|
||||
$ ninja -C out/fuchsia_debug_x64 \
|
||||
flutter/shell/platform/fuchsia/flutter/tests/integration/embedder/parent-view:package
|
||||
$ fx pm publish -a -repo "$(cat $FUCHSIA_DIR/.fx-build-dir)/amber-files/" \
|
||||
-f $(find "$FLUTTER_ENGINE_DIR"/src/out/fuchsia_*64 -name parent-view.far)
|
||||
```
|
||||
|
||||
Then re-run the test as above.
|
||||
|
||||
The tests use a flutter runner with "oot_" prefixed to its package name, to
|
||||
avoid conflicting with any flutter_runner package in the base fuchsia image.
|
||||
After making a change to the flutter_runner you can re-deploy it with:
|
||||
|
||||
```shell
|
||||
$ ninja -C out/fuchsia_debug_x64 \
|
||||
flutter/shell/platform/fuchsia/flutter:oot_flutter_jit_runner
|
||||
$ fx pm publish -a -repo "$(cat $FUCHSIA_DIR/.fx-build-dir)/amber-files/" \
|
||||
-f $(find "$FLUTTER_ENGINE_DIR"/src/out/fuchsia_*64 -name oot_flutter_jit_runner.far)
|
||||
```
|
||||
|
||||
Then re-run the test as above.
|
||||
|
||||
From here, you can modify the Flutter test, rebuild flutter, and usually rerun
|
||||
the test without rebooting, by repeating the commands above.
|
||||
@ -0,0 +1,65 @@
|
||||
# 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.
|
||||
|
||||
assert(is_fuchsia)
|
||||
|
||||
import("//build/fuchsia/sdk.gni")
|
||||
import("//flutter/tools/fuchsia/fuchsia_archive.gni")
|
||||
|
||||
group("tests") {
|
||||
testonly = true
|
||||
deps = [ ":flutter-embedder-test" ]
|
||||
}
|
||||
|
||||
executable("flutter-embedder-test-bin") {
|
||||
testonly = true
|
||||
|
||||
output_name = "flutter-embedder-test"
|
||||
|
||||
sources = [
|
||||
"color.cc",
|
||||
"color.h",
|
||||
"flutter-embedder-test.cc",
|
||||
]
|
||||
|
||||
# This is needed for //third_party/googletest for linking zircon symbols.
|
||||
libs = [ "$fuchsia_sdk_path/arch/$target_cpu/sysroot/lib/libzircon.so" ]
|
||||
|
||||
deps = [
|
||||
"$fuchsia_sdk_root/fidl:fuchsia.logger",
|
||||
"$fuchsia_sdk_root/fidl:fuchsia.tracing.provider",
|
||||
"$fuchsia_sdk_root/fidl:fuchsia.ui.app",
|
||||
"$fuchsia_sdk_root/fidl:fuchsia.ui.composition",
|
||||
"$fuchsia_sdk_root/fidl:fuchsia.ui.observation.geometry",
|
||||
"$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",
|
||||
"$fuchsia_sdk_root/pkg:async-loop-testing",
|
||||
"$fuchsia_sdk_root/pkg:fidl_cpp",
|
||||
"$fuchsia_sdk_root/pkg:scenic_cpp",
|
||||
"$fuchsia_sdk_root/pkg:sys_component_cpp_testing",
|
||||
"$fuchsia_sdk_root/pkg:zx",
|
||||
"//flutter/fml",
|
||||
"//third_party/googletest:gtest",
|
||||
"//third_party/googletest:gtest_main",
|
||||
]
|
||||
}
|
||||
|
||||
fuchsia_test_archive("flutter-embedder-test") {
|
||||
deps = [
|
||||
":flutter-embedder-test-bin",
|
||||
"child-view:package",
|
||||
"parent-view:package",
|
||||
|
||||
# "OOT" copies of the runners used by tests, to avoid conflicting with the
|
||||
# runners in the base fuchsia image.
|
||||
# TODO(fxbug.dev/106575): Fix this with subpackages.
|
||||
"//flutter/shell/platform/fuchsia/flutter:oot_flutter_jit_runner",
|
||||
]
|
||||
|
||||
binary = "$target_name"
|
||||
|
||||
cml_file = rebase_path("meta/$target_name.cml")
|
||||
}
|
||||
@ -0,0 +1,26 @@
|
||||
# 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.
|
||||
|
||||
import("//build/fuchsia/sdk.gni")
|
||||
import("//flutter/tools/fuchsia/dart/dart_library.gni")
|
||||
import("//flutter/tools/fuchsia/flutter/flutter_component.gni")
|
||||
import("//flutter/tools/fuchsia/gn-sdk/package.gni")
|
||||
|
||||
dart_library("lib") {
|
||||
package_name = "child-view"
|
||||
sources = [ "child_view.dart" ]
|
||||
}
|
||||
|
||||
flutter_component("component") {
|
||||
main_package = "child-view"
|
||||
component_name = "child-view"
|
||||
main_dart = "child_view.dart"
|
||||
manifest = rebase_path("meta/child-view.cml")
|
||||
deps = [ ":lib" ]
|
||||
}
|
||||
|
||||
fuchsia_package("package") {
|
||||
package_name = "child-view"
|
||||
deps = [ ":component" ]
|
||||
}
|
||||
@ -4,10 +4,10 @@
|
||||
|
||||
import 'dart:ui';
|
||||
|
||||
TestApp app;
|
||||
|
||||
void main(List<String> args) {
|
||||
app = TestApp();
|
||||
print('child-view: starting');
|
||||
|
||||
TestApp app = TestApp();
|
||||
app.run();
|
||||
}
|
||||
|
||||
@ -19,11 +19,15 @@ class TestApp {
|
||||
|
||||
void run() {
|
||||
window.onPointerDataPacket = (PointerDataPacket packet) {
|
||||
app.pointerDataPacket(packet);
|
||||
this.pointerDataPacket(packet);
|
||||
};
|
||||
window.onMetricsChanged = () {
|
||||
window.scheduleFrame();
|
||||
};
|
||||
window.onBeginFrame = (Duration duration) {
|
||||
app.beginFrame(duration);
|
||||
this.beginFrame(duration);
|
||||
};
|
||||
|
||||
window.scheduleFrame();
|
||||
}
|
||||
|
||||
@ -46,7 +50,7 @@ class TestApp {
|
||||
|
||||
void pointerDataPacket(PointerDataPacket packet) {
|
||||
for (final data in packet.data) {
|
||||
if (data.change == PointerChange.up) {
|
||||
if (data.change == PointerChange.down) {
|
||||
this._backgroundColor = _yellow;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,21 @@
|
||||
{
|
||||
include: [ "syslog/client.shard.cml" ],
|
||||
program: {
|
||||
data: "data/child-view",
|
||||
|
||||
// Always use the jit runner for now.
|
||||
// TODO(fxbug.dev/106577): Implement manifest merging build rules for V2 components.
|
||||
runner: "flutter_jit_runner",
|
||||
},
|
||||
capabilities: [
|
||||
{
|
||||
protocol: [ "fuchsia.ui.app.ViewProvider" ],
|
||||
},
|
||||
],
|
||||
expose: [
|
||||
{
|
||||
protocol: [ "fuchsia.ui.app.ViewProvider" ],
|
||||
from: "self",
|
||||
},
|
||||
],
|
||||
}
|
||||
@ -2,10 +2,11 @@
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "color.h"
|
||||
|
||||
#include <zircon/status.h>
|
||||
|
||||
#include "flutter/fml/logging.h"
|
||||
#include "src/ui/testing/views/color.h"
|
||||
|
||||
namespace scenic {
|
||||
|
||||
@ -0,0 +1,602 @@
|
||||
// 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.
|
||||
|
||||
#include <fuchsia/logger/cpp/fidl.h>
|
||||
#include <fuchsia/tracing/provider/cpp/fidl.h>
|
||||
#include <fuchsia/ui/app/cpp/fidl.h>
|
||||
#include <fuchsia/ui/composition/cpp/fidl.h>
|
||||
#include <fuchsia/ui/observation/geometry/cpp/fidl.h>
|
||||
#include <fuchsia/ui/scenic/cpp/fidl.h>
|
||||
#include <fuchsia/ui/test/input/cpp/fidl.h>
|
||||
#include <fuchsia/ui/test/scene/cpp/fidl.h>
|
||||
#include <lib/async-loop/testing/cpp/real_loop.h>
|
||||
#include <lib/async/cpp/task.h>
|
||||
#include <lib/fidl/cpp/binding_set.h>
|
||||
#include <lib/sys/component/cpp/testing/realm_builder.h>
|
||||
#include <lib/sys/component/cpp/testing/realm_builder_types.h>
|
||||
#include <lib/ui/scenic/cpp/view_ref_pair.h>
|
||||
#include <lib/zx/clock.h>
|
||||
#include <zircon/status.h>
|
||||
#include <zircon/time.h>
|
||||
|
||||
#include <optional>
|
||||
#include <vector>
|
||||
|
||||
#include "flutter/fml/logging.h"
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
#include "color.h"
|
||||
|
||||
namespace flutter_embedder_test {
|
||||
namespace {
|
||||
|
||||
// Types imported for the realm_builder library.
|
||||
using component_testing::ChildOptions;
|
||||
using component_testing::ChildRef;
|
||||
using component_testing::DirectoryContents;
|
||||
using component_testing::ParentRef;
|
||||
using component_testing::Protocol;
|
||||
using component_testing::RealmRoot;
|
||||
using component_testing::Route;
|
||||
using component_testing::StartupMode;
|
||||
|
||||
// The FIDL bindings for this service are not exposed in the Fuchsia SDK, so we
|
||||
// must encode the name manually here.
|
||||
constexpr auto kVulkanLoaderServiceName = "fuchsia.vulkan.loader.Loader";
|
||||
|
||||
constexpr auto kFlutterJitRunnerUrl =
|
||||
"fuchsia-pkg://fuchsia.com/oot_flutter_jit_runner#meta/"
|
||||
"flutter_jit_runner.cm";
|
||||
constexpr auto kFlutterJitProductRunnerUrl =
|
||||
"fuchsia-pkg://fuchsia.com/oot_flutter_jit_product_runner#meta/"
|
||||
"flutter_jit_product_runner.cm";
|
||||
constexpr auto kFlutterAotRunnerUrl =
|
||||
"fuchsia-pkg://fuchsia.com/oot_flutter_aot_runner#meta/"
|
||||
"flutter_aot_runner.cm";
|
||||
constexpr auto kFlutterAotProductRunnerUrl =
|
||||
"fuchsia-pkg://fuchsia.com/oot_flutter_aot_product_runner#meta/"
|
||||
"flutter_aot_product_runner.cm";
|
||||
constexpr char kChildViewUrl[] =
|
||||
"fuchsia-pkg://fuchsia.com/child-view#meta/child-view.cm";
|
||||
constexpr char kParentViewUrl[] =
|
||||
"fuchsia-pkg://fuchsia.com/parent-view#meta/parent-view.cm";
|
||||
static constexpr auto kTestUIStackUrl =
|
||||
"fuchsia-pkg://fuchsia.com/test-ui-stack#meta/test-ui-stack.cm";
|
||||
|
||||
constexpr auto kFlutterRunnerEnvironment = "flutter_runner_env";
|
||||
constexpr auto kFlutterJitRunner = "flutter_jit_runner";
|
||||
constexpr auto kFlutterJitRunnerRef = ChildRef{kFlutterJitRunner};
|
||||
constexpr auto kFlutterJitProductRunner = "flutter_jit_product_runner";
|
||||
constexpr auto kFlutterJitProductRunnerRef = ChildRef{kFlutterJitProductRunner};
|
||||
constexpr auto kFlutterAotRunner = "flutter_aot_runner";
|
||||
constexpr auto kFlutterAotRunnerRef = ChildRef{kFlutterAotRunner};
|
||||
constexpr auto kFlutterAotProductRunner = "flutter_aot_product_runner";
|
||||
constexpr auto kFlutterAotProductRunnerRef = ChildRef{kFlutterAotProductRunner};
|
||||
constexpr auto kChildView = "child_view";
|
||||
constexpr auto kChildViewRef = ChildRef{kChildView};
|
||||
constexpr auto kParentView = "parent_view";
|
||||
constexpr auto kParentViewRef = ChildRef{kParentView};
|
||||
constexpr auto kTestUIStack = "ui";
|
||||
constexpr auto kTestUIStackRef = ChildRef{kTestUIStack};
|
||||
|
||||
constexpr scenic::Color kParentBackgroundColor = {0x00, 0x00, 0xFF,
|
||||
0xFF}; // Blue
|
||||
constexpr scenic::Color kParentTappedColor = {0x00, 0x00, 0x00, 0xFF}; // Black
|
||||
constexpr scenic::Color kChildBackgroundColor = {0xFF, 0x00, 0xFF,
|
||||
0xFF}; // Pink
|
||||
constexpr scenic::Color kChildTappedColor = {0xFF, 0xFF, 0x00, 0xFF}; // Yellow
|
||||
|
||||
// TODO(fxb/64201): Remove forced opacity colors when Flatland is enabled.
|
||||
constexpr scenic::Color kOverlayBackgroundColor1 = {
|
||||
0x00, 0xFF, 0x0E, 0xFF}; // Green, blended with blue (FEMU local)
|
||||
constexpr scenic::Color kOverlayBackgroundColor2 = {
|
||||
0x0E, 0xFF, 0x0E, 0xFF}; // Green, blended with pink (FEMU local)
|
||||
constexpr scenic::Color kOverlayBackgroundColor3 = {
|
||||
0x00, 0xFF, 0x0D, 0xFF}; // Green, blended with blue (AEMU infra)
|
||||
constexpr scenic::Color kOverlayBackgroundColor4 = {
|
||||
0x0D, 0xFF, 0x0D, 0xFF}; // Green, blended with pink (AEMU infra)
|
||||
constexpr scenic::Color kOverlayBackgroundColor5 = {
|
||||
0x00, 0xFE, 0x0D, 0xFF}; // Green, blended with blue (NUC)
|
||||
constexpr scenic::Color kOverlayBackgroundColor6 = {
|
||||
0x0D, 0xFF, 0x00, 0xFF}; // Green, blended with pink (NUC)
|
||||
|
||||
static size_t OverlayPixelCount(std::map<scenic::Color, size_t>& histogram) {
|
||||
return histogram[kOverlayBackgroundColor1] +
|
||||
histogram[kOverlayBackgroundColor2] +
|
||||
histogram[kOverlayBackgroundColor3] +
|
||||
histogram[kOverlayBackgroundColor4] +
|
||||
histogram[kOverlayBackgroundColor5] +
|
||||
histogram[kOverlayBackgroundColor6];
|
||||
}
|
||||
|
||||
// Timeout for Scenic's |TakeScreenshot| FIDL call.
|
||||
constexpr zx::duration kScreenshotTimeout = zx::sec(10);
|
||||
// Timeout to fail the test if it goes beyond this duration.
|
||||
constexpr zx::duration kTestTimeout = zx::min(1);
|
||||
|
||||
bool CheckViewExistsInSnapshot(
|
||||
const fuchsia::ui::observation::geometry::ViewTreeSnapshot& snapshot,
|
||||
zx_koid_t view_ref_koid) {
|
||||
if (!snapshot.has_views()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
auto snapshot_count =
|
||||
std::count_if(snapshot.views().begin(), snapshot.views().end(),
|
||||
[view_ref_koid](const auto& view) {
|
||||
return view.view_ref_koid() == view_ref_koid;
|
||||
});
|
||||
|
||||
return snapshot_count > 0;
|
||||
}
|
||||
|
||||
bool CheckViewExistsInUpdates(
|
||||
const std::vector<fuchsia::ui::observation::geometry::ViewTreeSnapshot>&
|
||||
updates,
|
||||
zx_koid_t view_ref_koid) {
|
||||
auto update_count = std::count_if(
|
||||
updates.begin(), updates.end(), [view_ref_koid](auto& snapshot) {
|
||||
return CheckViewExistsInSnapshot(snapshot, view_ref_koid);
|
||||
});
|
||||
|
||||
return update_count > 0;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
class FlutterEmbedderTest : public ::loop_fixture::RealLoop,
|
||||
public ::testing::Test {
|
||||
public:
|
||||
FlutterEmbedderTest()
|
||||
: realm_builder_(component_testing::RealmBuilder::Create()) {
|
||||
FML_VLOG(-1) << "Setting up base realm";
|
||||
SetUpRealmBase();
|
||||
|
||||
// Post a "just in case" quit task, if the test hangs.
|
||||
async::PostDelayedTask(
|
||||
dispatcher(),
|
||||
[] {
|
||||
FML_LOG(FATAL)
|
||||
<< "\n\n>> Test did not complete in time, terminating. <<\n\n";
|
||||
},
|
||||
kTestTimeout);
|
||||
}
|
||||
|
||||
bool HasViewConnected(
|
||||
const fuchsia::ui::observation::geometry::ViewTreeWatcherPtr&
|
||||
view_tree_watcher,
|
||||
std::optional<fuchsia::ui::observation::geometry::WatchResponse>&
|
||||
watch_response,
|
||||
zx_koid_t view_ref_koid);
|
||||
|
||||
void LaunchParentViewInRealm(
|
||||
const std::vector<std::string>& component_args = {});
|
||||
|
||||
scenic::Screenshot TakeScreenshot();
|
||||
|
||||
bool TakeScreenshotUntil(
|
||||
scenic::Color color,
|
||||
fit::function<void(std::map<scenic::Color, size_t>)> callback = nullptr,
|
||||
zx::duration timeout = kTestTimeout);
|
||||
|
||||
// Simulates a tap at location (x, y).
|
||||
void InjectTap(int32_t x, int32_t y);
|
||||
|
||||
// Injects an input event, and posts a task to retry after
|
||||
// `kTapRetryInterval`.
|
||||
//
|
||||
// We post the retry task because the first input event we send to Flutter may
|
||||
// be lost. The reason the first event may be lost is that there is a race
|
||||
// condition as the scene owner starts up.
|
||||
//
|
||||
// More specifically: in order for our app
|
||||
// to receive the injected input, two things must be true before we inject
|
||||
// touch input:
|
||||
// * The Scenic root view must have been installed, and
|
||||
// * The Input Pipeline must have received a viewport to inject touch into.
|
||||
//
|
||||
// The problem we have is that the `is_rendering` signal that we monitor only
|
||||
// guarantees us the view is ready. If the viewport is not ready in Input
|
||||
// Pipeline at that time, it will drop the touch event.
|
||||
//
|
||||
// TODO(fxbug.dev/96986): Improve synchronization and remove retry logic.
|
||||
void TryInject(int32_t x, int32_t y);
|
||||
|
||||
private:
|
||||
fuchsia::ui::scenic::Scenic* scenic() { return scenic_.get(); }
|
||||
|
||||
void SetUpRealmBase();
|
||||
|
||||
// Registers a fake touch screen device with an injection coordinate space
|
||||
// spanning [-1000, 1000] on both axes.
|
||||
void RegisterTouchScreen();
|
||||
|
||||
fuchsia::ui::scenic::ScenicPtr scenic_;
|
||||
fuchsia::ui::test::input::RegistryPtr input_registry_;
|
||||
fuchsia::ui::test::input::TouchScreenPtr fake_touchscreen_;
|
||||
fuchsia::ui::test::scene::ControllerPtr scene_provider_;
|
||||
fuchsia::ui::observation::geometry::ViewTreeWatcherPtr view_tree_watcher_;
|
||||
|
||||
// Wrapped in optional since the view is not created until the middle of SetUp
|
||||
component_testing::RealmBuilder realm_builder_;
|
||||
std::unique_ptr<component_testing::RealmRoot> realm_;
|
||||
|
||||
// The typical latency on devices we've tested is ~60 msec. The retry interval
|
||||
// is chosen to be a) Long enough that it's unlikely that we send a new tap
|
||||
// while a previous tap is still being
|
||||
// processed. That is, it should be far more likely that a new tap is sent
|
||||
// because the first tap was lost, than because the system is just running
|
||||
// slowly.
|
||||
// b) Short enough that we don't slow down tryjobs.
|
||||
//
|
||||
// The first property is important to avoid skewing the latency metrics that
|
||||
// we collect. For an explanation of why a tap might be lost, see the
|
||||
// documentation for TryInject().
|
||||
static constexpr auto kTapRetryInterval = zx::sec(1);
|
||||
};
|
||||
|
||||
void FlutterEmbedderTest::SetUpRealmBase() {
|
||||
FML_LOG(INFO) << "Setting up realm base.";
|
||||
|
||||
// First, add the flutter runner(s) as children.
|
||||
realm_builder_.AddChild(kFlutterJitRunner, kFlutterJitRunnerUrl);
|
||||
realm_builder_.AddChild(kFlutterJitProductRunner,
|
||||
kFlutterJitProductRunnerUrl);
|
||||
realm_builder_.AddChild(kFlutterAotRunner, kFlutterAotRunnerUrl);
|
||||
realm_builder_.AddChild(kFlutterAotProductRunner,
|
||||
kFlutterAotProductRunnerUrl);
|
||||
|
||||
// Then, add an environment providing them.
|
||||
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();
|
||||
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));
|
||||
fuchsia::component::decl::RunnerRegistration flutter_jit_product_runner_reg;
|
||||
flutter_jit_product_runner_reg.set_source(
|
||||
fuchsia::component::decl::Ref::WithChild(
|
||||
fuchsia::component::decl::ChildRef{.name =
|
||||
kFlutterJitProductRunner}));
|
||||
flutter_jit_product_runner_reg.set_source_name(kFlutterJitProductRunner);
|
||||
flutter_jit_product_runner_reg.set_target_name(kFlutterJitProductRunner);
|
||||
environment_runners->push_back(std::move(flutter_jit_product_runner_reg));
|
||||
fuchsia::component::decl::RunnerRegistration flutter_aot_runner_reg;
|
||||
flutter_aot_runner_reg.set_source(fuchsia::component::decl::Ref::WithChild(
|
||||
fuchsia::component::decl::ChildRef{.name = kFlutterAotRunner}));
|
||||
flutter_aot_runner_reg.set_source_name(kFlutterAotRunner);
|
||||
flutter_aot_runner_reg.set_target_name(kFlutterAotRunner);
|
||||
environment_runners->push_back(std::move(flutter_aot_runner_reg));
|
||||
fuchsia::component::decl::RunnerRegistration flutter_aot_product_runner_reg;
|
||||
flutter_aot_product_runner_reg.set_source(
|
||||
fuchsia::component::decl::Ref::WithChild(
|
||||
fuchsia::component::decl::ChildRef{.name =
|
||||
kFlutterAotProductRunner}));
|
||||
flutter_aot_product_runner_reg.set_source_name(kFlutterAotProductRunner);
|
||||
flutter_aot_product_runner_reg.set_target_name(kFlutterAotProductRunner);
|
||||
environment_runners->push_back(std::move(flutter_aot_product_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, kTestUIStackUrl);
|
||||
|
||||
// Add embedded parent and child components.
|
||||
realm_builder_.AddChild(kChildView, kChildViewUrl,
|
||||
ChildOptions{
|
||||
.environment = kFlutterRunnerEnvironment,
|
||||
});
|
||||
realm_builder_.AddChild(kParentView, kParentViewUrl,
|
||||
ChildOptions{
|
||||
.environment = kFlutterRunnerEnvironment,
|
||||
});
|
||||
|
||||
// Route base system services to flutter runners.
|
||||
realm_builder_.AddRoute(
|
||||
Route{.capabilities =
|
||||
{
|
||||
Protocol{fuchsia::logger::LogSink::Name_},
|
||||
Protocol{fuchsia::sysmem::Allocator::Name_},
|
||||
Protocol{fuchsia::tracing::provider::Registry::Name_},
|
||||
Protocol{kVulkanLoaderServiceName},
|
||||
},
|
||||
.source = ParentRef{},
|
||||
.targets = {kFlutterJitRunnerRef, kFlutterJitProductRunnerRef,
|
||||
kFlutterAotRunnerRef, kFlutterAotProductRunnerRef}});
|
||||
|
||||
// Route base system services to the test UI stack.
|
||||
realm_builder_.AddRoute(Route{
|
||||
.capabilities = {Protocol{fuchsia::logger::LogSink::Name_},
|
||||
Protocol{fuchsia::sysmem::Allocator::Name_},
|
||||
Protocol{fuchsia::tracing::provider::Registry::Name_},
|
||||
Protocol{kVulkanLoaderServiceName}},
|
||||
.source = ParentRef{},
|
||||
.targets = {kTestUIStackRef}});
|
||||
|
||||
// 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 = {kFlutterJitRunnerRef, kFlutterJitProductRunnerRef,
|
||||
kFlutterAotRunnerRef, kFlutterAotProductRunnerRef}});
|
||||
|
||||
// Route test capabilities from test UI stack 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::scenic::Scenic::Name_}},
|
||||
.source = kTestUIStackRef,
|
||||
.targets = {ParentRef{}}});
|
||||
|
||||
// Route ViewProvider from child to parent, and parent to test.
|
||||
realm_builder_.AddRoute(
|
||||
Route{.capabilities = {Protocol{fuchsia::ui::app::ViewProvider::Name_}},
|
||||
.source = kParentViewRef,
|
||||
.targets = {ParentRef()}});
|
||||
realm_builder_.AddRoute(
|
||||
Route{.capabilities = {Protocol{fuchsia::ui::app::ViewProvider::Name_}},
|
||||
.source = kChildViewRef,
|
||||
.targets = {kParentViewRef}});
|
||||
}
|
||||
|
||||
// Checks whether the view with |view_ref_koid| has connected to the view tree.
|
||||
// The response of a f.u.o.g.Provider.Watch call is stored in |watch_response|
|
||||
// if it contains |view_ref_koid|.
|
||||
bool FlutterEmbedderTest::HasViewConnected(
|
||||
const fuchsia::ui::observation::geometry::ViewTreeWatcherPtr&
|
||||
view_tree_watcher,
|
||||
std::optional<fuchsia::ui::observation::geometry::WatchResponse>&
|
||||
watch_response,
|
||||
zx_koid_t view_ref_koid) {
|
||||
std::optional<fuchsia::ui::observation::geometry::WatchResponse> watch_result;
|
||||
view_tree_watcher->Watch(
|
||||
[&watch_result](auto response) { watch_result = std::move(response); });
|
||||
FML_LOG(INFO) << "Waiting for view tree watch result";
|
||||
RunLoopUntil([&watch_result] { return watch_result.has_value(); });
|
||||
FML_LOG(INFO) << "Received for view tree watch result";
|
||||
if (CheckViewExistsInUpdates(watch_result->updates(), view_ref_koid)) {
|
||||
watch_response = std::move(watch_result);
|
||||
};
|
||||
return watch_response.has_value();
|
||||
}
|
||||
|
||||
void FlutterEmbedderTest::LaunchParentViewInRealm(
|
||||
const std::vector<std::string>& component_args) {
|
||||
FML_LOG(INFO) << "Launching parent-view";
|
||||
|
||||
if (!component_args.empty()) {
|
||||
// Construct a args.csv file containing the specified comma-separated
|
||||
// component args.
|
||||
std::string csv;
|
||||
for (const auto& arg : component_args) {
|
||||
csv += arg + ',';
|
||||
}
|
||||
// Remove last comma.
|
||||
csv.pop_back();
|
||||
|
||||
auto config_directory_contents = DirectoryContents();
|
||||
config_directory_contents.AddFile("args.csv", csv);
|
||||
realm_builder_.RouteReadOnlyDirectory("config-data", {kParentViewRef},
|
||||
std::move(config_directory_contents));
|
||||
}
|
||||
realm_ = std::make_unique<RealmRoot>(realm_builder_.Build());
|
||||
|
||||
// Register fake touch screen device.
|
||||
RegisterTouchScreen();
|
||||
|
||||
// Instruct Test UI Stack to present parent-view's View.
|
||||
std::optional<zx_koid_t> view_ref_koid;
|
||||
scene_provider_ = realm_->Connect<fuchsia::ui::test::scene::Controller>();
|
||||
scene_provider_.set_error_handler(
|
||||
[](auto) { FML_LOG(ERROR) << "Error from test scene provider"; });
|
||||
fuchsia::ui::test::scene::ControllerAttachClientViewRequest request;
|
||||
request.set_view_provider(realm_->Connect<fuchsia::ui::app::ViewProvider>());
|
||||
scene_provider_->RegisterViewTreeWatcher(view_tree_watcher_.NewRequest(),
|
||||
[]() {});
|
||||
scene_provider_->AttachClientView(
|
||||
std::move(request), [&view_ref_koid](auto client_view_ref_koid) {
|
||||
view_ref_koid = client_view_ref_koid;
|
||||
});
|
||||
|
||||
FML_LOG(INFO) << "Waiting for client view ref koid";
|
||||
RunLoopUntil([&view_ref_koid] { return view_ref_koid.has_value(); });
|
||||
|
||||
// Wait for the client view to get attached to the view tree.
|
||||
std::optional<fuchsia::ui::observation::geometry::WatchResponse>
|
||||
watch_response;
|
||||
FML_LOG(INFO) << "Waiting for client view to render; koid is "
|
||||
<< (view_ref_koid.has_value() ? view_ref_koid.value() : 0);
|
||||
RunLoopUntil([this, &watch_response, &view_ref_koid] {
|
||||
return HasViewConnected(view_tree_watcher_, watch_response, *view_ref_koid);
|
||||
});
|
||||
FML_LOG(INFO) << "Client view has rendered";
|
||||
|
||||
scenic_ = realm_->Connect<fuchsia::ui::scenic::Scenic>();
|
||||
FML_LOG(INFO) << "Launched parent-view";
|
||||
}
|
||||
|
||||
scenic::Screenshot FlutterEmbedderTest::TakeScreenshot() {
|
||||
FML_LOG(INFO) << "Taking screenshot... ";
|
||||
fuchsia::ui::scenic::ScreenshotData screenshot_out;
|
||||
scenic_->TakeScreenshot(
|
||||
[this, &screenshot_out](fuchsia::ui::scenic::ScreenshotData screenshot,
|
||||
bool status) {
|
||||
EXPECT_TRUE(status) << "Failed to take screenshot";
|
||||
screenshot_out = std::move(screenshot);
|
||||
QuitLoop();
|
||||
});
|
||||
EXPECT_FALSE(RunLoopWithTimeout(kScreenshotTimeout))
|
||||
<< "Timed out waiting for screenshot.";
|
||||
FML_LOG(INFO) << "Screenshot captured.";
|
||||
|
||||
return scenic::Screenshot(screenshot_out);
|
||||
}
|
||||
|
||||
bool FlutterEmbedderTest::TakeScreenshotUntil(
|
||||
scenic::Color color,
|
||||
fit::function<void(std::map<scenic::Color, size_t>)> callback,
|
||||
zx::duration timeout) {
|
||||
return RunLoopWithTimeoutOrUntil(
|
||||
[this, &callback, &color] {
|
||||
auto screenshot = TakeScreenshot();
|
||||
auto histogram = screenshot.Histogram();
|
||||
|
||||
bool color_found = histogram[color] > 0;
|
||||
if (color_found && callback != nullptr) {
|
||||
callback(std::move(histogram));
|
||||
}
|
||||
return color_found;
|
||||
},
|
||||
timeout);
|
||||
}
|
||||
|
||||
void FlutterEmbedderTest::RegisterTouchScreen() {
|
||||
FML_LOG(INFO) << "Registering fake touch screen";
|
||||
input_registry_ = realm_->Connect<fuchsia::ui::test::input::Registry>();
|
||||
input_registry_.set_error_handler(
|
||||
[](auto) { FML_LOG(ERROR) << "Error from input helper"; });
|
||||
bool touchscreen_registered = false;
|
||||
fuchsia::ui::test::input::RegistryRegisterTouchScreenRequest request;
|
||||
request.set_device(fake_touchscreen_.NewRequest());
|
||||
input_registry_->RegisterTouchScreen(
|
||||
std::move(request),
|
||||
[&touchscreen_registered]() { touchscreen_registered = true; });
|
||||
RunLoopUntil([&touchscreen_registered] { return touchscreen_registered; });
|
||||
FML_LOG(INFO) << "Touchscreen registered";
|
||||
}
|
||||
|
||||
void FlutterEmbedderTest::InjectTap(int32_t x, int32_t y) {
|
||||
fuchsia::ui::test::input::TouchScreenSimulateTapRequest tap_request;
|
||||
tap_request.mutable_tap_location()->x = x;
|
||||
tap_request.mutable_tap_location()->y = y;
|
||||
fake_touchscreen_->SimulateTap(std::move(tap_request), [x, y]() {
|
||||
FML_LOG(INFO) << "Tap injected at (" << x << ", " << y << ")";
|
||||
});
|
||||
}
|
||||
|
||||
void FlutterEmbedderTest::TryInject(int32_t x, int32_t y) {
|
||||
InjectTap(x, y);
|
||||
async::PostDelayedTask(
|
||||
dispatcher(), [this, x, y] { TryInject(x, y); }, kTapRetryInterval);
|
||||
}
|
||||
|
||||
TEST_F(FlutterEmbedderTest, Embedding) {
|
||||
LaunchParentViewInRealm();
|
||||
|
||||
// Take screenshot until we see the child-view's embedded color.
|
||||
ASSERT_TRUE(TakeScreenshotUntil(
|
||||
kChildBackgroundColor, [](std::map<scenic::Color, size_t> histogram) {
|
||||
// Expect parent and child background colors, with parent color > child
|
||||
// color.
|
||||
EXPECT_GT(histogram[kParentBackgroundColor], 0u);
|
||||
EXPECT_GT(histogram[kChildBackgroundColor], 0u);
|
||||
EXPECT_GT(histogram[kParentBackgroundColor],
|
||||
histogram[kChildBackgroundColor]);
|
||||
}));
|
||||
}
|
||||
|
||||
TEST_F(FlutterEmbedderTest, HittestEmbedding) {
|
||||
LaunchParentViewInRealm();
|
||||
|
||||
// Take screenshot until we see the child-view's embedded color.
|
||||
ASSERT_TRUE(TakeScreenshotUntil(kChildBackgroundColor));
|
||||
|
||||
// Simulate a tap at the center of the child view.
|
||||
TryInject(/* x = */ 0, /* y = */ 0);
|
||||
|
||||
// Take screenshot until we see the child-view's tapped color.
|
||||
ASSERT_TRUE(TakeScreenshotUntil(
|
||||
kChildTappedColor, [](std::map<scenic::Color, size_t> histogram) {
|
||||
// Expect parent and child background colors, with parent color > child
|
||||
// color.
|
||||
EXPECT_GT(histogram[kParentBackgroundColor], 0u);
|
||||
EXPECT_EQ(histogram[kChildBackgroundColor], 0u);
|
||||
EXPECT_GT(histogram[kChildTappedColor], 0u);
|
||||
EXPECT_GT(histogram[kParentBackgroundColor],
|
||||
histogram[kChildTappedColor]);
|
||||
}));
|
||||
}
|
||||
|
||||
TEST_F(FlutterEmbedderTest, HittestDisabledEmbedding) {
|
||||
LaunchParentViewInRealm({"--no-hitTestable"});
|
||||
|
||||
// Take screenshots until we see the child-view's embedded color.
|
||||
ASSERT_TRUE(TakeScreenshotUntil(kChildBackgroundColor));
|
||||
|
||||
// Simulate a tap at the center of the child view.
|
||||
TryInject(/* x = */ 0, /* y = */ 0);
|
||||
|
||||
// The parent-view should change color.
|
||||
ASSERT_TRUE(TakeScreenshotUntil(
|
||||
kParentTappedColor, [](std::map<scenic::Color, size_t> histogram) {
|
||||
// Expect parent and child background colors, with parent color > child
|
||||
// color.
|
||||
EXPECT_EQ(histogram[kParentBackgroundColor], 0u);
|
||||
EXPECT_GT(histogram[kParentTappedColor], 0u);
|
||||
EXPECT_GT(histogram[kChildBackgroundColor], 0u);
|
||||
EXPECT_EQ(histogram[kChildTappedColor], 0u);
|
||||
EXPECT_GT(histogram[kParentTappedColor],
|
||||
histogram[kChildBackgroundColor]);
|
||||
}));
|
||||
}
|
||||
|
||||
TEST_F(FlutterEmbedderTest, EmbeddingWithOverlay) {
|
||||
LaunchParentViewInRealm({"--showOverlay"});
|
||||
|
||||
// Take screenshot until we see the child-view's embedded color.
|
||||
ASSERT_TRUE(TakeScreenshotUntil(
|
||||
kChildBackgroundColor, [](std::map<scenic::Color, size_t> histogram) {
|
||||
// Expect parent, overlay and child background colors.
|
||||
// With parent color > child color and overlay color > child color.
|
||||
const size_t overlay_pixel_count = OverlayPixelCount(histogram);
|
||||
EXPECT_GT(histogram[kParentBackgroundColor], 0u);
|
||||
EXPECT_GT(overlay_pixel_count, 0u);
|
||||
EXPECT_GT(histogram[kChildBackgroundColor], 0u);
|
||||
EXPECT_GT(histogram[kParentBackgroundColor],
|
||||
histogram[kChildBackgroundColor]);
|
||||
EXPECT_GT(overlay_pixel_count, histogram[kChildBackgroundColor]);
|
||||
}));
|
||||
}
|
||||
|
||||
TEST_F(FlutterEmbedderTest, HittestEmbeddingWithOverlay) {
|
||||
LaunchParentViewInRealm({"--showOverlay"});
|
||||
|
||||
// Take screenshot until we see the child-view's embedded color.
|
||||
ASSERT_TRUE(TakeScreenshotUntil(kChildBackgroundColor));
|
||||
|
||||
// The bottom-left corner of the overlay is at the center of the screen,
|
||||
// which is at (0, 0) in the injection coordinate space. Inject a pointer
|
||||
// event just outside the overlay's bounds, and ensure that it goes to the
|
||||
// embedded view.
|
||||
TryInject(/* x = */ -1, /* y = */ 1);
|
||||
|
||||
// Take screenshot until we see the child-view's tapped color.
|
||||
ASSERT_TRUE(TakeScreenshotUntil(
|
||||
kChildTappedColor, [](std::map<scenic::Color, size_t> histogram) {
|
||||
// Expect parent, overlay and child background colors.
|
||||
// With parent color > child color and overlay color > child color.
|
||||
const size_t overlay_pixel_count = OverlayPixelCount(histogram);
|
||||
EXPECT_GT(histogram[kParentBackgroundColor], 0u);
|
||||
EXPECT_GT(overlay_pixel_count, 0u);
|
||||
EXPECT_EQ(histogram[kChildBackgroundColor], 0u);
|
||||
EXPECT_GT(histogram[kChildTappedColor], 0u);
|
||||
EXPECT_GT(histogram[kParentBackgroundColor],
|
||||
histogram[kChildTappedColor]);
|
||||
EXPECT_GT(overlay_pixel_count, histogram[kChildTappedColor]);
|
||||
}));
|
||||
}
|
||||
|
||||
} // namespace flutter_embedder_test
|
||||
@ -0,0 +1,27 @@
|
||||
{
|
||||
include: [
|
||||
"gtest_runner.shard.cml",
|
||||
"sys/component/realm_builder_absolute.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",
|
||||
],
|
||||
program: {
|
||||
binary: "bin/app",
|
||||
},
|
||||
offer: [
|
||||
{
|
||||
// Offer capabilities needed by components in this test realm.
|
||||
// Keep it minimal, describe only what's actually needed.
|
||||
protocol: [
|
||||
"fuchsia.logger.LogSink",
|
||||
"fuchsia.sysmem.Allocator",
|
||||
"fuchsia.tracing.provider.Registry",
|
||||
"fuchsia.vulkan.loader.Loader",
|
||||
],
|
||||
from: "parent",
|
||||
to: "#realm_builder",
|
||||
},
|
||||
],
|
||||
}
|
||||
@ -0,0 +1,14 @@
|
||||
{
|
||||
program: {
|
||||
runner: "gtest_runner",
|
||||
},
|
||||
capabilities: [
|
||||
{ protocol: "fuchsia.test.Suite" },
|
||||
],
|
||||
expose: [
|
||||
{
|
||||
protocol: "fuchsia.test.Suite",
|
||||
from: "self",
|
||||
},
|
||||
],
|
||||
}
|
||||
@ -0,0 +1,36 @@
|
||||
# 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.
|
||||
|
||||
import("//build/fuchsia/sdk.gni")
|
||||
import("//flutter/tools/fuchsia/dart/dart_library.gni")
|
||||
import("//flutter/tools/fuchsia/flutter/flutter_component.gni")
|
||||
import("//flutter/tools/fuchsia/gn-sdk/package.gni")
|
||||
|
||||
dart_library("lib") {
|
||||
package_name = "parent-view"
|
||||
sources = [ "parent_view.dart" ]
|
||||
|
||||
deps = [
|
||||
"//flutter/shell/platform/fuchsia/dart:args",
|
||||
"//flutter/shell/platform/fuchsia/dart:vector_math",
|
||||
"//flutter/tools/fuchsia/dart:fuchsia_services",
|
||||
"//flutter/tools/fuchsia/dart:zircon",
|
||||
"//flutter/tools/fuchsia/fidl:fuchsia.sys",
|
||||
"//flutter/tools/fuchsia/fidl:fuchsia.ui.app",
|
||||
"//flutter/tools/fuchsia/fidl:fuchsia.ui.views",
|
||||
]
|
||||
}
|
||||
|
||||
flutter_component("component") {
|
||||
main_package = "parent-view"
|
||||
component_name = "parent-view"
|
||||
main_dart = "parent_view.dart"
|
||||
manifest = rebase_path("meta/parent-view.cml")
|
||||
deps = [ ":lib" ]
|
||||
}
|
||||
|
||||
fuchsia_package("package") {
|
||||
package_name = "parent-view"
|
||||
deps = [ ":component" ]
|
||||
}
|
||||
@ -4,42 +4,49 @@
|
||||
|
||||
import 'dart:convert';
|
||||
import 'dart:typed_data';
|
||||
import 'dart:io';
|
||||
import 'dart:ui';
|
||||
import 'package:vector_math/vector_math_64.dart' as vector_math_64;
|
||||
|
||||
import 'package:args/args.dart';
|
||||
import 'package:fidl_fuchsia_sys/fidl_async.dart';
|
||||
import 'package:fidl_fuchsia_ui_app/fidl_async.dart';
|
||||
import 'package:fidl_fuchsia_ui_views/fidl_async.dart';
|
||||
import 'package:fuchsia_services/services.dart';
|
||||
import 'package:vector_math/vector_math_64.dart' as vector_math_64;
|
||||
import 'package:zircon/zircon.dart';
|
||||
|
||||
// TODO(richkadel): To run the test serving the runner and test packages from
|
||||
// the flutter/engine package server (via
|
||||
// `//flutter/tools/fuchsia/devshell/serve.sh`), change `fuchsia.com` to
|
||||
// `engine`.
|
||||
const _kChildAppUrl =
|
||||
'fuchsia-pkg://fuchsia.com/child-view2#meta/child-view2.cmx';
|
||||
|
||||
TestApp app;
|
||||
final _argsCsvFilePath = '/config/data/args.csv';
|
||||
|
||||
void main(List<String> args) {
|
||||
print('parent-view: starting');
|
||||
|
||||
args = args + _GetArgsFromConfigFile();
|
||||
final parser = ArgParser()
|
||||
..addFlag('showOverlay', defaultsTo: false)
|
||||
..addFlag('hitTestable', defaultsTo: true)
|
||||
..addFlag('focusable', defaultsTo: true);
|
||||
..addFlag('focusable', defaultsTo: true)
|
||||
..addFlag('useFlatland', defaultsTo: false);
|
||||
final arguments = parser.parse(args);
|
||||
for (final option in arguments.options) {
|
||||
print('parent-view2: $option: ${arguments[option]}');
|
||||
print('parent-view: $option: ${arguments[option]}');
|
||||
}
|
||||
|
||||
final childViewToken = _launchApp(_kChildAppUrl);
|
||||
|
||||
app = TestApp(
|
||||
ChildView(childViewToken),
|
||||
showOverlay: arguments['showOverlay'],
|
||||
hitTestable: arguments['hitTestable'],
|
||||
focusable: arguments['focusable'],
|
||||
);
|
||||
TestApp app;
|
||||
final useFlatland = arguments['useFlatland'];
|
||||
if (useFlatland) {
|
||||
app = TestApp(
|
||||
ChildView(_launchFlatlandChildView()),
|
||||
showOverlay: arguments['showOverlay'],
|
||||
hitTestable: arguments['hitTestable'],
|
||||
focusable: arguments['focusable'],
|
||||
);
|
||||
} else {
|
||||
app = TestApp(
|
||||
ChildView.gfx(_launchGfxChildView()),
|
||||
showOverlay: arguments['showOverlay'],
|
||||
hitTestable: arguments['hitTestable'],
|
||||
focusable: arguments['focusable'],
|
||||
);
|
||||
}
|
||||
|
||||
app.run();
|
||||
}
|
||||
@ -64,19 +71,24 @@ class TestApp {
|
||||
|
||||
void run() {
|
||||
childView.create(hitTestable, focusable, (ByteData reply) {
|
||||
// The child-view2 should be attached to Scenic now.
|
||||
// Ready to build the scene.
|
||||
// Set up window allbacks.
|
||||
window.onPointerDataPacket = (PointerDataPacket packet) {
|
||||
for (final data in packet.data) {
|
||||
if (data.change == PointerChange.up) {
|
||||
if (data.change == PointerChange.down) {
|
||||
this._backgroundColor = _black;
|
||||
}
|
||||
}
|
||||
window.scheduleFrame();
|
||||
};
|
||||
window.onBeginFrame = (Duration duration) {
|
||||
app.beginFrame(duration);
|
||||
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();
|
||||
});
|
||||
}
|
||||
@ -149,45 +161,16 @@ class TestApp {
|
||||
}
|
||||
}
|
||||
|
||||
ViewHolderToken _launchApp(String componentUrl) {
|
||||
final incoming = Incoming();
|
||||
final componentController = ComponentControllerProxy();
|
||||
|
||||
final launcher = LauncherProxy();
|
||||
Incoming.fromSvcPath()
|
||||
..connectToService(launcher)
|
||||
..close();
|
||||
launcher.createComponent(
|
||||
LaunchInfo(
|
||||
url: componentUrl,
|
||||
directoryRequest: incoming.request().passChannel(),
|
||||
),
|
||||
componentController.ctrl.request(),
|
||||
);
|
||||
launcher.ctrl.close();
|
||||
|
||||
ViewProviderProxy viewProvider = ViewProviderProxy();
|
||||
incoming
|
||||
..connectToService(viewProvider)
|
||||
..close();
|
||||
|
||||
final viewTokens = EventPairPair();
|
||||
assert(viewTokens.status == ZX.OK);
|
||||
final viewHolderToken = ViewHolderToken(value: viewTokens.first);
|
||||
final viewToken = ViewToken(value: viewTokens.second);
|
||||
|
||||
viewProvider.createView(viewToken.value, null, null);
|
||||
viewProvider.ctrl.close();
|
||||
|
||||
return viewHolderToken;
|
||||
}
|
||||
|
||||
class ChildView {
|
||||
|
||||
final ViewHolderToken viewToken;
|
||||
final ViewHolderToken viewHolderToken;
|
||||
final ViewportCreationToken viewportCreationToken;
|
||||
final int viewId;
|
||||
|
||||
ChildView(this.viewToken) : viewId = viewToken.value.handle.handle {
|
||||
ChildView(this.viewportCreationToken) : viewHolderToken = null, viewId = viewportCreationToken.value.handle.handle {
|
||||
assert(viewId != null);
|
||||
}
|
||||
|
||||
ChildView.gfx(this.viewHolderToken) : viewportCreationToken = null, viewId = viewHolderToken.value.handle.handle {
|
||||
assert(viewId != null);
|
||||
}
|
||||
|
||||
@ -227,3 +210,49 @@ class ChildView {
|
||||
callback);
|
||||
}
|
||||
}
|
||||
|
||||
ViewportCreationToken _launchFlatlandChildView() {
|
||||
ViewProviderProxy viewProvider = ViewProviderProxy();
|
||||
Incoming.fromSvcPath()
|
||||
..connectToService(viewProvider)
|
||||
..close();
|
||||
|
||||
final viewTokens = ChannelPair();
|
||||
assert(viewTokens.status == ZX.OK);
|
||||
final viewportCreationToken = ViewportCreationToken(value: viewTokens.first);
|
||||
final viewCreationToken = ViewCreationToken(value: viewTokens.second);
|
||||
|
||||
final createViewArgs = CreateView2Args(viewCreationToken: viewCreationToken);
|
||||
viewProvider.createView2(createViewArgs);
|
||||
viewProvider.ctrl.close();
|
||||
|
||||
return viewportCreationToken;
|
||||
}
|
||||
|
||||
ViewHolderToken _launchGfxChildView() {
|
||||
ViewProviderProxy viewProvider = ViewProviderProxy();
|
||||
Incoming.fromSvcPath()
|
||||
..connectToService(viewProvider)
|
||||
..close();
|
||||
|
||||
final viewTokens = EventPairPair();
|
||||
assert(viewTokens.status == ZX.OK);
|
||||
final viewHolderToken = ViewHolderToken(value: viewTokens.first);
|
||||
final viewToken = ViewToken(value: viewTokens.second);
|
||||
|
||||
viewProvider.createView(viewToken.value, null, null);
|
||||
viewProvider.ctrl.close();
|
||||
|
||||
return viewHolderToken;
|
||||
}
|
||||
|
||||
List<String> _GetArgsFromConfigFile() {
|
||||
List<String> args;
|
||||
final f = File(_argsCsvFilePath);
|
||||
if (!f.existsSync()) {
|
||||
return List.empty();
|
||||
}
|
||||
final fileContentCsv = f.readAsStringSync();
|
||||
args = fileContentCsv.split('\n');
|
||||
return args;
|
||||
}
|
||||
@ -0,0 +1,33 @@
|
||||
{
|
||||
include: [ "syslog/client.shard.cml" ],
|
||||
program: {
|
||||
data: "data/parent-view",
|
||||
|
||||
// Always use the jit runner for now.
|
||||
// TODO(fxbug.dev/106577): Implement manifest merging build rules for V2 components.
|
||||
runner: "flutter_jit_runner",
|
||||
},
|
||||
capabilities: [
|
||||
{
|
||||
protocol: [ "fuchsia.ui.app.ViewProvider" ],
|
||||
},
|
||||
],
|
||||
use: [
|
||||
{
|
||||
protocol: [
|
||||
"fuchsia.ui.app.ViewProvider",
|
||||
],
|
||||
},
|
||||
{
|
||||
directory: "config-data",
|
||||
rights: [ "r*" ],
|
||||
path: "/config/data",
|
||||
},
|
||||
],
|
||||
expose: [
|
||||
{
|
||||
protocol: [ "fuchsia.ui.app.ViewProvider" ],
|
||||
from: "self",
|
||||
},
|
||||
],
|
||||
}
|
||||
@ -1,15 +1,13 @@
|
||||
# This configuration file specifies several test suites with their package and
|
||||
# test command for femu_test.py.
|
||||
|
||||
# Legacy Component Framework v1 components.
|
||||
- test_command: run-test-component fuchsia-pkg://fuchsia.com/flutter-embedder-test2#meta/flutter-embedder-test2.cmx
|
||||
packages:
|
||||
- flutter-embedder-test2-0.far
|
||||
- gen/flutter/shell/platform/fuchsia/flutter/integration_flutter_tests/embedder/child-view2/child-view2/child-view2.far
|
||||
- gen/flutter/shell/platform/fuchsia/flutter/integration_flutter_tests/embedder/parent-view2/parent-view2/parent-view2.far
|
||||
- flutter_jit_runner-0.far
|
||||
|
||||
# v2 components.
|
||||
- test_command: run-test-suite fuchsia-pkg://fuchsia.com/flutter-embedder-test#meta/flutter-embedder-test.cm
|
||||
packages:
|
||||
- flutter-embedder-test-0.far
|
||||
- oot_flutter_jit_runner-0.far
|
||||
- gen/flutter/shell/platform/fuchsia/flutter/tests/integration/embedder/child-view/child-view/child-view.far
|
||||
- gen/flutter/shell/platform/fuchsia/flutter/tests/integration/embedder/parent-view/parent-view/parent-view.far
|
||||
- test_command: run-test-suite fuchsia-pkg://fuchsia.com/dart_runner_tests#meta/dart_runner_tests.cm
|
||||
package: dart_runner_tests-0.far
|
||||
- test_command: run-test-suite fuchsia-pkg://fuchsia.com/flutter_runner_tests#meta/flutter_runner_tests.cm
|
||||
|
||||
@ -36,6 +36,8 @@ template("_compile_cml") {
|
||||
"--output",
|
||||
rebase_path(invoker.output, root_build_dir),
|
||||
"--includepath",
|
||||
rebase_path("$fuchsia_sdk/pkg/", root_build_dir),
|
||||
"--includepath",
|
||||
get_path_info(invoker.manifest, "dir"),
|
||||
"--includepath",
|
||||
rebase_path("//"),
|
||||
@ -258,42 +260,66 @@ template("fuchsia_archive") {
|
||||
# cmx_file (optional):
|
||||
# A path to the .cmx file for the test archive.
|
||||
# If not defined, a generated .cml file for the test archive will be used instead.
|
||||
# cml_file (optional):
|
||||
# The path to the V2 component manifest (.cml file) for the test archive's component.
|
||||
# Should include the file extension.
|
||||
# If not defined, a generated .cml file for the test archive will be used instead.
|
||||
# libraries (optional):
|
||||
# Paths to .so libraries that should be dynamically linked to the binary.
|
||||
# resources (optional):
|
||||
# Files that should be placed into the `data/` directory of the archive.
|
||||
template("fuchsia_test_archive") {
|
||||
assert(defined(invoker.deps), "package must define deps")
|
||||
|
||||
# Interpolate test_suite.cml template with the test suite's name.
|
||||
test_suite = target_name
|
||||
interpolate_cml_target = "${test_suite}_interpolate_cml"
|
||||
generated_cml_file = "$root_out_dir/$test_suite.cml"
|
||||
action(interpolate_cml_target) {
|
||||
testonly = true
|
||||
script = "//flutter/tools/fuchsia/interpolate_test_suite.py"
|
||||
sources = [ "//flutter/testing/fuchsia/meta/test_suite.cml" ]
|
||||
args = [
|
||||
"--input",
|
||||
rebase_path("//flutter/testing/fuchsia/meta/test_suite.cml"),
|
||||
"--test-suite",
|
||||
test_suite,
|
||||
"--output",
|
||||
rebase_path(generated_cml_file),
|
||||
]
|
||||
outputs = [ generated_cml_file ]
|
||||
_deps = []
|
||||
if (defined(invoker.deps)) {
|
||||
_deps += invoker.deps
|
||||
}
|
||||
|
||||
far_base_dir = "$root_out_dir/${target_name}_far"
|
||||
if (defined(invoker.cml_file)) {
|
||||
_far_base_dir = "$root_out_dir/${target_name}_far"
|
||||
_cml_file_name = get_path_info(invoker.cml_file, "name")
|
||||
_compile_cml_target = "${target_name}_${_cml_file_name}_compile_cml"
|
||||
|
||||
# Compile the resulting interpolated test suite's cml.
|
||||
compile_test_suite_cml_target = "${test_suite}_test_suite_compile_cml"
|
||||
_compile_cml(compile_test_suite_cml_target) {
|
||||
testonly = true
|
||||
deps = [ ":$interpolate_cml_target" ]
|
||||
_compile_cml(_compile_cml_target) {
|
||||
forward_variables_from(invoker, [ "testonly" ])
|
||||
|
||||
manifest = generated_cml_file
|
||||
output = "$far_base_dir/meta/${test_suite}.cm"
|
||||
manifest = invoker.cml_file
|
||||
output = "$_far_base_dir/meta/${_cml_file_name}.cm"
|
||||
}
|
||||
_deps += [ ":$_compile_cml_target" ]
|
||||
} else {
|
||||
# Interpolate test_suite.cml template with the test suite's name.
|
||||
test_suite = target_name
|
||||
interpolate_cml_target = "${test_suite}_interpolate_cml"
|
||||
generated_cml_file = "$root_out_dir/$test_suite.cml"
|
||||
action(interpolate_cml_target) {
|
||||
testonly = true
|
||||
script = "//flutter/tools/fuchsia/interpolate_test_suite.py"
|
||||
sources = [ "//flutter/testing/fuchsia/meta/test_suite.cml" ]
|
||||
args = [
|
||||
"--input",
|
||||
rebase_path("//flutter/testing/fuchsia/meta/test_suite.cml"),
|
||||
"--test-suite",
|
||||
test_suite,
|
||||
"--output",
|
||||
rebase_path(generated_cml_file),
|
||||
]
|
||||
outputs = [ generated_cml_file ]
|
||||
}
|
||||
|
||||
far_base_dir = "$root_out_dir/${target_name}_far"
|
||||
|
||||
# Compile the resulting interpolated test suite's cml.
|
||||
compile_test_suite_cml_target = "${test_suite}_test_suite_compile_cml"
|
||||
_compile_cml(compile_test_suite_cml_target) {
|
||||
testonly = true
|
||||
deps = [ ":$interpolate_cml_target" ]
|
||||
|
||||
manifest = generated_cml_file
|
||||
output = "$far_base_dir/meta/${test_suite}.cm"
|
||||
}
|
||||
|
||||
_deps += [ ":$compile_test_suite_cml_target" ]
|
||||
}
|
||||
|
||||
_fuchsia_archive(target_name) {
|
||||
@ -309,15 +335,6 @@ template("fuchsia_test_archive") {
|
||||
libraries += invoker.libraries
|
||||
}
|
||||
|
||||
# TODO(fxbug.dev/79873): Only cfv2 components should be allowed after
|
||||
# FakeScenic is available.
|
||||
if (defined(invoker.cmx_file)) {
|
||||
cmx_file = invoker.cmx_file
|
||||
|
||||
# Don't include cml files.
|
||||
deps = invoker.deps
|
||||
} else {
|
||||
deps = invoker.deps + [ ":$compile_test_suite_cml_target" ]
|
||||
}
|
||||
deps = _deps
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user