| 1 | @[has_globals] |
| 2 | module main |
| 3 | |
| 4 | __global test_runner TestRunner |
| 5 | |
| 6 | /////////////////////////////////////////////////////////////////////////////// |
| 7 | // This file will be compiled as part of the main program, for a _test.v file. |
| 8 | // The methods defined here are called back by the test program's assert |
| 9 | // statements, on each success/fail. The goal is to make customizing the look & |
| 10 | // feel of the assertions results easier, since it is done in normal V code. |
| 11 | /////////////////////////////////////////////////////////////////////////////// |
| 12 | |
| 13 | interface TestRunner { |
| 14 | mut: |
| 15 | file_test_info VTestFileMetaInfo // filled in by generated code, before .start() is called. |
| 16 | fn_test_info VTestFnMetaInfo // filled in by generated code, before .fn_start() is called. |
| 17 | fn_assert_passes u64 // reset this to 0 in .fn_start(), increase it in .assert_pass() |
| 18 | fn_passes u64 // increase this in .fn_pass() |
| 19 | fn_fails u64 // increase this in .fn_fails() |
| 20 | total_assert_passes u64 // increase this in .assert_pass() |
| 21 | total_assert_fails u64 // increase this in .assert_fail() |
| 22 | |
| 23 | start(ntests int) // called before all tests, you can initialise private data here. ntests is the number of test functions in the _test.v file. |
| 24 | finish() // called after all tests are finished, you can print some stats if you want here. |
| 25 | exit_code() int // called right after finish(), it should return the exit code, that the test program will exit with. |
| 26 | |
| 27 | fn_start() bool // called before the start of each test_ function. Return false, if the function should be skipped. |
| 28 | fn_pass() // called after the end of each test_ function, with NO failed assertion. |
| 29 | fn_fail() // called after the end of each test_ function, with a failed assertion, *or* returning an error. |
| 30 | fn_error(line_nr int, file string, mod string, fn_name string, errmsg string) // called only for `fn test_xyz() ? { return error('message') }`, before .fn_fail() is called. |
| 31 | |
| 32 | assert_pass(i &VAssertMetaInfo) // called after each `assert true`. |
| 33 | assert_fail(i &VAssertMetaInfo) // called after each `assert false`. |
| 34 | |
| 35 | free() // you should free all the private data of your runner here. |
| 36 | } |
| 37 | |
| 38 | struct VTestFileMetaInfo { |
| 39 | file string |
| 40 | tests int |
| 41 | } |
| 42 | |
| 43 | // vtest_new_filemetainfo will be called right before .start(ntests), |
| 44 | // to fill in the .file_test_info field of the runner interface. |
| 45 | fn vtest_new_filemetainfo(file string, tests int) VTestFileMetaInfo { |
| 46 | return VTestFileMetaInfo{ |
| 47 | file: file |
| 48 | tests: tests |
| 49 | } |
| 50 | } |
| 51 | |
| 52 | @[unsafe] |
| 53 | fn (i &VTestFileMetaInfo) free() { |
| 54 | unsafe { |
| 55 | i.file.free() |
| 56 | } |
| 57 | } |
| 58 | |
| 59 | struct VTestFnMetaInfo { |
| 60 | name string |
| 61 | mod string |
| 62 | file string |
| 63 | line_nr int |
| 64 | } |
| 65 | |
| 66 | // vtest_new_metainfo will be called once per each test function. |
| 67 | fn vtest_new_metainfo(name string, mod string, file string, line_nr int) VTestFnMetaInfo { |
| 68 | return VTestFnMetaInfo{ |
| 69 | name: name |
| 70 | mod: mod |
| 71 | file: file |
| 72 | line_nr: line_nr |
| 73 | } |
| 74 | } |
| 75 | |
| 76 | @[unsafe] |
| 77 | fn (i &VTestFnMetaInfo) free() { |
| 78 | unsafe { |
| 79 | i.name.free() |
| 80 | i.mod.free() |
| 81 | i.file.free() |
| 82 | } |
| 83 | } |
| 84 | |
| 85 | @[typedef] |
| 86 | pub struct C.main__TestRunner { |
| 87 | mut: |
| 88 | _object voidptr |
| 89 | } |
| 90 | |
| 91 | // change_test_runner should be called by preludes that implement the |
| 92 | // the TestRunner interface, in their vtest_init fn (see below), to |
| 93 | // customize the way that V shows test results |
| 94 | @[manualfree] |
| 95 | pub fn change_test_runner(x &TestRunner) { |
| 96 | pobj := unsafe { &C.main__TestRunner(&test_runner)._object } |
| 97 | if pobj != 0 { |
| 98 | unsafe { |
| 99 | test_runner.free() |
| 100 | (&C.main__TestRunner(&test_runner))._object = nil |
| 101 | } |
| 102 | } |
| 103 | test_runner = *x |
| 104 | } |
| 105 | |
| 106 | // vtest_init will be called *before* the normal _vinit() function, |
| 107 | // to give a chance to the test runner implementation to change the |
| 108 | // test_runner global variable. The reason vtest_init is called before |
| 109 | // _vinit, is because a _test.v file can define consts, and they in turn |
| 110 | // may use function calls in their declaration, which may do assertions. |
| 111 | // fn vtest_init() { |
| 112 | // change_test_runner(&TestRunner(AnotherTestRunner{})) |
| 113 | // } |
| 114 | |
| 115 | // TODO: remove vtest_option_cludge, it is only here so that |
| 116 | // `vlib/sync/channel_close_test.v` compiles with simpler runners, |
| 117 | // that do not `import os` (which has other `fn()?`). Without it, |
| 118 | // the C `Option_void` type is undefined -> C compilation error. |
| 119 | fn vtest_option_cludge() ! { |
| 120 | } |
| 121 | |