github.com/npaton/distribution@v2.3.1-rc.0+incompatible/reference/regexp_test.go (about)

     1  package reference
     2  
     3  import (
     4  	"regexp"
     5  	"strings"
     6  	"testing"
     7  )
     8  
     9  type regexpMatch struct {
    10  	input string
    11  	match bool
    12  	subs  []string
    13  }
    14  
    15  func checkRegexp(t *testing.T, r *regexp.Regexp, m regexpMatch) {
    16  	matches := r.FindStringSubmatch(m.input)
    17  	if m.match && matches != nil {
    18  		if len(matches) != (r.NumSubexp()+1) || matches[0] != m.input {
    19  			t.Fatalf("Bad match result %#v for %q", matches, m.input)
    20  		}
    21  		if len(matches) < (len(m.subs) + 1) {
    22  			t.Errorf("Expected %d sub matches, only have %d for %q", len(m.subs), len(matches)-1, m.input)
    23  		}
    24  		for i := range m.subs {
    25  			if m.subs[i] != matches[i+1] {
    26  				t.Errorf("Unexpected submatch %d: %q, expected %q for %q", i+1, matches[i+1], m.subs[i], m.input)
    27  			}
    28  		}
    29  	} else if m.match {
    30  		t.Errorf("Expected match for %q", m.input)
    31  	} else if matches != nil {
    32  		t.Errorf("Unexpected match for %q", m.input)
    33  	}
    34  }
    35  
    36  func TestHostRegexp(t *testing.T) {
    37  	hostcases := []regexpMatch{
    38  		{
    39  			input: "test.com",
    40  			match: true,
    41  		},
    42  		{
    43  			input: "test.com:10304",
    44  			match: true,
    45  		},
    46  		{
    47  			input: "test.com:http",
    48  			match: false,
    49  		},
    50  		{
    51  			input: "localhost",
    52  			match: true,
    53  		},
    54  		{
    55  			input: "localhost:8080",
    56  			match: true,
    57  		},
    58  		{
    59  			input: "a",
    60  			match: true,
    61  		},
    62  		{
    63  			input: "a.b",
    64  			match: true,
    65  		},
    66  		{
    67  			input: "ab.cd.com",
    68  			match: true,
    69  		},
    70  		{
    71  			input: "a-b.com",
    72  			match: true,
    73  		},
    74  		{
    75  			input: "-ab.com",
    76  			match: false,
    77  		},
    78  		{
    79  			input: "ab-.com",
    80  			match: false,
    81  		},
    82  		{
    83  			input: "ab.c-om",
    84  			match: true,
    85  		},
    86  		{
    87  			input: "ab.-com",
    88  			match: false,
    89  		},
    90  		{
    91  			input: "ab.com-",
    92  			match: false,
    93  		},
    94  		{
    95  			input: "0101.com",
    96  			match: true, // TODO(dmcgowan): valid if this should be allowed
    97  		},
    98  		{
    99  			input: "001a.com",
   100  			match: true,
   101  		},
   102  		{
   103  			input: "b.gbc.io:443",
   104  			match: true,
   105  		},
   106  		{
   107  			input: "b.gbc.io",
   108  			match: true,
   109  		},
   110  		{
   111  			input: "xn--n3h.com", // ☃.com in punycode
   112  			match: true,
   113  		},
   114  		{
   115  			input: "Asdf.com", // uppercase character
   116  			match: true,
   117  		},
   118  	}
   119  	r := regexp.MustCompile(`^` + hostnameRegexp.String() + `$`)
   120  	for i := range hostcases {
   121  		checkRegexp(t, r, hostcases[i])
   122  	}
   123  }
   124  
   125  func TestFullNameRegexp(t *testing.T) {
   126  	if anchoredNameRegexp.NumSubexp() != 2 {
   127  		t.Fatalf("anchored name regexp should have two submatches: %v, %v != 2",
   128  			anchoredNameRegexp, anchoredNameRegexp.NumSubexp())
   129  	}
   130  
   131  	testcases := []regexpMatch{
   132  		{
   133  			input: "",
   134  			match: false,
   135  		},
   136  		{
   137  			input: "short",
   138  			match: true,
   139  			subs:  []string{"", "short"},
   140  		},
   141  		{
   142  			input: "simple/name",
   143  			match: true,
   144  			subs:  []string{"simple", "name"},
   145  		},
   146  		{
   147  			input: "library/ubuntu",
   148  			match: true,
   149  			subs:  []string{"library", "ubuntu"},
   150  		},
   151  		{
   152  			input: "docker/stevvooe/app",
   153  			match: true,
   154  			subs:  []string{"docker", "stevvooe/app"},
   155  		},
   156  		{
   157  			input: "aa/aa/aa/aa/aa/aa/aa/aa/aa/bb/bb/bb/bb/bb/bb",
   158  			match: true,
   159  			subs:  []string{"aa", "aa/aa/aa/aa/aa/aa/aa/aa/bb/bb/bb/bb/bb/bb"},
   160  		},
   161  		{
   162  			input: "aa/aa/bb/bb/bb",
   163  			match: true,
   164  			subs:  []string{"aa", "aa/bb/bb/bb"},
   165  		},
   166  		{
   167  			input: "a/a/a/a",
   168  			match: true,
   169  			subs:  []string{"a", "a/a/a"},
   170  		},
   171  		{
   172  			input: "a/a/a/a/",
   173  			match: false,
   174  		},
   175  		{
   176  			input: "a//a/a",
   177  			match: false,
   178  		},
   179  		{
   180  			input: "a",
   181  			match: true,
   182  			subs:  []string{"", "a"},
   183  		},
   184  		{
   185  			input: "a/aa",
   186  			match: true,
   187  			subs:  []string{"a", "aa"},
   188  		},
   189  		{
   190  			input: "a/aa/a",
   191  			match: true,
   192  			subs:  []string{"a", "aa/a"},
   193  		},
   194  		{
   195  			input: "foo.com",
   196  			match: true,
   197  			subs:  []string{"", "foo.com"},
   198  		},
   199  		{
   200  			input: "foo.com/",
   201  			match: false,
   202  		},
   203  		{
   204  			input: "foo.com:8080/bar",
   205  			match: true,
   206  			subs:  []string{"foo.com:8080", "bar"},
   207  		},
   208  		{
   209  			input: "foo.com:http/bar",
   210  			match: false,
   211  		},
   212  		{
   213  			input: "foo.com/bar",
   214  			match: true,
   215  			subs:  []string{"foo.com", "bar"},
   216  		},
   217  		{
   218  			input: "foo.com/bar/baz",
   219  			match: true,
   220  			subs:  []string{"foo.com", "bar/baz"},
   221  		},
   222  		{
   223  			input: "localhost:8080/bar",
   224  			match: true,
   225  			subs:  []string{"localhost:8080", "bar"},
   226  		},
   227  		{
   228  			input: "sub-dom1.foo.com/bar/baz/quux",
   229  			match: true,
   230  			subs:  []string{"sub-dom1.foo.com", "bar/baz/quux"},
   231  		},
   232  		{
   233  			input: "blog.foo.com/bar/baz",
   234  			match: true,
   235  			subs:  []string{"blog.foo.com", "bar/baz"},
   236  		},
   237  		{
   238  			input: "a^a",
   239  			match: false,
   240  		},
   241  		{
   242  			input: "aa/asdf$$^/aa",
   243  			match: false,
   244  		},
   245  		{
   246  			input: "asdf$$^/aa",
   247  			match: false,
   248  		},
   249  		{
   250  			input: "aa-a/a",
   251  			match: true,
   252  			subs:  []string{"aa-a", "a"},
   253  		},
   254  		{
   255  			input: strings.Repeat("a/", 128) + "a",
   256  			match: true,
   257  			subs:  []string{"a", strings.Repeat("a/", 127) + "a"},
   258  		},
   259  		{
   260  			input: "a-/a/a/a",
   261  			match: false,
   262  		},
   263  		{
   264  			input: "foo.com/a-/a/a",
   265  			match: false,
   266  		},
   267  		{
   268  			input: "-foo/bar",
   269  			match: false,
   270  		},
   271  		{
   272  			input: "foo/bar-",
   273  			match: false,
   274  		},
   275  		{
   276  			input: "foo-/bar",
   277  			match: false,
   278  		},
   279  		{
   280  			input: "foo/-bar",
   281  			match: false,
   282  		},
   283  		{
   284  			input: "_foo/bar",
   285  			match: false,
   286  		},
   287  		{
   288  			input: "foo_bar",
   289  			match: true,
   290  			subs:  []string{"", "foo_bar"},
   291  		},
   292  		{
   293  			input: "foo_bar.com",
   294  			match: true,
   295  			subs:  []string{"", "foo_bar.com"},
   296  		},
   297  		{
   298  			input: "foo_bar.com:8080",
   299  			match: false,
   300  		},
   301  		{
   302  			input: "foo_bar.com:8080/app",
   303  			match: false,
   304  		},
   305  		{
   306  			input: "foo.com/foo_bar",
   307  			match: true,
   308  			subs:  []string{"foo.com", "foo_bar"},
   309  		},
   310  		{
   311  			input: "____/____",
   312  			match: false,
   313  		},
   314  		{
   315  			input: "_docker/_docker",
   316  			match: false,
   317  		},
   318  		{
   319  			input: "docker_/docker_",
   320  			match: false,
   321  		},
   322  		{
   323  			input: "b.gcr.io/test.example.com/my-app",
   324  			match: true,
   325  			subs:  []string{"b.gcr.io", "test.example.com/my-app"},
   326  		},
   327  		{
   328  			input: "xn--n3h.com/myimage", // ☃.com in punycode
   329  			match: true,
   330  			subs:  []string{"xn--n3h.com", "myimage"},
   331  		},
   332  		{
   333  			input: "xn--7o8h.com/myimage", // 🐳.com in punycode
   334  			match: true,
   335  			subs:  []string{"xn--7o8h.com", "myimage"},
   336  		},
   337  		{
   338  			input: "example.com/xn--7o8h.com/myimage", // 🐳.com in punycode
   339  			match: true,
   340  			subs:  []string{"example.com", "xn--7o8h.com/myimage"},
   341  		},
   342  		{
   343  			input: "example.com/some_separator__underscore/myimage",
   344  			match: true,
   345  			subs:  []string{"example.com", "some_separator__underscore/myimage"},
   346  		},
   347  		{
   348  			input: "example.com/__underscore/myimage",
   349  			match: false,
   350  		},
   351  		{
   352  			input: "example.com/..dots/myimage",
   353  			match: false,
   354  		},
   355  		{
   356  			input: "example.com/.dots/myimage",
   357  			match: false,
   358  		},
   359  		{
   360  			input: "example.com/nodouble..dots/myimage",
   361  			match: false,
   362  		},
   363  		{
   364  			input: "example.com/nodouble..dots/myimage",
   365  			match: false,
   366  		},
   367  		{
   368  			input: "docker./docker",
   369  			match: false,
   370  		},
   371  		{
   372  			input: ".docker/docker",
   373  			match: false,
   374  		},
   375  		{
   376  			input: "docker-/docker",
   377  			match: false,
   378  		},
   379  		{
   380  			input: "-docker/docker",
   381  			match: false,
   382  		},
   383  		{
   384  			input: "do..cker/docker",
   385  			match: false,
   386  		},
   387  		{
   388  			input: "do__cker:8080/docker",
   389  			match: false,
   390  		},
   391  		{
   392  			input: "do__cker/docker",
   393  			match: true,
   394  			subs:  []string{"", "do__cker/docker"},
   395  		},
   396  		{
   397  			input: "b.gcr.io/test.example.com/my-app",
   398  			match: true,
   399  			subs:  []string{"b.gcr.io", "test.example.com/my-app"},
   400  		},
   401  		{
   402  			input: "registry.io/foo/project--id.module--name.ver---sion--name",
   403  			match: true,
   404  			subs:  []string{"registry.io", "foo/project--id.module--name.ver---sion--name"},
   405  		},
   406  		{
   407  			input: "Asdf.com/foo/bar", // uppercase character in hostname
   408  			match: true,
   409  		},
   410  		{
   411  			input: "Foo/FarB", // uppercase characters in remote name
   412  			match: false,
   413  		},
   414  	}
   415  	for i := range testcases {
   416  		checkRegexp(t, anchoredNameRegexp, testcases[i])
   417  	}
   418  }
   419  
   420  func TestReferenceRegexp(t *testing.T) {
   421  	if ReferenceRegexp.NumSubexp() != 3 {
   422  		t.Fatalf("anchored name regexp should have three submatches: %v, %v != 3",
   423  			ReferenceRegexp, ReferenceRegexp.NumSubexp())
   424  	}
   425  
   426  	testcases := []regexpMatch{
   427  		{
   428  			input: "registry.com:8080/myapp:tag",
   429  			match: true,
   430  			subs:  []string{"registry.com:8080/myapp", "tag", ""},
   431  		},
   432  		{
   433  			input: "registry.com:8080/myapp@sha256:be178c0543eb17f5f3043021c9e5fcf30285e557a4fc309cce97ff9ca6182912",
   434  			match: true,
   435  			subs:  []string{"registry.com:8080/myapp", "", "sha256:be178c0543eb17f5f3043021c9e5fcf30285e557a4fc309cce97ff9ca6182912"},
   436  		},
   437  		{
   438  			input: "registry.com:8080/myapp:tag2@sha256:be178c0543eb17f5f3043021c9e5fcf30285e557a4fc309cce97ff9ca6182912",
   439  			match: true,
   440  			subs:  []string{"registry.com:8080/myapp", "tag2", "sha256:be178c0543eb17f5f3043021c9e5fcf30285e557a4fc309cce97ff9ca6182912"},
   441  		},
   442  		{
   443  			input: "registry.com:8080/myapp@sha256:badbadbadbad",
   444  			match: false,
   445  		},
   446  		{
   447  			input: "registry.com:8080/myapp:invalid~tag",
   448  			match: false,
   449  		},
   450  		{
   451  			input: "bad_hostname.com:8080/myapp:tag",
   452  			match: false,
   453  		},
   454  		{
   455  			input:// localhost treated as name, missing tag with 8080 as tag
   456  			"localhost:8080@sha256:be178c0543eb17f5f3043021c9e5fcf30285e557a4fc309cce97ff9ca6182912",
   457  			match: true,
   458  			subs:  []string{"localhost", "8080", "sha256:be178c0543eb17f5f3043021c9e5fcf30285e557a4fc309cce97ff9ca6182912"},
   459  		},
   460  		{
   461  			input: "localhost:8080/name@sha256:be178c0543eb17f5f3043021c9e5fcf30285e557a4fc309cce97ff9ca6182912",
   462  			match: true,
   463  			subs:  []string{"localhost:8080/name", "", "sha256:be178c0543eb17f5f3043021c9e5fcf30285e557a4fc309cce97ff9ca6182912"},
   464  		},
   465  		{
   466  			input: "localhost:http/name@sha256:be178c0543eb17f5f3043021c9e5fcf30285e557a4fc309cce97ff9ca6182912",
   467  			match: false,
   468  		},
   469  		{
   470  			// localhost will be treated as an image name without a host
   471  			input: "localhost@sha256:be178c0543eb17f5f3043021c9e5fcf30285e557a4fc309cce97ff9ca6182912",
   472  			match: true,
   473  			subs:  []string{"localhost", "", "sha256:be178c0543eb17f5f3043021c9e5fcf30285e557a4fc309cce97ff9ca6182912"},
   474  		},
   475  		{
   476  			input: "registry.com:8080/myapp@bad",
   477  			match: false,
   478  		},
   479  		{
   480  			input: "registry.com:8080/myapp@2bad",
   481  			match: false, // TODO(dmcgowan): Support this as valid
   482  		},
   483  	}
   484  
   485  	for i := range testcases {
   486  		checkRegexp(t, ReferenceRegexp, testcases[i])
   487  	}
   488  
   489  }