測試執行環境的完整規格。
背景
Bazel BUILD 語言包含規則,可用於定義多種語言的自動化測試計畫。
使用 bazel test
執行測試。
使用者也可以直接執行測試二進位檔。這是允許行為,但不為其背書,例如這類叫用未能遵循下述規定。
測試應密封,意即只須存取已宣告依附元件的資源。如果測試不作密封,就無法提供過去可重現的結果。這可能是重大發現的重大問題 (判斷哪些變更破壞了測試)、版本工程稽核能力,以及測試資源隔離 (自動化測試架構並不是 DDOS 伺服器,因為有些測試會相互通訊,因此不需要透過 DDOS 執行)。
目標
本頁的目標是為 Bazel 測試的正式行為正式建立執行階段環境。此外,它也會對測試執行器和建構系統強制執行要求。
測試環境規格可協助測試作者避免依賴未指定的行為,讓測試基礎架構更有彈性地變更實作項目。這個規範會鎖緊一些目前允許許多測試通過,但由於其密封、確定性和重性不合,因此仍能通過許多測試。
這個網頁旨在兼具規範與權威性。如果這個規格與測試執行器的實作行為不一致,則以指定的規格為優先。
建議規格
「必須」、「不得」、「必要」、「應」、「不應」、「應該」、「不應該」、「建議」、「可能」和「選用」等關鍵字均按照 IETF RFC 2119 的說明處理。
測試目的
Bazel 測試的目的是確認已檢查到存放區的來源檔案的部分屬性。(在本頁中,「來源檔案」包含測試資料、黃金輸出內容,以及保留在版本管控的所有其他項目)。某位使用者編寫測試以斷言要維護的不變性。其他使用者稍後會執行測試,檢查不變體是否損毀。如果測試依附於來源檔案以外的任何變數 (非密封),其值會降低,因為後續的使用者無法在測試停止時確定變更是否發生錯誤。
因此,測試結果必須僅取決於:
- 測試具有宣告依附元件的來源檔案
- 且測試已宣告依附元件的建構系統產品
- 測試執行器保證行為保持不變的資源
目前系統不會強制執行這類行為。然而,測試執行器保留日後可新增這類強制執行的權利。
建構系統的角色
測試規則類似於二進位規則,每個規則都必須產生可執行的程式。針對某些語言,這是一個虛設常式程式,結合了特定語言與特定語言的結合與測試程式碼。測試規則也必須產生其他輸出內容。除了主要測試執行檔外,測試執行器也需要執行檔案資訊清單,以及應在執行階段提供給測試的輸入檔案,且可能需要測試的類型、大小和標記資訊。
建構系統可能會使用執行檔案來傳送程式碼和資料。(這可做為最佳化措施,藉由跨測試共用檔案來縮減每個測試二進位檔,例如透過使用動態連結)。建構系統應確保產生的執行檔會透過測試執行器提供的執行檔案映像檔載入這些檔案,而非以硬式編碼參照來源或輸出樹狀結構中的絕對位置。
測試執行者的角色
從測試執行器的視角,每個測試都是可使用 execve()
叫用的程式。執行測試的方式可能還有其他方式,例如 IDE 可能會允許在處理過程中執行 Java 測試。不過,以獨立程序執行測試的結果必須視為權威性。如果測試程序在執行完成,並以 0 的結束代碼正常終止,就表示測試已通過。任何其他結果都視為測試失敗。特別要注意的是,將任何字串 PASS
或 FAIL
寫入 stdout 對測試執行器沒有顯著性。
如果測試的執行時間過長、超過資源限制,或測試執行器偵測到禁止的行為,可以選擇終止測試,並將執行視為失敗。執行器不得將測試回報為通過測試程序,或其任何子項後通過。
整個測試目標 (而非個別方法或測試) 有一定的時間才能執行。測試時間限制是根據其 timeout
屬性而定,詳情請參閱下表:
逾時 | 時間限制 (秒) |
---|---|
short | 60 |
中度篩選 | 300 |
long | 900 |
永恆族 | 3600 |
未明確指定逾時的測試會根據測試的 size
默示一個隱含,如下所示:
size | 隱含逾時標籤 |
---|---|
small | short |
媒介 | 中度篩選 |
large | long |
巨大 | 永恆族 |
沒有明確逾時設定的「大型」測試會分配到 900 秒執行。逾時為「短」的「中」測試將分配到 60 秒。
與 timeout
不同,size
也會另外判定在本機執行測試時,其他資源 (例如 RAM) 的預期用量高峰,詳情請參閱常見定義。
size
和 timeout
標籤的所有組合都符合規定,因此可能會宣告「大量」測試設為「短」。我想它會快速做到一些非常糟糕的事情
無論逾時狀況為何,測試都有可能快速傳回結果。測試不會因過度逾時而受到懲罰,但可能會發出警示:一般來說,請盡可能將逾時設定得越緊越好,因為這樣不會造成任何不穩定性。
如果在已知緩慢的情況手動執行,可以使用 --test_timeout
Bazel 標記覆寫測試逾時。--test_timeout
值以秒為單位。舉例來說,--test_timeout=120
會將測試逾時設為兩分鐘。
以下為測試逾時的建議下限:
逾時 | 最短時間 (秒) |
---|---|
short | 0 |
中度篩選 | 30 |
long | 300 |
永恆族 | 900 |
舉例來說,如果「中等」測試在 5.5 秒內完成,請考慮設定 timeout =
"short"
或 size = "small"
。如果使用 bazel --test_verbose_timeout_warnings
指令列選項,系統會顯示指定大小過大的測試。
測試大小和逾時時間會依這裡的規格在 BUILD 檔案中指定。如果未指定,則測試的大小會預設為「中」。
如果測試的主要程序結束,但部分子項仍在執行,測試執行器應將執行作業視為已完成,並根據從主要程序觀察到的結束程式碼,將其視為成功或失敗。測試執行工具可能會終止任何延遲程序。測試不應以這種方式洩漏程序。
測試資料分割
您可透過測試資料分割來平行測試。請參閱 --test_sharding_strategy
和 shard_count
來啟用測試資料分割。啟用資料分割時,測試執行器會在每個資料分割啟動時啟動。環境變數 TEST_TOTAL_SHARDS
是資料分割數量,TEST_SHARD_INDEX
則是資料分割索引,從 0 開始。執行器會根據這項資訊來選取要執行的測試,例如使用循環制策略。並非所有測試執行器都支援資料分割。如果執行器支援資料分割,則必須建立或更新 TEST_SHARD_STATUS_FILE
指定檔案的上次修改日期。否則,如果 --incompatible_check_sharding_support
已啟用,Bazel 就會在資料分割失敗時失敗。
初始條件
執行測試時,測試執行器必須建立特定的初始條件。
測試執行器必須使用 argv[0]
中測試執行檔的路徑叫用每項測試。這個路徑必須是相對於測試目前目錄 (位於執行檔案樹狀結構) 的相對路徑。除非使用者明確要求,測試執行工具不應將任何其他引數傳遞至測試。
初始環境區塊應按以下方式組成:
變數 | 值 | 狀態 |
---|---|---|
HOME |
值 $TEST_TMPDIR |
建議 |
LANG |
未設定 | 必填 |
LANGUAGE |
未設定 | 必填 |
LC_ALL |
未設定 | 必填 |
LC_COLLATE |
未設定 | 必填 |
LC_CTYPE |
未設定 | 必填 |
LC_MESSAGES |
未設定 | 必填 |
LC_MONETARY |
未設定 | 必填 |
LC_NUMERIC |
未設定 | 必填 |
LC_TIME |
未設定 | 必填 |
LD_LIBRARY_PATH |
包含共用程式庫的目錄清單 (以冒號分隔) | 選填 |
JAVA_RUNFILES |
值 $TEST_SRCDIR |
淘汰的 |
LOGNAME |
值 $USER |
必填 |
PATH |
/usr/local/bin:/usr/local/sbin:/usr/bin:/usr/sbin:/bin:/sbin:. |
建議 |
PWD |
$TEST_SRCDIR/workspace-name |
建議 |
SHLVL |
2 |
建議 |
TEST_INFRASTRUCTURE_FAILURE_FILE |
可寫入目錄中私人檔案的絕對路徑 (這個檔案只能用來回報來自測試基礎架構的錯誤,而非用於回報測試不穩定錯誤的一般機制。在此情況下,測試基礎架構將定義為非測試專用的系統或程式庫,但可能因故障而造成測試失敗。第一行是導致失敗的測試基礎架構元件名稱,第二行是使用者可理解失敗的錯誤說明。系統會忽略多行內容)。 | 選填 |
TEST_LOGSPLITTER_OUTPUT_FILE |
可寫入目錄中私人檔案的絕對路徑 (用於寫入 LogSplitter protobuffer 記錄) | 選填 |
TEST_PREMATURE_EXIT_FILE |
可寫入目錄中私人檔案的絕對路徑 (用於擷取對 exit() 的呼叫) |
選填 |
TEST_RANDOM_SEED |
如果使用 --runs_per_test 選項,則 TEST_RANDOM_SEED 會設為每次測試執行作業的 run number (從 1 開始)。 |
選填 |
TEST_RUN_NUMBER |
如果使用 --runs_per_test 選項,則 TEST_RUN_NUMBER 會設為每次測試執行作業的 run number (從 1 開始)。 |
選填 |
TEST_TARGET |
要測試的目標名稱 | 選填 |
TEST_SIZE |
測試 size |
選填 |
TEST_TIMEOUT |
以秒為單位的測試 timeout |
選填 |
TEST_SHARD_INDEX |
資料分割索引 (如有使用 sharding ) |
選填 |
TEST_SHARD_STATUS_FILE |
表示支援 sharding 的檔案路徑 |
選填 |
TEST_SRCDIR |
執行檔案樹狀結構基地的絕對路徑 | 必填 |
TEST_TOTAL_SHARDS |
總計 shard count (如果使用 sharding ) |
選填 |
TEST_TMPDIR |
私人可寫入目錄的絕對路徑 | 必填 |
TEST_WORKSPACE |
本機存放區的工作區名稱 | 選填 |
TEST_UNDECLARED_OUTPUTS_DIR |
私人可寫入目錄的絕對路徑 (用於寫入未宣告的測試輸出內容)。系統會壓縮寫入 TEST_UNDECLARED_OUTPUTS_DIR 目錄的所有檔案,並新增至 bazel-testlogs 下的 outputs.zip 檔案。 |
選填 |
TEST_UNDECLARED_OUTPUTS_ANNOTATIONS_DIR |
私人可寫入目錄的絕對路徑 (用於寫入未宣告的測試輸出註解 .part 和 .pb 檔案)。 |
選填 |
TEST_WARNINGS_OUTPUT_FILE |
可寫入目錄中私人檔案的絕對路徑 (用於寫入測試目標警告) | 選填 |
TESTBRIDGE_TEST_ONLY |
--test_filter 的值 (如有指定) |
選填 |
TZ |
UTC |
必填 |
USER |
值 getpwuid(getuid())->pw_name |
必填 |
XML_OUTPUT_FILE |
應寫入測試結果 XML 輸出檔案的位置。 否則,Bazel 會產生預設的 XML 輸出檔案,並將測試記錄包裝成測試動作。XML 結構定義是以 JUnit 測試結果結構定義為基礎。 | 選填 |
BAZEL_TEST |
表示測試執行檔是由 bazel test 驅動 |
必填 |
環境可能包含其他項目。測試不應依附於上述任何環境變數的存在狀態、不存在或值。
初始工作目錄應為 $TEST_SRCDIR/$TEST_WORKSPACE
。
未指定目前的程序 ID、程序群組 ID、工作階段 ID 和父項程序 ID。這個程序不一定是程序群組領導者或工作階段領導者。過程中不一定有控制端子。程序可能有零或多個執行中或未未經移除的子項程序。當測試程式碼取得控制權時,此程序不應有多個執行緒。
檔案描述元 0 (stdin
) 應開放讀取,但未指定附加的內容。測試不得讀取資料。檔案描述元 1 (stdout
) 和 2 (stderr
) 應開放寫入,但未指定附加的項目。可以是終端機、管道、一般檔案,或任何可以寫入字元的任何字元。他們可能會共用已開啟檔案資料表中的項目 (表示他們無法單獨搜尋)。測試不應繼承其他開啟的檔案描述元。
初始遮罩應為 022
或 027
。
沒有待處理的鬧鐘或間隔計時器。
已封鎖信號的初始遮罩應為空白。所有信號都應設為其預設動作。
初始資源限制 (軟性與困難) 應設定如下:
資源 | 限制 |
---|---|
RLIMIT_AS |
無限制 |
RLIMIT_CORE |
未指定 |
RLIMIT_CPU |
無限制 |
RLIMIT_DATA |
無限制 |
RLIMIT_FSIZE |
無限制 |
RLIMIT_LOCKS |
無限制 |
RLIMIT_MEMLOCK |
無限制 |
RLIMIT_MSGQUEUE |
未指定 |
RLIMIT_NICE |
未指定 |
RLIMIT_NOFILE |
至少 1024 |
RLIMIT_NPROC |
未指定 |
RLIMIT_RSS |
無限制 |
RLIMIT_RTPRIO |
未指定 |
RLIMIT_SIGPENDING |
未指定 |
RLIMIT_STACK |
無限制,或是 2044 KB <= rlim <= 8192 KB |
未指定初始程序時間 (由 times()
傳回) 和資源使用率 (由 getrusage()
傳回)。
未指定初始排程政策和優先順序。
主機系統的角色
除了由測試執行工具直接控制使用者情境之外,執行測試的作業系統也必須滿足特定屬性,測試執行作業才有效。
檔案系統
在測試中觀察到的根目錄不一定是實際的根目錄。
應掛接 /proc
。
所有建構工具都必須位於本機安裝所使用的 /usr
底下的絕對路徑。
開頭為 /home
的路徑可能無法使用。測試不應存取任何這類路徑。
應可寫入 /tmp
,但測試應避免使用這些路徑。
測試不得假設所有常數路徑可供專屬用途使用。
測試不得假設任何掛接的檔案系統都會啟用時間。
使用者和群組
必須具備使用者身分、無主體和單元測試。群組根目錄、無人和工程成員都必須存在。
測試必須以非超級使用者的身分執行。實際且有效的使用者 ID 必須相等;同樣地,群組 ID 也須相同。此外,系統不會指定目前的使用者 ID、群組 ID、使用者名稱和群組名稱。未指定一組補充群組 ID。
目前的使用者 ID 和群組 ID 必須具備對應名稱,可透過 getpwuid()
和 getgrgid()
擷取。補餘群組 ID 的情況則可能有所不同。
目前的使用者必須擁有主目錄。該功能可能無法寫入。測試不得嘗試寫入。
網路
未指定主機名稱。這個欄位不一定包含點號。您必須提供目前主機的 IP 位址,才能解析主機名稱。還是應該在第一個點之後解析主機名稱。主機名稱 localhost 必須解析。
其他資源
測試至少會授予一個 CPU 核心。也有其他可以使用,但不保證一定如此。未指定這個核心的其他效能面向。您可以將「cpu:n」標記 (n 為正數) 新增至測試規則,藉此增加預留項目的 CPU 核心數量。如果機器的 CPU 核心總數少於要求數量,Bazel 仍會執行測試。如果測試使用資料分割,則每個資料分割都會保留此處指定的 CPU 核心數量。
測試可能會建立子程序,但不會處理群組或工作階段。
測試可使用的輸入檔案數量有限制。這項限制隨時可能變動,但目前位於數萬筆輸入的範圍。
時間與日期
未指定目前的時間和日期。未指定系統時區。
且 Windows Windows 裝置可能無法使用。需要 X 伺服器的測試應啟動 Xvfb。
測試與檔案系統的互動
除非另有指定,測試環境變數中指定的所有檔案路徑都指向本機檔案系統中的某個位置。
測試應只在 $TEST_TMPDIR
和 $TEST_UNDECLARED_OUTPUTS_DIR
指定的目錄中建立檔案 (如果已設定)。
這些目錄一開始會沒有任何內容。
測試不得嘗試移除、chmod 或以其他方式更改這些目錄。
這些目錄可能是符號化連結。
未指定 $TEST_TMPDIR/.
的檔案系統類型。
測試也可能會將 .part 檔案寫入 $TEST_UNDECLARED_OUTPUTS_ANNOTATIONS_DIR
,為未宣告的輸出檔案加上註解。
在極少數情況下,系統可能會強制測試在 /tmp
中建立檔案。舉例來說,Unix 網域通訊端的路徑長度限制通常必須在 /tmp
下建立通訊端。Bazel 無法追蹤這類檔案;測試本身必須謹慎行事,使用不重複的路徑以避免彼此衝突、同時執行測試和非測試程序,以及清除在 /tmp
中建立的檔案。
一些熱門的測試架構 (例如 JUnit4 TemporaryFolder
或 Go TempDir
) 有自己的方法,在 /tmp
下建立臨時目錄。這些測試架構內含會清理 /tmp
檔案的功能,因此即使這些檔案在 TEST_TMPDIR
之外建立檔案,您還是可以使用。
測試必須透過「執行檔案」機制,或是執行環境的其他部分 (專門用於提供輸入檔案) 存取輸入。
測試不得在根據其執行檔位置推測的路徑存取建構系統的其他輸出內容。
系統無法確定執行檔案樹狀結構包含的是一般檔案、符號連結或混合資料。執行檔案樹狀結構可能包含目錄的符號連結。測試應避免在執行檔案樹狀結構中使用包含 ..
元件的路徑。
執行檔案樹狀結構中沒有任何目錄、檔案或符號連結 (包括週遊符號連結的路徑) 應可寫入。初始工作目錄不得寫入。)測試不得假設執行檔案的任何部分可寫入,或由目前使用者擁有 (例如,chmod
和 chgrp
可能會失敗)。
在測試執行期間,執行檔案樹狀結構 (包括週遊符號連結的路徑) 不得變更。父項目錄和檔案系統掛接項目不得以任何方式影響解析執行檔案樹狀結構中路徑的結果。
為了掌握早期結束事件,測試可能會在 TEST_PREMATURE_EXIT_FILE
指定的路徑建立檔案,並在結束時移除。如果 Bazel 在測試完成時看到檔案,就會假設測試已提前結束,並將測試標示為失敗。
代碼慣例
測試規則中的部分標記具有特殊意義。另請參閱 tags
屬性的 Bazel Build Enlopedia。
標記 | 意義 |
---|---|
exclusive |
不同時執行其他測試 |
external |
測試具有外部相依性;請停用測試快取 |
large |
test_suite 慣例;大型測試套件 |
manual * |
請勿在萬用字元目標模式 (例如 :... 、:* 或 :all ) 中加入測試目標 |
medium |
test_suite 慣例;中等測試套件 |
small |
test_suite 慣例;小型測試套件 |
smoke |
test_suite 慣例,代表應先執行程式碼,再將程式碼變更提交至版本管控系統 |
執行檔案
在以下範例中,假設有一個名為 //foo/bar:unittest
的 *_binary() 規則,且執行階段依附元件依附於標示為 //deps/server:server
的規則。
位置
目標 //foo/bar:unittest
的 runfiles 目錄是 $(WORKSPACE)/$(BINDIR)/foo/bar/unittest.runfiles
目錄。這個路徑稱為 runfiles_dir
。
依附元件
系統會將執行檔案目錄宣告為 *_binary()
規則的編譯時間依附元件。執行檔案目錄本身取決於一組會影響 *_binary()
規則或其任何編譯時間或執行階段依附元件的 BUILD 檔案。修改來源檔案不會影響執行檔案目錄的結構,也不會觸發任何重新建構。
目錄
runfiles 目錄包含下列內容:
- 執行階段依附元件的符號連結:是
*_binary()
規則執行階段依附元件的每個 OutputFile 和 CommandRule 以在 runfiles 目錄中的一個符號連結表示。符號連結的名稱為$(WORKSPACE)/package_name/rule_name
。例如,伺服器的符號連結將命名為$(WORKSPACE)/deps/server/server
,且完整路徑會是$(WORKSPACE)/foo/bar/unittest.runfiles/$(WORKSPACE)/deps/server/server
。符號連結的目的地是 OutputFile 或 CommandRule 的 OutputFileName(),並以絕對路徑表示。因此,符號連結的目的地可能是$(WORKSPACE)/linux-dbg/deps/server/42/server
。 - 子執行檔案的符號連結:每
*_binary()
Z 都是執行時間依附元件*_binary()
C,在 C 的執行檔案目錄中會有第二個連結,可連結至 Z 的執行檔案。符號連結的名稱為$(WORKSPACE)/package_name/rule_name.runfiles
。符號連結的目標是 runfiles 目錄。舉例來說,所有子程式都共用同一個執行檔案目錄。