Improved test coverage
This commit is contained in:
parent
bd91077f83
commit
3008940025
@ -46,10 +46,13 @@ data := router.LookupNoAlloc("GET", "/users/42", func(key string, value string)
|
|||||||
PASS: TestStatic
|
PASS: TestStatic
|
||||||
PASS: TestParameter
|
PASS: TestParameter
|
||||||
PASS: TestWildcard
|
PASS: TestWildcard
|
||||||
|
PASS: TestMap
|
||||||
PASS: TestMethods
|
PASS: TestMethods
|
||||||
PASS: TestGitHub
|
PASS: TestGitHub
|
||||||
|
PASS: TestTrailingSlash
|
||||||
PASS: TestOverwrite
|
PASS: TestOverwrite
|
||||||
coverage: 82.5% of statements
|
PASS: TestInvalidMethod
|
||||||
|
coverage: 99.1% of statements
|
||||||
```
|
```
|
||||||
|
|
||||||
## Benchmarks
|
## Benchmarks
|
||||||
|
30
Router.go
30
Router.go
@ -1,7 +1,5 @@
|
|||||||
package router
|
package router
|
||||||
|
|
||||||
import "os"
|
|
||||||
|
|
||||||
// Router is a high-performance router.
|
// Router is a high-performance router.
|
||||||
type Router[T any] struct {
|
type Router[T any] struct {
|
||||||
get Tree[T]
|
get Tree[T]
|
||||||
@ -46,23 +44,17 @@ func (router *Router[T]) LookupNoAlloc(method string, path string, addParameter
|
|||||||
return tree.LookupNoAlloc(path, addParameter)
|
return tree.LookupNoAlloc(path, addParameter)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Bind traverses all trees and calls the given function on every node.
|
// Map traverses all trees and calls the given function on every node.
|
||||||
func (router *Router[T]) Bind(transform func(T) T) {
|
func (router *Router[T]) Map(transform func(T) T) {
|
||||||
router.get.Bind(transform)
|
router.get.Map(transform)
|
||||||
router.post.Bind(transform)
|
router.post.Map(transform)
|
||||||
router.delete.Bind(transform)
|
router.delete.Map(transform)
|
||||||
router.put.Bind(transform)
|
router.put.Map(transform)
|
||||||
router.patch.Bind(transform)
|
router.patch.Map(transform)
|
||||||
router.head.Bind(transform)
|
router.head.Map(transform)
|
||||||
router.connect.Bind(transform)
|
router.connect.Map(transform)
|
||||||
router.trace.Bind(transform)
|
router.trace.Map(transform)
|
||||||
router.options.Bind(transform)
|
router.options.Map(transform)
|
||||||
}
|
|
||||||
|
|
||||||
// Print shows a pretty print of all the routes.
|
|
||||||
func (router *Router[T]) Print(method string) {
|
|
||||||
tree := router.selectTree(method)
|
|
||||||
tree.root.PrettyPrint(os.Stdout)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// selectTree returns the tree by the given HTTP method.
|
// selectTree returns the tree by the given HTTP method.
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
package router_test
|
package router_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"git.akyoto.dev/go/assert"
|
"git.akyoto.dev/go/assert"
|
||||||
@ -60,6 +61,7 @@ func TestWildcard(t *testing.T) {
|
|||||||
r := router.New[string]()
|
r := router.New[string]()
|
||||||
r.Add("GET", "/", "Front page")
|
r.Add("GET", "/", "Front page")
|
||||||
r.Add("GET", "/:post", "Blog post")
|
r.Add("GET", "/:post", "Blog post")
|
||||||
|
r.Add("GET", "/*any", "Wildcard")
|
||||||
r.Add("GET", "/users/:id", "Parameter")
|
r.Add("GET", "/users/:id", "Parameter")
|
||||||
r.Add("GET", "/images/static", "Static")
|
r.Add("GET", "/images/static", "Static")
|
||||||
r.Add("GET", "/images/*path", "Wildcard")
|
r.Add("GET", "/images/*path", "Wildcard")
|
||||||
@ -80,6 +82,9 @@ func TestWildcard(t *testing.T) {
|
|||||||
assert.Equal(t, params[0].Value, "42")
|
assert.Equal(t, params[0].Value, "42")
|
||||||
assert.Equal(t, data, "Parameter")
|
assert.Equal(t, data, "Parameter")
|
||||||
|
|
||||||
|
data, _ = r.Lookup("GET", "/users/42/test.txt")
|
||||||
|
assert.Equal(t, data, "Wildcard")
|
||||||
|
|
||||||
data, params = r.Lookup("GET", "/images/static")
|
data, params = r.Lookup("GET", "/images/static")
|
||||||
assert.Equal(t, len(params), 0)
|
assert.Equal(t, len(params), 0)
|
||||||
assert.Equal(t, data, "Static")
|
assert.Equal(t, data, "Static")
|
||||||
@ -103,6 +108,34 @@ func TestWildcard(t *testing.T) {
|
|||||||
assert.Equal(t, data, "Wildcard")
|
assert.Equal(t, data, "Wildcard")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestMap(t *testing.T) {
|
||||||
|
r := router.New[string]()
|
||||||
|
r.Add("GET", "/hello", "Hello")
|
||||||
|
r.Add("GET", "/world", "World")
|
||||||
|
r.Add("GET", "/user/:user", "User")
|
||||||
|
r.Add("GET", "/*path", "Path")
|
||||||
|
|
||||||
|
r.Map(func(data string) string {
|
||||||
|
return strings.Repeat(data, 2)
|
||||||
|
})
|
||||||
|
|
||||||
|
data, params := r.Lookup("GET", "/hello")
|
||||||
|
assert.Equal(t, len(params), 0)
|
||||||
|
assert.Equal(t, data, "HelloHello")
|
||||||
|
|
||||||
|
data, params = r.Lookup("GET", "/world")
|
||||||
|
assert.Equal(t, len(params), 0)
|
||||||
|
assert.Equal(t, data, "WorldWorld")
|
||||||
|
|
||||||
|
data, params = r.Lookup("GET", "/user/123")
|
||||||
|
assert.Equal(t, len(params), 1)
|
||||||
|
assert.Equal(t, data, "UserUser")
|
||||||
|
|
||||||
|
data, params = r.Lookup("GET", "/test.txt")
|
||||||
|
assert.Equal(t, len(params), 1)
|
||||||
|
assert.Equal(t, data, "PathPath")
|
||||||
|
}
|
||||||
|
|
||||||
func TestMethods(t *testing.T) {
|
func TestMethods(t *testing.T) {
|
||||||
methods := []string{
|
methods := []string{
|
||||||
"GET",
|
"GET",
|
||||||
@ -145,6 +178,20 @@ func TestGitHub(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestTrailingSlash(t *testing.T) {
|
||||||
|
r := router.New[string]()
|
||||||
|
r.Add("GET", "/hello", "Hello 1")
|
||||||
|
r.Add("GET", "/hello/", "Hello 2")
|
||||||
|
|
||||||
|
data, params := r.Lookup("GET", "/hello")
|
||||||
|
assert.Equal(t, len(params), 0)
|
||||||
|
assert.Equal(t, data, "Hello 1")
|
||||||
|
|
||||||
|
data, params = r.Lookup("GET", "/hello/")
|
||||||
|
assert.Equal(t, len(params), 0)
|
||||||
|
assert.Equal(t, data, "Hello 2")
|
||||||
|
}
|
||||||
|
|
||||||
func TestOverwrite(t *testing.T) {
|
func TestOverwrite(t *testing.T) {
|
||||||
r := router.New[string]()
|
r := router.New[string]()
|
||||||
r.Add("GET", "/", "1")
|
r.Add("GET", "/", "1")
|
||||||
@ -158,6 +205,17 @@ func TestOverwrite(t *testing.T) {
|
|||||||
assert.Equal(t, data, "5")
|
assert.Equal(t, data, "5")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestInvalidMethod(t *testing.T) {
|
||||||
|
defer func() {
|
||||||
|
if recover() == nil {
|
||||||
|
t.FailNow()
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
r := router.New[string]()
|
||||||
|
r.Add("?", "/hello", "Hello")
|
||||||
|
}
|
||||||
|
|
||||||
func TestMemoryUsage(t *testing.T) {
|
func TestMemoryUsage(t *testing.T) {
|
||||||
escape := func(a any) {}
|
escape := func(a any) {}
|
||||||
|
|
||||||
|
12
Tree.go
12
Tree.go
@ -130,6 +130,8 @@ begin:
|
|||||||
return node.data
|
return node.data
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// node: /|*any
|
||||||
|
// path: /|image.png
|
||||||
if lastWildcard != nil {
|
if lastWildcard != nil {
|
||||||
addParameter(lastWildcard.prefix, path[lastWildcardOffset:])
|
addParameter(lastWildcard.prefix, path[lastWildcardOffset:])
|
||||||
return lastWildcard.data
|
return lastWildcard.data
|
||||||
@ -193,9 +195,9 @@ begin:
|
|||||||
|
|
||||||
// node: /|*any
|
// node: /|*any
|
||||||
// path: /|image.png
|
// path: /|image.png
|
||||||
if node.wildcard != nil {
|
if lastWildcard != nil {
|
||||||
addParameter(node.wildcard.prefix, path[i:])
|
addParameter(lastWildcard.prefix, path[lastWildcardOffset:])
|
||||||
return node.wildcard.data
|
return lastWildcard.data
|
||||||
}
|
}
|
||||||
|
|
||||||
return empty
|
return empty
|
||||||
@ -217,8 +219,8 @@ begin:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Bind binds all handlers to a new one provided by the callback.
|
// Map binds all handlers to a new one provided by the callback.
|
||||||
func (tree *Tree[T]) Bind(transform func(T) T) {
|
func (tree *Tree[T]) Map(transform func(T) T) {
|
||||||
tree.root.each(func(node *treeNode[T]) {
|
tree.root.each(func(node *treeNode[T]) {
|
||||||
node.data = transform(node.data)
|
node.data = transform(node.data)
|
||||||
})
|
})
|
||||||
|
46
treeNode.go
46
treeNode.go
@ -1,8 +1,6 @@
|
|||||||
package router
|
package router
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
|
||||||
"io"
|
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -283,47 +281,3 @@ func (node *treeNode[T]) each(callback func(*treeNode[T])) {
|
|||||||
node.wildcard.each(callback)
|
node.wildcard.each(callback)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// PrettyPrint prints a human-readable form of the tree to the given writer.
|
|
||||||
func (node *treeNode[T]) PrettyPrint(writer io.Writer) {
|
|
||||||
node.prettyPrint(writer, -1)
|
|
||||||
}
|
|
||||||
|
|
||||||
// prettyPrint is the underlying pretty printer.
|
|
||||||
func (node *treeNode[T]) prettyPrint(writer io.Writer, level int) {
|
|
||||||
prefix := ""
|
|
||||||
|
|
||||||
if level >= 0 {
|
|
||||||
prefix = strings.Repeat(" ", level) + "|_ "
|
|
||||||
}
|
|
||||||
|
|
||||||
switch node.kind {
|
|
||||||
case ':':
|
|
||||||
prefix += ":"
|
|
||||||
case '*':
|
|
||||||
prefix += "*"
|
|
||||||
}
|
|
||||||
|
|
||||||
fmt.Fprintf(writer, "%s%s [%v]\n", prefix, node.prefix, node.data)
|
|
||||||
|
|
||||||
for _, child := range node.children {
|
|
||||||
if child == nil {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
child.prettyPrint(writer, level+1)
|
|
||||||
}
|
|
||||||
|
|
||||||
if node.parameter != nil {
|
|
||||||
node.parameter.prettyPrint(writer, level+1)
|
|
||||||
}
|
|
||||||
|
|
||||||
if node.wildcard != nil {
|
|
||||||
node.wildcard.prettyPrint(writer, level+1)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// String returns the node prefix.
|
|
||||||
func (node *treeNode[T]) String() string {
|
|
||||||
return node.prefix
|
|
||||||
}
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user