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