github.com/mika/distribution@v2.2.2-0.20160108133430-a75790e3d8e0+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 r := regexp.MustCompile(`^` + hostnameRegexp.String() + `$`) 116 for i := range hostcases { 117 checkRegexp(t, r, hostcases[i]) 118 } 119 } 120 121 func TestFullNameRegexp(t *testing.T) { 122 if anchoredNameRegexp.NumSubexp() != 2 { 123 t.Fatalf("anchored name regexp should have two submatches: %v, %v != 2", 124 anchoredNameRegexp, anchoredNameRegexp.NumSubexp()) 125 } 126 127 testcases := []regexpMatch{ 128 { 129 input: "", 130 match: false, 131 }, 132 { 133 input: "short", 134 match: true, 135 subs: []string{"", "short"}, 136 }, 137 { 138 input: "simple/name", 139 match: true, 140 subs: []string{"simple", "name"}, 141 }, 142 { 143 input: "library/ubuntu", 144 match: true, 145 subs: []string{"library", "ubuntu"}, 146 }, 147 { 148 input: "docker/stevvooe/app", 149 match: true, 150 subs: []string{"docker", "stevvooe/app"}, 151 }, 152 { 153 input: "aa/aa/aa/aa/aa/aa/aa/aa/aa/bb/bb/bb/bb/bb/bb", 154 match: true, 155 subs: []string{"aa", "aa/aa/aa/aa/aa/aa/aa/aa/bb/bb/bb/bb/bb/bb"}, 156 }, 157 { 158 input: "aa/aa/bb/bb/bb", 159 match: true, 160 subs: []string{"aa", "aa/bb/bb/bb"}, 161 }, 162 { 163 input: "a/a/a/a", 164 match: true, 165 subs: []string{"a", "a/a/a"}, 166 }, 167 { 168 input: "a/a/a/a/", 169 match: false, 170 }, 171 { 172 input: "a//a/a", 173 match: false, 174 }, 175 { 176 input: "a", 177 match: true, 178 subs: []string{"", "a"}, 179 }, 180 { 181 input: "a/aa", 182 match: true, 183 subs: []string{"a", "aa"}, 184 }, 185 { 186 input: "a/aa/a", 187 match: true, 188 subs: []string{"a", "aa/a"}, 189 }, 190 { 191 input: "foo.com", 192 match: true, 193 subs: []string{"", "foo.com"}, 194 }, 195 { 196 input: "foo.com/", 197 match: false, 198 }, 199 { 200 input: "foo.com:8080/bar", 201 match: true, 202 subs: []string{"foo.com:8080", "bar"}, 203 }, 204 { 205 input: "foo.com:http/bar", 206 match: false, 207 }, 208 { 209 input: "foo.com/bar", 210 match: true, 211 subs: []string{"foo.com", "bar"}, 212 }, 213 { 214 input: "foo.com/bar/baz", 215 match: true, 216 subs: []string{"foo.com", "bar/baz"}, 217 }, 218 { 219 input: "localhost:8080/bar", 220 match: true, 221 subs: []string{"localhost:8080", "bar"}, 222 }, 223 { 224 input: "sub-dom1.foo.com/bar/baz/quux", 225 match: true, 226 subs: []string{"sub-dom1.foo.com", "bar/baz/quux"}, 227 }, 228 { 229 input: "blog.foo.com/bar/baz", 230 match: true, 231 subs: []string{"blog.foo.com", "bar/baz"}, 232 }, 233 { 234 input: "a^a", 235 match: false, 236 }, 237 { 238 input: "aa/asdf$$^/aa", 239 match: false, 240 }, 241 { 242 input: "asdf$$^/aa", 243 match: false, 244 }, 245 { 246 input: "aa-a/a", 247 match: true, 248 subs: []string{"aa-a", "a"}, 249 }, 250 { 251 input: strings.Repeat("a/", 128) + "a", 252 match: true, 253 subs: []string{"a", strings.Repeat("a/", 127) + "a"}, 254 }, 255 { 256 input: "a-/a/a/a", 257 match: false, 258 }, 259 { 260 input: "foo.com/a-/a/a", 261 match: false, 262 }, 263 { 264 input: "-foo/bar", 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: true, 286 subs: []string{"", "foo_bar"}, 287 }, 288 { 289 input: "foo_bar.com", 290 match: true, 291 subs: []string{"", "foo_bar.com"}, 292 }, 293 { 294 input: "foo_bar.com:8080", 295 match: false, 296 }, 297 { 298 input: "foo_bar.com:8080/app", 299 match: false, 300 }, 301 { 302 input: "foo.com/foo_bar", 303 match: true, 304 subs: []string{"foo.com", "foo_bar"}, 305 }, 306 { 307 input: "____/____", 308 match: false, 309 }, 310 { 311 input: "_docker/_docker", 312 match: false, 313 }, 314 { 315 input: "docker_/docker_", 316 match: false, 317 }, 318 { 319 input: "b.gcr.io/test.example.com/my-app", 320 match: true, 321 subs: []string{"b.gcr.io", "test.example.com/my-app"}, 322 }, 323 { 324 input: "xn--n3h.com/myimage", // ☃.com in punycode 325 match: true, 326 subs: []string{"xn--n3h.com", "myimage"}, 327 }, 328 { 329 input: "xn--7o8h.com/myimage", // 🐳.com in punycode 330 match: true, 331 subs: []string{"xn--7o8h.com", "myimage"}, 332 }, 333 { 334 input: "example.com/xn--7o8h.com/myimage", // 🐳.com in punycode 335 match: true, 336 subs: []string{"example.com", "xn--7o8h.com/myimage"}, 337 }, 338 { 339 input: "example.com/some_separator__underscore/myimage", 340 match: true, 341 subs: []string{"example.com", "some_separator__underscore/myimage"}, 342 }, 343 { 344 input: "example.com/__underscore/myimage", 345 match: false, 346 }, 347 { 348 input: "example.com/..dots/myimage", 349 match: false, 350 }, 351 { 352 input: "example.com/.dots/myimage", 353 match: false, 354 }, 355 { 356 input: "example.com/nodouble..dots/myimage", 357 match: false, 358 }, 359 { 360 input: "example.com/nodouble..dots/myimage", 361 match: false, 362 }, 363 { 364 input: "docker./docker", 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: "do..cker/docker", 381 match: false, 382 }, 383 { 384 input: "do__cker:8080/docker", 385 match: false, 386 }, 387 { 388 input: "do__cker/docker", 389 match: true, 390 subs: []string{"", "do__cker/docker"}, 391 }, 392 { 393 input: "b.gcr.io/test.example.com/my-app", 394 match: true, 395 subs: []string{"b.gcr.io", "test.example.com/my-app"}, 396 }, 397 { 398 input: "registry.io/foo/project--id.module--name.ver---sion--name", 399 match: true, 400 subs: []string{"registry.io", "foo/project--id.module--name.ver---sion--name"}, 401 }, 402 } 403 for i := range testcases { 404 checkRegexp(t, anchoredNameRegexp, testcases[i]) 405 } 406 } 407 408 func TestReferenceRegexp(t *testing.T) { 409 if ReferenceRegexp.NumSubexp() != 3 { 410 t.Fatalf("anchored name regexp should have three submatches: %v, %v != 3", 411 ReferenceRegexp, ReferenceRegexp.NumSubexp()) 412 } 413 414 testcases := []regexpMatch{ 415 { 416 input: "registry.com:8080/myapp:tag", 417 match: true, 418 subs: []string{"registry.com:8080/myapp", "tag", ""}, 419 }, 420 { 421 input: "registry.com:8080/myapp@sha256:be178c0543eb17f5f3043021c9e5fcf30285e557a4fc309cce97ff9ca6182912", 422 match: true, 423 subs: []string{"registry.com:8080/myapp", "", "sha256:be178c0543eb17f5f3043021c9e5fcf30285e557a4fc309cce97ff9ca6182912"}, 424 }, 425 { 426 input: "registry.com:8080/myapp:tag2@sha256:be178c0543eb17f5f3043021c9e5fcf30285e557a4fc309cce97ff9ca6182912", 427 match: true, 428 subs: []string{"registry.com:8080/myapp", "tag2", "sha256:be178c0543eb17f5f3043021c9e5fcf30285e557a4fc309cce97ff9ca6182912"}, 429 }, 430 { 431 input: "registry.com:8080/myapp@sha256:badbadbadbad", 432 match: false, 433 }, 434 { 435 input: "registry.com:8080/myapp:invalid~tag", 436 match: false, 437 }, 438 { 439 input: "bad_hostname.com:8080/myapp:tag", 440 match: false, 441 }, 442 { 443 input:// localhost treated as name, missing tag with 8080 as tag 444 "localhost:8080@sha256:be178c0543eb17f5f3043021c9e5fcf30285e557a4fc309cce97ff9ca6182912", 445 match: true, 446 subs: []string{"localhost", "8080", "sha256:be178c0543eb17f5f3043021c9e5fcf30285e557a4fc309cce97ff9ca6182912"}, 447 }, 448 { 449 input: "localhost:8080/name@sha256:be178c0543eb17f5f3043021c9e5fcf30285e557a4fc309cce97ff9ca6182912", 450 match: true, 451 subs: []string{"localhost:8080/name", "", "sha256:be178c0543eb17f5f3043021c9e5fcf30285e557a4fc309cce97ff9ca6182912"}, 452 }, 453 { 454 input: "localhost:http/name@sha256:be178c0543eb17f5f3043021c9e5fcf30285e557a4fc309cce97ff9ca6182912", 455 match: false, 456 }, 457 { 458 // localhost will be treated as an image name without a host 459 input: "localhost@sha256:be178c0543eb17f5f3043021c9e5fcf30285e557a4fc309cce97ff9ca6182912", 460 match: true, 461 subs: []string{"localhost", "", "sha256:be178c0543eb17f5f3043021c9e5fcf30285e557a4fc309cce97ff9ca6182912"}, 462 }, 463 { 464 input: "registry.com:8080/myapp@bad", 465 match: false, 466 }, 467 { 468 input: "registry.com:8080/myapp@2bad", 469 match: false, // TODO(dmcgowan): Support this as valid 470 }, 471 } 472 473 for i := range testcases { 474 checkRegexp(t, ReferenceRegexp, testcases[i]) 475 } 476 477 }