├── .gitignore ├── LICENSE.txt ├── Makefile ├── README.md ├── deploy.ftp ├── src ├── includes │ ├── kunit.h.kl │ └── vendor │ │ └── strings.h.kl ├── kunit.kl └── test_kunit.kl └── vendor └── strings.pc /.gitignore: -------------------------------------------------------------------------------- 1 | *.swp 2 | robot.ini 3 | bin/ 4 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2014 ONE Robotics Company LLC 2 | 3 | MIT License 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining 6 | a copy of this software and associated documentation files (the 7 | "Software"), to deal in the Software without restriction, including 8 | without limitation the rights to use, copy, modify, merge, publish, 9 | distribute, sublicense, and/or sell copies of the Software, and to 10 | permit persons to whom the Software is furnished to do so, subject to 11 | the following conditions: 12 | 13 | The above copyright notice and this permissions notice shall be 14 | included in all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 19 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 20 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 21 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 22 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 23 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | KL_FILES := $(wildcard src/*.kl) 2 | 3 | PC_FILES = $(addprefix bin/,$(notdir $(KL_FILES:.kl=.pc))) 4 | 5 | bin/%.pc: src/%.kl src/includes/*.kl 6 | ktrans $< $@ 7 | rm *.pc 8 | 9 | all: $(PC_FILES) 10 | 11 | .PHONY : clean todo deploy test 12 | 13 | clean: 14 | rm bin/*.* 15 | 16 | todo: 17 | grep -i "todo" src/*.* 18 | 19 | test: 20 | @curl -s http://localhost/karel/kunit?filenames=test_kunit 21 | 22 | deploy: 23 | ftp -s:deploy.ftp localhost 24 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # KUnit 2 | 3 | KUnit is a simple unit testing framework for FANUC's KAREL 4 | programming language. KUnit provides useful assertions and test output 5 | feedback via the web browser. 6 | 7 | ## Example Output 8 | 9 | KUnit v1.0.0 10 | 11 | ........ 12 | 13 | Finished in 0.002 seconds 14 | 4000.0 tests/sec, 9500.0 assertions/sec 15 | 16 | 8 tests, 19 assertions, 0 failures 17 | 18 | ## Usage 19 | 20 | 1. Download the latest release 21 | 2. Copy the `kunit.pc` and `vendor/strings.pc` files to your robot 22 | 3. Copy the `kunit.h.kl` to your project's support directory or the 23 | same directory as your KAREL test file 24 | 4. `%INCLUDE kunit.h` in your test KAREL program 25 | 5. Use the KUnit assertions as described below 26 | 6. Make sure to use `kunit_done` at the end of your test file 27 | 7. Translate and deploy your KAREL program to your robot 28 | 8. Run the test suite at `http://your.robot/KAREL/kunit?filenames=your_test` 29 | 30 | ## Output Formatting 31 | 32 | The output is unformatted by default. Set `output=html` to get HTML formatting. 33 | 34 | http://your.robot/KAREL/kunit?filenames=your_test&output=html 35 | 36 | ## Running Multiple Tests 37 | 38 | You can run multiple test files in parallel by a comma-separated list 39 | of filenames to the test runner. 40 | 41 | http://your.robot/KAREL/kunit?filenames=test_kunit,test_something_else 42 | 43 | ## Example 44 | 45 | Look at the `src/test_kunit.kl` file for a real-world example. (Note: the test_kunit.kl file is a bit hack-ish, but testing KUnit with KUnit? so meta...) 46 | 47 | Here's a simple example of testing a routine that adds two INTEGERs together: 48 | 49 | PROGRAM test_add_int 50 | -- %NOLOCKGROUP is required to run KAREL from browser 51 | %NOLOCKGROUP 52 | -- %INCLUDE the KUnit routines 53 | %INCLUDE kunit.h 54 | 55 | -- the ROUTINE under test 56 | ROUTINE add_int(l : INTEGER; r : INTEGER) : INTEGER 57 | BEGIN 58 | RETURN(l + r) 59 | END add_int 60 | 61 | -- one test 62 | ROUTINE test_11 : BOOLEAN 63 | BEGIN 64 | RETURN(kunit_eq_int(2, add_int(1,1))) 65 | END test_11 66 | 67 | -- second test 68 | ROUTINE test_22 : BOOLEAN 69 | BEGIN 70 | RETURN(kunit_eq_int(4, add_int(2,2))) 71 | END test_22 72 | 73 | -- one more test 74 | ROUTINE test_00 : BOOLEAN 75 | BEGIN 76 | RETURN(kunit_eq_int(0, add_int(0,0))) 77 | END test_00 78 | 79 | BEGIN 80 | -- do some tests 81 | kunit_test('1+1=2', test_11) 82 | kunit_test('2+2=4', test_22) 83 | kunit_test('0+0=0', test_00) 84 | 85 | -- tell the test runner we are done 86 | kunit_done 87 | END test_add_int 88 | 89 | Since KAREL doesn't support blocks as arguments to functions or 90 | routines, I've found that it's best to simply create a new routine 91 | that returns a BOOLEAN result for each test since there's typically 92 | some setup, teardown, etc. For these contrived examples you could have 93 | simply used the KUnit assertion as the second argument to `kunit_test`: 94 | 95 | kunit_test('1+1=2', kunit_eq_int(2, add_int(1,1))) 96 | 97 | ## Assertions 98 | 99 | `kunit_assert(b : BOOLEAN)` - Assert something is true 100 | 101 | `kunit_eq_int(expected : INTEGER; actual : INTEGER)` - Assert two 102 | INTEGERs are equal 103 | 104 | `kunit_eq_r(expected : REAL; actual : REAL)` - Assert two REALs are 105 | equal 106 | 107 | `kunit_eq_str(expected : STRING; actual : STRING)` - Assert two 108 | STRINGs are equal 109 | 110 | `kunit_eq_pos(expected : XYZWPR; actual : XYZWPR` - Assert two 111 | XYZWPR positions are equal. 112 | 113 | `kunit_eq_pip(fname : STRING)` - Assert that the KUnit pipe is equal 114 | to the file located at the provided path (generally for use comparing 115 | STRINGs longer than 254 characters) 116 | 117 | `kunit_un_int(actual : INTEGER)` - Assert an INTEGER is UNINIT. 118 | 119 | `kunit_un_str(actual : STRING)` - Assert a STRING is UNINIT. 120 | 121 | `kunit_un_r(actual : REAL)` - Assert a REAL is uninit. 122 | 123 | ## Under the Hood 124 | 125 | All KUnit really does is provide a simple environment that stores 126 | information about the tests you perform. Tests are just any routine that 127 | returns a `BOOLEAN` value. If `true`, the test passes, otherwise the 128 | test fails. KUnit then prints out some useful information about how many 129 | tests you ran, how many failed, how many assertions you made, etc. If 130 | you use the provided KUnit assertions, you'll also get some helpful 131 | debug information about the test. For example, if you use the 132 | `kunit_eq_str()` routine, you might see something like this if it fails: 133 | 134 | -- test routine: 135 | test('a eqls a', kunit_eq_str('a','b')) 136 | 137 | -- KUnit output: 138 | 1) Failure: 139 | a eqls a 140 | Expected "a" but got "b" 141 | 142 | ## Development 143 | 144 | You must have ROBOGUIDE installed and the the WinOLPC bin directory 145 | needs to be on your system $PATH. 146 | 147 | 1. Download [GnuWin](http://gnuwin32.sourceforge.net) if you don't 148 | already have it 149 | 2. Clone the repository 150 | 3. Run `make` to build the KAREL binary PC files 151 | 4. Copy the binaries to your ROBOGUIDE or real robot 152 | 5. Run the tests from `http://robot.ip/KAREL/test_kunit` 153 | 154 | ## Contributing 155 | 156 | 1. Fork it 157 | 2. Create your feature branch (`git checkout -b my-new-feature`) 158 | 3. Commit your changes (`git commit -am 'Add some feature'`) 159 | 4. Push to the branch (`git push origin my-new-feature`) 160 | 5. Create new Pull Request 161 | -------------------------------------------------------------------------------- /deploy.ftp: -------------------------------------------------------------------------------- 1 | anon 2 | 3 | bin 4 | prompt 5 | mput bin/*.pc 6 | put vendor/strings.pc 7 | quit 8 | -------------------------------------------------------------------------------- /src/includes/kunit.h.kl: -------------------------------------------------------------------------------- 1 | ROUTINE kunit_test(name : STRING; result : BOOLEAN) FROM kunit 2 | ROUTINE kunit_done FROM kunit 3 | 4 | ROUTINE kunit_assert(b : BOOLEAN) : BOOLEAN FROM kunit 5 | ROUTINE kunit_eq_int(expected : INTEGER; actual : INTEGER) : BOOLEAN FROM kunit 6 | ROUTINE kunit_eq_pip(fname : STRING) : BOOLEAN FROM kunit 7 | ROUTINE kunit_eq_pos(expected : XYZWPR; actual : XYZWPR) : BOOLEAN FROM kunit 8 | ROUTINE kunit_eq_r(expected : REAL; actual : REAL) : BOOLEAN FROM kunit 9 | ROUTINE kunit_eq_str(expected : STRING; actual : STRING) : BOOLEAN FROM kunit 10 | ROUTINE kunit_un_int(actual : INTEGER) : BOOLEAN FROM kunit 11 | ROUTINE kunit_un_str(actual : STRING) : BOOLEAN FROM kunit 12 | ROUTINE kunit_un_r(actual : REAL) : BOOLEAN FROM kunit 13 | 14 | ROUTINE k_init_pipe FROM kunit 15 | ROUTINE k_close_pipe FROM kunit 16 | ROUTINE kunit_pipe(s : STRING) FROM kunit 17 | -------------------------------------------------------------------------------- /src/includes/vendor/strings.h.kl: -------------------------------------------------------------------------------- 1 | ROUTINE split_str(str:STRING; sep:STRING; out:ARRAY[*] OF STRING) FROM strings 2 | ROUTINE lstrip(s : STRING) : STRING FROM strings 3 | ROUTINE rstrip(s : STRING) : STRING FROM strings 4 | ROUTINE b_to_s(b : BOOLEAN) : STRING FROM strings 5 | ROUTINE i_to_s(i : INTEGER) : STRING FROM strings 6 | ROUTINE r_to_s(r : REAL) : STRING FROM strings 7 | ROUTINE p_to_s(p : XYZWPR) : STRING FROM strings 8 | ROUTINE esc_quotes(s : STRING) : STRING FROM strings 9 | -------------------------------------------------------------------------------- /src/kunit.kl: -------------------------------------------------------------------------------- 1 | PROGRAM kunit 2 | %NOLOCKGROUP 3 | %UNINITVARS 4 | %NOBUSYLAMP 5 | CONST 6 | VERSION = '1.0.0' 7 | LINE_WRAP = 40 8 | PIPE_FILE = 'PIP:KUNIT.DAT' 9 | FAIL_FILE = 'PIP:KUNIT_FAIL.DAT' 10 | PROG_PIPE = 'PIP:KUNIT_PROGRESS.DAT' 11 | KUNIT_SEMA = 10 12 | MAX_WAIT = 10000 13 | SEV_ABORT = 2 14 | VAR 15 | response : FILE 16 | pipe_f : FILE 17 | other_f : FILE 18 | fail_f : FILE 19 | prog_f : FILE 20 | test_count : INTEGER 21 | fail_count : INTEGER 22 | pass_count : INTEGER 23 | assrtn_count : INTEGER 24 | error_msg : STRING[254] 25 | filenames : STRING[254] 26 | filenames_a : ARRAY[16] OF STRING[36] 27 | timeout : BOOLEAN 28 | task_count : INTEGER 29 | i : INTEGER 30 | output : STRING[12] 31 | url : STRING[254] 32 | 33 | start_time : INTEGER 34 | end_time : INTEGER 35 | total_time : REAL 36 | entry : INTEGER 37 | status : INTEGER 38 | 39 | %INCLUDE includes/vendor/strings.h 40 | 41 | -- Private: Initialize KUNIT 42 | ROUTINE kunit_init 43 | BEGIN 44 | GET_VAR(entry, '*system*', '$fast_clock', start_time, status) 45 | 46 | IF UNINIT(output) THEN output = ''; ENDIF 47 | IF UNINIT(filenames) THEN filenames = ''; ENDIF 48 | 49 | OPEN FILE response ('RW', 'RD:RESPONSE.HTM') 50 | -- empty fail_f 51 | OPEN FILE fail_f ('RW', FAIL_FILE) 52 | CLOSE FILE fail_f 53 | -- empty prog_pipe 54 | OPEN FILE prog_f ('RW', PROG_PIPE) 55 | CLOSE file prog_f 56 | 57 | IF output = 'html' THEN 58 | WRITE response ( 59 | '', 60 | '', 61 | 'KUnit v',VERSION,'', 62 | '', 63 | '', 64 | '', 65 | '
', CR)
 66 |     ENDIF
 67 | 
 68 |     WRITE response ('KUnit v', VERSION, CR, CR)
 69 | 
 70 |     test_count    = 0
 71 |     fail_count    = 0
 72 |     pass_count    = 0
 73 |     assrtn_count  = 0
 74 |     task_count    = 0
 75 | 
 76 |     CLEAR_SEMA(KUNIT_SEMA)
 77 |   END kunit_init
 78 | 
 79 | -- Private: Fail a test
 80 | ROUTINE kunit_fail(name : STRING)
 81 |   BEGIN
 82 |     fail_count = fail_count + 1
 83 | 
 84 |     OPEN FILE fail_f ('AP', FAIL_FILE)
 85 |     WRITE fail_f (fail_count, ') Failure:', CR)
 86 |     WRITE fail_f (name, CR)
 87 |     WRITE fail_f (error_msg,CR,CR)
 88 |     CLOSE FILE fail_f
 89 |   END kunit_fail
 90 | 
 91 | -- Private: pass a test
 92 | ROUTINE kunit_pass
 93 |   BEGIN
 94 |     pass_count = pass_count + 1
 95 |   END kunit_pass
 96 | 
 97 | -- Public: Perform a test
 98 | --
 99 | -- name   - A STRING describing what this test is testing
100 | -- result - A BOOLEAN that determines if the test passes or fails
101 | --
102 | -- Examples
103 | --
104 | --   kunit_test('true should pass', true)
105 | --   # => test passes
106 | --
107 | --   kunit_test('this test should fail', false)
108 | --   # => test fails
109 | ROUTINE kunit_test(name : STRING; result : BOOLEAN)
110 |   BEGIN
111 |     test_count = test_count + 1
112 | 
113 |     OPEN FILE prog_f ('AP', PROG_PIPE)
114 |     IF result THEN
115 |       WRITE prog_f ('.')
116 |       kunit_pass
117 |     ELSE
118 |       WRITE prog_f ('F')
119 |       kunit_fail(name)
120 |     ENDIF
121 | 
122 |     IF ((pass_count+fail_count) MOD LINE_WRAP = 0) THEN
123 |       WRITE prog_f (CR)
124 |     ENDIF
125 |     CLOSE FILE prog_f
126 |   END kunit_test
127 | 
128 | -- Public: tell the test runner that this file is finished
129 | ROUTINE kunit_done
130 |   BEGIN
131 |     POST_SEMA(KUNIT_SEMA)
132 |   END kunit_done
133 | 
134 | -- Public: Assert that something is true
135 | --
136 | -- actual - A BOOLEAN value
137 | --
138 | -- Examples
139 | --
140 | --   kunit_assert(true)
141 | --   # => true
142 | --
143 | --   kunit_assert(false)
144 | --   # => false
145 | ROUTINE kunit_assert(actual : BOOLEAN) : BOOLEAN
146 |   BEGIN
147 |     assrtn_count = assrtn_count + 1
148 | 
149 |     IF actual THEN
150 |       RETURN(true)
151 |     ELSE
152 |       error_msg = 'Expected true but got false'
153 |       RETURN(false)
154 |     ENDIF
155 |   END kunit_assert
156 | 
157 | -- Public: Assert that two INTEGERs are equal
158 | --
159 | -- expected - The expected INTEGER value
160 | -- actual   - The actual INTEGER value
161 | --
162 | -- Examples
163 | --
164 | --   kunit_eq_int(1,1)
165 | --   # => true
166 | --
167 | --   kunit_eq_int(1,2)
168 | --   # => False
169 | ROUTINE kunit_eq_int(expected : INTEGER; actual : INTEGER) : BOOLEAN
170 |   BEGIN
171 |     assrtn_count = assrtn_count + 1
172 | 
173 | 	IF UNINIT(actual) THEN
174 | 	  error_msg = 'Expected ' + i_to_s(expected) + ' but got UNINIT'
175 | 	  RETURN(false)
176 | 	ENDIF
177 |     IF expected=actual THEN
178 |       RETURN(true)
179 |     ELSE
180 |       error_msg = 'Expected ' + i_to_s(expected) + ' but got ' + i_to_s(actual)
181 |       RETURN(false)
182 |     ENDIF
183 |   END kunit_eq_int
184 | 
185 | ROUTINE kunit_un_int(actual : INTEGER) : BOOLEAN
186 | BEGIN
187 | 	assrtn_count = assrtn_count + 1
188 | 	IF UNINIT(actual) THEN
189 | 		RETURN(true)
190 | 	ELSE
191 | 		error_msg = 'Expected UNINIT but got ' + i_to_s(actual)
192 | 		RETURN(false)
193 | 	ENDIF
194 | END kunit_un_int
195 | 
196 | ROUTINE kunit_un_str(actual : STRING) : BOOLEAN
197 | BEGIN
198 | 	assrtn_count = assrtn_count + 1
199 | 	IF UNINIT(actual) THEN
200 | 		RETURN(true)
201 | 	ELSE
202 | 		error_msg = 'Expected UNINIT but got "' + actual + '"'
203 | 		RETURN(false)
204 | 	ENDIF
205 | END kunit_un_str
206 | 
207 | ROUTINE kunit_un_r(actual : REAL) : BOOLEAN
208 | BEGIN
209 | 	assrtn_count = assrtn_count + 1
210 | 	IF UNINIT(actual) THEN
211 | 		RETURN(true)
212 | 	ELSE
213 | 		error_msg = 'Expected UNINIT but got ' + r_to_s(actual)
214 | 		RETURN(false)
215 | 	ENDIF
216 | END kunit_un_r
217 | 
218 | 
219 | ROUTINE kunit_eq_r(expected : REAL; actual : REAL) : BOOLEAN
220 |   BEGIN
221 |     assrtn_count = assrtn_count + 1
222 | 
223 |     IF expected=actual THEN
224 |       RETURN(true)
225 |     ELSE
226 |       error_msg = 'Expected ' + r_to_s(expected) + ' but got ' + r_to_s(actual) 
227 |       RETURN(false)
228 |     ENDIF
229 |   END kunit_eq_r
230 | 
231 | -- Public: Assert that two STRINGs are equal
232 | --
233 | -- expected - The expected STRING value
234 | -- actual   - The actual STRING value
235 | --
236 | -- Examples
237 | --
238 | --   kunit_eq_str('foo','foo')
239 | --   # => true
240 | --
241 | --   kunit_eq_str('foo','bar')
242 | --   # => false
243 | ROUTINE kunit_eq_str(expected : STRING; actual : STRING) : BOOLEAN
244 |   BEGIN
245 |     assrtn_count = assrtn_count + 1
246 | 
247 |     IF UNINIT(expected) THEN
248 |       error_msg = 'Expected was UNINIT'
249 |       RETURN(false)
250 |     ENDIF
251 | 
252 |     IF UNINIT(actual) THEN
253 |       error_msg = 'Actual was UNINIT'
254 |       RETURN(false)
255 |     ENDIF
256 | 
257 |     IF expected=actual THEN
258 |       RETURN(true)
259 |     ELSE
260 |       error_msg = 'Expected "' + expected + '" but got "' + actual + '"' 
261 |       RETURN(false)
262 |     ENDIF
263 |   END kunit_eq_str
264 | 
265 | -- Public: Assert that two XYZWPR positions are equal
266 | --
267 | -- expected - The expected XYZWPR value
268 | -- actual   - The actual XYZWPR value
269 | --
270 | -- Returns true if the X, Y, Z, W, P and R compontents are equal,
271 | -- false otherwise
272 | ROUTINE kunit_eq_pos(expected : XYZWPR; actual : XYZWPR) : BOOLEAN
273 |   BEGIN
274 |     assrtn_count = assrtn_count + 1
275 | 
276 |     IF UNINIT(expected) OR UNINIT(actual) THEN
277 |       error_msg = 'Expected and/or actual are UNINIT'
278 |       RETURN(false)
279 |     ENDIF
280 | 
281 |     IF (expected.x=actual.x) AND (expected.y=actual.y) AND (expected.z=actual.z) AND &
282 |        (expected.w=actual.w) AND (expected.p=actual.p) AND (expected.r=actual.r) THEN
283 |       RETURN(true)
284 |     ELSE
285 |       error_msg = 'Expected: ' + chr(13) &
286 |         + p_to_s(expected) + chr(13) &
287 |         + 'Actual: ' + chr(13) &
288 |         + p_to_s(actual)
289 |       RETURN(false)
290 |     ENDIF
291 |   END kunit_eq_pos
292 | 
293 | -- Public: Opens the KUNIT pipe for reading and writing
294 | --
295 | -- This is used in conjunction with `k_close_pipe` and `kunit_pipe` to
296 | -- test long strings are equal.
297 | ROUTINE k_init_pipe
298 |   BEGIN
299 |     OPEN FILE pipe_f ('RW', PIPE_FILE)
300 |   END k_init_pipe
301 | 
302 | -- Public: Close the KUNIT pipe file
303 | ROUTINE k_close_pipe
304 |   BEGIN
305 |     CLOSE FILE pipe_f
306 |   END k_close_pipe
307 | 
308 | -- Public: Write to the KUNIT pipe file
309 | --
310 | -- This is used in conjunction with `kunit_eq_pip` to test that the
311 | -- value of the KUNIT pipe file is equal to the provided file
312 | ROUTINE kunit_pipe(s : STRING)
313 |   BEGIN
314 |     WRITE pipe_f (s)
315 |   END kunit_pipe
316 | 
317 | -- Public: Assert that the KUNIT pipe is equal to the provided FILE
318 | --
319 | -- fname - The STRING filename of the FILE to test
320 | --
321 | -- Examples
322 | --
323 | --   kunit_eq_pipe('MD:somefile.dat')
324 | --   # => true if the KUNIT file has the same contents of
325 | --        'MD:somefile.dat', false otherwise
326 | ROUTINE kunit_eq_pip(fname : STRING) : BOOLEAN
327 |   VAR
328 |     e      : STRING[64]
329 |     a      : STRING[64]
330 |     i      : INTEGER
331 |     k      : INTEGER
332 |     j      : INTEGER
333 |     status : INTEGER
334 |     b      : BOOLEAN
335 |     r      : BOOLEAN
336 |   BEGIN
337 |     r = true
338 |     OPEN FILE pipe_f ('RO', PIPE_FILE)
339 |     OPEN FILE other_f ('RO', fname)
340 |     BYTES_AHEAD(pipe_f, i, status)
341 |     BYTES_AHEAD(other_f, k, status)
342 | 
343 |     WHILE (i > 0) AND (k > 0) DO
344 |       IF i > k THEN
345 |         j = k
346 |       ELSE
347 |         j = i
348 |       ENDIF
349 | 
350 |       READ pipe_f  (e::j::0)
351 |       READ other_f (a::j::0)
352 |       b = kunit_eq_str(e, a)
353 |       IF NOT(b) THEN
354 |         r = false
355 |       ENDIF
356 | 
357 |       BYTES_AHEAD(pipe_f, i, status)
358 |       BYTES_AHEAD(other_f, k, status)
359 |     ENDWHILE
360 | 
361 |     CLOSE FILE pipe_f
362 |     CLOSE FILE other_f
363 | 
364 |     RETURN(r)
365 |   END kunit_eq_pip
366 | 
367 | -- Private: Writes the results of the KUNIT test suite
368 | ROUTINE kunit_output
369 |   VAR
370 |     i      : INTEGER
371 |     b      : INTEGER
372 |     status : INTEGER
373 |     s      : STRING[254]
374 |   BEGIN
375 |     GET_VAR(entry, '*system*', '$fast_clock', end_time, status)
376 |     total_time = end_time - start_time
377 | 
378 |     -- make sure we do at least 1 ITP
379 |     IF total_time = 0 THEN
380 |       total_time = 2
381 |     ENDIF
382 | 
383 |     OPEN FILE prog_f ('RO', PROG_PIPE)
384 |     status = IO_STATUS(prog_f)
385 |     IF status<>0 THEN
386 |     	POST_ERR(status, '', 0, SEV_ABORT)
387 |     ENDIF
388 | 
389 |     BYTES_AHEAD(prog_f, b, status)
390 |     WHILE b > 0 DO
391 |       READ prog_f (s::b::0)
392 |       WRITE response (s)
393 |       BYTES_AHEAD(prog_f, b, status)
394 |     ENDWHILE
395 |     CLOSE FILE prog_f
396 | 
397 |     WRITE response (CR,CR)
398 | 
399 |     WRITE response ('Finished in ', r_to_s((total_time / 1000.0)), ' seconds', CR)
400 |     WRITE response (r_to_s(test_count/(total_time/1000.0)), ' tests/sec, ')
401 |     WRITE response (r_to_s(assrtn_count/(total_time/1000.0)), ' assertions/sec', CR, CR)
402 | 
403 |     OPEN FILE fail_f ('RO', FAIL_FILE)
404 |     BYTES_AHEAD(fail_f, b, status)
405 |     WHILE b > 0 DO
406 |       READ fail_f (s::b::0)
407 |       WRITE response (s)
408 |       BYTES_AHEAD(fail_f, b, status)
409 |     ENDWHILE
410 |     CLOSE FILE fail_f
411 | 
412 |     WRITE response (i_to_s(test_count), ' tests, ')
413 |     WRITE response (i_to_s(assrtn_count), ' assertions, ')
414 |     WRITE response (i_to_s(fail_count), ' failures', CR)
415 | 
416 |     IF output = 'html' THEN
417 |       WRITE response ('
', CR) 418 | ENDIF 419 | END kunit_output 420 | 421 | ROUTINE kunit_spawn(filename : STRING) 422 | BEGIN 423 | RUN_TASK(filename, 0, false, false, 0, status) 424 | IF status<>0 THEN 425 | WRITE response ('could not spawn task ', filename, CR, 'status', status) 426 | ELSE 427 | task_count = task_count + 1 428 | ENDIF 429 | END kunit_spawn 430 | 431 | ROUTINE kunit_clean 432 | BEGIN 433 | CLOSE FILE response 434 | CLOSE FILE fail_f 435 | CLOSE FILE prog_f 436 | 437 | filenames = '' 438 | output = '' 439 | END kunit_clean 440 | 441 | 442 | BEGIN 443 | kunit_init 444 | 445 | IF filenames='' THEN 446 | WRITE response ('Please provide a list of test filenames via the GET parameter.', CR) 447 | kunit_clean 448 | ABORT 449 | ENDIF 450 | 451 | split_str(filenames, ',', filenames_a) 452 | FOR i=1 TO 16 DO 453 | IF NOT(UNINIT(filenames_a[i])) THEN 454 | IF filenames_a[i]<>'' THEN 455 | -- todo: validate filename 456 | kunit_spawn(filenames_a[i]) 457 | ENDIF 458 | ENDIF 459 | ENDFOR 460 | 461 | WHILE task_count>0 DO 462 | PEND_SEMA(KUNIT_SEMA, MAX_WAIT, timeout) 463 | task_count = task_count - 1 464 | IF timeout THEN 465 | WRITE response ('Timed out waiting for a task', CR) 466 | ENDIF 467 | ENDWHILE 468 | 469 | kunit_output 470 | kunit_clean 471 | END kunit 472 | -------------------------------------------------------------------------------- /src/test_kunit.kl: -------------------------------------------------------------------------------- 1 | PROGRAM test_kunit 2 | %NOLOCKGROUP 3 | %UNINITVARS 4 | VAR 5 | test_count FROM kunit : INTEGER 6 | fail_count FROM kunit : INTEGER 7 | pass_count FROM kunit : INTEGER 8 | assrtn_count FROM kunit : INTEGER 9 | error_msg FROM kunit : STRING[254] 10 | uninitInt : INTEGER 11 | 12 | %INCLUDE includes/kunit.h 13 | -- test private routines 14 | ROUTINE kunit_fail(name : STRING) FROM kunit 15 | ROUTINE kunit_pass FROM kunit 16 | 17 | ROUTINE t_fail : BOOLEAN 18 | VAR 19 | i : INTEGER 20 | b : BOOLEAN 21 | BEGIN 22 | i = fail_count 23 | -- fail a test 24 | error_msg = 'this is not a real failure... ignore me' 25 | kunit_fail('kunit_fail()') 26 | 27 | b = kunit_eq_int(i+1, fail_count) 28 | -- reset fail_count 29 | fail_count = i 30 | -- use i 31 | i = i + 1 32 | 33 | RETURN(b) 34 | END t_fail 35 | 36 | ROUTINE t_pass : BOOLEAN 37 | VAR 38 | i : INTEGER 39 | b : BOOLEAN 40 | BEGIN 41 | i = pass_count 42 | kunit_pass 43 | b = kunit_eq_int(i+1,pass_count) 44 | -- reset pass_count 45 | pass_count = i 46 | RETURN(b) 47 | END t_pass 48 | 49 | ROUTINE t_test_p : BOOLEAN 50 | VAR 51 | i : INTEGER 52 | b : BOOLEAN 53 | BEGIN 54 | i = pass_count 55 | kunit_test('kunit_test() on pass', true) 56 | b = kunit_eq_int(i+1, pass_count) 57 | -- reset pass_count 58 | pass_count = i 59 | RETURN(b) 60 | END t_test_p 61 | 62 | ROUTINE t_test_f : BOOLEAN 63 | VAR 64 | i : INTEGER 65 | b : BOOLEAN 66 | BEGIN 67 | i = fail_count 68 | error_msg = 'this one is responsible for the F' 69 | kunit_test('kunit_test() on fail', false) 70 | b = kunit_eq_int(i+1, fail_count) 71 | -- reset fail_count 72 | fail_count = i 73 | RETURN(b) 74 | END t_test_f 75 | 76 | ROUTINE t_assert_t : BOOLEAN 77 | VAR 78 | i : INTEGER 79 | k : INTEGER 80 | b : BOOLEAN 81 | c : BOOLEAN 82 | BEGIN 83 | i = assrtn_count 84 | b = kunit_assert(true) 85 | k = assrtn_count 86 | c = kunit_eq_int(i+1, k) 87 | RETURN(b AND c) 88 | END t_assert_t 89 | 90 | ROUTINE t_assert_f : BOOLEAN 91 | VAR 92 | i : INTEGER 93 | k : INTEGER 94 | b : BOOLEAN 95 | c : BOOLEAN 96 | BEGIN 97 | i = assrtn_count 98 | b = kunit_assert(false) 99 | k = assrtn_count 100 | c = kunit_eq_int(i+1, k) 101 | RETURN(NOT(b) AND c) 102 | END t_assert_f 103 | 104 | ROUTINE t_eq_intp : BOOLEAN 105 | VAR 106 | i : INTEGER 107 | k : INTEGER 108 | b : BOOLEAN 109 | c : BOOLEAN 110 | BEGIN 111 | i = assrtn_count 112 | b = kunit_eq_int(1,1) 113 | k = assrtn_count 114 | c = kunit_eq_int(i+1, k) 115 | RETURN(b AND c) 116 | END t_eq_intp 117 | 118 | ROUTINE t_eq_intf : BOOLEAN 119 | VAR 120 | i : INTEGER 121 | k : INTEGER 122 | b : BOOLEAN 123 | c : BOOLEAN 124 | BEGIN 125 | i = assrtn_count 126 | b = kunit_eq_int(1,2) 127 | k = assrtn_count 128 | c = kunit_eq_str('Expected 1 but got 2', error_msg) 129 | c = kunit_eq_int(i+1, k) 130 | RETURN(NOT(b) AND c) 131 | END t_eq_intf 132 | 133 | ROUTINE t_un_intp : BOOLEAN 134 | VAR 135 | i : INTEGER 136 | k : INTEGER 137 | b : BOOLEAN 138 | c : BOOLEAN 139 | BEGIN 140 | i = assrtn_count 141 | b = kunit_un_int(uninitInt) 142 | k = assrtn_count 143 | c = kunit_eq_int(i+1, k) 144 | RETURN(b AND c) 145 | END t_un_intp 146 | 147 | ROUTINE t_un_intf : BOOLEAN 148 | VAR 149 | i : INTEGER 150 | k : INTEGER 151 | b : BOOLEAN 152 | c : BOOLEAN 153 | BEGIN 154 | i = assrtn_count 155 | b = kunit_un_int(1) 156 | k = assrtn_count 157 | c = kunit_eq_str('Expected UNINIT but got 1', error_msg) 158 | c = kunit_eq_int(i+1, k) 159 | RETURN(NOT(b) AND c) 160 | END t_un_intf 161 | 162 | 163 | ROUTINE t_eq_rp : BOOLEAN 164 | VAR 165 | i : INTEGER 166 | k : INTEGER 167 | b : BOOLEAN 168 | c : BOOLEAN 169 | BEGIN 170 | i = assrtn_count 171 | b = kunit_eq_r(3.14,3.14) 172 | k = assrtn_count 173 | c = kunit_eq_int(i+1, k) 174 | RETURN(b AND c) 175 | END t_eq_rp 176 | 177 | ROUTINE t_eq_rf : BOOLEAN 178 | VAR 179 | i : INTEGER 180 | k : INTEGER 181 | b : BOOLEAN 182 | c : BOOLEAN 183 | BEGIN 184 | i = assrtn_count 185 | b = kunit_eq_r(3.14,3.15) 186 | k = assrtn_count 187 | c = kunit_eq_str('Expected 3.14 but got 3.15', error_msg) 188 | c = kunit_eq_int(i+1, k) 189 | RETURN(NOT(b) AND c) 190 | END t_eq_rf 191 | 192 | ROUTINE t_eq_strp : BOOLEAN 193 | VAR 194 | i : INTEGER 195 | k : INTEGER 196 | b : BOOLEAN 197 | c : BOOLEAN 198 | BEGIN 199 | i = assrtn_count 200 | b = kunit_eq_str('a','a') 201 | k = assrtn_count 202 | c = kunit_eq_int(i+1, k) 203 | RETURN(b AND c) 204 | END t_eq_strp 205 | 206 | ROUTINE t_eq_strf : BOOLEAN 207 | VAR 208 | i : INTEGER 209 | k : INTEGER 210 | b : BOOLEAN 211 | c : BOOLEAN 212 | BEGIN 213 | i = assrtn_count 214 | b = kunit_eq_str('a','b') 215 | k = assrtn_count 216 | c = kunit_eq_str('Expected "a" but got "b"', error_msg) 217 | c = kunit_eq_int(i+1, k) 218 | RETURN(NOT(b) AND c) 219 | END t_eq_strf 220 | 221 | ROUTINE t_eq_posp : BOOLEAN 222 | VAR 223 | i : INTEGER 224 | k : INTEGER 225 | b : BOOLEAN 226 | c : BOOLEAN 227 | p : XYZWPR 228 | BEGIN 229 | p.x = 0.0 230 | p.y = 0.0 231 | p.z = 0.0 232 | p.w = 0.0 233 | p.p = 0.0 234 | p.r = 0.0 235 | 236 | i = assrtn_count 237 | b = kunit_eq_pos(p,p) 238 | k = assrtn_count 239 | c = kunit_eq_int(i+1, k) 240 | RETURN(b AND c) 241 | END t_eq_posp 242 | 243 | ROUTINE t_eq_posf : BOOLEAN 244 | VAR 245 | i : INTEGER 246 | k : INTEGER 247 | b : BOOLEAN 248 | c : BOOLEAN 249 | p : XYZWPR 250 | p2 : XYZWPR 251 | BEGIN 252 | p.x = 0.0 253 | p.y = 0.0 254 | p.z = 0.0 255 | p.w = 0.0 256 | p.p = 0.0 257 | p.r = 0.0 258 | p2.x = 1.0 259 | p2.y = 0.0 260 | p2.z = 0.0 261 | p2.w = 0.0 262 | p2.p = 0.0 263 | p2.r = 0.0 264 | 265 | i = assrtn_count 266 | b = kunit_eq_pos(p,p2) 267 | k = assrtn_count 268 | c = kunit_eq_str('Expected: ' + chr(13) & 269 | + 'X: 0.0 Y: 0.0 Z: 0.0' + chr(13) & 270 | + 'W: 0.0 P: 0.0 R: 0.0' + chr(13) & 271 | + 'Actual: ' + chr(13) & 272 | + 'X: 1.0 Y: 0.0 Z: 0.0' + chr(13) & 273 | + 'W: 0.0 P: 0.0 R: 0.0', error_msg) 274 | c = kunit_eq_int(i+1, k) 275 | RETURN(NOT(b) AND c) 276 | END t_eq_posf 277 | 278 | ROUTINE t_eq_posu : BOOLEAN 279 | VAR 280 | i : INTEGER 281 | k : INTEGER 282 | b : BOOLEAN 283 | c : BOOLEAN 284 | p : XYZWPR 285 | BEGIN 286 | i = assrtn_count 287 | b = kunit_eq_pos(p,p) 288 | k = assrtn_count 289 | c = kunit_eq_str('Expected and/or actual are UNINIT', error_msg) 290 | c = kunit_eq_int(i+1, k) 291 | RETURN(NOT(b) AND c) 292 | END t_eq_posu 293 | 294 | BEGIN 295 | kunit_test('kunit_fail()', t_fail) 296 | kunit_test('kunit_pass()', t_pass) 297 | kunit_test('kunit_test() on success', t_test_p) 298 | kunit_test('kunit_test() on fail', t_test_f) 299 | kunit_test('kunit_assert(true)', t_assert_t) 300 | kunit_test('kunit_assert(false)', t_assert_f) 301 | kunit_test('kunit_eq_int pass', t_eq_intp) 302 | kunit_test('kunit_eq_int fail', t_eq_intf) 303 | kunit_test('kunit_un_int pass', t_un_intp) 304 | kunit_test('kunit_un_int fail', t_un_intf) 305 | kunit_test('kunit_eq_r pass', t_eq_rp) 306 | kunit_test('kunit_eq_r fail', t_eq_rf) 307 | kunit_test('kunit_eq_str pass', t_eq_strp) 308 | kunit_test('kunit_eq_str fail', t_eq_strf) 309 | kunit_test('kunit_eq_pos pass', t_eq_posp) 310 | kunit_test('kunit_eq_pos fail', t_eq_posf) 311 | kunit_test('kunit_eq_pos with uninit', t_eq_posu) 312 | kunit_done 313 | END test_kunit 314 | -------------------------------------------------------------------------------- /vendor/strings.pc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/onerobotics/KUnit/76a6a92cc6869084a59eb16e343584ae18a340e6/vendor/strings.pc --------------------------------------------------------------------------------