...

Source file src/cmd/go/internal/modfetch/codehost/git_test.go

Documentation: cmd/go/internal/modfetch/codehost

     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  	"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  // TODO: Convert gitrepo1 to svn, bzr, fossil and add tests.
    46  // For now, at least the hgrepo1 tests check the general vcs.go logic.
    47  
    48  // localGitRepo is like gitrepo1 but allows archive access
    49  // (although that doesn't really matter after CL 120041),
    50  // and has a file:// URL instead of http:// or https://
    51  // (which might still matter).
    52  var localGitRepo string
    53  
    54  // localGitURL initializes the repo in localGitRepo and returns its URL.
    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  		// Clone gitrepo1 into a local directory.
    63  		// If we use a file:// URL to access the local directory,
    64  		// then git starts up all the usual protocol machinery,
    65  		// which will let us test remote git archive invocations.
    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  	// 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  	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  	// Redirect the module cache to a fresh directory to avoid crosstalk, and make
   121  	// it read/write so that the test can still clean it up easily when done.
   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  // A testWriter is an io.Writer that writes to a test's log.
   135  //
   136  // The writer batches written data until the last byte of a write is a newline
   137  // character, then flushes the batched data as a single call to Logf.
   138  // Any remaining unflushed data is logged during Cleanup.
   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  			// Clear hashes.
   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", // not tip due to bad hgrepo1 conversion
   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 // TestLatest and ../../../testdata/script/reuse_git.txt test Origin well enough
   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", // branch
   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", // badly-named branch (semver should be a tag)
   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", // badly-named tag (we only respect full semver v2.3.0)
   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", // tag
   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", // hash prefix in refs
   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", // hash prefix not in refs
   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", // annotated tag uses unwrapped commit hash
   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