From 68ada041e650705eb39f32c806e965aef6c317fb Mon Sep 17 00:00:00 2001 From: Delyan Angelov Date: Mon, 20 Dec 2021 17:22:02 +0200 Subject: [PATCH] tests: support `VTEST_ONLY_FN=*test_sincos* ./v test .` and `./v test -run-only test_sin .` --- cmd/tools/modules/testing/common.v | 4 ++ cmd/tools/vtest.v | 93 +++++++++++++++++++++++++----- vlib/v/gen/c/cmain.v | 28 ++++++++- vlib/v/pref/pref.v | 7 +++ 4 files changed, 117 insertions(+), 15 deletions(-) diff --git a/cmd/tools/modules/testing/common.v b/cmd/tools/modules/testing/common.v index 0b7d169836..a08bcc30c6 100644 --- a/cmd/tools/modules/testing/common.v +++ b/cmd/tools/modules/testing/common.v @@ -19,6 +19,10 @@ pub const hide_oks = os.getenv('VTEST_HIDE_OK') == '1' pub const fail_fast = os.getenv('VTEST_FAIL_FAST') == '1' +pub const test_only = os.getenv('VTEST_ONLY').split_any(',') + +pub const test_only_fn = os.getenv('VTEST_ONLY_FN').split_any(',') + pub const is_node_present = os.execute('node --version').exit_code == 0 pub const all_processes = os.execute('ps ax').output.split_any('\r\n') diff --git a/cmd/tools/vtest.v b/cmd/tools/vtest.v index 2c7b963a25..8245f14167 100644 --- a/cmd/tools/vtest.v +++ b/cmd/tools/vtest.v @@ -5,6 +5,13 @@ import os.cmdline import testing import v.pref +struct Context { +mut: + verbose bool + fail_fast bool + run_only []string +} + fn main() { args := os.args.clone() if os.args.last() == 'test' { @@ -14,7 +21,11 @@ fn main() { args_to_executable := args[1..] mut args_before := cmdline.options_before(args_to_executable, ['test']) mut args_after := cmdline.options_after(args_to_executable, ['test']) - fail_fast := extract_flag('-fail-fast', mut args_after, testing.fail_fast) + mut ctx := Context{} + ctx.fail_fast = extract_flag_bool('-fail-fast', mut args_after, testing.fail_fast) + ctx.verbose = extract_flag_bool('-v', mut args_after, false) + ctx.run_only = extract_flag_string_array('-run-only', mut args_after, testing.test_only_fn) + os.setenv('VTEST_ONLY_FN', ctx.run_only.join(','), true) if args_after == ['v'] { eprintln('`v test v` has been deprecated.') eprintln('Use `v test-all` instead.') @@ -24,21 +35,25 @@ fn main() { backend := if backend_pos == -1 { '.c' } else { args_before[backend_pos + 1] } // this giant mess because closures are not implemented mut ts := testing.new_test_session(args_before.join(' '), true) - ts.fail_fast = fail_fast + ts.fail_fast = ctx.fail_fast for targ in args_after { if os.is_dir(targ) { // Fetch all tests from the directory - files, skip_files := should_test_dir(targ.trim_right(os.path_separator), backend) + files, skip_files := ctx.should_test_dir(targ.trim_right(os.path_separator), + backend) ts.files << files ts.skip_files << skip_files continue } else if os.exists(targ) { - match should_test(targ, backend) { + match ctx.should_test(targ, backend) { .test { ts.files << targ continue } .skip { + if ctx.run_only.len > 0 { + continue + } ts.files << targ ts.skip_files << targ continue @@ -71,7 +86,7 @@ fn show_usage() { println('') } -pub fn should_test_dir(path string, backend string) ([]string, []string) { // return is (files, skip_files) +pub fn (mut ctx Context) should_test_dir(path string, backend string) ([]string, []string) { // return is (files, skip_files) mut files := os.ls(path) or { return []string{}, []string{} } mut local_path_separator := os.path_separator if path.ends_with(os.path_separator) { @@ -85,15 +100,18 @@ pub fn should_test_dir(path string, backend string) ([]string, []string) { // re if file == 'testdata' { continue } - ret_files, ret_skip_files := should_test_dir(p, backend) + ret_files, ret_skip_files := ctx.should_test_dir(p, backend) res_files << ret_files skip_files << ret_skip_files } else if os.exists(p) { - match should_test(p, backend) { + match ctx.should_test(p, backend) { .test { res_files << p } .skip { + if ctx.run_only.len > 0 { + continue + } res_files << p skip_files << p } @@ -110,7 +128,7 @@ enum ShouldTestStatus { ignore // just ignore the file, so it will not be printed at all in the list of tests } -fn should_test(path string, backend string) ShouldTestStatus { +fn (mut ctx Context) should_test(path string, backend string) ShouldTestStatus { if path.ends_with('mysql_orm_test.v') { testing.find_started_process('mysqld') or { return .skip } } @@ -126,11 +144,11 @@ fn should_test(path string, backend string) ShouldTestStatus { } } if path.ends_with('_test.v') { - return .test + return ctx.should_test_when_it_contains_matching_fns(path, backend) } if path.ends_with('_test.js.v') { if testing.is_node_present { - return .test + return ctx.should_test_when_it_contains_matching_fns(path, backend) } return .skip } @@ -141,13 +159,21 @@ fn should_test(path string, backend string) ShouldTestStatus { backend_arg := path.all_before_last('.v').all_after_last('.') arch := pref.arch_from_string(backend_arg) or { pref.Arch._auto } if arch == pref.get_host_arch() { - return .test + return ctx.should_test_when_it_contains_matching_fns(path, backend) } else if arch == ._auto { if backend_arg == 'c' { // .c.v - return if backend == 'c' { ShouldTestStatus.test } else { ShouldTestStatus.skip } + return if backend == 'c' { + ctx.should_test_when_it_contains_matching_fns(path, backend) + } else { + ShouldTestStatus.skip + } } if backend_arg == 'js' { - return if backend == 'js' { ShouldTestStatus.test } else { ShouldTestStatus.skip } + return if backend == 'js' { + ctx.should_test_when_it_contains_matching_fns(path, backend) + } else { + ShouldTestStatus.skip + } } } else { return .skip @@ -156,7 +182,33 @@ fn should_test(path string, backend string) ShouldTestStatus { return .ignore } -fn extract_flag(flag_name string, mut after []string, flag_default bool) bool { +fn (mut ctx Context) should_test_when_it_contains_matching_fns(path string, backend string) ShouldTestStatus { + if ctx.run_only.len == 0 { + // no filters set, so just compile and test + return .test + } + lines := os.read_lines(path) or { return .ignore } + for line in lines { + if line.match_glob('fn test_*') || line.match_glob('pub fn test_*') { + tname := line.replace_each(['pub fn ', '', 'fn ', '']).all_before('(') + for pattern in ctx.run_only { + mut pat := pattern.clone() + if pat.contains('.') { + pat = pat.all_after_last('.') + } + if tname.match_glob(pat) { + if ctx.verbose { + println('> compiling path: $path, since test fn `$tname` matches glob pattern `$pat`') + } + return .test + } + } + } + } + return .ignore +} + +fn extract_flag_bool(flag_name string, mut after []string, flag_default bool) bool { mut res := flag_default orig_after := after.clone() // workaround for after.filter() codegen bug, when `mut after []string` matches_after := orig_after.filter(it != flag_name) @@ -166,3 +218,16 @@ fn extract_flag(flag_name string, mut after []string, flag_default bool) bool { } return res } + +fn extract_flag_string_array(flag_name string, mut after []string, flag_default []string) []string { + mut res := flag_default.clone() + mut found := after.index(flag_name) + if found > -1 { + if found + 1 < after.len { + res = after[found + 1].split_any(',') + after.delete(found) + } + after.delete(found) + } + return res +} diff --git a/vlib/v/gen/c/cmain.v b/vlib/v/gen/c/cmain.v index 1df1bee2ef..04bf404aaf 100644 --- a/vlib/v/gen/c/cmain.v +++ b/vlib/v/gen/c/cmain.v @@ -190,7 +190,8 @@ pub fn (mut g Gen) gen_c_main_for_tests() { g.writeln('\tmain__vtest_init();') g.writeln('\t_vinit(___argc, (voidptr)___argv);') // - all_tfuncs := g.get_all_test_function_names() + mut all_tfuncs := g.get_all_test_function_names() + all_tfuncs = g.filter_only_matching_fn_names(all_tfuncs) g.writeln('\tstring v_test_file = ${ctoslit(g.pref.path)};') if g.pref.is_stats { g.writeln('\tmain__BenchedTests bt = main__start_testing($all_tfuncs.len, v_test_file);') @@ -248,3 +249,28 @@ pub fn (mut g Gen) gen_c_main_for_tests() { println(g.out.after(main_fn_start_pos)) } } + +pub fn (mut g Gen) filter_only_matching_fn_names(fnames []string) []string { + if g.pref.run_only.len == 0 { + return fnames + } + mut res := []string{} + for tname in fnames { + if tname.contains('testsuite_') { + res << tname + continue + } + mut is_matching := false + for fn_glob_pattern in g.pref.run_only { + if tname.match_glob(fn_glob_pattern) { + is_matching = true + break + } + } + if !is_matching { + continue + } + res << tname + } + return res +} diff --git a/vlib/v/pref/pref.v b/vlib/v/pref/pref.v index 9d3f3459b7..90dd13e69e 100644 --- a/vlib/v/pref/pref.v +++ b/vlib/v/pref/pref.v @@ -162,6 +162,8 @@ pub mut: out_name string path string // Path to file/folder to compile // -d vfmt and -d another=0 for `$if vfmt { will execute }` and `$if another ? { will NOT get here }` + run_only []string // VTEST_ONLY_FN and -run-only accept comma separated glob patterns. + // Only test_ functions that match these patterns will be run. -run-only is valid only for _test.v files. compile_defines []string // just ['vfmt'] compile_defines_all []string // contains both: ['vfmt','another'] run_args []string // `v run x.v 1 2 3` => `1 2 3` @@ -207,6 +209,7 @@ pub fn parse_args_and_show_errors(known_external_commands []string, args []strin $if x64 { res.m64 = true // follow V model by default } + res.run_only = os.getenv('VTEST_ONLY_FN').split_any(',') mut command := '' mut command_pos := 0 // for i, arg in args { @@ -466,6 +469,10 @@ pub fn parse_args_and_show_errors(known_external_commands []string, args []strin '-show-depgraph' { res.show_depgraph = true } + '-run-only' { + res.run_only = cmdline.option(current_args, arg, os.getenv('VTEST_ONLY_FN')).split_any(',') + i++ + } '-test-runner' { res.test_runner = cmdline.option(current_args, arg, res.test_runner) i++