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: TestParameter
|
||||
PASS: TestWildcard
|
||||
PASS: TestMap
|
||||
PASS: TestMethods
|
||||
PASS: TestGitHub
|
||||
PASS: TestTrailingSlash
|
||||
PASS: TestOverwrite
|
||||
coverage: 82.5% of statements
|
||||
PASS: TestInvalidMethod
|
||||
coverage: 99.1% of statements
|
||||
```
|
||||
|
||||
## Benchmarks
|
||||
|
30
Router.go
30
Router.go
@ -1,7 +1,5 @@
|
||||
package router
|
||||
|
||||
import "os"
|
||||
|
||||
// Router is a high-performance router.
|
||||
type Router[T any] struct {
|
||||
get Tree[T]
|
||||
@ -46,23 +44,17 @@ func (router *Router[T]) LookupNoAlloc(method string, path string, addParameter
|
||||
return tree.LookupNoAlloc(path, addParameter)
|
||||
}
|
||||
|
||||
// Bind traverses all trees and calls the given function on every node.
|
||||
func (router *Router[T]) Bind(transform func(T) T) {
|
||||
router.get.Bind(transform)
|
||||
router.post.Bind(transform)
|
||||
router.delete.Bind(transform)
|
||||
router.put.Bind(transform)
|
||||
router.patch.Bind(transform)
|
||||
router.head.Bind(transform)
|
||||
router.connect.Bind(transform)
|
||||
router.trace.Bind(transform)
|
||||
router.options.Bind(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)
|
||||
// Map traverses all trees and calls the given function on every node.
|
||||
func (router *Router[T]) Map(transform func(T) T) {
|
||||
router.get.Map(transform)
|
||||
router.post.Map(transform)
|
||||
router.delete.Map(transform)
|
||||
router.put.Map(transform)
|
||||
router.patch.Map(transform)
|
||||
router.head.Map(transform)
|
||||
router.connect.Map(transform)
|
||||
router.trace.Map(transform)
|
||||
router.options.Map(transform)
|
||||
}
|
||||
|
||||
// selectTree returns the tree by the given HTTP method.
|
||||
|
@ -1,6 +1,7 @@
|
||||
package router_test
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"git.akyoto.dev/go/assert"
|
||||
@ -60,6 +61,7 @@ func TestWildcard(t *testing.T) {
|
||||
r := router.New[string]()
|
||||
r.Add("GET", "/", "Front page")
|
||||
r.Add("GET", "/:post", "Blog post")
|
||||
r.Add("GET", "/*any", "Wildcard")
|
||||
r.Add("GET", "/users/:id", "Parameter")
|
||||
r.Add("GET", "/images/static", "Static")
|
||||
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, data, "Parameter")
|
||||
|
||||
data, _ = r.Lookup("GET", "/users/42/test.txt")
|
||||
assert.Equal(t, data, "Wildcard")
|
||||
|
||||
data, params = r.Lookup("GET", "/images/static")
|
||||
assert.Equal(t, len(params), 0)
|
||||
assert.Equal(t, data, "Static")
|
||||
@ -103,6 +108,34 @@ func TestWildcard(t *testing.T) {
|
||||
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) {
|
||||
methods := []string{
|
||||
"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) {
|
||||
r := router.New[string]()
|
||||
r.Add("GET", "/", "1")
|
||||
@ -158,6 +205,17 @@ func TestOverwrite(t *testing.T) {
|
||||
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) {
|
||||
escape := func(a any) {}
|
||||
|
||||
|
12
Tree.go
12
Tree.go
@ -130,6 +130,8 @@ begin:
|
||||
return node.data
|
||||
}
|
||||
|
||||
// node: /|*any
|
||||
// path: /|image.png
|
||||
if lastWildcard != nil {
|
||||
addParameter(lastWildcard.prefix, path[lastWildcardOffset:])
|
||||
return lastWildcard.data
|
||||
@ -193,9 +195,9 @@ begin:
|
||||
|
||||
// node: /|*any
|
||||
// path: /|image.png
|
||||
if node.wildcard != nil {
|
||||
addParameter(node.wildcard.prefix, path[i:])
|
||||
return node.wildcard.data
|
||||
if lastWildcard != nil {
|
||||
addParameter(lastWildcard.prefix, path[lastWildcardOffset:])
|
||||
return lastWildcard.data
|
||||
}
|
||||
|
||||
return empty
|
||||
@ -217,8 +219,8 @@ begin:
|
||||
}
|
||||
}
|
||||
|
||||
// Bind binds all handlers to a new one provided by the callback.
|
||||
func (tree *Tree[T]) Bind(transform func(T) T) {
|
||||
// Map binds all handlers to a new one provided by the callback.
|
||||
func (tree *Tree[T]) Map(transform func(T) T) {
|
||||
tree.root.each(func(node *treeNode[T]) {
|
||||
node.data = transform(node.data)
|
||||
})
|
||||
|
46
treeNode.go
46
treeNode.go
@ -1,8 +1,6 @@
|
||||
package router
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"strings"
|
||||
)
|
||||
|
||||
@ -283,47 +281,3 @@ func (node *treeNode[T]) each(callback func(*treeNode[T])) {
|
||||
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