github.com/gagliardetto/golang-go@v0.0.0-20201020153340-53909ea70814/cmd/go/not-internal/modfetch/codehost/git_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 codehost 6 7 import ( 8 "archive/zip" 9 "bytes" 10 "flag" 11 "fmt" 12 "github.com/gagliardetto/golang-go/not-internal/testenv" 13 "io/ioutil" 14 "log" 15 "os" 16 "os/exec" 17 "path" 18 "path/filepath" 19 "reflect" 20 "strings" 21 "testing" 22 "time" 23 ) 24 25 func TestMain(m *testing.M) { 26 // needed for initializing the test environment variables as testing.Short 27 // and HasExternalNetwork 28 flag.Parse() 29 os.Exit(testMain(m)) 30 } 31 32 const ( 33 gitrepo1 = "https://vcs-test.golang.org/git/gitrepo1" 34 hgrepo1 = "https://vcs-test.golang.org/hg/hgrepo1" 35 ) 36 37 var altRepos = []string{ 38 "localGitRepo", 39 hgrepo1, 40 } 41 42 // TODO: Convert gitrepo1 to svn, bzr, fossil and add tests. 43 // For now, at least the hgrepo1 tests check the general vcs.go logic. 44 45 // localGitRepo is like gitrepo1 but allows archive access. 46 var localGitRepo string 47 48 func testMain(m *testing.M) int { 49 if _, err := exec.LookPath("git"); err != nil { 50 fmt.Fprintln(os.Stderr, "skipping because git binary not found") 51 fmt.Println("PASS") 52 return 0 53 } 54 55 dir, err := ioutil.TempDir("", "gitrepo-test-") 56 if err != nil { 57 log.Fatal(err) 58 } 59 defer os.RemoveAll(dir) 60 WorkRoot = dir 61 62 if testenv.HasExternalNetwork() && testenv.HasExec() { 63 // Clone gitrepo1 into a local directory. 64 // If we use a file:// URL to access the local directory, 65 // then git starts up all the usual protocol machinery, 66 // which will let us test remote git archive invocations. 67 localGitRepo = filepath.Join(dir, "gitrepo2") 68 if _, err := Run("", "git", "clone", "--mirror", gitrepo1, localGitRepo); err != nil { 69 log.Fatal(err) 70 } 71 if _, err := Run(localGitRepo, "git", "config", "daemon.uploadarch", "true"); err != nil { 72 log.Fatal(err) 73 } 74 } 75 76 return m.Run() 77 } 78 79 func testRepo(remote string) (Repo, error) { 80 if remote == "localGitRepo" { 81 // Convert absolute path to file URL. LocalGitRepo will not accept 82 // Windows absolute paths because they look like a host:path remote. 83 // TODO(golang.org/issue/32456): use url.FromFilePath when implemented. 84 var url string 85 if strings.HasPrefix(localGitRepo, "/") { 86 url = "file://" + localGitRepo 87 } else { 88 url = "file:///" + filepath.ToSlash(localGitRepo) 89 } 90 return LocalGitRepo(url) 91 } 92 kind := "git" 93 for _, k := range []string{"hg"} { 94 if strings.Contains(remote, "/"+k+"/") { 95 kind = k 96 } 97 } 98 return NewRepo(kind, remote) 99 } 100 101 var tagsTests = []struct { 102 repo string 103 prefix string 104 tags []string 105 }{ 106 {gitrepo1, "xxx", []string{}}, 107 {gitrepo1, "", []string{"v1.2.3", "v1.2.4-annotated", "v2.0.1", "v2.0.2", "v2.3"}}, 108 {gitrepo1, "v", []string{"v1.2.3", "v1.2.4-annotated", "v2.0.1", "v2.0.2", "v2.3"}}, 109 {gitrepo1, "v1", []string{"v1.2.3", "v1.2.4-annotated"}}, 110 {gitrepo1, "2", []string{}}, 111 } 112 113 func TestTags(t *testing.T) { 114 testenv.MustHaveExternalNetwork(t) 115 testenv.MustHaveExec(t) 116 117 for _, tt := range tagsTests { 118 f := func(t *testing.T) { 119 r, err := testRepo(tt.repo) 120 if err != nil { 121 t.Fatal(err) 122 } 123 tags, err := r.Tags(tt.prefix) 124 if err != nil { 125 t.Fatal(err) 126 } 127 if !reflect.DeepEqual(tags, tt.tags) { 128 t.Errorf("Tags: incorrect tags\nhave %v\nwant %v", tags, tt.tags) 129 } 130 } 131 t.Run(path.Base(tt.repo)+"/"+tt.prefix, f) 132 if tt.repo == gitrepo1 { 133 for _, tt.repo = range altRepos { 134 t.Run(path.Base(tt.repo)+"/"+tt.prefix, f) 135 } 136 } 137 } 138 } 139 140 var latestTests = []struct { 141 repo string 142 info *RevInfo 143 }{ 144 { 145 gitrepo1, 146 &RevInfo{ 147 Name: "ede458df7cd0fdca520df19a33158086a8a68e81", 148 Short: "ede458df7cd0", 149 Version: "ede458df7cd0fdca520df19a33158086a8a68e81", 150 Time: time.Date(2018, 4, 17, 19, 43, 22, 0, time.UTC), 151 Tags: []string{"v1.2.3", "v1.2.4-annotated"}, 152 }, 153 }, 154 { 155 hgrepo1, 156 &RevInfo{ 157 Name: "18518c07eb8ed5c80221e997e518cccaa8c0c287", 158 Short: "18518c07eb8e", 159 Version: "18518c07eb8ed5c80221e997e518cccaa8c0c287", 160 Time: time.Date(2018, 6, 27, 16, 16, 30, 0, time.UTC), 161 }, 162 }, 163 } 164 165 func TestLatest(t *testing.T) { 166 testenv.MustHaveExternalNetwork(t) 167 testenv.MustHaveExec(t) 168 169 for _, tt := range latestTests { 170 f := func(t *testing.T) { 171 r, err := testRepo(tt.repo) 172 if err != nil { 173 t.Fatal(err) 174 } 175 info, err := r.Latest() 176 if err != nil { 177 t.Fatal(err) 178 } 179 if !reflect.DeepEqual(info, tt.info) { 180 t.Errorf("Latest: incorrect info\nhave %+v\nwant %+v", *info, *tt.info) 181 } 182 } 183 t.Run(path.Base(tt.repo), f) 184 if tt.repo == gitrepo1 { 185 tt.repo = "localGitRepo" 186 t.Run(path.Base(tt.repo), f) 187 } 188 } 189 } 190 191 var readFileTests = []struct { 192 repo string 193 rev string 194 file string 195 err string 196 data string 197 }{ 198 { 199 repo: gitrepo1, 200 rev: "latest", 201 file: "README", 202 data: "", 203 }, 204 { 205 repo: gitrepo1, 206 rev: "v2", 207 file: "another.txt", 208 data: "another\n", 209 }, 210 { 211 repo: gitrepo1, 212 rev: "v2.3.4", 213 file: "another.txt", 214 err: os.ErrNotExist.Error(), 215 }, 216 } 217 218 func TestReadFile(t *testing.T) { 219 testenv.MustHaveExternalNetwork(t) 220 testenv.MustHaveExec(t) 221 222 for _, tt := range readFileTests { 223 f := func(t *testing.T) { 224 r, err := testRepo(tt.repo) 225 if err != nil { 226 t.Fatal(err) 227 } 228 data, err := r.ReadFile(tt.rev, tt.file, 100) 229 if err != nil { 230 if tt.err == "" { 231 t.Fatalf("ReadFile: unexpected error %v", err) 232 } 233 if !strings.Contains(err.Error(), tt.err) { 234 t.Fatalf("ReadFile: wrong error %q, want %q", err, tt.err) 235 } 236 if len(data) != 0 { 237 t.Errorf("ReadFile: non-empty data %q with error %v", data, err) 238 } 239 return 240 } 241 if tt.err != "" { 242 t.Fatalf("ReadFile: no error, wanted %v", tt.err) 243 } 244 if string(data) != tt.data { 245 t.Errorf("ReadFile: incorrect data\nhave %q\nwant %q", data, tt.data) 246 } 247 } 248 t.Run(path.Base(tt.repo)+"/"+tt.rev+"/"+tt.file, f) 249 if tt.repo == gitrepo1 { 250 for _, tt.repo = range altRepos { 251 t.Run(path.Base(tt.repo)+"/"+tt.rev+"/"+tt.file, f) 252 } 253 } 254 } 255 } 256 257 var readZipTests = []struct { 258 repo string 259 rev string 260 subdir string 261 err string 262 files map[string]uint64 263 }{ 264 { 265 repo: gitrepo1, 266 rev: "v2.3.4", 267 subdir: "", 268 files: map[string]uint64{ 269 "prefix/": 0, 270 "prefix/README": 0, 271 "prefix/v2": 3, 272 }, 273 }, 274 { 275 repo: hgrepo1, 276 rev: "v2.3.4", 277 subdir: "", 278 files: map[string]uint64{ 279 "prefix/.hg_archival.txt": ^uint64(0), 280 "prefix/README": 0, 281 "prefix/v2": 3, 282 }, 283 }, 284 285 { 286 repo: gitrepo1, 287 rev: "v2", 288 subdir: "", 289 files: map[string]uint64{ 290 "prefix/": 0, 291 "prefix/README": 0, 292 "prefix/v2": 3, 293 "prefix/another.txt": 8, 294 "prefix/foo.txt": 13, 295 }, 296 }, 297 { 298 repo: hgrepo1, 299 rev: "v2", 300 subdir: "", 301 files: map[string]uint64{ 302 "prefix/.hg_archival.txt": ^uint64(0), 303 "prefix/README": 0, 304 "prefix/v2": 3, 305 "prefix/another.txt": 8, 306 "prefix/foo.txt": 13, 307 }, 308 }, 309 310 { 311 repo: gitrepo1, 312 rev: "v3", 313 subdir: "", 314 files: map[string]uint64{ 315 "prefix/": 0, 316 "prefix/v3/": 0, 317 "prefix/v3/sub/": 0, 318 "prefix/v3/sub/dir/": 0, 319 "prefix/v3/sub/dir/file.txt": 16, 320 "prefix/README": 0, 321 }, 322 }, 323 { 324 repo: hgrepo1, 325 rev: "v3", 326 subdir: "", 327 files: map[string]uint64{ 328 "prefix/.hg_archival.txt": ^uint64(0), 329 "prefix/.hgtags": 405, 330 "prefix/v3/sub/dir/file.txt": 16, 331 "prefix/README": 0, 332 }, 333 }, 334 335 { 336 repo: gitrepo1, 337 rev: "v3", 338 subdir: "v3/sub/dir", 339 files: map[string]uint64{ 340 "prefix/": 0, 341 "prefix/v3/": 0, 342 "prefix/v3/sub/": 0, 343 "prefix/v3/sub/dir/": 0, 344 "prefix/v3/sub/dir/file.txt": 16, 345 }, 346 }, 347 { 348 repo: hgrepo1, 349 rev: "v3", 350 subdir: "v3/sub/dir", 351 files: map[string]uint64{ 352 "prefix/v3/sub/dir/file.txt": 16, 353 }, 354 }, 355 356 { 357 repo: gitrepo1, 358 rev: "v3", 359 subdir: "v3/sub", 360 files: map[string]uint64{ 361 "prefix/": 0, 362 "prefix/v3/": 0, 363 "prefix/v3/sub/": 0, 364 "prefix/v3/sub/dir/": 0, 365 "prefix/v3/sub/dir/file.txt": 16, 366 }, 367 }, 368 { 369 repo: hgrepo1, 370 rev: "v3", 371 subdir: "v3/sub", 372 files: map[string]uint64{ 373 "prefix/v3/sub/dir/file.txt": 16, 374 }, 375 }, 376 377 { 378 repo: gitrepo1, 379 rev: "aaaaaaaaab", 380 subdir: "", 381 err: "unknown revision", 382 }, 383 { 384 repo: hgrepo1, 385 rev: "aaaaaaaaab", 386 subdir: "", 387 err: "unknown revision", 388 }, 389 390 { 391 repo: "https://github.com/rsc/vgotest1", 392 rev: "submod/v1.0.4", 393 subdir: "submod", 394 files: map[string]uint64{ 395 "prefix/": 0, 396 "prefix/submod/": 0, 397 "prefix/submod/go.mod": 53, 398 "prefix/submod/pkg/": 0, 399 "prefix/submod/pkg/p.go": 31, 400 }, 401 }, 402 } 403 404 type zipFile struct { 405 name string 406 size int64 407 } 408 409 func TestReadZip(t *testing.T) { 410 testenv.MustHaveExternalNetwork(t) 411 testenv.MustHaveExec(t) 412 413 for _, tt := range readZipTests { 414 f := func(t *testing.T) { 415 r, err := testRepo(tt.repo) 416 if err != nil { 417 t.Fatal(err) 418 } 419 rc, err := r.ReadZip(tt.rev, tt.subdir, 100000) 420 if err != nil { 421 if tt.err == "" { 422 t.Fatalf("ReadZip: unexpected error %v", err) 423 } 424 if !strings.Contains(err.Error(), tt.err) { 425 t.Fatalf("ReadZip: wrong error %q, want %q", err, tt.err) 426 } 427 if rc != nil { 428 t.Errorf("ReadZip: non-nil io.ReadCloser with error %v", err) 429 } 430 return 431 } 432 defer rc.Close() 433 if tt.err != "" { 434 t.Fatalf("ReadZip: no error, wanted %v", tt.err) 435 } 436 zipdata, err := ioutil.ReadAll(rc) 437 if err != nil { 438 t.Fatal(err) 439 } 440 z, err := zip.NewReader(bytes.NewReader(zipdata), int64(len(zipdata))) 441 if err != nil { 442 t.Fatalf("ReadZip: cannot read zip file: %v", err) 443 } 444 have := make(map[string]bool) 445 for _, f := range z.File { 446 size, ok := tt.files[f.Name] 447 if !ok { 448 t.Errorf("ReadZip: unexpected file %s", f.Name) 449 continue 450 } 451 have[f.Name] = true 452 if size != ^uint64(0) && f.UncompressedSize64 != size { 453 t.Errorf("ReadZip: file %s has unexpected size %d != %d", f.Name, f.UncompressedSize64, size) 454 } 455 } 456 for name := range tt.files { 457 if !have[name] { 458 t.Errorf("ReadZip: missing file %s", name) 459 } 460 } 461 } 462 t.Run(path.Base(tt.repo)+"/"+tt.rev+"/"+tt.subdir, f) 463 if tt.repo == gitrepo1 { 464 tt.repo = "localGitRepo" 465 t.Run(path.Base(tt.repo)+"/"+tt.rev+"/"+tt.subdir, f) 466 } 467 } 468 } 469 470 var hgmap = map[string]string{ 471 "HEAD": "41964ddce1180313bdc01d0a39a2813344d6261d", // not tip due to bad hgrepo1 conversion 472 "9d02800338b8a55be062c838d1f02e0c5780b9eb": "8f49ee7a6ddcdec6f0112d9dca48d4a2e4c3c09e", 473 "76a00fb249b7f93091bc2c89a789dab1fc1bc26f": "88fde824ec8b41a76baa16b7e84212cee9f3edd0", 474 "ede458df7cd0fdca520df19a33158086a8a68e81": "41964ddce1180313bdc01d0a39a2813344d6261d", 475 "97f6aa59c81c623494825b43d39e445566e429a4": "c0cbbfb24c7c3c50c35c7b88e7db777da4ff625d", 476 } 477 478 var statTests = []struct { 479 repo string 480 rev string 481 err string 482 info *RevInfo 483 }{ 484 { 485 repo: gitrepo1, 486 rev: "HEAD", 487 info: &RevInfo{ 488 Name: "ede458df7cd0fdca520df19a33158086a8a68e81", 489 Short: "ede458df7cd0", 490 Version: "ede458df7cd0fdca520df19a33158086a8a68e81", 491 Time: time.Date(2018, 4, 17, 19, 43, 22, 0, time.UTC), 492 Tags: []string{"v1.2.3", "v1.2.4-annotated"}, 493 }, 494 }, 495 { 496 repo: gitrepo1, 497 rev: "v2", // branch 498 info: &RevInfo{ 499 Name: "9d02800338b8a55be062c838d1f02e0c5780b9eb", 500 Short: "9d02800338b8", 501 Version: "9d02800338b8a55be062c838d1f02e0c5780b9eb", 502 Time: time.Date(2018, 4, 17, 20, 00, 32, 0, time.UTC), 503 Tags: []string{"v2.0.2"}, 504 }, 505 }, 506 { 507 repo: gitrepo1, 508 rev: "v2.3.4", // badly-named branch (semver should be a tag) 509 info: &RevInfo{ 510 Name: "76a00fb249b7f93091bc2c89a789dab1fc1bc26f", 511 Short: "76a00fb249b7", 512 Version: "76a00fb249b7f93091bc2c89a789dab1fc1bc26f", 513 Time: time.Date(2018, 4, 17, 19, 45, 48, 0, time.UTC), 514 Tags: []string{"v2.0.1", "v2.3"}, 515 }, 516 }, 517 { 518 repo: gitrepo1, 519 rev: "v2.3", // badly-named tag (we only respect full semver v2.3.0) 520 info: &RevInfo{ 521 Name: "76a00fb249b7f93091bc2c89a789dab1fc1bc26f", 522 Short: "76a00fb249b7", 523 Version: "v2.3", 524 Time: time.Date(2018, 4, 17, 19, 45, 48, 0, time.UTC), 525 Tags: []string{"v2.0.1", "v2.3"}, 526 }, 527 }, 528 { 529 repo: gitrepo1, 530 rev: "v1.2.3", // tag 531 info: &RevInfo{ 532 Name: "ede458df7cd0fdca520df19a33158086a8a68e81", 533 Short: "ede458df7cd0", 534 Version: "v1.2.3", 535 Time: time.Date(2018, 4, 17, 19, 43, 22, 0, time.UTC), 536 Tags: []string{"v1.2.3", "v1.2.4-annotated"}, 537 }, 538 }, 539 { 540 repo: gitrepo1, 541 rev: "ede458df", // hash prefix in refs 542 info: &RevInfo{ 543 Name: "ede458df7cd0fdca520df19a33158086a8a68e81", 544 Short: "ede458df7cd0", 545 Version: "ede458df7cd0fdca520df19a33158086a8a68e81", 546 Time: time.Date(2018, 4, 17, 19, 43, 22, 0, time.UTC), 547 Tags: []string{"v1.2.3", "v1.2.4-annotated"}, 548 }, 549 }, 550 { 551 repo: gitrepo1, 552 rev: "97f6aa59", // hash prefix not in refs 553 info: &RevInfo{ 554 Name: "97f6aa59c81c623494825b43d39e445566e429a4", 555 Short: "97f6aa59c81c", 556 Version: "97f6aa59c81c623494825b43d39e445566e429a4", 557 Time: time.Date(2018, 4, 17, 20, 0, 19, 0, time.UTC), 558 }, 559 }, 560 { 561 repo: gitrepo1, 562 rev: "v1.2.4-annotated", // annotated tag uses unwrapped commit hash 563 info: &RevInfo{ 564 Name: "ede458df7cd0fdca520df19a33158086a8a68e81", 565 Short: "ede458df7cd0", 566 Version: "v1.2.4-annotated", 567 Time: time.Date(2018, 4, 17, 19, 43, 22, 0, time.UTC), 568 Tags: []string{"v1.2.3", "v1.2.4-annotated"}, 569 }, 570 }, 571 { 572 repo: gitrepo1, 573 rev: "aaaaaaaaab", 574 err: "unknown revision", 575 }, 576 } 577 578 func TestStat(t *testing.T) { 579 testenv.MustHaveExternalNetwork(t) 580 testenv.MustHaveExec(t) 581 582 for _, tt := range statTests { 583 f := func(t *testing.T) { 584 r, err := testRepo(tt.repo) 585 if err != nil { 586 t.Fatal(err) 587 } 588 info, err := r.Stat(tt.rev) 589 if err != nil { 590 if tt.err == "" { 591 t.Fatalf("Stat: unexpected error %v", err) 592 } 593 if !strings.Contains(err.Error(), tt.err) { 594 t.Fatalf("Stat: wrong error %q, want %q", err, tt.err) 595 } 596 if info != nil { 597 t.Errorf("Stat: non-nil info with error %q", err) 598 } 599 return 600 } 601 if !reflect.DeepEqual(info, tt.info) { 602 t.Errorf("Stat: incorrect info\nhave %+v\nwant %+v", *info, *tt.info) 603 } 604 } 605 t.Run(path.Base(tt.repo)+"/"+tt.rev, f) 606 if tt.repo == gitrepo1 { 607 for _, tt.repo = range altRepos { 608 old := tt 609 var m map[string]string 610 if tt.repo == hgrepo1 { 611 m = hgmap 612 } 613 if tt.info != nil { 614 info := *tt.info 615 tt.info = &info 616 tt.info.Name = remap(tt.info.Name, m) 617 tt.info.Version = remap(tt.info.Version, m) 618 tt.info.Short = remap(tt.info.Short, m) 619 } 620 tt.rev = remap(tt.rev, m) 621 t.Run(path.Base(tt.repo)+"/"+tt.rev, f) 622 tt = old 623 } 624 } 625 } 626 } 627 628 func remap(name string, m map[string]string) string { 629 if m[name] != "" { 630 return m[name] 631 } 632 if AllHex(name) { 633 for k, v := range m { 634 if strings.HasPrefix(k, name) { 635 return v[:len(name)] 636 } 637 } 638 } 639 return name 640 }