nx_test_harness.nx source
↩ module page · 214 lines · 5998 B
1// nx_test_harness.nx -- assertion DSL for the bits-up test driver.
2// Replaces the JS `expect()` / `fail++` / `pass++` accounting in the
3// .mjs Playwright harnesses with NishiLang.
4//
5// Test files call:
6// nx_test_init() // once at start
7// nx_test_expect_eq(label, got, want)
8// nx_test_expect_contains(label, html, n, needle)
9// nx_test_expect_class_count(label, html, n, class_name, want_count)
10// nx_test_expect_click(label, intent, want_action, want_arg0)
11// return nx_test_summary() // returns 0 if all pass, 1 if any fail
12//
13// Output is human-readable: one [PASS]/[FAIL] line per assertion + a
14// summary at the end matching the .mjs format ("X pass, Y fail").
15
16import "nx_dom_query.nx"
17import "nx_dom_click.nx"
18
19const _TH_STDOUT: i64 = 1
20const _TH_SYS_WRITE: i64 = 1 // syscall number for x86_64 write
21
22static NX_TEST_PASSES: i64
23static NX_TEST_FAILS: i64
24
25func nx_test_init() -> i64 {
26 NX_TEST_PASSES = 0
27 NX_TEST_FAILS = 0
28 return 0
29}
30
31func _th_strlen(s: *u8) -> i64 {
32 var n: i64 = 0
33 while s[n] != 0 { n = n + 1 }
34 return n
35}
36
37func _th_w(s: *u8) -> i64 {
38 return sys_write(_TH_STDOUT, s, _th_strlen(s))
39}
40
41func _th_wn(s: *u8, n: i64) -> i64 {
42 return sys_write(_TH_STDOUT, s, n)
43}
44
45func _th_wln(s: *u8) -> i64 {
46 _th_w(s)
47 let nl: *u8 = sys_mmap(2) as *u8
48 nl[0] = 10 as u8
49 return sys_write(_TH_STDOUT, nl, 1 as i64)
50}
51
52// Write a signed decimal int. Handles negatives + up to 20 digits.
53func _th_wi(n: i64) -> i64 {
54 let buf: *u8 = sys_mmap(24) as *u8
55 var x: i64 = n
56 var neg: i64 = 0
57 if x < 0 { neg = 1; x = 0 - x }
58 var digits: *u8 = sys_mmap(24) as *u8
59 var nd: i64 = 0
60 if x == 0 {
61 digits[0] = 48 as u8
62 nd = 1
63 }
64 while x > 0 {
65 digits[nd] = ((x - (x / 10) * 10) + 48) as u8
66 nd = nd + 1
67 x = x / 10
68 }
69 var off: i64 = 0
70 if neg == 1 { buf[off] = 45 as u8; off = off + 1 }
71 var i: i64 = nd
72 while i > 0 {
73 i = i - 1
74 buf[off] = digits[i]
75 off = off + 1
76 }
77 return sys_write(_TH_STDOUT, buf, off)
78}
79
80// ===== Assertions ===================================================
81
82func nx_test_expect_eq(label: *u8, got: i64, want: i64) -> i64 {
83 if got == want {
84 _th_w(" [PASS] " as *u8)
85 _th_wln(label)
86 NX_TEST_PASSES = NX_TEST_PASSES + 1
87 return 1
88 }
89 _th_w(" [FAIL] " as *u8)
90 _th_w(label)
91 _th_w(" (got " as *u8)
92 _th_wi(got)
93 _th_w(" want " as *u8)
94 _th_wi(want)
95 _th_wln(")" as *u8)
96 NX_TEST_FAILS = NX_TEST_FAILS + 1
97 return 0
98}
99
100// Substring search. Returns 1 if needle in haystack.
101func _th_contains(hay: *u8, hay_len: i64, ndl: *u8, ndl_len: i64) -> i64 {
102 if ndl_len == 0 { return 1 }
103 if hay_len < ndl_len { return 0 }
104 var i: i64 = 0
105 let end: i64 = hay_len - ndl_len
106 while i <= end {
107 var j: i64 = 0
108 var hit: i64 = 1
109 while j < ndl_len {
110 if hay[i + j] != ndl[j] { hit = 0; j = ndl_len }
111 j = j + 1
112 }
113 if hit == 1 { return 1 }
114 i = i + 1
115 }
116 return 0
117}
118
119func nx_test_expect_contains(label: *u8, html: *u8, n: i64, needle: *u8) -> i64 {
120 let nl: i64 = _th_strlen(needle)
121 if _th_contains(html, n, needle, nl) == 1 {
122 _th_w(" [PASS] " as *u8)
123 _th_wln(label)
124 NX_TEST_PASSES = NX_TEST_PASSES + 1
125 return 1
126 }
127 _th_w(" [FAIL] " as *u8)
128 _th_w(label)
129 _th_w(" (needle '" as *u8)
130 _th_w(needle)
131 _th_wln("' not in HTML)" as *u8)
132 NX_TEST_FAILS = NX_TEST_FAILS + 1
133 return 0
134}
135
136func nx_test_expect_not_contains(label: *u8, html: *u8, n: i64,
137 needle: *u8) -> i64 {
138 let nl: i64 = _th_strlen(needle)
139 if _th_contains(html, n, needle, nl) == 0 {
140 _th_w(" [PASS] " as *u8)
141 _th_wln(label)
142 NX_TEST_PASSES = NX_TEST_PASSES + 1
143 return 1
144 }
145 _th_w(" [FAIL] " as *u8)
146 _th_w(label)
147 _th_w(" (needle '" as *u8)
148 _th_w(needle)
149 _th_wln("' UNEXPECTEDLY in HTML)" as *u8)
150 NX_TEST_FAILS = NX_TEST_FAILS + 1
151 return 0
152}
153
154func nx_test_expect_class_count(label: *u8, html: *u8, n: i64,
155 class_name: *u8, want: i64) -> i64 {
156 let got: i64 = nx_dom_count_class(html, n, class_name)
157 return nx_test_expect_eq(label, got, want)
158}
159
160func nx_test_expect_tag_count(label: *u8, html: *u8, n: i64,
161 tag_name: *u8, want: i64) -> i64 {
162 let got: i64 = nx_dom_count_tag(html, n, tag_name)
163 return nx_test_expect_eq(label, got, want)
164}
165
166func nx_test_expect_click(label: *u8, intent: *NxClickIntent,
167 want_action: i64, want_arg0: i64) -> i64 {
168 if intent.found != 1 {
169 _th_w(" [FAIL] " as *u8)
170 _th_w(label)
171 _th_wln(" (intent.found = 0)" as *u8)
172 NX_TEST_FAILS = NX_TEST_FAILS + 1
173 return 0
174 }
175 if intent.action != want_action {
176 _th_w(" [FAIL] " as *u8)
177 _th_w(label)
178 _th_w(" (action: got " as *u8)
179 _th_wi(intent.action)
180 _th_w(" want " as *u8)
181 _th_wi(want_action)
182 _th_wln(")" as *u8)
183 NX_TEST_FAILS = NX_TEST_FAILS + 1
184 return 0
185 }
186 if intent.arg0 != want_arg0 {
187 _th_w(" [FAIL] " as *u8)
188 _th_w(label)
189 _th_w(" (arg0: got " as *u8)
190 _th_wi(intent.arg0)
191 _th_w(" want " as *u8)
192 _th_wi(want_arg0)
193 _th_wln(")" as *u8)
194 NX_TEST_FAILS = NX_TEST_FAILS + 1
195 return 0
196 }
197 _th_w(" [PASS] " as *u8)
198 _th_wln(label)
199 NX_TEST_PASSES = NX_TEST_PASSES + 1
200 return 1
201}
202
203// ===== Summary ======================================================
204
205func nx_test_summary() -> i64 {
206 _th_w("========== SUMMARY ==========" as *u8)
207 _th_wln("" as *u8)
208 _th_wi(NX_TEST_PASSES)
209 _th_w(" pass, " as *u8)
210 _th_wi(NX_TEST_FAILS)
211 _th_wln(" fail" as *u8)
212 if NX_TEST_FAILS > 0 { return 1 }
213 return 0
214}