github.com/sdboyer/gps@v0.16.3/deduce_test.go (about) 1 package gps 2 3 import ( 4 "bytes" 5 "context" 6 "errors" 7 "fmt" 8 "net/url" 9 "reflect" 10 "testing" 11 ) 12 13 type pathDeductionFixture struct { 14 in string 15 root string 16 rerr error 17 mb maybeSource 18 srcerr error 19 } 20 21 // helper func to generate testing *url.URLs, panicking on err 22 func mkurl(s string) (u *url.URL) { 23 var err error 24 u, err = url.Parse(s) 25 if err != nil { 26 panic(fmt.Sprint("string is not a valid URL:", s)) 27 } 28 return 29 } 30 31 var pathDeductionFixtures = map[string][]pathDeductionFixture{ 32 "github": []pathDeductionFixture{ 33 { 34 in: "github.com/sdboyer/gps", 35 root: "github.com/sdboyer/gps", 36 mb: maybeSources{ 37 maybeGitSource{url: mkurl("https://github.com/sdboyer/gps")}, 38 maybeGitSource{url: mkurl("ssh://git@github.com/sdboyer/gps")}, 39 maybeGitSource{url: mkurl("git://github.com/sdboyer/gps")}, 40 maybeGitSource{url: mkurl("http://github.com/sdboyer/gps")}, 41 }, 42 }, 43 { 44 in: "github.com/sdboyer/gps/foo", 45 root: "github.com/sdboyer/gps", 46 mb: maybeSources{ 47 maybeGitSource{url: mkurl("https://github.com/sdboyer/gps")}, 48 maybeGitSource{url: mkurl("ssh://git@github.com/sdboyer/gps")}, 49 maybeGitSource{url: mkurl("git://github.com/sdboyer/gps")}, 50 maybeGitSource{url: mkurl("http://github.com/sdboyer/gps")}, 51 }, 52 }, 53 { 54 // TODO(sdboyer) is this a problem for enforcing uniqueness? do we 55 // need to collapse these extensions? 56 in: "github.com/sdboyer/gps.git/foo", 57 root: "github.com/sdboyer/gps.git", 58 mb: maybeSources{ 59 maybeGitSource{url: mkurl("https://github.com/sdboyer/gps.git")}, 60 maybeGitSource{url: mkurl("ssh://git@github.com/sdboyer/gps.git")}, 61 maybeGitSource{url: mkurl("git://github.com/sdboyer/gps.git")}, 62 maybeGitSource{url: mkurl("http://github.com/sdboyer/gps.git")}, 63 }, 64 }, 65 { 66 in: "git@github.com:sdboyer/gps", 67 root: "github.com/sdboyer/gps", 68 mb: maybeGitSource{url: mkurl("ssh://git@github.com/sdboyer/gps")}, 69 }, 70 { 71 in: "https://github.com/sdboyer/gps", 72 root: "github.com/sdboyer/gps", 73 mb: maybeGitSource{url: mkurl("https://github.com/sdboyer/gps")}, 74 }, 75 { 76 in: "https://github.com/sdboyer/gps/foo/bar", 77 root: "github.com/sdboyer/gps", 78 mb: maybeGitSource{url: mkurl("https://github.com/sdboyer/gps")}, 79 }, 80 { 81 in: "github.com/sdboyer-/gps/foo", 82 root: "github.com/sdboyer-/gps", 83 mb: maybeSources{ 84 maybeGitSource{url: mkurl("https://github.com/sdboyer-/gps")}, 85 maybeGitSource{url: mkurl("ssh://git@github.com/sdboyer-/gps")}, 86 maybeGitSource{url: mkurl("git://github.com/sdboyer-/gps")}, 87 maybeGitSource{url: mkurl("http://github.com/sdboyer-/gps")}, 88 }, 89 }, 90 { 91 in: "github.com/a/gps/foo", 92 root: "github.com/a/gps", 93 mb: maybeSources{ 94 maybeGitSource{url: mkurl("https://github.com/a/gps")}, 95 maybeGitSource{url: mkurl("ssh://git@github.com/a/gps")}, 96 maybeGitSource{url: mkurl("git://github.com/a/gps")}, 97 maybeGitSource{url: mkurl("http://github.com/a/gps")}, 98 }, 99 }, 100 // some invalid github username patterns 101 { 102 in: "github.com/-sdboyer/gps/foo", 103 rerr: errors.New("github.com/-sdboyer/gps/foo is not a valid path for a source on github.com"), 104 }, 105 { 106 in: "github.com/sdbo.yer/gps/foo", 107 rerr: errors.New("github.com/sdbo.yer/gps/foo is not a valid path for a source on github.com"), 108 }, 109 { 110 in: "github.com/sdbo_yer/gps/foo", 111 rerr: errors.New("github.com/sdbo_yer/gps/foo is not a valid path for a source on github.com"), 112 }, 113 // Regression - gh does allow two-letter usernames 114 { 115 in: "github.com/kr/pretty", 116 root: "github.com/kr/pretty", 117 mb: maybeSources{ 118 maybeGitSource{url: mkurl("https://github.com/kr/pretty")}, 119 maybeGitSource{url: mkurl("ssh://git@github.com/kr/pretty")}, 120 maybeGitSource{url: mkurl("git://github.com/kr/pretty")}, 121 maybeGitSource{url: mkurl("http://github.com/kr/pretty")}, 122 }, 123 }, 124 }, 125 "gopkg.in": []pathDeductionFixture{ 126 { 127 in: "gopkg.in/sdboyer/gps.v0", 128 root: "gopkg.in/sdboyer/gps.v0", 129 mb: maybeSources{ 130 maybeGopkginSource{opath: "gopkg.in/sdboyer/gps.v0", url: mkurl("https://github.com/sdboyer/gps"), major: 0}, 131 maybeGopkginSource{opath: "gopkg.in/sdboyer/gps.v0", url: mkurl("ssh://git@github.com/sdboyer/gps"), major: 0}, 132 maybeGopkginSource{opath: "gopkg.in/sdboyer/gps.v0", url: mkurl("git://github.com/sdboyer/gps"), major: 0}, 133 maybeGopkginSource{opath: "gopkg.in/sdboyer/gps.v0", url: mkurl("http://github.com/sdboyer/gps"), major: 0}, 134 }, 135 }, 136 { 137 in: "gopkg.in/sdboyer/gps.v0/foo", 138 root: "gopkg.in/sdboyer/gps.v0", 139 mb: maybeSources{ 140 maybeGopkginSource{opath: "gopkg.in/sdboyer/gps.v0", url: mkurl("https://github.com/sdboyer/gps"), major: 0}, 141 maybeGopkginSource{opath: "gopkg.in/sdboyer/gps.v0", url: mkurl("ssh://git@github.com/sdboyer/gps"), major: 0}, 142 maybeGopkginSource{opath: "gopkg.in/sdboyer/gps.v0", url: mkurl("git://github.com/sdboyer/gps"), major: 0}, 143 maybeGopkginSource{opath: "gopkg.in/sdboyer/gps.v0", url: mkurl("http://github.com/sdboyer/gps"), major: 0}, 144 }, 145 }, 146 { 147 in: "gopkg.in/sdboyer/gps.v1/foo/bar", 148 root: "gopkg.in/sdboyer/gps.v1", 149 mb: maybeSources{ 150 maybeGopkginSource{opath: "gopkg.in/sdboyer/gps.v1", url: mkurl("https://github.com/sdboyer/gps"), major: 1}, 151 maybeGopkginSource{opath: "gopkg.in/sdboyer/gps.v1", url: mkurl("ssh://git@github.com/sdboyer/gps"), major: 1}, 152 maybeGopkginSource{opath: "gopkg.in/sdboyer/gps.v1", url: mkurl("git://github.com/sdboyer/gps"), major: 1}, 153 maybeGopkginSource{opath: "gopkg.in/sdboyer/gps.v1", url: mkurl("http://github.com/sdboyer/gps"), major: 1}, 154 }, 155 }, 156 { 157 in: "gopkg.in/yaml.v1", 158 root: "gopkg.in/yaml.v1", 159 mb: maybeSources{ 160 maybeGopkginSource{opath: "gopkg.in/yaml.v1", url: mkurl("https://github.com/go-yaml/yaml"), major: 1}, 161 maybeGopkginSource{opath: "gopkg.in/yaml.v1", url: mkurl("ssh://git@github.com/go-yaml/yaml"), major: 1}, 162 maybeGopkginSource{opath: "gopkg.in/yaml.v1", url: mkurl("git://github.com/go-yaml/yaml"), major: 1}, 163 maybeGopkginSource{opath: "gopkg.in/yaml.v1", url: mkurl("http://github.com/go-yaml/yaml"), major: 1}, 164 }, 165 }, 166 { 167 in: "gopkg.in/yaml.v1/foo/bar", 168 root: "gopkg.in/yaml.v1", 169 mb: maybeSources{ 170 maybeGopkginSource{opath: "gopkg.in/yaml.v1", url: mkurl("https://github.com/go-yaml/yaml"), major: 1}, 171 maybeGopkginSource{opath: "gopkg.in/yaml.v1", url: mkurl("ssh://git@github.com/go-yaml/yaml"), major: 1}, 172 maybeGopkginSource{opath: "gopkg.in/yaml.v1", url: mkurl("git://github.com/go-yaml/yaml"), major: 1}, 173 maybeGopkginSource{opath: "gopkg.in/yaml.v1", url: mkurl("http://github.com/go-yaml/yaml"), major: 1}, 174 }, 175 }, 176 { 177 in: "gopkg.in/inf.v0", 178 root: "gopkg.in/inf.v0", 179 mb: maybeSources{ 180 maybeGopkginSource{opath: "gopkg.in/inf.v0", url: mkurl("https://github.com/go-inf/inf"), major: 0}, 181 maybeGopkginSource{opath: "gopkg.in/inf.v0", url: mkurl("ssh://git@github.com/go-inf/inf"), major: 0}, 182 maybeGopkginSource{opath: "gopkg.in/inf.v0", url: mkurl("git://github.com/go-inf/inf"), major: 0}, 183 maybeGopkginSource{opath: "gopkg.in/inf.v0", url: mkurl("http://github.com/go-inf/inf"), major: 0}, 184 }, 185 }, 186 { 187 // gopkg.in only allows specifying major version in import path 188 in: "gopkg.in/yaml.v1.2", 189 rerr: errors.New("gopkg.in/yaml.v1.2 is not a valid import path; gopkg.in only allows major versions (\"v1\" instead of \"v1.2\")"), 190 }, 191 }, 192 "jazz": []pathDeductionFixture{ 193 // IBM hub devops services - fixtures borrowed from go get 194 { 195 in: "hub.jazz.net/git/user1/pkgname", 196 root: "hub.jazz.net/git/user1/pkgname", 197 mb: maybeGitSource{url: mkurl("https://hub.jazz.net/git/user1/pkgname")}, 198 }, 199 { 200 in: "hub.jazz.net/git/user1/pkgname/submodule/submodule/submodule", 201 root: "hub.jazz.net/git/user1/pkgname", 202 mb: maybeGitSource{url: mkurl("https://hub.jazz.net/git/user1/pkgname")}, 203 }, 204 { 205 in: "hub.jazz.net/someotherprefix", 206 rerr: errors.New("hub.jazz.net/someotherprefix is not a valid path for a source on hub.jazz.net"), 207 }, 208 { 209 in: "hub.jazz.net/someotherprefix/user1/packagename", 210 rerr: errors.New("hub.jazz.net/someotherprefix/user1/packagename is not a valid path for a source on hub.jazz.net"), 211 }, 212 // Spaces are not valid in user names or package names 213 { 214 in: "hub.jazz.net/git/User 1/pkgname", 215 rerr: errors.New("hub.jazz.net/git/User 1/pkgname is not a valid path for a source on hub.jazz.net"), 216 }, 217 { 218 in: "hub.jazz.net/git/user1/pkg name", 219 rerr: errors.New("hub.jazz.net/git/user1/pkg name is not a valid path for a source on hub.jazz.net"), 220 }, 221 // Dots are not valid in user names 222 { 223 in: "hub.jazz.net/git/user.1/pkgname", 224 rerr: errors.New("hub.jazz.net/git/user.1/pkgname is not a valid path for a source on hub.jazz.net"), 225 }, 226 { 227 in: "hub.jazz.net/git/user1/pkg.name", 228 root: "hub.jazz.net/git/user1/pkg.name", 229 mb: maybeGitSource{url: mkurl("https://hub.jazz.net/git/user1/pkg.name")}, 230 }, 231 // User names cannot have uppercase letters 232 { 233 in: "hub.jazz.net/git/USER/pkgname", 234 rerr: errors.New("hub.jazz.net/git/USER/pkgname is not a valid path for a source on hub.jazz.net"), 235 }, 236 }, 237 "bitbucket": []pathDeductionFixture{ 238 { 239 in: "bitbucket.org/sdboyer/reporoot", 240 root: "bitbucket.org/sdboyer/reporoot", 241 mb: maybeSources{ 242 maybeHgSource{url: mkurl("https://bitbucket.org/sdboyer/reporoot")}, 243 maybeHgSource{url: mkurl("ssh://hg@bitbucket.org/sdboyer/reporoot")}, 244 maybeHgSource{url: mkurl("http://bitbucket.org/sdboyer/reporoot")}, 245 maybeGitSource{url: mkurl("https://bitbucket.org/sdboyer/reporoot")}, 246 maybeGitSource{url: mkurl("ssh://git@bitbucket.org/sdboyer/reporoot")}, 247 maybeGitSource{url: mkurl("git://bitbucket.org/sdboyer/reporoot")}, 248 maybeGitSource{url: mkurl("http://bitbucket.org/sdboyer/reporoot")}, 249 }, 250 }, 251 { 252 in: "bitbucket.org/sdboyer/reporoot/foo/bar", 253 root: "bitbucket.org/sdboyer/reporoot", 254 mb: maybeSources{ 255 maybeHgSource{url: mkurl("https://bitbucket.org/sdboyer/reporoot")}, 256 maybeHgSource{url: mkurl("ssh://hg@bitbucket.org/sdboyer/reporoot")}, 257 maybeHgSource{url: mkurl("http://bitbucket.org/sdboyer/reporoot")}, 258 maybeGitSource{url: mkurl("https://bitbucket.org/sdboyer/reporoot")}, 259 maybeGitSource{url: mkurl("ssh://git@bitbucket.org/sdboyer/reporoot")}, 260 maybeGitSource{url: mkurl("git://bitbucket.org/sdboyer/reporoot")}, 261 maybeGitSource{url: mkurl("http://bitbucket.org/sdboyer/reporoot")}, 262 }, 263 }, 264 { 265 in: "https://bitbucket.org/sdboyer/reporoot/foo/bar", 266 root: "bitbucket.org/sdboyer/reporoot", 267 mb: maybeSources{ 268 maybeHgSource{url: mkurl("https://bitbucket.org/sdboyer/reporoot")}, 269 maybeGitSource{url: mkurl("https://bitbucket.org/sdboyer/reporoot")}, 270 }, 271 }, 272 // Less standard behaviors possible due to the hg/git ambiguity 273 { 274 in: "bitbucket.org/sdboyer/reporoot.git", 275 root: "bitbucket.org/sdboyer/reporoot.git", 276 mb: maybeSources{ 277 maybeGitSource{url: mkurl("https://bitbucket.org/sdboyer/reporoot.git")}, 278 maybeGitSource{url: mkurl("ssh://git@bitbucket.org/sdboyer/reporoot.git")}, 279 maybeGitSource{url: mkurl("git://bitbucket.org/sdboyer/reporoot.git")}, 280 maybeGitSource{url: mkurl("http://bitbucket.org/sdboyer/reporoot.git")}, 281 }, 282 }, 283 { 284 in: "git@bitbucket.org:sdboyer/reporoot.git", 285 root: "bitbucket.org/sdboyer/reporoot.git", 286 mb: maybeGitSource{url: mkurl("ssh://git@bitbucket.org/sdboyer/reporoot.git")}, 287 }, 288 { 289 in: "bitbucket.org/sdboyer/reporoot.hg", 290 root: "bitbucket.org/sdboyer/reporoot.hg", 291 mb: maybeSources{ 292 maybeHgSource{url: mkurl("https://bitbucket.org/sdboyer/reporoot.hg")}, 293 maybeHgSource{url: mkurl("ssh://hg@bitbucket.org/sdboyer/reporoot.hg")}, 294 maybeHgSource{url: mkurl("http://bitbucket.org/sdboyer/reporoot.hg")}, 295 }, 296 }, 297 { 298 in: "hg@bitbucket.org:sdboyer/reporoot", 299 root: "bitbucket.org/sdboyer/reporoot", 300 mb: maybeHgSource{url: mkurl("ssh://hg@bitbucket.org/sdboyer/reporoot")}, 301 }, 302 { 303 in: "git://bitbucket.org/sdboyer/reporoot.hg", 304 root: "bitbucket.org/sdboyer/reporoot.hg", 305 srcerr: errors.New("git is not a valid scheme for accessing an hg repository"), 306 }, 307 }, 308 "launchpad": []pathDeductionFixture{ 309 // tests for launchpad, mostly bazaar 310 // TODO(sdboyer) need more tests to deal w/launchpad's oddities 311 { 312 in: "launchpad.net/govcstestbzrrepo", 313 root: "launchpad.net/govcstestbzrrepo", 314 mb: maybeSources{ 315 maybeBzrSource{url: mkurl("https://launchpad.net/govcstestbzrrepo")}, 316 maybeBzrSource{url: mkurl("bzr+ssh://launchpad.net/govcstestbzrrepo")}, 317 maybeBzrSource{url: mkurl("bzr://launchpad.net/govcstestbzrrepo")}, 318 maybeBzrSource{url: mkurl("http://launchpad.net/govcstestbzrrepo")}, 319 }, 320 }, 321 { 322 in: "launchpad.net/govcstestbzrrepo/foo/bar", 323 root: "launchpad.net/govcstestbzrrepo", 324 mb: maybeSources{ 325 maybeBzrSource{url: mkurl("https://launchpad.net/govcstestbzrrepo")}, 326 maybeBzrSource{url: mkurl("bzr+ssh://launchpad.net/govcstestbzrrepo")}, 327 maybeBzrSource{url: mkurl("bzr://launchpad.net/govcstestbzrrepo")}, 328 maybeBzrSource{url: mkurl("http://launchpad.net/govcstestbzrrepo")}, 329 }, 330 }, 331 { 332 in: "launchpad.net/repo root", 333 rerr: errors.New("launchpad.net/repo root is not a valid path for a source on launchpad.net"), 334 }, 335 }, 336 "git.launchpad": []pathDeductionFixture{ 337 { 338 in: "git.launchpad.net/reporoot", 339 root: "git.launchpad.net/reporoot", 340 mb: maybeSources{ 341 maybeGitSource{url: mkurl("https://git.launchpad.net/reporoot")}, 342 maybeGitSource{url: mkurl("ssh://git.launchpad.net/reporoot")}, 343 maybeGitSource{url: mkurl("git://git.launchpad.net/reporoot")}, 344 maybeGitSource{url: mkurl("http://git.launchpad.net/reporoot")}, 345 }, 346 }, 347 { 348 in: "git.launchpad.net/reporoot/foo/bar", 349 root: "git.launchpad.net/reporoot", 350 mb: maybeSources{ 351 maybeGitSource{url: mkurl("https://git.launchpad.net/reporoot")}, 352 maybeGitSource{url: mkurl("ssh://git.launchpad.net/reporoot")}, 353 maybeGitSource{url: mkurl("git://git.launchpad.net/reporoot")}, 354 maybeGitSource{url: mkurl("http://git.launchpad.net/reporoot")}, 355 }, 356 }, 357 { 358 in: "git.launchpad.net/repo root", 359 rerr: errors.New("git.launchpad.net/repo root is not a valid path for a source on launchpad.net"), 360 }, 361 }, 362 "apache": []pathDeductionFixture{ 363 { 364 in: "git.apache.org/package-name.git", 365 root: "git.apache.org/package-name.git", 366 mb: maybeSources{ 367 maybeGitSource{url: mkurl("https://git.apache.org/package-name.git")}, 368 maybeGitSource{url: mkurl("ssh://git.apache.org/package-name.git")}, 369 maybeGitSource{url: mkurl("git://git.apache.org/package-name.git")}, 370 maybeGitSource{url: mkurl("http://git.apache.org/package-name.git")}, 371 }, 372 }, 373 { 374 in: "git.apache.org/package-name.git/foo/bar", 375 root: "git.apache.org/package-name.git", 376 mb: maybeSources{ 377 maybeGitSource{url: mkurl("https://git.apache.org/package-name.git")}, 378 maybeGitSource{url: mkurl("ssh://git.apache.org/package-name.git")}, 379 maybeGitSource{url: mkurl("git://git.apache.org/package-name.git")}, 380 maybeGitSource{url: mkurl("http://git.apache.org/package-name.git")}, 381 }, 382 }, 383 }, 384 "vcsext": []pathDeductionFixture{ 385 // VCS extension-based syntax 386 { 387 in: "foobar.com/baz.git", 388 root: "foobar.com/baz.git", 389 mb: maybeSources{ 390 maybeGitSource{url: mkurl("https://foobar.com/baz.git")}, 391 maybeGitSource{url: mkurl("ssh://foobar.com/baz.git")}, 392 maybeGitSource{url: mkurl("git://foobar.com/baz.git")}, 393 maybeGitSource{url: mkurl("http://foobar.com/baz.git")}, 394 }, 395 }, 396 { 397 in: "foobar.com/baz.git/extra/path", 398 root: "foobar.com/baz.git", 399 mb: maybeSources{ 400 maybeGitSource{url: mkurl("https://foobar.com/baz.git")}, 401 maybeGitSource{url: mkurl("ssh://foobar.com/baz.git")}, 402 maybeGitSource{url: mkurl("git://foobar.com/baz.git")}, 403 maybeGitSource{url: mkurl("http://foobar.com/baz.git")}, 404 }, 405 }, 406 { 407 in: "foobar.com/baz.bzr", 408 root: "foobar.com/baz.bzr", 409 mb: maybeSources{ 410 maybeBzrSource{url: mkurl("https://foobar.com/baz.bzr")}, 411 maybeBzrSource{url: mkurl("bzr+ssh://foobar.com/baz.bzr")}, 412 maybeBzrSource{url: mkurl("bzr://foobar.com/baz.bzr")}, 413 maybeBzrSource{url: mkurl("http://foobar.com/baz.bzr")}, 414 }, 415 }, 416 { 417 in: "foo-bar.com/baz.hg", 418 root: "foo-bar.com/baz.hg", 419 mb: maybeSources{ 420 maybeHgSource{url: mkurl("https://foo-bar.com/baz.hg")}, 421 maybeHgSource{url: mkurl("ssh://foo-bar.com/baz.hg")}, 422 maybeHgSource{url: mkurl("http://foo-bar.com/baz.hg")}, 423 }, 424 }, 425 { 426 in: "git@foobar.com:baz.git", 427 root: "foobar.com/baz.git", 428 mb: maybeGitSource{url: mkurl("ssh://git@foobar.com/baz.git")}, 429 }, 430 { 431 in: "bzr+ssh://foobar.com/baz.bzr", 432 root: "foobar.com/baz.bzr", 433 mb: maybeBzrSource{url: mkurl("bzr+ssh://foobar.com/baz.bzr")}, 434 }, 435 { 436 in: "ssh://foobar.com/baz.bzr", 437 root: "foobar.com/baz.bzr", 438 mb: maybeBzrSource{url: mkurl("ssh://foobar.com/baz.bzr")}, 439 }, 440 { 441 in: "https://foobar.com/baz.hg", 442 root: "foobar.com/baz.hg", 443 mb: maybeHgSource{url: mkurl("https://foobar.com/baz.hg")}, 444 }, 445 { 446 in: "git://foobar.com/baz.hg", 447 root: "foobar.com/baz.hg", 448 srcerr: errors.New("git is not a valid scheme for accessing hg repositories (path foobar.com/baz.hg)"), 449 }, 450 // who knows why anyone would do this, but having a second vcs ext 451 // shouldn't throw us off - only the first one counts 452 { 453 in: "foobar.com/baz.git/quark/quizzle.bzr/quorum", 454 root: "foobar.com/baz.git", 455 mb: maybeSources{ 456 maybeGitSource{url: mkurl("https://foobar.com/baz.git")}, 457 maybeGitSource{url: mkurl("ssh://foobar.com/baz.git")}, 458 maybeGitSource{url: mkurl("git://foobar.com/baz.git")}, 459 maybeGitSource{url: mkurl("http://foobar.com/baz.git")}, 460 }, 461 }, 462 }, 463 "vanity": []pathDeductionFixture{ 464 // Vanity imports 465 { 466 in: "golang.org/x/exp", 467 root: "golang.org/x/exp", 468 mb: maybeGitSource{url: mkurl("https://go.googlesource.com/exp")}, 469 }, 470 { 471 in: "golang.org/x/exp/inotify", 472 root: "golang.org/x/exp", 473 mb: maybeGitSource{url: mkurl("https://go.googlesource.com/exp")}, 474 }, 475 { 476 in: "golang.org/x/net/html", 477 root: "golang.org/x/net", 478 mb: maybeGitSource{url: mkurl("https://go.googlesource.com/net")}, 479 }, 480 }, 481 } 482 483 func TestDeduceFromPath(t *testing.T) { 484 do := func(typ string, fixtures []pathDeductionFixture, t *testing.T) { 485 t.Run(typ, func(t *testing.T) { 486 t.Parallel() 487 488 var deducer pathDeducer 489 switch typ { 490 case "github": 491 deducer = githubDeducer{regexp: ghRegex} 492 case "gopkg.in": 493 deducer = gopkginDeducer{regexp: gpinNewRegex} 494 case "jazz": 495 deducer = jazzDeducer{regexp: jazzRegex} 496 case "bitbucket": 497 deducer = bitbucketDeducer{regexp: bbRegex} 498 case "launchpad": 499 deducer = launchpadDeducer{regexp: lpRegex} 500 case "git.launchpad": 501 deducer = launchpadGitDeducer{regexp: glpRegex} 502 case "apache": 503 deducer = apacheDeducer{regexp: apacheRegex} 504 case "vcsext": 505 deducer = vcsExtensionDeducer{regexp: vcsExtensionRegex} 506 default: 507 // Should just be the vanity imports, which we do elsewhere 508 t.Log("skipping") 509 t.SkipNow() 510 } 511 512 var printmb func(mb maybeSource, t *testing.T) string 513 printmb = func(mb maybeSource, t *testing.T) string { 514 switch tmb := mb.(type) { 515 case maybeSources: 516 var buf bytes.Buffer 517 fmt.Fprintf(&buf, "%v maybeSources:", len(tmb)) 518 for _, elem := range tmb { 519 fmt.Fprintf(&buf, "\n\t\t%s", printmb(elem, t)) 520 } 521 return buf.String() 522 case maybeGitSource: 523 return fmt.Sprintf("%T: %s", tmb, ufmt(tmb.url)) 524 case maybeBzrSource: 525 return fmt.Sprintf("%T: %s", tmb, ufmt(tmb.url)) 526 case maybeHgSource: 527 return fmt.Sprintf("%T: %s", tmb, ufmt(tmb.url)) 528 case maybeGopkginSource: 529 return fmt.Sprintf("%T: %s (v%v) %s ", tmb, tmb.opath, tmb.major, ufmt(tmb.url)) 530 default: 531 t.Errorf("Unknown maybeSource type: %T", mb) 532 } 533 return "" 534 } 535 536 for _, fix := range fixtures { 537 fix := fix 538 t.Run(fix.in, func(t *testing.T) { 539 t.Parallel() 540 u, in, uerr := normalizeURI(fix.in) 541 if uerr != nil { 542 if fix.rerr == nil { 543 t.Errorf("bad input URI %s", uerr) 544 } 545 t.SkipNow() 546 } 547 548 root, rerr := deducer.deduceRoot(in) 549 if fix.rerr != nil { 550 if rerr == nil { 551 t.Errorf("Expected error on deducing root, got none:\n\t(WNT) %s", fix.rerr) 552 } else if fix.rerr.Error() != rerr.Error() { 553 t.Errorf("Got unexpected error on deducing root:\n\t(GOT) %s\n\t(WNT) %s", rerr, fix.rerr) 554 } 555 } else if rerr != nil { 556 t.Errorf("Got unexpected error on deducing root:\n\t(GOT) %s", rerr) 557 } else if root != fix.root { 558 t.Errorf("Deducer did not return expected root:\n\t(GOT) %s\n\t(WNT) %s", root, fix.root) 559 } 560 561 mb, mberr := deducer.deduceSource(in, u) 562 if fix.srcerr != nil { 563 if mberr == nil { 564 t.Errorf("Expected error on deducing source, got none:\n\t(WNT) %s", fix.srcerr) 565 } else if fix.srcerr.Error() != mberr.Error() { 566 t.Errorf("Got unexpected error on deducing source:\n\t(GOT) %s\n\t(WNT) %s", mberr, fix.srcerr) 567 } 568 } else if mberr != nil { 569 // don't complain the fix already expected an rerr 570 if fix.rerr == nil { 571 t.Errorf("Got unexpected error on deducing source:\n\t(GOT) %s", mberr) 572 } 573 } else if !reflect.DeepEqual(mb, fix.mb) { 574 if mb == nil { 575 t.Errorf("Deducer returned source maybes, but none expected:\n\t(GOT) (none)\n\t(WNT) %s", printmb(fix.mb, t)) 576 } else if fix.mb == nil { 577 t.Errorf("Deducer returned source maybes, but none expected:\n\t(GOT) %s\n\t(WNT) (none)", printmb(mb, t)) 578 } else { 579 t.Errorf("Deducer did not return expected source:\n\t(GOT) %s\n\t(WNT) %s", printmb(mb, t), printmb(fix.mb, t)) 580 } 581 } 582 }) 583 } 584 }) 585 } 586 for typ, fixtures := range pathDeductionFixtures { 587 typ, fixtures := typ, fixtures 588 t.Run("first", func(t *testing.T) { 589 do(typ, fixtures, t) 590 }) 591 } 592 593 // Run the test set twice to ensure results are correct for both cached 594 // and uncached deductions. 595 for typ, fixtures := range pathDeductionFixtures { 596 typ, fixtures := typ, fixtures 597 t.Run("second", func(t *testing.T) { 598 do(typ, fixtures, t) 599 }) 600 } 601 } 602 603 func TestVanityDeduction(t *testing.T) { 604 if testing.Short() { 605 t.Skip("Skipping slow test in short mode") 606 } 607 608 sm, clean := mkNaiveSM(t) 609 defer clean() 610 611 vanities := pathDeductionFixtures["vanity"] 612 // group to avoid sourcemanager cleanup 613 ctx := context.Background() 614 do := func(t *testing.T) { 615 for _, fix := range vanities { 616 fix := fix 617 t.Run(fmt.Sprintf("%s", fix.in), func(t *testing.T) { 618 t.Parallel() 619 620 pr, err := sm.DeduceProjectRoot(fix.in) 621 if err != nil { 622 t.Errorf("Unexpected err on deducing project root: %s", err) 623 return 624 } else if string(pr) != fix.root { 625 t.Errorf("Deducer did not return expected root:\n\t(GOT) %s\n\t(WNT) %s", pr, fix.root) 626 } 627 628 pd, err := sm.deduceCoord.deduceRootPath(ctx, fix.in) 629 if err != nil { 630 t.Errorf("Unexpected err on deducing source: %s", err) 631 return 632 } 633 634 goturl, wanturl := pd.mb.(maybeGitSource).url.String(), fix.mb.(maybeGitSource).url.String() 635 if goturl != wanturl { 636 t.Errorf("Deduced repo ident does not match fixture:\n\t(GOT) %s\n\t(WNT) %s", goturl, wanturl) 637 } 638 }) 639 } 640 } 641 642 // Run twice, to ensure correctness of cache 643 t.Run("first", do) 644 t.Run("second", do) 645 } 646 647 func TestVanityDeductionSchemeMismatch(t *testing.T) { 648 if testing.Short() { 649 t.Skip("Skipping slow test in short mode") 650 } 651 652 ctx := context.Background() 653 cm := newSupervisor(ctx) 654 dc := newDeductionCoordinator(cm) 655 _, err := dc.deduceRootPath(ctx, "ssh://golang.org/exp") 656 if err == nil { 657 t.Error("should have errored on scheme mismatch between input and go-get metadata") 658 } 659 } 660 661 // borrow from stdlib 662 // more useful string for debugging than fmt's struct printer 663 func ufmt(u *url.URL) string { 664 var user, pass interface{} 665 if u.User != nil { 666 user = u.User.Username() 667 if p, ok := u.User.Password(); ok { 668 pass = p 669 } 670 } 671 return fmt.Sprintf("host=%q, path=%q, opaque=%q, scheme=%q, user=%#v, pass=%#v, rawpath=%q, rawq=%q, frag=%q", 672 u.Host, u.Path, u.Opaque, u.Scheme, user, pass, u.RawPath, u.RawQuery, u.Fragment) 673 }