Fuchsia 測試執行者架構

Fuchsia 元件架構可讓開發人員以各種語言和執行階段建立元件。Fuchsia 自己的程式碼使用多種程式設計語言組合元件,包括 C/C++、Rust、Dart 和 Go。

測試執行器架構會使用元件架構執行器,做為不同測試執行階段與常見的 Fuchsia 通訊協定的整合層,用於啟動測試及接收結果。因此,開發人員可以一手掌握多元包容的設計,讓開發人員能用自己偏好的語言和測試架構,同時讓開發人員可以在各種系統上建構及測試 Fuchsia 應用程式,並指定不同硬體。

測試經理

test_manager 元件負責在 Fuchsia 裝置上執行測試。測試管理員會公開 fuchsia.test.manager.RunBuilder 通訊協定,允許啟動測試套件。

每個測試套件都是以測試管理員的子項的形式啟動。測試套件由測試管理員提供,讓測試套件能夠執行作業,同時維持測試與系統其餘部分之間的區隔。針對執行個體密封測試,我們獲得記錄訊息的能力,但無法與沙箱以外的真實系統資源互動。測試管理員只會使用測試領域的一項能力,也就是測試套件公開的控制器通訊協定。這是為了確保完整性 (測試結果不會受預定沙箱以外的任何項目影響) 和隔離 (測試不會互相影響或系統的其餘部分)。

測試管理員控制器本身會提供給系統中的其他元件,以便將測試執行作業與各種開發人員工具整合。接著,您就能使用 fx testffx 等工具啟動測試。

測試套件通訊協定

測試管理員會使用測試套件通訊協定 fuchsia.test.Suite 控管測試,例如叫用測試案例及收集結果。

測試作者通常不需要實作這個通訊協定。而是改用「測試執行器」。舉例來說,您可以使用 GoogleTest 架構在 C++ 中編寫測試,然後在元件資訊清單中使用 gtest_runner 與 Test Runner Framework 整合。

測試執行者

語言與包含執行階段的架構

測試執行器是測試執行器架構,以及開發人員用來撰寫測試的常用語言和架構之間的可重複使用轉接器。他們會代表測試作者實作 fuchsia.test.Suite 通訊協定,方便開發人員針對所選語言和架構編寫慣用測試。

簡易單元測試的元件資訊清單可透過建構規則產生。為 v2 測試產生的元件資訊清單會根據建構定義納入適當的測試執行元件。舉例來說,依附 GoogleTest 程式庫的測試執行檔會在產生的資訊清單中納入 GoogleTest Runner

測試執行器目錄

下列測試執行器目前可供一般使用:

GoogleTest 執行元件

執行器,適用於使用 GoogleTest 架構以 C/C++ 編寫的測試。這適用於所有使用 GoogleTest 編寫的測試。

支援常見的 GoogleTest 功能,例如停用測試、僅執行指定的測試、多次執行相同測試等。測試會從測試中擷取標準輸出內容、標準錯誤和記錄。

如要使用此執行元件,請將以下內容新增至元件資訊清單:

{
    include: [ "//src/sys/test_runners/gtest/default.shard.cml" ]
}

根據預設,系統會依序執行 GoogleTest 測試案例 (一次一個測試案例)。

GoogleTest (Gunit) 執行元件

執行器,適用於使用 GUnit 架構以 C/C++ 編寫的測試。這適用於使用 GoogleTest gUnit 變種版本編寫的所有測試。

支援常見的 GoogleTest 功能,例如停用測試、僅執行指定的測試、多次執行相同測試等。測試會從測試中擷取標準輸出內容、標準錯誤和記錄。

如要使用此執行元件,請將以下內容新增至元件資訊清單:

{
    include: [ "sys/testing/gunit_runner.shard.cml" ]
}

根據預設,測試案例會依序執行 (一次一個測試案例)。

Rust 執行元件

執行檔,用於以 Rust 程式設計語言編寫的測試,並遵循 Rust 測試慣用語。這適用於所有慣用的 Rust 語言測試 (即使用設定屬性 [cfg(test)] 的模組進行測試)。

