| 1 | import os |
| 2 | |
| 3 | fn testsuite_begin() { |
| 4 | os.setenv('VCOLORS', 'never', true) |
| 5 | // TODO: remove this, when after vc/v.c *also* uses `_cache`, instead of `cache`: |
| 6 | old_cache_path := os.join_path(os.vmodules_dir(), 'cache') |
| 7 | dump(old_cache_path) |
| 8 | if os.exists(old_cache_path) { |
| 9 | os.rmdir_all(old_cache_path)! |
| 10 | } |
| 11 | } |
| 12 | |
| 13 | fn vroot_path(relpath string) string { |
| 14 | return os.real_path(os.join_path(@VMODROOT, relpath)) |
| 15 | } |
| 16 | |
| 17 | fn vrun_ok_in_dir(workdir string, options string, path string) string { |
| 18 | old_wd := os.getwd() |
| 19 | os.chdir(workdir) or { panic(err) } |
| 20 | defer { |
| 21 | os.chdir(old_wd) or { panic(err) } |
| 22 | } |
| 23 | return vrun_ok(options, path) |
| 24 | } |
| 25 | |
| 26 | fn write_file(path string, content string) { |
| 27 | os.write_file(path, content) or { panic(err) } |
| 28 | } |
| 29 | |
| 30 | fn setup_module_resolution_workdir_fixture() string { |
| 31 | workspace := os.join_path(os.vtmp_dir(), |
| 32 | 'v_module_resolution_independent_of_workdir_${os.getpid()}') |
| 33 | interp := r'${' |
| 34 | os.rmdir_all(workspace) or {} |
| 35 | os.mkdir_all(os.join_path(workspace, 'app')) or { panic(err) } |
| 36 | os.mkdir_all(os.join_path(workspace, 'lib')) or { panic(err) } |
| 37 | write_file(os.join_path(workspace, '.v.mod.stop'), '') |
| 38 | write_file(os.join_path(workspace, 'app', 'v.mod'), |
| 39 | "Module {\n\tname: 'app'\n\tdescription: ''\n\tversion: ''\n\tlicense: ''\n\tdependencies: []\n}\n") |
| 40 | write_file(os.join_path(workspace, 'app', 'main.v'), |
| 41 | "module main\n\nimport lib\n\nfn main() {\n\tprintln('Hello ${interp}lib.square(4)}!')\n}\n") |
| 42 | write_file(os.join_path(workspace, 'lib', 'v.mod'), |
| 43 | "Module {\n\tname: 'lib'\n\tdescription: ''\n\tversion: ''\n\tlicense: ''\n\tdependencies: []\n}\n") |
| 44 | write_file(os.join_path(workspace, 'lib', 'lib.v'), |
| 45 | 'module lib\n\npub fn square(x int) int {\n\treturn x * x\n}\n') |
| 46 | return workspace |
| 47 | } |
| 48 | |
| 49 | fn vrun_ok(options string, path string) string { |
| 50 | cmd := '${os.quoted_path(@VEXE)} ${options} ${os.quoted_path(path)}' |
| 51 | res := os.execute(cmd) |
| 52 | if res.exit_code != 0 { |
| 53 | eprintln('> failing vrun cmd: ${cmd}') |
| 54 | eprintln('> output:\n${res.output}') |
| 55 | assert res.exit_code == 0 |
| 56 | } |
| 57 | return res.output |
| 58 | } |
| 59 | |
| 60 | fn test_projects_should_run() { |
| 61 | $if windows { |
| 62 | return |
| 63 | } |
| 64 | res := vrun_ok('run', vroot_path('vlib/v/tests/testdata/enum_in_builtin') + os.path_separator) |
| 65 | assert res.trim_space() == 'v0' |
| 66 | } |
| 67 | |
| 68 | fn test_running_subdir_project_with_parent_vmod_works() { |
| 69 | root := os.join_path(os.vtmp_dir(), 'v_subdir_project_with_parent_vmod') |
| 70 | os.rmdir_all(root) or {} |
| 71 | defer { |
| 72 | os.rmdir_all(root) or {} |
| 73 | } |
| 74 | os.mkdir_all(os.join_path(root, 'hexagonal', 'application'))! |
| 75 | os.write_file(os.join_path(root, 'v.mod'), 'Module {\nname: "vanilla3"\n}')! |
| 76 | os.write_file(os.join_path(root, 'hexagonal', 'application', 'auth_usecase.v'), |
| 77 | 'module application\n\npub struct AuthUseCase {}\n')! |
| 78 | os.write_file(os.join_path(root, 'hexagonal', 'main.v'), |
| 79 | "module main\n\nimport application\n\nfn main() {\n\t_ := application.AuthUseCase{}\n\tprintln('built')\n}\n")! |
| 80 | old_dir := os.getwd() |
| 81 | defer { |
| 82 | os.chdir(old_dir) or {} |
| 83 | } |
| 84 | os.chdir(root)! |
| 85 | res := os.execute('${os.quoted_path(@VEXE)} run hexagonal') |
| 86 | assert res.exit_code == 0, res.output |
| 87 | assert res.output.trim_space() == 'built' |
| 88 | } |
| 89 | |
| 90 | fn test_running_module_with_same_module_subdirs_setting_works() { |
| 91 | root := os.join_path(os.vtmp_dir(), 'v_same_module_subdirs_${os.getpid()}') |
| 92 | os.rmdir_all(root) or {} |
| 93 | defer { |
| 94 | os.rmdir_all(root) or {} |
| 95 | } |
| 96 | os.mkdir_all(os.join_path(root, 'app', 'foo', 'internal', 'nested'))! |
| 97 | os.write_file(os.join_path(root, 'app', 'main.v'), |
| 98 | 'module main\n\nimport foo\n\nfn main() {\n\tprintln(foo.answer())\n}\n')! |
| 99 | os.write_file(os.join_path(root, 'app', 'foo', 'v.mod'), |
| 100 | "Module {\n\tname: 'foo'\n\tsubdirs: ['internal']\n}\n")! |
| 101 | os.write_file(os.join_path(root, 'app', 'foo', 'foo.v'), |
| 102 | 'module foo\n\npub fn answer() int {\n\treturn secret()\n}\n')! |
| 103 | os.write_file(os.join_path(root, 'app', 'foo', 'internal', 'nested', 'secret.v'), |
| 104 | 'module foo\n\nfn secret() int {\n\treturn 42\n}\n')! |
| 105 | old_dir := os.getwd() |
| 106 | defer { |
| 107 | os.chdir(old_dir) or {} |
| 108 | } |
| 109 | os.chdir(root)! |
| 110 | res := os.execute('${os.quoted_path(@VEXE)} run app/main.v') |
| 111 | assert res.exit_code == 0, res.output |
| 112 | assert res.output.trim_space() == '42' |
| 113 | } |
| 114 | |
| 115 | fn test_running_project_with_same_module_subdirs_can_find_templates_next_to_vmod() { |
| 116 | root := os.join_path(os.vtmp_dir(), 'v_same_module_subdirs_veb_templates_${os.getpid()}') |
| 117 | os.rmdir_all(root) or {} |
| 118 | defer { |
| 119 | os.rmdir_all(root) or {} |
| 120 | } |
| 121 | os.mkdir_all(os.join_path(root, 'user'))! |
| 122 | os.mkdir_all(os.join_path(root, 'ci'))! |
| 123 | os.mkdir_all(os.join_path(root, 'templates'))! |
| 124 | os.write_file(os.join_path(root, 'v.mod'), |
| 125 | "Module {\n\tname: 'vebsubdirs'\n\tsubdirs: ['user', 'ci']\n}\n")! |
| 126 | os.write_file(os.join_path(root, 'main.v'), 'import veb |
| 127 | |
| 128 | pub struct App {} |
| 129 | |
| 130 | pub struct Context { |
| 131 | veb.Context |
| 132 | } |
| 133 | |
| 134 | fn main() {} |
| 135 | ')! |
| 136 | os.write_file(os.join_path(root, 'user', 'register.v'), 'module main |
| 137 | |
| 138 | import veb |
| 139 | |
| 140 | @["/register"] |
| 141 | pub fn (mut app App) register(mut ctx Context) veb.Result { |
| 142 | return $veb.html() |
| 143 | } |
| 144 | |
| 145 | @["/settings"] |
| 146 | pub fn (mut app App) settings(mut ctx Context) veb.Result { |
| 147 | return $veb.html("templates/settings.html") |
| 148 | } |
| 149 | ')! |
| 150 | os.write_file(os.join_path(root, 'ci', 'ci_routes.v'), 'module main |
| 151 | |
| 152 | import veb |
| 153 | |
| 154 | @["/ci"] |
| 155 | pub fn (mut app App) ci_runs(mut ctx Context) veb.Result { |
| 156 | return $veb.html() |
| 157 | } |
| 158 | ')! |
| 159 | os.write_file(os.join_path(root, 'templates', 'register.html'), |
| 160 | '<html><body><p>Register</p></body></html>\n')! |
| 161 | os.write_file(os.join_path(root, 'templates', 'ci_runs.html'), |
| 162 | '<html><body><p>CI Runs</p></body></html>\n')! |
| 163 | os.write_file(os.join_path(root, 'templates', 'settings.html'), |
| 164 | '<html><body><p>Settings</p></body></html>\n')! |
| 165 | old_dir := os.getwd() |
| 166 | defer { |
| 167 | os.chdir(old_dir) or {} |
| 168 | } |
| 169 | os.chdir(root)! |
| 170 | res := os.execute('${os.quoted_path(@VEXE)} .') |
| 171 | assert res.exit_code == 0, res.output |
| 172 | } |
| 173 | |
| 174 | fn test_module_resolution_is_independent_of_working_directory() { |
| 175 | workspace := setup_module_resolution_workdir_fixture() |
| 176 | defer { |
| 177 | os.rmdir_all(workspace) or {} |
| 178 | } |
| 179 | res_root := vrun_ok_in_dir(workspace, 'run', 'app') |
| 180 | assert res_root.trim_space() == 'Hello 16!' |
| 181 | |
| 182 | res_app := vrun_ok_in_dir(os.join_path(workspace, 'app'), 'run', '.') |
| 183 | assert res_app.trim_space() == 'Hello 16!' |
| 184 | } |
| 185 | |
| 186 | fn test_running_project_from_its_own_vmod_with_parent_vmod_works_issue_26828() { |
| 187 | root := os.join_path(os.vtmp_dir(), 'v_issue_26828_${os.getpid()}') |
| 188 | os.rmdir_all(root) or {} |
| 189 | defer { |
| 190 | os.rmdir_all(root) or {} |
| 191 | } |
| 192 | project_root := os.join_path(root, 'outer') |
| 193 | module_root := os.join_path(project_root, 'cli004') |
| 194 | os.mkdir_all(os.join_path(module_root, 'sub'))! |
| 195 | os.write_file(os.join_path(project_root, 'v.mod'), "Module {\n\tname: 'outer'\n}\n")! |
| 196 | os.write_file(os.join_path(module_root, 'v.mod'), "Module {\n\tname: 'cli004'\n}\n")! |
| 197 | os.write_file(os.join_path(module_root, 'cli004.v'), |
| 198 | 'module main\n\nimport sub\n\nfn main() {\n\tsub.greetings()\n}\n')! |
| 199 | os.write_file(os.join_path(module_root, 'sub', 'hey.v'), |
| 200 | "module sub\n\npub fn greetings() {\n\tprintln('greetings from sub')\n}\n")! |
| 201 | |
| 202 | res := vrun_ok_in_dir(module_root, 'crun', '.') |
| 203 | assert res.trim_space() == 'greetings from sub' |
| 204 | } |
| 205 | |
| 206 | fn test_custom_print_should_compile_with_no_builtin() { |
| 207 | source_path := os.join_path(os.vtmp_dir(), 'custom_print_no_builtin_${os.getpid()}.v') |
| 208 | output_path := os.join_path(os.vtmp_dir(), 'custom_print_no_builtin_${os.getpid()}.c') |
| 209 | source := [ |
| 210 | '@[markused]', |
| 211 | 'pub fn error() {', |
| 212 | "\tprint(c'\\n')", |
| 213 | '}', |
| 214 | '', |
| 215 | 'pub fn print(fmt voidptr, ...) {}', |
| 216 | ].join_lines() |
| 217 | os.write_file(source_path, source)! |
| 218 | defer { |
| 219 | os.rm(source_path) or {} |
| 220 | os.rm(output_path) or {} |
| 221 | } |
| 222 | _ = vrun_ok('-o ${os.quoted_path(output_path)} -no-builtin', source_path) |
| 223 | assert os.exists(output_path) |
| 224 | } |
| 225 | |
| 226 | fn test_generic_recursive_self_method_call_should_compile() { |
| 227 | source_path := os.join_path(os.vtmp_dir(), |
| 228 | 'generic_recursive_self_method_call_${os.getpid()}.v') |
| 229 | output_path := os.join_path(os.vtmp_dir(), 'generic_recursive_self_method_call_${os.getpid()}') |
| 230 | mut expected_output_path := output_path |
| 231 | $if windows { |
| 232 | expected_output_path += '.exe' |
| 233 | } |
| 234 | source := [ |
| 235 | 'struct Decoder {}', |
| 236 | '', |
| 237 | 'struct StructTypePointer[T] {', |
| 238 | 'mut:', |
| 239 | '\tval &T', |
| 240 | '}', |
| 241 | '', |
| 242 | 'pub fn decode[T](val string) !T {', |
| 243 | '\tmut decoder := Decoder{}', |
| 244 | '', |
| 245 | '\tmut result := T{}', |
| 246 | '\tdecoder.decode_value(mut result)!', |
| 247 | '\treturn result', |
| 248 | '}', |
| 249 | '', |
| 250 | 'fn (mut decoder Decoder) decode_value[T](mut val T) ! {', |
| 251 | '\t\$if T.indirections != 0 {', |
| 252 | '\t\tunsafe {', |
| 253 | '\t\t\t*val = 2', |
| 254 | '\t\t}', |
| 255 | '\t} \$else \$if T is \$struct {', |
| 256 | '\t\tdecode_value(mut val.val)!', |
| 257 | '\t}', |
| 258 | '}', |
| 259 | '', |
| 260 | 'fn main() {', |
| 261 | "\tassert *decode[StructTypePointer[int]]('2')!.val == 2", |
| 262 | '}', |
| 263 | ].join_lines() |
| 264 | write_file(source_path, source) |
| 265 | defer { |
| 266 | os.rm(source_path) or {} |
| 267 | os.rm(output_path) or {} |
| 268 | os.rm(expected_output_path) or {} |
| 269 | } |
| 270 | _ = vrun_ok('-o ${os.quoted_path(output_path)}', source_path) |
| 271 | assert os.exists(expected_output_path) |
| 272 | } |
| 273 | |
| 274 | fn test_sync_waitgroup_should_check_for_windows() { |
| 275 | source_path := os.join_path(os.vtmp_dir(), 'sync_waitgroup_issue_14468_${os.getpid()}.v') |
| 276 | source := [ |
| 277 | 'module main', |
| 278 | '', |
| 279 | 'import sync', |
| 280 | '', |
| 281 | 'fn main() {', |
| 282 | '\tmut wg := sync.new_waitgroup()', |
| 283 | '\twg.add(1)', |
| 284 | '\twg.done()', |
| 285 | '\twg.wait()', |
| 286 | '}', |
| 287 | ].join_lines() |
| 288 | write_file(source_path, source) |
| 289 | defer { |
| 290 | os.rm(source_path) or {} |
| 291 | } |
| 292 | _ = vrun_ok('-os windows -check', source_path) |
| 293 | } |
| 294 | |
| 295 | fn test_usecache_build_module_sumtype_uses_canonical_type_id_helper() { |
| 296 | root := os.join_path(os.vtmp_dir(), 'usecache_sumtype_cross_module_${os.getpid()}') |
| 297 | cache_dir := os.join_path(root, '.cache') |
| 298 | vtmp_dir := os.join_path(root, '.vtmp') |
| 299 | os.rmdir_all(root) or {} |
| 300 | defer { |
| 301 | os.rmdir_all(root) or {} |
| 302 | } |
| 303 | os.mkdir_all(os.join_path(root, 'payload'))! |
| 304 | os.mkdir_all(os.join_path(root, 'maker'))! |
| 305 | os.mkdir_all(vtmp_dir)! |
| 306 | write_file(os.join_path(root, 'v.mod'), "Module {\n\tname: 'ucsmt'\n}\n") |
| 307 | write_file(os.join_path(root, 'payload', 'payload.v'), |
| 308 | 'module payload\n\npub struct Foo {\n\tpub:\n\t\tn int\n}\n\npub struct Bar {\n\tpub:\n\t\ts string\n}\n\npub type Value = Foo | Bar\n') |
| 309 | write_file(os.join_path(root, 'maker', 'maker.v'), |
| 310 | 'module maker\n\nimport payload\n\npub fn make() payload.Value {\n\treturn payload.Value(payload.Foo{n: 42})\n}\n') |
| 311 | old_vcache := os.getenv_opt('VCACHE') or { '' } |
| 312 | old_vtmp := os.getenv_opt('VTMP') or { '' } |
| 313 | os.setenv('VCACHE', cache_dir, true) |
| 314 | os.setenv('VTMP', vtmp_dir, true) |
| 315 | defer { |
| 316 | if old_vcache.len == 0 { |
| 317 | os.unsetenv('VCACHE') |
| 318 | } else { |
| 319 | os.setenv('VCACHE', old_vcache, true) |
| 320 | } |
| 321 | if old_vtmp.len == 0 { |
| 322 | os.unsetenv('VTMP') |
| 323 | } else { |
| 324 | os.setenv('VTMP', old_vtmp, true) |
| 325 | } |
| 326 | } |
| 327 | mut p := os.new_process(@VEXE) |
| 328 | p.set_work_folder(root) |
| 329 | p.set_args(['-keepc', 'build-module', 'maker']) |
| 330 | p.set_redirect_stdio() |
| 331 | p.wait() |
| 332 | stdout := p.stdout_slurp() |
| 333 | stderr := p.stderr_slurp() |
| 334 | exit_code := p.code |
| 335 | p.close() |
| 336 | assert exit_code == 0, '${stdout}${stderr}' |
| 337 | generated_c_path := os.join_path(vtmp_dir, 'maker.tmp.c') |
| 338 | assert os.exists(generated_c_path) |
| 339 | generated_c := os.read_file(generated_c_path)! |
| 340 | assert generated_c.contains('payload__Foo_to_sumtype_payload__Value(payload__Foo* x, bool is_mut)') |
| 341 | assert generated_c.contains('._typ = _v_type_idx_payload__Foo()') |
| 342 | } |
| 343 | |
| 344 | fn test_readline_raw_mode_methods_should_check_for_windows() { |
| 345 | source_path := os.join_path(os.vtmp_dir(), 'readline_raw_mode_issue_24686_${os.getpid()}.v') |
| 346 | source := [ |
| 347 | 'module main', |
| 348 | '', |
| 349 | 'import readline', |
| 350 | '', |
| 351 | 'fn main() {', |
| 352 | '\tmut r := readline.Readline{}', |
| 353 | '\tr.enable_raw_mode()', |
| 354 | '\tr.disable_raw_mode()', |
| 355 | '\tr.enable_raw_mode_nosig()', |
| 356 | '\tprintln(r.read_char()!)', |
| 357 | '\tr.disable_raw_mode()', |
| 358 | '}', |
| 359 | ].join_lines() |
| 360 | write_file(source_path, source) |
| 361 | defer { |
| 362 | os.rm(source_path) or {} |
| 363 | } |
| 364 | _ = vrun_ok('-os windows -check', source_path) |
| 365 | } |
| 366 | |