測試執行環境的完整規格。
背景
Bazel BUILD 語言包含可用於以多種語言定義自動化測試程式的規則。
測試會使用 bazel test
執行。
使用者也可以直接執行測試二進位檔。這項做法雖然允許,但不代表 Google 認可,因為這類呼叫不會遵守下方所述的規定。
測試應為密封的,也就是說,它們應只存取已宣告依附性的資源。如果測試無法正確隔離,就無法提供歷來可重現的結果。這可能會對找出問題所在 (判斷哪項變更導致測試失敗)、發布工程可稽核性,以及測試資源隔離 (自動化測試架構不應對伺服器進行分散式阻斷服務攻擊,因為某些測試會與伺服器通訊) 造成重大問題。
目標
本頁的目標是正式建立 Bazel 測試的預期行為和執行階段環境。並且會對測試執行程式和建構系統強制規定。
測試環境規格可協助測試作者避免依賴未指定的行為,進而讓測試基礎架構更自由地進行實作變更。規格會針對目前允許許多測試通過,但未正確實現密封、確定性和可重入的漏洞加以修正。
本頁面旨在提供規範性和權威性資訊。如果此規格和測試執行程式的實作行為不一致,則規格優先。
建議規格
關鍵字「MUST」、「MUST NOT」、「REQUIRED」、「SHALL」、「SHALL NOT」、「SHOULD」、「SHOULD NOT」、「RECOMMENDED」、「MAY」和「OPTIONAL」應依據 IETF RFC 2119 的說明進行解讀。
測試目的
Bazel 測試的目的是確認已簽入至存放區的來源檔案的某些屬性。(在本頁中,「來源檔案」包括測試資料、黃金輸出內容,以及任何版本管控下的內容)。一位使用者編寫測試,以便斷言預期要保留的變數不變量。其他使用者稍後執行測試,以便檢查是否有變異。如果測試依賴來源檔案以外的任何變數 (非密封),其價值就會降低,因為後續使用者無法確定測試停止通過時,他們的變更是否有錯。
因此,測試結果必須只取決於:
- 測試宣告依附元件的來源檔案
- 測試宣告依附元件的建構系統產品
- 資源的行為由測試執行器保證為恆定
目前系統不會強制執行這類行為。不過,測試執行程式保留日後新增這類強制執行機制的權利。
建構系統的角色
測試規則與二進位規則相似,因為每個規則都必須產生可執行的程式。對於某些語言,這是一個 Stub 程式,可將特定語言的測試套件與測試程式碼結合。測試規則也必須產生其他輸出內容。除了主要測試可執行檔外,測試執行程式還需要runfiles的資訊清單,也就是在執行階段提供給測試的輸入檔案,並且可能需要測試類型、大小和標記的相關資訊。
建構系統可能會使用執行檔來提供程式碼和資料。(這可能用於最佳化,藉由在各項測試之間共用檔案 (例如透過使用動態連結),讓每個測試二進位檔變得更小。)建構系統應確保產生的可執行檔會透過測試執行程式提供的 runfiles 映像檔載入這些檔案,而非硬式編碼參照來源或輸出樹狀結構中的絕對位置。
測試執行工具的角色
從測試執行工具的角度來看,每項測試都是可透過 execve()
叫用的程式。您也可以透過其他方式執行測試,例如 IDE 可能會允許在程序中執行 Java 測試。不過,以獨立程序執行測試的結果必須視為可靠的結果。如果測試程序已完成,並以結束代碼 0 正常終止,則表示測試已通過。任何其他結果都會視為測試失敗。特別是,將任何 PASS
或 FAIL
字串寫入 stdout 對測試執行程式沒有任何意義。
如果測試執行時間過長、超出某些資源限制,或是測試執行程式偵測到禁止行為,則可能會選擇終止測試,並將執行作業視為失敗。在向測試程序或任何子項傳送信號後,執行程式不得將測試回報為通過。
整個測試目標 (而非個別方法或測試) 會獲得有限的時間來執行至完成。測試的時間限制取決於其 timeout
屬性,請參閱下表:
逾時 | 時間限制 (秒) |
---|---|
short | 60 |
中度 | 300 |
long | 900 |
永恆 | 3600 |
未明確指定逾時時間的測試,會根據測試的 size
隱含一個逾時時間,如下所示:
大小 | 隱含的逾時標籤 |
---|---|
small | short |
medium | 中度 |
large | long |
巨大 | 永恆 |
沒有明確逾時設定的「大型」測試將分配 900 秒的執行時間。若測試為「中等」且逾時時間為「短」,則會分配 60 秒。
與 timeout
不同,size
還會在本機執行測試時,判斷其他資源 (例如 RAM) 的假設峰值用量,如常見定義所述。
所有 size
和 timeout
標籤組合都是合法的,因此您可以宣告「enormous」測試的逾時時間為「short」。這可能會導致一些非常可怕的後果。
無論逾時時間為何,測試可能會隨意快速傳回。雖然系統可能會發出警告,但不會對超長的逾時期限處以懲罰:您通常應盡可能縮短逾時期限,以免發生任何不穩定的情況。
在已知速度緩慢的情況下手動執行時,可以使用 --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 檔案中指定測試大小和逾時時間。如果未指定,測試的大小預設為「medium」。
如果測試的主要程序已結束,但其中部分子項仍在執行,則測試執行程式應視為執行完成,並根據從主要程序觀察到的結束代碼,將其計為成功或失敗。測試執行工具可能會終止任何異常程序。測試不應以這種方式洩漏程序。
測試分割
您可以透過測試區隔來並行執行測試。請參閱 --test_sharding_strategy
和 shard_count
啟用測試分割作業。啟用區塊化後,每個區塊都會啟動一次測試執行工具。環境變數 TEST_TOTAL_SHARDS
是分片數,而 TEST_SHARD_INDEX
是分片索引,從 0 開始。執行程式會利用這項資訊,選擇要執行哪些測試,例如使用輪替策略。並非所有測試執行程式都支援區隔。如果執行程式支援區隔,則必須建立或更新 TEST_SHARD_STATUS_FILE
指定檔案的上次修改日期。否則,Bazel 會假設不支援分割,並不會啟動其他執行程式。
初始條件
執行測試時,測試執行程式必須建立特定的初始條件。
測試執行工具必須使用 argv[0]
中測試可執行檔案的路徑,叫用每項測試。這個路徑必須是相對路徑,且位於測試的目前目錄下方 (位於 runfiles 樹狀結構中,請參閱下方說明)。除非使用者明確要求,否則測試執行程式不應將任何其他引數傳遞至測試。
初始環境區塊應按以下方式組成:
變數 | 值 | 狀態 |
---|---|---|
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 |
runfiles 樹狀結構基礎的絕對路徑 | 必填 |
TEST_TOTAL_SHARDS |
總計 shard count ,如果使用 sharding |
選用 |
TEST_TMPDIR |
可寫入的私人目錄絕對路徑 | 必填 |
TEST_WORKSPACE |
本機存放區的工作區名稱 | 選用 |
TEST_UNDECLARED_OUTPUTS_DIR |
可寫入私人目錄的絕對路徑 (用於寫入未宣告的測試輸出內容) | 選用 |
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
) 應開放供寫入,但不指定其附加的項目。可以是終端機、管道、一般檔案或任何可輸入字元的檔案。這些檔案可能會在開啟檔案表格中共用一個項目 (也就是說,它們無法獨立尋找)。測試不應繼承任何其他開啟檔案描述項。
初始 umask 應為 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
應可寫入,但測試應避免使用這些路徑。
測試不得假設任何常數路徑可供專屬使用。
測試不得假設已為任何已掛載的檔案系統啟用 atimes。
使用者和群組
使用者 root、nobody 和 unittest 必須存在。根群組、nobody 和 eng 群組必須存在。
測試必須以非超級使用者的身分執行。實際和有效使用者 ID 必須相同,群組 ID 也是如此。除了這個值之外,目前的使用者 ID、群組 ID、使用者名稱和群組名稱均未指定。未指定輔助群組 ID 集合。
目前的使用者 ID 和群組 ID 必須有對應的名稱,可透過 getpwuid()
和 getgrgid()
擷取。但對於輔助群組 ID 來說,情況可能並非如此。
目前使用者必須有主目錄。可能無法寫入。測試不得嘗試寫入該檔案。
網路
未指定主機名稱。可含可不含半形句號。解析主機名稱時,必須提供目前主機的 IP 位址。解析第一個點號後的主機名稱切割也必須有效。主機名稱 localhost 必須解析。
其他資源
測試至少會獲得一個 CPU 核心。但其他人可能也會使用,但我們無法保證。這個核心的其他效能方面未指定。您可以將標記「cpu:n」(n 為正整數) 新增至測試規則,將保留數量提高至較高的 CPU 核心數。如果機器的 CPU 核心總數少於要求的數量,Bazel 仍會執行測試。如果測試使用區塊處理,每個區塊都會保留此處指定的 CPU 核心數量。
測試可能會建立子程序,但不會處理群組或工作階段。
測試可使用的輸入檔案數量有上限。這個上限可能會有所變動,但目前的上限範圍為數萬個輸入內容。
時間與日期
未指定目前的時間和日期。未指定系統時區。
可能會提供 X 視窗,也可能不會。需要 X 伺服器的測試應啟動 Xvfb。
測試與檔案系統的互動
除非另有指定,否則測試環境變數中指定的所有檔案路徑都會指向本機檔案系統中的某處。
測試應只在 $TEST_TMPDIR
和 $TEST_UNDECLARED_OUTPUTS_DIR
(如果已設定) 指定的目錄中建立檔案。
這些目錄一開始會是空的。
測試不得嘗試移除、變更檔案權限或以其他方式變更這些目錄。
這些目錄可能是符號連結。
$TEST_TMPDIR/.
的檔案系統類型仍未指定。
測試也可能會將 .part 檔案寫入 $TEST_UNDECLARED_OUTPUTS_ANNOTATIONS_DIR
,以便為未宣告的輸出檔案加上註解。
在極少數情況下,測試可能會被迫在 /tmp
中建立檔案。舉例來說,Unix 網域 Socket 的路徑長度限制通常需要在 /tmp
下建立 Socket。Bazel 無法追蹤這類檔案;測試本身必須確保是密封的,以便使用專屬路徑避免與其他同時執行的測試和非測試程序發生衝突,並清理其在 /tmp
中建立的檔案。
部分熱門測試架構 (例如 JUnit4 TemporaryFolder
或 Go TempDir
) 會在 /tmp
下建立暫存資料夾。這些測試架構包含清理 /tmp
中檔案的功能,因此即使這些架構會在 TEST_TMPDIR
以外建立檔案,您還是可以使用這些架構。
測試必須透過 runfiles 機制或執行環境的其他部分存取輸入內容,這些部分是專門用於提供輸入檔案。
測試不得透過從自身可執行檔位置推斷的路徑存取建構系統的其他輸出內容。
未指定執行檔案樹狀結構是否包含一般檔案、符號連結或兩者混合。執行檔案樹狀結構可能包含目錄的符號連結。測試應避免在執行檔樹狀結構中使用含有 ..
元件的路徑。
執行檔案樹狀目錄 (包括遍歷符號連結的路徑) 中的目錄、檔案或符號連結,均不得設為可寫入。(因此,初始工作目錄不應可寫入)。測試不得假設任何執行檔的部分可供寫入,或由目前使用者擁有 (例如 chmod
和 chgrp
可能會失敗)。
執行檔案樹狀結構 (包括遍歷符號連結的路徑) 不得在測試執行期間變更。父項目錄和檔案系統掛接點不得以任何方式變更,以免影響在 runfiles 樹狀結構中解析路徑的結果。
為了偵測提早結束,測試可能會在 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
的 runfiles 目錄為 $(WORKSPACE)/$(BINDIR)/foo/bar/unittest.runfiles
目錄。這個路徑稱為 runfiles_dir
。
依附元件
系統會將 runfiles 目錄宣告為 *_binary()
規則的編譯時間依附元件。runfiles 目錄本身會依賴影響 *_binary()
規則或其任何編譯時間或執行時間依附元件的 BUILD 檔案組合。修改原始檔案不會影響 runfiles 目錄的結構,因此不會觸發任何重建作業。
目錄
runfiles 目錄包含下列項目:
- 執行階段依附元件的符號連結:每個 OutputFile 和 CommandRule 都是
*_binary()
規則的執行階段依附元件,會在 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 目錄。舉例來說,所有子程式都會共用一個共同的 runfiles 目錄。