FIDL 測試與分析;GN

本文將在 GN 建構系統中,標準化 FIDL 測試的定義和整理方式,並達成下列目標:

  • 命名時保持一致。如果 Rust 使用 fx test fidl_rust_conformance_tests,則 Go 應使用 fx test fidl_go_conformance_tests。一致且可預測的命名方式可提供更優質的開發人員體驗。
  • 執行所需作業。測試工作流程應能輕鬆執行單一測試元件,不必建構或執行任何額外項目。
  • 在主機上執行。如有可能,測試應支援在主機 (非 Fuchsia) 上執行,因為編輯、建構及執行週期通常會快上許多。
  • 遵循最佳做法。我們應遵循 Fuchsia 最佳做法,例如使用 fx test建構元件等。

術語

本文使用下列術語:

  • 目標:BUILD.gn 檔案中定義的 GN 目標
  • toolchain:請參閱 fx gn help toolchain
  • 主機:開發人員的平台,具體來說是 Linux 或 Mac
  • 裝置:Fuchsia 平台,可以是實體或模擬裝置 (即 qemu)
  • 套件Fuchsia 套件,Fuchsia 中的發布單位
  • 元件Fuchsia 元件,是 Fuchsia 中可執行的軟體單元

命名

一般指南:

  • 請使用底線,不要使用連字號。
  • 結尾名稱應為複數 _tests,而非單數 _test
  • 為套件、元件和二進位檔使用完整、描述性且不重複的名稱。

最後一點是指偏好使用完整名稱 (例如 fidl_rust_conformance_tests),而非情境名稱 (例如 conformance_tests)。在目錄、套件、元件和二進位檔層級重複「fidl」和「rust」,似乎會顯得冗長且多餘。但事實是,這些名稱不得重複,最好以一致的方式確保名稱不重複,而不是記住奇怪的規則,例如 fidl-bindings-test 用於 Dart,fidl-test 用於 C。

名稱應使用下列配置,並以底線連結各部分:

tool [ bindings ] [ category [ subcategory ] ] tests

其中 tool 為下列其中一項:

  • fidl:FIDL 執行階段支援
  • fidlc:FIDL 編譯器前端
  • fidlgen:FIDL 編譯器後端
  • gidlmeasure_tape 等:其他工具

其他部分包括:

  • bindings
    • ccppcpp_wirehlcpprustgodart 其中之一
  • categorysubcategory
    • 例如:conformancetypesparserlib
    • 使用:前端後端繫結 (工具會區分這些項目)

階層

定義測試的每個 BUILD.gn 檔案都應包含 "tests" 群組:

group("tests") {
  testonly = true
  deps = [ ... ]  # not public_deps
}

如果目錄結尾為「tests」,且 BUILD.gn 檔案只定義測試目標,則群組應改為與目錄名稱相符。舉例來說,foo_tests/BUILD.gn 可以使用 group("foo_tests")。這會啟用 GN 標籤簡寫 //path/to/foo_tests,相當於 //path/to/foo_tests:foo_tests

這些群組會匯總至父項目錄中 BUILD.gn 檔案的「tests」群組。根「tests」群組 (適用於部分程式碼集,例如 src/lib/fidl/BUILD.gn) 應包含在 bundles/fidl/BUILD.gn 中。這可讓 fx set ... --with //bundles/fidl:tests 在建構作業中納入所有 FIDL 測試。(測試也會在 CQ 中執行,因為 //bundles/buildbot/core 包含 //bundles/fidl:tests)。

二進位檔名稱

測試二進位檔名稱通常是以目標名稱為準。舉例來說,test("some_tests") { ... } 目標會產生 some_tests 二進位檔。不過,單一測試通常需要多個目標 (來源集、元件、套件等),且名稱不得重複。因此,本文中的範例會使用 some_tests_bin 等目標名稱,並使用 output_name 參數覆寫二進位檔名稱:

test("some_tests_bin") {
  output_name = "some_tests"
  ...
}

這也適用於 rustc_testgo_test 等。

裝置測試

假設我們有一個 :fidl_foo_tests_bin 目標,會產生 fidl_foo_tests 二進位檔。如要將此內容包裝在套件中,請使用 fuchsia_unittest_package

import("//build/components.gni")

fuchsia_unittest_package("fidl_foo_tests") {
  deps = [ ":fidl_foo_tests_bin" ]
}

現在可以使用 fx test fidl_foo_tests,依套件名稱或元件名稱 (兩者相同) 執行測試。

為每項測試使用不同的套件。如果無關的測試元件是綁定在一個套件中,執行其中一項測試會導致整個套件重建。只有在多個測試元件需要一起測試時,才應將這些元件組合在套件中,例如用戶端和伺服器整合測試。如需範例,請參閱「複雜拓撲和整合測試」。

如果測試需要 fuchsia_unittest_component 預設值以外的任何元件功能、服務等,您必須編寫元件資訊清單檔案:

# BUILD.gn
import("//build/components.gni")

