github.com/megatontech/mynoteforgo@v0.0.0-20200507084910-5d0c6ea6e890/源码/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  
   328  func TestCodeRepo(t *testing.T) {
   329  	testenv.MustHaveExternalNetwork(t)
   330  
   331  	tmpdir, err := ioutil.TempDir("", "vgo-modfetch-test-")
   332  	if err != nil {
   333  		t.Fatal(err)
   334  	}
   335  	defer os.RemoveAll(tmpdir)
   336  	for _, tt := range codeRepoTests {
   337  		f := func(t *testing.T) {
   338  			repo, err := Lookup(tt.path)
   339  			if tt.lookerr != "" {
   340  				if err != nil && err.Error() == tt.lookerr {
   341  					return
   342  				}
   343  				t.Errorf("Lookup(%q): %v, want error %q", tt.path, err, tt.lookerr)
   344  			}
   345  			if err != nil {
   346  				t.Fatalf("Lookup(%q): %v", tt.path, err)
   347  			}
   348  			if tt.mpath == "" {
   349  				tt.mpath = tt.path
   350  			}
   351  			if mpath := repo.ModulePath(); mpath != tt.mpath {
   352  				t.Errorf("repo.ModulePath() = %q, want %q", mpath, tt.mpath)
   353  			}
   354  			info, err := repo.Stat(tt.rev)
   355  			if err != nil {
   356  				if tt.err != "" {
   357  					if !strings.Contains(err.Error(), tt.err) {
   358  						t.Fatalf("repoStat(%q): %v, wanted %q", tt.rev, err, tt.err)
   359  					}
   360  					return
   361  				}
   362  				t.Fatalf("repo.Stat(%q): %v", tt.rev, err)
   363  			}
   364  			if tt.err != "" {
   365  				t.Errorf("repo.Stat(%q): success, wanted error", tt.rev)
   366  			}
   367  			if info.Version != tt.version {
   368  				t.Errorf("info.Version = %q, want %q", info.Version, tt.version)
   369  			}
   370  			if info.Name != tt.name {
   371  				t.Errorf("info.Name = %q, want %q", info.Name, tt.name)
   372  			}
   373  			if info.Short != tt.short {
   374  				t.Errorf("info.Short = %q, want %q", info.Short, tt.short)
   375  			}
   376  			if !info.Time.Equal(tt.time) {
   377  				t.Errorf("info.Time = %v, want %v", info.Time, tt.time)
   378  			}
   379  			if tt.gomod != "" || tt.gomoderr != "" {
   380  				data, err := repo.GoMod(tt.version)
   381  				if err != nil && tt.gomoderr == "" {
   382  					t.Errorf("repo.GoMod(%q): %v", tt.version, err)
   383  				} else if err != nil && tt.gomoderr != "" {
   384  					if err.Error() != tt.gomoderr {
   385  						t.Errorf("repo.GoMod(%q): %v, want %q", tt.version, err, tt.gomoderr)
   386  					}
   387  				} else if tt.gomoderr != "" {
   388  					t.Errorf("repo.GoMod(%q) = %q, want error %q", tt.version, data, tt.gomoderr)
   389  				} else if string(data) != tt.gomod {
   390  					t.Errorf("repo.GoMod(%q) = %q, want %q", tt.version, data, tt.gomod)
   391  				}
   392  			}
   393  			if tt.zip != nil || tt.ziperr != "" {
   394  				f, err := ioutil.TempFile(tmpdir, tt.version+".zip.")
   395  				if err != nil {
   396  					t.Fatalf("ioutil.TempFile: %v", err)
   397  				}
   398  				zipfile := f.Name()
   399  				err = repo.Zip(f, tt.version)
   400  				f.Close()
   401  				if err != nil {
   402  					if tt.ziperr != "" {
   403  						if err.Error() == tt.ziperr {
   404  							return
   405  						}
   406  						t.Fatalf("repo.Zip(%q): %v, want error %q", tt.version, err, tt.ziperr)
   407  					}
   408  					t.Fatalf("repo.Zip(%q): %v", tt.version, err)
   409  				}
   410  				if tt.ziperr != "" {
   411  					t.Errorf("repo.Zip(%q): success, want error %q", tt.version, tt.ziperr)
   412  				}
   413  				prefix := tt.path + "@" + tt.version + "/"
   414  				z, err := zip.OpenReader(zipfile)
   415  				if err != nil {
   416  					t.Fatalf("open zip %s: %v", zipfile, err)
   417  				}
   418  				var names []string
   419  				for _, file := range z.File {
   420  					if !strings.HasPrefix(file.Name, prefix) {
   421  						t.Errorf("zip entry %v does not start with prefix %v", file.Name, prefix)
   422  						continue
   423  					}
   424  					names = append(names, file.Name[len(prefix):])
   425  				}
   426  				z.Close()
   427  				if !reflect.DeepEqual(names, tt.zip) {
   428  					t.Fatalf("zip = %v\nwant %v\n", names, tt.zip)
   429  				}
   430  			}
   431  		}
   432  		t.Run(strings.ReplaceAll(tt.path, "/", "_")+"/"+tt.rev, f)
   433  		if strings.HasPrefix(tt.path, vgotest1git) {
   434  			for _, alt := range altVgotests {
   435  				// Note: Communicating with f through tt; should be cleaned up.
   436  				old := tt
   437  				tt.path = alt + strings.TrimPrefix(tt.path, vgotest1git)
   438  				if strings.HasPrefix(tt.mpath, vgotest1git) {
   439  					tt.mpath = alt + strings.TrimPrefix(tt.mpath, vgotest1git)
   440  				}
   441  				var m map[string]string
   442  				if alt == vgotest1hg {
   443  					m = hgmap
   444  				}
   445  				tt.version = remap(tt.version, m)
   446  				tt.name = remap(tt.name, m)
   447  				tt.short = remap(tt.short, m)
   448  				tt.rev = remap(tt.rev, m)
   449  				tt.gomoderr = remap(tt.gomoderr, m)
   450  				tt.ziperr = remap(tt.ziperr, m)
   451  				t.Run(strings.ReplaceAll(tt.path, "/", "_")+"/"+tt.rev, f)
   452  				tt = old
   453  			}
   454  		}
   455  	}
   456  }
   457  
   458  var hgmap = map[string]string{
   459  	"github.com/rsc/vgotest1/":                 "vcs-test.golang.org/hg/vgotest1.hg/",
   460  	"f18795870fb14388a21ef3ebc1d75911c8694f31": "a9ad6d1d14eb544f459f446210c7eb3b009807c6",
   461  	"ea65f87c8f52c15ea68f3bdd9925ef17e20d91e9": "f1fc0f22021b638d073d31c752847e7bf385def7",
   462  	"b769f2de407a4db81af9c5de0a06016d60d2ea09": "92c7eb888b4fac17f1c6bd2e1060a1b881a3b832",
   463  	"8afe2b2efed96e0880ecd2a69b98a53b8c2738b6": "4e58084d459ae7e79c8c2264d0e8e9a92eb5cd44",
   464  	"2f615117ce481c8efef46e0cc0b4b4dccfac8fea": "879ea98f7743c8eff54f59a918f3a24123d1cf46",
   465  	"80d85c5d4d17598a0e9055e7c175a32b415d6128": "e125018e286a4b09061079a81e7b537070b7ff71",
   466  	"1f863feb76bc7029b78b21c5375644838962f88d": "bf63880162304a9337477f3858f5b7e255c75459",
   467  	"45f53230a74ad275c7127e117ac46914c8126160": "814fce58e83abd5bf2a13892e0b0e1198abefcd4",
   468  }
   469  
   470  func remap(name string, m map[string]string) string {
   471  	if m[name] != "" {
   472  		return m[name]
   473  	}
   474  	if codehost.AllHex(name) {
   475  		for k, v := range m {
   476  			if strings.HasPrefix(k, name) {
   477  				return v[:len(name)]
   478  			}
   479  		}
   480  	}
   481  	for k, v := range m {
   482  		name = strings.ReplaceAll(name, k, v)
   483  		if codehost.AllHex(k) {
   484  			name = strings.ReplaceAll(name, k[:12], v[:12])
   485  		}
   486  	}
   487  	return name
   488  }
   489  
   490  var codeRepoVersionsTests = []struct {
   491  	path     string
   492  	prefix   string
   493  	versions []string
   494  }{
   495  	{
   496  		path:     "github.com/rsc/vgotest1",
   497  		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"},
   498  	},
   499  	{
   500  		path:     "github.com/rsc/vgotest1",
   501  		prefix:   "v1.0",
   502  		versions: []string{"v1.0.0", "v1.0.1", "v1.0.2", "v1.0.3"},
   503  	},
   504  	{
   505  		path:     "github.com/rsc/vgotest1/v2",
   506  		versions: []string{"v2.0.0", "v2.0.1", "v2.0.2", "v2.0.3", "v2.0.4", "v2.0.5", "v2.0.6"},
   507  	},
   508  	{
   509  		path:     "swtch.com/testmod",
   510  		versions: []string{"v1.0.0", "v1.1.1"},
   511  	},
   512  	{
   513  		path:     "gopkg.in/russross/blackfriday.v2",
   514  		versions: []string{"v2.0.0", "v2.0.1"},
   515  	},
   516  	{
   517  		path:     "gopkg.in/natefinch/lumberjack.v2",
   518  		versions: []string{"v2.0.0"},
   519  	},
   520  }
   521  
   522  func TestCodeRepoVersions(t *testing.T) {
   523  	testenv.MustHaveExternalNetwork(t)
   524  
   525  	tmpdir, err := ioutil.TempDir("", "vgo-modfetch-test-")
   526  	if err != nil {
   527  		t.Fatal(err)
   528  	}
   529  	defer os.RemoveAll(tmpdir)
   530  	for _, tt := range codeRepoVersionsTests {
   531  		t.Run(strings.ReplaceAll(tt.path, "/", "_"), func(t *testing.T) {
   532  			repo, err := Lookup(tt.path)
   533  			if err != nil {
   534  				t.Fatalf("Lookup(%q): %v", tt.path, err)
   535  			}
   536  			list, err := repo.Versions(tt.prefix)
   537  			if err != nil {
   538  				t.Fatalf("Versions(%q): %v", tt.prefix, err)
   539  			}
   540  			if !reflect.DeepEqual(list, tt.versions) {
   541  				t.Fatalf("Versions(%q):\nhave %v\nwant %v", tt.prefix, list, tt.versions)
   542  			}
   543  		})
   544  	}
   545  }
   546  
   547  var latestTests = []struct {
   548  	path    string
   549  	version string
   550  	err     string
   551  }{
   552  	{
   553  		path: "github.com/rsc/empty",
   554  		err:  "no commits",
   555  	},
   556  	{
   557  		path:    "github.com/rsc/vgotest1",
   558  		version: "v0.0.0-20180219223237-a08abb797a67",
   559  	},
   560  	{
   561  		path: "github.com/rsc/vgotest1/subdir",
   562  		err:  "missing github.com/rsc/vgotest1/subdir/go.mod at revision a08abb797a67",
   563  	},
   564  	{
   565  		path:    "swtch.com/testmod",
   566  		version: "v1.1.1",
   567  	},
   568  }
   569  
   570  func TestLatest(t *testing.T) {
   571  	testenv.MustHaveExternalNetwork(t)
   572  
   573  	tmpdir, err := ioutil.TempDir("", "vgo-modfetch-test-")
   574  	if err != nil {
   575  		t.Fatal(err)
   576  	}
   577  	defer os.RemoveAll(tmpdir)
   578  	for _, tt := range latestTests {
   579  		name := strings.ReplaceAll(tt.path, "/", "_")
   580  		t.Run(name, func(t *testing.T) {
   581  			repo, err := Lookup(tt.path)
   582  			if err != nil {
   583  				t.Fatalf("Lookup(%q): %v", tt.path, err)
   584  			}
   585  			info, err := repo.Latest()
   586  			if err != nil {
   587  				if tt.err != "" {
   588  					if err.Error() == tt.err {
   589  						return
   590  					}
   591  					t.Fatalf("Latest(): %v, want %q", err, tt.err)
   592  				}
   593  				t.Fatalf("Latest(): %v", err)
   594  			}
   595  			if tt.err != "" {
   596  				t.Fatalf("Latest() = %v, want error %q", info.Version, tt.err)
   597  			}
   598  			if info.Version != tt.version {
   599  				t.Fatalf("Latest() = %v, want %v", info.Version, tt.version)
   600  			}
   601  		})
   602  	}
   603  }
   604  
   605  // fixedTagsRepo is a fake codehost.Repo that returns a fixed list of tags
   606  type fixedTagsRepo struct {
   607  	tags []string
   608  }
   609  
   610  func (ch *fixedTagsRepo) Tags(string) ([]string, error)                  { return ch.tags, nil }
   611  func (ch *fixedTagsRepo) Latest() (*codehost.RevInfo, error)             { panic("not impl") }
   612  func (ch *fixedTagsRepo) ReadFile(string, string, int64) ([]byte, error) { panic("not impl") }
   613  func (ch *fixedTagsRepo) ReadFileRevs([]string, string, int64) (map[string]*codehost.FileRev, error) {
   614  	panic("not impl")
   615  }
   616  func (ch *fixedTagsRepo) ReadZip(string, string, int64) (io.ReadCloser, string, error) {
   617  	panic("not impl")
   618  }
   619  func (ch *fixedTagsRepo) RecentTag(string, string) (string, error) {
   620  	panic("not impl")
   621  }
   622  func (ch *fixedTagsRepo) Stat(string) (*codehost.RevInfo, error) { panic("not impl") }
   623  
   624  func TestNonCanonicalSemver(t *testing.T) {
   625  	root := "golang.org/x/issue24476"
   626  	ch := &fixedTagsRepo{
   627  		tags: []string{
   628  			"", "huh?", "1.0.1",
   629  			// what about "version 1 dot dogcow"?
   630  			"v1.🐕.🐄",
   631  			"v1", "v0.1",
   632  			// and one normal one that should pass through
   633  			"v1.0.1",
   634  		},
   635  	}
   636  
   637  	cr, err := newCodeRepo(ch, root, root)
   638  	if err != nil {
   639  		t.Fatal(err)
   640  	}
   641  
   642  	v, err := cr.Versions("")
   643  	if err != nil {
   644  		t.Fatal(err)
   645  	}
   646  	if len(v) != 1 || v[0] != "v1.0.1" {
   647  		t.Fatal("unexpected versions returned:", v)
   648  	}
   649  }