v/vlib/v/tests/closure_generator_test.v

175 lines
4.2 KiB
V

import strings
import os
const (
max_params = 16
all_param_names = []string{len: max_params, init: '${`a` + it}'}
all_param_values = []string{len: max_params, init: '${it + 1}'}
)
struct ReturnType {
name string
init string
assertion string
no_assert_kw bool
}
const return_types = [
ReturnType{
name: ''
init: ''
assertion: ''
no_assert_kw: true
},
ReturnType{
name: 'int'
init: '-123'
assertion: ' == -123'
},
ReturnType{
name: 'u64'
init: '123'
assertion: ' == 123'
},
ReturnType{
name: 'voidptr'
init: 'voidptr(123)'
assertion: ' == voidptr(123)'
},
ReturnType{
name: 'string'
init: "'hello'"
assertion: " == 'hello'"
},
ReturnType{
name: '?'
init: "error('an error')"
assertion: " or { assert err.msg() == 'an error' return }\npanic('got no error')"
no_assert_kw: true
},
ReturnType{
name: '?string'
init: "'hello'"
assertion: "? == 'hello'"
},
ReturnType{
name: 'BigStruct'
init: 'BigStruct{ a3: 56, a27: 1234, a61: 5555 }'
assertion: ' == BigStruct{ a3: 56, a27: 1234, a61: 5555 }'
},
]
// test_closures_with_n_args generates a new V file containing closures of `i`
// and parameters of type `typ`, to makes sure that all combinations work correctly
fn test_closures_with_n_args() ? {
mut v_code := strings.new_builder(1024)
// NB: the type or value of the captured arg doesn't matter for this test,
// as the entire closure context is always passed as one pointer anyways
v_code.write_string('struct BigStruct {')
for i in 0 .. 64 {
v_code.write_string('\ta$i int ')
}
v_code.writeln('}')
for typ in ['byte', 'u16', 'int', 'i64', 'voidptr', 'string'] {
for i in 0 .. max_params {
param_names := all_param_names[..i]
params := param_names.map('$it $typ')
mut values := all_param_values[..i]
if typ == 'string' {
values = values.map("'$it'")
}
values = values.map('${typ}($it)')
mut expected_val := if typ == 'string' {
s := all_param_values[..i].join('')
"'127' + '$s'"
} else {
'127 + ${i * (i + 1) / 2}'
}
init_val, return_type := if typ != 'string' {
'u64(127)', 'u64'
} else {
"'127'", 'string'
}
// NB: the captured arg doesn't matter for this test, as closures always receive
// a pointer to the entire closure context as their last argument anyways
v_code.writeln("
fn test_big_closure_${typ}_${i}() {
println('test_big_closure_${typ}_$i')
mut local := 123
mut local_2 := 234
mut z := $init_val
c := fn [z] (${params.join(', ')}) $return_type {
mut sum := z")
for j in 0 .. i {
v_code.writeln('\t\tsum += ${return_type}(${param_names[j]})')
}
v_code.writeln("
return sum
}
assert c(${values.join(', ')}) == $expected_val
// ensure stack wasn't overwritten:
assert local == 123
assert local_2 == 234
}")
}
}
for return_type in return_types {
typ := return_type.name
styp := typ.replace('?', 'option_').to_lower()
init := return_type.init
assertion := return_type.assertion
for i in 0 .. 10 {
param_names := all_param_names[..i]
params := param_names.map('$it int')
values := all_param_values[..i]
assert_line := if !return_type.no_assert_kw {
'assert c(${values.join(', ')}) $assertion'
} else {
'c(${values.join(', ')}) $assertion'
}
// NB: the captured arg doesn't matter for this test, as closures always receive
// a pointer to the entire closure context as their last argument anyways
v_code.writeln("
fn test_closure_return_${styp}_${i}() ? {
println('test_closure_return_${styp}_$i')
mut local := 123
mut local_2 := 234
mut z := 1234
c := fn [z] (${params.join(', ')}) $typ {
return $init
}
$assert_line
// ensure stack wasn't overwritten:
assert local == 123
assert local_2 == 234
}")
}
}
code := v_code.str()
println('Compiling V code (${code.count('\n')} lines) ...')
wrkdir := os.join_path(os.temp_dir(), 'vtests', 'closures')
os.mkdir_all(wrkdir) ?
os.chdir(wrkdir) ?
os.write_file('closure_return_test.v', code) ?
vexe := os.getenv('VEXE')
res := os.execute('${os.quoted_path(vexe)} -keepc -cg -showcc closure_return_test.v')
if res.exit_code != 0 {
eprintln(res.output)
assert false
}
println('Process exited with code $res.exit_code')
os.chdir(os.dir(vexe)) or {}
os.rmdir_all(wrkdir) or {}
}