Errors and testing
Error handling#
Faber 將三個相關概念分開處理,而許多語言會將它們折疊成同一種形式:
| 構造 | 意義 |
|---|---|
→ T | 一般成功回傳通道 |
T ∪ nihil | 成功值域中的缺值 |
⇥ E | 錯誤的可復原替代退出通道 |
一般回傳#
函式 porta(整數 x) → 整數 {
若 x ≺ 0 則 傳回 0
傳回 x * 2
}可失敗函式#
當函式可以透過錯誤通道離開時,使用 ⇥:
函式 divide(整數 a, 整數 b) → 整數 ⇥ 文字 {
若 b ≡ 0 {
拋出 "division by zero"
}
傳回 a / b
}拋出 — iace#
iace 將值送入錯誤通道:
函式 exigePositivum(整數 value) → 空值 ⇥ 文字 {
若 value ≺ 0 {
拋出 "negative value"
}
}復原 — fac / cape#
呼叫端使用 執行 區塊與 捕捉 處理常式在本地復原:
函式 divide(整數 a, 整數 b) → 整數 {
若 b ≡ 0 則 傳回 0
傳回 a / b
}
函式 tutum(整數 a, 整數 b) → 整數 {
執行 {
傳回 divide(a, b)
}
捕捉 err {
警告 err
傳回 0
}
}直接呼叫可失敗函式並不是一般運算式。請將對
→ T ⇥ E 函式的呼叫放在作用中的 執行 / 捕捉 邊界內。
內嵌轉換復原#
⇥ 也可以在 ↦ 轉換上指定內嵌復原值:
定值 文字 raw ← "42"
定值 整數 n ← raw ↦ 整數 ⇥ 0僅效果的可失敗函式#
對於會發生錯誤但不回傳成功值的函式,省略 → T:
函式 exigePositivum(整數 value) → 空值 ⇥ 文字 {
若 value ≺ 0 {
拋出 "negative value"
}
}目前狀態#
→、傳回、⇥、iace 與 執行 / 捕捉 都是現行的文法與檢查器
介面。Rust 與 Go 對完整 ⇥ / iace / 捕捉 執行時行為的降階處理
仍是後端缺口——這些內容可以通過型別檢查,但目前尚未對所有目標產生
可失敗的執行時程式碼。
Inline testing#
Faber 將一流的測試框架內建於語言中,提供三個關鍵字:probandum 宣告測試套件,proba 宣告單一測試案例,而 adfirma 則斷言條件。測試與其所測試的程式碼位於同一檔案中,透過 faber test 執行,並支援與正式程式碼相同的編譯器流程——具備地區設定感知、型別檢查及多目標支援。
三個關鍵字#
| 關鍵字 | 作用 | 約略對應 |
|---|---|---|
probandum | 宣告具名測試套件 | describe、#[cfg(test)] mod |
proba | 宣告單一測試案例 | it、#[test] |
adfirma | 在執行時斷言條件 | assert!、assert_eq! |
probandum — 測試套件#
probandum 區塊會將相關的測試案例分組。套件可以巢狀,以階層方式組織測試:
測試 "unum plus unum" {
斷言 1 + 1 ≡ 2
}
測試 "multiplicatio" {
斷言 3 * 4 ≡ 12
}
測試 "comparatio" {
定值 整數 x ← 10
斷言 x ≥ 10
}proba — 測試案例#
proba 區塊包含測試邏輯。它可以使用任何 Faber 程式碼——變數繫結、函式呼叫、控制流程——並以一個或多個 adfirma 斷言結束。測試可以使用選用的 tag 標記,以便選擇性執行:
proba "echo formats operands with one space" tag "coreutils" {
adfirma echo_textus(["hello", "world"]) ≡ "hello world"
}adfirma — 斷言#
adfirma 會評估布林運算式;如果結果為假,便回報失敗。選用的訊息字串可在失敗時提供上下文:
入口 {
定值 整數 x ← 10
# Simple assertion
斷言 x ≻ 0
# With custom message
斷言 x ≡ 10 secus "x decem esse debet"
# Multiple assertions in sequence
定值 文字 nomen ← "Marcus"
斷言 nomen ≡ "Marcus"
斷言 nomen ≠ "" secus "nomen vacuum non sit"
}工作流程#
測試透過 faber test 指令執行:
faber test # run all tests in the current package
faber test examples/coreutils/packages/echo # run tests for a specific package由於測試與原始碼位於同一個 .fab 檔案中,因此不需要獨立的測試目錄結構、不需要測試模組宣告,也不需要在測試建置與正式建置之間區分建置指令碼。編譯器會根據所使用的關鍵字,辨識哪些區塊是測試程式碼、哪些區塊是正式程式碼——probandum 和 proba 會被解析,但會從正式建置中排除。
實際範例#
coreutils 的 echo 套件展示了測試框架的實際應用。測試與實作位於同一個檔案中,涵蓋選項解析、跳脫展開及邊界情況:
probandum "echo formatting" tag "coreutils" {
proba "empty operands format as empty text" {
fixum lista<textus> words ← vacua
adfirma echo_textus(words) ≡ ""
}
proba "single operand is unchanged" {
adfirma echo_textus(["hello"]) ≡ "hello"
}
proba "-E is a leading no-op option" {
adfirma echo_textus(["-E", "hello", "world"]) ≡ "hello world"
}
proba "-n suppresses the trailing newline flag" {
adfirma echo_novam_lineam(["-n", "hello"]) ≡ falsum
}
proba "-e expands the declared escape subset" {
adfirma echo_textus(["-e", "a\\nb"]) ≡ "a\nb"
adfirma echo_textus(["-e", "a\\tb"]) ≡ "a\tb"
}
}設計說明#
有數項設計選擇,使 Faber 的測試框架有別於傳統方法:
- 沒有獨立的測試二進位檔。 測試是同一份原始碼中的宣告,而不是獨立的編譯目標。編譯器會將測試區塊從正式輸出中篩除。
- 使用標記,而不是目錄。 測試透過
tag標記組織,而不是目錄結構。測試可以同時屬於多個組織軸,不必為此搬移檔案。 - 完整的編譯器流程。 測試會進行型別檢查、分析,並具備地區設定感知——相同的
--reader-locale旗標也適用於測試輸出。 - 多目標。 測試會透過套件所指定的後端執行——
faber test --interpret使用 MIR 逐步執行器,faber test使用編譯後的 Rust。 - 巢狀套件。
probandum區塊可以巢狀,反映其所測試程式碼的結構。
參考資料#
examples/corpus/probandum/— probandum 範例檔案examples/corpus/proba/— proba 範例檔案examples/corpus/adfirma/— adfirma 範例檔案examples/coreutils/packages/echo/src/main.fab— 使用標記的實際範例