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

View as plain text