| 1 | import os |
| 2 | |
| 3 | const vexe = @VEXE |
| 4 | const tests_dir = os.dir(@FILE) |
| 5 | const v3_dir = os.dir(tests_dir) |
| 6 | const vlib_dir = os.dir(v3_dir) |
| 7 | const v3_src = os.join_path(v3_dir, 'v3.v') |
| 8 | |
| 9 | fn build_v3_with(name string, flags string) string { |
| 10 | v3_bin := os.join_path(os.temp_dir(), name) |
| 11 | build := |
| 12 | os.execute('${vexe} -gc none ${flags} -path "${vlib_dir}|@vlib|@vmodules" -o ${v3_bin} ${v3_src}') |
| 13 | assert build.exit_code == 0, build.output |
| 14 | return v3_bin |
| 15 | } |
| 16 | |
| 17 | fn build_v3() string { |
| 18 | return build_v3_with('v3_test_file_harness_test', '') |
| 19 | } |
| 20 | |
| 21 | fn write_source(name string, suffix string, src string) string { |
| 22 | src_path := os.join_path(os.temp_dir(), 'v3_${name}${suffix}') |
| 23 | os.write_file(src_path, src) or { panic(err) } |
| 24 | return src_path |
| 25 | } |
| 26 | |
| 27 | fn write_project(name string, files map[string]string) string { |
| 28 | root := os.join_path(os.temp_dir(), 'v3_${name}') |
| 29 | os.rmdir_all(root) or {} |
| 30 | for rel, src in files { |
| 31 | path := os.join_path(root, rel) |
| 32 | os.mkdir_all(os.dir(path)) or { panic(err) } |
| 33 | os.write_file(path, src) or { panic(err) } |
| 34 | } |
| 35 | return root |
| 36 | } |
| 37 | |
| 38 | fn gen_c(v3_bin string, name string, suffix string, src string) string { |
| 39 | return gen_c_flags(v3_bin, name, suffix, src, '') |
| 40 | } |
| 41 | |
| 42 | fn gen_c_flags(v3_bin string, name string, suffix string, src string, flags string) string { |
| 43 | src_path := write_source(name, suffix, src) |
| 44 | c_path := os.join_path(os.temp_dir(), 'v3_${name}.c') |
| 45 | os.rm(c_path) or {} |
| 46 | compile := os.execute('${v3_bin} ${flags} ${src_path} -b c -o ${c_path}') |
| 47 | assert compile.exit_code == 0, '${name}: C output failed: ${compile.output}' |
| 48 | assert os.exists(c_path), '${name}: missing generated C file ${c_path}' |
| 49 | return os.read_file(c_path) or { panic(err) } |
| 50 | } |
| 51 | |
| 52 | fn compile_and_run(v3_bin string, name string, suffix string, src string) os.Result { |
| 53 | return compile_and_run_flags(v3_bin, name, suffix, src, '') |
| 54 | } |
| 55 | |
| 56 | fn compile_and_run_flags(v3_bin string, name string, suffix string, src string, flags string) os.Result { |
| 57 | src_path := write_source(name, suffix, src) |
| 58 | bin_path := os.join_path(os.temp_dir(), 'v3_${name}') |
| 59 | compile := os.execute('${v3_bin} ${flags} ${src_path} -b c -o ${bin_path}') |
| 60 | assert compile.exit_code == 0, '${name}: compile failed: ${compile.output}' |
| 61 | assert !compile.output.contains("undefined reference to `main'"), compile.output |
| 62 | return os.execute(bin_path) |
| 63 | } |
| 64 | |
| 65 | fn compile_project_and_run(v3_bin string, name string, files map[string]string) (os.Result, string) { |
| 66 | root := write_project(name, files) |
| 67 | return compile_project_root_and_run(v3_bin, name, root) |
| 68 | } |
| 69 | |
| 70 | fn compile_project_root_and_run(v3_bin string, name string, root string) (os.Result, string) { |
| 71 | bin_path := os.join_path(root, 'out') |
| 72 | compile := os.execute('${v3_bin} ${root} -b c -o ${bin_path}') |
| 73 | assert compile.exit_code == 0, '${name}: compile failed: ${compile.output}' |
| 74 | run := os.execute(bin_path) |
| 75 | c_code := os.read_file(bin_path + '.c') or { panic(err) } |
| 76 | return run, c_code |
| 77 | } |
| 78 | |
| 79 | fn compile_expect_failure(v3_bin string, name string, suffix string, src string) os.Result { |
| 80 | return compile_expect_failure_flags(v3_bin, name, suffix, src, '') |
| 81 | } |
| 82 | |
| 83 | fn compile_expect_failure_flags(v3_bin string, name string, suffix string, src string, flags string) os.Result { |
| 84 | src_path := write_source(name, suffix, src) |
| 85 | bin_path := os.join_path(os.temp_dir(), 'v3_${name}') |
| 86 | c_path := bin_path + '.c' |
| 87 | os.rm(c_path) or {} |
| 88 | compile := os.execute('${v3_bin} ${flags} ${src_path} -b c -o ${bin_path}') |
| 89 | assert compile.exit_code != 0, '${name}: compile unexpectedly succeeded: ${compile.output}' |
| 90 | assert !os.exists(c_path), '${name}: generated C despite harness input failure' |
| 91 | return compile |
| 92 | } |
| 93 | |
| 94 | fn test_v3_generates_minimal_test_file_harness() { |
| 95 | v3_bin := build_v3() |
| 96 | order_src := "fn test_one() { |
| 97 | println('one') |
| 98 | } |
| 99 | |
| 100 | fn test_two() { |
| 101 | println('two') |
| 102 | } |
| 103 | " |
| 104 | order_c := gen_c(v3_bin, 'harness_order', '_test.c.v', order_src) |
| 105 | assert order_c.contains('int main('), order_c |
| 106 | one_idx := order_c.index('test_one();') or { -1 } |
| 107 | two_idx := order_c.index('test_two();') or { -1 } |
| 108 | assert one_idx >= 0, order_c |
| 109 | assert two_idx > one_idx, order_c |
| 110 | order_run := compile_and_run(v3_bin, 'harness_order_run', '_test.c.v', order_src) |
| 111 | assert order_run.exit_code == 0, order_run.output |
| 112 | assert order_run.output.trim_space() == 'one\ntwo' |
| 113 | |
| 114 | parallel_v3_bin := build_v3_with('v3_test_file_harness_parallel_test', '-d parallel') |
| 115 | parallel_c := gen_c(parallel_v3_bin, 'harness_parallel_order', '_test.c.v', order_src) |
| 116 | assert parallel_c.contains('int main('), parallel_c |
| 117 | assert parallel_c.contains('test_one();'), parallel_c |
| 118 | assert parallel_c.contains('test_two();'), parallel_c |
| 119 | parallel_run := compile_and_run(parallel_v3_bin, 'harness_parallel_order_run', '_test.c.v', |
| 120 | order_src) |
| 121 | assert parallel_run.exit_code == 0, parallel_run.output |
| 122 | assert parallel_run.output.trim_space() == 'one\ntwo' |
| 123 | |
| 124 | hooks_src := "fn testsuite_begin() { |
| 125 | println('begin') |
| 126 | } |
| 127 | |
| 128 | fn testsuite_end() { |
| 129 | println('end') |
| 130 | } |
| 131 | |
| 132 | fn before_each() { |
| 133 | println('before') |
| 134 | } |
| 135 | |
| 136 | fn after_each() { |
| 137 | println('after') |
| 138 | } |
| 139 | |
| 140 | fn test_option() ? { |
| 141 | println('option') |
| 142 | return |
| 143 | } |
| 144 | |
| 145 | fn test_result() ! { |
| 146 | println('result') |
| 147 | return |
| 148 | } |
| 149 | " |
| 150 | hooks_run := compile_and_run(v3_bin, 'harness_hooks', '_test.v', hooks_src) |
| 151 | assert hooks_run.exit_code == 0, hooks_run.output |
| 152 | assert hooks_run.output.trim_space() == 'begin\nbefore\noption\nafter\nbefore\nresult\nafter\nend' |
| 153 | |
| 154 | result_fail := compile_and_run(v3_bin, 'harness_result_fail', '_test.v', "fn test_fail() ! { |
| 155 | return error('bad') |
| 156 | } |
| 157 | ") |
| 158 | assert result_fail.exit_code != 0 |
| 159 | assert result_fail.output.contains('test failed: test_fail') |
| 160 | |
| 161 | result_fail_cleanup := compile_and_run(v3_bin, 'harness_result_fail_cleanup', '_test.v', "fn testsuite_end() { |
| 162 | println('end') |
| 163 | } |
| 164 | |
| 165 | fn after_each() { |
| 166 | println('after') |
| 167 | } |
| 168 | |
| 169 | fn test_fail() ! { |
| 170 | return error('bad') |
| 171 | } |
| 172 | ") |
| 173 | assert result_fail_cleanup.exit_code != 0 |
| 174 | assert result_fail_cleanup.output.contains('test failed: test_fail') |
| 175 | assert result_fail_cleanup.output.contains('after') |
| 176 | assert result_fail_cleanup.output.contains('end') |
| 177 | |
| 178 | option_fail := compile_and_run(v3_bin, 'harness_option_fail', '_test.v', 'fn test_fail() ? { |
| 179 | return none |
| 180 | } |
| 181 | ') |
| 182 | assert option_fail.exit_code != 0 |
| 183 | assert option_fail.output.contains('test failed: test_fail') |
| 184 | |
| 185 | option_fail_cleanup := compile_and_run(v3_bin, 'harness_option_fail_cleanup', '_test.v', "fn testsuite_end() { |
| 186 | println('end') |
| 187 | } |
| 188 | |
| 189 | fn after_each() { |
| 190 | println('after') |
| 191 | } |
| 192 | |
| 193 | fn test_fail() ? { |
| 194 | return none |
| 195 | } |
| 196 | ") |
| 197 | assert option_fail_cleanup.exit_code != 0 |
| 198 | assert option_fail_cleanup.output.contains('test failed: test_fail') |
| 199 | assert option_fail_cleanup.output.contains('after') |
| 200 | assert option_fail_cleanup.output.contains('end') |
| 201 | |
| 202 | assert_fail := compile_and_run(v3_bin, 'harness_assert_fail', '_test.v', 'fn test_fail() { |
| 203 | assert false |
| 204 | } |
| 205 | ') |
| 206 | assert assert_fail.exit_code != 0 |
| 207 | assert assert_fail.output.contains('assert failed') |
| 208 | |
| 209 | invalid_param := compile_expect_failure(v3_bin, 'harness_invalid_param', '_test.v', 'fn test_bad(i int) { |
| 210 | } |
| 211 | ') |
| 212 | assert invalid_param.output.contains('invalid test signature'), invalid_param.output |
| 213 | |
| 214 | generic_test := compile_expect_failure(v3_bin, 'harness_generic_test', '_test.v', 'fn test_generic[T]() { |
| 215 | } |
| 216 | ') |
| 217 | assert generic_test.output.contains('invalid test signature'), generic_test.output |
| 218 | |
| 219 | invalid_return := compile_expect_failure(v3_bin, 'harness_invalid_return', '_test.v', 'fn test_bad() int { |
| 220 | return 1 |
| 221 | } |
| 222 | ') |
| 223 | assert invalid_return.output.contains('invalid test signature'), invalid_return.output |
| 224 | |
| 225 | non_main := compile_expect_failure(v3_bin, 'harness_non_main_module', '_test.c.v', 'module sample |
| 226 | |
| 227 | fn test_one() { |
| 228 | } |
| 229 | ') |
| 230 | assert non_main.output.contains('no runnable tests'), non_main.output |
| 231 | |
| 232 | result_hook := compile_expect_failure(v3_bin, 'harness_result_hook', '_test.v', 'fn before_each() ! { |
| 233 | return |
| 234 | } |
| 235 | |
| 236 | fn test_one() { |
| 237 | } |
| 238 | ') |
| 239 | assert result_hook.output.contains('invalid test hook signature'), result_hook.output |
| 240 | |
| 241 | generic_hook := compile_expect_failure(v3_bin, 'harness_generic_hook', '_test.v', 'fn before_each[T]() { |
| 242 | } |
| 243 | |
| 244 | fn test_one() { |
| 245 | } |
| 246 | ') |
| 247 | assert generic_hook.output.contains('invalid test hook signature'), generic_hook.output |
| 248 | |
| 249 | option_hook := compile_expect_failure(v3_bin, 'harness_option_hook', '_test.v', 'fn after_each() ? { |
| 250 | return |
| 251 | } |
| 252 | |
| 253 | fn test_one() { |
| 254 | } |
| 255 | ') |
| 256 | assert option_hook.output.contains('invalid test hook signature'), option_hook.output |
| 257 | |
| 258 | ordinary_c := gen_c(v3_bin, 'harness_plain_file', '.v', "fn test_lonely() { |
| 259 | println('lonely') |
| 260 | } |
| 261 | ") |
| 262 | assert !ordinary_c.contains('int main('), ordinary_c |
| 263 | assert !ordinary_c.contains('test_lonely();'), ordinary_c |
| 264 | } |
| 265 | |
| 266 | fn test_v3_test_file_harness_rejects_top_level_stmt() { |
| 267 | v3_bin := build_v3() |
| 268 | src := "println('top') |
| 269 | |
| 270 | fn test_one() { |
| 271 | println('test') |
| 272 | } |
| 273 | " |
| 274 | invalid := compile_expect_failure(v3_bin, 'harness_top_level_and_test', '_test.v', src) |
| 275 | assert invalid.output.contains('executable top-level statements are not supported in test files'), invalid.output |
| 276 | } |
| 277 | |
| 278 | fn test_v3_test_file_harness_owns_entrypoint_over_user_main() { |
| 279 | v3_bin := build_v3() |
| 280 | src := "fn main() { |
| 281 | println('user-main') |
| 282 | } |
| 283 | |
| 284 | fn test_one() { |
| 285 | println('test') |
| 286 | } |
| 287 | " |
| 288 | c_code := gen_c(v3_bin, 'harness_user_main', '_test.v', src) |
| 289 | assert c_code.count('int main(') == 1, c_code |
| 290 | assert c_code.contains('test_one();'), c_code |
| 291 | run := compile_and_run(v3_bin, 'harness_user_main_run', '_test.v', src) |
| 292 | assert run.exit_code == 0, run.output |
| 293 | assert run.output.trim_space() == 'test' |
| 294 | } |
| 295 | |
| 296 | fn test_v3_test_file_harness_keeps_user_main_callable() { |
| 297 | v3_bin := build_v3() |
| 298 | src := "fn main() { |
| 299 | println('user-main') |
| 300 | } |
| 301 | |
| 302 | fn test_call_main() { |
| 303 | main() |
| 304 | } |
| 305 | " |
| 306 | c_code := gen_c(v3_bin, 'harness_user_main_callable', '_test.v', src) |
| 307 | assert c_code.count('int main(') == 1, c_code |
| 308 | assert c_code.contains('main__user_main'), c_code |
| 309 | assert c_code.contains('main__user_main();'), c_code |
| 310 | assert c_code.contains('test_call_main();'), c_code |
| 311 | run := compile_and_run(v3_bin, 'harness_user_main_callable_run', '_test.v', src) |
| 312 | assert run.exit_code == 0, run.output |
| 313 | assert run.output.trim_space() == 'user-main' |
| 314 | } |
| 315 | |
| 316 | fn test_v3_test_file_harness_forward_declares_user_main() { |
| 317 | v3_bin := build_v3() |
| 318 | src := "fn test_call_main() { |
| 319 | main() |
| 320 | } |
| 321 | |
| 322 | fn main() { |
| 323 | println('late-main') |
| 324 | } |
| 325 | " |
| 326 | c_code := gen_c(v3_bin, 'harness_user_main_forward', '_test.v', src) |
| 327 | assert c_code.count('int main(') == 1, c_code |
| 328 | proto_idx := c_code.index('void main__user_main(void);') or { -1 } |
| 329 | test_idx := c_code.index('void test_call_main(void) {') or { -1 } |
| 330 | assert proto_idx >= 0, c_code |
| 331 | assert test_idx > proto_idx, c_code |
| 332 | assert c_code.contains('main__user_main();'), c_code |
| 333 | run := compile_and_run(v3_bin, 'harness_user_main_forward_run', '_test.v', src) |
| 334 | assert run.exit_code == 0, run.output |
| 335 | assert run.output.trim_space() == 'late-main' |
| 336 | } |
| 337 | |
| 338 | fn test_v3_test_file_harness_renames_user_main_without_collision() { |
| 339 | v3_bin := build_v3() |
| 340 | src := "fn main__user_main() { |
| 341 | println('collision') |
| 342 | } |
| 343 | |
| 344 | fn main() { |
| 345 | println('user-main') |
| 346 | } |
| 347 | |
| 348 | fn test_call_both() { |
| 349 | main__user_main() |
| 350 | main() |
| 351 | } |
| 352 | " |
| 353 | c_code := gen_c(v3_bin, 'harness_user_main_collision', '_test.v', src) |
| 354 | assert c_code.count('int main(') == 1, c_code |
| 355 | assert c_code.contains('void main__user_main('), c_code |
| 356 | assert c_code.contains('void main__user_main_1('), c_code |
| 357 | assert c_code.contains('main__user_main();'), c_code |
| 358 | assert c_code.contains('main__user_main_1();'), c_code |
| 359 | run := compile_and_run(v3_bin, 'harness_user_main_collision_run', '_test.v', src) |
| 360 | assert run.exit_code == 0, run.output |
| 361 | assert run.output.trim_space() == 'collision\nuser-main' |
| 362 | } |
| 363 | |
| 364 | fn test_v3_test_file_harness_renames_user_main_fn_value() { |
| 365 | v3_bin := build_v3() |
| 366 | src := "fn main() { |
| 367 | println('user-main') |
| 368 | } |
| 369 | |
| 370 | fn test_fn_value_main() { |
| 371 | f := main |
| 372 | f() |
| 373 | } |
| 374 | " |
| 375 | c_code := gen_c(v3_bin, 'harness_user_main_fn_value', '_test.v', src) |
| 376 | assert c_code.count('int main(') == 1, c_code |
| 377 | assert c_code.contains('main__user_main'), c_code |
| 378 | assert c_code.contains('test_fn_value_main();'), c_code |
| 379 | assert !c_code.contains('= main;'), c_code |
| 380 | run := compile_and_run(v3_bin, 'harness_user_main_fn_value_run', '_test.v', src) |
| 381 | assert run.exit_code == 0, run.output |
| 382 | assert run.output.trim_space() == 'user-main' |
| 383 | } |
| 384 | |
| 385 | fn test_v3_test_file_harness_renames_user_main_fn_arg() { |
| 386 | v3_bin := build_v3() |
| 387 | src := "fn takes(cb fn ()) { |
| 388 | cb() |
| 389 | } |
| 390 | |
| 391 | fn main() { |
| 392 | println('user-main') |
| 393 | } |
| 394 | |
| 395 | fn test_fn_arg_main() { |
| 396 | takes(main) |
| 397 | } |
| 398 | " |
| 399 | c_code := gen_c(v3_bin, 'harness_user_main_fn_arg', '_test.v', src) |
| 400 | assert c_code.count('int main(') == 1, c_code |
| 401 | assert c_code.contains('takes(main__user_main);'), c_code |
| 402 | assert !c_code.contains('takes(main);'), c_code |
| 403 | run := compile_and_run(v3_bin, 'harness_user_main_fn_arg_run', '_test.v', src) |
| 404 | assert run.exit_code == 0, run.output |
| 405 | assert run.output.trim_space() == 'user-main' |
| 406 | } |
| 407 | |
| 408 | fn test_v3_test_file_harness_does_not_rename_shadowed_main_fn_value_call() { |
| 409 | v3_bin := build_v3() |
| 410 | src := "fn call_shadowed(main fn ()) { |
| 411 | main() |
| 412 | } |
| 413 | |
| 414 | fn local_cb() { |
| 415 | println('local-cb') |
| 416 | } |
| 417 | |
| 418 | fn main() { |
| 419 | println('user-main') |
| 420 | } |
| 421 | |
| 422 | fn test_shadowed_main() { |
| 423 | call_shadowed(local_cb) |
| 424 | } |
| 425 | " |
| 426 | c_code := gen_c(v3_bin, 'harness_shadowed_main_fn_value_call', '_test.v', src) |
| 427 | assert c_code.count('int main(') == 1, c_code |
| 428 | assert c_code.contains('main();'), c_code |
| 429 | assert !c_code.contains('main__user_main();'), c_code |
| 430 | run := compile_and_run(v3_bin, 'harness_shadowed_main_fn_value_call_run', '_test.v', src) |
| 431 | assert run.exit_code == 0, run.output |
| 432 | assert run.output.trim_space() == 'local-cb' |
| 433 | } |
| 434 | |
| 435 | fn test_v3_test_file_harness_finds_top_level_comptime_block_tests() { |
| 436 | v3_bin := build_v3() |
| 437 | src := "$if v3_nested_harness ? { |
| 438 | fn before_each() { |
| 439 | println('before') |
| 440 | } |
| 441 | |
| 442 | fn test_nested() { |
| 443 | println('nested') |
| 444 | } |
| 445 | } |
| 446 | " |
| 447 | c_code := gen_c_flags(v3_bin, 'harness_nested_block', '_test.v', src, '-d v3_nested_harness') |
| 448 | assert c_code.contains('before_each();'), c_code |
| 449 | assert c_code.contains('test_nested();'), c_code |
| 450 | run := compile_and_run_flags(v3_bin, 'harness_nested_block_run', '_test.v', src, |
| 451 | '-d v3_nested_harness') |
| 452 | assert run.exit_code == 0, run.output |
| 453 | assert run.output.trim_space() == 'before\nnested' |
| 454 | |
| 455 | invalid := compile_expect_failure_flags(v3_bin, 'harness_nested_invalid', '_test.v', '$if v3_nested_harness ? { |
| 456 | fn test_bad(i int) { |
| 457 | } |
| 458 | } |
| 459 | ', |
| 460 | '-d v3_nested_harness') |
| 461 | assert invalid.output.contains('invalid test signature'), invalid.output |
| 462 | |
| 463 | invalid_generic := compile_expect_failure_flags(v3_bin, 'harness_nested_generic_invalid', |
| 464 | '_test.v', '$if v3_nested_harness ? { |
| 465 | fn before_each[T]() { |
| 466 | } |
| 467 | |
| 468 | fn test_nested[T]() { |
| 469 | } |
| 470 | } |
| 471 | ', |
| 472 | '-d v3_nested_harness') |
| 473 | assert invalid_generic.output.contains('invalid test hook signature'), invalid_generic.output |
| 474 | assert invalid_generic.output.contains('invalid test signature'), invalid_generic.output |
| 475 | } |
| 476 | |
| 477 | fn test_v3_top_level_main_preserves_file_import_alias_context() { |
| 478 | v3_bin := build_v3() |
| 479 | run, c_code := compile_project_and_run(v3_bin, 'harness_file_alias_context', { |
| 480 | 'v.mod': 'Module { name: "alias_context" } |
| 481 | ' |
| 482 | 'a/a.v': 'module a |
| 483 | |
| 484 | pub fn value() int { |
| 485 | return 11 |
| 486 | } |
| 487 | ' |
| 488 | 'b/b.v': 'module b |
| 489 | |
| 490 | pub fn value() int { |
| 491 | return 22 |
| 492 | } |
| 493 | ' |
| 494 | 'aa_alias.v': 'module main |
| 495 | |
| 496 | import a as m |
| 497 | |
| 498 | a_value := m.value() |
| 499 | println(int_str(a_value)) |
| 500 | ' |
| 501 | 'zz_alias.v': 'module main |
| 502 | |
| 503 | import b as m |
| 504 | |
| 505 | b_value := m.value() |
| 506 | println(int_str(b_value)) |
| 507 | ' |
| 508 | }) |
| 509 | assert run.exit_code == 0, run.output |
| 510 | lines := run.output.trim_space().split_into_lines() |
| 511 | assert lines.len == 2, run.output |
| 512 | assert '11' in lines, run.output |
| 513 | assert '22' in lines, run.output |
| 514 | assert c_code.contains('a__value()'), c_code |
| 515 | assert c_code.contains('b__value()'), c_code |
| 516 | } |
| 517 | |
| 518 | fn test_v3_top_level_match_lowers_before_cgen_main() { |
| 519 | v3_bin := build_v3() |
| 520 | run := compile_and_run(v3_bin, 'harness_top_level_match', '.v', "x := match 2 { |
| 521 | 2 { |
| 522 | 7 |
| 523 | } |
| 524 | else { |
| 525 | 3 |
| 526 | } |
| 527 | } |
| 528 | println(int_str(x)) |
| 529 | match 2 { |
| 530 | 2 { |
| 531 | println('two') |
| 532 | } |
| 533 | else {} |
| 534 | } |
| 535 | ") |
| 536 | assert run.exit_code == 0, run.output |
| 537 | assert run.output.trim_space() == '7\ntwo' |
| 538 | } |
| 539 | |
| 540 | fn test_v3_top_level_defer_runs_after_synthetic_main_body() { |
| 541 | v3_bin := build_v3() |
| 542 | run := compile_and_run(v3_bin, 'harness_top_level_defer', '.v', "fn deferred_msg() string { |
| 543 | return 'deferred' |
| 544 | } |
| 545 | |
| 546 | defer { |
| 547 | println(deferred_msg()) |
| 548 | } |
| 549 | |
| 550 | println('body') |
| 551 | ") |
| 552 | assert run.exit_code == 0, run.output |
| 553 | assert run.output.trim_space() == 'body\ndeferred' |
| 554 | } |
| 555 | |