支援常見的 Rust 測試功能,例如停用測試、僅執行指定的測試、多次執行相同測試等。測試會從測試中擷取標準輸出內容、標準錯誤和記錄。

如要使用此執行元件,請將以下內容新增至元件資訊清單:

{
    include: [ "//src/sys/test_runners/rust/default.shard.cml" ]
}

根據預設,Rust 測試案例會平行執行,最多一次執行 10 個案例。

Go 測試執行元件

一個執行元件,用於以 Go 程式設計語言編寫的測試,並遵循 Go 測試慣用語。這適用於使用 import "testing" 以 Go 編寫的所有測試。

系統支援常見的 Go 測試功能,例如停用測試、僅執行指定的測試、多次執行相同測試等。測試會從測試中擷取標準輸出內容、標準錯誤和記錄。

如要使用此執行元件,請將以下內容新增至元件資訊清單:

{
    include: [ "//src/sys/test_runners/gotests/default.shard.cml" ]
}

根據預設,Go 測試案例最多可以同時執行 10 個案例。

ELF 測試執行元件

最簡單的測試執行元件 - 等待程式終止,然後回報如果程式傳回零,或程式因任何非零的傳回值而失敗,就會回報測試已通過。

如果您的測試是以 ELF 程式的形式實作 (例如以 C/C++ 編寫的執行檔),但它並非使用現有執行器支援的常見測試架構,且您不想實作量身打造的測試執行元件,請使用這個測試執行元件。

如要使用此執行元件,請將以下內容新增至元件資訊清單:

{
    include: [ "sys/testing/elf_test_runner.shard.cml" ]
}

如果您要樹狀結構內單元測試 GN 範本,且尚未將測試架構搭配專屬的測試執行元件使用,請在建構依附元件中加入以下內容:

fuchsia_unittest_package("my-test-packkage") {
    // ...
    deps = [
        // ...
        "//src/sys/testing/elftest",
    ]
}

控管測試案例的平行執行

使用 fx test 啟動測試時,測試案例可能會依序執行每個測試案例,或同時執行多個測試案例,直到指定的限制為止。預設的平行處理行為是由測試執行元件決定。如要手動控管平行使用測試規格執行的測試案例數量:

fuchsia_test_package("my-test-pkg") {
  test_components = [ ":my_test_component" ]
  test_specs = {
    # control the parallelism
    parallel = 10
  }
}

多次執行測試

如要多次執行測試,請使用:

 fx test --count=<n> <test_url>

如果疊代逾時,就不會再執行疊代。

傳遞引數

測試的自訂引數可以使用 fx test 傳遞:

fx test <test_url> -- <custom_args>

個別測試執行器對這些自訂標記設有限制:

GoogleTest 執行元件

請注意以下已知行為變更:

--gtest_break_on_failure:每個測試案例都是透過不同的程序執行,因此這個標記無法運作。

以下標記會受到限制,如果任何以 fuchsia.test.Suite 的形式傳遞,就測試會失敗,而功能等同於取代這些標記。

  • --gtest_filter - 請改用:
 fx test --test-filter=<glob_pattern> <test_url>

--test-filter 可以指定多次。系統會執行符合任何指定 glob 模式的測試。

  • --gtest_while_run_disable_tests - 請改用:
 fx test --also-run-disabled-tests <test_url>
  • --gtest_repeat:請參閱「多次執行測試」。
  • --gtest_output - 不支援傳送 gtest JSON 輸出。
  • --gtest_list_tests - 不支援列出測試案例。

GoogleTest (Gunit) 執行元件

請注意以下已知行為變更:

--gunit_break_on_failure:每個測試案例都是以不同的程序執行,因此這個標記無法運作。

以下標記會受到限制,如果任何以 fuchsia.test.Suite 的形式傳遞,就測試會失敗,而功能等同於取代這些標記。

  • --gunit_filter - 請改用:
 fx test --test-filter=<glob_pattern> <test_url>

--test-filter 可以指定多次。系統會執行符合任何指定 glob 模式的測試。

  • --gunit_avi_run_disable_tests - 請改用:
 fx test --also-run-disabled-tests <test_url>
  • --gunit_repeat:請參閱「多次執行測試」。
  • --gunit_output - 不支援 gtest json/xml 輸出傳送。
  • --gunit_list_tests - 不支援列出測試案例。

