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 จะถูกพาร์ส แต่จะถูกตัดออกจากบิลด์สำหรับการใช้งานจริง
ตัวอย่างการใช้งานจริง#
แพ็กเกจ echo ใน coreutils แสดงการใช้งานเฟรมเวิร์กการทดสอบในทางปฏิบัติ การทดสอบอยู่ในไฟล์เดียวกับส่วนการทำงานจริง และครอบคลุมการพาร์สออปชัน การขยายเอสเคป และกรณีขอบ:
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เดียวกันนี้ใช้กับเอาต์พุตการทดสอบ - รองรับหลายเป้าหมาย การทดสอบทำงานผ่านแบ็กเอนด์ที่แพ็กเกจเลือกใช้ — MIR stepper สำหรับ
faber test --interpretและ Rust ที่คอมไพล์แล้วสำหรับfaber test - ชุดทดสอบซ้อนกันได้ บล็อก
probandumสามารถซ้อนกันได้ เพื่อสะท้อนโครงสร้างของโค้ดที่กำลังทดสอบ
แหล่งอ้างอิง#
examples/corpus/probandum/— ไฟล์ตัวอย่างของ probandumexamples/corpus/proba/— ไฟล์ตัวอย่างของ probaexamples/corpus/adfirma/— ไฟล์ตัวอย่างของ adfirmaexamples/coreutils/packages/echo/src/main.fab— การใช้งานจริงพร้อมแท็ก