RFC-0180:测试界面堆栈

RFC-0180:测试界面堆栈
状态已接受
区域
  • 视图系统
说明

此 RFC 是 Test UI Stack 组件的设计提案,该组件将为树内和树外的测试客户端提供特定于界面的测试功能。

问题
Gerrit 更改
作者
审核人
提交日期(年-月-日)2022-06-16
审核日期(年-月-日)2022-07-26

摘要

此 RFC 是 Test UI Stack 组件的设计提案,该组件将提供特定于界面的测试功能,用于测试树内和树外的客户端。

设计初衷

集成测试对于 Fuchsia 花瓣稳定性至关重要。不过,目前很少有树外(“OOT”)密封界面集成测试,因为 OOT 客户端在编写这些测试时会遇到重大障碍。具体而言,他们必须:

  • 了解界面堆栈的深奥知识,这些知识通常超出了其生产用例的范围。
  • 将测试行为与界面堆栈的内部实现细节相关联。
  • 使用仅限内部使用的 FIDL API 运行相关界面组件。
  • 设计测试,使其能够对界面堆栈中进行的各种迁移(GFX -> flatland、根 Presenter -> 场景管理器、CFv1 -> CFv2)具有弹性。

测试界面堆栈组件旨在代表测试客户端处理低级界面详细信息,以缓解这些问题。

用例示例

  • 针对客户端运行时的触摸/鼠标/键盘输入测试:对于这些测试,客户端会调出测试界面堆栈,将视图附加到场景,等待视图树状态进入空闲状态,注入输入,并观察其视图如何处理传入事件。
  • 应用测试:这些测试可针对界面堆栈运行应用的某些部分,并观察其呈现内容、处理输入、与无障碍功能互动等方式。
  • 与界面相关的测试:某些未明确使用界面功能的测试可能仍需要界面。例如,针对 fuchsia.web 的测试可能需要测试客户端呈现视图。

利益相关方

哪些人对此 RFC 的接受与否有利益相关?(此部分为可选部分,但建议填写。)

教员

leannogasawara@google.com

Reviewers:

  • Fuchsia 测试架构:crjohns@google.com
  • 界面 + OOT 集成测试:dworsham@google.com、jaeheon@google.com

咨询了

列出应审核 RFC 但无需获得其批准的人员。

  • 输入:quiche@google.com、neelsa@google.com
  • 无障碍功能:neelsa@google.com、lucasradaelli@google.com
  • 组件框架:yaneury@google.com、geb@google.com、cgonyeo@google.com
  • Flutter:akbiggs@google.com
  • Chromium:sergeyu@google.com
  • Opal:cligh@google.com、anwilson@google.com、robinsontom@google.com

社交

此 RFC 已通过 Fuchsia 测试团队和 Fuchsia 输入团队的设计审核。我们还咨询了 OOT 界面客户团队。

术语库

  • 界面堆栈:一组用于提供基本界面服务的 Fuchsia 组件。大致而言,此组件包括 scenic、根 Presenter 或场景管理器、输入流水线、无障碍功能管理器、快捷方式管理器和文本管理器。
  • 测试界面堆栈:提议的组件,用于公开界面堆栈的外观(基本界面服务 + 辅助服务)。
  • 基本界面服务:从生产界面领域公开的一组服务。
  • 辅助服务:仅用于测试的服务,通过更高级别的 API 将低级界面功能代理给客户端。
  • 场景视图的层次结构,可让所有者向显示屏呈现可渲染内容、使用输入内容,以及与无障碍功能互动。

设计

要求

  • 涉及界面的密封 OOT 集成测试必须易于编写。
  • 涉及界面的密封 OOT 集成测试必须可读。
  • 测试功能不得泄露界面堆栈的内部信息。
  • 客户端必须能够配置包含界面堆栈的测试 realm。
  • 客户端必须能够扩展 Test UI Stack 定义的组件拓扑,以包含测试专用配置。
  • 必须保证测试能够干净利落地进行设置和拆解。
  • 测试必须相互隔离。
  • 测试界面堆栈不得禁止使用 RealmBuilder。
  • 客户必须能够使用自己选择的语言编写测试。

建议

  • 树内和树外界面集成测试应该是直接类似的。

概览

我们提议将一个测试界面堆栈组件添加到 Fuchsia 合作伙伴 SDK,该组件将公开正式版界面领域的外观。具体而言,此组件将公开以下服务:

  1. 大致来说,是指在生产版界面中公开的一组公共服务。
  2. 一组仅用于测试的“辅助服务”,通过更高级别的抽象(例如输入合成、屏幕截图等)提供低级界面功能。