Rust 執行元件

以下標記會受到限制,如果任何以 fuchsia.test.Suite 的形式傳遞,就測試會失敗,而功能等同於取代這些標記。

  • <test_name_matcher> - 請改用:
 fx test --test-filter=<glob_pattern> <test_url>

--test-filter 可以指定多次。系統會執行符合任何指定 glob 模式的測試。

  • --nocapture - 依預設會列印輸出內容。
  • --list - 不支援列出測試案例。

Go 測試執行元件

請注意以下已知行為變更:

-test.failfast:每個測試案例都是以不同的程序執行,因此這個標記只會影響子測試。

下列標記受到限制,而且如果任何以 fuchsia.test.Suite 的形式傳遞,測試就會失敗,但提供的功能可取代這些標記

  • -test.run - 請改用:
 fx test --test-filter=<glob_pattern> <test_url>

--test-filter 可以指定多次。系統會執行符合任何指定 glob 模式的測試。

適用於各種執行階段的跨執行階段測試架構

Fuchsia 的目標是多元包容,舉例來說,開發人員可以透過自身的語言和選擇的執行階段建立元件 (及其測試)。測試執行器架構本身是各語言通用的設計,因此每個測試執行者均專門從事特定程式設計語言或測試執行階段,因此適用於各種語言。任何人都能建立及使用新的測試執行器。

建立新的測試執行器相對簡單,也能在不同執行者之間共用程式碼。舉例來說,GoogleTest 執行元件和 Rust 執行元件共享程式碼,與啟動 ELF 二進位檔有關,但在程式碼中,將指令列引數傳遞至測試及剖析測試結果。

暫時儲存空間

如要在測試中使用臨時儲存空間,請在元件資訊清單中新增以下內容:

{
    include: [ "//src/sys/test_runners/tmp_storage.shard.cml" ]
}

在執行階段,測試將具備 /tmp 的讀取/寫入權限。測試開始時,這個目錄的內容會留空,並會在測試完成後刪除。

未指定自訂資訊清單的測試,改為透過建構系統產生元件資訊清單,可以新增下列依附元件:

fuchsia_unittest_package("foo-tests") {
  deps = [
    ":foo_test",
    "//src/sys/test_runners:tmp_storage",
  ]
}

匯出自訂檔案

如要從測試匯出自訂檔案,請使用 custom_artifacts 儲存空間能力。系統會在測試結束時複製 custom_artifacts 的內容。

如要在測試中使用 custom_artifacts,請在元件資訊清單中加入以下內容:

{
    use: [
        {
            storage: "custom_artifacts",
            rights: [ "rw*" ],
            path: "/custom_artifacts",
        },
    ],
}

在執行階段,測試將具備 /custom_artifacts 的讀取/寫入權限。測試開始時,這個目錄的內容會留空,並會在測試完成後刪除。

請參閱自訂構件測試範例。如要執行該程式碼,請將 //examples/tests/rust:tests 新增至建構作業,然後執行下列指令:

fx test --ffx-output-directory <output-dir> custom_artifact_user

測試結束後,<output-dir> 會包含測試產生的 artifact.txt 檔案。

密封

以下是密封的測試:

  1. 不會使用提供測試根層級父項的任何功能。
  2. 不會解析測試套件以外的任何元件。

除非另有明確說明,否則測試預設為密封。

測試的密封功能

有些功能可以使用,但所有測試並不會違反測試固態:

通訊協定 說明
fuchsia.boot.WriteOnlyLog 寫入核心記錄
fuchsia.logger.LogSink 寫入系統記錄檔
fuchsia.process.Launcher 透過測試套件啟動子程序
fuchsia.diagnostics.ArchiveAccessor 讀取測試中元件的診斷輸出內容

由於這些功能經過仔細規劃,可避免測試影響測試領域或其他測試以外的系統元件行為,因此會保留密封。

如要使用這些功能,必須在測試資訊清單檔案中加入使用宣告:

// my_test.cml
{
    use: [
        ...
        {
            protocol: [
              "fuchsia.logger.LogSink"
            ],
        },
    ],
}

