RFC-0202:测试管理器即服务

RFC-0202:将测试管理器作为服务
状态已接受
区域
  • 测试
说明

设计测试管理器即服务。

问题
Gerrit 更改
作者
审核人
提交日期(年-月-日)2022-07-22
审核日期(年-月-日)2022-12-14

摘要

目前,在 Fuchsia 上执行的测试是作为 Test Manager 的动态子项执行的。这种设计使 Test Manager 能够在任何领域(产品所有者有权创建的领域)中启动测试,从而允许测试作者在不更改 Test Manager 的情况下路由所需的运行程序和功能。

设计初衷

Test Manager 会将测试作为动态子项启动,为其提供各种密封/非密封功能和树内运行程序。Test Manager 的当前设计是使用一组静态内置测试运行程序和测试领域运行。它不允许树外 (OOT) 和树内客户添加自己的测试运行程序和所需功能,但随着客户数量的增加,我们需要支持他们的用例。本文档提出了“Test Manager as a Service”(Test Manager 作为服务)的概念,可让客户在其选择的测试环境(可能不在 Test Manager 的环境中)中运行测试。这样可以减少测试管理器在将所需系统功能路由到测试方面的责任。

利益相关方

教练:davemoore@google.com

审核员:geb@google.com、shayba@google.com、richkadel@google.com、kjharland@google.com、crjohns@google.com、cgonyeo@google.com、aaronwood@google.com、satsukiu@google.com、xbhatnag@google.com、yaneury@google.com、hjfreyer@google.com、akiggs@google.com

咨询了

我们咨询了组件框架团队,就此设计中使用的 capability 路由、框架和测试 API 相关问题进行了讨论。

社交

此 RFC 已通过测试架构和组件框架团队的设计审核。

设计

在此设计中,产品所有者将决定测试应在何处运行,测试执行器 (ffx test/run-test-suite) 将向测试管理器传递所需信息。我们将向 RunBuilder 协议添加一个名为 AddSuiteInRealm 的新方法,并传入所需的 Realm 信息。

  AddSuiteInRealm(resource struct {
      // The realm which contains the collection to launch the test in
      realm client_end:fuchsia.component.Realm;
      // All offers from the realm to the test collection
      offers: Vec<Capabilities>
      // the test collection to launch the test in.
      test_collection: string

      // ... existing fields from AddSuite
  });

Test Manager 将使用上述信息使用 Realm Builder 在指定集合中启动测试,同时提供隔离的记录器、覆盖率收集、树内运行程序等来支持测试执行。组件管理器将 LifecycleController 和 RealmQuery 协议(范围限定为“/”)路由到测试执行器。

测试领域可以由平台本身编写,也可以由产品所有者使用某种现有/新机制编写。到目前为止,只有平台可以定义测试 Realm,而这项工作旨在让测试 Realm 的定义和管理更加民主化。

此设计假定有两种类型的用户。

  • 测试 Realm 作者:此用户将创建和维护测试将在其中运行的 Realm(他们应有权在拓扑中创建 Realm)。
  • 测试作者:测试的作者,该测试将在由领域作者创建的领域中运行。

测试 Realm 作者将创建测试 Realm、进行设置并与构建工具集成,以便测试作者运行测试。他们需要在测试 Realm 中安装 Realm 构建器分片,此功能才能正常运行。

测试 Realm 示例:

{
  include: [
      "sys/component/realm_builder.shard.cml",
  ],
  collections: [
      // The collection to launch test in
      {
          name: "tests",
          environment: "#test_env",
          durability: "transient",
      },
  ],
  offer: [
      {
          protocol: [
              // Some system or mocked protocol
              "fuchsia.foo.bar",
              ...
          ],
          from: "parent",
          to: [
              "#tests",
          ],
      },
      ...
  ],
  environments: [
      {
          name: "test_env",
          extends: "realm",
          runners: [
              // Offer some OOT runner to the test
              {
                  runner: "fuchsia_oot_runner",
                  from: "parent",
              },
              // TODO(https://fxbug.dev/42063673): Abstract out into a shard.
              // This is important so that Realm Builder can work.
              {
                  runner: "realm_builder",
                  from: "#realm_builder_server",
              },
          ],
          resolvers: [
              // This is important so that Realm Builder can work.
              {
                  resolver: "realm_builder_resolver",
                  from: "#realm_builder_server",
                  scheme: "realm-builder",
              },
          ],
      },
  ]
}

领域作者将提供与构建工具的集成,以便测试执行器在执行期间读取标识名和测试集合。