fuchsia_unittest_package("fidl_foo_tests") {
  manifest = "meta/fidl_foo_tests.cml"
  deps = [ ":fidl_foo_tests_bin" ]
}

# meta/fidl_foo_tests.cml
{
    program: {
        "binary": "bin/fidl_foo_tests"
    },
    use: [
      {
        protocol: [
          "fuchsia.logger.LogSink",   # some example services
          "fuchsia.process.Launcher"
        ]
      }
    ]
}

如要進一步瞭解套件和元件範本,請參閱「建構元件」。

主機測試

假設我們有一個 :fidl_bar_tests_bin 目標,會產生 fidl_bar_tests 二進位檔。我們必須確保 GN 位於 $host_toolchain,才能達到該目標,否則系統會嘗試為 Fuchsia 建構 GN:

groups("tests") {
  testonly = true
  deps = [ ":fidl_bar_tests_bin($host_toolchain)" ]
}

(請一律將 ($host_toolchain) 放在 BUILD.gn 檔案的 tests 群組中,而非 //bundles/fidl:tests)。

這會建立名為 host_x64/fidl_bar_tests 的 test_spec 項目,最終會出現在 out/default/tests.json 中:

{
  "command": [ "host_x64/fidl_bar_tests", "--test.timeout", "5m" ],
  "cpu": "x64",
  "label": "//PATH/TO/BAR:fidl_bar_tests_bin(//build/toolchain:host_x64)",
  "name": "host_x64/fidl_bar_tests",
  "os": "linux",
  "path": "host_x64/fidl_bar_tests",
  "runtime_deps": "host_x64/gen/PATH/TO/BAR/fidl_bar_tests_bin.deps.json"
}

由於 tests.json 中的「name」欄位,因此可以執行 fx test fidl_bar_tests

主機/裝置測試

在主機和裝置上執行的測試分為兩類。在第一個類別中,測試目標只會建構任一工具鍊。例如:

import("//build/components.gni")

rustc_test("fidl_rust_conformance_tests_bin") {
  output_name = "fidl_rust_conformance_tests"              # host test name
  ...
}

fuchsia_unittest_package("fidl_rust_conformance_tests") {  # device test name
  deps = [ ":fidl_rust_conformance_tests_bin" ]
}

group("tests") {
  testonly = true
  deps = [
    ":fidl_rust_conformance_tests_bin($host_toolchain)",
    ":fidl_rust_conformance_tests",
  ]
}

現在可以透過下列兩種方式執行測試:

  • 裝置:fx test fidl_rust_conformance_tests --device
  • 在主機上:fx test fidl_rust_conformance_tests --host

在第二類中,裝置和主機測試會共用原始碼,但兩者差異極大,因此必須由個別目標定義。這需要將主機測試定義包裝在 if (is_host) { ... } 中,避免 GN 抱怨多個目標產生相同輸出內容。例如:

import("//build/components.gni")

source_set("conformance_test_sources") {
  ...
}

test("fidl_hlcpp_conformance_tests_bin") {
  output_name = "fidl_hlcpp_conformance_tests"
  ...
  deps = [
    ":conformance_test_sources",
    ...
  ]
}

if (is_host) {
  test("fidl_hlcpp_conformance_tests_bin_host") {
    output_name = "fidl_hlcpp_conformance_tests"            # host test name
    ...
    deps = [
      ":conformance_test_sources",
      ...
    ]
  }
}

fuchsia_unittest_package("fidl_hlcpp_conformance_tests") {  # device test name
  deps = [ ":fidl_hlcpp_conformance_tests_bin" ]
}

group("tests") {
  testonly = true
  deps = [
    ":fidl_hlcpp_conformance_tests_bin_host($host_toolchain)",
    ":fidl_hlcpp_conformance_tests",
  ]
}

現在,我們可以透過下列兩種方式執行測試:

  • 裝置:fx test fidl_hlcpp_conformance_tests --device
  • 在主機上:fx test fidl_hlcpp_conformance_tests --host

Rust 單元測試

Rust 程式庫可以定義如下:

rustc_library("baz") {
  with_unit_tests = true
  ...
}

這會自動建立 baz_test 目標,建構 baz_lib_test 二進位檔。請勿使用這個方法,原因有二:

  1. 命名規範規定使用 _tests 後置字元,而非 _test
  2. 這可能會造成混淆,且日後可能會淘汰

請改為編寫個別的 rustc_test 目標,並使用適當名稱:with_unit_tests

rustc_library("baz") {
  ...
}

rustc_test("fidl_baz_tests") {
  ...
}

分組

假設我們有下列測試結構:

  • FIDL Rust
    • 裝置
      • 一致性
      • 整合
    • 主機
      • 一致性

我們應該有葉子的測試目標:

  • fx test fidl_rust_conformance_tests
  • fx test fidl_rust_integration_tests

我們不應為執行各種測試子集建立額外套件。使用 fx test,我們已經可以

  • 執行所有測試:fx test //path/to/fidl/rust
  • 執行所有裝置測試:fx test //path/to/fidl/rust --device
  • 執行所有主機測試:fx test //path/to/fidl/rust --host

參考資料