RFC-0180:测试界面堆栈

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

此 RFC 是针对测试界面堆栈组件的设计提案,该组件将提供特定于界面的测试功能,以测试树内和树外的客户端。

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

摘要

此 RFC 是针对测试界面堆栈组件的设计提案,该组件将为树内和树外的测试客户端提供界面专属的测试功能。

设计初衷

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

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

Test UI Stack 组件旨在通过代表测试客户端处理低级界面详细信息来缓解这些问题。

用例示例

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

利益相关方

哪些人会受到此 RFC 是否被接受的影响?(此部分为可选,但建议填写。)

辅导员

leannogasawara@google.com

审核者

  • 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 组件。 大致来说,此集合包括场景、根演示器或场景管理器、输入流水线、无障碍管理器、快捷方式管理器和文本管理器。
  • 测试界面堆栈:建议的组件,用于公开界面堆栈(基本界面服务 + 辅助服务)的外观。
  • 基本界面服务:从生产界面 realm 中公开的一组服务。
  • 辅助服务:仅用于测试的服务,通过更高级别的 API 将低级别界面功能代理到客户端。
  • 场景视图的层次结构,可让所有者向显示屏呈现可渲染的内容、使用输入源并与无障碍功能互动。

设计

要求

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

建议

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

概览

我们建议在 Fuchsia 合作伙伴 SDK 中添加一个测试界面堆栈组件,该组件将公开生产界面 realm 的外观。具体而言,此组件将公开以下服务:

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

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

替代文本:
显示的组件拓扑:
测试管理器 -> 测试装置组件
测试装置组件 -> 测试界面堆栈组件
测试装置组件 -> 测试界面客户端
测试装置组件 -> 支持组件
测试装置组件 -> 本地模拟对象
测试界面堆栈组件 -> 辅助组件
测试界面堆栈组件 -> 基本界面组件 服务路由
辅助组件 -> 测试界面堆栈组件(辅助服务)
基本界面组件 -> 测试界面堆栈组件(基本界面服务)
测试界面堆栈组件 -> 测试装置组件(基本界面服务、辅助服务)
测试界面堆栈组件 -> 测试界面客户端(基本界面服务)
测试界面客户端 -> 测试装置组件 (fuchisa.ui.app.ViewProvider)
支持组件 -> 测试界面客户端(支持服务)
本地模拟对象 -> 测试界面客户端(模拟服务)

请注意,此设计与界面堆栈和测试组件如何配置各自的 Realm 无关。它们可以静态或通过 RealmBuilder 实现这一点。

基本界面拓扑

最初,测试界面堆栈将包含以下基本界面组件,这些组件与撰写本文时的“现代”生产界面堆栈相对应:

  1. 风景,配置为使用平地。
  2. 场景管理器
  3. Accessibility Manager
  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 Stack 迁移的进度,我们可能还会添加 Test UI Stack 组件的“旧版”变体,该变体使用根演示器和输入流水线来代替场景管理器。

辅助组件

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

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

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

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

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

可配置性

某些客户端可能需要配置显示旋转、像素密度等参数。测试界面堆栈可以通过结构化组件配置来满足这些使用情形。客户端可以替换它们希望控制的参数,然后测试界面堆栈组件可以将这些参数传播到相应的基本界面组件。

使用示例

以下伪 C++ 代码段概述了使用测试界面堆栈组件进行的基本触控输入测试。

// 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. Land FIDL 更改。
  2. 实现场景提供程序辅助组件。
  3. 重构现有树内测试以使用场景提供程序。

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

工作流:几何图形观测器

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

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

工作流:输入合成

  1. 重新设计了输入合成 API,以供 OOT 使用。
  2. 向 SDK 添加了输入合成 FIDL 库。
  3. 实现 FIDL 库。

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

工作流:重构了树内 UITestManager 库

  1. 将 realm 配置从现有的内部 UITestManager 类中分离出来,放入新的 UITestRealm 类中,该类可与测试界面堆栈共享。
  2. (可选)实现一种机制,用于与生产界面子实境共享 .cml。如果现在不进行清理,我们应在 One UI Stack 迁移完成后进行清理。

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

性能

此设计面向已是多组件的集成测试,因此我们预计所提议的测试拓扑扩展对性能的影响极小。

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

安全注意事项

此 RFC 没有安全方面的考虑因素。由于测试界面堆栈不消耗任何系统功能(除了 sysmem 和 vulkan),因此它无法执行普通最终用户 vulkan 程序无法执行的任何操作。

隐私注意事项

测试界面堆栈无法访问私密或敏感资源,因此此 RFC 不涉及隐私注意事项。

测试

随着我们编写使用测试界面堆栈的测试,我们对测试界面堆栈的行为有了足够的信心。

文档

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

缺点、替代方案和未知因素

缺点

在多个客户端之间复制样板

建议的设计让客户端需要编写一些常见的样板代码。我们或许可以通过针对特定界面自定义实现 fuchsia.test.Suite 来消除此痛点,从而使客户端能够将测试客户端和测试逻辑插入预定义的界面测试框架中。

考虑的替代方案

请参阅原始的 UI Test Manager RFC