完整說明測試執行環境。
背景
Bazel BUILD 語言包含規則,可用來定義多種語言的自動化測試程式。
測試是使用 bazel test
執行。
使用者也可以直接執行測試二進位檔。這並不是其中一種方式,但未經認可,因此這類叫用不會遵循下述的委託書。
測試應是「密封的」,也就是說,測試會要求只存取已宣告依附元件的資源。如果測試不適當,就無法在過去提供可重現的結果。這可能是出乎意料的重大問題 (判斷哪項變更會破壞測試)、發布工程的可稽核性,以及資源隔離測試 (自動化測試架構並非針對伺服器進行 DDOS 測試,因為有些測試正要發出通訊)。
目標
本頁面的目標是正式建立 Bazel 測試的執行階段環境,以及預期的行為。這也會對測試執行器和建構系統施加要求。
測試環境規格有助於測試作者避免依賴未指定的行為,因此可讓測試基礎架構更自由地變更實作內容。規格會強化一些目前允許許多測試通過的孔,儘管沒有正確的密封、確定性和重新測試。
以下為規範及公信力的參考網頁。如果這項規格和測試執行器的實作行為不一致,則以規格為優先。
提出的規格
「必須」、「不得」、「必要」、「應」、「不應」、「應」、「建議」、「建議」、「可能」及「選用」等重要詞彙如 IETF RFC 2119 所述。
測試目的
Bazel 測試的目的在於確認已在存放區中檢查來源檔案的某些屬性。(在本頁的「來源檔案」中,包括測試資料、黃金輸出內容,以及版本管控下的任何其他內容)。某位使用者編寫測試來斷言要維持的非變化形式。其他使用者稍後會執行測試,檢查不變量是否損毀。如果測試取決於來源檔案 (非密封) 以外的任何變數,其值將會減少,因為後續的使用者在測試停止通過時,將無法確定其變更有錯誤。
因此,測試結果只能根據下列項目:
- 測試已宣告依附元件的來源檔案
- 測試中已宣告依附元件的建構系統產品
- 測試執行器保證執行哪些資源行為會維持不變
我們目前並未強制執行這類行為。不過,測試執行器有權於日後新增這類強制執行。
建構系統的角色
測試規則類似於二進位規則,每個規則都必須產生可執行的程式。對某些語言而言,這是一個虛設常式程式,結合了語言專屬的限制與測試程式碼。測試規則也必須產生其他輸出內容。除了主要測試執行檔外,測試執行器還需具備「runfiles」資訊清單,也就是必須在執行階段供測試使用的輸入檔案,且可能需要測試的類型、大小和標記相關資訊。
建構系統可能會使用執行檔案來提供程式碼和資料。(這可能會做為最佳化做法,透過跨測試分享檔案 (例如使用動態連結),讓每個測試二進位檔縮減大小。)建構系統應確保產生的可執行檔透過測試執行器提供的執行檔案映像檔載入這些檔案,而非透過硬式編碼參照來源或輸出樹狀結構中的絕對位置。
測試執行工具的角色
從測試執行器的觀點來看,每項測試都是可使用 execve()
叫用的程式。可能有其他執行測試的方法;舉例來說,IDE 可能會允許在處理過程中執行 Java 測試。然而,以獨立程序的形式執行測試的結果必須具有權威性。如果測試程序執行完畢並正常終止,且結束代碼為 0,表示測試已通過。任何其他結果均視為測試失敗。請特別注意,將任何字串 PASS
或 FAIL
寫入 stdout,對測試執行者沒有任何意義。
如果測試執行時間過長、超過某些資源限制,或者測試執行器偵測到禁止的行為,則可以選擇終止測試,並將執行作業視為失敗。執行器將信號傳送至測試程序或其任何子項後,不得將測試回報為通過。
整個測試目標 (而非個別方法或測試) 會在有限的執行時間內完成。測試的時間限制是以測試的 timeout
屬性為準,如下表所示:
逾時 | 時間限制 (秒) |
---|---|
short | 60 |
和緩 | 300 |
long | 900 |
Eternal | 3600 |
未明確指定逾時的測試會根據測試的 size
隱含,如下所示:
大小 | 「默示逾時」標籤 |
---|---|
small | short |
媒介 | 和緩 |
large | long |
巨大 | Eternal |
沒有明確逾時設定的「大型」測試將分配到執行 900 秒的時間。逾時為「短」的「中」測試將會分配 60 秒。
與 timeout
不同,size
在本機執行測試時,會額外確定其他資源 (例如 RAM) 的假設用量高峰,如「一般定義」一節所述。
size
和 timeout
標籤的所有組合皆為合法,因此您可以宣告「龐大」測試,將逾時設為「短」。它可能會快速做一些非常可怕的事情
無論逾時,測試都能以任意速度傳回。雖然測試可能會收到警示,但測試不會因為過度逾時而受到懲罰:一般來說,您應該盡可能將逾時設定為越嚴密,且不會造成任何不著痕跡。
在已知的緩慢條件下手動執行時,您可以使用 --test_timeout
bazel 標記覆寫測試逾時時間。--test_timeout
值以秒為單位。例如,--test_timeout=120
會將測試逾時設為兩分鐘。
建議的測試逾時下限值如下:
逾時 | 最短時間 (秒) |
---|---|
short | 0 |
和緩 | 30 |
long | 300 |
Eternal | 900 |
舉例來說,如果「中等」測試在 5.5 秒內完成,請考慮設定 timeout =
"short"
或 size = "small"
。使用 bazel --test_verbose_timeout_warnings
指令列選項,即可顯示指定大小過大的測試。
您可以根據這裡的規格,在 BUILD 檔案中指定測試大小和逾時。如果未指定,測試大小將預設為「medium」。
如果測試結束的主要程序,但部分子項仍在執行,測試執行器應將執行作業視為執行完畢,並根據從主要程序中觀察到的結束程式碼將其視為成功或失敗。測試執行器可能會終止任何不愉快的程序。測試不應以這種方式洩漏程序。
測試資料分割
測試可透過測試資料分割平行處理。如要啟用測試資料分割,請參閱 --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 |
無限制,或 2044KB <= rlim <= 8192KB |
初始程序時間 (由 times()
傳回) 和資源使用率 (由 getrusage()
傳回) 未指定。
未指定初始排程政策和優先順序。
主機系統的角色
除了可以直接透過測試執行器控管使用者情境,執行測試的作業系統也必須滿足特定屬性,測試執行作業才能有效。
檔案系統
測試觀察到的根目錄不一定是真正的根目錄。
應掛接「/proc
」。
所有建構工具均應位於本機安裝作業使用的 /usr
下的絕對路徑。
可能無法提供以 /home
開頭的路徑。測試不應存取任何這類路徑。
/tmp
應可寫入,但測試應避免使用這些路徑。
測試不得假設任何常數路徑可供專屬用途使用。
測試不得假設任何已掛接的檔案系統已啟用時間。
使用者和群組
使用者的 root、無人和 unittest 必須存在。根、無人和工程群組必須存在。
測試必須以非超級使用者的身分執行。真正有效的使用者 ID 必須相等;群組 ID 也是如此。此外,未指定目前的使用者 ID、群組 ID、使用者名稱和群組名稱。未指定補充群組 ID 組合。
目前的使用者 ID 和群組 ID 必須有對應的名稱,可使用 getpwuid()
和 getgrgid()
擷取。至於補充群組 ID 也是如此。
目前的使用者必須擁有主目錄。可能無法寫入。測試不得嘗試寫入。
網路
未指定主機名稱。不一定包含點。解析主機名稱時,必須提供目前主機的 IP 位址。即使在第一點後解析主機名稱無效,也必須這麼做。主機名稱 localhost 必須解析。
其他資源
測試至少會獲得一個 CPU 核心。其他供應商可能也有提供,但無法保證。未指定這個核心的其他效能層面。您可以在測試規則中新增「cpu:n」(其中 n 是正數) 標記,將預留項目增加到更多的 CPU 核心。如果機器的 CPU 核心總數少於所要求的總數,Bazel 仍會執行測試。如果測試使用資料分割,每個資料分割都會保留此處指定的 CPU 核心數量。
測試可能會建立子程序,但無法處理群組或工作階段。
系統對測試可耗用的輸入檔案有數量限制,這個限制隨時可能變動,但目前要輸入至數萬個輸入值之間。
時間與日期
未指定目前的時間和日期。未指定系統時區。
X 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
之外建立檔案,您還是可以使用這些架構。
測試必須透過 runfiles 機制,或執行環境的其他部分 (專門用來提供輸入檔案) 存取輸入內容。
測試不得在透過自身可執行檔位置推斷的路徑存取建構系統的其他輸出內容。
未指定執行檔案樹狀結構是否包含一般檔案、符號連結或混合。執行檔案樹狀結構可能包含指向目錄的符號連結。測試應避免在執行檔案樹狀結構中使用含有 ..
元件的路徑。
不應可寫入執行檔案樹狀結構中的目錄、檔案或符號連結 (包括週遊符號連結的路徑)。(不會寫入初始工作目錄)。測試不得假設執行檔案的任何部分都可以寫入,或由目前使用者擁有 (例如 chmod
和 chgrp
可能會失敗)。
執行檔案樹狀結構 (包括週遊符號連結的路徑) 在測試執行期間不得變更。父項目錄和檔案系統掛接不得以任何方式變更,進而影響執行檔案樹狀結構中解析路徑的結果。
為掌握早期結束事件,測試可能會在開始時於 TEST_PREMATURE_EXIT_FILE
指定的路徑上建立一個檔案,並在結束時將其移除。如果 Bazel 在測試完成後看過檔案,就會假設測試提前結束,並將測試標示為失敗。
代碼慣例
測試規則中的部分代碼具有特殊意義。另請參閱 Bazel 建構百科全書的 tags
屬性。
標記 | 意義 |
---|---|
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
的執行檔案目錄是 $(WORKSPACE)/$(BINDIR)/foo/bar/unittest.runfiles
目錄。這個路徑稱為 runfiles_dir
。
依附元件
執行檔案目錄會宣告為 *_binary()
規則的編譯時間依附元件。執行檔案目錄本身取決於一組影響 *_binary()
規則的 BUILD 檔案,或是影響其任何編譯時間或執行階段依附元件的 BUILD 檔案。修改來源檔案不會影響執行檔案目錄的結構,因此不會觸發任何重新建構。
目錄
runfiles 目錄包含以下內容:
- 執行時間依附元件的 Symlink:若為
*_binary()
規則的執行階段依附元件,則每個 OutputFile 和 CommandRule 會以執行檔案目錄中的一個符號連結表示。符號連結的名稱為$(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()
C 的執行階段依附元件的*_binary()
Z,C 的執行檔案目錄中有一個連至 Z 執行檔案的連結。符號連結的名稱為$(WORKSPACE)/package_name/rule_name.runfiles
。符號連結的目標是 runfiles 目錄例如,所有子程式會共用一個通用的執行檔案目錄。