範圍
本文說明如何定義、建構及執行僅存在於 Bazel 圖中的測試。
Fuchsia 支援符合下列規定的 Bazel 主機測試:
這些測試必須只能使用 Bazel 建構,也就是說,測試不依賴先前 Ninja 叫用產生的構件。
這些測試只包含二進位檔和執行階段檔案,可在「未」連線至任何 Fuchsia 裝置或模擬器的相容主機系統上執行。
Fuchsia 也支援 Bazel 裝置測試,這類測試是以 fx_test() 宣告,並在 Fuchsia 裝置或模擬器上執行。這些規則同樣只能使用 Bazel 建構,此外:
- 除了
environments(預設為建構作業的預設測試環境)、build_only、max_log_severity(預設為WARN) 和test_type(用於在非密封測試領域中執行,例如"starnix"或"system") 之外,測試規格欄位 (逾時等) 目前無法依測試自訂。
fxbug.dev/564574581 追蹤 Bazel 中目標測試的剩餘工作。
在 Bazel 中定義 Fuchsia 主機測試
一般 Bazel 測試規則 (例如 rustc_test、go_test) 與 Fuchsia 測試執行工具不相容。fx test 無法查看這些內容,基礎架構建構工具也不會執行這些內容。
因此,如要在 Bazel 中定義與 Fuchsia 相容的主機測試,請按照下列步驟操作:
使用
//build/bazel/rules/host_tests:host_<xxx>_test.bzl中的語言專屬主機測試包裝函式,例如host_rustc_test()、host_go_test()等。這些規則可直接取代一般 Bazel 測試規則,並提供
test_data、test_args等額外引數,讓 Fuchsia 測試執行工具能以正確的執行階段環境和引數叫用這些規則。只有在測試沒有特定語言的包裝函式時,才使用
//build/bazel/rules/host_tests:host_test.bzl的一般host_test()包裝函式。例如:load("//build/bazel/rules/host_tests:host_test.bzl", "host_test") load("@rules_sh//sh:sh_binary.bzl", "sh_binary") sh_binary( name = "test_bin", ... ) host_test( name = "shell_test", binary = ":test_bin", )請勿假設測試二進位檔的執行位置 (例如
out/default),因為測試二進位檔是從確切路徑無法預測的目錄啟動。Bazel 主機測試必須是封閉式,且明確列出所有執行階段依附元件,就像 GN 主機測試一樣。您可以透過下列其中一種機制提供執行階段依附元件:
做為
host_test_data_files()或host_test_data_map()目標的test_data依附元件 (請參閱//build/bazel/rules/host_tests:host_test_data.bzl)。這會讓檔案在執行階段時,於相對於測試執行目錄的固定位置可用,類似於 GNhost_test_data()範本。host_xxx_test()目標上的data屬性。在這種情況下,檔案會新增至測試的執行檔案,並可使用執行檔案程式庫存取。
使用
test_suite()定義測試群組,而非filegroup()。測試套件可能依附於測試和其他測試套件。
將 Bazel 主機測試匯出至 fx test 和基礎架構建構工具
如果只定義 host_xxx_test() 目標,fx test 和基礎架構建構工具將無法看到該目標。
如要讓 fx test 和基礎架構建構工具看到 Bazel 主機測試,請在 GN 中編寫 bazel_test_suite 目標,列出一或多個 Bazel 主機測試或套件的 Bazel 標籤,然後將該目標新增至 GN 中的「tests」群組。例如:
//src/foo/bar/BUILD.bazel:
load("//build/bazel/platforms:constraints.bzl", "HOST_OS_CONSTRAINTS")
test_suite(
name = "host_tests",
host_tests = [
"//src/foo/bar/lib:lib_tests",
"//src/foo/bar/util:util_tests",
]
target_compatible_with = HOST_OS_CONSTRAINTS,
)
//src/foo/bar/BUILD.gn:
import("//build/bazel/bazel_test_suite.gni")
bazel_test_suite("bazel_tests") {
host_tests = [
# This references the target in BUILD.bazel.
"//src/foo/bar:host_tests",
]
}
group("tests") {
deps = [
":bazel_tests",
...
]
}
這樣一來,Bazel 測試的項目就會顯示在 out/default/tests.json 中。如有疑慮,請在 fx set 或 fx gen 後檢查這個檔案的內容。Bazel 測試的 label 欄位開頭為 @。
在開發期間,您可以在定義測試目標後,先建構並執行測試目標,再按照上述方式匯出。請參閱下方的「直接使用 Bazel 執行測試」一節。
最佳做法
建議避免 Bazel test_suite 目標的深度巢狀結構。請改為針對每個原始碼子區域 (例如主機工具或 Fuchsia 元件),只使用一個 test_suite Bazel 目標和一個對應的 bazel_test_suite GN 目標。
在 Bazel 中定義 Fuchsia 裝置測試
裝置測試是 Fuchsia 套件中的元件,與 GN 建構作業中的元件完全相同。使用 fx_test_component() 宣告測試元件,使用 fx_package() 封裝,並使用 fx_test() 將其公開為測試:
//src/foo/bar/BUILD.bazel:
load("//build/bazel/rules/packages:fx_packaged_binary.bzl", "fx_packaged_binary")
load(
"//build/bazel/rules/components:fx_component.bzl",
"fx_component_manifest",
"fx_test_component",
)
load("//build/bazel/rules/packages:fx_package.bzl", "fx_package")
load("//build/bazel/rules/testing:fx_test.bzl", "fx_test")
fx_packaged_binary(
name = "bar_test_packaged_bin",
testonly = True,
binary = ":bar_test_bin",
binary_name = "bar_test",
)
fx_component_manifest(
name = "bar_test_manifest",
testonly = True,
component_name = "bar_test",
includes = ["//src/sys/test_runners/gtest:default.shard.cml"],
manifest = "meta/bar_test.cml",
)
fx_test_component(
name = "bar_test_component",
compiled_manifest = ":bar_test_manifest",
component_name = "bar_test",
deps = [":bar_test_packaged_bin"],
)
fx_package(
name = "bar_tests_package",
package_name = "bar_tests",
test_components = [":bar_test_component"],
)
fx_test(
name = "bar_tests",
package = ":bar_tests_package",
# Optional: defaults to the build's default test environments when omitted.
environments = [
"//build/testing/environments:aemu_env",
"//build/testing/environments:nuc11_env",
],
)
test_components 中的每個元件都會成為獨立測試,名稱為套件網址,例如 fuchsia-pkg://fuchsia.com/bar_tests#meta/bar_test.cm。
如要自訂測試執行的環境指定目標,請將 environments 傳遞至 fx_test(),做為環境指定目標清單。//build/testing/environments 在 //build/testing/environments.gni 中為每個個別環境設定一個目標,並使用相同名稱 (例如 :emu_env 或 :nuc11_env)。如果省略 environments,測試會在建構作業的預設測試環境 (GN 的 basic_envs) 中執行,就像未設定 environments 的 GN 測試一樣。如要建構測試而不執行,請改為設定 build_only = True。
如要將基礎架構設定 (例如標記) 新增至其中一個環境,請使用 fx_test_environment_variant():
load(
"//build/bazel/rules/testing:fx_test_environment.bzl",
"fx_test_environment_variant",
)
fx_test_environment_variant(
name = "nuc11_isolated_env",
base = "//build/testing/environments:nuc11_env",
env_tags = ["e2e-isolated"],
)
新環境應該很少見;大多數測試都應使用 //build/testing/environments 中的現有環境之一。如需新環境,請將其新增至 //build/testing/environments.gni 和 //build/testing/environments/BUILD.bazel,而不是在測試旁使用 fx_test_environment() 定義。環境的 Swarming 維度是與基礎架構互動的介面,因此會集中在同一個位置。
如要在非密封測試領域 (例如 "starnix"、"system" 或 "vulkan") 執行測試,請將 test_type 傳遞至 fx_test_component() (或傳遞至 fx_test(),套用至套件中的所有測試元件)。有效的 test_type 值與 //build/components/fuchsia_test_component.gni 中的 type_moniker_map 相符。
將 Bazel 裝置測試匯出至 fx test 和基礎架構建構工具
與主機測試一樣,裝置測試必須列在 GN bazel_test_suite() 目標中,並使用 target_tests 參數,而非 host_tests:
//src/foo/bar/BUILD.gn:
import("//build/bazel/bazel_test_suite.gni")
bazel_test_suite("bazel_tests") {
target_tests = [
# This references the target in BUILD.bazel.
"//src/foo/bar:bar_tests",
]
}
裝置測試與主機測試分開追蹤,因為裝置測試是在 fuchsia_platform Bazel 設定中查詢和建構,而非主機設定。
除了 tests.json 項目外,這項操作也會寫入 ${root_build_dir}/bazel_test_packages.list,列舉建構圖中所有 Bazel 測試套件的套件資訊清單。完整的 fx build 叫用會將這份清單合併至 all_package_manifests.list,並將其套件發布至 amber-files,而 fx test 也會在視需要建構特定測試目標時直接合併。
在兩個建構系統中測試程式庫
如果程式庫由兩個建構系統中的目標使用,請在兩個建構系統中執行測試。當程式庫的使用者僅限 Bazel 時,請只在 Bazel 中執行測試。僅在 Bazel 中建構的目標測試 (例如遷移的主機工具、沒有剩餘 GN 使用者的程式庫,以及未將程式庫匯出至 GN 的分葉整合測試套件),只會在 Bazel 中執行。
遷移至 Bazel 的程式庫會保留在 GN 中 (通常由 bazel2gn 產生),直到沒有 GN 目標依附於該程式庫為止。在此期間,程式庫的 GN 和 Bazel 建構作業可能會有差異,只有測試才能發現。編譯器旗標、建構變化版本 (例如清理工具) 和僅限 GN 的 bazel2gn 覆寫,在兩個建構系統之間都有所不同。部分屬性也會在翻譯期間遭到捨棄,而 GN 目標是未遷移的依附元件實際連結的目標。在兩個建構系統中執行每項測試,會耗費額外的建構時間和測試容量,但只有在程式庫同時有兩個建構系統的使用者時,才會發生這種情況。
實際應用:
在 Bazel 中定義測試,並讓
bazel2gn產生 GN 副本。遷移由bazel2gn產生BUILD.gn的程式庫時,請一併遷移測試,且不要在測試中加入# @bazel2gn:skip。bazel2gn會轉譯rustc_test()、go_test()、host_rustc_test()、host_go_test()、fx_unittest_package(),以及 Rust 規則的with_unit_tests、with_host_unit_tests和test_deps屬性。按照上述說明,使用bazel_test_suite()匯出 Bazel 測試,並在列出原始 GN 測試的 GN 測試群組中,列出產生的 GN 副本。裝置測試套件在 GN 中有不同的名稱。GN 測試套件及其 Bazel 副本會發布至同一個 Package Repository,因此不能共用套件名稱。
bazel2gn會將-gn附加至fx_unittest_package()的 GN 副本套件名稱,而 Bazel 測試會保留原始名稱,因此遷移時測試網址不會變更。如果
bazel2gn還無法轉譯測試的套件,而您想繼續在 GN 中執行測試,請使用bazel2gn轉譯測試二進位檔,並在BUILD.gn中## BAZEL2GN SENTINEL的上方,使用package_name = "<name>-gn"手動編寫 GN 測試套件。這適用於使用fx_unittest_package(resources = [...])的裝置測試 (將# @bazel2gn:skip放在fx_unittest_package()和fuchsia_package_resource_group()上,而非測試二進位檔),或自訂.cml資訊清單的明確fx_package()和fx_test_component()目標 (bazel2gn已忽略沒有# @bazel2gn:skip的目標)。何時應使用
# @bazel2gn:skip選擇不採用。根據預設,如果bazel2gn可以自動轉譯程式庫的測試,請繼續在兩個建構系統中執行這些測試。在測試目標或測試屬性上加上# @bazel2gn:skip(並附上簡短註解說明原因),且只在 Bazel 中執行,是可行的做法:- 受測目標只會在 Bazel 中建構 (例如,同一個
BUILD.bazel檔案中的二進位檔或程式庫,本身具有# @bazel2gn:skip)。 bazel2gn不支援測試的規則或屬性 (例如cc_test()、host_cc_test()、host_py_test()、host_shac_test()、host_test()、具有data/test_data/test_label的主機測試、具有resources的fx_unittest_package(),或具有跨套件srcs/crate或衝突rustc_env的rustc_test()),且在哨兵上方維護手寫 GN 測試目標不值得額外增加複雜度。- 在兩個建構系統中執行或維護測試的成本,高於僅限 GN 迴歸的風險。請根據下列條件評估取捨:
- 目標同等性:程式庫的
BUILD.bazel是否在依附元件、設定或編譯器旗標上使用# @bazel2gn:raw_overwrite、# @bazel2gn:path_overwrite或# @bazel2gn:skip,或者是否為在 GN 中跨多個設定編譯的 IDK/SDK 程式庫?如果產生的 GN 目標是 Bazel 目標的 1:1 轉譯,Bazel 測試就已經執行相同的程式碼。 - 未遷移的 GN 依附元件:程式庫是否依附於經常變更的 GN 專屬目標?預先提交受影響測試分析不會從 GN 目標追蹤到 Bazel 測試,因此變更僅限 GN 的依附元件不會在預先提交中觸發僅限 Bazel 的測試。
- 測試權重:快速、獨立的單元測試很容易鏡像;緩慢或耗用大量資源的整合測試 (或其餘 GN 使用者正在積極遷移的程式庫) 適合僅在 Bazel 中執行。
- 受測目標只會在 Bazel 中建構 (例如,同一個
如果 Bazel 還無法表示測試,請將測試保留在
## BAZEL2GN SENTINEL上方的 GN 中,並在註解中說明原因。例如端對端測試、需要 Ninja 生成輸入內容的測試、bootfs_test(),以及跨多個 C++ 標準編譯的sdk_cpp_unittest_package()。移除 GN 副本和程式庫。當程式庫的最後一位 GN 使用者遷移至 Bazel 時,請刪除程式庫的 GN 定義及其測試,並從列出 GN 測試的 GN 測試群組中移除 GN 測試。
執行 Fuchsia Bazel 裝置測試
匯出後,請按照 GN 測試的方式,透過套件網址或套件名稱執行 Bazel 裝置測試:
fx test fuchsia-pkg://fuchsia.com/bar_tests#meta/bar_test.cmfx test 會直接叫用 Bazel (做為 fx build --fuchsia_platform bazel_label) 建構測試,然後在執行測試前發布 bazel_test_packages.list 中列出的套件資訊清單。與 GN 裝置測試一樣,您必須執行套件伺服器,並連結裝置或模擬器。
執行 Fuchsia Bazel 主機測試
本機測試
使用 fx test 執行測試
如要使用 fx test 執行 Bazel 主機測試,請務必按照上述「將 Bazel 主機測試匯出至 fx test 和基礎架構建構工具」一節中的操作說明匯出測試。
匯出測試後,請使用 fx test --host <name_or_label> 在本機建構及執行 Bazel 主機測試,方法與 GN 定義的主機測試相同。
唯一的差別在於,測試會直接叫用 Bazel,完全略過 Ninja,依需求建構。
使用 fx test 時,請注意以下事項:
<name_or_label>將採用模糊比對,而fx test則會在情況不明確時回報多個候選項目。<name_or_label>無法參照 Bazeltest_suite()標籤,就像無法參照 GNgroup()標籤一樣。
如要進一步瞭解如何使用 fx test,請參閱 fx 測試的使用手冊。
直接使用 fx bazel test 執行測試
您可以使用 fx bazel test --config=host <target_pattern>,直接透過 Bazel 在本機建構及執行測試。這不需要額外的管道,就能讓 fx test 看到測試,如上一節所述,並具有下列優點:
<target_pattern>是一組 Bazel 目標模式,可比對「指定套件中的所有測試目標」,在這種情況下,Bazel 會自動略過非測試目標。<target_pattern>可比對test_suite()標籤,方便在開發期間重複執行一組主機測試。bazel test會快取測試結果,且只會重新執行自上次叫用後,受最新變更影響的測試。(使用
--nocache_test_results旗標即可停用)。<target_pattern>可以參照tests.json中未顯示的標籤。 在開發期間新增測試時,這項功能非常實用。
基礎架構測試
只有 tests.json 中列出的主機測試,才能在基礎架構測試機器人上啟動。這同樣適用於 GN 和 Bazel 主機測試。因此,您必須按照上述「將 Bazel 主機測試匯出至 fx test 和基礎架構建構工具」一節,確保這些測試在 tests.json 中顯示,否則基礎架構不會持續測試這些測試。
Infra 使用 fint 建構 Bazel 測試,該指令會在執行 ninja 後直接呼叫 bazel build,建構 bazel_test_suite GN 目標中列出的所有 Bazel 測試。
在基礎架構測試執行工具上執行 GN 和 Bazel 定義的主機測試,或收集結果時,兩者之間沒有明確區別。具體來說,基礎架構不會使用 bazel test 執行測試。而是直接執行 bazel build 產生的二進位檔,就像執行 Ninja 的主機測試一樣。
偵錯符號傳播
使用偵錯符號編譯的主機測試二進位檔必須註冊未去除的 ELF 二進位檔,工具才能符號化回溯追蹤,並關聯 LLVM 程式碼涵蓋率設定檔 (-profile-correlate=binary)。
如需 Fuchsia 如何管理 Bazel 偵錯符號的一般背景資訊,請參閱偵錯符號產生的技術附註。
使用偵錯符號編寫主機測試規則
一般 host_test() 規則會在 FuchsiaHostTestInfo 供應器中公開 unstripped_binary 欄位:
如果未明確設定
unstripped_binary,host_test()會檢查基礎binary屬性:- 如果
binary提供DebugPackageInfo(C++ 目標),則會使用DebugPackageInfo.unstripped_file。 - 如果
binary提供CrateInfo(Rust 目標),則會使用CrateInfo.output。
- 如果
如果測試使用包裝函式指令碼啟動器 (例如
host_rustc_test()中的rust_test_parser),巨集「必須」透過unstripped_binary明確轉送原始未去除的二進位檔目標:host_test( name = name, binary = wrapper_script, unstripped_binary = ":" + binary_name, ... )
傳播至基礎架構和 debuginfod
將 Bazel 主機測試匯出至 GN 建構作業時:
測試查詢:
bazel_tests_utils.py使用//build/bazel/starlark/FuchsiaHostTestInfo.cquery執行bazel cquery,為所有已註冊的主機測試擷取unstripped_binary的 execroot 路徑。主機測試資訊清單:路徑會相對於 Ninja 建構目錄進行正規化,並寫入
${root_build_dir}/bazel_host_tests.debug_symbols.json。GN 建構 API 整合:
BUILD.gn中的//:bazel_host_test_suites會附加指向bazel_host_tests.debug_symbols.json的debug_symbol_manifests中繼資料,由build_api_module("debug_symbols")目標使用。Artifactory 上傳:在 CI/CQ 建構作業中,Artifactory 會處理
debug_symbols.json,並將未去除的 ELF 二進位檔上傳至雲端儲存空間 / debuginfod 伺服器,讓covargs等工具解析建構 ID。
本機開發和 fx coverage
在本機執行涵蓋率時,未提交的二進位檔會產生新的建構 ID,而這些 ID 不會出現在 debuginfod 伺服器上。
為支援本機符號關聯,fx coverage 會執行:
python3 ${FUCHSIA_DIR}/build/bazel/scripts/copy_bazel_debug_symbols.py ${FUCHSIA_BUILD_DIR}
這個指令碼會讀取 bazel_host_tests.debug_symbols.json、計算每個 ELF 二進位檔的 GNU 建構 ID,並將未去除的二進位檔複製或符號連結至 ${root_build_dir}/.build-id/xx/yyyyyyyy.debug。ffx coverage 和 llvm-profdata 等工具會透過 .symbol-index.json 解析這些二進位檔。
常見問題
Bazel 測試相容性
如果 test_suite() (或其任何依附元件) 中的任何測試與目前設定不相容 (例如使用 target_compatible_with),系統會在評估套件時略過該測試,而不會導致建構失敗。
在指令列使用 //... 和 :all 等模式時,也適用同樣的規則。
詳情請參閱不相容目標的處理方式。
執行 test_suite() 或使用目標模式時,開發人員必須確認相關測試回報為 PASSED (而非 SKIPPED)。