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 := t.TempDir()
   219  
   220  	for _, vcs := range vcsList {
   221  		for r, root := range vcs.RootNames {
   222  			vcsName := fmt.Sprint(vcs.Name, r)
   223  			dir := filepath.Join(tempDir, "example.com", vcsName, root.filename)
   224  			if root.isDir {
   225  				err := os.MkdirAll(dir, 0755)
   226  				if err != nil {
   227  					t.Fatal(err)
   228  				}
   229  			} else {
   230  				err := os.MkdirAll(filepath.Dir(dir), 0755)
   231  				if err != nil {
   232  					t.Fatal(err)
   233  				}
   234  				f, err := os.Create(dir)
   235  				if err != nil {
   236  					t.Fatal(err)
   237  				}
   238  				f.Close()
   239  			}
   240  
   241  			wantRepoDir := filepath.Dir(dir)
   242  			gotRepoDir, gotVCS, err := FromDir(dir, tempDir, false)
   243  			if err != nil {
   244  				t.Errorf("FromDir(%q, %q): %v", dir, tempDir, err)
   245  				continue
   246  			}
   247  			if gotRepoDir != wantRepoDir || gotVCS.Name != vcs.Name {
   248  				t.Errorf("FromDir(%q, %q) = RepoDir(%s), VCS(%s); want RepoDir(%s), VCS(%s)", dir, tempDir, gotRepoDir, gotVCS.Name, wantRepoDir, vcs.Name)
   249  			}
   250  		}
   251  	}
   252  }
   253  
   254  func TestIsSecure(t *testing.T) {
   255  	tests := []struct {
   256  		vcs    *Cmd
   257  		url    string
   258  		secure bool
   259  	}{
   260  		{vcsGit, "http://example.com/foo.git", false},
   261  		{vcsGit, "https://example.com/foo.git", true},
   262  		{vcsBzr, "http://example.com/foo.bzr", false},
   263  		{vcsBzr, "https://example.com/foo.bzr", true},
   264  		{vcsSvn, "http://example.com/svn", false},
   265  		{vcsSvn, "https://example.com/svn", true},
   266  		{vcsHg, "http://example.com/foo.hg", false},
   267  		{vcsHg, "https://example.com/foo.hg", true},
   268  		{vcsGit, "ssh://user@example.com/foo.git", true},
   269  		{vcsGit, "user@server:path/to/repo.git", false},
   270  		{vcsGit, "user@server:", false},
   271  		{vcsGit, "server:repo.git", false},
   272  		{vcsGit, "server:path/to/repo.git", false},
   273  		{vcsGit, "example.com:path/to/repo.git", false},
   274  		{vcsGit, "path/that/contains/a:colon/repo.git", false},
   275  		{vcsHg, "ssh://user@example.com/path/to/repo.hg", true},
   276  		{vcsFossil, "http://example.com/foo", false},
   277  		{vcsFossil, "https://example.com/foo", true},
   278  	}
   279  
   280  	for _, test := range tests {
   281  		secure := test.vcs.IsSecure(test.url)
   282  		if secure != test.secure {
   283  			t.Errorf("%s isSecure(%q) = %t; want %t", test.vcs, test.url, secure, test.secure)
   284  		}
   285  	}
   286  }
   287  
   288  func TestIsSecureGitAllowProtocol(t *testing.T) {
   289  	tests := []struct {
   290  		vcs    *Cmd
   291  		url    string
   292  		secure bool
   293  	}{
   294  		// Same as TestIsSecure to verify same behavior.
   295  		{vcsGit, "http://example.com/foo.git", false},
   296  		{vcsGit, "https://example.com/foo.git", true},
   297  		{vcsBzr, "http://example.com/foo.bzr", false},
   298  		{vcsBzr, "https://example.com/foo.bzr", true},
   299  		{vcsSvn, "http://example.com/svn", false},
   300  		{vcsSvn, "https://example.com/svn", true},
   301  		{vcsHg, "http://example.com/foo.hg", false},
   302  		{vcsHg, "https://example.com/foo.hg", true},
   303  		{vcsGit, "user@server:path/to/repo.git", false},
   304  		{vcsGit, "user@server:", false},
   305  		{vcsGit, "server:repo.git", false},
   306  		{vcsGit, "server:path/to/repo.git", false},
   307  		{vcsGit, "example.com:path/to/repo.git", false},
   308  		{vcsGit, "path/that/contains/a:colon/repo.git", false},
   309  		{vcsHg, "ssh://user@example.com/path/to/repo.hg", true},
   310  		// New behavior.
   311  		{vcsGit, "ssh://user@example.com/foo.git", false},
   312  		{vcsGit, "foo://example.com/bar.git", true},
   313  		{vcsHg, "foo://example.com/bar.hg", false},
   314  		{vcsSvn, "foo://example.com/svn", false},
   315  		{vcsBzr, "foo://example.com/bar.bzr", false},
   316  	}
   317  
   318  	defer os.Unsetenv("GIT_ALLOW_PROTOCOL")
   319  	os.Setenv("GIT_ALLOW_PROTOCOL", "https:foo")
   320  	for _, test := range tests {
   321  		secure := test.vcs.IsSecure(test.url)
   322  		if secure != test.secure {
   323  			t.Errorf("%s isSecure(%q) = %t; want %t", test.vcs, test.url, secure, test.secure)
   324  		}
   325  	}
   326  }
   327  
   328  func TestMatchGoImport(t *testing.T) {
   329  	tests := []struct {
   330  		imports []metaImport
   331  		path    string
   332  		mi      metaImport
   333  		err     error
   334  	}{
   335  		{
   336  			imports: []metaImport{
   337  				{Prefix: "example.com/user/foo", VCS: "git", RepoRoot: "https://example.com/repo/target"},
   338  			},
   339  			path: "example.com/user/foo",
   340  			mi:   metaImport{Prefix: "example.com/user/foo", VCS: "git", RepoRoot: "https://example.com/repo/target"},
   341  		},
   342  		{
   343  			imports: []metaImport{
   344  				{Prefix: "example.com/user/foo", VCS: "git", RepoRoot: "https://example.com/repo/target"},
   345  			},
   346  			path: "example.com/user/foo/",
   347  			mi:   metaImport{Prefix: "example.com/user/foo", VCS: "git", RepoRoot: "https://example.com/repo/target"},
   348  		},
   349  		{
   350  			imports: []metaImport{
   351  				{Prefix: "example.com/user/foo", VCS: "git", RepoRoot: "https://example.com/repo/target"},
   352  				{Prefix: "example.com/user/fooa", VCS: "git", RepoRoot: "https://example.com/repo/target"},
   353  			},
   354  			path: "example.com/user/foo",
   355  			mi:   metaImport{Prefix: "example.com/user/foo", VCS: "git", RepoRoot: "https://example.com/repo/target"},
   356  		},
   357  		{
   358  			imports: []metaImport{
   359  				{Prefix: "example.com/user/foo", VCS: "git", RepoRoot: "https://example.com/repo/target"},
   360  				{Prefix: "example.com/user/fooa", VCS: "git", RepoRoot: "https://example.com/repo/target"},
   361  			},
   362  			path: "example.com/user/fooa",
   363  			mi:   metaImport{Prefix: "example.com/user/fooa", VCS: "git", RepoRoot: "https://example.com/repo/target"},
   364  		},
   365  		{
   366  			imports: []metaImport{
   367  				{Prefix: "example.com/user/foo", VCS: "git", RepoRoot: "https://example.com/repo/target"},
   368  				{Prefix: "example.com/user/foo/bar", VCS: "git", RepoRoot: "https://example.com/repo/target"},
   369  			},
   370  			path: "example.com/user/foo/bar",
   371  			err:  errors.New("should not be allowed to create nested repo"),
   372  		},
   373  		{
   374  			imports: []metaImport{
   375  				{Prefix: "example.com/user/foo", VCS: "git", RepoRoot: "https://example.com/repo/target"},
   376  				{Prefix: "example.com/user/foo/bar", VCS: "git", RepoRoot: "https://example.com/repo/target"},
   377  			},
   378  			path: "example.com/user/foo/bar/baz",
   379  			err:  errors.New("should not be allowed to create nested repo"),
   380  		},
   381  		{
   382  			imports: []metaImport{
   383  				{Prefix: "example.com/user/foo", VCS: "git", RepoRoot: "https://example.com/repo/target"},
   384  				{Prefix: "example.com/user/foo/bar", VCS: "git", RepoRoot: "https://example.com/repo/target"},
   385  			},
   386  			path: "example.com/user/foo/bar/baz/qux",
   387  			err:  errors.New("should not be allowed to create nested repo"),
   388  		},
   389  		{
   390  			imports: []metaImport{
   391  				{Prefix: "example.com/user/foo", VCS: "git", RepoRoot: "https://example.com/repo/target"},
   392  				{Prefix: "example.com/user/foo/bar", VCS: "git", RepoRoot: "https://example.com/repo/target"},
   393  			},
   394  			path: "example.com/user/foo/bar/baz/",
   395  			err:  errors.New("should not be allowed to create nested repo"),
   396  		},
   397  		{
   398  			imports: []metaImport{
   399  				{Prefix: "example.com/user/foo", VCS: "git", RepoRoot: "https://example.com/repo/target"},
   400  				{Prefix: "example.com/user/foo/bar", VCS: "git", RepoRoot: "https://example.com/repo/target"},
   401  			},
   402  			path: "example.com",
   403  			err:  errors.New("pathologically short path"),
   404  		},
   405  		{
   406  			imports: []metaImport{
   407  				{Prefix: "example.com/user/foo", VCS: "git", RepoRoot: "https://example.com/repo/target"},
   408  			},
   409  			path: "different.example.com/user/foo",
   410  			err:  errors.New("meta tags do not match import path"),
   411  		},
   412  		{
   413  			imports: []metaImport{
   414  				{Prefix: "myitcv.io/blah2", VCS: "mod", RepoRoot: "https://raw.githubusercontent.com/myitcv/pubx/master"},
   415  				{Prefix: "myitcv.io", VCS: "git", RepoRoot: "https://github.com/myitcv/x"},
   416  			},
   417  			path: "myitcv.io/blah2/foo",
   418  			mi:   metaImport{Prefix: "myitcv.io/blah2", VCS: "mod", RepoRoot: "https://raw.githubusercontent.com/myitcv/pubx/master"},
   419  		},
   420  		{
   421  			imports: []metaImport{
   422  				{Prefix: "myitcv.io/blah2", VCS: "mod", RepoRoot: "https://raw.githubusercontent.com/myitcv/pubx/master"},
   423  				{Prefix: "myitcv.io", VCS: "git", RepoRoot: "https://github.com/myitcv/x"},
   424  			},
   425  			path: "myitcv.io/other",
   426  			mi:   metaImport{Prefix: "myitcv.io", VCS: "git", RepoRoot: "https://github.com/myitcv/x"},
   427  		},
   428  	}
   429  
   430  	for _, test := range tests {
   431  		mi, err := matchGoImport(test.imports, test.path)
   432  		if mi != test.mi {
   433  			t.Errorf("unexpected metaImport; got %v, want %v", mi, test.mi)
   434  		}
   435  
   436  		got := err
   437  		want := test.err
   438  		if (got == nil) != (want == nil) {
   439  			t.Errorf("unexpected error; got %v, want %v", got, want)
   440  		}
   441  	}
   442  }
   443  
   444  func TestValidateRepoRoot(t *testing.T) {
   445  	tests := []struct {
   446  		root string
   447  		ok   bool
   448  	}{
   449  		{
   450  			root: "",
   451  			ok:   false,
   452  		},
   453  		{
   454  			root: "http://",
   455  			ok:   true,
   456  		},
   457  		{
   458  			root: "git+ssh://",
   459  			ok:   true,
   460  		},
   461  		{
   462  			root: "http#://",
   463  			ok:   false,
   464  		},
   465  		{
   466  			root: "-config",
   467  			ok:   false,
   468  		},
   469  		{
   470  			root: "-config://",
   471  			ok:   false,
   472  		},
   473  	}
   474  
   475  	for _, test := range tests {
   476  		err := validateRepoRoot(test.root)
   477  		ok := err == nil
   478  		if ok != test.ok {
   479  			want := "error"
   480  			if test.ok {
   481  				want = "nil"
   482  			}
   483  			t.Errorf("validateRepoRoot(%q) = %q, want %s", test.root, err, want)
   484  		}
   485  	}
   486  }
   487  
   488  var govcsTests = []struct {
   489  	govcs string
   490  	path  string
   491  	vcs   string
   492  	ok    bool
   493  }{
   494  	{"private:all", "is-public.com/foo", "zzz", false},
   495  	{"private:all", "is-private.com/foo", "zzz", true},
   496  	{"public:all", "is-public.com/foo", "zzz", true},
   497  	{"public:all", "is-private.com/foo", "zzz", false},
   498  	{"public:all,private:none", "is-public.com/foo", "zzz", true},
   499  	{"public:all,private:none", "is-private.com/foo", "zzz", false},
   500  	{"*:all", "is-public.com/foo", "zzz", true},
   501  	{"golang.org:git", "golang.org/x/text", "zzz", false},
   502  	{"golang.org:git", "golang.org/x/text", "git", true},
   503  	{"golang.org:zzz", "golang.org/x/text", "zzz", true},
   504  	{"golang.org:zzz", "golang.org/x/text", "git", false},
   505  	{"golang.org:zzz", "golang.org/x/text", "zzz", true},
   506  	{"golang.org:zzz", "golang.org/x/text", "git", false},
   507  	{"golang.org:git|hg", "golang.org/x/text", "hg", true},
   508  	{"golang.org:git|hg", "golang.org/x/text", "git", true},
   509  	{"golang.org:git|hg", "golang.org/x/text", "zzz", false},
   510  	{"golang.org:all", "golang.org/x/text", "hg", true},
   511  	{"golang.org:all", "golang.org/x/text", "git", true},
   512  	{"golang.org:all", "golang.org/x/text", "zzz", true},
   513  	{"other.xyz/p:none,golang.org/x:git", "other.xyz/p/x", "git", false},
   514  	{"other.xyz/p:none,golang.org/x:git", "unexpected.com", "git", false},
   515  	{"other.xyz/p:none,golang.org/x:git", "golang.org/x/text", "zzz", false},
   516  	{"other.xyz/p:none,golang.org/x:git", "golang.org/x/text", "git", true},
   517  	{"other.xyz/p:none,golang.org/x:zzz", "golang.org/x/text", "zzz", true},
   518  	{"other.xyz/p:none,golang.org/x:zzz", "golang.org/x/text", "git", false},
   519  	{"other.xyz/p:none,golang.org/x:git|hg", "golang.org/x/text", "hg", true},
   520  	{"other.xyz/p:none,golang.org/x:git|hg", "golang.org/x/text", "git", true},
   521  	{"other.xyz/p:none,golang.org/x:git|hg", "golang.org/x/text", "zzz", false},
   522  	{"other.xyz/p:none,golang.org/x:all", "golang.org/x/text", "hg", true},
   523  	{"other.xyz/p:none,golang.org/x:all", "golang.org/x/text", "git", true},
   524  	{"other.xyz/p:none,golang.org/x:all", "golang.org/x/text", "zzz", true},
   525  	{"other.xyz/p:none,golang.org/x:git", "golang.org/y/text", "zzz", false},
   526  	{"other.xyz/p:none,golang.org/x:git", "golang.org/y/text", "git", false},
   527  	{"other.xyz/p:none,golang.org/x:zzz", "golang.org/y/text", "zzz", false},
   528  	{"other.xyz/p:none,golang.org/x:zzz", "golang.org/y/text", "git", false},
   529  	{"other.xyz/p:none,golang.org/x:git|hg", "golang.org/y/text", "hg", false},
   530  	{"other.xyz/p:none,golang.org/x:git|hg", "golang.org/y/text", "git", false},
   531  	{"other.xyz/p:none,golang.org/x:git|hg", "golang.org/y/text", "zzz", false},
   532  	{"other.xyz/p:none,golang.org/x:all", "golang.org/y/text", "hg", false},
   533  	{"other.xyz/p:none,golang.org/x:all", "golang.org/y/text", "git", false},
   534  	{"other.xyz/p:none,golang.org/x:all", "golang.org/y/text", "zzz", false},
   535  }
   536  
   537  func TestGOVCS(t *testing.T) {
   538  	for _, tt := range govcsTests {
   539  		cfg, err := parseGOVCS(tt.govcs)
   540  		if err != nil {
   541  			t.Errorf("parseGOVCS(%q): %v", tt.govcs, err)
   542  			continue
   543  		}
   544  		private := strings.HasPrefix(tt.path, "is-private")
   545  		ok := cfg.allow(tt.path, private, tt.vcs)
   546  		if ok != tt.ok {
   547  			t.Errorf("parseGOVCS(%q).allow(%q, %v, %q) = %v, want %v",
   548  				tt.govcs, tt.path, private, tt.vcs, ok, tt.ok)
   549  		}
   550  	}
   551  }
   552  
   553  var govcsErrors = []struct {
   554  	s   string
   555  	err string
   556  }{
   557  	{`,`, `empty entry in GOVCS`},
   558  	{`,x`, `empty entry in GOVCS`},
   559  	{`x,`, `malformed entry in GOVCS (missing colon): "x"`},
   560  	{`x:y,`, `empty entry in GOVCS`},
   561  	{`x`, `malformed entry in GOVCS (missing colon): "x"`},
   562  	{`x:`, `empty VCS list in GOVCS: "x:"`},
   563  	{`x:|`, `empty VCS name in GOVCS: "x:|"`},
   564  	{`x:y|`, `empty VCS name in GOVCS: "x:y|"`},
   565  	{`x:|y`, `empty VCS name in GOVCS: "x:|y"`},
   566  	{`x:y,z:`, `empty VCS list in GOVCS: "z:"`},
   567  	{`x:y,z:|`, `empty VCS name in GOVCS: "z:|"`},
   568  	{`x:y,z:|w`, `empty VCS name in GOVCS: "z:|w"`},
   569  	{`x:y,z:w|`, `empty VCS name in GOVCS: "z:w|"`},
   570  	{`x:y,z:w||v`, `empty VCS name in GOVCS: "z:w||v"`},
   571  	{`x:y,x:z`, `unreachable pattern in GOVCS: "x:z" after "x:y"`},
   572  }
   573  
   574  func TestGOVCSErrors(t *testing.T) {
   575  	for _, tt := range govcsErrors {
   576  		_, err := parseGOVCS(tt.s)
   577  		if err == nil || !strings.Contains(err.Error(), tt.err) {
   578  			t.Errorf("parseGOVCS(%s): err=%v, want %v", tt.s, err, tt.err)
   579  		}
   580  	}
   581  }
   582  

View as plain text