v
Зеркало из https://github.com/vlang/v
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
19const hw_native_no_builtin_size_limit = 300
20
21const l2w_crosscc = os.find_abs_path_of_executable('x86_64-w64-mingw32-gcc-win32') or { '' }
22
23const clang_path = os.find_abs_path_of_executable('clang') or { '' }
24
25fn main() {
26mut commands := get_all_commands()
27// summary
28sw := time.new_stopwatch()
29for mut cmd in commands {
30cmd.run()
31}
32spent := sw.elapsed().milliseconds()
33oks := commands.filter(it.ecode == 0)
34fails := commands.filter(it.ecode != 0)
35flush_stdout()
36println('')
37println(term.header_left(term_highlight('Summary of `v test-all`:'), '-'))
38println(term_highlight('Total runtime: ${spent} ms'))
39for ocmd in oks {
40msg := if ocmd.okmsg != '' { ocmd.okmsg } else { ocmd.line }
41println(term.colorize(term.green, '> OK: ${msg} '))
42}
43for fcmd in fails {
44msg := if fcmd.errmsg != '' { fcmd.errmsg } else { fcmd.line }
45println(term.failed('> Failed:') + ' ${msg}')
46}
47flush_stdout()
48if fails.len > 0 {
49exit(1)
50}
51}
52
53enum RunCommandKind {
54system
55execute
56}
57
58const expect_nothing = '<nothing>'
59
60const starts_with_nothing = '<nothing>'
61
62const ends_with_nothing = '<nothing>'
63
64const contains_nothing = '<nothing>'
65
66type FnCheck = fn () !
67
68struct Command {
69mut:
70line string
71label string // when set, the label will be printed *before* cmd.line is executed
72ecode int
73okmsg string
74errmsg string
75rmfile string
76runcmd RunCommandKind = .system
77expect string = expect_nothing
78starts_with string = starts_with_nothing
79ends_with string = ends_with_nothing
80contains string = contains_nothing
81output string
82before_cb FnCheck = unsafe { nil }
83after_cb FnCheck = unsafe { nil }
84}
85
86fn get_all_commands() []Command {
87mut res := []Command{}
88res << Command{
89line: '${vexe} examples/hello_world.v'
90okmsg: 'V can compile hello world.'
91rmfile: 'examples/hello_world'
92}
93$if linux {
94if l2w_crosscc != '' {
95res << Command{
96line: '${vexe} -os windows examples/hello_world.v'
97okmsg: 'V cross compiles hello_world.v on linux, to a windows .exe file'
98rmfile: 'examples/hello_world.exe'
99}
100} else {
101eprintln('Testing cross compilation from linux to windows needs x86_64-w64-mingw32-gcc-win32. Skipping hello_world.exe test.')
102}
103}
104res << Command{
105line: '${vexe} -o hhww.c examples/hello_world.v'
106okmsg: 'V can output a .c file, without compiling further.'
107rmfile: 'hhww.c'
108}
109res << Command{
110line: '${vexe} -skip-unused examples/hello_world.v'
111okmsg: 'V can compile hello world with -skip-unused.'
112rmfile: 'examples/hello_world'
113}
114res << Command{
115line: '${vexe} -skip-unused test vlib/builtin'
116okmsg: 'V can test vlib/builtin with -skip-unused'
117}
118res << Command{
119line: '${vexe} -skip-unused -profile - examples/hello_world.v'
120okmsg: 'V can compile hello world with both -skip-unused and -profile .'
121rmfile: 'examples/hello_world'
122}
123res << Command{
124line: '${vexe} -e "print(84/2)"'
125okmsg: 'V can run code given after `-e`'
126runcmd: .execute
127expect: '42'
128}
129res << Command{
130line: '${vexe} -e "import os; import math; print(os.args#[1..]) print(math.sin(math.pi/2).str())" arg1 arg2'
131okmsg: 'V can run code with `-e`, that use semicolons and several imports, and that accepts CLI parameters.'
132runcmd: .execute
133expect: "['arg1', 'arg2']1.0"
134}
135res << Command{
136line: '${vexe} -o calling_c.exe run examples/call_c_from_v/main.c.v'
137okmsg: 'V can run main.c.v files'
138runcmd: .execute
139contains: 'V can call C functions like `puts` too.'
140}
141$if linux || macos {
142res << Command{
143line: '${vexe} run examples/hello_world.v'
144okmsg: 'V can run hello world.'
145runcmd: .execute
146expect: 'Hello, World!\n'
147}
148if clang_path != '' {
149res << Command{
150line: '${vexe} -os freebsd -gc none examples/hello_world.v'
151okmsg: 'V cross compiles hello_world.v, to a FreeBSD executable'
152rmfile: 'examples/hello_world'
153after_cb: fn () ! {
154for file in ['examples/hello_world',
155os.join_path(os.vmodules_dir(), 'freebsdroot/usr/include/stdio.h')] {
156if !os.exists(file) {
157return error('>> file ${file} does not exist')
158}
159}
160}
161}
162} else {
163eprintln('Testing cross compilation to FreeBSD, needs clang.')
164}
165if os.getenv('V_CI_MUSL').len == 0 {
166for compiler_name in ['clang', 'gcc'] {
167if _ := os.find_abs_path_of_executable(compiler_name) {
168res << Command{
169line: '${vexe} -cc ${compiler_name} -gc boehm run examples/hello_world.v'
170okmsg: '`v -cc ${compiler_name} -gc boehm run examples/hello_world.v` works'
171runcmd: .execute
172expect: 'Hello, World!\n'
173}
174}
175}
176}
177res << Command{
178line: '${vexe} interpret examples/hello_world.v'
179okmsg: 'V can interpret hello world.'
180runcmd: .execute
181expect: 'Hello, World!\n'
182}
183res << Command{
184line: '${vexe} interpret examples/hanoi.v'
185okmsg: 'V can interpret hanoi.v'
186runcmd: .execute
187starts_with: 'Disc 1 from A to C...\n'
188ends_with: 'Disc 1 from A to C...\n'
189contains: 'Disc 7 from A to C...\n'
190}
191res << Command{
192line: '${vexe} -o - examples/hello_world.v | grep "#define V_COMMIT_HASH" > /dev/null'
193okmsg: 'V prints the generated source code to stdout with `-o -` .'
194}
195res << Command{
196line: '${vexe} run examples/v_script.vsh > /dev/null'
197okmsg: 'V can run the .VSH script file examples/v_script.vsh'
198}
199// Note: -experimental is used here, just to suppress the warnings,
200// that are otherwise printed by the native backend,
201// until globals and hash statements *are implemented*:
202$if linux {
203res << Command{
204line: '${vexe} -experimental -b native run examples/native/hello_world.v > /dev/null'
205okmsg: 'V compiles and runs examples/native/hello_world.v on the native backend for linux'
206}
207res << Command{
208line: '${vexe} -no-builtin -experimental -b native examples/hello_world.v > /dev/null'
209okmsg: 'V compiles examples/hello_world.v on the native backend for linux with `-no-builtin` & the executable is <= ${hw_native_no_builtin_size_limit} bytes'
210rmfile: 'examples/hello_world'
211after_cb: fn () ! {
212file := 'examples/hello_world'
213if !os.exists(file) {
214return error('>> file ${file} does not exist')
215}
216if os.file_size(file) > hw_native_no_builtin_size_limit {
217return error('>> file ${file} bigger than ${hw_native_no_builtin_size_limit} bytes')
218}
219}
220}
221}
222// only compilation:
223res << Command{
224line: '${vexe} -os linux -experimental -b native -o hw.linux examples/hello_world.v'
225okmsg: 'V compiles hello_world.v on the native backend for linux'
226rmfile: 'hw.linux'
227}
228res << Command{
229line: '${vexe} -os macos -experimental -b native -o hw.macos examples/hello_world.v'
230okmsg: 'V compiles hello_world.v on the native backend for macos'
231rmfile: 'hw.macos'
232}
233$if windows {
234res << Command{
235line: '${vexe} -os windows -experimental -b native -o hw.exe examples/hello_world.v'
236okmsg: 'V compiles hello_world.v on the native backend for windows'
237rmfile: 'hw.exe'
238}
239}
240//
241res << Command{
242line: '${vexe} -b js -o hw.js examples/hello_world.v'
243okmsg: 'V compiles hello_world.v on the JS backend'
244rmfile: 'hw.js'
245}
246res << Command{
247line: '${vexe} -skip-unused -b js -o hw_skip_unused.js examples/hello_world.v'
248okmsg: 'V compiles hello_world.v on the JS backend, with -skip-unused'
249rmfile: 'hw_skip_unused.js'
250}
251res << Command{
252line: '${vexe} -skip-unused examples/2048'
253okmsg: 'V can compile 2048 with -skip-unused.'
254rmfile: 'examples/2048/2048'
255}
256if _ := os.find_abs_path_of_executable('emcc') {
257res << Command{
258line: '${vexe} -os wasm32_emscripten examples/2048'
259okmsg: 'V can compile 2048 with -os wasm32_emscripten, using emcc.'
260rmfile: 'examples/2048/2048'
261}
262} else {
263println('> emcc not found, skipping `v -os wasm32_emscripten examples/2048`.')
264}
265res << Command{
266line: '${vexe} -skip-unused -live examples/hot_reload/bounce.v'
267okmsg: 'V can compile the hot code reloading bounce.v example with both: -skip-unused -live'
268rmfile: 'examples/hot_reload/bounce'
269}
270}
271res << Command{
272line: '${vexe} -o vtmp cmd/v'
273okmsg: 'V can compile itself.'
274rmfile: 'vtmp'
275}
276res << Command{
277line: '${vexe} -o vtmp_werror -cstrict cmd/v'
278okmsg: 'V can compile itself with -cstrict.'
279rmfile: 'vtmp_werror'
280}
281res << Command{
282line: '${vexe} -o vtmp_autofree -autofree cmd/v'
283okmsg: 'V can compile itself with -autofree.'
284rmfile: 'vtmp_autofree'
285}
286res << Command{
287line: '${vexe} -o vtmp_prealloc -prealloc cmd/v'
288okmsg: 'V can compile itself with -prealloc.'
289rmfile: 'vtmp_prealloc'
290}
291res << Command{
292line: '${vexe} -o vtmp_unused -skip-unused cmd/v'
293okmsg: 'V can compile itself with -skip-unused.'
294rmfile: 'vtmp_unused'
295}
296$if linux {
297res << Command{
298line: '${vexe} -cc gcc -keepc -freestanding -o bel vlib/os/bare/bare_example_linux.v'
299okmsg: 'V can compile with -freestanding on Linux with GCC.'
300rmfile: 'bel'
301}
302
303res << Command{
304line: '${vexe} -cc gcc -keepc -freestanding -o str_array vlib/strconv/bare/str_array_example.v'
305okmsg: 'V can compile & allocate memory with -freestanding on Linux with GCC.'
306rmfile: 'str_array'
307}
308}
309////////////////////////////////////////////////////////////////////////
310// Test compilation of a shared library (.so, .dll. .dylib) with -shared:
311common_shared_flags := '-shared -skip-unused -d no_backtrace -o library examples/dynamic_library_loader/modules/library/library.v'
312$if macos {
313res << Command{
314line: '${vexe} ${common_shared_flags}'
315okmsg: 'V compiles library.v with -shared on macos'
316rmfile: 'library.dylib'
317}
318}
319$if linux {
320res << Command{
321line: '${vexe} ${common_shared_flags}'
322okmsg: 'V compiles library.v with -shared on linux'
323rmfile: 'library.so'
324}
325}
326// Test cross compilation to windows with -shared:
327$if linux {
328if l2w_crosscc != '' {
329res << Command{
330line: '${vexe} -os windows ${common_shared_flags}'
331okmsg: 'V cross compiles library.v with -shared on linux, to a windows library.dll file'
332rmfile: 'library.dll'
333}
334} else {
335eprintln('Testing cross compilation from linux to windows needs x86_64-w64-mingw32-gcc-win32. Skipping library.dll test.')
336}
337}
338////////////////////////////////////////////////////////////////////////
339res << Command{
340line: '${vexe} ${vargs} -progress test-cleancode'
341okmsg: 'All .v files are invariant when processed with `v fmt`'
342}
343res << Command{
344line: '${vexe} ${vargs} -progress test-fmt'
345okmsg: 'All .v files can be processed with `v fmt`. Note: the result may not always be compilable, but `v fmt` should not crash.'
346}
347res << Command{
348line: '${vexe} ${vargs} -progress test-self'
349okmsg: 'There are no _test.v file regressions.'
350}
351res << Command{
352line: '${vexe} ${vargs} -progress -N -W build-tools'
353okmsg: 'All tools can be compiled.'
354}
355res << Command{
356line: '${vexe} ${vargs} -progress -N -W build-examples'
357okmsg: 'All examples can be compiled.'
358}
359res << Command{
360line: '${vexe} check-md -hide-warnings .'
361label: 'Check ```v ``` code examples and formatting of .MD files...'
362okmsg: 'All .md files look good.'
363}
364res << Command{
365line: '${vexe} install nedpals.args'
366okmsg: '`v install` works.'
367}
368res << Command{
369okmsg: 'Running net.http with -d trace_http_request works.'
370line: '${vexe} -d trace_http_request -e \'import net.http; x := http.fetch(url: "https://vpm.url4e.com/some/unknown/url")!; println(x.status_code)\''
371runcmd: .execute
372starts_with: '> GET /some/unknown/url HTTP/1.1'
373contains: 'User-Agent: v.http'
374ends_with: '404\n'
375}
376res << Command{
377okmsg: 'Running net.http with -d trace_http_response works.'
378line: '${vexe} -d trace_http_response -e \'import net.http; x := http.fetch(url: "https://vpm.url4e.com/some/unknown/url")!; println(x.status_code)\''
379runcmd: .execute
380starts_with: '< HTTP/1.1 404 Not Found'
381contains: 'Server: nginx'
382ends_with: '404\n'
383}
384res << Command{
385line: '${vexe} -usecache -cg examples/hello_world.v'
386okmsg: '`v -usecache -cg` works.'
387rmfile: 'examples/hello_world'
388}
389// Note: test that a program that depends on thirdparty libraries with its
390// own #flags (tetris depends on gg, which uses sokol) can be compiled
391// with -usecache:
392res << Command{
393line: '${vexe} -usecache examples/tetris/tetris.v'
394okmsg: '`v -usecache` works.'
395rmfile: 'examples/tetris/tetris'
396}
397$if macos || linux {
398res << Command{
399line: '${vexe} -o v.c cmd/v && cc -Werror v.c -lpthread -lm && rm -rf a.out'
400label: 'v.c should be buildable with no warnings...'
401okmsg: 'v.c can be compiled without warnings. This is good :)'
402rmfile: 'v.c'
403}
404}
405$if linux || macos {
406res << Command{
407line: '${vexe} -gc none -no-retry-compilation -cc tcc -d use_openssl -showcc examples/veb/todo/main.v'
408okmsg: 'A simple veb app, compiles with `-gc none -no-retry-compilation -cc tcc -d use_openssl` on macos and linux'
409rmfile: 'examples/veb/todo/main'
410}
411}
412$if linux {
413res << Command{
414line: '${vexe} vlib/v/tests/bench/bench_stbi_load.v && prlimit -v10485760 vlib/v/tests/bench/bench_stbi_load'
415okmsg: 'STBI load does not leak with GC on, when loading images multiple times (use < 10MB)'
416runcmd: .execute
417contains: 'logo.png 1000 times.'
418rmfile: 'vlib/v/tests/bench/bench_stbi_load'
419}
420}
421$if !windows {
422res << Command{
423line: '${vexe} -raw-vsh-tmp-prefix tmp vlib/v/tests/script_with_no_extension'
424okmsg: 'V can crun a script, that lacks a .vsh extension'
425runcmd: .execute
426expect: 'Test\n'
427rmfile: 'vlib/v/tests/tmp.script_with_no_extension'
428}
429
430res << Command{
431line: '${vexe} -raw-vsh-tmp-prefix tmp run vlib/v/tests/script_with_no_extension'
432okmsg: 'V can run a script, that lacks a .vsh extension'
433runcmd: .execute
434expect: 'Test\n'
435}
436}
437return res
438}
439
440fn (mut cmd Command) run() {
441// Changing the current directory is needed for some of the compiler tests,
442// vlib/v/tests/local_test.v and vlib/v/tests/repl/repl_test.v
443os.chdir(vroot) or {}
444if cmd.label != '' {
445println(term.header_left(cmd.label, '*'))
446}
447if cmd.before_cb != unsafe { nil } {
448cmd.before_cb() or {
449cmd.ecode = -1
450cmd.errmsg = '> before_cb callback for "${cmd.line}" ${term.failed('FAILED')}\n${err}'
451println(cmd.errmsg)
452return
453}
454}
455sw := time.new_stopwatch()
456if cmd.runcmd == .system {
457cmd.ecode = os.system(cmd.line)
458cmd.output = ''
459}
460if cmd.runcmd == .execute {
461res := os.execute(cmd.line)
462cmd.ecode = res.exit_code
463cmd.output = res.output
464}
465spent := sw.elapsed().milliseconds()
466if cmd.after_cb != unsafe { nil } {
467cmd.after_cb() or {
468cmd.ecode = -1
469cmd.errmsg = '> after_cb callback for "${cmd.line}" ${term.failed('FAILED')}\n${err}'
470println(cmd.errmsg)
471return
472}
473}
474
475mut is_failed := false
476mut is_failed_expected := false
477mut is_failed_starts_with := false
478mut is_failed_ends_with := false
479mut is_failed_contains := false
480if cmd.ecode != 0 {
481is_failed = true
482}
483if cmd.expect != expect_nothing {
484if cmd.output != cmd.expect {
485is_failed = true
486is_failed_expected = true
487}
488}
489if cmd.starts_with != starts_with_nothing {
490if !cmd.output.starts_with(cmd.starts_with) {
491is_failed = true
492is_failed_starts_with = true
493}
494}
495if cmd.ends_with != ends_with_nothing {
496if !cmd.output.ends_with(cmd.ends_with) {
497is_failed = true
498is_failed_ends_with = true
499}
500}
501if cmd.contains != contains_nothing {
502if !cmd.output.contains(cmd.contains) {
503is_failed = true
504is_failed_contains = true
505}
506}
507
508run_label := if is_failed { term.failed('FAILED') } else { term_highlight('OK') }
509println('> Running: "${cmd.line}" took: ${spent} ms ... ${run_label}')
510
511if is_failed && is_failed_expected {
512eprintln('> expected:\n${cmd.expect}')
513eprintln('> output:\n${cmd.output}')
514}
515if is_failed && is_failed_starts_with {
516eprintln('> expected to start with:\n${cmd.starts_with}')
517eprintln('> output:\n${cmd.output#[..cmd.starts_with.len]}')
518}
519if is_failed && is_failed_ends_with {
520eprintln('> expected to end with:\n${cmd.ends_with}')
521eprintln('> output:\n${cmd.output#[-cmd.starts_with.len..]}')
522}
523if is_failed && is_failed_contains {
524eprintln('> expected to contain:\n${cmd.contains}')
525eprintln('> output:\n${cmd.output}')
526}
527if vtest_nocleanup {
528return
529}
530if cmd.rmfile != '' {
531mut file_existed := rm_existing(cmd.rmfile)
532if os.user_os() == 'windows' {
533file_existed = file_existed || rm_existing(cmd.rmfile + '.exe')
534}
535if !file_existed {
536eprintln('Expected file did not exist: ${cmd.rmfile}')
537cmd.ecode = 999
538}
539}
540}
541
542// try to remove a file, return if it existed before the removal attempt
543fn rm_existing(path string) bool {
544existed := os.exists(path)
545os.rm(path) or {}
546return existed
547}
548
549fn term_highlight(s string) string {
550return term.colorize(term.yellow, term.colorize(term.bold, s))
551}
552