| 1 | module main |
| 2 | |
| 3 | // This program verifies that `v test` propagates errors |
| 4 | // and that it exits with code 1, when at least 1 FAIL happen. |
| 5 | import os |
| 6 | import rand |
| 7 | import time |
| 8 | |
| 9 | const vexe = os.quoted_path(get_vexe_path()) |
| 10 | const vroot = os.dir(vexe) |
| 11 | const tdir = new_tdir() |
| 12 | |
| 13 | fn get_vexe_path() string { |
| 14 | env_vexe := os.getenv('VEXE') |
| 15 | if env_vexe != '' { |
| 16 | return env_vexe |
| 17 | } |
| 18 | me := os.executable() |
| 19 | eprintln('me: ${me}') |
| 20 | mut vexe_ := os.join_path(os.dir(os.dir(os.dir(me))), 'v') |
| 21 | if os.user_os() == 'windows' { |
| 22 | vexe_ += '.exe' |
| 23 | } |
| 24 | return vexe_ |
| 25 | } |
| 26 | |
| 27 | fn new_tdir() string { |
| 28 | dir := os.join_path(os.vtmp_dir(), rand.ulid()) |
| 29 | os.rmdir_all(dir) or {} |
| 30 | os.mkdir_all(dir) or { panic(err) } |
| 31 | at_exit(cleanup_tdir) or {} |
| 32 | return dir |
| 33 | } |
| 34 | |
| 35 | fn cleanup_tdir() { |
| 36 | println('... removing tdir: ${tdir}') |
| 37 | os.rmdir_all(tdir) or { eprintln(err) } |
| 38 | } |
| 39 | |
| 40 | type MyResult = string |
| 41 | |
| 42 | @[noreturn] |
| 43 | fn (result MyResult) fail(reason string) { |
| 44 | eprintln('> ${reason}, but it does not. Result:\n${result}') |
| 45 | exit(1) |
| 46 | } |
| 47 | |
| 48 | fn (result MyResult) has(sub string) MyResult { |
| 49 | if !result.contains(sub) { |
| 50 | result.fail(' result should have the substring `${sub}`') |
| 51 | } |
| 52 | return result |
| 53 | } |
| 54 | |
| 55 | fn (result MyResult) matches(gpattern string) MyResult { |
| 56 | if !result.match_glob(gpattern) { |
| 57 | result.fail('result should match the glob pattern `${gpattern}`') |
| 58 | } |
| 59 | return result |
| 60 | } |
| 61 | |
| 62 | fn create_test(tname string, tcontent string) !string { |
| 63 | tpath := os.join_path(tdir, tname) |
| 64 | os.write_file(tpath, tcontent)! |
| 65 | eprintln('>>>>>>>> tpath: ${tpath} | tcontent: ${tcontent}') |
| 66 | return os.quoted_path(tpath) |
| 67 | } |
| 68 | |
| 69 | fn check_assert_continues_works() ! { |
| 70 | os.chdir(tdir)! |
| 71 | create_test('assert_continues_option_works_test.v', |
| 72 | 'fn test_fail1() { assert 2==4\nassert 2==1\nassert 2==0 }\nfn test_ok(){ assert true }\nfn test_fail2() { assert false }')! |
| 73 | result := check_fail('${vexe} -assert continues assert_continues_option_works_test.v') |
| 74 | result.has('assert_continues_option_works_test.v:1: fn test_fail1') |
| 75 | result.has('assert_continues_option_works_test.v:2: fn test_fail1') |
| 76 | result.has('assert_continues_option_works_test.v:3: fn test_fail1') |
| 77 | result.has('assert_continues_option_works_test.v:5: fn test_fail2') |
| 78 | result.has('> assert 2 == 4').has('> assert 2 == 1').has('> assert 2 == 0') |
| 79 | // Check if a test function, tagged with [assert_continues], has the same behaviour, without needing additional options |
| 80 | create_test('assert_continues_tag_works_test.v', |
| 81 | '@[assert_continues]fn test_fail1() { assert 2==4\nassert 2==1\nassert 2==0 }\nfn test_ok(){ assert true }\nfn test_fail2() { assert false\n assert false }')! |
| 82 | tag_res := check_fail('${vexe} assert_continues_tag_works_test.v') |
| 83 | tag_res.has('assert_continues_tag_works_test.v:1: fn test_fail1') |
| 84 | tag_res.has('assert_continues_tag_works_test.v:2: fn test_fail1') |
| 85 | tag_res.has('assert_continues_tag_works_test.v:3: fn test_fail1') |
| 86 | tag_res.has('assert_continues_tag_works_test.v:5: fn test_fail2') |
| 87 | if tag_res.contains('assert_continues_tag_works_test.v:6: fn test_fail2') { |
| 88 | exit(1) |
| 89 | } |
| 90 | } |
| 91 | |
| 92 | fn check_ok(cmd string) MyResult { |
| 93 | println('> check_ok cmd: ${cmd}') |
| 94 | res := os.execute(cmd) |
| 95 | if res.exit_code != 0 { |
| 96 | eprintln('> check_ok failed.\n${res.output}') |
| 97 | exit(1) |
| 98 | } |
| 99 | return res.output |
| 100 | } |
| 101 | |
| 102 | fn check_fail(cmd string) MyResult { |
| 103 | println('> check_fail cmd: ${cmd}') |
| 104 | res := os.execute(cmd) |
| 105 | if res.exit_code == 0 { |
| 106 | eprintln('> check_fail succeeded, but it should have failed.\n${res.output}') |
| 107 | exit(1) |
| 108 | } |
| 109 | return res.output |
| 110 | } |
| 111 | |
| 112 | fn main() { |
| 113 | defer { |
| 114 | os.chdir(os.wd_at_startup) or {} |
| 115 | } |
| 116 | unbuffer_stdout() |
| 117 | spawn fn () { |
| 118 | time.sleep(120 * time.second) |
| 119 | eprintln('>>> exiting due to an expired watchdog timer <<<') |
| 120 | exit(1) |
| 121 | }() |
| 122 | println('> vroot: ${vroot} | vexe: ${vexe} | tdir: ${tdir}') |
| 123 | os.setenv('VTEST_HIDE_OK', '0', true) |
| 124 | ok_fpath := create_test('a_single_ok_test.v', 'fn test_ok(){ assert true }')! |
| 125 | if check_ok('${vexe} ${ok_fpath}') != '' { |
| 126 | exit(1) |
| 127 | } |
| 128 | check_ok('${vexe} test ${ok_fpath}').matches('*OK*a_single_ok_test.v*') |
| 129 | check_ok('${vexe} test "${tdir}"').matches('*OK*a_single_ok_test.v*') |
| 130 | check_ok('${vexe} -stats test "${tdir}"').matches('*OK*a_single_ok_test.v*') |
| 131 | |
| 132 | fail_fpath := create_test('a_single_failing_test.v', 'fn test_fail(){ assert 1 == 2 }')! |
| 133 | check_fail('${vexe} ${fail_fpath}').has('> assert 1 == 2').has('a_single_failing_test.v:1: fn test_fail') |
| 134 | check_fail('${vexe} test ${fail_fpath}').has('> assert 1 == 2').has('a_single_failing_test.v:1: fn test_fail') |
| 135 | check_fail('${vexe} test "${tdir}"').has('> assert 1 == 2') |
| 136 | check_fail('${vexe} -stats test "${tdir}"').has('> assert 1 == 2') |
| 137 | rel_dir := os.join_path(tdir, rand.ulid()) |
| 138 | os.mkdir(rel_dir)! |
| 139 | os.chdir(rel_dir)! |
| 140 | relative_path := '..' + os.path_separator + 'a_single_ok_test.v' |
| 141 | check_ok('${vexe} test ${os.quoted_path(relative_path)}').has('OK').has('a_single_ok_test.v') |
| 142 | |
| 143 | check_assert_continues_works()! |
| 144 | println('> all done') |
| 145 | } |
| 146 | |