github.com/zxy12/go_duplicate_112_new@v0.0.0-20200807091221-747231827200/src/cmd/go/internal/modfetch/coderepo_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 modfetch
     6  
     7  import (
     8  	"archive/zip"
     9  	"internal/testenv"
    10  	"io"
    11  	"io/ioutil"
    12  	"log"
    13  	"os"
    14  	"reflect"
    15  	"strings"
    16  	"testing"
    17  	"time"
    18  
    19  	"cmd/go/internal/modfetch/codehost"
    20  )
    21  
    22  func TestMain(m *testing.M) {
    23  	os.Exit(testMain(m))
    24  }
    25  
    26  func testMain(m *testing.M) int {
    27  	dir, err := ioutil.TempDir("", "gitrepo-test-")
    28  	if err != nil {
    29  		log.Fatal(err)
    30  	}
    31  	defer os.RemoveAll(dir)
    32  
    33  	codehost.WorkRoot = dir
    34  	return m.Run()
    35  }
    36  
    37  const (
    38  	vgotest1git = "github.com/rsc/vgotest1"
    39  	vgotest1hg  = "vcs-test.golang.org/hg/vgotest1.hg"
    40  )
    41  
    42  var altVgotests = []string{
    43  	vgotest1hg,
    44  }
    45  
    46  var codeRepoTests = []struct {
    47  	path     string
    48  	lookerr  string
    49  	mpath    string
    50  	rev      string
    51  	err      string
    52  	version  string
    53  	name     string
    54  	short    string
    55  	time     time.Time
    56  	gomod    string
    57  	gomoderr string
    58  	zip      []string
    59  	ziperr   string
    60  }{
    61  	{
    62  		path:    "github.com/rsc/vgotest1",
    63  		rev:     "v0.0.0",
    64  		version: "v0.0.0",
    65  		name:    "80d85c5d4d17598a0e9055e7c175a32b415d6128",
    66  		short:   "80d85c5d4d17",
    67  		time:    time.Date(2018, 2, 19, 23, 10, 6, 0, time.UTC),
    68  		zip: []string{
    69  			"LICENSE",
    70  			"README.md",
    71  			"pkg/p.go",
    72  		},
    73  	},
    74  	{
    75  		path:    "github.com/rsc/vgotest1",
    76  		rev:     "v1.0.0",
    77  		version: "v1.0.0",
    78  		name:    "80d85c5d4d17598a0e9055e7c175a32b415d6128",
    79  		short:   "80d85c5d4d17",
    80  		time:    time.Date(2018, 2, 19, 23, 10, 6, 0, time.UTC),
    81  		zip: []string{
    82  			"LICENSE",
    83  			"README.md",
    84  			"pkg/p.go",
    85  		},
    86  	},
    87  	{
    88  		path:    "github.com/rsc/vgotest1/v2",
    89  		rev:     "v2.0.0",
    90  		version: "v2.0.0",
    91  		name:    "45f53230a74ad275c7127e117ac46914c8126160",
    92  		short:   "45f53230a74a",
    93  		time:    time.Date(2018, 7, 19, 1, 21, 27, 0, time.UTC),
    94  		ziperr:  "missing github.com/rsc/vgotest1/go.mod and .../v2/go.mod at revision v2.0.0",
    95  	},
    96  	{
    97  		path:    "github.com/rsc/vgotest1",
    98  		rev:     "80d85c5",
    99  		version: "v1.0.0",
   100  		name:    "80d85c5d4d17598a0e9055e7c175a32b415d6128",
   101  		short:   "80d85c5d4d17",
   102  		time:    time.Date(2018, 2, 19, 23, 10, 6, 0, time.UTC),
   103  		zip: []string{
   104  			"LICENSE",
   105  			"README.md",
   106  			"pkg/p.go",
   107  		},
   108  	},
   109  	{
   110  		path:    "github.com/rsc/vgotest1",
   111  		rev:     "mytag",
   112  		version: "v1.0.0",
   113  		name:    "80d85c5d4d17598a0e9055e7c175a32b415d6128",
   114  		short:   "80d85c5d4d17",
   115  		time:    time.Date(2018, 2, 19, 23, 10, 6, 0, time.UTC),
   116  		zip: []string{
   117  			"LICENSE",
   118  			"README.md",
   119  			"pkg/p.go",
   120  		},
   121  	},
   122  	{
   123  		path:     "github.com/rsc/vgotest1/v2",
   124  		rev:      "45f53230a",
   125  		version:  "v2.0.0",
   126  		name:     "45f53230a74ad275c7127e117ac46914c8126160",
   127  		short:    "45f53230a74a",
   128  		time:     time.Date(2018, 7, 19, 1, 21, 27, 0, time.UTC),
   129  		gomoderr: "missing github.com/rsc/vgotest1/go.mod and .../v2/go.mod at revision v2.0.0",
   130  		ziperr:   "missing github.com/rsc/vgotest1/go.mod and .../v2/go.mod at revision v2.0.0",
   131  	},
   132  	{
   133  		path:    "github.com/rsc/vgotest1/v54321",
   134  		rev:     "80d85c5",
   135  		version: "v54321.0.0-20180219231006-80d85c5d4d17",
   136  		name:    "80d85c5d4d17598a0e9055e7c175a32b415d6128",
   137  		short:   "80d85c5d4d17",
   138  		time:    time.Date(2018, 2, 19, 23, 10, 6, 0, time.UTC),
   139  		ziperr:  "missing github.com/rsc/vgotest1/go.mod and .../v54321/go.mod at revision 80d85c5d4d17",
   140  	},
   141  	{
   142  		path: "github.com/rsc/vgotest1/submod",
   143  		rev:  "v1.0.0",
   144  		err:  "unknown revision submod/v1.0.0",
   145  	},
   146  	{
   147  		path: "github.com/rsc/vgotest1/submod",
   148  		rev:  "v1.0.3",
   149  		err:  "unknown revision submod/v1.0.3",
   150  	},
   151  	{
   152  		path:    "github.com/rsc/vgotest1/submod",
   153  		rev:     "v1.0.4",
   154  		version: "v1.0.4",
   155  		name:    "8afe2b2efed96e0880ecd2a69b98a53b8c2738b6",
   156  		short:   "8afe2b2efed9",
   157  		time:    time.Date(2018, 2, 19, 23, 12, 7, 0, time.UTC),
   158  		gomod:   "module \"github.com/vgotest1/submod\" // submod/go.mod\n",
   159  		zip: []string{
   160  			"go.mod",
   161  			"pkg/p.go",
   162  			"LICENSE",
   163  		},
   164  	},
   165  	{
   166  		path:    "github.com/rsc/vgotest1",
   167  		rev:     "v1.1.0",
   168  		version: "v1.1.0",
   169  		name:    "b769f2de407a4db81af9c5de0a06016d60d2ea09",
   170  		short:   "b769f2de407a",
   171  		time:    time.Date(2018, 2, 19, 23, 13, 36, 0, time.UTC),
   172  		gomod:   "module \"github.com/rsc/vgotest1\" // root go.mod\nrequire \"github.com/rsc/vgotest1/submod\" v1.0.5\n",
   173  		zip: []string{
   174  			"LICENSE",
   175  			"README.md",
   176  			"go.mod",
   177  			"pkg/p.go",
   178  		},
   179  	},
   180  	{
   181  		path:    "github.com/rsc/vgotest1/v2",
   182  		rev:     "v2.0.1",
   183  		version: "v2.0.1",
   184  		name:    "ea65f87c8f52c15ea68f3bdd9925ef17e20d91e9",
   185  		short:   "ea65f87c8f52",
   186  		time:    time.Date(2018, 2, 19, 23, 14, 23, 0, time.UTC),
   187  		gomod:   "module \"github.com/rsc/vgotest1/v2\" // root go.mod\n",
   188  	},
   189  	{
   190  		path:     "github.com/rsc/vgotest1/v2",
   191  		rev:      "v2.0.3",
   192  		version:  "v2.0.3",
   193  		name:     "f18795870fb14388a21ef3ebc1d75911c8694f31",
   194  		short:    "f18795870fb1",
   195  		time:     time.Date(2018, 2, 19, 23, 16, 4, 0, time.UTC),
   196  		gomoderr: "github.com/rsc/vgotest1/v2/go.mod has non-.../v2 module path \"github.com/rsc/vgotest\" at revision v2.0.3",
   197  	},
   198  	{
   199  		path:     "github.com/rsc/vgotest1/v2",
   200  		rev:      "v2.0.4",
   201  		version:  "v2.0.4",
   202  		name:     "1f863feb76bc7029b78b21c5375644838962f88d",
   203  		short:    "1f863feb76bc",
   204  		time:     time.Date(2018, 2, 20, 0, 3, 38, 0, time.UTC),
   205  		gomoderr: "github.com/rsc/vgotest1/go.mod and .../v2/go.mod both have .../v2 module paths at revision v2.0.4",
   206  	},
   207  	{
   208  		path:    "github.com/rsc/vgotest1/v2",
   209  		rev:     "v2.0.5",
   210  		version: "v2.0.5",
   211  		name:    "2f615117ce481c8efef46e0cc0b4b4dccfac8fea",
   212  		short:   "2f615117ce48",
   213  		time:    time.Date(2018, 2, 20, 0, 3, 59, 0, time.UTC),
   214  		gomod:   "module \"github.com/rsc/vgotest1/v2\" // v2/go.mod\n",
   215  	},
   216  	{
   217  		// redirect to github
   218  		path:    "rsc.io/quote",
   219  		rev:     "v1.0.0",
   220  		version: "v1.0.0",
   221  		name:    "f488df80bcdbd3e5bafdc24ad7d1e79e83edd7e6",
   222  		short:   "f488df80bcdb",
   223  		time:    time.Date(2018, 2, 14, 0, 45, 20, 0, time.UTC),
   224  		gomod:   "module \"rsc.io/quote\"\n",
   225  	},
   226  	{
   227  		// redirect to static hosting proxy
   228  		path:    "swtch.com/testmod",
   229  		rev:     "v1.0.0",
   230  		version: "v1.0.0",
   231  		// NO name or short - we intentionally ignore those in the proxy protocol
   232  		time:  time.Date(1972, 7, 18, 12, 34, 56, 0, time.UTC),
   233  		gomod: "module \"swtch.com/testmod\"\n",
   234  	},
   235  	{
   236  		// redirect to googlesource
   237  		path:    "golang.org/x/text",
   238  		rev:     "4e4a3210bb",
   239  		version: "v0.3.1-0.20180208041248-4e4a3210bb54",
   240  		name:    "4e4a3210bb54bb31f6ab2cdca2edcc0b50c420c1",
   241  		short:   "4e4a3210bb54",
   242  		time:    time.Date(2018, 2, 8, 4, 12, 48, 0, time.UTC),
   243  	},
   244  	{
   245  		path:    "github.com/pkg/errors",
   246  		rev:     "v0.8.0",
   247  		version: "v0.8.0",
   248  		name:    "645ef00459ed84a119197bfb8d8205042c6df63d",
   249  		short:   "645ef00459ed",
   250  		time:    time.Date(2016, 9, 29, 1, 48, 1, 0, time.UTC),
   251  	},
   252  	{
   253  		// package in subdirectory - custom domain
   254  		// In general we can't reject these definitively in Lookup,
   255  		// but gopkg.in is special.
   256  		path:    "gopkg.in/yaml.v2/abc",
   257  		lookerr: "invalid module path \"gopkg.in/yaml.v2/abc\"",
   258  	},
   259  	{
   260  		// package in subdirectory - github
   261  		// Because it's a package, Stat should fail entirely.
   262  		path: "github.com/rsc/quote/buggy",
   263  		rev:  "c4d4236f",
   264  		err:  "missing github.com/rsc/quote/buggy/go.mod at revision c4d4236f9242",
   265  	},
   266  	{
   267  		path:    "gopkg.in/yaml.v2",
   268  		rev:     "d670f940",
   269  		version: "v2.0.0",
   270  		name:    "d670f9405373e636a5a2765eea47fac0c9bc91a4",
   271  		short:   "d670f9405373",
   272  		time:    time.Date(2018, 1, 9, 11, 43, 31, 0, time.UTC),
   273  		gomod:   "module gopkg.in/yaml.v2\n",
   274  	},
   275  	{
   276  		path:    "gopkg.in/check.v1",
   277  		rev:     "20d25e280405",
   278  		version: "v1.0.0-20161208181325-20d25e280405",
   279  		name:    "20d25e2804050c1cd24a7eea1e7a6447dd0e74ec",
   280  		short:   "20d25e280405",
   281  		time:    time.Date(2016, 12, 8, 18, 13, 25, 0, time.UTC),
   282  		gomod:   "module gopkg.in/check.v1\n",
   283  	},
   284  	{
   285  		path:    "gopkg.in/yaml.v2",
   286  		rev:     "v2",
   287  		version: "v2.2.2",
   288  		name:    "51d6538a90f86fe93ac480b35f37b2be17fef232",
   289  		short:   "51d6538a90f8",
   290  		time:    time.Date(2018, 11, 15, 11, 05, 04, 0, time.UTC),
   291  		gomod:   "module \"gopkg.in/yaml.v2\"\n\nrequire (\n\t\"gopkg.in/check.v1\" v0.0.0-20161208181325-20d25e280405\n)\n",
   292  	},
   293  	{
   294  		path:    "vcs-test.golang.org/go/mod/gitrepo1",
   295  		rev:     "master",
   296  		version: "v1.2.4-annotated",
   297  		name:    "ede458df7cd0fdca520df19a33158086a8a68e81",
   298  		short:   "ede458df7cd0",
   299  		time:    time.Date(2018, 4, 17, 19, 43, 22, 0, time.UTC),
   300  		gomod:   "module vcs-test.golang.org/go/mod/gitrepo1\n",
   301  	},
   302  	{
   303  		path:    "gopkg.in/natefinch/lumberjack.v2",
   304  		rev:     "latest",
   305  		version: "v2.0.0-20170531160350-a96e63847dc3",
   306  		name:    "a96e63847dc3c67d17befa69c303767e2f84e54f",
   307  		short:   "a96e63847dc3",
   308  		time:    time.Date(2017, 5, 31, 16, 3, 50, 0, time.UTC),
   309  		gomod:   "module gopkg.in/natefinch/lumberjack.v2\n",
   310  	},
   311  	{
   312  		path: "gopkg.in/natefinch/lumberjack.v2",
   313  		// This repo has a v2.1 tag.
   314  		// We only allow semver references to tags that are fully qualified, as in v2.1.0.
   315  		// Because we can't record v2.1.0 (the actual tag is v2.1), we record a pseudo-version
   316  		// instead, same as if the tag were any other non-version-looking string.
   317  		// We use a v2 pseudo-version here because of the .v2 in the path, not because
   318  		// of the v2 in the rev.
   319  		rev:     "v2.1", // non-canonical semantic version turns into pseudo-version
   320  		version: "v2.0.0-20170531160350-a96e63847dc3",
   321  		name:    "a96e63847dc3c67d17befa69c303767e2f84e54f",
   322  		short:   "a96e63847dc3",
   323  		time:    time.Date(2017, 5, 31, 16, 3, 50, 0, time.UTC),
   324  		gomod:   "module gopkg.in/natefinch/lumberjack.v2\n",
   325  	},
   326  	{
   327  		path:    "nanomsg.org/go/mangos/v2",
   328  		rev:     "v2.0.2",
   329  		version: "v2.0.2",
   330  		name:    "63f66a65137b9a648ac9f7bf0160b4a4d17d7999",
   331  		short:   "63f66a65137b",
   332  		time:    time.Date(2018, 12, 1, 15, 7, 40, 0, time.UTC),
   333  		gomod:   "module nanomsg.org/go/mangos/v2\n\nrequire (\n\tgithub.com/Microsoft/go-winio v0.4.11\n\tgithub.com/droundy/goopt v0.0.0-20170604162106-0b8effe182da\n\tgithub.com/gopherjs/gopherjs v0.0.0-20181103185306-d547d1d9531e // indirect\n\tgithub.com/gorilla/websocket v1.4.0\n\tgithub.com/jtolds/gls v4.2.1+incompatible // indirect\n\tgithub.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d // indirect\n\tgithub.com/smartystreets/goconvey v0.0.0-20181108003508-044398e4856c\n\tgolang.org/x/sys v0.0.0-20181128092732-4ed8d59d0b35 // indirect\n)\n",
   334  	},
   335  }
   336  
   337  func TestCodeRepo(t *testing.T) {
   338  	testenv.MustHaveExternalNetwork(t)
   339  
   340  	tmpdir, err := ioutil.TempDir("", "vgo-modfetch-test-")
   341  	if err != nil {
   342  		t.Fatal(err)
   343  	}
   344  	defer os.RemoveAll(tmpdir)
   345  	for _, tt := range codeRepoTests {
   346  		f := func(t *testing.T) {
   347  			repo, err := Lookup(tt.path)
   348  			if tt.lookerr != "" {
   349  				if err != nil && err.Error() == tt.lookerr {
   350  					return
   351  				}
   352  				t.Errorf("Lookup(%q): %v, want error %q", tt.path, err, tt.lookerr)
   353  			}
   354  			if err != nil {
   355  				t.Fatalf("Lookup(%q): %v", tt.path, err)
   356  			}
   357  			if tt.mpath == "" {
   358  				tt.mpath = tt.path
   359  			}
   360  			if mpath := repo.ModulePath(); mpath != tt.mpath {
   361  				t.Errorf("repo.ModulePath() = %q, want %q", mpath, tt.mpath)
   362  			}
   363  			info, err := repo.Stat(tt.rev)
   364  			if err != nil {
   365  				if tt.err != "" {
   366  					if !strings.Contains(err.Error(), tt.err) {
   367  						t.Fatalf("repoStat(%q): %v, wanted %q", tt.rev, err, tt.err)
   368  					}
   369  					return
   370  				}
   371  				t.Fatalf("repo.Stat(%q): %v", tt.rev, err)
   372  			}
   373  			if tt.err != "" {
   374  				t.Errorf("repo.Stat(%q): success, wanted error", tt.rev)
   375  			}
   376  			if info.Version != tt.version {
   377  				t.Errorf("info.Version = %q, want %q", info.Version, tt.version)
   378  			}
   379  			if info.Name != tt.name {
   380  				t.Errorf("info.Name = %q, want %q", info.Name, tt.name)
   381  			}
   382  			if info.Short != tt.short {
   383  				t.Errorf("info.Short = %q, want %q", info.Short, tt.short)
   384  			}
   385  			if !info.Time.Equal(tt.time) {
   386  				t.Errorf("info.Time = %v, want %v", info.Time, tt.time)
   387  			}
   388  			if tt.gomod != "" || tt.gomoderr != "" {
   389  				data, err := repo.GoMod(tt.version)
   390  				if err != nil && tt.gomoderr == "" {
   391  					t.Errorf("repo.GoMod(%q): %v", tt.version, err)
   392  				} else if err != nil && tt.gomoderr != "" {
   393  					if err.Error() != tt.gomoderr {
   394  						t.Errorf("repo.GoMod(%q): %v, want %q", tt.version, err, tt.gomoderr)
   395  					}
   396  				} else if tt.gomoderr != "" {
   397  					t.Errorf("repo.GoMod(%q) = %q, want error %q", tt.version, data, tt.gomoderr)
   398  				} else if string(data) != tt.gomod {
   399  					t.Errorf("repo.GoMod(%q) = %q, want %q", tt.version, data, tt.gomod)
   400  				}
   401  			}
   402  			if tt.zip != nil || tt.ziperr != "" {
   403  				f, err := ioutil.TempFile(tmpdir, tt.version+".zip.")
   404  				if err != nil {
   405  					t.Fatalf("ioutil.TempFile: %v", err)
   406  				}
   407  				zipfile := f.Name()
   408  				err = repo.Zip(f, tt.version)
   409  				f.Close()
   410  				if err != nil {
   411  					if tt.ziperr != "" {
   412  						if err.Error() == tt.ziperr {
   413  							return
   414  						}
   415  						t.Fatalf("repo.Zip(%q): %v, want error %q", tt.version, err, tt.ziperr)
   416  					}
   417  					t.Fatalf("repo.Zip(%q): %v", tt.version, err)
   418  				}
   419  				if tt.ziperr != "" {
   420  					t.Errorf("repo.Zip(%q): success, want error %q", tt.version, tt.ziperr)
   421  				}
   422  				prefix := tt.path + "@" + tt.version + "/"
   423  				z, err := zip.OpenReader(zipfile)
   424  				if err != nil {
   425  					t.Fatalf("open zip %s: %v", zipfile, err)
   426  				}
   427  				var names []string
   428  				for _, file := range z.File {
   429  					if !strings.HasPrefix(file.Name, prefix) {
   430  						t.Errorf("zip entry %v does not start with prefix %v", file.Name, prefix)
   431  						continue
   432  					}
   433  					names = append(names, file.Name[len(prefix):])
   434  				}
   435  				z.Close()
   436  				if !reflect.DeepEqual(names, tt.zip) {
   437  					t.Fatalf("zip = %v\nwant %v\n", names, tt.zip)
   438  				}
   439  			}
   440  		}
   441  		t.Run(strings.ReplaceAll(tt.path, "/", "_")+"/"+tt.rev, f)
   442  		if strings.HasPrefix(tt.path, vgotest1git) {
   443  			for _, alt := range altVgotests {
   444  				// Note: Communicating with f through tt; should be cleaned up.
   445  				old := tt
   446  				tt.path = alt + strings.TrimPrefix(tt.path, vgotest1git)
   447  				if strings.HasPrefix(tt.mpath, vgotest1git) {
   448  					tt.mpath = alt + strings.TrimPrefix(tt.mpath, vgotest1git)
   449  				}
   450  				var m map[string]string
   451  				if alt == vgotest1hg {
   452  					m = hgmap
   453  				}
   454  				tt.version = remap(tt.version, m)
   455  				tt.name = remap(tt.name, m)
   456  				tt.short = remap(tt.short, m)
   457  				tt.rev = remap(tt.rev, m)
   458  				tt.gomoderr = remap(tt.gomoderr, m)
   459  				tt.ziperr = remap(tt.ziperr, m)
   460  				t.Run(strings.ReplaceAll(tt.path, "/", "_")+"/"+tt.rev, f)
   461  				tt = old
   462  			}
   463  		}
   464  	}
   465  }
   466  
   467  var hgmap = map[string]string{
   468  	"github.com/rsc/vgotest1/":                 "vcs-test.golang.org/hg/vgotest1.hg/",
   469  	"f18795870fb14388a21ef3ebc1d75911c8694f31": "a9ad6d1d14eb544f459f446210c7eb3b009807c6",
   470  	"ea65f87c8f52c15ea68f3bdd9925ef17e20d91e9": "f1fc0f22021b638d073d31c752847e7bf385def7",
   471  	"b769f2de407a4db81af9c5de0a06016d60d2ea09": "92c7eb888b4fac17f1c6bd2e1060a1b881a3b832",
   472  	"8afe2b2efed96e0880ecd2a69b98a53b8c2738b6": "4e58084d459ae7e79c8c2264d0e8e9a92eb5cd44",
   473  	"2f615117ce481c8efef46e0cc0b4b4dccfac8fea": "879ea98f7743c8eff54f59a918f3a24123d1cf46",
   474  	"80d85c5d4d17598a0e9055e7c175a32b415d6128": "e125018e286a4b09061079a81e7b537070b7ff71",
   475  	"1f863feb76bc7029b78b21c5375644838962f88d": "bf63880162304a9337477f3858f5b7e255c75459",
   476  	"45f53230a74ad275c7127e117ac46914c8126160": "814fce58e83abd5bf2a13892e0b0e1198abefcd4",
   477  }
   478  
   479  func remap(name string, m map[string]string) string {
   480  	if m[name] != "" {
   481  		return m[name]
   482  	}
   483  	if codehost.AllHex(name) {
   484  		for k, v := range m {
   485  			if strings.HasPrefix(k, name) {
   486  				return v[:len(name)]
   487  			}
   488  		}
   489  	}
   490  	for k, v := range m {
   491  		name = strings.ReplaceAll(name, k, v)
   492  		if codehost.AllHex(k) {
   493  			name = strings.ReplaceAll(name, k[:12], v[:12])
   494  		}
   495  	}
   496  	return name
   497  }
   498  
   499  var codeRepoVersionsTests = []struct {
   500  	path     string
   501  	prefix   string
   502  	versions []string
   503  }{
   504  	{
   505  		path:     "github.com/rsc/vgotest1",
   506  		versions: []string{"v0.0.0", "v0.0.1", "v1.0.0", "v1.0.1", "v1.0.2", "v1.0.3", "v1.1.0", "v2.0.0+incompatible"},
   507  	},
   508  	{
   509  		path:     "github.com/rsc/vgotest1",
   510  		prefix:   "v1.0",
   511  		versions: []string{"v1.0.0", "v1.0.1", "v1.0.2", "v1.0.3"},
   512  	},
   513  	{
   514  		path:     "github.com/rsc/vgotest1/v2",
   515  		versions: []string{"v2.0.0", "v2.0.1", "v2.0.2", "v2.0.3", "v2.0.4", "v2.0.5", "v2.0.6"},
   516  	},
   517  	{
   518  		path:     "swtch.com/testmod",
   519  		versions: []string{"v1.0.0", "v1.1.1"},
   520  	},
   521  	{
   522  		path:     "gopkg.in/russross/blackfriday.v2",
   523  		versions: []string{"v2.0.0", "v2.0.1"},
   524  	},
   525  	{
   526  		path:     "gopkg.in/natefinch/lumberjack.v2",
   527  		versions: []string{"v2.0.0"},
   528  	},
   529  }
   530  
   531  func TestCodeRepoVersions(t *testing.T) {
   532  	testenv.MustHaveExternalNetwork(t)
   533  
   534  	tmpdir, err := ioutil.TempDir("", "vgo-modfetch-test-")
   535  	if err != nil {
   536  		t.Fatal(err)
   537  	}
   538  	defer os.RemoveAll(tmpdir)
   539  	for _, tt := range codeRepoVersionsTests {
   540  		t.Run(strings.ReplaceAll(tt.path, "/", "_"), func(t *testing.T) {
   541  			repo, err := Lookup(tt.path)
   542  			if err != nil {
   543  				t.Fatalf("Lookup(%q): %v", tt.path, err)
   544  			}
   545  			list, err := repo.Versions(tt.prefix)
   546  			if err != nil {
   547  				t.Fatalf("Versions(%q): %v", tt.prefix, err)
   548  			}
   549  			if !reflect.DeepEqual(list, tt.versions) {
   550  				t.Fatalf("Versions(%q):\nhave %v\nwant %v", tt.prefix, list, tt.versions)
   551  			}
   552  		})
   553  	}
   554  }
   555  
   556  var latestTests = []struct {
   557  	path    string
   558  	version string
   559  	err     string
   560  }{
   561  	{
   562  		path: "github.com/rsc/empty",
   563  		err:  "no commits",
   564  	},
   565  	{
   566  		path:    "github.com/rsc/vgotest1",
   567  		version: "v0.0.0-20180219223237-a08abb797a67",
   568  	},
   569  	{
   570  		path: "github.com/rsc/vgotest1/subdir",
   571  		err:  "missing github.com/rsc/vgotest1/subdir/go.mod at revision a08abb797a67",
   572  	},
   573  	{
   574  		path:    "swtch.com/testmod",
   575  		version: "v1.1.1",
   576  	},
   577  }
   578  
   579  func TestLatest(t *testing.T) {
   580  	testenv.MustHaveExternalNetwork(t)
   581  
   582  	tmpdir, err := ioutil.TempDir("", "vgo-modfetch-test-")
   583  	if err != nil {
   584  		t.Fatal(err)
   585  	}
   586  	defer os.RemoveAll(tmpdir)
   587  	for _, tt := range latestTests {
   588  		name := strings.ReplaceAll(tt.path, "/", "_")
   589  		t.Run(name, func(t *testing.T) {
   590  			repo, err := Lookup(tt.path)
   591  			if err != nil {
   592  				t.Fatalf("Lookup(%q): %v", tt.path, err)
   593  			}
   594  			info, err := repo.Latest()
   595  			if err != nil {
   596  				if tt.err != "" {
   597  					if err.Error() == tt.err {
   598  						return
   599  					}
   600  					t.Fatalf("Latest(): %v, want %q", err, tt.err)
   601  				}
   602  				t.Fatalf("Latest(): %v", err)
   603  			}
   604  			if tt.err != "" {
   605  				t.Fatalf("Latest() = %v, want error %q", info.Version, tt.err)
   606  			}
   607  			if info.Version != tt.version {
   608  				t.Fatalf("Latest() = %v, want %v", info.Version, tt.version)
   609  			}
   610  		})
   611  	}
   612  }
   613  
   614  // fixedTagsRepo is a fake codehost.Repo that returns a fixed list of tags
   615  type fixedTagsRepo struct {
   616  	tags []string
   617  }
   618  
   619  func (ch *fixedTagsRepo) Tags(string) ([]string, error)                  { return ch.tags, nil }
   620  func (ch *fixedTagsRepo) Latest() (*codehost.RevInfo, error)             { panic("not impl") }
   621  func (ch *fixedTagsRepo) ReadFile(string, string, int64) ([]byte, error) { panic("not impl") }
   622  func (ch *fixedTagsRepo) ReadFileRevs([]string, string, int64) (map[string]*codehost.FileRev, error) {
   623  	panic("not impl")
   624  }
   625  func (ch *fixedTagsRepo) ReadZip(string, string, int64) (io.ReadCloser, string, error) {
   626  	panic("not impl")
   627  }
   628  func (ch *fixedTagsRepo) RecentTag(string, string) (string, error) {
   629  	panic("not impl")
   630  }
   631  func (ch *fixedTagsRepo) Stat(string) (*codehost.RevInfo, error) { panic("not impl") }
   632  
   633  func TestNonCanonicalSemver(t *testing.T) {
   634  	root := "golang.org/x/issue24476"
   635  	ch := &fixedTagsRepo{
   636  		tags: []string{
   637  			"", "huh?", "1.0.1",
   638  			// what about "version 1 dot dogcow"?
   639  			"v1.🐕.🐄",
   640  			"v1", "v0.1",
   641  			// and one normal one that should pass through
   642  			"v1.0.1",
   643  		},
   644  	}
   645  
   646  	cr, err := newCodeRepo(ch, root, root)
   647  	if err != nil {
   648  		t.Fatal(err)
   649  	}
   650  
   651  	v, err := cr.Versions("")
   652  	if err != nil {
   653  		t.Fatal(err)
   654  	}
   655  	if len(v) != 1 || v[0] != "v1.0.1" {
   656  		t.Fatal("unexpected versions returned:", v)
   657  	}
   658  }