客户端可以实例化此组件,将所需的界面服务路由到受测组件,向场景呈现视图,并使用提供的各种辅助服务来驱动其测试。

备注文字:
显示的组件拓扑结构:
测试管理器 -> 测试实例组件
测试实例组件 -> 测试界面堆栈组件
测试实例组件 -> 测试界面客户端
测试实例组件 -> 支持组件
测试实例组件 -> 本地模拟对象
测试界面堆栈组件 -> 辅助组件
测试界面堆栈组件 -> 基础界面组件
服务路线
辅助组件 -> 测试界面堆栈组件(辅助服务)
基础界面组件 -> 测试界面堆栈组件(基础界面服务)
测试界面堆栈组件 -> 测试实例组件(基础界面服务)
测试界面客户端 -> 测试实例组件 (fuchisa.ui.app.ViewProvider)
支持组件 -> 测试界面客户端(支持服务)
本地模拟对象 -> 测试界面客户端(模拟服务)

请注意,此设计与界面堆栈和测试组件如何配置各自的领域无关。它们可以静态或通过 RealmBuilder 执行此操作。

基本界面拓扑

最初,测试界面堆栈将包含以下基本界面组件,这些组件反映了本文撰写时“现代”正式版界面堆栈:

  1. 风景模式,配置为使用平原。
  2. 场景管理器
  3. 无障碍功能管理器
  4. 文本管理器
  5. 快捷键管理器
  6. Cobalt(不是界面组件,但必须运行 scenic)
  7. 虚构的硬件显示控制器(再次强调,这不是界面组件,但 scenic 需要它)

此外,测试界面堆栈最初会向测试公开以下基本界面服务:

  1. fuchsia.accessibility.semantics.SemanticsManager
  2. fuchsia.ui.composition.Allocator
  3. fuchsia.ui.composition.Flatland
  4. fuchsia.ui.scenic.Scenic
  5. fuchsia.ui.input.ImeService
  6. fuchsia.ui.input3.Keyboard
  7. fuchsia.ui.input3.KeyEventInjector
  8. fuchsia.ui.shortcut.Manager
  9. fuchsia.ui.shortcut.Registry

请注意,测试界面堆栈可以并将会演变为与正式版堆栈一致。

根据 One UI 堆栈迁移的进度,我们可能还会添加 Test UI Stack 组件的“旧版”变体,该变体使用根 Presenter 和输入流水线来替代场景管理器。

辅助组件

除了上述基本界面组件之外,测试界面堆栈还将包含一组范围较小的辅助组件,以便通过更高级别的 API 向客户端提供特定于界面的低级功能。在发布时,此集合可能包括:

  1. 输入合成组件,可让客户端直接将文本、鼠标和触摸输入注入输入流水线。
  2. 屏幕截图组件,可让客户端以符合人体工学的方式截取屏幕截图。
  3. 场景提供程序组件,用于将客户端视图附加到场景并代表它们注册特权功能(例如受限几何图形观察器)。请注意,由于场景提供程序代表客户端注册观察器,因此测试界面堆栈无需公开任何观察器注册表服务。

测试界面堆栈将公开这些组件中的辅助服务,客户端可以使用这些服务来驱动测试。

辅助组件抽象具有以下几项重要优势:

  • 抽象:辅助服务向客户端提供稳定且明确定义的表层,有助于最大限度地减少对界面堆栈内部的依赖。
  • 简单性:通过专用辅助组件提供界面功能可确保每个 FIDL API 都简单而专用,从而改进了编写和维护界面测试的 DX。
  • 可扩展性:我们可以通过添加新的辅助组件轻松扩展界面外观。
  • 与子软件包的兼容性:我们可以转换为子软件包,而不会使客户端失去功能。

可配置性

某些客户端可能需要配置显示旋转、像素密度等参数。借助结构化组件配置,测试界面堆栈可以适应这些用例。客户端可以替换他们想要控制的参数,然后测试界面堆栈组件可以将这些参数传播到相应的基准界面组件。

使用示例

以下伪 C++ 代码段概述了使用 Test UI Stack 组件进行的基本触摸输入测试。

// Client test code creates a RealmBuilder instance.
component_testing::RealmBuilder realm_builder;

// Instantiate Test UI Stack component by absolute URL in the test realm.
realm_builder.AddChild("test-ui-stack",
            "fuchsia-pkg://fuchsia.com/test-ui-stack#meta/test-ui-stack.cm");

