v / cmd / tools
Raw file | 418 loc (398 sloc) | 11.04 KB | Latest commit hash 017ace6ea
1module main
2
3import os
4import term
5import time
6
7const vexe_path = os.getenv('VEXE')
8
9const vroot = os.dir(vexe_path)
10
11const vexe = os.quoted_path(vexe_path)
12
13const args_string = os.args[1..].join(' ')
14
15const vargs = args_string.all_before('test-all')
16
17const vtest_nocleanup = os.getenv('VTEST_NOCLEANUP').bool()
18
19fn main() {
20 mut commands := get_all_commands()
21 // summary
22 sw := time.new_stopwatch()
23 for mut cmd in commands {
24 cmd.run()
25 }
26 spent := sw.elapsed().milliseconds()
27 oks := commands.filter(it.ecode == 0)
28 fails := commands.filter(it.ecode != 0)
29 flush_stdout()
30 println('')
31 println(term.header_left(term_highlight('Summary of `v test-all`:'), '-'))
32 println(term_highlight('Total runtime: ${spent} ms'))
33 for ocmd in oks {
34 msg := if ocmd.okmsg != '' { ocmd.okmsg } else { ocmd.line }
35 println(term.colorize(term.green, '> OK: ${msg} '))
36 }
37 for fcmd in fails {
38 msg := if fcmd.errmsg != '' { fcmd.errmsg } else { fcmd.line }
39 println(term.failed('> Failed:') + ' ${msg}')
40 }
41 flush_stdout()
42 if fails.len > 0 {
43 exit(1)
44 }
45}
46
47enum RunCommandKind {
48 system
49 execute
50}
51
52const expect_nothing = '<nothing>'
53
54const starts_with_nothing = '<nothing>'
55
56const ends_with_nothing = '<nothing>'
57
58const contains_nothing = '<nothing>'
59
60struct Command {
61mut:
62 line string
63 label string // when set, the label will be printed *before* cmd.line is executed
64 ecode int
65 okmsg string
66 errmsg string
67 rmfile string
68 runcmd RunCommandKind = .system
69 expect string = expect_nothing
70 starts_with string = starts_with_nothing
71 ends_with string = ends_with_nothing
72 contains string = contains_nothing
73 output string
74}
75
76fn get_all_commands() []Command {
77 mut res := []Command{}
78 res << Command{
79 line: '${vexe} examples/hello_world.v'
80 okmsg: 'V can compile hello world.'
81 rmfile: 'examples/hello_world'
82 }
83 res << Command{
84 line: '${vexe} -o hhww.c examples/hello_world.v'
85 okmsg: 'V can output a .c file, without compiling further.'
86 rmfile: 'hhww.c'
87 }
88 res << Command{
89 line: '${vexe} -skip-unused examples/hello_world.v'
90 okmsg: 'V can compile hello world with -skip-unused.'
91 rmfile: 'examples/hello_world'
92 }
93 res << Command{
94 line: '${vexe} -skip-unused -profile - examples/hello_world.v'
95 okmsg: 'V can compile hello world with both -skip-unused and -profile .'
96 rmfile: 'examples/hello_world'
97 }
98 res << Command{
99 line: '${vexe} -e "print(84/2)"'
100 okmsg: 'V can run code given after `-e`'
101 runcmd: .execute
102 expect: '42'
103 }
104 res << Command{
105 line: '${vexe} -e "import os; import math; print(os.args#[1..]) print(math.sin(math.pi/2).str())" arg1 arg2'
106 okmsg: 'V can run code with `-e`, that use semicolons and several imports, and that accepts CLI parameters.'
107 runcmd: .execute
108 expect: "['arg1', 'arg2']1.0"
109 }
110 $if linux || macos {
111 res << Command{
112 line: '${vexe} run examples/hello_world.v'
113 okmsg: 'V can run hello world.'
114 runcmd: .execute
115 expect: 'Hello, World!\n'
116 }
117 if os.getenv('V_CI_MUSL').len == 0 {
118 for compiler_name in ['clang', 'gcc'] {
119 if _ := os.find_abs_path_of_executable(compiler_name) {
120 res << Command{
121 line: '${vexe} -cc ${compiler_name} -gc boehm run examples/hello_world.v'
122 okmsg: '`v -cc ${compiler_name} -gc boehm run examples/hello_world.v` works'
123 runcmd: .execute
124 expect: 'Hello, World!\n'
125 }
126 }
127 }
128 }
129 res << Command{
130 line: '${vexe} interpret examples/hello_world.v'
131 okmsg: 'V can interpret hello world.'
132 runcmd: .execute
133 expect: 'Hello, World!\n'
134 }
135 res << Command{
136 line: '${vexe} interpret examples/hanoi.v'
137 okmsg: 'V can interpret hanoi.v'
138 runcmd: .execute
139 starts_with: 'Disc 1 from A to C...\n'
140 ends_with: 'Disc 1 from A to C...\n'
141 contains: 'Disc 7 from A to C...\n'
142 }
143 res << Command{
144 line: '${vexe} -o - examples/hello_world.v | grep "#define V_COMMIT_HASH" > /dev/null'
145 okmsg: 'V prints the generated source code to stdout with `-o -` .'
146 }
147 res << Command{
148 line: '${vexe} run examples/v_script.vsh > /dev/null'
149 okmsg: 'V can run the .VSH script file examples/v_script.vsh'
150 }
151 // Note: -experimental is used here, just to suppress the warningss,
152 // that are otherwise printed by the native backend,
153 // until globals and hash statements *are implemented*:
154 $if linux {
155 res << Command{
156 line: '${vexe} -experimental -b native run examples/native/hello_world.v > /dev/null'
157 okmsg: 'V compiles and runs examples/native/hello_world.v on the native backend for linux'
158 }
159 }
160 // only compilation:
161 res << Command{
162 line: '${vexe} -os linux -experimental -b native -o hw.linux examples/hello_world.v'
163 okmsg: 'V compiles hello_world.v on the native backend for linux'
164 rmfile: 'hw.linux'
165 }
166 res << Command{
167 line: '${vexe} -os macos -experimental -b native -o hw.macos examples/hello_world.v'
168 okmsg: 'V compiles hello_world.v on the native backend for macos'
169 rmfile: 'hw.macos'
170 }
171 res << Command{
172 line: '${vexe} -os windows -experimental -b native -o hw.exe examples/hello_world.v'
173 okmsg: 'V compiles hello_world.v on the native backend for windows'
174 rmfile: 'hw.exe'
175 }
176 //
177 res << Command{
178 line: '${vexe} -b js -o hw.js examples/hello_world.v'
179 okmsg: 'V compiles hello_world.v on the JS backend'
180 rmfile: 'hw.js'
181 }
182 res << Command{
183 line: '${vexe} -skip-unused -b js -o hw_skip_unused.js examples/hello_world.v'
184 okmsg: 'V compiles hello_world.v on the JS backend, with -skip-unused'
185 rmfile: 'hw_skip_unused.js'
186 }
187 res << Command{
188 line: '${vexe} -skip-unused examples/2048'
189 okmsg: 'V can compile 2048 with -skip-unused.'
190 rmfile: 'examples/2048/2048'
191 }
192 if _ := os.find_abs_path_of_executable('emcc') {
193 res << Command{
194 line: '${vexe} -os wasm32_emscripten examples/2048'
195 okmsg: 'V can compile 2048 with -os wasm32_emscripten, using emcc.'
196 rmfile: 'examples/2048/2048'
197 }
198 } else {
199 println('> emcc not found, skipping `v -os wasm32_emscripten examples/2048`.')
200 }
201 res << Command{
202 line: '${vexe} -skip-unused -live examples/hot_reload/bounce.v'
203 okmsg: 'V can compile the hot code reloading bounce.v example with both: -skip-unused -live'
204 rmfile: 'examples/hot_reload/bounce'
205 }
206 }
207 res << Command{
208 line: '${vexe} -o vtmp cmd/v'
209 okmsg: 'V can compile itself.'
210 rmfile: 'vtmp'
211 }
212 res << Command{
213 line: '${vexe} -o vtmp_werror -cstrict cmd/v'
214 okmsg: 'V can compile itself with -cstrict.'
215 rmfile: 'vtmp_werror'
216 }
217 res << Command{
218 line: '${vexe} -o vtmp_autofree -autofree cmd/v'
219 okmsg: 'V can compile itself with -autofree.'
220 rmfile: 'vtmp_autofree'
221 }
222 res << Command{
223 line: '${vexe} -o vtmp_prealloc -prealloc cmd/v'
224 okmsg: 'V can compile itself with -prealloc.'
225 rmfile: 'vtmp_prealloc'
226 }
227 res << Command{
228 line: '${vexe} -o vtmp_unused -skip-unused cmd/v'
229 okmsg: 'V can compile itself with -skip-unused.'
230 rmfile: 'vtmp_unused'
231 }
232 $if linux {
233 res << Command{
234 line: '${vexe} -cc gcc -keepc -freestanding -o bel vlib/os/bare/bare_example_linux.v'
235 okmsg: 'V can compile with -freestanding on Linux with GCC.'
236 rmfile: 'bel'
237 }
238
239 res << Command{
240 line: '${vexe} -cc gcc -keepc -freestanding -o str_array vlib/strconv/bare/str_array_example.v'
241 okmsg: 'V can compile & allocate memory with -freestanding on Linux with GCC.'
242 rmfile: 'str_array'
243 }
244 }
245 res << Command{
246 line: '${vexe} ${vargs} -progress test-cleancode'
247 okmsg: 'All .v files are invariant when processed with `v fmt`'
248 }
249 res << Command{
250 line: '${vexe} ${vargs} -progress test-fmt'
251 okmsg: 'All .v files can be processed with `v fmt`. Note: the result may not always be compilable, but `v fmt` should not crash.'
252 }
253 res << Command{
254 line: '${vexe} ${vargs} -progress test-self'
255 okmsg: 'There are no _test.v file regressions.'
256 }
257 res << Command{
258 line: '${vexe} ${vargs} -progress -W build-tools'
259 okmsg: 'All tools can be compiled.'
260 }
261 res << Command{
262 line: '${vexe} ${vargs} -progress -W build-examples'
263 okmsg: 'All examples can be compiled.'
264 }
265 res << Command{
266 line: '${vexe} check-md -hide-warnings .'
267 label: 'Check ```v ``` code examples and formatting of .MD files...'
268 okmsg: 'All .md files look good.'
269 }
270 res << Command{
271 line: '${vexe} install nedpals.args'
272 okmsg: '`v install` works.'
273 }
274 res << Command{
275 line: '${vexe} -usecache -cg examples/hello_world.v'
276 okmsg: '`v -usecache -cg` works.'
277 rmfile: 'examples/hello_world'
278 }
279 // Note: test that a program that depends on thirdparty libraries with its
280 // own #flags (tetris depends on gg, which uses sokol) can be compiled
281 // with -usecache:
282 res << Command{
283 line: '${vexe} -usecache examples/tetris/tetris.v'
284 okmsg: '`v -usecache` works.'
285 rmfile: 'examples/tetris/tetris'
286 }
287 $if macos || linux {
288 res << Command{
289 line: '${vexe} -o v.c cmd/v && cc -Werror v.c -lpthread -lm && rm -rf a.out'
290 label: 'v.c should be buildable with no warnings...'
291 okmsg: 'v.c can be compiled without warnings. This is good :)'
292 rmfile: 'v.c'
293 }
294 }
295 $if linux {
296 res << Command{
297 line: '${vexe} vlib/v/tests/bench/bench_stbi_load.v && prlimit -v10485760 vlib/v/tests/bench/bench_stbi_load'
298 okmsg: 'STBI load does not leak with GC on, when loading images multiple times (use < 10MB)'
299 runcmd: .execute
300 contains: 'logo.png 1000 times.'
301 rmfile: 'vlib/v/tests/bench/bench_stbi_load'
302 }
303 }
304 $if !windows {
305 res << Command{
306 line: '${vexe} -raw-vsh-tmp-prefix tmp vlib/v/tests/script_with_no_extension'
307 okmsg: 'V can crun a script, that lacks a .vsh extension'
308 runcmd: .execute
309 expect: 'Test\n'
310 rmfile: 'vlib/v/tests/tmp.script_with_no_extension'
311 }
312
313 res << Command{
314 line: '${vexe} -raw-vsh-tmp-prefix tmp run vlib/v/tests/script_with_no_extension'
315 okmsg: 'V can run a script, that lacks a .vsh extension'
316 runcmd: .execute
317 expect: 'Test\n'
318 }
319 }
320 return res
321}
322
323fn (mut cmd Command) run() {
324 // Changing the current directory is needed for some of the compiler tests,
325 // vlib/v/tests/local_test.v and vlib/v/tests/repl/repl_test.v
326 os.chdir(vroot) or {}
327 if cmd.label != '' {
328 println(term.header_left(cmd.label, '*'))
329 }
330 sw := time.new_stopwatch()
331 if cmd.runcmd == .system {
332 cmd.ecode = os.system(cmd.line)
333 cmd.output = ''
334 }
335 if cmd.runcmd == .execute {
336 res := os.execute(cmd.line)
337 cmd.ecode = res.exit_code
338 cmd.output = res.output
339 }
340 spent := sw.elapsed().milliseconds()
341 //
342 mut is_failed := false
343 mut is_failed_expected := false
344 mut is_failed_starts_with := false
345 mut is_failed_ends_with := false
346 mut is_failed_contains := false
347 if cmd.ecode != 0 {
348 is_failed = true
349 }
350 if cmd.expect != expect_nothing {
351 if cmd.output != cmd.expect {
352 is_failed = true
353 is_failed_expected = true
354 }
355 }
356 if cmd.starts_with != starts_with_nothing {
357 if !cmd.output.starts_with(cmd.starts_with) {
358 is_failed = true
359 is_failed_starts_with = true
360 }
361 }
362 if cmd.ends_with != ends_with_nothing {
363 if !cmd.output.ends_with(cmd.ends_with) {
364 is_failed = true
365 is_failed_ends_with = true
366 }
367 }
368 if cmd.contains != contains_nothing {
369 if !cmd.output.contains(cmd.contains) {
370 is_failed = true
371 is_failed_contains = true
372 }
373 }
374 //
375 run_label := if is_failed { term.failed('FAILED') } else { term_highlight('OK') }
376 println('> Running: "${cmd.line}" took: ${spent} ms ... ${run_label}')
377 //
378 if is_failed && is_failed_expected {
379 eprintln('> expected:\n${cmd.expect}')
380 eprintln('> output:\n${cmd.output}')
381 }
382 if is_failed && is_failed_starts_with {
383 eprintln('> expected to start with:\n${cmd.starts_with}')
384 eprintln('> output:\n${cmd.output#[..cmd.starts_with.len]}')
385 }
386 if is_failed && is_failed_ends_with {
387 eprintln('> expected to end with:\n${cmd.ends_with}')
388 eprintln('> output:\n${cmd.output#[-cmd.starts_with.len..]}')
389 }
390 if is_failed && is_failed_contains {
391 eprintln('> expected to contain:\n${cmd.contains}')
392 eprintln('> output:\n${cmd.output}')
393 }
394 if vtest_nocleanup {
395 return
396 }
397 if cmd.rmfile != '' {
398 mut file_existed := rm_existing(cmd.rmfile)
399 if os.user_os() == 'windows' {
400 file_existed = file_existed || rm_existing(cmd.rmfile + '.exe')
401 }
402 if !file_existed {
403 eprintln('Expected file did not exist: ${cmd.rmfile}')
404 cmd.ecode = 999
405 }
406 }
407}
408
409// try to remove a file, return if it existed before the removal attempt
410fn rm_existing(path string) bool {
411 existed := os.exists(path)
412 os.rm(path) or {}
413 return existed
414}
415
416fn term_highlight(s string) string {
417 return term.colorize(term.yellow, term.colorize(term.bold, s))
418}