Skip to content

Commit 88aa7bd

Browse files
committed
feat: enhancing std.Testing to add test cases support to describe tests
1 parent 1e0dea0 commit 88aa7bd

File tree

2 files changed

+103
-21
lines changed

2 files changed

+103
-21
lines changed

Testing.ark

Lines changed: 102 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,9 @@
33
(mut _passed 0)
44
(mut _failed 0)
55
(mut _failures [])
6+
(let _case_desc "")
7+
(mut _cases [])
8+
(mut _case_pointer 0)
69

710
(let _start_time (time))
811
# run test
@@ -21,63 +24,142 @@
2124
(mut _i 0)
2225
(let _failures_count (len _failures))
2326
(while (< _i _failures_count) {
24-
(print "\t" (@ _failures _i))
27+
(print " " (@ _failures _i))
2528
(set _i (+ 1 _i))})
2629

2730
[_passed _failed]}))
2831

32+
(let _case_description (fun (_desc)
33+
(if (empty? _desc)
34+
""
35+
(str:format " for case '{}'" (head _desc)))))
36+
2937
# internal, do not use
30-
(let _report_error (fun (_lhs _rhs _lhs_repr _rhs_repr) {
38+
# Has a _case_desc which also exists (empty) inside _runner so that tests without a
39+
# case won't crash the testing library when trying to access the case name.
40+
# Add the test name to a pile so that we can nicely print all the case names later.
41+
# Update the pointer to current case to its old value later on
42+
(let _case (fun (_case_desc _callable) {
43+
(let _old_pointer _case_pointer)
44+
(append! _cases _case_desc)
45+
(_callable)
46+
(pop! _cases -1)
47+
(set _case_pointer _old_pointer)}))
48+
49+
# @brief Create a test case with a label to help with debugging when one or more tests fail
50+
# @details Test cases can be nested.
51+
# @param _desc a description for the test, a string
52+
# @param _body test to execute
53+
# =begin
54+
# (test:suite name {
55+
# (test:expect (my_function 1 2 3))
56+
# (test:case "a description" {
57+
# (test:expect (return_true) "return true"})
58+
# (test:eq 1 2 "1 is 2, this should fail")})
59+
# =end
60+
# @author https://github.com/SuperFola
61+
($ test:case (_desc _body)
62+
(_case _desc (fun () {_body})))
63+
64+
# internal, do not use
65+
# This can only be used within a (nested or not) call to test:suite
66+
# because it updates _failed and _failures, which are defined by
67+
# test:suite call to _runner
68+
(let _report_error (fun (_lhs _rhs _lhs_repr _rhs_repr _desc) {
3169
(set _failed (+ 1 _failed))
32-
(append! _failures (str:format "{} was not equal to {}" _lhs_repr _rhs_repr))
33-
(let _rhs_start (+ (len _lhs_repr) (len " was not equal to ")))
70+
71+
# Until _case_pointer isn't at the end of the pile (where our failing test case's is),
72+
# iterate on the list, writing the case name in a cascade pattern.
73+
# This way if we have CASE A>CASE B>CASE C and no test crashed in A nor in A>B,
74+
# we are still able to display the cascade A>B>C with the correct indentation.
75+
(let _add_case (fun () {
76+
(let _target_len (len _cases))
77+
(while (< _case_pointer _target_len) {
78+
(mut _indent (* 2 _case_pointer))
79+
(mut _fmt (if (> _indent 0) (+ "{: <" (toString _indent) "}{}") "{}{}"))
80+
(append! _failures (str:format _fmt "" (@ _cases _case_pointer)))
81+
(set _case_pointer (+ 1 _case_pointer))})}))
82+
83+
# If we have a case description AND the pointer isn't up to date, display the case(s)' names
84+
(if (and (not (empty? _case_desc)) (!= _case_pointer (len _cases)))
85+
(_add_case))
86+
87+
# Compute global indent for the failing test resume
88+
(let _indent_case_len (* 2 (len _cases)))
89+
(let _indent (if (> _indent_case_len 0)
90+
(str:format (+ "{: <" (toString _indent_case_len) "}") "")
91+
""))
92+
# Add the error message
93+
(append! _failures (str:format "{}expected '{}' but got '{}'{}" _indent _lhs_repr _rhs_repr (_case_description _desc)))
94+
95+
(let _rhs_start (+ (len _lhs_repr) (len "expected ''")))
3496
(let _lhs_align (len _lhs_repr))
3597
(let _rhs_align (len _rhs_repr))
36-
(append! _failures (str:format (+ "{: <" (toString _rhs_start) "}{:~<" (toString _rhs_align) "} {}") "|" "\\" _lhs))
37-
(append! _failures (str:format (+ "{:~<" (toString _lhs_align) "} {}") "\\" _rhs))}))
98+
(let _show_expected (!= _lhs_repr (toString _lhs)))
99+
100+
(append! _failures
101+
(str:format
102+
(+ "{}{: <" (toString (len "expected ")) "}" "{: <" (toString _rhs_start) "}{:~<" (toString _rhs_align) "} {}")
103+
_indent
104+
# to position one char before the first ' surrounding the expected value
105+
""
106+
# writes the | right under the first ' surrounding the expected value
107+
(if _show_expected "|" "")
108+
# begins the \~~~~ under the real value
109+
"\\"
110+
_rhs))
111+
(if _show_expected
112+
(append! _failures (str:format (+ "{}{: <9}{:~<" (toString _lhs_align) "} {}") _indent "" "\\" _lhs)))}))
38113

