cuelang.org/go@v0.10.1/mod/module/module_test.go (about) 1 // Copyright 2018 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 package module 6 7 import ( 8 "testing" 9 10 "cuelang.org/go/internal/cuetest" 11 "github.com/go-quicktest/qt" 12 ) 13 14 var checkTests = []struct { 15 path string 16 version string 17 ok bool 18 }{ 19 {"rsc.io/quote@v0", "0.1.0", false}, 20 {"rsc io/quote", "v1.0.0", false}, 21 22 {"github.com/go-yaml/yaml@v0", "v0.8.0", true}, 23 {"github.com/go-yaml/yaml@v1", "v1.0.0", true}, 24 {"github.com/go-yaml/yaml", "v2.0.0", false}, 25 {"github.com/go-yaml/yaml@v1", "v2.1.5", false}, 26 {"github.com/go-yaml/yaml@v3.0", "v3.0.0", false}, 27 28 {"github.com/go-yaml/yaml@v2", "v1.0.0", false}, 29 {"github.com/go-yaml/yaml@v2", "v2.0.0", true}, 30 {"github.com/go-yaml/yaml@v2", "v2.1.5", true}, 31 {"github.com/go-yaml/yaml@v2", "v3.0.0", false}, 32 33 {"rsc.io/quote", "v17.0.0", false}, 34 } 35 36 func TestCheck(t *testing.T) { 37 for _, tt := range checkTests { 38 err := Check(tt.path, tt.version) 39 if tt.ok && err != nil { 40 t.Errorf("Check(%q, %q) = %v, wanted nil error", tt.path, tt.version, err) 41 } else if !tt.ok && err == nil { 42 t.Errorf("Check(%q, %q) succeeded, wanted error", tt.path, tt.version) 43 } 44 } 45 } 46 47 type checkPathTest struct { 48 path string 49 modErr string 50 importErr string 51 fileErr string 52 } 53 54 var checkPathTests = []checkPathTest{{ 55 path: `x.y/z`, 56 }, { 57 path: `x.y`, 58 }, { 59 path: ``, 60 modErr: `empty string`, 61 importErr: `malformed import path "": empty string`, 62 fileErr: `malformed file path "": empty string`, 63 }, { 64 path: "x.y/\xffz", 65 modErr: `invalid UTF-8`, 66 importErr: `malformed import path "x.y/\xffz": invalid UTF-8`, 67 fileErr: `malformed file path "x.y/\xffz": invalid UTF-8`, 68 }, { 69 path: `/x.y/z`, 70 modErr: `empty path element`, 71 importErr: `malformed import path "/x.y/z": empty path element`, 72 fileErr: `malformed file path "/x.y/z": empty path element`, 73 }, { 74 path: `x./z`, 75 modErr: `trailing '.' in path element`, 76 importErr: `malformed import path "x./z": trailing dot in path element`, 77 fileErr: `malformed file path "x./z": trailing dot in path element`, 78 }, { 79 path: `.x/z`, 80 modErr: `leading '.' in path element`, 81 }, { 82 path: `-x/z`, 83 modErr: `leading dash`, 84 importErr: `malformed import path "-x/z": leading dash`, 85 }, { 86 path: `x..y/z`, 87 modErr: `path does not conform to OCI repository name restrictions; see https://github.com/opencontainers/distribution-spec/blob/HEAD/spec.md#pulling-manifests`, 88 }, { 89 path: `x.y/z/../../w`, 90 modErr: `invalid path element ".."`, 91 importErr: `malformed import path "x.y/z/../../w": invalid path element ".."`, 92 fileErr: `malformed file path "x.y/z/../../w": invalid path element ".."`, 93 }, { 94 path: `x.y//z`, 95 modErr: `double slash`, 96 importErr: `malformed import path "x.y//z": double slash`, 97 fileErr: `malformed file path "x.y//z": double slash`, 98 }, { 99 path: `x.y/z//w`, 100 modErr: `double slash`, 101 importErr: `malformed import path "x.y/z//w": double slash`, 102 fileErr: `malformed file path "x.y/z//w": double slash`, 103 }, { 104 path: `x.y/z/`, 105 modErr: `trailing slash`, 106 importErr: `malformed import path "x.y/z/": trailing slash`, 107 fileErr: `malformed file path "x.y/z/": trailing slash`, 108 }, { 109 path: `x.y/z/v0`, 110 }, { 111 path: `x.y/z/v1`, 112 }, { 113 path: `x.y/z/v2`, 114 }, { 115 path: `x.y/z/v2.0`, 116 }, { 117 path: `X.y/z`, 118 modErr: `invalid char 'X'`, 119 }, { 120 path: `!x.y/z`, 121 modErr: `invalid char '!'`, 122 importErr: `malformed import path "!x.y/z": invalid char '!'`, 123 }, { 124 path: `_x.y/z`, 125 modErr: `leading '_' in path element`, 126 }, { 127 path: `x.y!/z`, 128 modErr: `invalid char '!'`, 129 importErr: `malformed import path "x.y!/z": invalid char '!'`, 130 }, { 131 path: `x.y"/z`, 132 modErr: `invalid char '"'`, 133 importErr: `malformed import path "x.y\"/z": invalid char '"'`, 134 fileErr: `malformed file path "x.y\"/z": invalid char '"'`, 135 }, { 136 path: `x.y#/z`, 137 modErr: `invalid char '#'`, 138 importErr: `malformed import path "x.y#/z": invalid char '#'`, 139 }, { 140 path: `x.y$/z`, 141 modErr: `invalid char '$'`, 142 importErr: `malformed import path "x.y$/z": invalid char '$'`, 143 }, { 144 path: `x.y%/z`, 145 modErr: `invalid char '%'`, 146 importErr: `malformed import path "x.y%/z": invalid char '%'`, 147 }, { 148 path: `x.y&/z`, 149 modErr: `invalid char '&'`, 150 importErr: `malformed import path "x.y&/z": invalid char '&'`, 151 }, { 152 path: `x.y'/z`, 153 modErr: `invalid char '\''`, 154 importErr: `malformed import path "x.y'/z": invalid char '\''`, 155 fileErr: `malformed file path "x.y'/z": invalid char '\''`, 156 }, { 157 path: `x.y(/z`, 158 modErr: `invalid char '('`, 159 importErr: `malformed import path "x.y(/z": invalid char '('`, 160 }, { 161 path: `x.y)/z`, 162 modErr: `invalid char ')'`, 163 importErr: `malformed import path "x.y)/z": invalid char ')'`, 164 }, { 165 path: `x.y*/z`, 166 modErr: `invalid char '*'`, 167 importErr: `malformed import path "x.y*/z": invalid char '*'`, 168 fileErr: `malformed file path "x.y*/z": invalid char '*'`, 169 }, { 170 path: `x.y+/z`, 171 modErr: `invalid char '+'`, 172 }, { 173 path: `x.y,/z`, 174 modErr: `invalid char ','`, 175 importErr: `malformed import path "x.y,/z": invalid char ','`, 176 }, { 177 path: `x.y-/z`, 178 modErr: `trailing '-' in path element`, 179 }, { 180 path: `x.y./zt`, 181 modErr: `trailing '.' in path element`, 182 importErr: `malformed import path "x.y./zt": trailing dot in path element`, 183 fileErr: `malformed file path "x.y./zt": trailing dot in path element`, 184 }, { 185 path: `x.y:/z`, 186 modErr: `invalid char ':'`, 187 importErr: `malformed import path "x.y:/z": invalid char ':'`, 188 fileErr: `malformed file path "x.y:/z": invalid char ':'`, 189 }, { 190 path: `x.y;/z`, 191 modErr: `invalid char ';'`, 192 importErr: `malformed import path "x.y;/z": invalid char ';'`, 193 fileErr: `malformed file path "x.y;/z": invalid char ';'`, 194 }, { 195 path: `x.y</z`, 196 modErr: `invalid char '<'`, 197 importErr: `malformed import path "x.y</z": invalid char '<'`, 198 fileErr: `malformed file path "x.y</z": invalid char '<'`, 199 }, { 200 path: `x.y=/z`, 201 modErr: `invalid char '='`, 202 importErr: `malformed import path "x.y=/z": invalid char '='`, 203 }, { 204 path: `x.y>/z`, 205 modErr: `invalid char '>'`, 206 importErr: `malformed import path "x.y>/z": invalid char '>'`, 207 fileErr: `malformed file path "x.y>/z": invalid char '>'`, 208 }, { 209 path: `x.y?/z`, 210 modErr: `invalid char '?'`, 211 importErr: `malformed import path "x.y?/z": invalid char '?'`, 212 fileErr: `malformed file path "x.y?/z": invalid char '?'`, 213 }, { 214 path: `x.y@/z`, 215 modErr: `invalid char '@'`, 216 importErr: `malformed import path "x.y@/z": invalid char '@'`, 217 }, { 218 path: `x.y[/z`, 219 modErr: `invalid char '['`, 220 importErr: `malformed import path "x.y[/z": invalid char '['`, 221 }, { 222 path: `x.y\/z`, 223 modErr: `invalid char '\\'`, 224 importErr: `malformed import path "x.y\\/z": invalid char '\\'`, 225 fileErr: `malformed file path "x.y\\/z": invalid char '\\'`, 226 }, { 227 path: `x.y]/z`, 228 modErr: `invalid char ']'`, 229 importErr: `malformed import path "x.y]/z": invalid char ']'`, 230 }, { 231 path: `x.y^/z`, 232 modErr: `invalid char '^'`, 233 importErr: `malformed import path "x.y^/z": invalid char '^'`, 234 }, { 235 path: `x.y_/z`, 236 modErr: `trailing '_' in path element`, 237 }, { 238 path: "x.y`/z", 239 modErr: "invalid char '`'", 240 importErr: "malformed import path \"x.y`/z\": invalid char '`'", 241 fileErr: "malformed file path \"x.y`/z\": invalid char '`'", 242 }, { 243 path: `x.y{/z`, 244 modErr: `invalid char '{'`, 245 importErr: `malformed import path "x.y{/z": invalid char '{'`, 246 }, { 247 path: `x.y}/z`, 248 modErr: `invalid char '}'`, 249 importErr: `malformed import path "x.y}/z": invalid char '}'`, 250 }, { 251 path: `x.y~/z`, 252 modErr: `invalid char '~'`, 253 }, { 254 path: `x.y/z!`, 255 modErr: `invalid char '!'`, 256 importErr: `malformed import path "x.y/z!": invalid char '!'`, 257 }, { 258 path: `x.y/z"`, 259 modErr: `invalid char '"'`, 260 importErr: `malformed import path "x.y/z\"": invalid char '"'`, 261 fileErr: `malformed file path "x.y/z\"": invalid char '"'`, 262 }, { 263 path: `x.y/z#`, 264 modErr: `invalid char '#'`, 265 importErr: `malformed import path "x.y/z#": invalid char '#'`, 266 }, { 267 path: `x.y/z$`, 268 modErr: `invalid char '$'`, 269 importErr: `malformed import path "x.y/z$": invalid char '$'`, 270 }, { 271 path: `x.y/z%`, 272 modErr: `invalid char '%'`, 273 importErr: `malformed import path "x.y/z%": invalid char '%'`, 274 }, { 275 path: `x.y/z&`, 276 modErr: `invalid char '&'`, 277 importErr: `malformed import path "x.y/z&": invalid char '&'`, 278 }, { 279 path: `x.y/z'`, 280 modErr: `invalid char '\''`, 281 importErr: `malformed import path "x.y/z'": invalid char '\''`, 282 fileErr: `malformed file path "x.y/z'": invalid char '\''`, 283 }, { 284 path: `x.y/z(`, 285 modErr: `invalid char '('`, 286 importErr: `malformed import path "x.y/z(": invalid char '('`, 287 }, { 288 path: `x.y/z)`, 289 modErr: `invalid char ')'`, 290 importErr: `malformed import path "x.y/z)": invalid char ')'`, 291 }, { 292 path: `x.y/z*`, 293 modErr: `invalid char '*'`, 294 importErr: `malformed import path "x.y/z*": invalid char '*'`, 295 fileErr: `malformed file path "x.y/z*": invalid char '*'`, 296 }, { 297 path: `x.y/z++`, 298 modErr: `invalid char '+'`, 299 }, { 300 path: `x.y/z,`, 301 modErr: `invalid char ','`, 302 importErr: `malformed import path "x.y/z,": invalid char ','`, 303 }, { 304 path: `x.y/z-`, 305 modErr: `trailing '-' in path element`, 306 }, { 307 path: `x.y/z.t`, 308 }, { 309 path: `x.y/z/t`, 310 }, { 311 path: `x.y/z:`, 312 modErr: `invalid char ':'`, 313 fileErr: `malformed file path "x.y/z:": invalid char ':'`, 314 }, { 315 path: `x.y/z;`, 316 modErr: `invalid char ';'`, 317 importErr: `malformed import path "x.y/z;": invalid char ';'`, 318 fileErr: `malformed file path "x.y/z;": invalid char ';'`, 319 }, { 320 path: `x.y/z<`, 321 modErr: `invalid char '<'`, 322 importErr: `malformed import path "x.y/z<": invalid char '<'`, 323 fileErr: `malformed file path "x.y/z<": invalid char '<'`, 324 }, { 325 path: `x.y/z=`, 326 modErr: `invalid char '='`, 327 importErr: `malformed import path "x.y/z=": invalid char '='`, 328 }, { 329 path: `x.y/z>`, 330 modErr: `invalid char '>'`, 331 importErr: `malformed import path "x.y/z>": invalid char '>'`, 332 fileErr: `malformed file path "x.y/z>": invalid char '>'`, 333 }, { 334 path: `x.y/z?`, 335 modErr: `invalid char '?'`, 336 importErr: `malformed import path "x.y/z?": invalid char '?'`, 337 fileErr: `malformed file path "x.y/z?": invalid char '?'`, 338 }, { 339 path: `x.y/z@`, 340 modErr: `invalid char '@'`, 341 importErr: `malformed import path "x.y/z@": invalid char '@'`, 342 }, { 343 path: `x.y/z[`, 344 modErr: `invalid char '['`, 345 importErr: `malformed import path "x.y/z[": invalid char '['`, 346 }, { 347 path: `x.y/z\`, 348 modErr: `invalid char '\\'`, 349 importErr: `malformed import path "x.y/z\\": invalid char '\\'`, 350 fileErr: `malformed file path "x.y/z\\": invalid char '\\'`, 351 }, { 352 path: `x.y/z]`, 353 modErr: `invalid char ']'`, 354 importErr: `malformed import path "x.y/z]": invalid char ']'`, 355 }, { 356 path: `x.y/z^`, 357 modErr: `invalid char '^'`, 358 importErr: `malformed import path "x.y/z^": invalid char '^'`, 359 }, { 360 path: `x.y/z_`, 361 modErr: `trailing '_' in path element`, 362 }, { 363 path: "x.y/z`", 364 modErr: "invalid char '`'", 365 importErr: "malformed import path \"x.y/z`\": invalid char '`'", 366 fileErr: "malformed file path \"x.y/z`\": invalid char '`'", 367 }, { 368 path: `x.y/z{`, 369 modErr: `invalid char '{'`, 370 importErr: `malformed import path "x.y/z{": invalid char '{'`, 371 }, { 372 path: `x.y/z}`, 373 modErr: `invalid char '}'`, 374 importErr: `malformed import path "x.y/z}": invalid char '}'`, 375 }, { 376 path: `x.y/z~`, 377 modErr: `invalid char '~'`, 378 }, { 379 path: `x.y/x.foo`, 380 }, { 381 path: `x.y/aux.foo`, 382 modErr: `"aux" disallowed as path element component on Windows`, 383 importErr: `malformed import path "x.y/aux.foo": "aux" disallowed as path element component on Windows`, 384 fileErr: `malformed file path "x.y/aux.foo": "aux" disallowed as path element component on Windows`, 385 }, { 386 path: `x.y/prn`, 387 modErr: `"prn" disallowed as path element component on Windows`, 388 importErr: `malformed import path "x.y/prn": "prn" disallowed as path element component on Windows`, 389 fileErr: `malformed file path "x.y/prn": "prn" disallowed as path element component on Windows`, 390 }, { 391 path: `x.y/prn2`, 392 }, { 393 path: `x.y/com`, 394 }, { 395 path: `x.y/com1`, 396 modErr: `"com1" disallowed as path element component on Windows`, 397 importErr: `malformed import path "x.y/com1": "com1" disallowed as path element component on Windows`, 398 fileErr: `malformed file path "x.y/com1": "com1" disallowed as path element component on Windows`, 399 }, { 400 path: `x.y/com1.txt`, 401 modErr: `"com1" disallowed as path element component on Windows`, 402 importErr: `malformed import path "x.y/com1.txt": "com1" disallowed as path element component on Windows`, 403 fileErr: `malformed file path "x.y/com1.txt": "com1" disallowed as path element component on Windows`, 404 }, { 405 path: `x.y/calm1`, 406 }, { 407 path: `x.y/z~`, 408 modErr: `invalid char '~'`, 409 }, { 410 path: `x.y/z~0`, 411 modErr: `invalid char '~'`, 412 importErr: `malformed import path "x.y/z~0": trailing tilde and digits in path element`, 413 }, { 414 path: `x.y/z~09`, 415 modErr: `invalid char '~'`, 416 importErr: `malformed import path "x.y/z~09": trailing tilde and digits in path element`, 417 }, { 418 path: `x.y/z09`, 419 }, { 420 path: `x.y/z09~`, 421 modErr: `invalid char '~'`, 422 }, { 423 path: `x.y/z09~09z`, 424 modErr: `invalid char '~'`, 425 }, { 426 path: `x.y/z09~09z~09`, 427 modErr: `invalid char '~'`, 428 importErr: `malformed import path "x.y/z09~09z~09": trailing tilde and digits in path element`, 429 }, { 430 path: `github.com/!123/logrus`, 431 modErr: `invalid char '!'`, 432 importErr: `malformed import path "github.com/!123/logrus": invalid char '!'`, 433 }, { 434 path: `github.com/user/unicode/испытание`, 435 modErr: `invalid char 'и'`, 436 importErr: `malformed import path "github.com/user/unicode/испытание": invalid char 'и'`, 437 }, { 438 path: `../x`, 439 modErr: `invalid path element ".."`, 440 importErr: `malformed import path "../x": invalid path element ".."`, 441 fileErr: `malformed file path "../x": invalid path element ".."`, 442 }, { 443 path: `./y`, 444 modErr: `invalid path element "."`, 445 importErr: `malformed import path "./y": invalid path element "."`, 446 fileErr: `malformed file path "./y": invalid path element "."`, 447 }, { 448 path: `x:y`, 449 modErr: `invalid char ':'`, 450 fileErr: `malformed file path "x:y": invalid char ':'`, 451 }, { 452 path: `\temp\foo`, 453 modErr: `invalid char '\\'`, 454 importErr: `malformed import path "\\temp\\foo": invalid char '\\'`, 455 fileErr: `malformed file path "\\temp\\foo": invalid char '\\'`, 456 }, { 457 path: `.gitignore`, 458 modErr: `leading '.' in path element`, 459 }, { 460 path: `.github/ISSUE_TEMPLATE`, 461 modErr: `leading '.' in path element`, 462 }, { 463 path: `x☺y`, 464 modErr: `invalid char '☺'`, 465 importErr: `malformed import path "x☺y": invalid char '☺'`, 466 fileErr: `malformed file path "x☺y": invalid char '☺'`, 467 }, { 468 path: `bar.com/foo.`, 469 modErr: `trailing '.' in path element`, 470 importErr: `malformed import path "bar.com/foo.": trailing dot in path element`, 471 fileErr: `malformed file path "bar.com/foo.": trailing dot in path element`, 472 }, { 473 path: `bar.com/_foo`, 474 modErr: `leading '_' in path element`, 475 }, { 476 path: `bar.com/foo___x`, 477 modErr: `path does not conform to OCI repository name restrictions; see https://github.com/opencontainers/distribution-spec/blob/HEAD/spec.md#pulling-manifests`, 478 }, { 479 path: `bar.com/Sushi`, 480 modErr: `invalid char 'S'`, 481 }, { 482 path: `rsc io/quote`, 483 modErr: `invalid char ' '`, 484 importErr: `malformed import path "rsc io/quote": invalid char ' '`, 485 }, { 486 path: `foo.com@v0`, 487 modErr: `module path inappropriately contains major version`, 488 }, { 489 path: `foo.com/bar/baz`, 490 }} 491 492 func TestCheckPathWithoutVersion(t *testing.T) { 493 cuetest.Run(t, checkPathTests, func(t *cuetest.T, test *checkPathTest) { 494 t.Logf("path: `%s`", test.path) 495 t.Equal(errStr(CheckPathWithoutVersion(test.path)), test.modErr) 496 }) 497 } 498 499 func TestCheckImportPath(t *testing.T) { 500 cuetest.Run(t, checkPathTests, func(t *cuetest.T, test *checkPathTest) { 501 t.Logf("path: `%s`", test.path) 502 t.Equal(errStr(CheckImportPath(test.path)), test.importErr) 503 }) 504 } 505 506 func TestCheckFilePath(t *testing.T) { 507 cuetest.Run(t, checkPathTests, func(t *cuetest.T, test *checkPathTest) { 508 t.Logf("path: `%s`", test.path) 509 t.Equal(errStr(CheckFilePath(test.path)), test.fileErr) 510 }) 511 } 512 513 var newVersionTests = []struct { 514 path, vers string 515 wantError string 516 wantPath string 517 wantBasePath string 518 }{{ 519 path: "github.com/foo/bar@v0", 520 vers: "v0.1.2", 521 wantPath: "github.com/foo/bar@v0", 522 wantBasePath: "github.com/foo/bar", 523 }, { 524 path: "github.com/foo/bar", 525 vers: "v3.1.2", 526 wantPath: "github.com/foo/bar@v3", 527 wantBasePath: "github.com/foo/bar", 528 }, { 529 path: "github.com/foo/bar@v1", 530 vers: "", 531 wantPath: "github.com/foo/bar@v1", 532 wantBasePath: "github.com/foo/bar", 533 }, { 534 path: "github.com/foo/bar@v1", 535 vers: "v3.1.2", 536 wantError: `mismatched major version suffix in "github.com/foo/bar@v1" \(version v3\.1\.2\)`, 537 }, { 538 path: "github.com/foo/bar@v1", 539 vers: "v3.1", 540 wantError: `version "v3.1" \(of module "github.com/foo/bar@v1"\) is not canonical`, 541 }, { 542 path: "github.com/foo/bar@v1", 543 vers: "v3.10.4+build", 544 wantError: `version "v3.10.4\+build" \(of module "github.com/foo/bar@v1"\) is not canonical`, 545 }, { 546 path: "something/bad@v1", 547 vers: "v1.2.3", 548 wantError: `malformed module path "something/bad@v1": missing dot in first path element`, 549 }, { 550 path: "foo.com/bar", 551 vers: "", 552 wantError: `path "foo.com/bar" has no major version`, 553 }, { 554 path: "x.com", 555 vers: "bad", 556 wantError: `version "bad" \(of module "x.com"\) is not well formed`, 557 }, { 558 path: "local", 559 vers: "", 560 wantPath: "local", 561 wantBasePath: "local", 562 }, { 563 path: "local", 564 vers: "v0.1.2", 565 wantError: `module 'local' cannot have version`, 566 }, { 567 path: "local@v1", 568 vers: "", 569 wantError: `module 'local' cannot have version`, 570 }} 571 572 func TestNewVersion(t *testing.T) { 573 for _, test := range newVersionTests { 574 t.Run(test.path+"@"+test.vers, func(t *testing.T) { 575 v, err := NewVersion(test.path, test.vers) 576 if test.wantError != "" { 577 qt.Assert(t, qt.ErrorMatches(err, test.wantError)) 578 return 579 } 580 qt.Assert(t, qt.IsNil(err)) 581 qt.Assert(t, qt.Equals(v.Path(), test.wantPath)) 582 qt.Assert(t, qt.Equals(v.BasePath(), test.wantBasePath)) 583 qt.Assert(t, qt.Equals(v.Version(), test.vers)) 584 }) 585 } 586 } 587 588 var parseVersionTests = []struct { 589 s string 590 wantError string 591 }{{ 592 s: "github.com/foo/bar@v0.1.2", 593 }} 594 595 func TestParseVersion(t *testing.T) { 596 for _, test := range parseVersionTests { 597 t.Run(test.s, func(t *testing.T) { 598 v, err := ParseVersion(test.s) 599 if test.wantError != "" { 600 qt.Assert(t, qt.ErrorMatches(err, test.wantError)) 601 return 602 } 603 qt.Assert(t, qt.IsNil(err)) 604 qt.Assert(t, qt.Equals(v.String(), test.s)) 605 }) 606 } 607 } 608 609 var escapeVersionTests = []struct { 610 v string 611 esc string // empty means same as path 612 }{ 613 {v: "v1.2.3-alpha"}, 614 {v: "v3"}, 615 {v: "v2.3.1-ABcD", esc: "v2.3.1-!a!bc!d"}, 616 } 617 618 func TestEscapeVersion(t *testing.T) { 619 for _, tt := range escapeVersionTests { 620 esc, err := EscapeVersion(tt.v) 621 if err != nil { 622 t.Errorf("EscapeVersion(%q): unexpected error: %v", tt.v, err) 623 continue 624 } 625 want := tt.esc 626 if want == "" { 627 want = tt.v 628 } 629 if esc != want { 630 t.Errorf("EscapeVersion(%q) = %q, want %q", tt.v, esc, want) 631 } 632 } 633 } 634 635 func TestEscapePath(t *testing.T) { 636 // Check invalid paths. 637 for _, tt := range checkPathTests { 638 if tt.modErr != "" { 639 _, err := EscapePath(tt.path) 640 if err == nil { 641 t.Errorf("EscapePath(%q): succeeded, want error (invalid path)", tt.path) 642 } 643 } 644 } 645 path := "foo.com/bar" 646 esc, err := EscapePath(path) 647 if err != nil { 648 t.Fatal(err) 649 } 650 if esc != path { 651 t.Fatalf("EscapePath(%q) = %q, want %q", path, esc, path) 652 } 653 } 654 655 var parseImportPathTests = []struct { 656 testName string 657 path string 658 want ImportPath 659 wantCanonical string 660 }{{ 661 testName: "StdlibLikeWithSlash", 662 path: "stdlib/path", 663 want: ImportPath{ 664 Path: "stdlib/path", 665 Qualifier: "path", 666 }, 667 }, { 668 testName: "StdlibLikeNoSlash", 669 path: "math", 670 want: ImportPath{ 671 Path: "math", 672 Qualifier: "math", 673 }, 674 }, { 675 testName: "StdlibLikeExplicitQualifier", 676 path: "stdlib/path:other", 677 want: ImportPath{ 678 Path: "stdlib/path", 679 ExplicitQualifier: true, 680 Qualifier: "other", 681 }, 682 }, { 683 testName: "StdlibLikeExplicitQualifierNoSlash", 684 path: "math:other", 685 want: ImportPath{ 686 Path: "math", 687 ExplicitQualifier: true, 688 Qualifier: "other", 689 }, 690 }, { 691 testName: "WithMajorVersion", 692 path: "foo.com/bar@v0", 693 want: ImportPath{ 694 Path: "foo.com/bar", 695 Version: "v0", 696 Qualifier: "bar", 697 }, 698 }, { 699 testName: "WithMajorVersionNoSlash", 700 path: "main.test@v0", 701 want: ImportPath{ 702 Path: "main.test", 703 Version: "v0", 704 Qualifier: "", 705 }, 706 }, { 707 testName: "WithMajorVersionAndExplicitQualifier", 708 path: "foo.com/bar@v0:other", 709 want: ImportPath{ 710 Path: "foo.com/bar", 711 Version: "v0", 712 ExplicitQualifier: true, 713 Qualifier: "other", 714 }, 715 }, { 716 testName: "WithMajorVersionAndNoQualifier", 717 path: "foo.com/bar@v0", 718 want: ImportPath{ 719 Path: "foo.com/bar", 720 Version: "v0", 721 Qualifier: "bar", 722 }, 723 }, { 724 testName: "WithRedundantQualifier", 725 path: "foo.com/bar@v0:bar", 726 want: ImportPath{ 727 Path: "foo.com/bar", 728 Version: "v0", 729 ExplicitQualifier: true, 730 Qualifier: "bar", 731 }, 732 wantCanonical: "foo.com/bar@v0", 733 }, { 734 testName: "WithPattern", 735 path: "foo.com/bar/.../blah", 736 want: ImportPath{ 737 Path: "foo.com/bar/.../blah", 738 Version: "", 739 ExplicitQualifier: false, 740 Qualifier: "blah", 741 }, 742 wantCanonical: "foo.com/bar/.../blah", 743 }, { 744 testName: "WithPatternAtEnd", 745 path: "foo.com/bar/...", 746 want: ImportPath{ 747 Path: "foo.com/bar/...", 748 Version: "", 749 ExplicitQualifier: false, 750 Qualifier: "", 751 }, 752 wantCanonical: "foo.com/bar/...", 753 }, { 754 testName: "WithUnderscoreLastElement", 755 path: "foo.com/bar/_foo", 756 want: ImportPath{ 757 Path: "foo.com/bar/_foo", 758 Version: "", 759 ExplicitQualifier: false, 760 Qualifier: "_foo", 761 }, 762 wantCanonical: "foo.com/bar/_foo", 763 }, { 764 testName: "WithHashLastElement", 765 path: "foo.com/bar/#foo", 766 want: ImportPath{ 767 Path: "foo.com/bar/#foo", 768 Version: "", 769 ExplicitQualifier: false, 770 Qualifier: "", 771 }, 772 wantCanonical: "foo.com/bar/#foo", 773 }} 774 775 func TestParseImportPath(t *testing.T) { 776 for _, test := range parseImportPathTests { 777 t.Run(test.testName, func(t *testing.T) { 778 parts := ParseImportPath(test.path) 779 qt.Assert(t, qt.DeepEquals(parts, test.want)) 780 qt.Assert(t, qt.Equals(parts.String(), test.path)) 781 if test.wantCanonical == "" { 782 test.wantCanonical = test.path 783 } 784 qt.Assert(t, qt.Equals(parts.Canonical().String(), test.wantCanonical)) 785 }) 786 } 787 } 788 789 func TestImportPathStringAddsQualifier(t *testing.T) { 790 ip := ImportPath{ 791 Path: "foo.com/bar", 792 Version: "v0", 793 Qualifier: "baz", 794 } 795 qt.Assert(t, qt.Equals(ip.String(), "foo.com/bar@v0:baz")) 796 } 797 798 func TestImportPathStringAddsQualifierWhenNoVersion(t *testing.T) { 799 ip := ImportPath{ 800 Path: "foo.com/bar", 801 Qualifier: "baz", 802 } 803 qt.Assert(t, qt.Equals(ip.String(), "foo.com/bar:baz")) 804 } 805 806 func errStr(err error) string { 807 if err == nil { 808 return "" 809 } 810 if e := err.Error(); e != "" { 811 return e 812 } 813 panic("non-nil error with empty string") 814 }