| 1 | import os |
| 2 | import term |
| 3 | import v.util.diff |
| 4 | |
| 5 | const vroot = @VMODROOT |
| 6 | const test_file = os.join_path(vroot, 'vlib', 'v', 'tests', 'vls', 'goto_def_test_data.vv') |
| 7 | const mod1_text_file = os.join_path(vroot, 'vlib', 'v', 'tests', 'vls', 'sample_mod1', 'sample.v') |
| 8 | |
| 9 | struct TestCase { |
| 10 | name string |
| 11 | line int |
| 12 | col int |
| 13 | expected string |
| 14 | description string |
| 15 | } |
| 16 | |
| 17 | const test_cases = [ |
| 18 | // test_local_struct() tests |
| 19 | TestCase{ |
| 20 | name: 'method_call' |
| 21 | line: 17 |
| 22 | col: 13 |
| 23 | expected: '${test_file}:10:20' |
| 24 | description: 'Go to method definition from method call' |
| 25 | }, |
| 26 | TestCase{ |
| 27 | name: 'struct_init' |
| 28 | line: 20 |
| 29 | col: 13 |
| 30 | expected: '${test_file}:6:7' |
| 31 | description: 'Go to struct definition from StructInit' |
| 32 | }, |
| 33 | TestCase{ |
| 34 | name: 'struct_init_field_name' |
| 35 | line: 15 |
| 36 | col: 28 |
| 37 | expected: '${test_file}:7:1' |
| 38 | description: 'Go to field definition from field name in struct initialization' |
| 39 | }, |
| 40 | TestCase{ |
| 41 | name: 'field_access' |
| 42 | line: 21 |
| 43 | col: 21 |
| 44 | expected: '${test_file}:7:1' |
| 45 | description: 'Go to field definition from field access' |
| 46 | }, |
| 47 | TestCase{ |
| 48 | name: 'variable_reference' |
| 49 | line: 23 |
| 50 | col: 17 |
| 51 | expected: '${test_file}:15:6' |
| 52 | description: 'Go to variable definition from reference' |
| 53 | }, |
| 54 | TestCase{ |
| 55 | name: 'field_access_via_variable' |
| 56 | line: 26 |
| 57 | col: 17 |
| 58 | expected: '${test_file}:7:1' |
| 59 | description: 'Go to field definition from obj.value' |
| 60 | }, |
| 61 | TestCase{ |
| 62 | name: 'method_receiver_type' |
| 63 | line: 10 |
| 64 | col: 8 |
| 65 | expected: '${test_file}:6:7' |
| 66 | description: 'Go to struct definition from method receiver type (LocalStruct)' |
| 67 | }, |
| 68 | TestCase{ |
| 69 | name: 'method_return_type_builtin' |
| 70 | line: 10 |
| 71 | col: 33 |
| 72 | expected: os.join_path(vroot, 'vlib', 'builtin', 'string.v') + ':45:11' |
| 73 | description: 'Builtin return type (string) jumps to builtin string definition' |
| 74 | }, |
| 75 | // test_deep_struct() tests |
| 76 | TestCase{ |
| 77 | name: 'nested_struct_st' |
| 78 | line: 49 |
| 79 | col: 22 |
| 80 | expected: '${test_file}:38:1' |
| 81 | description: 'Go to nested anonymous struct field definition (deep.st)' |
| 82 | }, |
| 83 | TestCase{ |
| 84 | name: 'nested_struct_sstt' |
| 85 | line: 49 |
| 86 | col: 25 |
| 87 | expected: '${test_file}:39:2' |
| 88 | description: 'Go to deeply nested anonymous struct field definition (deep.st.sstt)' |
| 89 | }, |
| 90 | TestCase{ |
| 91 | name: 'nested_struct_l2' |
| 92 | line: 49 |
| 93 | col: 30 |
| 94 | expected: '${test_file}:40:3' |
| 95 | description: 'Go to deeply nested struct field definition (deep.st.sstt.l2)' |
| 96 | }, |
| 97 | TestCase{ |
| 98 | name: 'embedded_struct_field' |
| 99 | line: 52 |
| 100 | col: 23 |
| 101 | expected: '${test_file}:37:1' |
| 102 | description: 'Go to embedded struct field declaration (deep.LocalStruct)' |
| 103 | }, |
| 104 | TestCase{ |
| 105 | name: 'embedded_field_to_type' |
| 106 | line: 37 |
| 107 | col: 2 |
| 108 | expected: '${test_file}:6:7' |
| 109 | description: 'Go to struct type definition from embedded field declaration' |
| 110 | }, |
| 111 | TestCase{ |
| 112 | name: 'deep_2_nested_sstt_in_init' |
| 113 | line: 58 |
| 114 | col: 21 |
| 115 | expected: '${test_file}:39:2' |
| 116 | description: 'Go to field definition from deep.st.sstt in struct init' |
| 117 | }, |
| 118 | TestCase{ |
| 119 | name: 'deep_2_nested_l1_in_init' |
| 120 | line: 60 |
| 121 | col: 16 |
| 122 | expected: '${test_file}:42:2' |
| 123 | description: 'Go to field definition from deep.st.l1 in struct init' |
| 124 | }, |
| 125 | // test_local_enum() tests |
| 126 | TestCase{ |
| 127 | name: 'enum_value_qualified' |
| 128 | line: 72 |
| 129 | col: 18 |
| 130 | expected: '${test_file}:66:1' |
| 131 | description: 'Go to enum value definition (qualified form: LocalEnum.first)' |
| 132 | }, |
| 133 | TestCase{ |
| 134 | name: 'enum_value_short_form' |
| 135 | line: 75 |
| 136 | col: 3 |
| 137 | expected: '${test_file}:67:1' |
| 138 | description: 'Go to enum value definition (short form: .second in match)' |
| 139 | }, |
| 140 | // test_local_alias() tests |
| 141 | TestCase{ |
| 142 | name: 'type_alias_cast' |
| 143 | line: 83 |
| 144 | col: 13 |
| 145 | expected: '${test_file}:80:5' |
| 146 | description: 'Go to type alias definition in cast expression' |
| 147 | }, |
| 148 | TestCase{ |
| 149 | name: 'type_alias_rhs_type' |
| 150 | line: 80 |
| 151 | col: 19 |
| 152 | expected: os.join_path(vroot, 'vlib', 'builtin', 'string.v') + ':45:11' |
| 153 | description: 'Go to builtin type definition from type alias RHS' |
| 154 | }, |
| 155 | // test_local_sum() tests |
| 156 | TestCase{ |
| 157 | name: 'sum_type_cast' |
| 158 | line: 99 |
| 159 | col: 13 |
| 160 | expected: '${test_file}:86:5' |
| 161 | description: 'Go to sum type definition in cast expression' |
| 162 | }, |
| 163 | TestCase{ |
| 164 | name: 'user_sum_type_rhs_type_a' |
| 165 | line: 96 |
| 166 | col: 18 |
| 167 | expected: '${test_file}:88:7' |
| 168 | description: 'Go to TypeA definition from UserSum type RHS' |
| 169 | }, |
| 170 | TestCase{ |
| 171 | name: 'user_sum_type_rhs_type_b' |
| 172 | line: 96 |
| 173 | col: 26 |
| 174 | expected: '${test_file}:92:7' |
| 175 | description: 'Go to TypeB definition from UserSum type RHS' |
| 176 | }, |
| 177 | // test_match_enum() tests |
| 178 | TestCase{ |
| 179 | name: 'match_enum_short_form' |
| 180 | line: 115 |
| 181 | col: 3 |
| 182 | expected: '${test_file}:107:1' |
| 183 | description: 'Go to enum value definition in match expression (short form .completion)' |
| 184 | }, |
| 185 | TestCase{ |
| 186 | name: 'match_enum_variable' |
| 187 | line: 114 |
| 188 | col: 22 |
| 189 | expected: '${test_file}:113:1' |
| 190 | description: 'Go to variable definition from match expression condition' |
| 191 | }, |
| 192 | // test_imported_enum() tests |
| 193 | TestCase{ |
| 194 | name: 'imported_enum_value' |
| 195 | line: 129 |
| 196 | col: 25 |
| 197 | expected: '${mod1_text_file}:25:1' |
| 198 | description: 'Go to imported enum value definition' |
| 199 | }, |
| 200 | // test_operator_overload() tests |
| 201 | TestCase{ |
| 202 | name: 'operator_receiver_type' |
| 203 | line: 137 |
| 204 | col: 8 |
| 205 | expected: '${test_file}:133:7' |
| 206 | description: 'Go to struct definition from operator overload receiver type' |
| 207 | }, |
| 208 | TestCase{ |
| 209 | name: 'operator_param_type' |
| 210 | line: 137 |
| 211 | col: 22 |
| 212 | expected: '${test_file}:133:7' |
| 213 | description: 'Go to struct definition from operator overload parameter type' |
| 214 | }, |
| 215 | TestCase{ |
| 216 | name: 'operator_return_type' |
| 217 | line: 137 |
| 218 | col: 33 |
| 219 | expected: '${test_file}:133:7' |
| 220 | description: 'Go to struct definition from operator overload return type' |
| 221 | }, |
| 222 | TestCase{ |
| 223 | name: 'struct_init_field_in_params_left' |
| 224 | line: 138 |
| 225 | col: 23 |
| 226 | expected: '${test_file}:137:4' |
| 227 | description: 'Go to param definition from struct init variable (left fn param)' |
| 228 | }, |
| 229 | TestCase{ |
| 230 | name: 'struct_init_field_in_params_right' |
| 231 | line: 138 |
| 232 | col: 31 |
| 233 | expected: '${test_file}:137:19' |
| 234 | description: 'Go to param definition from struct init variable (right fn param)' |
| 235 | }, |
| 236 | TestCase{ |
| 237 | name: 'field_selector_in_struct_init' |
| 238 | line: 138 |
| 239 | col: 25 |
| 240 | expected: '${test_file}:134:1' |
| 241 | description: 'Go to field definition from selector expression in struct init value (a.val)' |
| 242 | }, |
| 243 | // test_function_params() tests |
| 244 | TestCase{ |
| 245 | name: 'function_param_type' |
| 246 | line: 141 |
| 247 | col: 24 |
| 248 | expected: '${test_file}:133:7' |
| 249 | description: 'Go to struct definition from function parameter type' |
| 250 | }, |
| 251 | // test_array_field_types() tests |
| 252 | TestCase{ |
| 253 | name: 'array_elem_type_in_struct_field' |
| 254 | line: 150 |
| 255 | col: 10 |
| 256 | expected: '${test_file}:145:7' |
| 257 | description: 'Go to element type definition from array type in struct field ([]ArrayElemStruct)' |
| 258 | }, |
| 259 | TestCase{ |
| 260 | name: 'fixed_array_elem_type_in_struct_field' |
| 261 | line: 151 |
| 262 | col: 17 |
| 263 | expected: '${test_file}:145:7' |
| 264 | description: 'Go to element type definition from fixed array type in struct field ([5]ArrayElemStruct)' |
| 265 | }, |
| 266 | ] |
| 267 | |
| 268 | fn test_goto_definition() { |
| 269 | mut total_errors := 0 |
| 270 | mut passed := 0 |
| 271 | |
| 272 | for tc in test_cases { |
| 273 | cmd := 'v -w -check -json-errors -nocolor -vls-mode -line-info "${test_file}:${tc.line}:gd^${tc.col}" ${os.quoted_path(test_file)}' |
| 274 | res := os.execute(cmd) |
| 275 | |
| 276 | if res.exit_code < 0 { |
| 277 | println('${term.red('FAIL')} ${tc.name}: Command failed to execute') |
| 278 | println(' Command: ${cmd}') |
| 279 | total_errors++ |
| 280 | continue |
| 281 | } |
| 282 | |
| 283 | res_output := $if windows { |
| 284 | res.output.replace('\r\n', '\n').trim_space() |
| 285 | } $else { |
| 286 | res.output.trim_space() |
| 287 | } |
| 288 | |
| 289 | if tc.expected != res_output { |
| 290 | println('${term.red('FAIL')} ${tc.name}') |
| 291 | println(' Description: ${tc.description}') |
| 292 | println(' Line ${tc.line}, Column ${tc.col}') |
| 293 | if diff_ := diff.compare_text(tc.expected, res_output) { |
| 294 | println(' Difference:') |
| 295 | println(diff_) |
| 296 | } else { |
| 297 | println(' Expected: ${tc.expected}') |
| 298 | println(' Got: ${res_output}') |
| 299 | } |
| 300 | total_errors++ |
| 301 | } else { |
| 302 | println('${term.green('OK ')} ${tc.name}: ${tc.description}') |
| 303 | passed++ |
| 304 | } |
| 305 | } |
| 306 | |
| 307 | println('') |
| 308 | println('${term.header('Summary:', '=')}') |
| 309 | println('Passed: ${passed}/${test_cases.len}') |
| 310 | if total_errors > 0 { |
| 311 | println('${term.red('Failed:')} ${total_errors}') |
| 312 | } |
| 313 | |
| 314 | assert total_errors == 0, 'Some tests failed' |
| 315 | } |
| 316 | |