v4 / vlib / v / gen / wasm / tests_decompile / must_have_test.v
142 lines · 128 sloc · 4.23 KB · f05b0450b1493d16b6518e7cee11b1b09b39beb4
Raw
1import os
2import term
3import benchmark
4
5fn test_wasm_when_decompiled_must_have() {
6 vexe := @VEXE
7 vroot := os.dir(vexe).replace(os.path_separator, '/')
8 decompiler := os.find_abs_path_of_executable('wasm-decompile') or {
9 eprintln('> skipping test, since it needs `wasm-decompile`, which is not present')
10 return
11 }
12
13 dir := vroot + '/vlib/v/gen/wasm/tests_decompile'
14 vv_files := os.walk_ext(dir, '.vv')
15 assert vv_files.len > 0
16
17 mut bench := benchmark.new_benchmark()
18 bench.set_total_expected_steps(vv_files.len)
19
20 wrkdir := os.join_path(os.vtmp_dir(), 'wasm_tests_decompile')
21 os.mkdir_all(wrkdir)!
22 os.chdir(wrkdir)!
23
24 vv_files_loop: for vv_file in vv_files {
25 bench.step()
26 must_have_path := vv_file.replace('.vv', '.wasm.must_have')
27 must_not_have_path := vv_file.replace('.vv', '.wasm.must_not_have')
28 vv_file_name := os.file_name(vv_file).replace('.vv', '')
29 full_vv_path := os.real_path(vv_file).replace(os.path_separator, '/')
30 relative_vv_path := full_vv_path.replace(vroot + '/', '')
31 full_wasm_path := '${wrkdir}/${vv_file_name}.wasm'
32 full_dcmp_path := '${wrkdir}/${vv_file_name}.dcmp'
33
34 file_options := get_file_options(vv_file)
35 cmd := '${os.quoted_path(vexe)} -b wasm ${file_options.vflags} -o ${os.quoted_path(full_wasm_path)} ${os.quoted_path(full_vv_path)}'
36 // println(cmd)
37 res_wasm := os.execute(cmd)
38 if res_wasm.exit_code != 0 {
39 bench.fail()
40 eprintln(bench.step_message_fail(cmd))
41 eprintln(' res_wasm.exit_code: ${res_wasm.exit_code}')
42 eprintln(' res_wasm.output : ${res_wasm.output}')
43 continue
44 }
45
46 cmd_decompile := '${os.quoted_path(decompiler)} ${os.quoted_path(full_wasm_path)} --output=${os.quoted_path(full_dcmp_path)}'
47 // println(cmd_decompile)
48 res_dcmp := os.execute(cmd_decompile)
49 if res_dcmp.exit_code != 0 {
50 bench.fail()
51 eprintln(bench.step_message_fail(cmd_decompile))
52 eprintln(' res_dcmp.exit_code: ${res_dcmp.exit_code}')
53 eprintln(' res_dcmp.output : ${res_dcmp.output}')
54 continue
55 }
56
57 expected_lines := os.read_lines(must_have_path) or { [] }
58 generated_wasm_lines := os.read_lines(full_dcmp_path) or {
59 bench.fail()
60 eprintln('missing ${full_dcmp_path}')
61 continue
62 }
63
64 mut failed_patterns := []string{}
65 for idx_expected_line, eline in expected_lines {
66 if eline == '' {
67 continue
68 }
69 if !does_line_match_one_of_generated_lines(eline, generated_wasm_lines) {
70 failed_patterns << eline
71 eprintln('${must_have_path}:${idx_expected_line + 1}: expected match error:')
72 eprintln('`${cmd_decompile}` did NOT produce expected line:')
73 eprintln(term.colorize(term.red, eline))
74 continue
75 }
76 }
77 if failed_patterns.len > 0 {
78 eprintln('> failed match patterns: ${failed_patterns.len}')
79 bench.fail()
80 continue
81 }
82
83 // Optional `.wasm.must_not_have` companion: each non-empty line is a
84 // pattern that must NOT appear in the decompiled output (negative assertion).
85 forbidden_lines := os.read_lines(must_not_have_path) or { [] }
86 mut forbidden_patterns := []string{}
87 for idx_forbidden_line, fline in forbidden_lines {
88 if fline == '' {
89 continue
90 }
91 if does_line_match_one_of_generated_lines(fline, generated_wasm_lines) {
92 forbidden_patterns << fline
93 eprintln('${must_not_have_path}:${idx_forbidden_line + 1}: forbidden match error:')
94 eprintln('`${cmd_decompile}` unexpectedly produced a forbidden line:')
95 eprintln(term.colorize(term.red, fline))
96 }
97 }
98 if forbidden_patterns.len > 0 {
99 eprintln('> forbidden match patterns: ${forbidden_patterns.len}')
100 bench.fail()
101 continue
102 }
103
104 bench.ok()
105 eprintln(bench.step_message_ok(relative_vv_path))
106 }
107 bench.stop()
108 eprintln(term.h_divider('-'))
109 eprintln(bench.total_message('decompiled wasm must have'))
110 if bench.nfail > 0 {
111 exit(1)
112 }
113 os.rmdir_all(wrkdir) or {}
114}
115
116struct FileOptions {
117mut:
118 vflags string
119}
120
121pub fn get_file_options(file string) FileOptions {
122 mut res := FileOptions{}
123 lines := os.read_lines(file) or { [] }
124 for line in lines {
125 if line.starts_with('// vtest vflags:') {
126 res.vflags = line.all_after(':').trim_space()
127 }
128 }
129 return res
130}
131
132fn does_line_match_one_of_generated_lines(line string, generated_c_lines []string) bool {
133 for cline in generated_c_lines {
134 if line == cline {
135 return true
136 }
137 if cline.contains(line) {
138 return true
139 }
140 }
141 return false
142}
143