人体工学部分简要介绍了向测试执行程序传递标识名和测试集合名称的一些解决方案,详细讨论超出了本文档的范围。

测试作者将使用某种人体工学解决方案(待定)来运行其测试,该解决方案会将信息传递给测试执行器以运行测试。

测试执行器将使用 RealmQuery API 查询相应 realm 的 realm 对象以及测试集合中的所有方案,并使用建议的 AddSuiteInRealm API 将其传递给 Test Manager。

它还会根据需要使用 LifecycleController API 解析标识名。

此设计需要进行以下更改:

  • 修改了 RealmQuery API 以读取“offer”声明。
  • 我们将修改 Realm Builder Rust 客户端库,以允许使用提供的 fuchsia.component/Realm 代理构建 Realm。
  • Test Manager 将使用 Realm Builder 库中的新方法,使用范围限定为自定义 Realm 的 Realm Builder 启动测试实例。
  • Test Manager 将使用提供的 offers,并使用 RealmBuilder 进行路由。
  • Test Manager 会在测试环境中设置隔离的记录器、调试数据协议、树内运行程序等。

由于现在具有组件实例的句柄,因此 Test Manager 可以连接到测试领域中的所有功能。

测试结束后,Test Manager 会收集所有工件并上传测试结果。测试将能够访问其父级领域提供的任何 capability,以及测试架构提供的所有封闭 capability。

这种方法的优势

  • 开发者可以自行提供运行程序。
  • 您现在就可以实现这一点。无需等待任何其他功能。

其他福利

  • 开发者可以按照自己的方式配置测试 Realm,而无需对核心产品或测试架构进行任何更改。
  • 无需支持自定义测试类型。

此解决方案的缺点如下:

  • 开发者可以创建自己的非容器化 Realm 来启动测试,因此我们最终可能会得到一堆本可以通过一些简单的操作变为容器化的非容器化测试。
  • 直接使用 ffx test 的开发者需要向该工具提供其标识名和集合名称。
    • ffx 测试是一款基础工具,因此开发者应使用调用 ffx 测试的其他工具,而无需手动传递别名。

测试管理器可用作服务后测试拓扑的示意图。

测试拓扑

实现

  • 更改了 Realm 构建器,使其能够通过接受 fuchsia.component.Realm 对象在任意 Realm 中启动组件。
  • 更改了测试执行器,以接受 realm 信息查询标识名以获取所需信息。
  • 更改了 Test Manager 以实现新的 FIDL API,并在所提供的 realm 和集合中启动测试。
  • 记录更改和帮助指南
  • 与 OOT 开发者合作,创建包含 OOT 运行程序的领域。
  • 将当前的 OOT 测试移植到新领域

后续工作:

  • 将使用自定义测试类型的当前测试移植到自己的领域。
  • 探索如何移除对标识名的依赖项。
  • 从测试管理器下移除所有测试领域,并将其发布到产品领域中。

性能

此更改对性能没有影响或影响微乎其微,因为它不会影响测试的启动方式,只会影响测试在拓扑中的启动位置。

工效学设计

本部分介绍了向测试执行器传入标识符和测试集合的几种解决方案。本文档不涉及详细解决方案。

与 build 集成

定义自定义领域后,产品所有者可以在其构建系统中定义测试类别。测试作者将使用该类别,构建系统将生成相应的标识符和测试集合,作为测试执行器的输入。

向测试清单添加信息

我们可以在测试细分中嵌入标识名和测试集合信息。测试执行器需要解析和读取组件清单文件才能获取相关信息。

为了更好地适应人体工学,测试 Realm 作者将提供清单分片,测试作者可以将其包含在测试清单中。

向后兼容性

我们将继续支持当前测试,并且始终支持不需要自定义运行程序的密封式测试 Realm。

安全注意事项

  • 开发者可以在系统中的任何领域启动测试,但由于测试将在 eng build 上运行,因此用户设备不会受到此更改的影响。
    • 我们还将在测试执行器中添加一个标志,以便在某些共享机器和 build 上停用该功能。
  • 我们需要将 RealmQuery 和 LifecycleController 路由到所有测试执行器。这可能会带来安全问题,具体取决于我们决定采用的方式。我们将在下一份文档中讨论这一点,届时我们将设计测试执行者如何访问此类信息。

隐私注意事项

我们不会收集任何个人数据,因此这种设计不会对隐私造成任何影响。

测试

当前测试以及用于在自定义领域中启动测试的新集成和单元测试应能全面测试此功能。

文档

记录此功能,说明用例和实现指南,以便开发者创建自己的测试领域。

此外,还应记录测试开发者使用自定义王国的途径。

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

