gitee.com/ks-custle/core-gm@v0.0.0-20230922171213-b83bdd97b62c/mux/old_test.go (about)

     1  // Old tests ported to Go1. This is a mess. Want to drop it one day.
     2  
     3  // Copyright 2011 Gorilla Authors. All rights reserved.
     4  // Use of this source code is governed by a BSD-style
     5  // license that can be found in the LICENSE file.
     6  
     7  package mux
     8  
     9  import (
    10  	"bytes"
    11  	"testing"
    12  
    13  	http "gitee.com/ks-custle/core-gm/gmhttp"
    14  )
    15  
    16  // ----------------------------------------------------------------------------
    17  // ResponseRecorder
    18  // ----------------------------------------------------------------------------
    19  // Copyright 2009 The Go Authors. All rights reserved.
    20  // Use of this source code is governed by a BSD-style
    21  // license that can be found in the LICENSE file.
    22  
    23  // ResponseRecorder is an implementation of http.ResponseWriter that
    24  // records its mutations for later inspection in tests.
    25  type ResponseRecorder struct {
    26  	Code      int           // the HTTP response code from WriteHeader
    27  	HeaderMap http.Header   // the HTTP response headers
    28  	Body      *bytes.Buffer // if non-nil, the bytes.Buffer to append written data to
    29  	Flushed   bool
    30  }
    31  
    32  // NewRecorder returns an initialized ResponseRecorder.
    33  func NewRecorder() *ResponseRecorder {
    34  	return &ResponseRecorder{
    35  		HeaderMap: make(http.Header),
    36  		Body:      new(bytes.Buffer),
    37  	}
    38  }
    39  
    40  // Header returns the response headers.
    41  func (rw *ResponseRecorder) Header() http.Header {
    42  	return rw.HeaderMap
    43  }
    44  
    45  // Write always succeeds and writes to rw.Body, if not nil.
    46  func (rw *ResponseRecorder) Write(buf []byte) (int, error) {
    47  	if rw.Body != nil {
    48  		rw.Body.Write(buf)
    49  	}
    50  	if rw.Code == 0 {
    51  		rw.Code = http.StatusOK
    52  	}
    53  	return len(buf), nil
    54  }
    55  
    56  // WriteHeader sets rw.Code.
    57  func (rw *ResponseRecorder) WriteHeader(code int) {
    58  	rw.Code = code
    59  }
    60  
    61  // Flush sets rw.Flushed to true.
    62  func (rw *ResponseRecorder) Flush() {
    63  	rw.Flushed = true
    64  }
    65  
    66  // ----------------------------------------------------------------------------
    67  
    68  func TestRouteMatchers(t *testing.T) {
    69  	var scheme, host, path, query, method string
    70  	var headers map[string]string
    71  	var resultVars map[bool]map[string]string
    72  
    73  	router := NewRouter()
    74  	router.NewRoute().Host("{var1}.google.com").
    75  		Path("/{var2:[a-z]+}/{var3:[0-9]+}").
    76  		Queries("foo", "bar").
    77  		Methods("GET").
    78  		Schemes("https").
    79  		Headers("x-requested-with", "XMLHttpRequest")
    80  	router.NewRoute().Host("www.{var4}.com").
    81  		PathPrefix("/foo/{var5:[a-z]+}/{var6:[0-9]+}").
    82  		Queries("baz", "ding").
    83  		Methods("POST").
    84  		Schemes("http").
    85  		Headers("Content-Type", "application/json")
    86  
    87  	reset := func() {
    88  		// Everything match.
    89  		scheme = "https"
    90  		host = "www.google.com"
    91  		path = "/product/42"
    92  		query = "?foo=bar"
    93  		method = "GET"
    94  		headers = map[string]string{"X-Requested-With": "XMLHttpRequest"}
    95  		resultVars = map[bool]map[string]string{
    96  			true:  {"var1": "www", "var2": "product", "var3": "42"},
    97  			false: {},
    98  		}
    99  	}
   100  
   101  	reset2 := func() {
   102  		// Everything match.
   103  		scheme = "http"
   104  		host = "www.google.com"
   105  		path = "/foo/product/42/path/that/is/ignored"
   106  		query = "?baz=ding"
   107  		method = "POST"
   108  		headers = map[string]string{"Content-Type": "application/json"}
   109  		resultVars = map[bool]map[string]string{
   110  			true:  {"var4": "google", "var5": "product", "var6": "42"},
   111  			false: {},
   112  		}
   113  	}
   114  
   115  	match := func(shouldMatch bool) {
   116  		url := scheme + "://" + host + path + query
   117  		request, _ := http.NewRequest(method, url, nil)
   118  		for key, value := range headers {
   119  			request.Header.Add(key, value)
   120  		}
   121  
   122  		var routeMatch RouteMatch
   123  		matched := router.Match(request, &routeMatch)
   124  		if matched != shouldMatch {
   125  			t.Errorf("Expected: %v\nGot: %v\nRequest: %v %v", shouldMatch, matched, request.Method, url)
   126  		}
   127  
   128  		if matched {
   129  			currentRoute := routeMatch.Route
   130  			if currentRoute == nil {
   131  				t.Errorf("Expected a current route.")
   132  			}
   133  			vars := routeMatch.Vars
   134  			expectedVars := resultVars[shouldMatch]
   135  			if len(vars) != len(expectedVars) {
   136  				t.Errorf("Expected vars: %v Got: %v.", expectedVars, vars)
   137  			}
   138  			for name, value := range vars {
   139  				if expectedVars[name] != value {
   140  					t.Errorf("Expected vars: %v Got: %v.", expectedVars, vars)
   141  				}
   142  			}
   143  		}
   144  	}
   145  
   146  	// 1st route --------------------------------------------------------------
   147  
   148  	// Everything match.
   149  	reset()
   150  	match(true)
   151  
   152  	// Scheme doesn't match.
   153  	reset()
   154  	scheme = "http"
   155  	match(false)
   156  
   157  	// Host doesn't match.
   158  	reset()
   159  	host = "www.mygoogle.com"
   160  	match(false)
   161  
   162  	// Path doesn't match.
   163  	reset()
   164  	path = "/product/notdigits"
   165  	match(false)
   166  
   167  	// Query doesn't match.
   168  	reset()
   169  	query = "?foo=baz"
   170  	match(false)
   171  
   172  	// Method doesn't match.
   173  	reset()
   174  	method = "POST"
   175  	match(false)
   176  
   177  	// Header doesn't match.
   178  	reset()
   179  	headers = map[string]string{}
   180  	match(false)
   181  
   182  	// Everything match, again.
   183  	reset()
   184  	match(true)
   185  
   186  	// 2nd route --------------------------------------------------------------
   187  	// Everything match.
   188  	reset2()
   189  	match(true)
   190  
   191  	// Scheme doesn't match.
   192  	reset2()
   193  	scheme = "https"
   194  	match(false)
   195  
   196  	// Host doesn't match.
   197  	reset2()
   198  	host = "sub.google.com"
   199  	match(false)
   200  
   201  	// Path doesn't match.
   202  	reset2()
   203  	path = "/bar/product/42"
   204  	match(false)
   205  
   206  	// Query doesn't match.
   207  	reset2()
   208  	query = "?foo=baz"
   209  	match(false)
   210  
   211  	// Method doesn't match.
   212  	reset2()
   213  	method = "GET"
   214  	match(false)
   215  
   216  	// Header doesn't match.
   217  	reset2()
   218  	headers = map[string]string{}
   219  	match(false)
   220  
   221  	// Everything match, again.
   222  	reset2()
   223  	match(true)
   224  }
   225  
   226  type headerMatcherTest struct {
   227  	matcher headerMatcher
   228  	headers map[string]string
   229  	result  bool
   230  }
   231  
   232  var headerMatcherTests = []headerMatcherTest{
   233  	{
   234  		matcher: headerMatcher(map[string]string{"x-requested-with": "XMLHttpRequest"}),
   235  		headers: map[string]string{"X-Requested-With": "XMLHttpRequest"},
   236  		result:  true,
   237  	},
   238  	{
   239  		matcher: headerMatcher(map[string]string{"x-requested-with": ""}),
   240  		headers: map[string]string{"X-Requested-With": "anything"},
   241  		result:  true,
   242  	},
   243  	{
   244  		matcher: headerMatcher(map[string]string{"x-requested-with": "XMLHttpRequest"}),
   245  		headers: map[string]string{},
   246  		result:  false,
   247  	},
   248  }
   249  
   250  type hostMatcherTest struct {
   251  	matcher *Route
   252  	url     string
   253  	vars    map[string]string
   254  	result  bool
   255  }
   256  
   257  var hostMatcherTests = []hostMatcherTest{
   258  	{
   259  		matcher: NewRouter().NewRoute().Host("{foo:[a-z][a-z][a-z]}.{bar:[a-z][a-z][a-z]}.{baz:[a-z][a-z][a-z]}"),
   260  		url:     "http://abc.def.ghi/",
   261  		vars:    map[string]string{"foo": "abc", "bar": "def", "baz": "ghi"},
   262  		result:  true,
   263  	},
   264  	{
   265  		matcher: NewRouter().NewRoute().Host("{foo:[a-z][a-z][a-z]}.{bar:[a-z][a-z][a-z]}.{baz:[a-z][a-z][a-z]}:{port:.*}"),
   266  		url:     "http://abc.def.ghi:65535/",
   267  		vars:    map[string]string{"foo": "abc", "bar": "def", "baz": "ghi", "port": "65535"},
   268  		result:  true,
   269  	},
   270  	{
   271  		matcher: NewRouter().NewRoute().Host("{foo:[a-z][a-z][a-z]}.{bar:[a-z][a-z][a-z]}.{baz:[a-z][a-z][a-z]}"),
   272  		url:     "http://abc.def.ghi:65535/",
   273  		vars:    map[string]string{"foo": "abc", "bar": "def", "baz": "ghi"},
   274  		result:  true,
   275  	},
   276  	{
   277  		matcher: NewRouter().NewRoute().Host("{foo:[a-z][a-z][a-z]}.{bar:[a-z][a-z][a-z]}.{baz:[a-z][a-z][a-z]}"),
   278  		url:     "http://a.b.c/",
   279  		vars:    map[string]string{"foo": "abc", "bar": "def", "baz": "ghi"},
   280  		result:  false,
   281  	},
   282  }
   283  
   284  type methodMatcherTest struct {
   285  	matcher methodMatcher
   286  	method  string
   287  	result  bool
   288  }
   289  
   290  var methodMatcherTests = []methodMatcherTest{
   291  	{
   292  		matcher: methodMatcher([]string{"GET", "POST", "PUT"}),
   293  		method:  "GET",
   294  		result:  true,
   295  	},
   296  	{
   297  		matcher: methodMatcher([]string{"GET", "POST", "PUT"}),
   298  		method:  "POST",
   299  		result:  true,
   300  	},
   301  	{
   302  		matcher: methodMatcher([]string{"GET", "POST", "PUT"}),
   303  		method:  "PUT",
   304  		result:  true,
   305  	},
   306  	{
   307  		matcher: methodMatcher([]string{"GET", "POST", "PUT"}),
   308  		method:  "DELETE",
   309  		result:  false,
   310  	},
   311  }
   312  
   313  type pathMatcherTest struct {
   314  	matcher *Route
   315  	url     string
   316  	vars    map[string]string
   317  	result  bool
   318  }
   319  
   320  var pathMatcherTests = []pathMatcherTest{
   321  	{
   322  		matcher: NewRouter().NewRoute().Path("/{foo:[0-9][0-9][0-9]}/{bar:[0-9][0-9][0-9]}/{baz:[0-9][0-9][0-9]}"),
   323  		url:     "http://localhost:8080/123/456/789",
   324  		vars:    map[string]string{"foo": "123", "bar": "456", "baz": "789"},
   325  		result:  true,
   326  	},
   327  	{
   328  		matcher: NewRouter().NewRoute().Path("/{foo:[0-9][0-9][0-9]}/{bar:[0-9][0-9][0-9]}/{baz:[0-9][0-9][0-9]}"),
   329  		url:     "http://localhost:8080/1/2/3",
   330  		vars:    map[string]string{"foo": "123", "bar": "456", "baz": "789"},
   331  		result:  false,
   332  	},
   333  }
   334  
   335  type schemeMatcherTest struct {
   336  	matcher schemeMatcher
   337  	url     string
   338  	result  bool
   339  }
   340  
   341  var schemeMatcherTests = []schemeMatcherTest{
   342  	{
   343  		matcher: schemeMatcher([]string{"http", "https"}),
   344  		url:     "http://localhost:8080/",
   345  		result:  true,
   346  	},
   347  	{
   348  		matcher: schemeMatcher([]string{"http", "https"}),
   349  		url:     "https://localhost:8080/",
   350  		result:  true,
   351  	},
   352  	{
   353  		matcher: schemeMatcher([]string{"https"}),
   354  		url:     "http://localhost:8080/",
   355  		result:  false,
   356  	},
   357  	{
   358  		matcher: schemeMatcher([]string{"http"}),
   359  		url:     "https://localhost:8080/",
   360  		result:  false,
   361  	},
   362  }
   363  
   364  type urlBuildingTest struct {
   365  	route *Route
   366  	vars  []string
   367  	url   string
   368  }
   369  
   370  var urlBuildingTests = []urlBuildingTest{
   371  	{
   372  		route: new(Route).Host("foo.domain.com"),
   373  		vars:  []string{},
   374  		url:   "http://foo.domain.com",
   375  	},
   376  	{
   377  		route: new(Route).Host("{subdomain}.domain.com"),
   378  		vars:  []string{"subdomain", "bar"},
   379  		url:   "http://bar.domain.com",
   380  	},
   381  	{
   382  		route: new(Route).Host("{subdomain}.domain.com:{port:.*}"),
   383  		vars:  []string{"subdomain", "bar", "port", "65535"},
   384  		url:   "http://bar.domain.com:65535",
   385  	},
   386  	{
   387  		route: new(Route).Host("foo.domain.com").Path("/articles"),
   388  		vars:  []string{},
   389  		url:   "http://foo.domain.com/articles",
   390  	},
   391  	{
   392  		route: new(Route).Path("/articles"),
   393  		vars:  []string{},
   394  		url:   "/articles",
   395  	},
   396  	{
   397  		route: new(Route).Path("/articles/{category}/{id:[0-9]+}"),
   398  		vars:  []string{"category", "technology", "id", "42"},
   399  		url:   "/articles/technology/42",
   400  	},
   401  	{
   402  		route: new(Route).Host("{subdomain}.domain.com").Path("/articles/{category}/{id:[0-9]+}"),
   403  		vars:  []string{"subdomain", "foo", "category", "technology", "id", "42"},
   404  		url:   "http://foo.domain.com/articles/technology/42",
   405  	},
   406  	{
   407  		route: new(Route).Host("example.com").Schemes("https", "http"),
   408  		vars:  []string{},
   409  		url:   "https://example.com",
   410  	},
   411  }
   412  
   413  func TestHeaderMatcher(t *testing.T) {
   414  	for _, v := range headerMatcherTests {
   415  		request, _ := http.NewRequest("GET", "http://localhost:8080/", nil)
   416  		for key, value := range v.headers {
   417  			request.Header.Add(key, value)
   418  		}
   419  		var routeMatch RouteMatch
   420  		result := v.matcher.Match(request, &routeMatch)
   421  		if result != v.result {
   422  			if v.result {
   423  				t.Errorf("%#v: should match %v.", v.matcher, request.Header)
   424  			} else {
   425  				t.Errorf("%#v: should not match %v.", v.matcher, request.Header)
   426  			}
   427  		}
   428  	}
   429  }
   430  
   431  func TestHostMatcher(t *testing.T) {
   432  	for _, v := range hostMatcherTests {
   433  		request, err := http.NewRequest("GET", v.url, nil)
   434  		if err != nil {
   435  			t.Errorf("http.NewRequest failed %#v", err)
   436  			continue
   437  		}
   438  		var routeMatch RouteMatch
   439  		result := v.matcher.Match(request, &routeMatch)
   440  		vars := routeMatch.Vars
   441  		if result != v.result {
   442  			if v.result {
   443  				t.Errorf("%#v: should match %v.", v.matcher, v.url)
   444  			} else {
   445  				t.Errorf("%#v: should not match %v.", v.matcher, v.url)
   446  			}
   447  		}
   448  		if result {
   449  			if len(vars) != len(v.vars) {
   450  				t.Errorf("%#v: vars length should be %v, got %v.", v.matcher, len(v.vars), len(vars))
   451  			}
   452  			for name, value := range vars {
   453  				if v.vars[name] != value {
   454  					t.Errorf("%#v: expected value %v for key %v, got %v.", v.matcher, v.vars[name], name, value)
   455  				}
   456  			}
   457  		} else {
   458  			if len(vars) != 0 {
   459  				t.Errorf("%#v: vars length should be 0, got %v.", v.matcher, len(vars))
   460  			}
   461  		}
   462  	}
   463  }
   464  
   465  func TestMethodMatcher(t *testing.T) {
   466  	for _, v := range methodMatcherTests {
   467  		request, _ := http.NewRequest(v.method, "http://localhost:8080/", nil)
   468  		var routeMatch RouteMatch
   469  		result := v.matcher.Match(request, &routeMatch)
   470  		if result != v.result {
   471  			if v.result {
   472  				t.Errorf("%#v: should match %v.", v.matcher, v.method)
   473  			} else {
   474  				t.Errorf("%#v: should not match %v.", v.matcher, v.method)
   475  			}
   476  		}
   477  	}
   478  }
   479  
   480  func TestPathMatcher(t *testing.T) {
   481  	for _, v := range pathMatcherTests {
   482  		request, _ := http.NewRequest("GET", v.url, nil)
   483  		var routeMatch RouteMatch
   484  		result := v.matcher.Match(request, &routeMatch)
   485  		vars := routeMatch.Vars
   486  		if result != v.result {
   487  			if v.result {
   488  				t.Errorf("%#v: should match %v.", v.matcher, v.url)
   489  			} else {
   490  				t.Errorf("%#v: should not match %v.", v.matcher, v.url)
   491  			}
   492  		}
   493  		if result {
   494  			if len(vars) != len(v.vars) {
   495  				t.Errorf("%#v: vars length should be %v, got %v.", v.matcher, len(v.vars), len(vars))
   496  			}
   497  			for name, value := range vars {
   498  				if v.vars[name] != value {
   499  					t.Errorf("%#v: expected value %v for key %v, got %v.", v.matcher, v.vars[name], name, value)
   500  				}
   501  			}
   502  		} else {
   503  			if len(vars) != 0 {
   504  				t.Errorf("%#v: vars length should be 0, got %v.", v.matcher, len(vars))
   505  			}
   506  		}
   507  	}
   508  }
   509  
   510  func TestSchemeMatcher(t *testing.T) {
   511  	for _, v := range schemeMatcherTests {
   512  		request, _ := http.NewRequest("GET", v.url, nil)
   513  		var routeMatch RouteMatch
   514  		result := v.matcher.Match(request, &routeMatch)
   515  		if result != v.result {
   516  			if v.result {
   517  				t.Errorf("%#v: should match %v.", v.matcher, v.url)
   518  			} else {
   519  				t.Errorf("%#v: should not match %v.", v.matcher, v.url)
   520  			}
   521  		}
   522  	}
   523  }
   524  
   525  func TestUrlBuilding(t *testing.T) {
   526  
   527  	for _, v := range urlBuildingTests {
   528  		u, _ := v.route.URL(v.vars...)
   529  		url := u.String()
   530  		if url != v.url {
   531  			t.Errorf("expected %v, got %v", v.url, url)
   532  		}
   533  	}
   534  
   535  	ArticleHandler := func(w http.ResponseWriter, r *http.Request) {
   536  	}
   537  
   538  	router := NewRouter()
   539  	router.HandleFunc("/articles/{category}/{id:[0-9]+}", ArticleHandler).Name("article")
   540  
   541  	url, _ := router.Get("article").URL("category", "technology", "id", "42")
   542  	expected := "/articles/technology/42"
   543  	if url.String() != expected {
   544  		t.Errorf("Expected %v, got %v", expected, url.String())
   545  	}
   546  }
   547  
   548  func TestMatchedRouteName(t *testing.T) {
   549  	routeName := "stock"
   550  	router := NewRouter()
   551  	route := router.NewRoute().Path("/products/").Name(routeName)
   552  
   553  	url := "http://www.example.com/products/"
   554  	request, _ := http.NewRequest("GET", url, nil)
   555  	var rv RouteMatch
   556  	ok := router.Match(request, &rv)
   557  
   558  	if !ok || rv.Route != route {
   559  		t.Errorf("Expected same route, got %+v.", rv.Route)
   560  	}
   561  
   562  	retName := rv.Route.GetName()
   563  	if retName != routeName {
   564  		t.Errorf("Expected %q, got %q.", routeName, retName)
   565  	}
   566  }
   567  
   568  func TestSubRouting(t *testing.T) {
   569  	// Example from docs.
   570  	router := NewRouter()
   571  	subrouter := router.NewRoute().Host("www.example.com").Subrouter()
   572  	route := subrouter.NewRoute().Path("/products/").Name("products")
   573  
   574  	url := "http://www.example.com/products/"
   575  	request, _ := http.NewRequest("GET", url, nil)
   576  	var rv RouteMatch
   577  	ok := router.Match(request, &rv)
   578  
   579  	if !ok || rv.Route != route {
   580  		t.Errorf("Expected same route, got %+v.", rv.Route)
   581  	}
   582  
   583  	u, _ := router.Get("products").URL()
   584  	builtURL := u.String()
   585  	// Yay, subroute aware of the domain when building!
   586  	if builtURL != url {
   587  		t.Errorf("Expected %q, got %q.", url, builtURL)
   588  	}
   589  }
   590  
   591  func TestVariableNames(t *testing.T) {
   592  	route := new(Route).Host("{arg1}.domain.com").Path("/{arg1}/{arg2:[0-9]+}")
   593  	if route.err == nil {
   594  		t.Errorf("Expected error for duplicated variable names")
   595  	}
   596  }
   597  
   598  func TestRedirectSlash(t *testing.T) {
   599  	var route *Route
   600  	var routeMatch RouteMatch
   601  	r := NewRouter()
   602  
   603  	r.StrictSlash(false)
   604  	route = r.NewRoute()
   605  	if route.strictSlash != false {
   606  		t.Errorf("Expected false redirectSlash.")
   607  	}
   608  
   609  	r.StrictSlash(true)
   610  	route = r.NewRoute()
   611  	if route.strictSlash != true {
   612  		t.Errorf("Expected true redirectSlash.")
   613  	}
   614  
   615  	route = new(Route)
   616  	route.strictSlash = true
   617  	route.Path("/{arg1}/{arg2:[0-9]+}/")
   618  	request, _ := http.NewRequest("GET", "http://localhost/foo/123", nil)
   619  	routeMatch = RouteMatch{}
   620  	_ = route.Match(request, &routeMatch)
   621  	vars := routeMatch.Vars
   622  	if vars["arg1"] != "foo" {
   623  		t.Errorf("Expected foo.")
   624  	}
   625  	if vars["arg2"] != "123" {
   626  		t.Errorf("Expected 123.")
   627  	}
   628  	rsp := NewRecorder()
   629  	routeMatch.Handler.ServeHTTP(rsp, request)
   630  	if rsp.HeaderMap.Get("Location") != "http://localhost/foo/123/" {
   631  		t.Errorf("Expected redirect header.")
   632  	}
   633  
   634  	route = new(Route)
   635  	route.strictSlash = true
   636  	route.Path("/{arg1}/{arg2:[0-9]+}")
   637  	request, _ = http.NewRequest("GET", "http://localhost/foo/123/", nil)
   638  	routeMatch = RouteMatch{}
   639  	_ = route.Match(request, &routeMatch)
   640  	vars = routeMatch.Vars
   641  	if vars["arg1"] != "foo" {
   642  		t.Errorf("Expected foo.")
   643  	}
   644  	if vars["arg2"] != "123" {
   645  		t.Errorf("Expected 123.")
   646  	}
   647  	rsp = NewRecorder()
   648  	routeMatch.Handler.ServeHTTP(rsp, request)
   649  	if rsp.HeaderMap.Get("Location") != "http://localhost/foo/123" {
   650  		t.Errorf("Expected redirect header.")
   651  	}
   652  }
   653  
   654  // Test for the new regexp library, still not available in stable Go.
   655  func TestNewRegexp(t *testing.T) {
   656  	var p *routeRegexp
   657  	var matches []string
   658  
   659  	tests := map[string]map[string][]string{
   660  		"/{foo:a{2}}": {
   661  			"/a":    nil,
   662  			"/aa":   {"aa"},
   663  			"/aaa":  nil,
   664  			"/aaaa": nil,
   665  		},
   666  		"/{foo:a{2,}}": {
   667  			"/a":    nil,
   668  			"/aa":   {"aa"},
   669  			"/aaa":  {"aaa"},
   670  			"/aaaa": {"aaaa"},
   671  		},
   672  		"/{foo:a{2,3}}": {
   673  			"/a":    nil,
   674  			"/aa":   {"aa"},
   675  			"/aaa":  {"aaa"},
   676  			"/aaaa": nil,
   677  		},
   678  		"/{foo:[a-z]{3}}/{bar:[a-z]{2}}": {
   679  			"/a":       nil,
   680  			"/ab":      nil,
   681  			"/abc":     nil,
   682  			"/abcd":    nil,
   683  			"/abc/ab":  {"abc", "ab"},
   684  			"/abc/abc": nil,
   685  			"/abcd/ab": nil,
   686  		},
   687  		`/{foo:\w{3,}}/{bar:\d{2,}}`: {
   688  			"/a":        nil,
   689  			"/ab":       nil,
   690  			"/abc":      nil,
   691  			"/abc/1":    nil,
   692  			"/abc/12":   {"abc", "12"},
   693  			"/abcd/12":  {"abcd", "12"},
   694  			"/abcd/123": {"abcd", "123"},
   695  		},
   696  	}
   697  
   698  	for pattern, paths := range tests {
   699  		p, _ = newRouteRegexp(pattern, regexpTypePath, routeRegexpOptions{})
   700  		for path, result := range paths {
   701  			matches = p.regexp.FindStringSubmatch(path)
   702  			if result == nil {
   703  				if matches != nil {
   704  					t.Errorf("%v should not match %v.", pattern, path)
   705  				}
   706  			} else {
   707  				if len(matches) != len(result)+1 {
   708  					t.Errorf("Expected %v matches, got %v.", len(result)+1, len(matches))
   709  				} else {
   710  					for k, v := range result {
   711  						if matches[k+1] != v {
   712  							t.Errorf("Expected %v, got %v.", v, matches[k+1])
   713  						}
   714  					}
   715  				}
   716  			}
   717  		}
   718  	}
   719  }