tests: support `fn test_fn() ? { opt()? }`
parent
f4c03e8ed8
commit
0f042124cb
|
@ -317,6 +317,8 @@ pub:
|
|||
is_variadic bool
|
||||
is_anon bool
|
||||
is_manualfree bool // true, when [manualfree] is used on a fn
|
||||
is_main bool // true for `fn main()`
|
||||
is_test bool // true for `fn test_abcde`
|
||||
receiver Field
|
||||
receiver_pos token.Position // `(u User)` in `fn (u User) name()` position
|
||||
is_method bool
|
||||
|
|
|
@ -170,6 +170,7 @@ pub fn (mut c Checker) check_files(ast_files []ast.File) {
|
|||
the_main_file.stmts << ast.FnDecl{
|
||||
name: 'main.main'
|
||||
mod: 'main'
|
||||
is_main: true
|
||||
file: the_main_file.path
|
||||
return_type: table.void_type
|
||||
scope: &ast.Scope{
|
||||
|
@ -205,7 +206,7 @@ pub fn (mut c Checker) check_files(ast_files []ast.File) {
|
|||
if c.pref.is_test {
|
||||
mut n_test_fns := 0
|
||||
for _, f in c.table.fns {
|
||||
if f.name.contains('.test_') {
|
||||
if f.is_test {
|
||||
n_test_fns++
|
||||
}
|
||||
}
|
||||
|
@ -1213,8 +1214,7 @@ pub fn (mut c Checker) call_expr(mut call_expr ast.CallExpr) table.Type {
|
|||
c.expected_or_type = table.void_type
|
||||
if call_expr.or_block.kind == .propagate && !c.cur_fn.return_type.has_flag(.optional)
|
||||
&& !c.inside_const {
|
||||
cur_names := c.cur_fn.name.split('.')
|
||||
if cur_names[cur_names.len - 1] != 'main' {
|
||||
if !c.cur_fn.is_main {
|
||||
c.error('to propagate the optional call, `$c.cur_fn.name` must return an optional',
|
||||
call_expr.or_block.pos)
|
||||
}
|
||||
|
@ -5440,7 +5440,9 @@ pub fn (mut c Checker) enum_val(mut node ast.EnumVal) table.Type {
|
|||
// rintln('checker: x = $info.x enum val $c.expected_type $typ_sym.name')
|
||||
// println(info.vals)
|
||||
if node.val !in info.vals {
|
||||
c.error('enum `$typ_sym.name` does not have a value `$node.val`', node.pos)
|
||||
suggestion := util.new_suggestion(node.val, info.vals)
|
||||
c.error(suggestion.say('enum `$typ_sym.name` does not have a value `$node.val`'),
|
||||
node.pos)
|
||||
}
|
||||
node.typ = typ
|
||||
return typ
|
||||
|
@ -5916,8 +5918,7 @@ fn (mut c Checker) fn_decl(mut node ast.FnDecl) {
|
|||
}
|
||||
}
|
||||
// TODO c.pref.is_vet
|
||||
if node.language == .v && !node.is_method && node.params.len == 0
|
||||
&& node.name.after('.').starts_with('test_') {
|
||||
if node.language == .v && !node.is_method && node.params.len == 0 && node.is_test {
|
||||
if !c.pref.is_test {
|
||||
// simple heuristic
|
||||
for st in node.stmts {
|
||||
|
@ -5928,9 +5929,10 @@ fn (mut c Checker) fn_decl(mut node ast.FnDecl) {
|
|||
}
|
||||
}
|
||||
}
|
||||
// eprintln('> node.name: $node.name | node.return_type: $node.return_type')
|
||||
if node.return_type != table.void_type_idx {
|
||||
c.error('test functions should not return anything', node.pos)
|
||||
if node.return_type != table.void_type_idx
|
||||
&& node.return_type.clear_flag(.optional) != table.void_type_idx {
|
||||
c.error('test functions should either return nothing at all, or be marked to return `?`',
|
||||
node.pos)
|
||||
}
|
||||
}
|
||||
c.expected_type = table.void_type
|
||||
|
|
|
@ -1,13 +1,7 @@
|
|||
vlib/v/checker/tests/test_functions_should_not_return_test.vv:9:1: error: test functions should not return anything
|
||||
vlib/v/checker/tests/test_functions_should_not_return_test.vv:9:1: error: test functions should either return nothing at all, or be marked to return `?`
|
||||
7 |
|
||||
8 | // should be disallowed:
|
||||
9 | fn test_returning_int() int {
|
||||
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
10 |
|
||||
11 | }
|
||||
vlib/v/checker/tests/test_functions_should_not_return_test.vv:14:1: error: test functions should not return anything
|
||||
12 |
|
||||
13 | // should be disallowed:
|
||||
14 | fn test_returning_opt() ? {
|
||||
| ~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
15 | }
|
||||
|
|
|
@ -10,6 +10,8 @@ fn test_returning_int() int {
|
|||
|
||||
}
|
||||
|
||||
// should be disallowed:
|
||||
// NB: this is allowed explicitly now, to allow for shorter tests
|
||||
// of functions returning optionals.
|
||||
fn test_returning_opt() ? {
|
||||
assert true
|
||||
}
|
||||
|
|
|
@ -5252,7 +5252,7 @@ fn (mut g Gen) or_block(var_name string, or_block ast.OrExpr, return_type table.
|
|||
g.stmts(stmts)
|
||||
}
|
||||
} else if or_block.kind == .propagate {
|
||||
if g.file.mod.name == 'main' && (isnil(g.fn_decl) || g.fn_decl.name == 'main.main') {
|
||||
if g.file.mod.name == 'main' && (isnil(g.fn_decl) || g.fn_decl.is_main) {
|
||||
// In main(), an `opt()?` call is sugar for `opt() or { panic(err) }`
|
||||
if g.pref.is_debug {
|
||||
paline, pafile, pamod, pafn := g.panic_debug_info(or_block.pos)
|
||||
|
@ -5260,6 +5260,8 @@ fn (mut g Gen) or_block(var_name string, or_block ast.OrExpr, return_type table.
|
|||
} else {
|
||||
g.writeln('\tv_panic(_STR("optional not set (%.*s\\000)", 2, ${cvar_name}.err.msg));')
|
||||
}
|
||||
} else if !isnil(g.fn_decl) && g.fn_decl.is_test {
|
||||
g.gen_failing_error_propagation_for_test_fn(or_block, cvar_name)
|
||||
} else {
|
||||
// In ordinary functions, `opt()?` call is sugar for:
|
||||
// `opt() or { return err }`
|
||||
|
@ -5455,11 +5457,7 @@ fn (g &Gen) get_all_test_function_names() []string {
|
|||
if tsuite_end.len > 0 {
|
||||
all_tfuncs << tsuite_end
|
||||
}
|
||||
mut all_tfuncs_c := []string{}
|
||||
for f in all_tfuncs {
|
||||
all_tfuncs_c << util.no_dots(f)
|
||||
}
|
||||
return all_tfuncs_c
|
||||
return all_tfuncs
|
||||
}
|
||||
|
||||
fn (g &Gen) is_importing_os() bool {
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
module c
|
||||
|
||||
import v.util
|
||||
import v.ast
|
||||
|
||||
pub fn (mut g Gen) gen_c_main() {
|
||||
if !g.has_main {
|
||||
|
@ -135,6 +136,16 @@ pub fn (mut g Gen) write_tests_definitions() {
|
|||
g.definitions.writeln('jmp_buf g_jump_buffer;')
|
||||
}
|
||||
|
||||
pub fn (mut g Gen) gen_failing_error_propagation_for_test_fn(or_block ast.OrExpr, cvar_name string) {
|
||||
// in test_() functions, an `opt()?` call is sugar for
|
||||
// `or { cb_propagate_test_error(@LINE, @FILE, @MOD, @FN, err.msg) }`
|
||||
// and the test is considered failed
|
||||
paline, pafile, pamod, pafn := g.panic_debug_info(or_block.pos)
|
||||
g.writeln('\tmain__cb_propagate_test_error($paline, tos3("$pafile"), tos3("$pamod"), tos3("$pafn"), ${cvar_name}.err.msg );')
|
||||
g.writeln('\tg_test_fails++;')
|
||||
g.writeln('\tlongjmp(g_jump_buffer, 1);')
|
||||
}
|
||||
|
||||
pub fn (mut g Gen) gen_c_main_for_tests() {
|
||||
main_fn_start_pos := g.out.len
|
||||
g.writeln('')
|
||||
|
@ -145,11 +156,12 @@ pub fn (mut g Gen) gen_c_main_for_tests() {
|
|||
g.writeln('\tmain__BenchedTests bt = main__start_testing($all_tfuncs.len, _SLIT("$g.pref.path"));')
|
||||
}
|
||||
g.writeln('')
|
||||
for t in all_tfuncs {
|
||||
for tname in all_tfuncs {
|
||||
tcname := util.no_dots(tname)
|
||||
if g.pref.is_stats {
|
||||
g.writeln('\tmain__BenchedTests_testing_step_start(&bt, _SLIT("$t"));')
|
||||
g.writeln('\tmain__BenchedTests_testing_step_start(&bt, _SLIT("$tcname"));')
|
||||
}
|
||||
g.writeln('\tif (!setjmp(g_jump_buffer)) ${t}();')
|
||||
g.writeln('\tif (!setjmp(g_jump_buffer)) ${tcname}();')
|
||||
if g.pref.is_stats {
|
||||
g.writeln('\tmain__BenchedTests_testing_step_end(&bt);')
|
||||
}
|
||||
|
|
|
@ -296,6 +296,9 @@ fn (mut p Parser) fn_decl() ast.FnDecl {
|
|||
mut type_sym_method_idx := 0
|
||||
no_body := p.tok.kind != .lcbr
|
||||
end_pos := p.prev_tok.position()
|
||||
short_fn_name := name
|
||||
is_main := short_fn_name == 'main' && p.mod == 'main'
|
||||
is_test := short_fn_name.starts_with('test_') || short_fn_name.starts_with('testsuite_')
|
||||
// Register
|
||||
if is_method {
|
||||
mut type_sym := p.table.get_type_symbol(rec.typ)
|
||||
|
@ -326,6 +329,8 @@ fn (mut p Parser) fn_decl() ast.FnDecl {
|
|||
is_pub: is_pub
|
||||
is_deprecated: is_deprecated
|
||||
is_unsafe: is_unsafe
|
||||
is_main: is_main
|
||||
is_test: is_test
|
||||
no_body: no_body
|
||||
mod: p.mod
|
||||
attrs: p.attrs
|
||||
|
@ -351,6 +356,8 @@ fn (mut p Parser) fn_decl() ast.FnDecl {
|
|||
is_pub: is_pub
|
||||
is_deprecated: is_deprecated
|
||||
is_unsafe: is_unsafe
|
||||
is_main: is_main
|
||||
is_test: is_test
|
||||
no_body: no_body
|
||||
mod: p.mod
|
||||
attrs: p.attrs
|
||||
|
@ -388,6 +395,8 @@ fn (mut p Parser) fn_decl() ast.FnDecl {
|
|||
is_direct_arr: is_direct_arr
|
||||
is_pub: is_pub
|
||||
is_variadic: is_variadic
|
||||
is_main: is_main
|
||||
is_test: is_test
|
||||
receiver: ast.Field{
|
||||
name: rec.name
|
||||
typ: rec.typ
|
||||
|
|
|
@ -556,6 +556,7 @@ pub fn (mut p Parser) top_stmt() ast.Stmt {
|
|||
return ast.FnDecl{
|
||||
name: 'main.main'
|
||||
mod: 'main'
|
||||
is_main: true
|
||||
stmts: stmts
|
||||
file: p.file_name
|
||||
return_type: table.void_type
|
||||
|
|
|
@ -350,7 +350,7 @@ pub fn parse_args(known_external_commands []string, args []string) (&Preferences
|
|||
res.build_options << '$arg $target_os'
|
||||
}
|
||||
'-printfn' {
|
||||
res.printfn_list << cmdline.option(current_args, '-printfn', '')
|
||||
res.printfn_list << cmdline.option(current_args, '-printfn', '').split(',')
|
||||
i++
|
||||
}
|
||||
'-cflags' {
|
||||
|
|
|
@ -86,3 +86,21 @@ fn cb_assertion_ok(i &VAssertMetaInfo) {
|
|||
println('$final_funcname ($final_filepath)')
|
||||
*/
|
||||
}
|
||||
|
||||
fn cb_propagate_test_error(line_nr int, file string, mod string, fn_name string, errmsg string) {
|
||||
filepath := if use_relative_paths { file } else { os.real_path(file) }
|
||||
mut final_filepath := filepath + ':$line_nr:'
|
||||
if use_color {
|
||||
final_filepath = term.gray(final_filepath)
|
||||
}
|
||||
mut final_funcname := 'fn ' + fn_name.replace('main.', '').replace('__', '.')
|
||||
if use_color {
|
||||
final_funcname = term.red('✗ ' + final_funcname)
|
||||
}
|
||||
final_msg := if use_color { term.dim(errmsg) } else { errmsg }
|
||||
eprintln('$final_filepath $final_funcname failed propagation with error: $final_msg')
|
||||
if os.is_file(file) {
|
||||
source_lines := os.read_lines(file) or { []string{len: line_nr + 1} }
|
||||
eprintln('${line_nr:5} | ${source_lines[line_nr - 1]}')
|
||||
}
|
||||
}
|
||||
|
|
|
@ -34,6 +34,8 @@ pub:
|
|||
is_deprecated bool
|
||||
is_unsafe bool
|
||||
is_placeholder bool
|
||||
is_main bool
|
||||
is_test bool
|
||||
no_body bool
|
||||
mod string
|
||||
ctdefine string // compile time define. "myflag", when [if myflag] tag
|
||||
|
|
Loading…
Reference in New Issue