// Add a test view component to the test realm, and route required UI services
// to it.
realm_builder.AddChild("test-view", ...);
realm_builder.AddRoute({
    .capabilities = {Protocol{fuchsia::ui::scenic::Scenic::Name_}},
    .source = ChildRef{"test-ui-stack"},
    .targets = {"test-view"}},
}});

// Expose fuchsia.ui.app.ViewProvider from the test view.
realm_builder.AddRoute({
    .capabilities = {Protocol{fuchsia::ui::app::ViewProvider::Name_}},
    .source = ChildRef{"test-view"},
    .targets = {ParentRef()}},
}});

// Build the test realm.
RealmRoot realm_root = realm_builder_.Build();

// Connect to the scene provider "helper service", and request to attach a
// test view to the scene.
std::optional<zx_koid_t> client_view_ref_koid;
fuchsia::ui::observation::geometry::Provider geometry_provider;
auto scene_provider = realm_root->Connect<fuchsia::ui::test::scene::Provider>();
auto view_provider = realm_root_->Connect<fuchsia::ui::app::ViewProvider>();
scene_provider->AttachView(std::move(view_provider), geometry_provider.NewRequest(),
  [&client_view_ref_koid](auto view_ref_koid) {
    // Save the client's ViewRef koid.
    client_view_ref_koid = view_ref_koid;
  });

// Wait for client view ref koid to become available.
RunLoopUntil([&client_view_ref_koid] {
  return client_view_ref_koid.has_value();
});

// Use registered geometry provider to wait for client view to render.
ASSERT_TRUE(geometry_provider.is_bound());
geometry_provider.Watch(...);
RunLoopUntil(...);

// Connect to input synthesis helper service, and use to inject input.
auto input_synthesis = realm_root->Connect<fuchsia::ui::test::input::Touch>();
input_synthesis->InjectTap(...);

实现

下列列出的工作流可以并行进行。

工作流:场景提供程序辅助服务

  1. 土地 FIDL 变更。
  2. 实现场景提供程序帮助程序组件。
  3. 重构现有的树内测试以使用场景提供程序。

此工作流可让测试将视图附加到场景,这是任何图形/输入测试的硬性要求。

工作流:几何图形观察器

  1. 在 SDK 中使 fuchsia.ui.observation.geometry 协议可供 OOT 使用。
  2. 实现了“作用域”几何图形观察器注册表。

通过此工作流,OOT 客户端可以使用与场景图的根无关的几何观察器数据,该数据可能会因不同的产品和界面堆栈配置而异。

工作流:输入合成

  1. 重新设计了输入合成 API,以便在 OOT 中使用。
  2. 将输入合成 FIDL 库添加到 SDK。
  3. 实现了 FIDL 库。

此工作流可让 OOT 测试界面堆栈用户注入输入;目前,没有替代方案。

工作流:重构树内 UITestManager 库

  1. 将现有内部 UITestManager 类中的 Realm 配置提取到新的 UITestRealm 类中,该类可与测试界面堆栈共享。
  2. (可选)实现一种机制,用于与生产版界面子领域共享 .cml。如果现在不执行,我们应在 One UI 堆栈迁移完成后执行此清理。

上述工作流完成后,我们可以在合作伙伴 SDK 中组装测试界面堆栈软件包,并将其添加到 OOT 客户端运行测试的产品 build。

性能

此设计适用于已经是多组件集成测试,因此我们预计,测试拓扑结构的建议扩展对性能的影响将会降到最低。

某些 OOT 测试的性能实际上可能会有所提升,因为它们可以依赖更稳定的同步模式。

安全注意事项

此 RFC 没有安全注意事项。由于测试界面堆栈不会使用任何系统功能(sysmem 和 vulkan 除外),因此它无法执行普通最终用户 vulkan 程序无法执行的任何操作。

隐私注意事项

测试界面堆栈无权访问私密或敏感资源,因此此 RFC 没有隐私权注意事项。

测试

在编写测试来使用测试界面堆栈时,我们可以对测试界面堆栈的行为有足够的信心。

文档

我们打算发布一份开发者指南,介绍如何使用测试界面堆栈。

缺点、替代方案和未知情况

缺点

客户端之间存在重复的样板代码

所提议的设计会让客户需要编写一些常见的样板代码。我们或许可以通过自定义界面专用 fuchsia.test.Suite 实现来消除这一痛点,这样客户就可以将测试客户端和测试逻辑插入预定义的界面测试框架中。

考虑的替代方案

请参阅原始的 UI Test Manager RFC