此外,測試也會提供一些預設儲存空間功能,這些功能會在測試執行完成後刪除。

儲存空間功能 說明 路徑
data 隔離資料儲存空間目錄 /data
cache 隔離快取儲存空間目錄 /cache
tmp 隔離的記憶體內暫存儲存空間目錄 /tmp

如要使用這些功能,請在測試的資訊清單檔案中新增使用宣告。

// my_test.cml
{
    use: [
        ...
        {
            storage: "data",
            path: "/data",
        },
    ],
}

這個架構也會為所有元件提供一些功能,且可視需要供測試元件使用。

密封元件解析度

密封測試元件會在使用密封元件解析器的領域中啟動。這個解析器不允許解析測試套件以外的網址。這是強制執行密封的必要條件,因為我們不希望系統或相關套件伺服器中任意元件的可用性影響測試結果。

嘗試解析不在測試套件中的元件時,會發生 PackageNotFound 錯誤,以及 Syslog 中的以下訊息:

failed to resolve component fuchsia-pkg://fuchsia.com/[package_name]#meta/[component_name]: package [package_name] is not in the set of allowed packages...

如要避免發生這個錯誤,請將測試用於測試套件的所有元件納入測試套件。如需相關做法的範例,請參閱這個 CL 或使用子套件

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


fuchsia_test_package("simple_test") {
  test_components = [ ":simple_test_component" ]
  subpackages = [ "//path/to/subpackage:subpackage" ]
}
 // test.cml
 {
...
    children: [
        {
            name: "child",
            url: "subpackage#meta/subpackaged_component.cm",
        },
    ],
...
}

如需使用子套件的範例,請參閱這個 CL

// my_component_test.cml

{
...

    facets: {
        "fuchsia.test": {
            "deprecated-allowed-packages": [ "non_hermetic_package" ],
        },
    },
...
}

非密封測試

這些測試可以存取測試領域以外的部分預先定義功能。經由測試領域之外的非密封測試存取的功能稱為「系統功能」

如要使用系統能力,測試必須明確標記為在非密封的領域中執行,如下所示。

# BUILD.gn (in-tree build rule)

fuchsia_test_component("my_test_component") {
  component_name = "my_test"
  manifest = "meta/my_test.cml"
  deps = [ ":my_test_bin" ]

  # This runs the test in "system-tests" non-hermetic realm.
  test_type = "system"
}

與建構規則整合後,測試即可執行以下程式碼:

fx test <my_test>

適用於樹狀結構外開發人員

ffx test run --realm <realm_moniker> <test_url>

其中 realm_moniker 應替換為 /core/testing:system-tests,如上例。

test_type 可能的值:

說明
chromium Chromium 測試領域
ctf CTF 測試領域
device 裝置測試
drm DRM 測試
starnix Starnix 測驗
system_validation 系統驗證測試
system 舊版非密封領域,可存取部分系統功能。
test_arch 測試架構測試
vfs-compliance VFS 法規遵循測試
vulkan Vulkan 測試

瞭解如何建立您自己的測試領域。

非密封的舊版測試領域

這些是我們在測試管理工具式服務之前建立的舊版測試領域。我們正在著手移植這些領域如果測試需要使用其中一個運作範圍,請明確標示在舊版領域中執行,如下所示。

// my_component_test.cml

{
    include: [
        // Select the appropriate test runner shard here:
        // rust, gtest, go, etc.
        "//src/sys/test_runners/rust/default.shard.cml",

        // This includes the facet which marks the test type as 'starnix'.
        "//src/devices/testing/starnix_test.shard.cml",
    ],
    program: {
        binary: "bin/my_component_test",
    },
    
    use: [
        {
            protocol: [ "fuchsia.vulkan.loader.Loader" ],
        },
    ],
}

資料分割的資訊清單檔案包含下列 facet:

// Copyright 2022 The Fuchsia 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/starnix/tests/starnix_test_common.shard.cml" ],
    expose: [
        {
            protocol: "fuchsia.test.Suite",
            from: "self",
        },
    ],
}

fuchsia.test.type 可能的值:

說明
hermetic 密封領域
chromium-system Chromium 系統測試領域
google Google 測試領域

