Skip to content

Commit 35b1cff

Browse files
authored
tools: support // vtest build: !do_not_test ?, // vtest build: !windows && tinyc to skip files during testing on specific platforms, without having to keep centralised skip lists (#23900)
1 parent 5439ff9 commit 35b1cff

9 files changed

Lines changed: 473 additions & 5 deletions

File tree

‎cmd/tools/modules/testing/common.v‎

Lines changed: 35 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import v.util.vtest
1212
import runtime
1313
import rand
1414
import strings
15+
import v.build_constraint
1516

1617
pub const max_header_len = get_max_header_len()
1718

@@ -98,6 +99,8 @@ pub mut:
9899
hash string // used as part of the name of the temporary directory created for tests, to ease cleanup
99100

100101
exec_mode ActionMode = .compile // .compile_and_run only for `v test`
102+
103+
build_environment build_constraint.Environment // see the documentation in v.build_constraint
101104
}
102105

103106
pub fn (mut ts TestSession) add_failed_cmd(cmd string) {
@@ -443,6 +446,9 @@ pub fn (mut ts TestSession) test() {
443446
printing_thread := spawn ts.print_messages()
444447
pool_of_test_runners.set_shared_context(ts)
445448
ts.reporter.worker_threads_start(remaining_files, mut ts)
449+
450+
ts.build_environment = get_build_environment()
451+
446452
// all the testing happens here:
447453
pool_of_test_runners.work_on_pointers(unsafe { remaining_files.pointers() })
448454

@@ -568,9 +574,23 @@ fn worker_trunner(mut p pool.PoolProcessor, idx int, thread_id int) voidptr {
568574
} else {
569575
os.quoted_path(generated_binary_fpath)
570576
}
577+
mut details := get_test_details(file)
578+
mut should_be_built := true
579+
if details.vbuild != '' {
580+
should_be_built = ts.build_environment.eval(details.vbuild) or {
581+
eprintln('${file}:${details.vbuild_line}:17: error during parsing the `// v test build` expression `${details.vbuild}`: ${err}')
582+
false
583+
}
584+
$if trace_should_be_built ? {
585+
eprintln('${file} has specific build constraint: `${details.vbuild}` => should_be_built: `${should_be_built}`')
586+
eprintln('> env facts: ${ts.build_environment.facts}')
587+
eprintln('> env defines: ${ts.build_environment.defines}')
588+
}
589+
}
590+
571591
ts.benchmark.step()
572592
tls_bench.step()
573-
if !ts.build_tools && abs_path in ts.skip_files {
593+
if !ts.build_tools && (!should_be_built || abs_path in ts.skip_files) {
574594
ts.benchmark.skip()
575595
tls_bench.skip()
576596
if !hide_skips {
@@ -599,7 +619,6 @@ fn worker_trunner(mut p pool.PoolProcessor, idx int, thread_id int) voidptr {
599619
ts.append_message_with_duration(.cmd_end, '', cmd_duration, mtc)
600620

601621
if status != 0 {
602-
details := get_test_details(file)
603622
os.setenv('VTEST_RETRY_MAX', '${details.retry}', true)
604623
for retry := 1; retry <= details.retry; retry++ {
605624
if !details.hide_retries {
@@ -686,7 +705,6 @@ fn worker_trunner(mut p pool.PoolProcessor, idx int, thread_id int) voidptr {
686705
println(r.output.split_into_lines().filter(it.contains(' assert')).join('\n'))
687706
}
688707
if r.exit_code != 0 {
689-
mut details := get_test_details(file)
690708
mut trimmed_output := r.output.trim_space()
691709
if trimmed_output.len == 0 {
692710
// retry running at least 1 more time, to avoid CI false positives as much as possible
@@ -895,19 +913,25 @@ pub mut:
895913
retry int
896914
flaky bool // when flaky tests fail, the whole run is still considered successful, unless VTEST_FAIL_FLAKY is 1
897915
//
898-
hide_retries bool // when true, all retry tries are silent; used by `vlib/v/tests/retry_test.v`
916+
hide_retries bool // when true, all retry tries are silent; used by `vlib/v/tests/retry_test.v`
917+
vbuild string // could be `!(windows && tinyc)`
918+
vbuild_line int // for more precise error reporting, if the `vbuild` expression is incorrect
899919
}
900920

901921
pub fn get_test_details(file string) TestDetails {
902922
mut res := TestDetails{}
903923
lines := os.read_lines(file) or { [] }
904-
for line in lines {
924+
for idx, line in lines {
905925
if line.starts_with('// vtest retry:') {
906926
res.retry = line.all_after(':').trim_space().int()
907927
}
908928
if line.starts_with('// vtest flaky:') {
909929
res.flaky = line.all_after(':').trim_space().bool()
910930
}
931+
if line.starts_with('// vtest build:') {
932+
res.vbuild = line.all_after(':').trim_space()
933+
res.vbuild_line = idx + 1
934+
}
911935
if line.starts_with('// vtest hide_retries') {
912936
res.hide_retries = true
913937
}
@@ -949,3 +973,9 @@ fn get_max_header_len() int {
949973
}
950974
return cols
951975
}
976+
977+
fn get_build_environment() &build_constraint.Environment {
978+
facts := os.getenv('VBUILD_FACTS').split_any(',')
979+
defines := os.getenv('VBUILD_DEFINES').split_any(',')
980+
return build_constraint.new_environment(facts, defines)
981+
}

‎cmd/v/v.v‎

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,9 @@ fn main() {
9898
exit(1)
9999
}
100100
timers.show('v parsing CLI args')
101+
102+
setup_vbuild_env_vars(prefs)
103+
101104
// Start calling the correct functions/external tools
102105
// Note for future contributors: Please add new subcommands in the `match` block below.
103106
if command in external_tools {
@@ -206,3 +209,28 @@ fn rebuild(prefs &pref.Preferences) {
206209
}
207210
}
208211
}
212+
213+
@[manualfree]
214+
fn setup_vbuild_env_vars(prefs &pref.Preferences) {
215+
mut facts := []string{cap: 10}
216+
facts << prefs.os.lower()
217+
facts << prefs.ccompiler_type.str()
218+
facts << prefs.arch.str()
219+
if prefs.is_prod {
220+
facts << 'prod'
221+
}
222+
github_job := os.getenv('GITHUB_JOB')
223+
if github_job != '' {
224+
facts << github_job
225+
}
226+
sfacts := facts.join(',')
227+
os.setenv('VBUILD_FACTS', sfacts, true)
228+
229+
sdefines := prefs.compile_defines_all.join(',')
230+
os.setenv('VBUILD_DEFINES', sdefines, true)
231+
232+
unsafe { sdefines.free() }
233+
unsafe { sfacts.free() }
234+
unsafe { github_job.free() }
235+
unsafe { facts.free() }
236+
}

‎vlib/math/big/array_ops_test.v‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
// vtest build: !do_not_test ?
12
module big
23

34
fn test_add_digit_array_01() {

‎vlib/v/build_constraint/ast.v‎

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
module build_constraint
2+
3+
// ast:
4+
struct BExpr {
5+
expr BOr
6+
}
7+
8+
struct BOr {
9+
exprs []BAnd
10+
}
11+
12+
struct BAnd {
13+
exprs []BUnary
14+
}
15+
16+
type BUnary = BNot | BExpr | BFact | BDefine
17+
18+
struct BNot {
19+
expr BUnary
20+
}
21+
22+
type BFact = string
23+
type BDefine = string
Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
import v.build_constraint
2+
3+
const benv = build_constraint.new_environment(['linux', 'tinyc'], ['abc', 'def'])
4+
5+
fn test_eval_fact() {
6+
assert benv.is_fact('tinyc')
7+
assert benv.is_fact('linux')
8+
assert !benv.is_fact('macos')
9+
assert !benv.is_fact('windows')
10+
}
11+
12+
fn test_eval_define() {
13+
assert benv.is_define('abc')
14+
assert benv.is_define('def')
15+
assert !benv.is_define('xyz')
16+
}
17+
18+
fn test_eval_platforms_and_compilers() {
19+
assert benv.eval('tinyc')!
20+
assert benv.eval(' tinyc')!
21+
assert benv.eval('tinyc ')!
22+
assert benv.eval(' tinyc ')!
23+
assert !benv.eval('gcc')!
24+
assert !benv.eval('clang')!
25+
assert !benv.eval('msvc')!
26+
assert benv.eval('linux')!
27+
assert benv.eval(' linux')!
28+
assert benv.eval('linux ')!
29+
assert benv.eval(' linux ')!
30+
assert !benv.eval('windows')!
31+
assert !benv.eval('macos')!
32+
assert !benv.eval('freebsd')!
33+
}
34+
35+
fn test_eval_defines() {
36+
assert benv.eval('abc?')!
37+
assert benv.eval(' abc?')!
38+
assert benv.eval('abc? ')!
39+
assert benv.eval(' abc? ')!
40+
assert benv.eval('abc ?')!
41+
assert benv.eval(' abc ?')!
42+
assert benv.eval('abc ? ')!
43+
assert benv.eval(' abc ? ')!
44+
assert benv.eval('def?')!
45+
}
46+
47+
fn test_eval_not() {
48+
assert benv.eval('!gcc')!
49+
assert benv.eval('!clang')!
50+
assert benv.eval('!msvc')!
51+
assert !benv.eval('!tinyc')!
52+
assert !benv.eval(' !tinyc')!
53+
assert !benv.eval('!tinyc ')!
54+
assert !benv.eval(' !tinyc ')!
55+
assert benv.eval('!xyz?')!
56+
}
57+
58+
fn test_eval_and() {
59+
assert benv.eval('linux && tinyc')!
60+
assert !benv.eval('macos && tinyc')!
61+
assert !benv.eval('windows && tinyc')!
62+
assert !benv.eval('linux && gcc')!
63+
//
64+
assert benv.eval('linux && tinyc && abc?')!
65+
assert benv.eval('linux && tinyc && def?')!
66+
assert !benv.eval('linux && tinyc && xyz?')!
67+
//
68+
assert benv.eval('linux && !gcc')!
69+
assert benv.eval('linux && !clang')!
70+
assert benv.eval('!gcc && !windows')!
71+
assert !benv.eval('!windows && tcc')!
72+
assert !benv.eval('windows && gcc')!
73+
assert !benv.eval('gcc && !windows')!
74+
}
75+
76+
fn test_eval_or() {
77+
assert benv.eval('windows||tinyc')!
78+
assert benv.eval('windows || macos || tinyc')!
79+
assert benv.eval('windows || macos || tinyc')!
80+
assert benv.eval('windows || macos || gcc || abc?')!
81+
assert benv.eval('!windows||gcc')!
82+
}
83+
84+
fn test_complex() {
85+
assert benv.eval(' (windows || tinyc) && linux ')!
86+
assert !benv.eval(' (windows || gcc) && linux ')!
87+
assert benv.eval(' (windows || tinyc) && !macos ')!
88+
assert !benv.eval(' (windows || tinyc) && macos ')!
89+
}
90+
91+
fn test_precedence() {
92+
assert benv.eval(' tinyc && !windows ')! == benv.eval(' tinyc && (!windows)')!
93+
assert benv.eval(' tinyc && !windows ')! == benv.eval(' (!windows) && tinyc')!
94+
assert benv.eval(' !windows && tinyc')! == benv.eval(' (!windows) && tinyc')!
95+
assert benv.eval(' !windows || tinyc')! == benv.eval(' (!windows) || tinyc')!
96+
assert benv.eval(' !linux && tinyc')! == benv.eval(' (!linux) && tinyc')!
97+
assert benv.eval(' !linux || tinyc')! == benv.eval(' (!linux) || tinyc')!
98+
assert benv.eval(' !windows && gcc ')! == benv.eval(' (!windows) && gcc ')!
99+
assert benv.eval(' !windows || gcc ')! == benv.eval(' (!windows) || gcc ')!
100+
assert benv.eval(' !linux && gcc ')! == benv.eval(' (!linux) && gcc ')!
101+
assert benv.eval(' !linux || gcc ')! == benv.eval(' (!linux) || gcc ')!
102+
}
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
module build_constraint
2+
3+
// evaluating the AST nodes, in the given environment
4+
fn (b BExpr) eval(env &Environment) !bool {
5+
return b.expr.eval(env)
6+
}
7+
8+
fn (b BOr) eval(env &Environment) !bool {
9+
for e in b.exprs {
10+
if e.eval(env)! {
11+
return true
12+
}
13+
}
14+
return false
15+
}
16+
17+
fn (b BAnd) eval(env &Environment) !bool {
18+
for e in b.exprs {
19+
if !e.eval(env)! {
20+
return false
21+
}
22+
}
23+
return true
24+
}
25+
26+
fn (b BUnary) eval(env &Environment) !bool {
27+
match b {
28+
BNot, BExpr, BFact, BDefine { return b.eval(env)! }
29+
}
30+
return false
31+
}
32+
33+
fn (b BNot) eval(env &Environment) !bool {
34+
return !b.expr.eval(env)!
35+
}
36+
37+
fn (b BFact) eval(env &Environment) !bool {
38+
return env.is_fact(b)
39+
}
40+
41+
fn (b BDefine) eval(env &Environment) !bool {
42+
return env.is_define(b)
43+
}

0 commit comments

Comments
 (0)