1
2
3
4
5 package codehost
6
7 import (
8 "archive/zip"
9 "bytes"
10 "cmd/go/internal/cfg"
11 "cmd/go/internal/vcweb/vcstest"
12 "context"
13 "flag"
14 "internal/testenv"
15 "io"
16 "io/fs"
17 "log"
18 "os"
19 "path"
20 "path/filepath"
21 "reflect"
22 "runtime"
23 "strings"
24 "sync"
25 "testing"
26 "time"
27 )
28
29 func TestMain(m *testing.M) {
30 flag.Parse()
31 if err := testMain(m); err != nil {
32 log.Fatal(err)
33 }
34 }
35
36 var gitrepo1, hgrepo1, vgotest1 string
37
38 var altRepos = func() []string {
39 return []string{
40 "localGitRepo",
41 hgrepo1,
42 }
43 }
44
45
46
47
48
49
50
51
52 var localGitRepo string
53
54
55 func localGitURL(t testing.TB) string {
56 testenv.MustHaveExecPath(t, "git")
57 if runtime.GOOS == "android" && strings.HasSuffix(testenv.Builder(), "-corellium") {
58 testenv.SkipFlaky(t, 59940)
59 }
60
61 localGitURLOnce.Do(func() {
62
63
64
65
66 _, localGitURLErr = Run(context.Background(), "", "git", "clone", "--mirror", gitrepo1, localGitRepo)
67 if localGitURLErr != nil {
68 return
69 }
70 _, localGitURLErr = Run(context.Background(), localGitRepo, "git", "config", "daemon.uploadarch", "true")
71 })
72
73 if localGitURLErr != nil {
74 t.Fatal(localGitURLErr)
75 }
76
77
78
79 if strings.HasPrefix(localGitRepo, "/") {
80 return "file://" + localGitRepo
81 } else {
82 return "file:///" + filepath.ToSlash(localGitRepo)
83 }
84 }
85
86 var (
87 localGitURLOnce sync.Once
88 localGitURLErr error
89 )
90
91 func testMain(m *testing.M) (err error) {
92 cfg.BuildX = testing.Verbose()
93
94 srv, err := vcstest.NewServer()
95 if err != nil {
96 return err
97 }
98 defer func() {
99 if closeErr := srv.Close(); err == nil {
100 err = closeErr
101 }
102 }()
103
104 gitrepo1 = srv.HTTP.URL + "/git/gitrepo1"
105 hgrepo1 = srv.HTTP.URL + "/hg/hgrepo1"
106 vgotest1 = srv.HTTP.URL + "/git/vgotest1"
107
108 dir, err := os.MkdirTemp("", "gitrepo-test-")
109 if err != nil {
110 return err
111 }
112 defer func() {
113 if rmErr := os.RemoveAll(dir); err == nil {
114 err = rmErr
115 }
116 }()
117
118 localGitRepo = filepath.Join(dir, "gitrepo2")
119
120
121
122 cfg.GOMODCACHE = filepath.Join(dir, "modcache")
123 cfg.ModCacheRW = true
124
125 m.Run()
126 return nil
127 }
128
129 func testContext(t testing.TB) context.Context {
130 w := newTestWriter(t)
131 return cfg.WithBuildXWriter(context.Background(), w)
132 }
133
134
135
136
137
138
139 type testWriter struct {
140 t testing.TB
141
142 mu sync.Mutex
143 buf bytes.Buffer
144 }
145
146 func newTestWriter(t testing.TB) *testWriter {
147 w := &testWriter{t: t}
148
149 t.Cleanup(func() {
150 w.mu.Lock()
151 defer w.mu.Unlock()
152 if b := w.buf.Bytes(); len(b) > 0 {
153 w.t.Logf("%s", b)
154 w.buf.Reset()
155 }
156 })
157
158 return w
159 }
160
161 func (w *testWriter) Write(p []byte) (int, error) {
162 w.mu.Lock()
163 defer w.mu.Unlock()
164 n, err := w.buf.Write(p)
165 if b := w.buf.Bytes(); len(b) > 0 && b[len(b)-1] == '\n' {
166 w.t.Logf("%s", b)
167 w.buf.Reset()
168 }
169 return n, err
170 }
171
172 func testRepo(ctx context.Context, t *testing.T, remote string) (Repo, error) {
173 if remote == "localGitRepo" {
174 return LocalGitRepo(ctx, localGitURL(t))
175 }
176 vcsName := "git"
177 for _, k := range []string{"hg"} {
178 if strings.Contains(remote, "/"+k+"/") {
179 vcsName = k
180 }
181 }
182 if testing.Short() && vcsName == "hg" {
183 t.Skipf("skipping hg test in short mode: hg is slow")
184 }
185 testenv.MustHaveExecPath(t, vcsName)
186 if runtime.GOOS == "android" && strings.HasSuffix(testenv.Builder(), "-corellium") {
187 testenv.SkipFlaky(t, 59940)
188 }
189 return NewRepo(ctx, vcsName, remote)
190 }
191
192 func TestTags(t *testing.T) {
193 t.Parallel()
194
195 type tagsTest struct {
196 repo string
197 prefix string
198 tags []Tag
199 }
200
201 runTest := func(tt tagsTest) func(*testing.T) {
202 return func(t *testing.T) {
203 t.Parallel()
204 ctx := testContext(t)
205
206 r, err := testRepo(ctx, t, tt.repo)
207 if err != nil {
208 t.Fatal(err)
209 }
210 tags, err := r.Tags(ctx, tt.prefix)
211 if err != nil {
212 t.Fatal(err)
213 }
214 if tags == nil || !reflect.DeepEqual(tags.List, tt.tags) {
215 t.Errorf("Tags(%q): incorrect tags\nhave %v\nwant %v", tt.prefix, tags, tt.tags)
216 }
217 }
218 }
219
220 for _, tt := range []tagsTest{
221 {gitrepo1, "xxx", []Tag{}},
222 {gitrepo1, "", []Tag{
223 {"v1.2.3", "ede458df7cd0fdca520df19a33158086a8a68e81"},
224 {"v1.2.4-annotated", "ede458df7cd0fdca520df19a33158086a8a68e81"},
225 {"v2.0.1", "76a00fb249b7f93091bc2c89a789dab1fc1bc26f"},
226 {"v2.0.2", "9d02800338b8a55be062c838d1f02e0c5780b9eb"},
227 {"v2.3", "76a00fb249b7f93091bc2c89a789dab1fc1bc26f"},
228 }},
229 {gitrepo1, "v", []Tag{
230 {"v1.2.3", "ede458df7cd0fdca520df19a33158086a8a68e81"},
231 {"v1.2.4-annotated", "ede458df7cd0fdca520df19a33158086a8a68e81"},
232 {"v2.0.1", "76a00fb249b7f93091bc2c89a789dab1fc1bc26f"},
233 {"v2.0.2", "9d02800338b8a55be062c838d1f02e0c5780b9eb"},
234 {"v2.3", "76a00fb249b7f93091bc2c89a789dab1fc1bc26f"},
235 }},
236 {gitrepo1, "v1", []Tag{
237 {"v1.2.3", "ede458df7cd0fdca520df19a33158086a8a68e81"},
238 {"v1.2.4-annotated", "ede458df7cd0fdca520df19a33158086a8a68e81"},
239 }},
240 {gitrepo1, "2", []Tag{}},
241 } {
242 t.Run(path.Base(tt.repo)+"/"+tt.prefix, runTest(tt))
243 if tt.repo == gitrepo1 {
244
245 clearTags := []Tag{}
246 for _, tag := range tt.tags {
247 clearTags = append(clearTags, Tag{tag.Name, ""})
248 }
249 tags := tt.tags
250 for _, tt.repo = range altRepos() {
251 if strings.Contains(tt.repo, "Git") {
252 tt.tags = tags
253 } else {
254 tt.tags = clearTags
255 }
256 t.Run(path.Base(tt.repo)+"/"+tt.prefix, runTest(tt))
257 }
258 }
259 }
260 }
261
262 func TestLatest(t *testing.T) {
263 t.Parallel()
264
265 type latestTest struct {
266 repo string
267 info *RevInfo
268 }
269 runTest := func(tt latestTest) func(*testing.T) {
270 return func(t *testing.T) {
271 t.Parallel()
272 ctx := testContext(t)
273
274 r, err := testRepo(ctx, t, tt.repo)
275 if err != nil {
276 t.Fatal(err)
277 }
278 info, err := r.Latest(ctx)
279 if err != nil {
280 t.Fatal(err)
281 }
282 if !reflect.DeepEqual(info, tt.info) {
283 t.Errorf("Latest: incorrect info\nhave %+v (origin %+v)\nwant %+v (origin %+v)", info, info.Origin, tt.info, tt.info.Origin)
284 }
285 }
286 }
287
288 for _, tt := range []latestTest{
289 {
290 gitrepo1,
291 &RevInfo{
292 Origin: &Origin{
293 VCS: "git",
294 URL: gitrepo1,
295 Ref: "HEAD",
296 Hash: "ede458df7cd0fdca520df19a33158086a8a68e81",
297 },
298 Name: "ede458df7cd0fdca520df19a33158086a8a68e81",
299 Short: "ede458df7cd0",
300 Version: "ede458df7cd0fdca520df19a33158086a8a68e81",
301 Time: time.Date(2018, 4, 17, 19, 43, 22, 0, time.UTC),
302 Tags: []string{"v1.2.3", "v1.2.4-annotated"},
303 },
304 },
305 {
306 hgrepo1,
307 &RevInfo{
308 Origin: &Origin{
309 VCS: "hg",
310 URL: hgrepo1,
311 Hash: "18518c07eb8ed5c80221e997e518cccaa8c0c287",
312 },
313 Name: "18518c07eb8ed5c80221e997e518cccaa8c0c287",
314 Short: "18518c07eb8e",
315 Version: "18518c07eb8ed5c80221e997e518cccaa8c0c287",
316 Time: time.Date(2018, 6, 27, 16, 16, 30, 0, time.UTC),
317 },
318 },
319 } {
320 t.Run(path.Base(tt.repo), runTest(tt))
321 if tt.repo == gitrepo1 {
322 tt.repo = "localGitRepo"
323 info := *tt.info
324 tt.info = &info
325 o := *info.Origin
326 info.Origin = &o
327 o.URL = localGitURL(t)
328 t.Run(path.Base(tt.repo), runTest(tt))
329 }
330 }
331 }
332
333 func TestReadFile(t *testing.T) {
334 t.Parallel()
335
336 type readFileTest struct {
337 repo string
338 rev string
339 file string
340 err string
341 data string
342 }
343 runTest := func(tt readFileTest) func(*testing.T) {
344 return func(t *testing.T) {
345 t.Parallel()
346 ctx := testContext(t)
347
348 r, err := testRepo(ctx, t, tt.repo)
349 if err != nil {
350 t.Fatal(err)
351 }
352 data, err := r.ReadFile(ctx, tt.rev, tt.file, 100)
353 if err != nil {
354 if tt.err == "" {
355 t.Fatalf("ReadFile: unexpected error %v", err)
356 }
357 if !strings.Contains(err.Error(), tt.err) {
358 t.Fatalf("ReadFile: wrong error %q, want %q", err, tt.err)
359 }
360 if len(data) != 0 {
361 t.Errorf("ReadFile: non-empty data %q with error %v", data, err)
362 }
363 return
364 }
365 if tt.err != "" {
366 t.Fatalf("ReadFile: no error, wanted %v", tt.err)
367 }
368 if string(data) != tt.data {
369 t.Errorf("ReadFile: incorrect data\nhave %q\nwant %q", data, tt.data)
370 }
371 }
372 }
373
374 for _, tt := range []readFileTest{
375 {
376 repo: gitrepo1,
377 rev: "latest",
378 file: "README",
379 data: "",
380 },
381 {
382 repo: gitrepo1,
383 rev: "v2",
384 file: "another.txt",
385 data: "another\n",
386 },
387 {
388 repo: gitrepo1,
389 rev: "v2.3.4",
390 file: "another.txt",
391 err: fs.ErrNotExist.Error(),
392 },
393 } {
394 t.Run(path.Base(tt.repo)+"/"+tt.rev+"/"+tt.file, runTest(tt))
395 if tt.repo == gitrepo1 {
396 for _, tt.repo = range altRepos() {
397 t.Run(path.Base(tt.repo)+"/"+tt.rev+"/"+tt.file, runTest(tt))
398 }
399 }
400 }
401 }
402
403 type zipFile struct {
404 name string
405 size int64
406 }
407
408 func TestReadZip(t *testing.T) {
409 t.Parallel()
410
411 type readZipTest struct {
412 repo string
413 rev string
414 subdir string
415 err string
416 files map[string]uint64
417 }
418 runTest := func(tt readZipTest) func(*testing.T) {
419 return func(t *testing.T) {
420 t.Parallel()
421 ctx := testContext(t)
422
423 r, err := testRepo(ctx, t, tt.repo)
424 if err != nil {
425 t.Fatal(err)
426 }
427 rc, err := r.ReadZip(ctx, tt.rev, tt.subdir, 100000)
428 if err != nil {
429 if tt.err == "" {
430 t.Fatalf("ReadZip: unexpected error %v", err)
431 }
432 if !strings.Contains(err.Error(), tt.err) {
433 t.Fatalf("ReadZip: wrong error %q, want %q", err, tt.err)
434 }
435 if rc != nil {
436 t.Errorf("ReadZip: non-nil io.ReadCloser with error %v", err)
437 }
438 return
439 }
440 defer rc.Close()
441 if tt.err != "" {
442 t.Fatalf("ReadZip: no error, wanted %v", tt.err)
443 }
444 zipdata, err := io.ReadAll(rc)
445 if err != nil {
446 t.Fatal(err)
447 }
448 z, err := zip.NewReader(bytes.NewReader(zipdata), int64(len(zipdata)))
449 if err != nil {
450 t.Fatalf("ReadZip: cannot read zip file: %v", err)
451 }
452 have := make(map[string]bool)
453 for _, f := range z.File {
454 size, ok := tt.files[f.Name]
455 if !ok {
456 t.Errorf("ReadZip: unexpected file %s", f.Name)
457 continue
458 }
459 have[f.Name] = true
460 if size != ^uint64(0) && f.UncompressedSize64 != size {
461 t.Errorf("ReadZip: file %s has unexpected size %d != %d", f.Name, f.UncompressedSize64, size)
462 }
463 }
464 for name := range tt.files {
465 if !have[name] {
466 t.Errorf("ReadZip: missing file %s", name)
467 }
468 }
469 }
470 }
471
472 for _, tt := range []readZipTest{
473 {
474 repo: gitrepo1,
475 rev: "v2.3.4",
476 subdir: "",
477 files: map[string]uint64{
478 "prefix/": 0,
479 "prefix/README": 0,
480 "prefix/v2": 3,
481 },
482 },
483 {
484 repo: hgrepo1,
485 rev: "v2.3.4",
486 subdir: "",
487 files: map[string]uint64{
488 "prefix/.hg_archival.txt": ^uint64(0),
489 "prefix/README": 0,
490 "prefix/v2": 3,
491 },
492 },
493
494 {
495 repo: gitrepo1,
496 rev: "v2",
497 subdir: "",
498 files: map[string]uint64{
499 "prefix/": 0,
500 "prefix/README": 0,
501 "prefix/v2": 3,
502 "prefix/another.txt": 8,
503 "prefix/foo.txt": 13,
504 },
505 },
506 {
507 repo: hgrepo1,
508 rev: "v2",
509 subdir: "",
510 files: map[string]uint64{
511 "prefix/.hg_archival.txt": ^uint64(0),
512 "prefix/README": 0,
513 "prefix/v2": 3,
514 "prefix/another.txt": 8,
515 "prefix/foo.txt": 13,
516 },
517 },
518
519 {
520 repo: gitrepo1,
521 rev: "v3",
522 subdir: "",
523 files: map[string]uint64{
524 "prefix/": 0,
525 "prefix/v3/": 0,
526 "prefix/v3/sub/": 0,
527 "prefix/v3/sub/dir/": 0,
528 "prefix/v3/sub/dir/file.txt": 16,
529 "prefix/README": 0,
530 },
531 },
532 {
533 repo: hgrepo1,
534 rev: "v3",
535 subdir: "",
536 files: map[string]uint64{
537 "prefix/.hg_archival.txt": ^uint64(0),
538 "prefix/.hgtags": 405,
539 "prefix/v3/sub/dir/file.txt": 16,
540 "prefix/README": 0,
541 },
542 },
543
544 {
545 repo: gitrepo1,
546 rev: "v3",
547 subdir: "v3/sub/dir",
548 files: map[string]uint64{
549 "prefix/": 0,
550 "prefix/v3/": 0,
551 "prefix/v3/sub/": 0,
552 "prefix/v3/sub/dir/": 0,
553 "prefix/v3/sub/dir/file.txt": 16,
554 },
555 },
556 {
557 repo: hgrepo1,
558 rev: "v3",
559 subdir: "v3/sub/dir",
560 files: map[string]uint64{
561 "prefix/v3/sub/dir/file.txt": 16,
562 },
563 },
564
565 {
566 repo: gitrepo1,
567 rev: "v3",
568 subdir: "v3/sub",
569 files: map[string]uint64{
570 "prefix/": 0,
571 "prefix/v3/": 0,
572 "prefix/v3/sub/": 0,
573 "prefix/v3/sub/dir/": 0,
574 "prefix/v3/sub/dir/file.txt": 16,
575 },
576 },
577 {
578 repo: hgrepo1,
579 rev: "v3",
580 subdir: "v3/sub",
581 files: map[string]uint64{
582 "prefix/v3/sub/dir/file.txt": 16,
583 },
584 },
585
586 {
587 repo: gitrepo1,
588 rev: "aaaaaaaaab",
589 subdir: "",
590 err: "unknown revision",
591 },
592 {
593 repo: hgrepo1,
594 rev: "aaaaaaaaab",
595 subdir: "",
596 err: "unknown revision",
597 },
598
599 {
600 repo: vgotest1,
601 rev: "submod/v1.0.4",
602 subdir: "submod",
603 files: map[string]uint64{
604 "prefix/": 0,
605 "prefix/submod/": 0,
606 "prefix/submod/go.mod": 53,
607 "prefix/submod/pkg/": 0,
608 "prefix/submod/pkg/p.go": 31,
609 },
610 },
611 } {
612 t.Run(path.Base(tt.repo)+"/"+tt.rev+"/"+tt.subdir, runTest(tt))
613 if tt.repo == gitrepo1 {
614 tt.repo = "localGitRepo"
615 t.Run(path.Base(tt.repo)+"/"+tt.rev+"/"+tt.subdir, runTest(tt))
616 }
617 }
618 }
619
620 var hgmap = map[string]string{
621 "HEAD": "41964ddce1180313bdc01d0a39a2813344d6261d",
622 "9d02800338b8a55be062c838d1f02e0c5780b9eb": "8f49ee7a6ddcdec6f0112d9dca48d4a2e4c3c09e",
623 "76a00fb249b7f93091bc2c89a789dab1fc1bc26f": "88fde824ec8b41a76baa16b7e84212cee9f3edd0",
624 "ede458df7cd0fdca520df19a33158086a8a68e81": "41964ddce1180313bdc01d0a39a2813344d6261d",
625 "97f6aa59c81c623494825b43d39e445566e429a4": "c0cbbfb24c7c3c50c35c7b88e7db777da4ff625d",
626 }
627
628 func TestStat(t *testing.T) {
629 t.Parallel()
630
631 type statTest struct {
632 repo string
633 rev string
634 err string
635 info *RevInfo
636 }
637 runTest := func(tt statTest) func(*testing.T) {
638 return func(t *testing.T) {
639 t.Parallel()
640 ctx := testContext(t)
641
642 r, err := testRepo(ctx, t, tt.repo)
643 if err != nil {
644 t.Fatal(err)
645 }
646 info, err := r.Stat(ctx, tt.rev)
647 if err != nil {
648 if tt.err == "" {
649 t.Fatalf("Stat: unexpected error %v", err)
650 }
651 if !strings.Contains(err.Error(), tt.err) {
652 t.Fatalf("Stat: wrong error %q, want %q", err, tt.err)
653 }
654 if info != nil && info.Origin == nil {
655 t.Errorf("Stat: non-nil info with nil Origin with error %q", err)
656 }
657 return
658 }
659 info.Origin = nil
660 if !reflect.DeepEqual(info, tt.info) {
661 t.Errorf("Stat: incorrect info\nhave %+v\nwant %+v", *info, *tt.info)
662 }
663 }
664 }
665
666 for _, tt := range []statTest{
667 {
668 repo: gitrepo1,
669 rev: "HEAD",
670 info: &RevInfo{
671 Name: "ede458df7cd0fdca520df19a33158086a8a68e81",
672 Short: "ede458df7cd0",
673 Version: "ede458df7cd0fdca520df19a33158086a8a68e81",
674 Time: time.Date(2018, 4, 17, 19, 43, 22, 0, time.UTC),
675 Tags: []string{"v1.2.3", "v1.2.4-annotated"},
676 },
677 },
678 {
679 repo: gitrepo1,
680 rev: "v2",
681 info: &RevInfo{
682 Name: "9d02800338b8a55be062c838d1f02e0c5780b9eb",
683 Short: "9d02800338b8",
684 Version: "9d02800338b8a55be062c838d1f02e0c5780b9eb",
685 Time: time.Date(2018, 4, 17, 20, 00, 32, 0, time.UTC),
686 Tags: []string{"v2.0.2"},
687 },
688 },
689 {
690 repo: gitrepo1,
691 rev: "v2.3.4",
692 info: &RevInfo{
693 Name: "76a00fb249b7f93091bc2c89a789dab1fc1bc26f",
694 Short: "76a00fb249b7",
695 Version: "76a00fb249b7f93091bc2c89a789dab1fc1bc26f",
696 Time: time.Date(2018, 4, 17, 19, 45, 48, 0, time.UTC),
697 Tags: []string{"v2.0.1", "v2.3"},
698 },
699 },
700 {
701 repo: gitrepo1,
702 rev: "v2.3",
703 info: &RevInfo{
704 Name: "76a00fb249b7f93091bc2c89a789dab1fc1bc26f",
705 Short: "76a00fb249b7",
706 Version: "v2.3",
707 Time: time.Date(2018, 4, 17, 19, 45, 48, 0, time.UTC),
708 Tags: []string{"v2.0.1", "v2.3"},
709 },
710 },
711 {
712 repo: gitrepo1,
713 rev: "v1.2.3",
714 info: &RevInfo{
715 Name: "ede458df7cd0fdca520df19a33158086a8a68e81",
716 Short: "ede458df7cd0",
717 Version: "v1.2.3",
718 Time: time.Date(2018, 4, 17, 19, 43, 22, 0, time.UTC),
719 Tags: []string{"v1.2.3", "v1.2.4-annotated"},
720 },
721 },
722 {
723 repo: gitrepo1,
724 rev: "ede458df",
725 info: &RevInfo{
726 Name: "ede458df7cd0fdca520df19a33158086a8a68e81",
727 Short: "ede458df7cd0",
728 Version: "ede458df7cd0fdca520df19a33158086a8a68e81",
729 Time: time.Date(2018, 4, 17, 19, 43, 22, 0, time.UTC),
730 Tags: []string{"v1.2.3", "v1.2.4-annotated"},
731 },
732 },
733 {
734 repo: gitrepo1,
735 rev: "97f6aa59",
736 info: &RevInfo{
737 Name: "97f6aa59c81c623494825b43d39e445566e429a4",
738 Short: "97f6aa59c81c",
739 Version: "97f6aa59c81c623494825b43d39e445566e429a4",
740 Time: time.Date(2018, 4, 17, 20, 0, 19, 0, time.UTC),
741 },
742 },
743 {
744 repo: gitrepo1,
745 rev: "v1.2.4-annotated",
746 info: &RevInfo{
747 Name: "ede458df7cd0fdca520df19a33158086a8a68e81",
748 Short: "ede458df7cd0",
749 Version: "v1.2.4-annotated",
750 Time: time.Date(2018, 4, 17, 19, 43, 22, 0, time.UTC),
751 Tags: []string{"v1.2.3", "v1.2.4-annotated"},
752 },
753 },
754 {
755 repo: gitrepo1,
756 rev: "aaaaaaaaab",
757 err: "unknown revision",
758 },
759 } {
760 t.Run(path.Base(tt.repo)+"/"+tt.rev, runTest(tt))
761 if tt.repo == gitrepo1 {
762 for _, tt.repo = range altRepos() {
763 old := tt
764 var m map[string]string
765 if tt.repo == hgrepo1 {
766 m = hgmap
767 }
768 if tt.info != nil {
769 info := *tt.info
770 tt.info = &info
771 tt.info.Name = remap(tt.info.Name, m)
772 tt.info.Version = remap(tt.info.Version, m)
773 tt.info.Short = remap(tt.info.Short, m)
774 }
775 tt.rev = remap(tt.rev, m)
776 t.Run(path.Base(tt.repo)+"/"+tt.rev, runTest(tt))
777 tt = old
778 }
779 }
780 }
781 }
782
783 func remap(name string, m map[string]string) string {
784 if m[name] != "" {
785 return m[name]
786 }
787 if AllHex(name) {
788 for k, v := range m {
789 if strings.HasPrefix(k, name) {
790 return v[:len(name)]
791 }
792 }
793 }
794 return name
795 }
796
View as plain text