39114
# internal, do not use
115+
# This can only be used within a (nested or not) call to test:suite
116+
# because it updates _passed, which is defined by test:suite call to _runner
40117
(let _report_success (fun () (set _passed (+ 1 _passed))))
41118

42119
# @brief Given a value or function call returning a boolean, generate a test case
43120
# @param _cond the value to test for truthiness
121+
# @param _desc an optional description (string) for the test
44122
# =begin
45123
# (test:suite name {
46-
# (test:expect (my_function 1 2 3))})
124+
# (test:expect (my_function 1 2 3))
125+
# (test:expect (return_true) "return true"})
47126
# =end
48127
# @author https://github.com/SuperFola
49-
($ test:expect (_cond) {
128+
($ test:expect (_cond ..._desc) {
50129
(if (!= true _cond)
51130
{
52131
(set _failed (+ 1 _failed))
53-
(append! _failures (str:format "{} was not true but {}" ($repr _cond) _cond)) }
132+
(append! _failures (str:format "{} returned {}{}" ($repr _cond) _cond) (_case_description _desc))}
54133
(_report_success))})
55134

56135
# @brief Compare two values that should be equal and generate a test case
57-
# @param _lhs the first value
58-
# @param _rhs the second value
136+
# @param _expected expected value
137+
# @param _expr computed value to test
138+
# @param _desc an optional description (string) for the test
59139
# =begin
60140
# (test:suite name {
61-
# (test:eq 6 (my_function 1 2 3))})
141+
# (test:eq 6 (my_function 1 2 3))
142+
# (test:eq 5 (foo) "foo should return 5")})
62143
# =end
63144
# @author https://github.com/SuperFola
64-
($ test:eq (_lhs _rhs) {
65-
(if (= _lhs _rhs)
145+
($ test:eq (_expected _expr ..._desc) {
146+
(if (= _expected _expr)
66147
(_report_success)
67-
(_report_error _lhs _rhs ($repr _lhs) ($repr _rhs)))})
148+
(_report_error _expected _expr ($repr _expected) ($repr _expr) _desc))})
68149

69150
# @brief Compare two values that should **not** be equal and generate a test case
70-
# @param _lhs the first value
71-
# @param _rhs the second value
151+
# @param _unexpected the value we don't want
152+
# @param _value tested value
153+
# @param _desc an optional description (string) for the test
72154
# =begin
73155
# (test:suite name {
74156
# (test:neq 0 (my_function 1 2 3))})
75157
# =end
76158
# @author https://github.com/SuperFola
77-
($ test:neq (_lhs _rhs) {
78-
(if (!= _lhs _rhs)
159+
($ test:neq (_unexpected _value ..._desc) {
160+
(if (!= _unexpected _value)
79161
(_report_success)
80-
(_report_error _lhs _rhs ($repr _lhs) ($repr _rhs)))})
162+
(_report_error _unexpected _value ($repr _unexpected) ($repr _value) _desc))})
81163

82164
# @brief Generate the code for a test suite
83165
# @details Create two variables: _name-output (a list: [successes, failures]) and _name-status (boolean, true on success)

tests/events-tests.ark

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55

66
(test:suite events {
77
# _check_valid tests
8-
(test:expect (em._check_valid (fun () ())))
8+
(test:expect (em._check_valid (fun () ())) "a function is valid")
99
(test:expect (not (em._check_valid 4)))
1010

1111
# on tests

0 commit comments

Comments
 (0)