v2 / vlib / v / tests / fns / closure_generator_test.v
208 lines · 187 sloc · 5.38 KB · 32be45d08d274b9af0a0dabb5879ae37eed82be0
Raw
1import strings
2import os
3
4const max_params = get_max_params()
5const max_string_params = get_max_string_params()
6const all_param_names = []string{len: max_params, init: '${`a` + index}'}
7const all_param_values = []string{len: max_params, init: '${index + 1}'}
8
9fn get_max_params() int {
10 return 16
11}
12
13fn get_max_string_params() int {
14 return 16
15}
16
17struct ReturnType {
18 name string
19 init string
20 assertion string
21 no_assert_kw bool
22}
23
24const return_types = [
25 ReturnType{
26 name: ''
27 init: ''
28 assertion: ''
29 no_assert_kw: true
30 },
31 ReturnType{
32 name: 'int'
33 init: '-123'
34 assertion: ' == -123'
35 },
36 ReturnType{
37 name: 'u64'
38 init: '123'
39 assertion: ' == 123'
40 },
41 ReturnType{
42 name: 'voidptr'
43 init: 'voidptr(123)'
44 assertion: ' == voidptr(123)'
45 },
46 ReturnType{
47 name: 'string'
48 init: "'hello'"
49 assertion: " == 'hello'"
50 },
51 ReturnType{
52 name: '!'
53 init: "error('an error')"
54 assertion: " or { assert err.msg() == 'an error' return }\npanic('got no error')"
55 no_assert_kw: true
56 },
57 ReturnType{
58 name: '?string'
59 init: "'hello'"
60 assertion: "? == 'hello'"
61 },
62 ReturnType{
63 name: 'BigStruct'
64 init: 'BigStruct{ a3: 56, a27: 1234, a61: 5555 }'
65 assertion: ' == BigStruct{ a3: 56, a27: 1234, a61: 5555 }'
66 },
67]
68
69// test_closures_with_n_args generates a new V file containing closures of `i`
70// and parameters of type `typ`, to makes sure that all combinations work correctly
71fn test_closures_with_n_args() {
72 mut v_code := strings.new_builder(50_000)
73 // Note: the type or value of the captured arg doesn't matter for this test,
74 // as the entire closure context is always passed as one pointer anyways
75
76 v_code.write_string('struct BigStruct {')
77 for i in 0 .. 64 {
78 v_code.write_string('\ta${i} int ')
79 }
80 v_code.writeln('}')
81
82 for typ in ['u8', 'u16', 'int', 'i64', 'voidptr'] {
83 for i in 0 .. max_params {
84 param_names := all_param_names[..i]
85 params := param_names.map('${it} ${typ}')
86
87 mut values := all_param_values[..i].clone()
88 values = values.map('${typ}(${it})')
89
90 expected_val := '127 + ${i * (i + 1) / 2}'
91
92 init_val := 'u64(127)'
93 return_type := 'u64'
94
95 // Note: the captured arg doesn't matter for this test, as closures always receive
96 // a pointer to the entire closure context as their last argument anyways
97 v_code.writeln("
98fn test_big_closure_${typ}_${i}() {
99 println('test_big_closure_${typ}_${i}')
100 mut local := 123
101 mut local_2 := 234
102 mut z := ${init_val}
103 c := fn [z] (${params.join(', ')}) ${return_type} {
104 mut sum := z")
105 for j in 0 .. i {
106 v_code.writeln('\t\tsum += ${return_type}(${param_names[j]})')
107 }
108 v_code.writeln("
109 return sum
110 }
111 assert c(${values.join(', ')}) == ${expected_val}
112 // ensure stack wasn't overwritten:
113 assert local == 123
114 assert local_2 == 234
115}")
116 }
117 }
118
119 // handle string type separately
120 for i in 0 .. max_string_params {
121 param_names := all_param_names[..i]
122 params := param_names.map('${it} string')
123
124 mut values := all_param_values[..i].clone()
125 values = values.map("'${it}'")
126
127 s := all_param_values[..i].join('')
128 expected_val := "'127' + '${s}'"
129
130 init_val := "'127'"
131 return_type := 'string'
132
133 // Note: the captured arg doesn't matter for this test, as closures always receive
134 // a pointer to the entire closure context as their last argument anyways
135 v_code.writeln("
136fn test_big_closure_string_${i}() {
137 println('test_big_closure_string_${i}')
138 mut local := 123
139 mut local_2 := 234
140 mut z := ${init_val}
141 c := fn [z] (${params.join(', ')}) ${return_type} {
142 mut sum := z")
143 for j in 0 .. i {
144 v_code.writeln('\t\tsum += ${param_names[j]}')
145 }
146 v_code.writeln("
147 return sum
148 }
149 assert c(${values.join(', ')}) == ${expected_val}
150 // ensure stack wasn't overwritten:
151 assert local == 123
152 assert local_2 == 234
153}")
154 }
155
156 for return_type in return_types {
157 typ := return_type.name
158 styp := typ.replace_each(['?', 'option_', '!', 'result_']).to_lower_ascii()
159 init := return_type.init
160 assertion := return_type.assertion
161
162 for i in 0 .. max_params {
163 param_names := all_param_names[..i]
164 params := param_names.map('${it} int')
165 values := all_param_values[..i]
166
167 assert_line := if !return_type.no_assert_kw {
168 'assert c(${values.join(', ')}) ${assertion}'
169 } else {
170 'c(${values.join(', ')}) ${assertion}'
171 }
172 // Note: the captured arg doesn't matter for this test, as closures always receive
173 // a pointer to the entire closure context as their last argument anyways
174 v_code.writeln("
175fn test_closure_return_${styp}_${i}() ! {
176 println('test_closure_return_${styp}_${i}')
177 mut local := 123
178 mut local_2 := 234
179 mut z := 1234
180 c := fn [z] (${params.join(', ')}) ${typ} {
181 return ${init}
182 }
183 ${assert_line}
184 // ensure stack wasn't overwritten:
185 assert local == 123
186 assert local_2 == 234
187}")
188 }
189 }
190
191 code := v_code.str()
192 println('Compiling V code (${code.count('\n')} lines) ...')
193 wrkdir := os.join_path(os.vtmp_dir(), 'closure_generator_tests')
194 os.mkdir_all(wrkdir)!
195 os.chdir(wrkdir)!
196 full_path_to_target := os.join_path(wrkdir, 'closure_return_test.v')
197 os.write_file(full_path_to_target, code)!
198 vexe := os.getenv('VEXE')
199 cmd := '${os.quoted_path(vexe)} -keepc -cg -showcc ${full_path_to_target}'
200 res := os.execute(cmd)
201 if res.exit_code != 0 {
202 eprintln(res.output)
203 eprintln('> failed exit code: ${res.exit_code} | cmd:\n${cmd}')
204 assert false
205 }
206 os.chdir(os.dir(vexe)) or {}
207 os.rmdir_all(wrkdir) or {}
208}
209