替代方案:客户启动自己的 Test Manager

测试开发者将在其拓扑中启动自己的 Test Manager。ffx 测试将使用 RCS 连接到 fuchsia.test.manager.RunBuilder 协议,以执行测试并收集测试结果。

此解决方案的好处:

  • 我们只需更改 ffx 测试即可支持此功能
  • 开发者可以将当前的 Test Manager 代码与自定义清单搭配使用,在其拓扑中使用 Test Manager 组件

此解决方案存在以下问题:

  • 一旦 Test Manager 在各种拓扑和 OOT 中运行,就很难更改。这将极大地影响测试架构团队的速度。
  • 客户需要将 Test Manager 所需的所有功能路由到自己的拓扑。
  • 客户需要编写测试,以确保 Test Manager 在其拓扑中正常运行并防止任何回归问题

替代方案:子组件

子组件可以解决“自带测试运行程序”问题,但灵活性不如建议的解决方案。

通过提出的解决方案,我们日后可以灵活地从测试管理器中移除所有硬编码的测试领域,并将所有权交给产品所有者。

备选方案:测试打包自己的运行程序

测试可以打包自己的运行程序,并使用它们运行测试组件

此解决方案的好处:

  • 无需进行任何更改
  • 这可以立即实现

此解决方案存在以下问题:

  • 针对每个测试运行新运行程序的性能影响。
  • 需要将运行程序所需的功能路由到测试(会破坏密封性并削弱我们的保证)。
  • 需要为每个新运行程序创建一个自定义王国(由于功能路由要求)。这会加重技术债务。

替代方案:测试配置

使用功能及其来源对测试进行参数化,然后将这些参数管道到测试 realm 本身。

"fuchsia.test.additional_capabilities": {
  "runner": "dart_runner",
  "source": "/core/dart/runner"
}

对于可路由功能,Test Manager 可以使用 RealmQuery API 使用 Realm Builder 代理请求。对于运行程序,Test Manager 可以使用 hub 代理运行程序协议。

此解决方案的好处:

  • 测试会作为 Test Manager 的子项运行,因此 Test Manager 可以完全控制其功能。

此解决方案存在以下问题:

  • 测试可能依赖于正式版标识名,因此它们会成为公开 API 的一部分。
  • 使用集线器代理运行器请求只是一个短期解决方案,我们最终需要嵌套或链式运行器。
  • 测试将能够访问任何系统功能,这可能会破坏显式路由,从而造成安全问题,并远程创建操作。

替代方案:Test Manager 使用 RealmQuery API

在此设计中,Test Manager 将有权使用 RealmQuery API,并使用该 API 从测试 Realm 中查询所需信息以启动测试。

测试作者将使用分片在其测试清单中添加 realm 和测试集合信息。

分片:

{
  facets: {
    "fuchsia.test": {
      launch: {
        realm: "/core/foo/bar/test_realm",
        collection: "tests" // default is "tests", can be omitted.
      }
    },
  },
}

test.cml:

{
    include: [
        "syslog/client.shard.cml",
        "//some/path/oot_runner/default.shard.cml",
        "//some/path/test_realm/default.shard.cml",
    ],
    program: {
        binary: "bin/sample_test",
    },
    use: [
      {
          protocol: [
              "fuchsia.foo.bar",
              ...
          ],
      },
      ...
  ],
}

测试管理器将读取这些 facet,并使用提供的功能在指定的测试领域中查询和启动测试。

测试拓扑

替代方案:Test Manager 在拓扑中的某个已知位置运行系统测试

此设计提议在拓扑中有一个常见的已知位置(例如 /core/tests),测试管理器可以在其中执行所有系统测试。平台开发者将创建并维护此已知领域,而 Test Manager 仅提供执行测试和从测试中收集工件的机制。

缺点

  • 可配置性较低,如果日后需要在多个位置运行测试,则无法扩展。
  • 我们希望未来能够使用嵌套的 Test Manager,在同级领域中运行测试。此设计不鼓励这样做。
  • 我们可以使用此 RFC 中的设计实现相同的功能,将 /core/tests 作为应运行测试的领域进行定位。我们认为,此 RFC 提供了更大的灵活性,对您会很有用。

在先技术和参考文档

这是 Fuchsia 独有的概念,因此没有先前的相关技术。

后续工作

  • 从测试管理器拓扑中移除所有非密封区域,并与客户合作将其测试移至自己的自定义测试区域。
  • 将“自带测试管理器”设计为测试领域的同级兄弟。这对于在会话或任何 OOT 领域下运行测试非常有用,而无需在平台中提供任何产品专用支持。