ตัวลดขนาดเคสทดสอบ: เครื่องมือดีบักที่ถูกมองข้าม
(tratt.net)- เมื่อพยายามหาว่าส่วนใดของอินพุตขนาดใหญ่ที่ทำให้เกิดปัญหา ตัวลดขนาดเคสทดสอบ จะช่วยย่ออินพุตให้อัตโนมัติเพื่อให้ดีบักได้ง่ายขึ้น
- ตัวลดขนาดจะรับโปรแกรม อินพุต และ การทดสอบความน่าสนใจ แล้วตรวจซ้ำว่าข้อมูลนำเข้าตัวเลือกที่สั้นลงยังสร้างปัญหาเดิมได้หรือไม่
- แม้แต่ตัวลดขนาดแบบลบบรรทัดอย่างง่ายก็ยังสามารถเหลือคำยาวคำเดียวจาก
/usr/share/dict/wordsได้ และในตัวอย่าง C ก็ลดจาก 78 บรรทัดเหลือ 54 บรรทัดได้ในเวลาไม่ถึง 10 วินาที - การทดสอบความน่าสนใจต้องเขียนให้แม่นยำและรวดเร็ว เพราะมีปัจจัยอย่างการย่อเกินจำเป็น การรันช้า การรันไม่จบ และสภาพแวดล้อมการรันแบบขนาน
- นอกจากความยาวอินพุตแล้ว หากใส่ตัวชี้วัดอย่างความถี่ในการเกิดข้อผิดพลาดหรือความยาวของร่องรอยการทำงานลงใน การทดสอบความน่าสนใจ ก็จะช่วยดีบัก บั๊กแบบไม่กำหนดแน่นอน และล็อกร่องรอยขนาดใหญ่ได้
การย่อเคสทดสอบ
- เมื่อโปรแกรมล่มกับอินพุตขนาดใหญ่ และไม่รู้ว่าส่วนใดของอินพุตเป็นสาเหตุ การย่ออินพุตจะช่วยให้หาต้นตอของปัญหาได้ง่ายขึ้น
- การย่อด้วยมือคือการลบบางส่วนของอินพุตในโปรแกรมแก้ไขข้อความ แล้วตรวจว่าการล่มเดิมยังคงเกิดอยู่หรือไม่
- การย่อด้วยมือทำให้คนมักพลาดโอกาสลบหลายจุด และหลังการลบ โปรแกรมอาจจบการทำงานตามปกติหรือแสดงข้อผิดพลาดปกติชนิดอื่นแทน
- หากต้องลบส่วน A และ B ที่อยู่ห่างกันพร้อมกันจึงจะได้ผล พื้นที่ค้นหาจะขยายใหญ่ขึ้นมาก
โครงสร้างพื้นฐานของตัวลดขนาดเคสทดสอบ
- ตัวลดขนาดเคสทดสอบคือเครื่องมือที่รับโปรแกรม อินพุต และ การทดสอบความน่าสนใจ แล้วทำให้อินพุตสั้นลง
- การทดสอบความน่าสนใจจะคืนค่า 0 หากอินพุตที่ถูกย่อแล้วยังสร้างข้อผิดพลาดที่เราสนใจได้ และคืนค่าอื่นที่ไม่ใช่ 0 หากไม่เป็นเช่นนั้น
- ตัวลดขนาดเคสทดสอบมักย่อได้ 95~99% และช่วยให้การดีบักง่ายขึ้นมาก
- ตัวลดขนาดทำงานได้แม้ไม่เข้าใจความหมายเชิงโครงสร้างของส่วนต่าง ๆ ในอินพุตว่าควรลบอะไร
-
ตัวอย่างตัวลดขนาดอย่างง่าย
- โปรแกรมตัวอย่างจะอ่านบรรทัดจากไฟล์ และถ้ามีบรรทัดใดยาวเกิน 25 อักขระ จะพิมพ์
Word too long - การทดสอบความน่าสนใจจะคืนค่า 0 หากเอาต์พุตของโปรแกรมมี
Word too longและคืนค่า 1 หากไม่มี - ตัวลดขนาด Python แบบง่ายจะอ่านอินพุตทีละบรรทัด เขียนอินพุตตัวเลือกที่ลบบรรทัดหนึ่งออกลงไฟล์ชั่วคราว แล้วรันการทดสอบความน่าสนใจ
- หากอินพุตตัวเลือกยังน่าสนใจ ก็จะเปลี่ยนอินพุตปัจจุบันเป็นตัวเลือกนั้น และถ้าลดต่อไม่ได้แล้วก็พิมพ์ผลลัพธ์ไปที่
stdout - เมื่อนำไปรันกับ
/usr/share/dict/wordsผลลัพธ์จะเหลือเพียงantidisestablishmentarianism
- โปรแกรมตัวอย่างจะอ่านบรรทัดจากไฟล์ และถ้ามีบรรทัดใดยาวเกิน 25 อักขระ จะพิมพ์
ตัวลดขนาดที่ทรงพลังกว่าและ Shrink Ray
- ตัวอย่างโปรแกรม C 78 บรรทัดเกี่ยวข้องกับปัญหาที่ให้ผลลัพธ์ต่างกันระหว่างการตั้งค่า
FAST=0และFAST=1 - การทดสอบความน่าสนใจจะคอมไพล์ด้วยทั้งสองการตั้งค่า แล้วผ่านก็ต่อเมื่อเอาต์พุตของ
FAST=0เป็น0d754a56และเอาต์พุตของFAST=1ต่างจากค่านั้น - ตัวลดขนาดอย่างง่ายสามารถลดอินพุต C 78 บรรทัดให้เหลือ 54 บรรทัดได้ในเวลาไม่ถึง 10 วินาที คิดเป็นการย่อประมาณ 30% ตามจำนวนบรรทัด
- หากเพิ่ม
i=0เพื่อให้กลับไปเริ่มลบจากบรรทัดแรกใหม่ทุกครั้งที่พบอินพุตตัวเลือกที่น่าสนใจ เวลารันจะเพิ่มขึ้นเกือบ 10 เท่า แต่ลดได้เพิ่มอีก 3 บรรทัด - Shrink Ray มีทั้งกฎการย่อหลายแบบและการรันแบบขนาน และถ้าใส่
--no-clang-deltaก็จะไม่ใช้ความรู้เฉพาะทางเกี่ยวกับ C - Shrink Ray ใช้เวลาราว 15 นาทีเพื่อลดอินพุตลงมากกว่า 60% ตามขนาดไบต์ และในอีกกรณีหนึ่งพบการย่อราว 90% หลังประมาณ 20 นาที ก่อนจะลดต่อไปจนถึง 99%
- Shrink Ray รู้จักไวยากรณ์คอมเมนต์มาตรฐานและพยายามลบส่วนนั้นก่อนตั้งแต่ต้น รวมถึงพยายามลดจำนวนเต็มให้เป็นค่าที่เล็กลงด้วย
ความยากในการเขียนการทดสอบความน่าสนใจ
- ตัวลดขนาดเคสทดสอบจะทำตามการทดสอบความน่าสนใจแบบตรงตัว ดังนั้นถ้าการทดสอบผ่านอย่างผิดพลาด ก็อาจเกิด การย่อเกินจำเป็น ที่ลดเลยจุดที่เราต้องการ
- Shrink Ray จะตรวจอย่างชัดเจนว่าการทดสอบความน่าสนใจยอมรับอินพุตว่างหรือไม่ และสถานการณ์แบบนี้เกิดขึ้นได้บ่อย
- ในตัวอย่าง C หากตรวจแค่ว่าเอาต์พุตทั้งสองต่างกันหรือไม่ ก็อาจจัดให้ความแตกต่างของเอาต์พุตที่ไม่สำคัญหรือชวนให้เข้าใจผิดเป็นอินพุตที่น่าสนใจได้
- การตรวจ
test "$slow_out" = "0d754a56"ช่วยยืนยันว่าเวอร์ชันช้าทำงานตามที่คาดจริง จึงลดโอกาสการย่อเกินจำเป็น -
ความเร็วและการตั้งเวลา timeout
- หากการทดสอบความน่าสนใจเร็ว ตัวลดขนาดอาจรันได้หลายร้อยครั้งต่อวินาที
- แม้แต่ตัวอย่างขนาดกลางก็อาจนำไปสู่ความพยายามย่อหลายแสนครั้ง ดังนั้นการปรับแต่งการทดสอบความน่าสนใจจึงมีผลมากต่อเวลารวม
- มีกรณีที่ทำให้การทดสอบความน่าสนใจเร็วขึ้นราว 3 เท่าโดยปิดการสร้าง core dump อัตโนมัติ
- ตัวลดขนาดอาจลบบรรทัดอย่าง
i-=1แล้วทำให้โปรแกรมที่เคยจบการทำงานได้ กลายเป็นโปรแกรมที่รันไม่สิ้นสุด - ถ้าโปรแกรมใช้เวลา 0.1 วินาทีต่อการรัน แต่ตั้ง timeout ไว้ 60 วินาที การย่อทั้งกระบวนการจะช้าลงมาก
- สำหรับโปรแกรมที่เร็ว มักตั้ง
timeoutแบบปัดขึ้นเป็น 1~2 วินาที ส่วนกรณีอื่นมักใช้ราว 1.5~2 เท่าของเวลาเริ่มต้นในการรัน
-
การรันแบบขนาน
- ตัวลดขนาดอย่าง Shrink Ray จะรันการทดสอบความน่าสนใจแบบขนาน
- Shrink Ray จะรันการทดสอบความน่าสนใจแต่ละครั้งในไดเรกทอรีชั่วคราว และล้างไดเรกทอรีนั้นให้อัตโนมัติ
- บางกรณีไดเรกทอรีชั่วคราวอย่างเดียวก็ยังไม่พอ และมาตรการที่ต้องใช้จะแตกต่างกันไปในแต่ละกรณี
ทำให้เกิดความเป็นกำหนดแน่นอนด้วยการทดสอบความน่าสนใจ
- โค้ดตัวอย่างสร้างข้อผิดพลาดหารด้วยศูนย์เพราะ
len([])==0แต่เนื่องจากมีเงื่อนไขrandom.random() < 0.33ปัญหาจึงเกิดขึ้นเพียงประมาณหนึ่งในสามของการรัน - บั๊กแบบไม่กำหนดแน่นอนทำให้ข้อผิดพลาดปรากฏและหายไปแบบสุ่ม ส่งผลให้การตรวจสอบสมมติฐานยากขึ้นและใช้เวลานานขึ้น
- หากตัวลดขนาดลบการเรียก
random.random()ออก หรือเปลี่ยนนิพจน์เงื่อนไข บั๊กที่ไม่กำหนดแน่นอนก็อาจกลายเป็นบั๊กที่เกิดซ้ำได้แน่นอน - ความไม่กำหนดแน่นอนในโลกจริงมักมาจากหลายส่วนของอินพุตโต้ตอบกันในทางไม่พึงประสงค์ จึงอาจกำจัดออกได้ยาก
- ตัวลดขนาดเคสทดสอบทำงานคล้ายอัลกอริทึมไต่เขาที่ใช้ความยาวอินพุตเป็นตัวชี้วัดแทนว่าอะไร “ดีกว่า”
- แนวทางแบบไต่เขามักติดอยู่ที่จุดเหมาะสมเฉพาะที่ และอินพุตที่สั้นกว่าก็ไม่ได้แปลว่าจะดีกว่าสำหรับการค้นหาบั๊กเสมอไป
-
วิธีรันซ้ำหลายครั้ง
- เมื่อต้องจัดการกับบั๊กแบบไม่กำหนดแน่นอน มักใช้การทดสอบความน่าสนใจที่รันอินพุตเดียวกันหลายครั้ง และยอมรับอินพุตนั้นหากข้อผิดพลาดที่สนใจเกิดขึ้นอย่างน้อยหนึ่งครั้ง
- วิธีนี้อาจช่วยเพิ่มความถี่ในการเกิดข้อผิดพลาดได้
- แต่การทดสอบที่ผ่านเมื่อเกิดเพียงครั้งเดียวจะยังยอมรับอินพุตแบบไม่กำหนดแน่นอน จึงอาจทำให้ระหว่างการย่อ ความไม่กำหนดแน่นอนเพิ่มขึ้นแทน
- วิธีที่เข้มงวดกว่าคือทดสอบให้ผ่านเฉพาะเมื่อข้อผิดพลาดเกิดขึ้นครบทุกครั้งจากการรันซ้ำ
nครั้ง - การทดสอบที่เข้มงวดทำให้ความน่าจะเป็นที่อินพุตเริ่มต้นจะผ่านต่ำ จึงเริ่มใช้ Shrink Ray ได้ยาก และในตัวอย่างที่กำหนดให้รันซ้ำ 3 ครั้ง ความน่าจะเป็นเริ่มต้นที่จะผ่านมีเพียง 3.6%
- วิธีเลี่ยงที่ใช้งานได้จริงคือเริ่มจากการทดสอบแบบ “เกิดข้อผิดพลาดอย่างน้อย 1 ครั้งจาก
nครั้ง” ก่อน แล้วเมื่อได้อินพุตที่ย่อแล้วซึ่งทำให้ข้อผิดพลาดเกิดบ่อยขึ้น ค่อยเปลี่ยนเป็น “เกิดข้อผิดพลาดต่อเนื่องnครั้ง”
ตัวนับแบบโกลบอลและตัวชี้วัดเป้าหมายอื่น ๆ
- การแทรกแซงด้วยมือนั้นทรงพลัง แต่ต้องคอยเฝ้า Shrink Ray และพลาดจังหวะแทรกแซงได้ง่าย
- หากต้องการชี้นำตัวลดขนาดด้วยคุณสมบัติอื่นที่ไม่ใช่ความยาวอินพุต ก็สามารถบังคับคุณสมบัตินั้นไว้ภายในการทดสอบความน่าสนใจเดียวได้
- ในการดีบัก yk สิ่งที่สำคัญกว่าความยาวอินพุตคือความยาวของร่องรอยการทำงาน หรือค่าที่ใกล้เคียงกับจำนวนคำสั่งที่โปรแกรมรัน
- เอาต์พุต
YKD_LOG="$t:jit-asm"จะเขียนทั้ง trace IR แบบข้อความและคำสั่งภาษาเครื่องลงไฟล์ และเอาต์พุตjit-asmที่สั้นกว่าจะทำให้ดีบักง่ายขึ้น wc -lใช้นับจำนวนบรรทัดของไฟล์ล็อก เพื่อเป็นตัวชี้วัดแทนที่ใกล้เคียงกับความยาวของร่องรอย- การทดสอบความน่าสนใจจะถือว่าอินพุตไม่น่าสนใจ หากจำนวนบรรทัดของร่องรอยปัจจุบันมากกว่าค่าต่ำสุดก่อนหน้า โดยเก็บค่าต่ำสุดไว้ที่
/tmp/global_best - วิธีนี้ไม่ปลอดภัยสำหรับการย่อแบบขนาน และมีสมมติฐานบางอย่างเกี่ยวกับวิธีที่ตัวลดขนาดเรียกใช้งาน แต่สำหรับสคริปต์สั้น ๆ ที่เขียนใช้แล้วทิ้งก็ถือเป็นความไม่สมบูรณ์ที่ยอมรับได้
- ในกรณี segfault ของ yk การย่อแบบทั่วไปยังคงเหลือร่องรอย 40K บรรทัด แต่เทคนิคนี้ให้ร่องรอย 10.1K บรรทัดแทนอินพุตที่ใหญ่กว่าซึ่งถูกย่อ และช่วยให้หาบั๊กรากเหง้าได้ภายใน 30 นาที
สรุปประเด็นสำคัญ
- ตัวลดขนาดเคสทดสอบไม่ใช่เครื่องมือที่มีประโยชน์เฉพาะสำหรับผู้เขียนคอมไพเลอร์เท่านั้น แต่ยังใช้กับปัญหาที่ไม่ใช่คอมไพเลอร์ได้ด้วย
- นอกจากเป้าหมายพื้นฐานอย่างการลดความยาวอินพุตแล้ว ยังสามารถชี้นำด้วยการทดสอบความน่าสนใจให้มุ่งไปที่คุณสมบัติอย่างความถี่ของข้อผิดพลาด เวลาแบบ wall-clock ระดับความไม่กำหนดแน่นอน หรือความยาวของร่องรอยได้
- ความแม่นยำ ความเร็ว เวลา timeout และความปลอดภัยในการรันแบบขนานของการทดสอบความน่าสนใจ เป็นตัวกำหนดประสิทธิผลจริงของตัวลดขนาด
- แม้ตัวลดขนาดแทบไม่เข้าใจความหมายของอินพุตและโปรแกรม แต่ก็ยังช่วยคงปัญหาไว้ในรูปที่เล็กลง และเพิ่มประสิทธิภาพการดีบักได้
1 ความคิดเห็น
ความคิดเห็นจาก Lobste.rs
สงสัยจริง ๆ ว่ามีใครบ้างที่ไม่เห็นคุณค่าของ การย่อ test case อัตโนมัติ? คำว่า “ถูกประเมินต่ำไป” ฟังเหมือนมีคนที่ไม่ต้องการการย่อ test case อยู่เสมอ
ต่อให้ระบุบั๊กได้ทันที ก็ยังต้องมีเคสที่ย่อแล้วไว้ใช้เป็น regression test ไม่ใช่หรือ?
ทั้งสองอย่างมักรวมการย่อเคสล้มเหลวหรือ “shrinking” เอาไว้ในรูปแบบใดรูปแบบหนึ่ง และนั่นทำให้ใช้งานได้จริงมากขึ้นมาก
แต่จากประสบการณ์ที่ใช้ fuzzing โดยรวม โดยเฉพาะ AmericanFuzzyLop และ AFL++ การตั้งค่ามันทรมานเกินไปจนมักจะเลี่ยง
บั๊กส่วนใหญ่ที่ผมเจอก็ไม่ได้เป็นแนว “ใส่ไฟล์อินพุตนี้แล้วจะทำงานผิด” แต่จะใกล้กับ “มีผู้ใช้บางคนที่ไหนสักแห่งเจอการทำงานผิด” มากกว่า บางครั้งพอลดรูปได้ถึงระดับ “ถ้าทำตามขั้นตอนชุดหนึ่งภายใต้เงื่อนไขเฉพาะแล้วจะพัง” แต่ 1) ไม่ค่อยรู้ว่าจะเอาตัวลด test case อัตโนมัติมาใช้กับ “ผู้ใช้ทำอะไรตามลำดับ” ยังไง และ 2) พอหาวิธีทำให้เกิดซ้ำบนเครื่องตัวเองได้ การดีบักก็ถือว่าเสร็จไป 99% แล้ว
ผู้เขียนคงมองท่าทีแบบนี้ของผมว่าเป็นการ “ประเมินต่ำไป”
แม้บทความและตัวอย่างนี้จะบอกว่าตัวลดเคสควรถูกใช้ให้แพร่หลายขึ้นในบริบทที่ไม่ใช่คอมไพเลอร์ด้วย แต่มุมมองก็ยังเอนเอียงไปทาง ผู้เขียนคอมไพเลอร์ มากพอสมควร
อย่างที่ ~silentbicycle เขียนไว้ การย่อ test case ส่วนใหญ่เกิดขึ้นในบริบทของ fuzzer หรือการทดสอบเชิงคุณสมบัติ และความสามารถในการย่อมักฝังมาในเฟรมเวิร์กที่ใหญ่กว่าอยู่แล้ว คอมไพเลอร์เป็นหนึ่งในโดเมนพิเศษที่ตัวย่อ test case แบบแยกเดี่ยวมีประโยชน์ ผมก็ไม่แน่ใจว่ามีกรณีอื่นอีกไหมที่ตัวย่อแบบแยกเดี่ยวจะช่วยได้
ประเด็นเรื่อง ความกำหนดแน่นอน ก็น่าสนใจ ตัวอย่างเริ่มจากไฟล์อินพุตที่ก่อบั๊ก คือสคริปต์ ซึ่งทำให้เกิดความกำหนดแน่นอน ไม่ใช่จากตัวโปรแกรมที่มีบั๊กอย่างอินเทอร์พรีเตอร์เอง บทความไม่ได้ชัดเจนนักว่าหมายถึงเทคนิค “interestingness” ใช้ได้กับสถานการณ์ที่ไม่ใช่คอมไพเลอร์ซึ่งตัวโปรแกรมที่มีบั๊กเองก็ไม่กำหนดแน่นอนด้วยหรือไม่
วิธีหนึ่งในการปรับปัญหาการทดสอบให้เข้ากับ fuzzing และการย่อ test case คือแนะนำให้สร้างชุดคำสั่งเชิงคำสั่งที่มีหมายเลขกำกับ แต่ละคำสั่งใส่การตรวจสอบความสอดคล้องแบบเบา ๆ เพื่อจับความล้มเหลวของการทดสอบ รวมถึงกรณีที่ไม่ crash ทันที ส่วนการตรวจสอบที่หนักกว่าควรแยกเป็นอีกคำสั่งหนึ่งเพื่อลดผลกระทบต่อความเร็วการทดสอบ ในการทดสอบแบบสุ่มง่าย ๆ ก็ให้ test harness สุ่มเลือกคำสั่งไปเรื่อย ๆ จนกว่าจะมีอะไรพัง แล้วตอนเปลี่ยนไปใช้ harness ของ fuzzer ก็ให้ใช้สตรีมไบต์อินพุตจากการ fuzz เพื่อเลือกคำสั่ง เท่านี้ก็จะได้ของดีอย่าง regression test ที่กำหนดแน่นอนและการย่อ test case แบบอัตโนมัติ
ผมไม่เคยได้ผลอะไรมากนักจากการสั่ง libfuzzer ให้ย่อ test case อย่างชัดเจน น่าจะเพราะมันย่อไปแล้วระหว่างสร้างอินพุตอยู่ก่อนแล้ว เลยไม่ค่อยมีแรงจูงใจจะลองเพิ่มการตรวจ interestingness สำหรับการย่อเคสด้วย fuzzer ทั่วไปมากกว่านี้ สงสัยเหมือนกันว่าคนอื่นเคยทำสำเร็จไหม
จะเรียกมันว่าการทดสอบเชิงคุณสมบัติ, fuzzing หรือ model checking แบบเบา ๆ ก็ได้ แต่มันมีประสิทธิภาพน่าทึ่งในการพบบั๊กลึก ๆ ผมเห็นอินเทอร์เฟซแบบมีสถานะจำนวนมากที่แต่ละ operation เองถูกต้อง แต่สมมติฐานของมันเหลื่อมกันนิดหน่อย และเมื่อ operation เหล่านี้ถูกนำมาผสมกันในแบบที่ไม่คาดคิด ก็ลุกลามเป็นความเสียหายภายในได้
ยังมีประโยชน์มากถ้ารันรายการ operation นี้ควบคู่ไปกับ implementation แบบง่ายที่เป็น in-memory hash table หรือแบบ list แล้วตรวจด้วยว่าผลลัพธ์ตรงกันไหม ถ้าต่างกันก็มักเป็นบั๊ก หรือไม่ก็เป็นกรณีขอบที่ควรมีเอกสารอธิบายให้ดีกว่านี้
น่าเสียดายที่ไฟล์ข้อมูลซับซ้อนเกินไปจน shrinkray น่าจะจัดการได้ยาก มันอ่านข้อมูลตารางจาก “ไฟล์” หลายชุดที่แตกต่างกัน และยังมีการพึ่งพากันระยะไกลด้วย ดังนั้นคงต้องเข้ารหัสความรู้เฉพาะโดเมนเกี่ยวกับวิธีย่อเอาเอง
ด้วยความเร็วของความก้าวหน้าด้าน AI ถ้ามีสถานการณ์แบบนี้อีกครั้งหน้า ผมคงเขียนตัวย่อเฉพาะงานขึ้นมาเอง
[0] ถ้าจะดึงประเด็นออนโทโลยีที่กำกวมมาใช้ ปัญหาการหาค่าเหมาะที่สุดคือปัญหาการค้นหาที่ทำให้ต้นทุนต่ำสุด และนั่นก็แทบจะเป็นสิ่งเดียวกับคอมไพเลอร์ ดังนั้นจึงไม่ใช่ตัวอย่างที่สมบูรณ์นัก
ผมอ่านสามรอบเพื่อพยายามหาว่าจะเอาสิ่งนี้ไปใช้กับเทสต์ที่เขียนด้วย pytest ยังไง
ผมอยากลดความซับซ้อนของ test suite เลยคิดว่าไว้ว่างจากงานจะกลับมาอ่านรอบที่สี่
ปีที่แล้วตอนกำลังดูปัญหาเรื่อง ลำดับการรันเทสต์ ใน CI ผมทำเครื่องมือขึ้นมาตัวหนึ่งเพื่อช่วยย่อรายการเทสต์
โดยพื้นฐานคือมันลองตัดบรรทัดออกทีละครึ่งแล้วรันดู
ตัวสคริปต์เองมีบั๊กอยู่พอสมควร แต่การที่รายชื่อเทสต์ 5000 ตัวถูกย่อเหลือประมาณ 4 เทสต์ที่ก่อให้เกิดบั๊กด้าน concurrency ของผมได้นั้นเจ๋งมาก
ผมสงสัยจริง ๆ ว่าในกรณีของผม Shrink Ray จะใช้ได้เลยหรือเปล่า ผมเชื่อจริง ๆ ว่า “การย่อเซ็ตของบรรทัดโดยอิงจากเทสต์” ควรเป็นความสามารถที่มีอยู่ในชุด เครื่องมือบรรทัดคำสั่ง มาตรฐาน
ที่เกี่ยวข้องกับเรื่องนี้ การทดสอบเชิงคุณสมบัติ ก็ใช้แนวทางที่คล้ายกันมาก โดย “ย่อ” state space ของอินพุตที่ถูกสร้างขึ้นมาเพื่อหาตัวอย่างโต้แย้งของการทดสอบ
ข้อดีของการทดสอบเชิงคุณสมบัติคือคุณสามารถชี้นำและจัดโครงสร้างพื้นที่ค้นหาได้ คุณสามารถทำให้อินพุตเป็นชุดของ transition ที่ขับเคลื่อน state machine ซึ่งใช้จำลองโปรแกรมได้
ผมยังแปลกใจทุกครั้งที่เห็นว่าเทคนิคนี้ถูกใช้น้อยเกินไป แม้แต่ในโดเมนที่มันเหมาะมากเป็นพิเศษ เช่น ฐานข้อมูลและระบบกระจายศูนย์ แค่สัปดาห์ก่อนเองที่ $WORK ผมสร้างเทสต์ลักษณะนี้ขึ้นมาได้ภายในเวลาไม่กี่ชั่วโมง และก็พบอย่างรวดเร็วว่าระบบของเราไม่ convergent เทสต์พิมพ์ trace ที่สะอาดและเข้าใจได้ทันทีเมื่อเอาไปให้เพื่อนร่วมงานดู
โดยส่วนตัว ผมมองว่านี่เป็นเทคนิคการทดสอบที่ คุ้มค่าการลงทุน มากที่สุดเวลาต้องดีบักระบบซับซ้อน