github.com/hellobchain/third_party@v0.0.0-20230331131523-deb0478a2e52/gin/tree_test.go (about) 1 // Copyright 2013 Julien Schmidt. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be found 3 // at https://github.com/julienschmidt/httprouter/blob/master/LICENSE 4 5 package gin 6 7 import ( 8 "fmt" 9 "reflect" 10 "regexp" 11 "strings" 12 "testing" 13 ) 14 15 // Used as a workaround since we can't compare functions or their addresses 16 var fakeHandlerValue string 17 18 func fakeHandler(val string) HandlersChain { 19 return HandlersChain{func(c *Context) { 20 fakeHandlerValue = val 21 }} 22 } 23 24 type testRequests []struct { 25 path string 26 nilHandler bool 27 route string 28 ps Params 29 } 30 31 func getParams() *Params { 32 ps := make(Params, 0, 20) 33 return &ps 34 } 35 36 func checkRequests(t *testing.T, tree *node, requests testRequests, unescapes ...bool) { 37 unescape := false 38 if len(unescapes) >= 1 { 39 unescape = unescapes[0] 40 } 41 42 for _, request := range requests { 43 value := tree.getValue(request.path, getParams(), unescape) 44 45 if value.handlers == nil { 46 if !request.nilHandler { 47 t.Errorf("handle mismatch for route '%s': Expected non-nil handle", request.path) 48 } 49 } else if request.nilHandler { 50 t.Errorf("handle mismatch for route '%s': Expected nil handle", request.path) 51 } else { 52 value.handlers[0](nil) 53 if fakeHandlerValue != request.route { 54 t.Errorf("handle mismatch for route '%s': Wrong handle (%s != %s)", request.path, fakeHandlerValue, request.route) 55 } 56 } 57 58 if value.params != nil { 59 if !reflect.DeepEqual(*value.params, request.ps) { 60 t.Errorf("Params mismatch for route '%s'", request.path) 61 } 62 } 63 64 } 65 } 66 67 func checkPriorities(t *testing.T, n *node) uint32 { 68 var prio uint32 69 for i := range n.children { 70 prio += checkPriorities(t, n.children[i]) 71 } 72 73 if n.handlers != nil { 74 prio++ 75 } 76 77 if n.priority != prio { 78 t.Errorf( 79 "priority mismatch for node '%s': is %d, should be %d", 80 n.path, n.priority, prio, 81 ) 82 } 83 84 return prio 85 } 86 87 func TestCountParams(t *testing.T) { 88 if countParams("/path/:param1/static/*catch-all") != 2 { 89 t.Fail() 90 } 91 if countParams(strings.Repeat("/:param", 256)) != 256 { 92 t.Fail() 93 } 94 } 95 96 func TestTreeAddAndGet(t *testing.T) { 97 tree := &node{} 98 99 routes := [...]string{ 100 "/hi", 101 "/contact", 102 "/co", 103 "/c", 104 "/a", 105 "/ab", 106 "/doc/", 107 "/doc/go_faq.html", 108 "/doc/go1.html", 109 "/α", 110 "/β", 111 } 112 for _, route := range routes { 113 tree.addRoute(route, fakeHandler(route)) 114 } 115 116 checkRequests(t, tree, testRequests{ 117 {"/a", false, "/a", nil}, 118 {"/", true, "", nil}, 119 {"/hi", false, "/hi", nil}, 120 {"/contact", false, "/contact", nil}, 121 {"/co", false, "/co", nil}, 122 {"/con", true, "", nil}, // key mismatch 123 {"/cona", true, "", nil}, // key mismatch 124 {"/no", true, "", nil}, // no matching child 125 {"/ab", false, "/ab", nil}, 126 {"/α", false, "/α", nil}, 127 {"/β", false, "/β", nil}, 128 }) 129 130 checkPriorities(t, tree) 131 } 132 133 func TestTreeWildcard(t *testing.T) { 134 tree := &node{} 135 136 routes := [...]string{ 137 "/", 138 "/cmd/:tool/", 139 "/cmd/:tool/:sub", 140 "/cmd/whoami", 141 "/cmd/whoami/root", 142 "/cmd/whoami/root/", 143 "/src/*filepath", 144 "/search/", 145 "/search/:query", 146 "/search/gin-gonic", 147 "/search/google", 148 "/user_:name", 149 "/user_:name/about", 150 "/files/:dir/*filepath", 151 "/doc/", 152 "/doc/go_faq.html", 153 "/doc/go1.html", 154 "/info/:user/public", 155 "/info/:user/project/:project", 156 "/info/:user/project/golang", 157 } 158 for _, route := range routes { 159 tree.addRoute(route, fakeHandler(route)) 160 } 161 162 checkRequests(t, tree, testRequests{ 163 {"/", false, "/", nil}, 164 {"/cmd/test", true, "/cmd/:tool/", Params{Param{"tool", "test"}}}, 165 {"/cmd/test/", false, "/cmd/:tool/", Params{Param{"tool", "test"}}}, 166 {"/cmd/test/3", false, "/cmd/:tool/:sub", Params{Param{Key: "tool", Value: "test"}, Param{Key: "sub", Value: "3"}}}, 167 {"/cmd/who", true, "/cmd/:tool/", Params{Param{"tool", "who"}}}, 168 {"/cmd/who/", false, "/cmd/:tool/", Params{Param{"tool", "who"}}}, 169 {"/cmd/whoami", false, "/cmd/whoami", nil}, 170 {"/cmd/whoami/", true, "/cmd/whoami", nil}, 171 {"/cmd/whoami/r", false, "/cmd/:tool/:sub", Params{Param{Key: "tool", Value: "whoami"}, Param{Key: "sub", Value: "r"}}}, 172 {"/cmd/whoami/r/", true, "/cmd/:tool/:sub", Params{Param{Key: "tool", Value: "whoami"}, Param{Key: "sub", Value: "r"}}}, 173 {"/cmd/whoami/root", false, "/cmd/whoami/root", nil}, 174 {"/cmd/whoami/root/", false, "/cmd/whoami/root/", nil}, 175 {"/src/", false, "/src/*filepath", Params{Param{Key: "filepath", Value: "/"}}}, 176 {"/src/some/file.png", false, "/src/*filepath", Params{Param{Key: "filepath", Value: "/some/file.png"}}}, 177 {"/search/", false, "/search/", nil}, 178 {"/search/someth!ng+in+ünìcodé", false, "/search/:query", Params{Param{Key: "query", Value: "someth!ng+in+ünìcodé"}}}, 179 {"/search/someth!ng+in+ünìcodé/", true, "", Params{Param{Key: "query", Value: "someth!ng+in+ünìcodé"}}}, 180 {"/search/gin", false, "/search/:query", Params{Param{"query", "gin"}}}, 181 {"/search/gin-gonic", false, "/search/gin-gonic", nil}, 182 {"/search/google", false, "/search/google", nil}, 183 {"/user_gopher", false, "/user_:name", Params{Param{Key: "name", Value: "gopher"}}}, 184 {"/user_gopher/about", false, "/user_:name/about", Params{Param{Key: "name", Value: "gopher"}}}, 185 {"/files/js/inc/framework.js", false, "/files/:dir/*filepath", Params{Param{Key: "dir", Value: "js"}, Param{Key: "filepath", Value: "/inc/framework.js"}}}, 186 {"/info/gordon/public", false, "/info/:user/public", Params{Param{Key: "user", Value: "gordon"}}}, 187 {"/info/gordon/project/go", false, "/info/:user/project/:project", Params{Param{Key: "user", Value: "gordon"}, Param{Key: "project", Value: "go"}}}, 188 {"/info/gordon/project/golang", false, "/info/:user/project/golang", Params{Param{Key: "user", Value: "gordon"}}}, 189 }) 190 191 checkPriorities(t, tree) 192 } 193 194 func TestUnescapeParameters(t *testing.T) { 195 tree := &node{} 196 197 routes := [...]string{ 198 "/", 199 "/cmd/:tool/:sub", 200 "/cmd/:tool/", 201 "/src/*filepath", 202 "/search/:query", 203 "/files/:dir/*filepath", 204 "/info/:user/project/:project", 205 "/info/:user", 206 } 207 for _, route := range routes { 208 tree.addRoute(route, fakeHandler(route)) 209 } 210 211 unescape := true 212 checkRequests(t, tree, testRequests{ 213 {"/", false, "/", nil}, 214 {"/cmd/test/", false, "/cmd/:tool/", Params{Param{Key: "tool", Value: "test"}}}, 215 {"/cmd/test", true, "", Params{Param{Key: "tool", Value: "test"}}}, 216 {"/src/some/file.png", false, "/src/*filepath", Params{Param{Key: "filepath", Value: "/some/file.png"}}}, 217 {"/src/some/file+test.png", false, "/src/*filepath", Params{Param{Key: "filepath", Value: "/some/file test.png"}}}, 218 {"/src/some/file++++%%%%test.png", false, "/src/*filepath", Params{Param{Key: "filepath", Value: "/some/file++++%%%%test.png"}}}, 219 {"/src/some/file%2Ftest.png", false, "/src/*filepath", Params{Param{Key: "filepath", Value: "/some/file/test.png"}}}, 220 {"/search/someth!ng+in+ünìcodé", false, "/search/:query", Params{Param{Key: "query", Value: "someth!ng in ünìcodé"}}}, 221 {"/info/gordon/project/go", false, "/info/:user/project/:project", Params{Param{Key: "user", Value: "gordon"}, Param{Key: "project", Value: "go"}}}, 222 {"/info/slash%2Fgordon", false, "/info/:user", Params{Param{Key: "user", Value: "slash/gordon"}}}, 223 {"/info/slash%2Fgordon/project/Project%20%231", false, "/info/:user/project/:project", Params{Param{Key: "user", Value: "slash/gordon"}, Param{Key: "project", Value: "Project #1"}}}, 224 {"/info/slash%%%%", false, "/info/:user", Params{Param{Key: "user", Value: "slash%%%%"}}}, 225 {"/info/slash%%%%2Fgordon/project/Project%%%%20%231", false, "/info/:user/project/:project", Params{Param{Key: "user", Value: "slash%%%%2Fgordon"}, Param{Key: "project", Value: "Project%%%%20%231"}}}, 226 }, unescape) 227 228 checkPriorities(t, tree) 229 } 230 231 func catchPanic(testFunc func()) (recv interface{}) { 232 defer func() { 233 recv = recover() 234 }() 235 236 testFunc() 237 return 238 } 239 240 type testRoute struct { 241 path string 242 conflict bool 243 } 244 245 func testRoutes(t *testing.T, routes []testRoute) { 246 tree := &node{} 247 248 for _, route := range routes { 249 recv := catchPanic(func() { 250 tree.addRoute(route.path, nil) 251 }) 252 253 if route.conflict { 254 if recv == nil { 255 t.Errorf("no panic for conflicting route '%s'", route.path) 256 } 257 } else if recv != nil { 258 t.Errorf("unexpected panic for route '%s': %v", route.path, recv) 259 } 260 } 261 } 262 263 func TestTreeWildcardConflict(t *testing.T) { 264 routes := []testRoute{ 265 {"/cmd/:tool/:sub", false}, 266 {"/cmd/vet", false}, 267 {"/foo/bar", false}, 268 {"/foo/:name", false}, 269 {"/foo/:names", true}, 270 {"/cmd/*path", true}, 271 {"/cmd/:badvar", true}, 272 {"/cmd/:tool/names", false}, 273 {"/cmd/:tool/:badsub/details", true}, 274 {"/src/*filepath", false}, 275 {"/src/:file", true}, 276 {"/src/static.json", true}, 277 {"/src/*filepathx", true}, 278 {"/src/", true}, 279 {"/src/foo/bar", true}, 280 {"/src1/", false}, 281 {"/src1/*filepath", true}, 282 {"/src2*filepath", true}, 283 {"/src2/*filepath", false}, 284 {"/search/:query", false}, 285 {"/search/valid", false}, 286 {"/user_:name", false}, 287 {"/user_x", false}, 288 {"/user_:name", false}, 289 {"/id:id", false}, 290 {"/id/:id", false}, 291 } 292 testRoutes(t, routes) 293 } 294 295 func TestCatchAllAfterSlash(t *testing.T) { 296 routes := []testRoute{ 297 {"/non-leading-*catchall", true}, 298 } 299 testRoutes(t, routes) 300 } 301 302 func TestTreeChildConflict(t *testing.T) { 303 routes := []testRoute{ 304 {"/cmd/vet", false}, 305 {"/cmd/:tool", false}, 306 {"/cmd/:tool/:sub", false}, 307 {"/cmd/:tool/misc", false}, 308 {"/cmd/:tool/:othersub", true}, 309 {"/src/AUTHORS", false}, 310 {"/src/*filepath", true}, 311 {"/user_x", false}, 312 {"/user_:name", false}, 313 {"/id/:id", false}, 314 {"/id:id", false}, 315 {"/:id", false}, 316 {"/*filepath", true}, 317 } 318 testRoutes(t, routes) 319 } 320 321 func TestTreeDupliatePath(t *testing.T) { 322 tree := &node{} 323 324 routes := [...]string{ 325 "/", 326 "/doc/", 327 "/src/*filepath", 328 "/search/:query", 329 "/user_:name", 330 } 331 for _, route := range routes { 332 recv := catchPanic(func() { 333 tree.addRoute(route, fakeHandler(route)) 334 }) 335 if recv != nil { 336 t.Fatalf("panic inserting route '%s': %v", route, recv) 337 } 338 339 // Add again 340 recv = catchPanic(func() { 341 tree.addRoute(route, nil) 342 }) 343 if recv == nil { 344 t.Fatalf("no panic while inserting duplicate route '%s", route) 345 } 346 } 347 348 //printChildren(tree, "") 349 350 checkRequests(t, tree, testRequests{ 351 {"/", false, "/", nil}, 352 {"/doc/", false, "/doc/", nil}, 353 {"/src/some/file.png", false, "/src/*filepath", Params{Param{"filepath", "/some/file.png"}}}, 354 {"/search/someth!ng+in+ünìcodé", false, "/search/:query", Params{Param{"query", "someth!ng+in+ünìcodé"}}}, 355 {"/user_gopher", false, "/user_:name", Params{Param{"name", "gopher"}}}, 356 }) 357 } 358 359 func TestEmptyWildcardName(t *testing.T) { 360 tree := &node{} 361 362 routes := [...]string{ 363 "/user:", 364 "/user:/", 365 "/cmd/:/", 366 "/src/*", 367 } 368 for _, route := range routes { 369 recv := catchPanic(func() { 370 tree.addRoute(route, nil) 371 }) 372 if recv == nil { 373 t.Fatalf("no panic while inserting route with empty wildcard name '%s", route) 374 } 375 } 376 } 377 378 func TestTreeCatchAllConflict(t *testing.T) { 379 routes := []testRoute{ 380 {"/src/*filepath/x", true}, 381 {"/src2/", false}, 382 {"/src2/*filepath/x", true}, 383 {"/src3/*filepath", false}, 384 {"/src3/*filepath/x", true}, 385 } 386 testRoutes(t, routes) 387 } 388 389 func TestTreeCatchAllConflictRoot(t *testing.T) { 390 routes := []testRoute{ 391 {"/", false}, 392 {"/*filepath", true}, 393 } 394 testRoutes(t, routes) 395 } 396 397 func TestTreeCatchMaxParams(t *testing.T) { 398 tree := &node{} 399 var route = "/cmd/*filepath" 400 tree.addRoute(route, fakeHandler(route)) 401 } 402 403 func TestTreeDoubleWildcard(t *testing.T) { 404 const panicMsg = "only one wildcard per path segment is allowed" 405 406 routes := [...]string{ 407 "/:foo:bar", 408 "/:foo:bar/", 409 "/:foo*bar", 410 } 411 412 for _, route := range routes { 413 tree := &node{} 414 recv := catchPanic(func() { 415 tree.addRoute(route, nil) 416 }) 417 418 if rs, ok := recv.(string); !ok || !strings.HasPrefix(rs, panicMsg) { 419 t.Fatalf(`"Expected panic "%s" for route '%s', got "%v"`, panicMsg, route, recv) 420 } 421 } 422 } 423 424 /*func TestTreeDuplicateWildcard(t *testing.T) { 425 tree := &node{} 426 routes := [...]string{ 427 "/:id/:name/:id", 428 } 429 for _, route := range routes { 430 ... 431 } 432 }*/ 433 434 func TestTreeTrailingSlashRedirect(t *testing.T) { 435 tree := &node{} 436 437 routes := [...]string{ 438 "/hi", 439 "/b/", 440 "/search/:query", 441 "/cmd/:tool/", 442 "/src/*filepath", 443 "/x", 444 "/x/y", 445 "/y/", 446 "/y/z", 447 "/0/:id", 448 "/0/:id/1", 449 "/1/:id/", 450 "/1/:id/2", 451 "/aa", 452 "/a/", 453 "/admin", 454 "/admin/:category", 455 "/admin/:category/:page", 456 "/doc", 457 "/doc/go_faq.html", 458 "/doc/go1.html", 459 "/no/a", 460 "/no/b", 461 "/api/hello/:name", 462 } 463 for _, route := range routes { 464 recv := catchPanic(func() { 465 tree.addRoute(route, fakeHandler(route)) 466 }) 467 if recv != nil { 468 t.Fatalf("panic inserting route '%s': %v", route, recv) 469 } 470 } 471 472 tsrRoutes := [...]string{ 473 "/hi/", 474 "/b", 475 "/search/gopher/", 476 "/cmd/vet", 477 "/src", 478 "/x/", 479 "/y", 480 "/0/go/", 481 "/1/go", 482 "/a", 483 "/admin/", 484 "/admin/config/", 485 "/admin/config/permissions/", 486 "/doc/", 487 } 488 for _, route := range tsrRoutes { 489 value := tree.getValue(route, nil, false) 490 if value.handlers != nil { 491 t.Fatalf("non-nil handler for TSR route '%s", route) 492 } else if !value.tsr { 493 t.Errorf("expected TSR recommendation for route '%s'", route) 494 } 495 } 496 497 noTsrRoutes := [...]string{ 498 "/", 499 "/no", 500 "/no/", 501 "/_", 502 "/_/", 503 "/api/world/abc", 504 } 505 for _, route := range noTsrRoutes { 506 value := tree.getValue(route, nil, false) 507 if value.handlers != nil { 508 t.Fatalf("non-nil handler for No-TSR route '%s", route) 509 } else if value.tsr { 510 t.Errorf("expected no TSR recommendation for route '%s'", route) 511 } 512 } 513 } 514 515 func TestTreeRootTrailingSlashRedirect(t *testing.T) { 516 tree := &node{} 517 518 recv := catchPanic(func() { 519 tree.addRoute("/:test", fakeHandler("/:test")) 520 }) 521 if recv != nil { 522 t.Fatalf("panic inserting test route: %v", recv) 523 } 524 525 value := tree.getValue("/", nil, false) 526 if value.handlers != nil { 527 t.Fatalf("non-nil handler") 528 } else if value.tsr { 529 t.Errorf("expected no TSR recommendation") 530 } 531 } 532 533 func TestTreeFindCaseInsensitivePath(t *testing.T) { 534 tree := &node{} 535 536 longPath := "/l" + strings.Repeat("o", 128) + "ng" 537 lOngPath := "/l" + strings.Repeat("O", 128) + "ng/" 538 539 routes := [...]string{ 540 "/hi", 541 "/b/", 542 "/ABC/", 543 "/search/:query", 544 "/cmd/:tool/", 545 "/src/*filepath", 546 "/x", 547 "/x/y", 548 "/y/", 549 "/y/z", 550 "/0/:id", 551 "/0/:id/1", 552 "/1/:id/", 553 "/1/:id/2", 554 "/aa", 555 "/a/", 556 "/doc", 557 "/doc/go_faq.html", 558 "/doc/go1.html", 559 "/doc/go/away", 560 "/no/a", 561 "/no/b", 562 "/Π", 563 "/u/apfêl/", 564 "/u/äpfêl/", 565 "/u/öpfêl", 566 "/v/Äpfêl/", 567 "/v/Öpfêl", 568 "/w/♬", // 3 byte 569 "/w/♭/", // 3 byte, last byte differs 570 "/w/𠜎", // 4 byte 571 "/w/𠜏/", // 4 byte 572 longPath, 573 } 574 575 for _, route := range routes { 576 recv := catchPanic(func() { 577 tree.addRoute(route, fakeHandler(route)) 578 }) 579 if recv != nil { 580 t.Fatalf("panic inserting route '%s': %v", route, recv) 581 } 582 } 583 584 // Check out == in for all registered routes 585 // With fixTrailingSlash = true 586 for _, route := range routes { 587 out, found := tree.findCaseInsensitivePath(route, true) 588 if !found { 589 t.Errorf("Route '%s' not found!", route) 590 } else if string(out) != route { 591 t.Errorf("Wrong result for route '%s': %s", route, string(out)) 592 } 593 } 594 // With fixTrailingSlash = false 595 for _, route := range routes { 596 out, found := tree.findCaseInsensitivePath(route, false) 597 if !found { 598 t.Errorf("Route '%s' not found!", route) 599 } else if string(out) != route { 600 t.Errorf("Wrong result for route '%s': %s", route, string(out)) 601 } 602 } 603 604 tests := []struct { 605 in string 606 out string 607 found bool 608 slash bool 609 }{ 610 {"/HI", "/hi", true, false}, 611 {"/HI/", "/hi", true, true}, 612 {"/B", "/b/", true, true}, 613 {"/B/", "/b/", true, false}, 614 {"/abc", "/ABC/", true, true}, 615 {"/abc/", "/ABC/", true, false}, 616 {"/aBc", "/ABC/", true, true}, 617 {"/aBc/", "/ABC/", true, false}, 618 {"/abC", "/ABC/", true, true}, 619 {"/abC/", "/ABC/", true, false}, 620 {"/SEARCH/QUERY", "/search/QUERY", true, false}, 621 {"/SEARCH/QUERY/", "/search/QUERY", true, true}, 622 {"/CMD/TOOL/", "/cmd/TOOL/", true, false}, 623 {"/CMD/TOOL", "/cmd/TOOL/", true, true}, 624 {"/SRC/FILE/PATH", "/src/FILE/PATH", true, false}, 625 {"/x/Y", "/x/y", true, false}, 626 {"/x/Y/", "/x/y", true, true}, 627 {"/X/y", "/x/y", true, false}, 628 {"/X/y/", "/x/y", true, true}, 629 {"/X/Y", "/x/y", true, false}, 630 {"/X/Y/", "/x/y", true, true}, 631 {"/Y/", "/y/", true, false}, 632 {"/Y", "/y/", true, true}, 633 {"/Y/z", "/y/z", true, false}, 634 {"/Y/z/", "/y/z", true, true}, 635 {"/Y/Z", "/y/z", true, false}, 636 {"/Y/Z/", "/y/z", true, true}, 637 {"/y/Z", "/y/z", true, false}, 638 {"/y/Z/", "/y/z", true, true}, 639 {"/Aa", "/aa", true, false}, 640 {"/Aa/", "/aa", true, true}, 641 {"/AA", "/aa", true, false}, 642 {"/AA/", "/aa", true, true}, 643 {"/aA", "/aa", true, false}, 644 {"/aA/", "/aa", true, true}, 645 {"/A/", "/a/", true, false}, 646 {"/A", "/a/", true, true}, 647 {"/DOC", "/doc", true, false}, 648 {"/DOC/", "/doc", true, true}, 649 {"/NO", "", false, true}, 650 {"/DOC/GO", "", false, true}, 651 {"/π", "/Π", true, false}, 652 {"/π/", "/Π", true, true}, 653 {"/u/ÄPFÊL/", "/u/äpfêl/", true, false}, 654 {"/u/ÄPFÊL", "/u/äpfêl/", true, true}, 655 {"/u/ÖPFÊL/", "/u/öpfêl", true, true}, 656 {"/u/ÖPFÊL", "/u/öpfêl", true, false}, 657 {"/v/äpfêL/", "/v/Äpfêl/", true, false}, 658 {"/v/äpfêL", "/v/Äpfêl/", true, true}, 659 {"/v/öpfêL/", "/v/Öpfêl", true, true}, 660 {"/v/öpfêL", "/v/Öpfêl", true, false}, 661 {"/w/♬/", "/w/♬", true, true}, 662 {"/w/♭", "/w/♭/", true, true}, 663 {"/w/𠜎/", "/w/𠜎", true, true}, 664 {"/w/𠜏", "/w/𠜏/", true, true}, 665 {lOngPath, longPath, true, true}, 666 } 667 // With fixTrailingSlash = true 668 for _, test := range tests { 669 out, found := tree.findCaseInsensitivePath(test.in, true) 670 if found != test.found || (found && (string(out) != test.out)) { 671 t.Errorf("Wrong result for '%s': got %s, %t; want %s, %t", 672 test.in, string(out), found, test.out, test.found) 673 return 674 } 675 } 676 // With fixTrailingSlash = false 677 for _, test := range tests { 678 out, found := tree.findCaseInsensitivePath(test.in, false) 679 if test.slash { 680 if found { // test needs a trailingSlash fix. It must not be found! 681 t.Errorf("Found without fixTrailingSlash: %s; got %s", test.in, string(out)) 682 } 683 } else { 684 if found != test.found || (found && (string(out) != test.out)) { 685 t.Errorf("Wrong result for '%s': got %s, %t; want %s, %t", 686 test.in, string(out), found, test.out, test.found) 687 return 688 } 689 } 690 } 691 } 692 693 func TestTreeInvalidNodeType(t *testing.T) { 694 const panicMsg = "invalid node type" 695 696 tree := &node{} 697 tree.addRoute("/", fakeHandler("/")) 698 tree.addRoute("/:page", fakeHandler("/:page")) 699 700 // set invalid node type 701 tree.children[0].nType = 42 702 703 // normal lookup 704 recv := catchPanic(func() { 705 tree.getValue("/test", nil, false) 706 }) 707 if rs, ok := recv.(string); !ok || rs != panicMsg { 708 t.Fatalf("Expected panic '"+panicMsg+"', got '%v'", recv) 709 } 710 711 // case-insensitive lookup 712 recv = catchPanic(func() { 713 tree.findCaseInsensitivePath("/test", true) 714 }) 715 if rs, ok := recv.(string); !ok || rs != panicMsg { 716 t.Fatalf("Expected panic '"+panicMsg+"', got '%v'", recv) 717 } 718 } 719 720 func TestTreeWildcardConflictEx(t *testing.T) { 721 conflicts := [...]struct { 722 route string 723 segPath string 724 existPath string 725 existSegPath string 726 }{ 727 {"/who/are/foo", "/foo", `/who/are/\*you`, `/\*you`}, 728 {"/who/are/foo/", "/foo/", `/who/are/\*you`, `/\*you`}, 729 {"/who/are/foo/bar", "/foo/bar", `/who/are/\*you`, `/\*you`}, 730 {"/con:nection", ":nection", `/con:tact`, `:tact`}, 731 } 732 733 for _, conflict := range conflicts { 734 // I have to re-create a 'tree', because the 'tree' will be 735 // in an inconsistent state when the loop recovers from the 736 // panic which threw by 'addRoute' function. 737 tree := &node{} 738 routes := [...]string{ 739 "/con:tact", 740 "/who/are/*you", 741 "/who/foo/hello", 742 } 743 744 for _, route := range routes { 745 tree.addRoute(route, fakeHandler(route)) 746 } 747 748 recv := catchPanic(func() { 749 tree.addRoute(conflict.route, fakeHandler(conflict.route)) 750 }) 751 752 if !regexp.MustCompile(fmt.Sprintf("'%s' in new path .* conflicts with existing wildcard '%s' in existing prefix '%s'", conflict.segPath, conflict.existSegPath, conflict.existPath)).MatchString(fmt.Sprint(recv)) { 753 t.Fatalf("invalid wildcard conflict error (%v)", recv) 754 } 755 } 756 }