| 1 | module main |
| 2 | |
| 3 | import os |
| 4 | import rand |
| 5 | |
| 6 | const current_vexe = @VEXE |
| 7 | |
| 8 | const delegated_marker = 'VVM-DELEGATED' |
| 9 | |
| 10 | const delegated_v2_marker = 'V2-DELEGATED-WAITED' |
| 11 | |
| 12 | const requested_vvm_test_version = '99.99.99' |
| 13 | |
| 14 | struct CmdResult { |
| 15 | exit_code int |
| 16 | output string |
| 17 | } |
| 18 | |
| 19 | fn vvm_test_repo_root() string { |
| 20 | mut dir := os.dir(@FILE) |
| 21 | for _ in 0 .. 10 { |
| 22 | if os.exists(os.join_path(dir, 'cmd', 'v', 'v.v')) |
| 23 | && os.exists(os.join_path(dir, 'vlib', 'builtin')) { |
| 24 | return dir |
| 25 | } |
| 26 | dir = os.dir(dir) |
| 27 | } |
| 28 | panic('could not locate repo root for vvm tests') |
| 29 | } |
| 30 | |
| 31 | fn build_current_v_wrapper(tmp_root string) string { |
| 32 | vroot := vvm_test_repo_root() |
| 33 | wrapper_exe := os.join_path(tmp_root, if os.user_os() == 'windows' { |
| 34 | 'v_public_wrapper.exe' |
| 35 | } else { |
| 36 | 'v_public_wrapper' |
| 37 | }) |
| 38 | compile_res := os.execute('${os.quoted_path(current_vexe)} -path "${os.join_path(vroot, 'vlib')}|@vlib|@vmodules" -o ${os.quoted_path(wrapper_exe)} ${os.quoted_path(os.join_path(vroot, |
| 39 | 'cmd', 'v'))}') |
| 40 | assert compile_res.exit_code == 0, compile_res.output |
| 41 | return wrapper_exe |
| 42 | } |
| 43 | |
| 44 | fn test_parse_vvmrc_version() { |
| 45 | assert parse_vvmrc_version('') == '' |
| 46 | assert parse_vvmrc_version(' ') == '' |
| 47 | assert parse_vvmrc_version('# only comments') == '' |
| 48 | assert parse_vvmrc_version('\n# comment\n0.4.6\n') == '0.4.6' |
| 49 | assert parse_vvmrc_version(' latest # comment ') == 'latest' |
| 50 | } |
| 51 | |
| 52 | fn test_find_project_vvmrc_stops_at_stop_paths() { |
| 53 | tmp_root := os.join_path(os.vtmp_dir(), 'vvmrc_stop_test_${rand.ulid()}') |
| 54 | os.mkdir_all(tmp_root) or { panic(err) } |
| 55 | defer { |
| 56 | os.rmdir_all(tmp_root) or {} |
| 57 | } |
| 58 | project_dir := os.join_path(tmp_root, 'project') |
| 59 | source_dir := os.join_path(project_dir, 'src') |
| 60 | os.mkdir_all(source_dir) or { panic(err) } |
| 61 | os.mkdir_all(os.join_path(project_dir, '.git')) or { panic(err) } |
| 62 | os.write_file(os.join_path(tmp_root, '.vvmrc'), requested_vvm_test_version) or { panic(err) } |
| 63 | main_v := os.join_path(source_dir, 'main.v') |
| 64 | os.write_file(main_v, 'fn main() {}') or { panic(err) } |
| 65 | assert find_project_vvmrc(main_v) == '' |
| 66 | local_vvmrc := os.join_path(project_dir, '.vvmrc') |
| 67 | os.write_file(local_vvmrc, requested_vvm_test_version) or { panic(err) } |
| 68 | assert find_project_vvmrc(main_v) == os.real_path(local_vvmrc) |
| 69 | } |
| 70 | |
| 71 | fn test_vvmrc_delegates_to_matching_compiler_executable() { |
| 72 | tmp_root := os.join_path(os.vtmp_dir(), 'vvmrc_delegate_test_${rand.ulid()}') |
| 73 | os.mkdir_all(tmp_root) or { panic(err) } |
| 74 | defer { |
| 75 | os.rmdir_all(tmp_root) or {} |
| 76 | } |
| 77 | bin_dir := os.join_path(tmp_root, 'bin') |
| 78 | os.mkdir_all(bin_dir) or { panic(err) } |
| 79 | fake_compiler_source := os.join_path(tmp_root, 'fake_compiler.v') |
| 80 | os.write_file(fake_compiler_source, |
| 81 | "import os\nfn main() {\n\tprintln('${delegated_marker} ' + os.args[1..].join(' '))\n}\n") or { |
| 82 | panic(err) |
| 83 | } |
| 84 | fake_compiler_exe := os.join_path(bin_dir, if os.user_os() == 'windows' { |
| 85 | 'v${requested_vvm_test_version}.exe' |
| 86 | } else { |
| 87 | 'v${requested_vvm_test_version}' |
| 88 | }) |
| 89 | compile_res := |
| 90 | os.execute('${os.quoted_path(current_vexe)} -o ${os.quoted_path(fake_compiler_exe)} ${os.quoted_path(fake_compiler_source)}') |
| 91 | assert compile_res.exit_code == 0, compile_res.output |
| 92 | project_dir := os.join_path(tmp_root, 'project') |
| 93 | os.mkdir_all(project_dir) or { panic(err) } |
| 94 | os.write_file(os.join_path(project_dir, '.vvmrc'), requested_vvm_test_version) or { panic(err) } |
| 95 | os.write_file(os.join_path(project_dir, 'main.v'), |
| 96 | "fn main() {\n\tprintln('from project')\n}\n") or { panic(err) } |
| 97 | mut envs := os.environ() |
| 98 | path_key := find_path_env_key(envs) |
| 99 | envs[path_key] = '${bin_dir}${os.path_delimiter}${envs[path_key]}' |
| 100 | envs['V_SKIP_VVMRC'] = '' |
| 101 | res := run_v_command(project_dir, ['run', 'main.v'], envs) |
| 102 | assert res.exit_code == 0, res.output |
| 103 | assert res.output.contains('${delegated_marker} run main.v'), res.output |
| 104 | } |
| 105 | |
| 106 | fn test_v2_delegation_waits_for_compiler_process_and_propagates_exit_code() { |
| 107 | tmp_root := os.join_path(os.vtmp_dir(), 'v2_delegate_wait_test_${rand.ulid()}') |
| 108 | os.mkdir_all(tmp_root) or { panic(err) } |
| 109 | defer { |
| 110 | os.rmdir_all(tmp_root) or {} |
| 111 | } |
| 112 | fake_compiler_source := os.join_path(tmp_root, 'fake_v2_compiler.v') |
| 113 | marker_file := os.join_path(tmp_root, 'v2_marker.txt') |
| 114 | os.write_file(fake_compiler_source, |
| 115 | "import os\nimport time\nfn main() {\n\ttime.sleep(700 * time.millisecond)\n\tmarker := os.getenv('V_V2_TEST_MARKER')\n\tos.write_file(marker, os.args[1..].join(' ')) or { panic(err) }\n\tprintln('${delegated_v2_marker}')\n\texit(7)\n}\n") or { |
| 116 | panic(err) |
| 117 | } |
| 118 | fake_compiler_exe := os.join_path(tmp_root, if os.user_os() == 'windows' { |
| 119 | 'fake_v2_compiler.exe' |
| 120 | } else { |
| 121 | 'fake_v2_compiler' |
| 122 | }) |
| 123 | compile_res := |
| 124 | os.execute('${os.quoted_path(current_vexe)} -o ${os.quoted_path(fake_compiler_exe)} ${os.quoted_path(fake_compiler_source)}') |
| 125 | assert compile_res.exit_code == 0, compile_res.output |
| 126 | project_dir := os.join_path(tmp_root, 'project') |
| 127 | os.mkdir_all(project_dir) or { panic(err) } |
| 128 | os.write_file(os.join_path(project_dir, 'main.v'), "fn main() {\n\tprintln('unused')\n}\n") or { |
| 129 | panic(err) |
| 130 | } |
| 131 | mut envs := os.environ() |
| 132 | envs[delegated_v2_exe_env] = fake_compiler_exe |
| 133 | envs['V_V2_TEST_MARKER'] = marker_file |
| 134 | envs[vvmrc_skip_env] = '1' |
| 135 | envs['VFLAGS'] = '' |
| 136 | |
| 137 | res := run_v_command(project_dir, ['-v2', 'main.v'], envs) |
| 138 | assert res.exit_code == 7, res.output |
| 139 | assert res.output.contains(delegated_v2_marker), res.output |
| 140 | assert os.is_file(marker_file) |
| 141 | marker := os.read_file(marker_file) or { panic(err) } |
| 142 | assert marker.contains('main.v'), marker |
| 143 | assert !marker.contains('-v2'), marker |
| 144 | } |
| 145 | |
| 146 | fn test_v2_delegation_forwards_target_specific_flags() { |
| 147 | tmp_root := os.join_path(os.vtmp_dir(), 'v2_delegate_flags_test_${rand.ulid()}') |
| 148 | os.mkdir_all(tmp_root) or { panic(err) } |
| 149 | defer { |
| 150 | os.rmdir_all(tmp_root) or {} |
| 151 | } |
| 152 | fake_compiler_source := os.join_path(tmp_root, 'fake_v2_compiler.v') |
| 153 | marker_file := os.join_path(tmp_root, 'v2_args.txt') |
| 154 | os.write_file(fake_compiler_source, |
| 155 | "import os\nfn main() {\n\tmarker := os.getenv('V_V2_TEST_MARKER')\n\tos.write_file(marker, os.args[1..].join(' ')) or { panic(err) }\n}\n") or { |
| 156 | panic(err) |
| 157 | } |
| 158 | fake_compiler_exe := os.join_path(tmp_root, if os.user_os() == 'windows' { |
| 159 | 'fake_v2_compiler.exe' |
| 160 | } else { |
| 161 | 'fake_v2_compiler' |
| 162 | }) |
| 163 | compile_res := |
| 164 | os.execute('${os.quoted_path(current_vexe)} -o ${os.quoted_path(fake_compiler_exe)} ${os.quoted_path(fake_compiler_source)}') |
| 165 | assert compile_res.exit_code == 0, compile_res.output |
| 166 | project_dir := os.join_path(tmp_root, 'project') |
| 167 | os.mkdir_all(project_dir) or { panic(err) } |
| 168 | os.write_file(os.join_path(project_dir, 'main.v'), 'fn main() {}\n') or { panic(err) } |
| 169 | wrapper_exe := build_current_v_wrapper(tmp_root) |
| 170 | mut envs := os.environ() |
| 171 | envs[delegated_v2_exe_env] = fake_compiler_exe |
| 172 | envs['V_V2_TEST_MARKER'] = marker_file |
| 173 | envs[vvmrc_skip_env] = '1' |
| 174 | envs['VFLAGS'] = '' |
| 175 | envs['VEXE'] = os.join_path(vvm_test_repo_root(), 'v') |
| 176 | |
| 177 | res := run_v_command_with_exe(wrapper_exe, project_dir, [ |
| 178 | '-v2', |
| 179 | '-freestanding', |
| 180 | '-os', |
| 181 | 'none', |
| 182 | '--skip-builtin', |
| 183 | '--skip-type-check', |
| 184 | '-fhooks', |
| 185 | 'output,panic,alloc', |
| 186 | '-o', |
| 187 | 'out.c', |
| 188 | 'main.v', |
| 189 | ], envs) |
| 190 | assert res.exit_code == 0, res.output |
| 191 | assert os.is_file(marker_file) |
| 192 | marker := os.read_file(marker_file) or { panic(err) } |
| 193 | assert marker.contains('-freestanding'), marker |
| 194 | assert marker.contains('-os none'), marker |
| 195 | assert marker.contains('--skip-builtin'), marker |
| 196 | assert marker.contains('--skip-type-check'), marker |
| 197 | assert marker.contains('-fhooks output,panic,alloc'), marker |
| 198 | assert marker.contains('-o out.c'), marker |
| 199 | assert marker.contains('main.v'), marker |
| 200 | assert !marker.contains('-v2'), marker |
| 201 | |
| 202 | res_macos := run_v_command_with_exe(wrapper_exe, project_dir, [ |
| 203 | '-v2', |
| 204 | '-b', |
| 205 | 'x64', |
| 206 | '-os', |
| 207 | 'macos', |
| 208 | '-no-mos-tiny', |
| 209 | '-o', |
| 210 | 'out', |
| 211 | 'main.v', |
| 212 | ], envs) |
| 213 | assert res_macos.exit_code == 0, res_macos.output |
| 214 | macos_marker := os.read_file(marker_file) or { panic(err) } |
| 215 | assert macos_marker.contains('-b x64'), macos_marker |
| 216 | assert macos_marker.contains('-os macos'), macos_marker |
| 217 | assert macos_marker.contains('-no-mos-tiny'), macos_marker |
| 218 | assert macos_marker.contains('-o out'), macos_marker |
| 219 | assert macos_marker.contains('main.v'), macos_marker |
| 220 | assert !macos_marker.contains('-v2'), macos_marker |
| 221 | |
| 222 | os.rm(marker_file) or {} |
| 223 | legacy_macos_tiny_res := run_v_command_with_exe(wrapper_exe, project_dir, [ |
| 224 | '-v2', |
| 225 | '-mos-tiny', |
| 226 | 'main.v', |
| 227 | ], envs) |
| 228 | assert legacy_macos_tiny_res.exit_code == 1, legacy_macos_tiny_res.output |
| 229 | assert legacy_macos_tiny_res.output.contains('Unknown argument `-mos-tiny`'), legacy_macos_tiny_res.output |
| 230 | assert !os.exists(marker_file), 'legacy -mos-tiny was forwarded to V2' |
| 231 | |
| 232 | for blocked_args in [ |
| 233 | ['--skip-builtin', '--', '-v2', 'main.v'], |
| 234 | ['--skip-builtin', 'run', 'main.v', '-v2'], |
| 235 | ['main.v', '--skip-builtin', '-v2'], |
| 236 | ] { |
| 237 | blocked_res := run_v_command_with_exe(wrapper_exe, project_dir, blocked_args, envs) |
| 238 | assert blocked_res.exit_code == 1, blocked_res.output |
| 239 | assert blocked_res.output.contains('Unknown argument `--skip-builtin`'), blocked_res.output |
| 240 | } |
| 241 | |
| 242 | late_hooks_res := run_v_command_with_exe(wrapper_exe, project_dir, [ |
| 243 | '-fhooks', |
| 244 | 'output', |
| 245 | 'run', |
| 246 | 'main.v', |
| 247 | '-v2', |
| 248 | ], envs) |
| 249 | assert late_hooks_res.exit_code == 1, late_hooks_res.output |
| 250 | assert late_hooks_res.output.contains('Unknown argument `-fhooks`'), late_hooks_res.output |
| 251 | |
| 252 | os_none_after_run_res := run_v_command_with_exe(wrapper_exe, project_dir, [ |
| 253 | '-os', |
| 254 | 'none', |
| 255 | 'run', |
| 256 | 'main.v', |
| 257 | '-v2', |
| 258 | ], envs) |
| 259 | assert os_none_after_run_res.exit_code == 1, os_none_after_run_res.output |
| 260 | assert os_none_after_run_res.output.contains('unknown operating system target `none`'), os_none_after_run_res.output |
| 261 | } |
| 262 | |
| 263 | fn run_v_command(work_dir string, args []string, envs map[string]string) CmdResult { |
| 264 | return run_v_command_with_exe(current_vexe, work_dir, args, envs) |
| 265 | } |
| 266 | |
| 267 | fn run_v_command_with_exe(vexe string, work_dir string, args []string, envs map[string]string) CmdResult { |
| 268 | mut process := os.new_process(vexe) |
| 269 | process.set_work_folder(work_dir) |
| 270 | process.set_args(args) |
| 271 | process.set_environment(envs) |
| 272 | process.set_redirect_stdio() |
| 273 | process.wait() |
| 274 | output := process.stdout_slurp() + process.stderr_slurp() |
| 275 | exit_code := if process.code == -1 { 1 } else { process.code } |
| 276 | process.close() |
| 277 | return CmdResult{ |
| 278 | exit_code: exit_code |
| 279 | output: output |
| 280 | } |
| 281 | } |
| 282 | |
| 283 | fn find_path_env_key(envs map[string]string) string { |
| 284 | if 'PATH' in envs { |
| 285 | return 'PATH' |
| 286 | } |
| 287 | if 'Path' in envs { |
| 288 | return 'Path' |
| 289 | } |
| 290 | return 'PATH' |
| 291 | } |
| 292 | |