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  }