Source file src/cmd/go/internal/vcs/vcs_test.go

     1  // Copyright 2014 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 vcs
     6  
     7  import (
     8  	"errors"
     9  	"internal/testenv"
    10  	"os"
    11  	"path/filepath"
    12  	"strings"
    13  	"testing"
    14  
    15  	"cmd/go/internal/web"
    16  )
    17  
    18  func init() {
    19  	// GOVCS defaults to public:git|hg,private:all,
    20  	// which breaks many tests here - they can't use non-git, non-hg VCS at all!
    21  	// Change to fully permissive.
    22  	// The tests of the GOVCS setting itself are in ../../testdata/script/govcs.txt.
    23  	os.Setenv("GOVCS", "*:all")
    24  }
    25  
    26  // Test that RepoRootForImportPath determines the correct RepoRoot for a given importPath.
    27  // TODO(cmang): Add tests for SVN and BZR.
    28  func TestRepoRootForImportPath(t *testing.T) {
    29  	testenv.MustHaveExternalNetwork(t)
    30  
    31  	tests := []struct {
    32  		path string
    33  		want *RepoRoot
    34  	}{
    35  		{
    36  			"github.com/golang/groupcache",
    37  			&RepoRoot{
    38  				VCS:  vcsGit,
    39  				Repo: "https://github.com/golang/groupcache",
    40  			},
    41  		},
    42  		// Unicode letters in directories are not valid.
    43  		{
    44  			"github.com/user/unicode/испытание",
    45  			nil,
    46  		},
    47  		// IBM DevOps Services tests
    48  		{
    49  			"hub.jazz.net/git/user1/pkgname",
    50  			&RepoRoot{
    51  				VCS:  vcsGit,
    52  				Repo: "https://hub.jazz.net/git/user1/pkgname",
    53  			},
    54  		},
    55  		{
    56  			"hub.jazz.net/git/user1/pkgname/submodule/submodule/submodule",
    57  			&RepoRoot{
    58  				VCS:  vcsGit,
    59  				Repo: "https://hub.jazz.net/git/user1/pkgname",
    60  			},
    61  		},
    62  		{
    63  			"hub.jazz.net",
    64  			nil,
    65  		},
    66  		{
    67  			"hubajazz.net",
    68  			nil,
    69  		},
    70  		{
    71  			"hub2.jazz.net",
    72  			nil,
    73  		},
    74  		{
    75  			"hub.jazz.net/someotherprefix",
    76  			nil,
    77  		},
    78  		{
    79  			"hub.jazz.net/someotherprefix/user1/pkgname",
    80  			nil,
    81  		},
    82  		// Spaces are not valid in user names or package names
    83  		{
    84  			"hub.jazz.net/git/User 1/pkgname",
    85  			nil,
    86  		},
    87  		{
    88  			"hub.jazz.net/git/user1/pkg name",
    89  			nil,
    90  		},
    91  		// Dots are not valid in user names
    92  		{
    93  			"hub.jazz.net/git/user.1/pkgname",
    94  			nil,
    95  		},
    96  		{
    97  			"hub.jazz.net/git/user/pkg.name",
    98  			&RepoRoot{
    99  				VCS:  vcsGit,
   100  				Repo: "https://hub.jazz.net/git/user/pkg.name",
   101  			},
   102  		},
   103  		// User names cannot have uppercase letters
   104  		{
   105  			"hub.jazz.net/git/USER/pkgname",
   106  			nil,
   107  		},
   108  		// OpenStack tests
   109  		{
   110  			"git.openstack.org/openstack/swift",
   111  			&RepoRoot{
   112  				VCS:  vcsGit,
   113  				Repo: "https://git.openstack.org/openstack/swift",
   114  			},
   115  		},
   116  		// Trailing .git is less preferred but included for
   117  		// compatibility purposes while the same source needs to
   118  		// be compilable on both old and new go
   119  		{
   120  			"git.openstack.org/openstack/swift.git",
   121  			&RepoRoot{
   122  				VCS:  vcsGit,
   123  				Repo: "https://git.openstack.org/openstack/swift.git",
   124  			},
   125  		},
   126  		{
   127  			"git.openstack.org/openstack/swift/go/hummingbird",
   128  			&RepoRoot{
   129  				VCS:  vcsGit,
   130  				Repo: "https://git.openstack.org/openstack/swift",
   131  			},
   132  		},
   133  		{
   134  			"git.openstack.org",
   135  			nil,
   136  		},
   137  		{
   138  			"git.openstack.org/openstack",
   139  			nil,
   140  		},
   141  		// Spaces are not valid in package name
   142  		{
   143  			"git.apache.org/package name/path/to/lib",
   144  			nil,
   145  		},
   146  		// Should have ".git" suffix
   147  		{
   148  			"git.apache.org/package-name/path/to/lib",
   149  			nil,
   150  		},
   151  		{
   152  			"gitbapache.org",
   153  			nil,
   154  		},
   155  		{
   156  			"git.apache.org/package-name.git",
   157  			&RepoRoot{
   158  				VCS:  vcsGit,
   159  				Repo: "https://git.apache.org/package-name.git",
   160  			},
   161  		},
   162  		{
   163  			"git.apache.org/package-name_2.x.git/path/to/lib",
   164  			&RepoRoot{
   165  				VCS:  vcsGit,
   166  				Repo: "https://git.apache.org/package-name_2.x.git",
   167  			},
   168  		},
   169  		{
   170  			"chiselapp.com/user/kyle/repository/fossilgg",
   171  			&RepoRoot{
   172  				VCS:  vcsFossil,
   173  				Repo: "https://chiselapp.com/user/kyle/repository/fossilgg",
   174  			},
   175  		},
   176  		{
   177  			// must have a user/$name/repository/$repo path
   178  			"chiselapp.com/kyle/repository/fossilgg",
   179  			nil,
   180  		},
   181  		{
   182  			"chiselapp.com/user/kyle/fossilgg",
   183  			nil,
   184  		},
   185  		{
   186  			"bitbucket.org/workspace/pkgname",
   187  			&RepoRoot{
   188  				VCS:  vcsGit,
   189  				Repo: "https://bitbucket.org/workspace/pkgname",
   190  			},
   191  		},
   192  	}
   193  
   194  	for _, test := range tests {
   195  		got, err := RepoRootForImportPath(test.path, IgnoreMod, web.SecureOnly)
   196  		want := test.want
   197  
   198  		if want == nil {
   199  			if err == nil {
   200  				t.Errorf("RepoRootForImportPath(%q): Error expected but not received", test.path)
   201  			}
   202  			continue
   203  		}
   204  		if err != nil {
   205  			t.Errorf("RepoRootForImportPath(%q): %v", test.path, err)
   206  			continue
   207  		}
   208  		if got.VCS.Name != want.VCS.Name || got.Repo != want.Repo {
   209  			t.Errorf("RepoRootForImportPath(%q) = VCS(%s) Repo(%s), want VCS(%s) Repo(%s)", test.path, got.VCS, got.Repo, want.VCS, want.Repo)
   210  		}
   211  	}
   212  }
   213  
   214  // Test that vcs.FromDir correctly inspects a given directory and returns the
   215  // right VCS and repo directory.
   216  func TestFromDir(t *testing.T) {
   217  	tests := []struct {
   218  		name   string
   219  		vcs    string
   220  		root   string
   221  		create func(path string) error
   222  	}{
   223  		{"hg", "Mercurial", ".hg", mkdir},
   224  		{"git_dir", "Git", ".git", mkdir},
   225  		{"git_worktree", "Git", ".git", createGitWorktreeFile},
   226  		{"bzr", "Bazaar", ".bzr", mkdir},
   227  		{"svn", "Subversion", ".svn", mkdir},
   228  		{"fossil_fslckout", "Fossil", ".fslckout", touch},
   229  		{"fossil_FOSSIL_", "Fossil", "_FOSSIL_", touch},
   230  	}
   231  
   232  	for _, tt := range tests {
   233  		t.Run(tt.name, func(t *testing.T) {
   234  			tempDir := t.TempDir()
   235  			repoDir := filepath.Join(tempDir, "example.com")
   236  			if err := mkdir(repoDir); err != nil {
   237  				t.Fatal(err)
   238  			}
   239  			rootPath := filepath.Join(repoDir, tt.root)
   240  			if err := tt.create(rootPath); err != nil {
   241  				t.Fatal(err)
   242  			}
   243  			gotRepoDir, gotVCS, err := FromDir(repoDir, tempDir)
   244  			if err != nil {
   245  				t.Fatal(err)
   246  			}
   247  			if gotRepoDir != repoDir {
   248  				t.Errorf("RepoDir = %q, want %q", gotRepoDir, repoDir)
   249  			}
   250  			if gotVCS.Name != tt.vcs {
   251  				t.Errorf("VCS = %q, want %q", gotVCS.Name, tt.vcs)
   252  			}
   253  		})
   254  	}
   255  }
   256  
   257  func mkdir(path string) error {
   258  	return os.Mkdir(path, 0o755)
   259  }
   260  
   261  func touch(path string) error {
   262  	return os.WriteFile(path, nil, 0o644)
   263  }
   264  
   265  func createGitWorktreeFile(path string) error {
   266  	gitdir := path + ".worktree"
   267  	// gitdir must point to a real directory
   268  	if err := mkdir(gitdir); err != nil {
   269  		return err
   270  	}
   271  	return os.WriteFile(path, []byte("gitdir: "+gitdir+"\n"), 0o644)
   272  }
   273  
   274  func TestIsSecure(t *testing.T) {
   275  	tests := []struct {
   276  		vcs    *Cmd
   277  		url    string
   278  		secure bool
   279  	}{
   280  		{vcsGit, "http://example.com/foo.git", false},
   281  		{vcsGit, "https://example.com/foo.git", true},
   282  		{vcsBzr, "http://example.com/foo.bzr", false},
   283  		{vcsBzr, "https://example.com/foo.bzr", true},
   284  		{vcsSvn, "http://example.com/svn", false},
   285  		{vcsSvn, "https://example.com/svn", true},
   286  		{vcsHg, "http://example.com/foo.hg", false},
   287  		{vcsHg, "https://example.com/foo.hg", true},
   288  		{vcsGit, "ssh://user@example.com/foo.git", true},
   289  		{vcsGit, "user@server:path/to/repo.git", false},
   290  		{vcsGit, "user@server:", false},
   291  		{vcsGit, "server:repo.git", false},
   292  		{vcsGit, "server:path/to/repo.git", false},
   293  		{vcsGit, "example.com:path/to/repo.git", false},
   294  		{vcsGit, "path/that/contains/a:colon/repo.git", false},
   295  		{vcsHg, "ssh://user@example.com/path/to/repo.hg", true},
   296  		{vcsFossil, "http://example.com/foo", false},
   297  		{vcsFossil, "https://example.com/foo", true},
   298  	}
   299  
   300  	for _, test := range tests {
   301  		secure := test.vcs.IsSecure(test.url)
   302  		if secure != test.secure {
   303  			t.Errorf("%s isSecure(%q) = %t; want %t", test.vcs, test.url, secure, test.secure)
   304  		}
   305  	}
   306  }
   307  
   308  func TestIsSecureGitAllowProtocol(t *testing.T) {
   309  	tests := []struct {
   310  		vcs    *Cmd
   311  		url    string
   312  		secure bool
   313  	}{
   314  		// Same as TestIsSecure to verify same behavior.
   315  		{vcsGit, "http://example.com/foo.git", false},
   316  		{vcsGit, "https://example.com/foo.git", true},
   317  		{vcsBzr, "http://example.com/foo.bzr", false},
   318  		{vcsBzr, "https://example.com/foo.bzr", true},
   319  		{vcsSvn, "http://example.com/svn", false},
   320  		{vcsSvn, "https://example.com/svn", true},
   321  		{vcsHg, "http://example.com/foo.hg", false},
   322  		{vcsHg, "https://example.com/foo.hg", true},
   323  		{vcsGit, "user@server:path/to/repo.git", false},
   324  		{vcsGit, "user@server:", false},
   325  		{vcsGit, "server:repo.git", false},
   326  		{vcsGit, "server:path/to/repo.git", false},
   327  		{vcsGit, "example.com:path/to/repo.git", false},
   328  		{vcsGit, "path/that/contains/a:colon/repo.git", false},
   329  		{vcsHg, "ssh://user@example.com/path/to/repo.hg", true},
   330  		// New behavior.
   331  		{vcsGit, "ssh://user@example.com/foo.git", false},
   332  		{vcsGit, "foo://example.com/bar.git", true},
   333  		{vcsHg, "foo://example.com/bar.hg", false},
   334  		{vcsSvn, "foo://example.com/svn", false},
   335  		{vcsBzr, "foo://example.com/bar.bzr", false},
   336  	}
   337  
   338  	defer os.Unsetenv("GIT_ALLOW_PROTOCOL")
   339  	os.Setenv("GIT_ALLOW_PROTOCOL", "https:foo")
   340  	for _, test := range tests {
   341  		secure := test.vcs.IsSecure(test.url)
   342  		if secure != test.secure {
   343  			t.Errorf("%s isSecure(%q) = %t; want %t", test.vcs, test.url, secure, test.secure)
   344  		}
   345  	}
   346  }
   347  
   348  func TestMatchGoImport(t *testing.T) {
   349  	tests := []struct {
   350  		imports []metaImport
   351  		path    string
   352  		mi      metaImport
   353  		err     error
   354  	}{
   355  		{
   356  			imports: []metaImport{
   357  				{Prefix: "example.com/user/foo", VCS: "git", RepoRoot: "https://example.com/repo/target"},
   358  			},
   359  			path: "example.com/user/foo",
   360  			mi:   metaImport{Prefix: "example.com/user/foo", VCS: "git", RepoRoot: "https://example.com/repo/target"},
   361  		},
   362  		{
   363  			imports: []metaImport{
   364  				{Prefix: "example.com/user/foo", VCS: "git", RepoRoot: "https://example.com/repo/target"},
   365  			},
   366  			path: "example.com/user/foo/",
   367  			mi:   metaImport{Prefix: "example.com/user/foo", VCS: "git", RepoRoot: "https://example.com/repo/target"},
   368  		},
   369  		{
   370  			imports: []metaImport{
   371  				{Prefix: "example.com/user/foo", VCS: "git", RepoRoot: "https://example.com/repo/target"},
   372  				{Prefix: "example.com/user/fooa", VCS: "git", RepoRoot: "https://example.com/repo/target"},
   373  			},
   374  			path: "example.com/user/foo",
   375  			mi:   metaImport{Prefix: "example.com/user/foo", VCS: "git", RepoRoot: "https://example.com/repo/target"},
   376  		},
   377  		{
   378  			imports: []metaImport{
   379  				{Prefix: "example.com/user/foo", VCS: "git", RepoRoot: "https://example.com/repo/target"},
   380  				{Prefix: "example.com/user/fooa", VCS: "git", RepoRoot: "https://example.com/repo/target"},
   381  			},
   382  			path: "example.com/user/fooa",
   383  			mi:   metaImport{Prefix: "example.com/user/fooa", VCS: "git", RepoRoot: "https://example.com/repo/target"},
   384  		},
   385  		{
   386  			imports: []metaImport{
   387  				{Prefix: "example.com/user/foo", VCS: "git", RepoRoot: "https://example.com/repo/target"},
   388  				{Prefix: "example.com/user/foo/bar", VCS: "git", RepoRoot: "https://example.com/repo/target"},
   389  			},
   390  			path: "example.com/user/foo/bar",
   391  			err:  errors.New("should not be allowed to create nested repo"),
   392  		},
   393  		{
   394  			imports: []metaImport{
   395  				{Prefix: "example.com/user/foo", VCS: "git", RepoRoot: "https://example.com/repo/target"},
   396  				{Prefix: "example.com/user/foo/bar", VCS: "git", RepoRoot: "https://example.com/repo/target"},
   397  			},
   398  			path: "example.com/user/foo/bar/baz",
   399  			err:  errors.New("should not be allowed to create nested repo"),
   400  		},
   401  		{
   402  			imports: []metaImport{
   403  				{Prefix: "example.com/user/foo", VCS: "git", RepoRoot: "https://example.com/repo/target"},
   404  				{Prefix: "example.com/user/foo/bar", VCS: "git", RepoRoot: "https://example.com/repo/target"},
   405  			},
   406  			path: "example.com/user/foo/bar/baz/qux",
   407  			err:  errors.New("should not be allowed to create nested repo"),
   408  		},
   409  		{
   410  			imports: []metaImport{
   411  				{Prefix: "example.com/user/foo", VCS: "git", RepoRoot: "https://example.com/repo/target"},
   412  				{Prefix: "example.com/user/foo/bar", VCS: "git", RepoRoot: "https://example.com/repo/target"},
   413  			},
   414  			path: "example.com/user/foo/bar/baz/",
   415  			err:  errors.New("should not be allowed to create nested repo"),
   416  		},
   417  		{
   418  			imports: []metaImport{
   419  				{Prefix: "example.com/user/foo", VCS: "git", RepoRoot: "https://example.com/repo/target"},
   420  				{Prefix: "example.com/user/foo/bar", VCS: "git", RepoRoot: "https://example.com/repo/target"},
   421  			},
   422  			path: "example.com",
   423  			err:  errors.New("pathologically short path"),
   424  		},
   425  		{
   426  			imports: []metaImport{
   427  				{Prefix: "example.com/user/foo", VCS: "git", RepoRoot: "https://example.com/repo/target"},
   428  			},
   429  			path: "different.example.com/user/foo",
   430  			err:  errors.New("meta tags do not match import path"),
   431  		},
   432  		{
   433  			imports: []metaImport{
   434  				{Prefix: "myitcv.io/blah2", VCS: "mod", RepoRoot: "https://raw.githubusercontent.com/myitcv/pubx/master"},
   435  				{Prefix: "myitcv.io", VCS: "git", RepoRoot: "https://github.com/myitcv/x"},
   436  			},
   437  			path: "myitcv.io/blah2/foo",
   438  			mi:   metaImport{Prefix: "myitcv.io/blah2", VCS: "mod", RepoRoot: "https://raw.githubusercontent.com/myitcv/pubx/master"},
   439  		},
   440  		{
   441  			imports: []metaImport{
   442  				{Prefix: "myitcv.io/blah2", VCS: "mod", RepoRoot: "https://raw.githubusercontent.com/myitcv/pubx/master"},
   443  				{Prefix: "myitcv.io", VCS: "git", RepoRoot: "https://github.com/myitcv/x"},
   444  			},
   445  			path: "myitcv.io/other",
   446  			mi:   metaImport{Prefix: "myitcv.io", VCS: "git", RepoRoot: "https://github.com/myitcv/x"},
   447  		},
   448  		{
   449  			imports: []metaImport{
   450  				{Prefix: "example.com/user/foo", VCS: "git", RepoRoot: "https://example.com/repo/target", SubDir: "subdir"},
   451  			},
   452  			path: "example.com/user/foo",
   453  			mi:   metaImport{Prefix: "example.com/user/foo", VCS: "git", RepoRoot: "https://example.com/repo/target", SubDir: "subdir"},
   454  		},
   455  		{
   456  			imports: []metaImport{
   457  				{Prefix: "example.com/user/foo", VCS: "git", RepoRoot: "https://example.com/repo/target", SubDir: "foo/subdir"},
   458  			},
   459  			path: "example.com/user/foo",
   460  			mi:   metaImport{Prefix: "example.com/user/foo", VCS: "git", RepoRoot: "https://example.com/repo/target", SubDir: "foo/subdir"},
   461  		},
   462  		{
   463  			imports: []metaImport{
   464  				{Prefix: "example.com/user/foo", VCS: "git", RepoRoot: "https://example.com/repo/target", SubDir: "subdir"},
   465  				{Prefix: "example.com/user/foo", VCS: "git", RepoRoot: "https://example.com/repo/target", SubDir: ""},
   466  			},
   467  			path: "example.com/user/foo",
   468  			err:  errors.New("multiple meta tags match import path"),
   469  		},
   470  	}
   471  
   472  	for _, test := range tests {
   473  		mi, err := matchGoImport(test.imports, test.path)
   474  		if mi != test.mi {
   475  			t.Errorf("unexpected metaImport; got %v, want %v", mi, test.mi)
   476  		}
   477  
   478  		got := err
   479  		want := test.err
   480  		if (got == nil) != (want == nil) {
   481  			t.Errorf("unexpected error; got %v, want %v", got, want)
   482  		}
   483  	}
   484  }
   485  
   486  func TestValidateRepoRoot(t *testing.T) {
   487  	tests := []struct {
   488  		root string
   489  		ok   bool
   490  	}{
   491  		{
   492  			root: "",
   493  			ok:   false,
   494  		},
   495  		{
   496  			root: "http://",
   497  			ok:   true,
   498  		},
   499  		{
   500  			root: "git+ssh://",
   501  			ok:   true,
   502  		},
   503  		{
   504  			root: "http#://",
   505  			ok:   false,
   506  		},
   507  		{
   508  			root: "-config",
   509  			ok:   false,
   510  		},
   511  		{
   512  			root: "-config://",
   513  			ok:   false,
   514  		},
   515  	}
   516  
   517  	for _, test := range tests {
   518  		err := validateRepoRoot(test.root)
   519  		ok := err == nil
   520  		if ok != test.ok {
   521  			want := "error"
   522  			if test.ok {
   523  				want = "nil"
   524  			}
   525  			t.Errorf("validateRepoRoot(%q) = %q, want %s", test.root, err, want)
   526  		}
   527  	}
   528  }
   529  
   530  func TestValidateRepoSubDir(t *testing.T) {
   531  	tests := []struct {
   532  		subdir string
   533  		ok     bool
   534  	}{
   535  		{
   536  			subdir: "",
   537  			ok:     true,
   538  		},
   539  		{
   540  			subdir: "sub/dir",
   541  			ok:     true,
   542  		},
   543  		{
   544  			subdir: "/leading/slash",
   545  			ok:     false,
   546  		},
   547  		{
   548  			subdir: "-leading/hyphen",
   549  			ok:     false,
   550  		},
   551  	}
   552  
   553  	for _, test := range tests {
   554  		err := validateRepoSubDir(test.subdir)
   555  		ok := err == nil
   556  		if ok != test.ok {
   557  			want := "error"
   558  			if test.ok {
   559  				want = "nil"
   560  			}
   561  			t.Errorf("validateRepoSubDir(%q) = %q, want %s", test.subdir, err, want)
   562  		}
   563  	}
   564  }
   565  
   566  var govcsTests = []struct {
   567  	govcs string
   568  	path  string
   569  	vcs   string
   570  	ok    bool
   571  }{
   572  	{"private:all", "is-public.com/foo", "zzz", false},
   573  	{"private:all", "is-private.com/foo", "zzz", true},
   574  	{"public:all", "is-public.com/foo", "zzz", true},
   575  	{"public:all", "is-private.com/foo", "zzz", false},
   576  	{"public:all,private:none", "is-public.com/foo", "zzz", true},
   577  	{"public:all,private:none", "is-private.com/foo", "zzz", false},
   578  	{"*:all", "is-public.com/foo", "zzz", true},
   579  	{"golang.org:git", "golang.org/x/text", "zzz", false},
   580  	{"golang.org:git", "golang.org/x/text", "git", true},
   581  	{"golang.org:zzz", "golang.org/x/text", "zzz", true},
   582  	{"golang.org:zzz", "golang.org/x/text", "git", false},
   583  	{"golang.org:zzz", "golang.org/x/text", "zzz", true},
   584  	{"golang.org:zzz", "golang.org/x/text", "git", false},
   585  	{"golang.org:git|hg", "golang.org/x/text", "hg", true},
   586  	{"golang.org:git|hg", "golang.org/x/text", "git", true},
   587  	{"golang.org:git|hg", "golang.org/x/text", "zzz", false},
   588  	{"golang.org:all", "golang.org/x/text", "hg", true},
   589  	{"golang.org:all", "golang.org/x/text", "git", true},
   590  	{"golang.org:all", "golang.org/x/text", "zzz", true},
   591  	{"other.xyz/p:none,golang.org/x:git", "other.xyz/p/x", "git", false},
   592  	{"other.xyz/p:none,golang.org/x:git", "unexpected.com", "git", false},
   593  	{"other.xyz/p:none,golang.org/x:git", "golang.org/x/text", "zzz", false},
   594  	{"other.xyz/p:none,golang.org/x:git", "golang.org/x/text", "git", true},
   595  	{"other.xyz/p:none,golang.org/x:zzz", "golang.org/x/text", "zzz", true},
   596  	{"other.xyz/p:none,golang.org/x:zzz", "golang.org/x/text", "git", false},
   597  	{"other.xyz/p:none,golang.org/x:git|hg", "golang.org/x/text", "hg", true},
   598  	{"other.xyz/p:none,golang.org/x:git|hg", "golang.org/x/text", "git", true},
   599  	{"other.xyz/p:none,golang.org/x:git|hg", "golang.org/x/text", "zzz", false},
   600  	{"other.xyz/p:none,golang.org/x:all", "golang.org/x/text", "hg", true},
   601  	{"other.xyz/p:none,golang.org/x:all", "golang.org/x/text", "git", true},
   602  	{"other.xyz/p:none,golang.org/x:all", "golang.org/x/text", "zzz", true},
   603  	{"other.xyz/p:none,golang.org/x:git", "golang.org/y/text", "zzz", false},
   604  	{"other.xyz/p:none,golang.org/x:git", "golang.org/y/text", "git", false},
   605  	{"other.xyz/p:none,golang.org/x:zzz", "golang.org/y/text", "zzz", false},
   606  	{"other.xyz/p:none,golang.org/x:zzz", "golang.org/y/text", "git", false},
   607  	{"other.xyz/p:none,golang.org/x:git|hg", "golang.org/y/text", "hg", false},
   608  	{"other.xyz/p:none,golang.org/x:git|hg", "golang.org/y/text", "git", false},
   609  	{"other.xyz/p:none,golang.org/x:git|hg", "golang.org/y/text", "zzz", false},
   610  	{"other.xyz/p:none,golang.org/x:all", "golang.org/y/text", "hg", false},
   611  	{"other.xyz/p:none,golang.org/x:all", "golang.org/y/text", "git", false},
   612  	{"other.xyz/p:none,golang.org/x:all", "golang.org/y/text", "zzz", false},
   613  }
   614  
   615  func TestGOVCS(t *testing.T) {
   616  	for _, tt := range govcsTests {
   617  		cfg, err := parseGOVCS(tt.govcs)
   618  		if err != nil {
   619  			t.Errorf("parseGOVCS(%q): %v", tt.govcs, err)
   620  			continue
   621  		}
   622  		private := strings.HasPrefix(tt.path, "is-private")
   623  		ok := cfg.allow(tt.path, private, tt.vcs)
   624  		if ok != tt.ok {
   625  			t.Errorf("parseGOVCS(%q).allow(%q, %v, %q) = %v, want %v",
   626  				tt.govcs, tt.path, private, tt.vcs, ok, tt.ok)
   627  		}
   628  	}
   629  }
   630  
   631  var govcsErrors = []struct {
   632  	s   string
   633  	err string
   634  }{
   635  	{`,`, `empty entry in GOVCS`},
   636  	{`,x`, `empty entry in GOVCS`},
   637  	{`x,`, `malformed entry in GOVCS (missing colon): "x"`},
   638  	{`x:y,`, `empty entry in GOVCS`},
   639  	{`x`, `malformed entry in GOVCS (missing colon): "x"`},
   640  	{`x:`, `empty VCS list in GOVCS: "x:"`},
   641  	{`x:|`, `empty VCS name in GOVCS: "x:|"`},
   642  	{`x:y|`, `empty VCS name in GOVCS: "x:y|"`},
   643  	{`x:|y`, `empty VCS name in GOVCS: "x:|y"`},
   644  	{`x:y,z:`, `empty VCS list in GOVCS: "z:"`},
   645  	{`x:y,z:|`, `empty VCS name in GOVCS: "z:|"`},
   646  	{`x:y,z:|w`, `empty VCS name in GOVCS: "z:|w"`},
   647  	{`x:y,z:w|`, `empty VCS name in GOVCS: "z:w|"`},
   648  	{`x:y,z:w||v`, `empty VCS name in GOVCS: "z:w||v"`},
   649  	{`x:y,x:z`, `unreachable pattern in GOVCS: "x:z" after "x:y"`},
   650  }
   651  
   652  func TestGOVCSErrors(t *testing.T) {
   653  	for _, tt := range govcsErrors {
   654  		_, err := parseGOVCS(tt.s)
   655  		if err == nil || !strings.Contains(err.Error(), tt.err) {
   656  			t.Errorf("parseGOVCS(%s): err=%v, want %v", tt.s, err, tt.err)
   657  		}
   658  	}
   659  }
   660  

View as plain text