Errors and testing
Error handling#
Faber แยกแนวคิดที่เกี่ยวข้องกันสามอย่าง ซึ่งในหลายภาษาถูกรวมเป็นรูปแบบเดียวกัน:
| โครงสร้าง | ความหมาย |
|---|---|
→ T | ช่องทางคืนค่าความสำเร็จตามปกติ |
T ∪ นัล | การไม่มีค่าในโดเมนค่าความสำเร็จ |
⇥ E | ช่องทางออกทางเลือกสำหรับข้อผิดพลาดที่กู้คืนได้ |
การคืนค่าตามปกติ#
ฟังก์ชัน porta(จํานวน x) → จํานวน {
ถ้า x ≺ 0 ดังนั้น คืน 0
คืน x * 2
}ฟังก์ชันที่อาจล้มเหลว#
ใช้ ⇥ เมื่อฟังก์ชันสามารถออกผ่านช่องทางข้อผิดพลาดได้:
ฟังก์ชัน divide(จํานวน a, จํานวน b) → จํานวน ⇥ ข้อความ {
ถ้า b ≡ 0 {
โยน "division by zero"
}
คืน a / b
}การโยนข้อผิดพลาด — โยน#
โยน ส่งค่าผ่านช่องทางข้อผิดพลาด:
ฟังก์ชัน exigePositivum(จํานวน value) → เปล่า ⇥ ข้อความ {
ถ้า value ≺ 0 {
โยน "negative value"
}
}การกู้คืน — ทำ / จับ#
ผู้เรียกสามารถกู้คืนภายในบริบทได้ด้วยบล็อก ทำ และตัวจัดการ จับ:
ฟังก์ชัน 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"
}
}สถานะปัจจุบัน#
→, คืน, ⇥, โยน และ ทำ / จับ เป็นไวยากรณ์และพื้นผิวของตัวตรวจสอบที่ใช้งานได้แล้ว การลดรูปไปยัง Rust และ Go สำหรับพฤติกรรมรันไทม์ของ ⇥ / โยน / จับ แบบเต็มรูปแบบยังเป็นช่องว่างของแบ็กเอนด์อยู่ — โค้ดเหล่านี้ผ่านการตรวจสอบชนิด แต่ยังไม่สร้างโค้ดรันไทม์ที่อาจล้มเหลวสำหรับทุกเป้าหมาย
Inline testing#
Faber มีเฟรมเวิร์กการทดสอบแบบมากับภาษาโดยตรง โดยมีคีย์เวิร์ดสามคำ ได้แก่ ทดสอบชุด สำหรับประกาศชุดทดสอบ, ทดสอบ สำหรับประกาศกรณีทดสอบแต่ละกรณี และ ยืนยัน สำหรับยืนยันเงื่อนไข การทดสอบจะอยู่ในไฟล์เดียวกับโค้ดที่ต้องการทดสอบ ทำงานผ่าน faber test และรองรับไปป์ไลน์คอมไพเลอร์เดียวกับโค้ดสำหรับการใช้งานจริง ทั้งการรองรับโลแคล การตรวจสอบชนิดข้อมูล และหลายเป้าหมายการคอมไพล์
คีย์เวิร์ดทั้งสามคำ#
| คีย์เวิร์ด | หน้าที่ | คำเทียบเคียงโดยประมาณ |
|---|---|---|
ทดสอบชุด | ประกาศชุดทดสอบที่มีชื่อ | describe, #[cfg(test)] mod |
ทดสอบ | ประกาศกรณีทดสอบแต่ละกรณี | it, #[test] |
ยืนยัน | ยืนยันเงื่อนไขขณะรันไทม์ | assert!, assert_eq! |
ทดสอบชุด — ชุดทดสอบ#
บล็อก ทดสอบชุด ใช้จัดกลุ่มกรณีทดสอบที่เกี่ยวข้องกัน ชุดทดสอบสามารถซ้อนกันได้เพื่อจัดระเบียบการทดสอบเป็นลำดับชั้น:
ทดสอบชุด "arithmetica" {
ทดสอบ "unum plus unum" {
ยืนยัน 1 + 1 ≡ 2
}
ทดสอบ "multiplicatio" {
ยืนยัน 3 * 4 ≡ 12
}
ทดสอบชุด "implicata" {
ทดสอบ "comparatio" {
คงที่ _ x ← 10
ยืนยัน x ≥ 10
}
}
}ทดสอบ — กรณีทดสอบ#
บล็อก ทดสอบ บรรจุตรรกะของการทดสอบ โดยใช้โค้ด Faber ใดก็ได้ เช่น การผูกตัวแปร การเรียกฟังก์ชัน และการควบคุมลำดับการทำงาน จากนั้นจบด้วยการยืนยันด้วย ยืนยัน อย่างน้อยหนึ่งรายการ การทดสอบสามารถกำกับด้วยมาร์กเกอร์ แท็ก ที่เป็นตัวเลือก เพื่อเรียกใช้งานเฉพาะบางรายการ:
ทดสอบ "echo formats operands with one space" แท็ก "coreutils" {
ยืนยัน echo_textus(["hello", "world"]) ≡ "hello world"
}ยืนยัน — การยืนยัน#
ยืนยัน จะประเมินนิพจน์บูลีน และรายงานความล้มเหลวหากผลลัพธ์เป็นเท็จ สามารถระบุสตริงข้อความเพิ่มเติมเพื่อให้บริบทเมื่อเกิดความล้มเหลวได้:
เริ่ม {
คงที่ _ x ← 10
# Simple assertion
ยืนยัน x ≻ 0
# With custom message
ยืนยัน x ≡ 10 ตาย "x decem esse debet"
# Multiple assertions in sequence
คงที่ _ ชื่อ ← "Marcus"
ยืนยัน ชื่อ ≡ "Marcus"
ยืนยัน ชื่อ ≠ "" ตาย "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 เดียวกัน จึงไม่ต้องมีโครงสร้างไดเรกทอรีทดสอบแยกต่างหาก ไม่ต้องประกาศโมดูลทดสอบ และไม่มีความแตกต่างของสคริปต์การบิลด์ระหว่างบิลด์สำหรับการทดสอบกับบิลด์สำหรับการใช้งานจริง คอมไพเลอร์จะแยกได้ว่าบล็อกใดเป็นโค้ดทดสอบและบล็อกใดเป็นโค้ดสำหรับการใช้งานจริงจากคีย์เวิร์ดที่ใช้ — ทดสอบชุด และ ทดสอบ จะถูกพาร์ส แต่จะถูกตัดออกจากบิลด์สำหรับการใช้งานจริง
ตัวอย่างการใช้งานจริง#
แพ็กเกจ echo ใน coreutils แสดงการใช้งานเฟรมเวิร์กการทดสอบในทางปฏิบัติ การทดสอบอยู่ในไฟล์เดียวกับส่วนการทำงานจริง และครอบคลุมการพาร์สออปชัน การขยายเอสเคป และกรณีขอบ:
ทดสอบชุด "echo formatting" แท็ก "coreutils" {
ทดสอบ "empty operands format as empty text" {
คงที่ รายการ<ข้อความ> words ← เซตว่าง
ยืนยัน echo_textus(words) ≡ ""
}
ทดสอบ "single operand is unchanged" {
ยืนยัน echo_textus(["hello"]) ≡ "hello"
}
ทดสอบ "-E is a leading no-op option" {
ยืนยัน echo_textus(["-E", "hello", "world"]) ≡ "hello world"
}
ทดสอบ "-n suppresses the trailing newline flag" {
ยืนยัน echo_novam_lineam(["-n", "hello"]) ≡ เท็จ
}
ทดสอบ "-e expands the declared escape subset" {
ยืนยัน echo_textus(["-e", "a\\nb"]) ≡ "a\nb"
ยืนยัน echo_textus(["-e", "a\\tb"]) ≡ "a\tb"
}
}หมายเหตุด้านการออกแบบ#
ตัวเลือกด้านการออกแบบหลายประการทำให้เฟรมเวิร์กการทดสอบของ Faber แตกต่างจากแนวทางทั่วไป:
- ไม่มีไบนารีทดสอบแยกต่างหาก การทดสอบเป็นการประกาศในไฟล์ซอร์สเดียวกัน ไม่ใช่เป้าหมายการคอมไพล์แยกต่างหาก คอมไพเลอร์จะกรองบล็อกการทดสอบออกจากเอาต์พุตสำหรับการใช้งานจริง
- ใช้แท็กแทนไดเรกทอรี การทดสอบจัดระเบียบด้วยมาร์กเกอร์
แท็กแทนโครงสร้างไดเรกทอรี การทดสอบหนึ่งรายการจึงสังกัดแกนการจัดระเบียบได้หลายแบบโดยไม่ต้องย้ายตำแหน่ง - ใช้ไปป์ไลน์คอมไพเลอร์เต็มรูปแบบ การทดสอบจะถูกตรวจสอบชนิดข้อมูล วิเคราะห์ และรองรับโลแคล เช่นเดียวกับโค้ดส่วนอื่น — แฟล็ก
--reader-localeเดียวกันนี้ใช้กับเอาต์พุตการทดสอบ - รองรับหลายเป้าหมาย การทดสอบทำงานผ่านแบ็กเอนด์ที่แพ็กเกจเลือกใช้ — MIR stepper สำหรับ
faber test --interpretและ Rust ที่คอมไพล์แล้วสำหรับfaber test - ชุดทดสอบซ้อนกันได้ บล็อก
ทดสอบชุดสามารถซ้อนกันได้ เพื่อสะท้อนโครงสร้างของโค้ดที่กำลังทดสอบ
แหล่งอ้างอิง#
examples/corpus/probandum/— ไฟล์ตัวอย่างของ ทดสอบชุดexamples/corpus/proba/— ไฟล์ตัวอย่างของ ทดสอบexamples/corpus/adfirma/— ไฟล์ตัวอย่างของ ยืนยันexamples/coreutils/packages/echo/src/main.fab— การใช้งานจริงพร้อมแท็ก