├── .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 | '', 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 --------------------------------------------------------------------------------