受限制的記錄檔

根據預設,如果測試記錄的訊息嚴重性為 ERROR 以上,就會失敗。詳情請參閱這份指南

效能

編寫用於啟動程序的測試執行元件時,執行元件需要提供程式庫載入器的實作。

測試執行器通常會在不同程序中啟動個別測試案例,以達到在測試案例之間獲得更高的區隔程度。不過,這可能會產生可觀的效能成本。為減緩此問題,上述測試執行器使用快取載入器服務,減少每個程序啟動的額外負擔。

測試角色

測試領域的元件可能會在測試中扮演各種角色,如下所示:

  • 測試根層級:測試元件樹狀結構頂端的元件。測試網址會識別這個元件,測試管理員會叫用此元件所公開的 fuchsia.test.Suite 以驅動測試。
  • 測試驅動程式庫:實際執行測試的元件,並實作 (直接或透過測試執行器,或使用 fuchsia.test.Suite 通訊協定實作)。請注意,測試驅動程式庫和測試根憑證可能 (但不一定) 是同一個元件:測試驅動程式庫會是測試根目錄的子元件,並再次公開其 fuchsia.test.Suite,例如。
  • 功能提供者:提供測試會運動的元件。此元件可能會提供用於測試能力的「假造」實作,或提供與實際工作環境使用的「實際」實作項目。
  • 測試中的元件:一種元件,可用於測試某些行為並進行測試。這可能與實際工作環境的元件,或是專為模型生產行為而特別編寫的元件。

疑難排解

本節包含在使用測試執行器架構開發測試元件時可能會遇到的常見問題。如果其中一個測試元件無法執行,您可能會看到 fx test 中的錯誤,如下所示:

Test suite encountered error trying to run tests: getting test cases
Caused by:
    The test protocol was closed. This may mean `fuchsia.test.Suite` was not configured correctly.

如要解決這個問題,請參考下列做法:

測試使用了錯誤的測試執行元件

如果您在測試列舉期間遇到這個錯誤,可能是因為您使用的測試執行元件不正確。

舉例來說,Rust 測試檔案可能會在不使用 Rust 測試架構的情況下執行測試 (例如,這是有專屬主要函式的簡易 Rust 二進位檔)。在這種情況下,請將測試資訊清單檔案變更為使用 elf_test_runner

進一步瞭解內建的測試執行器

測試無法將「fuchsia.test.Suite」公開給測試管理員

如果測試根目錄無法從測試根公開 fuchsia.test.Suite,就會發生這種情況。簡單的修正是新增 expose 宣告:

// test_root.cml
expose: [
    ...
    {
        protocol: "fuchsia.test.Suite",
        from: "self",  // If a child component is the test driver, put `from: "#driver"`
    },
],

測試驅動程式庫無法將 fuchsia.test.Suite 公開至根層級

如果 fuchsia.test.Suite 通訊協定未正確公開,測試可能會失敗,並顯示如下的錯誤:

ERROR: Failed to route protocol `/svc/fuchsia.test.Suite` from component
`/test_manager/...`: An `expose from #driver` declaration was found at `/test_manager/...`
for `/svc/fuchsia.test.Suite`, but no matching `expose` declaration was found in the child

如果測試驅動程式庫和測試根層級是不同的元件,測試驅動程式庫也必須向其父項 (測試根層級) 公開 fuchsia.test.Suite

如要解決這個問題,請確認測試驅動程式元件資訊清單包含下列 expose 宣告:

// test_driver.cml
expose: [
    ...
    {
        protocol: "fuchsia.test.Suite",
        from: "self",
    },
],

測試驅動程式庫不使用測試執行元件

測試驅動程式必須使用對應用來撰寫測試的語言和測試架構的適當測試執行器。舉例來說,Rust 測試的驅動程式庫需要下列宣告:

// test_driver.cml
include: [ "//src/sys/test_runners/rust/default.shard.cml" ]

此外,如果測試驅動程式庫是測試根的子項,您必須將測試驅動程式提供給驅動程式庫:

// test_root.cml
offer: [
    {
        runner: "rust_test_runner",
        to: [ "#driver" ],
    },
],

其他資訊