mirror of
https://github.com/gin-gonic/gin.git
synced 2026-06-07 04:38:19 +08:00
Merge 6d97ab9c023c6f67b1621b871a092e43eceeef3d into d75fcd4c9ab260e5225de590f1f0f8c0e0e12d11
This commit is contained in:
commit
6d805f5a54
@ -121,6 +121,14 @@ func Benchmark404Many(B *testing.B) {
|
|||||||
runRequest(B, router, http.MethodGet, "/viewfake")
|
runRequest(B, router, http.MethodGet, "/viewfake")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func BenchmarkStaticRouteWithParamFallback(B *testing.B) {
|
||||||
|
router := New()
|
||||||
|
router.GET("/users/:id", func(c *Context) {})
|
||||||
|
router.GET("/users/new", func(c *Context) {})
|
||||||
|
router.GET("/users/:id/profile", func(c *Context) {})
|
||||||
|
runRequest(B, router, http.MethodGet, "/users/new")
|
||||||
|
}
|
||||||
|
|
||||||
type mockWriter struct {
|
type mockWriter struct {
|
||||||
headers http.Header
|
headers http.Header
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1403,6 +1403,26 @@ func TestPlainBinding(t *testing.T) {
|
|||||||
require.NoError(t, p.Bind(req, ptr))
|
require.NoError(t, p.Bind(req, ptr))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestPlainBindingBindBody(t *testing.T) {
|
||||||
|
p := Plain
|
||||||
|
|
||||||
|
var s string
|
||||||
|
require.NoError(t, p.BindBody([]byte("test string"), &s))
|
||||||
|
assert.Equal(t, "test string", s)
|
||||||
|
|
||||||
|
var bs []byte
|
||||||
|
require.NoError(t, p.BindBody([]byte("test []byte"), &bs))
|
||||||
|
assert.Equal(t, []byte("test []byte"), bs)
|
||||||
|
|
||||||
|
var i int
|
||||||
|
require.Error(t, p.BindBody([]byte("test fail"), &i))
|
||||||
|
|
||||||
|
require.NoError(t, p.BindBody(nil, nil))
|
||||||
|
|
||||||
|
var ptr *string
|
||||||
|
require.NoError(t, p.BindBody(nil, ptr))
|
||||||
|
}
|
||||||
|
|
||||||
func testProtoBodyBindingFail(t *testing.T, b Binding, name, path, badPath, body, badBody string) {
|
func testProtoBodyBindingFail(t *testing.T, b Binding, name, path, badPath, body, badBody string) {
|
||||||
assert.Equal(t, name, b.Name())
|
assert.Equal(t, name, b.Name())
|
||||||
|
|
||||||
|
|||||||
79
codec/json/json_test.go
Normal file
79
codec/json/json_test.go
Normal file
@ -0,0 +1,79 @@
|
|||||||
|
// Copyright 2026 Gin Core Team. All rights reserved.
|
||||||
|
// Use of this source code is governed by a MIT style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package json
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/json"
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestAPI(t *testing.T) {
|
||||||
|
if API == nil {
|
||||||
|
t.Fatal("API is nil")
|
||||||
|
}
|
||||||
|
if Package == "" {
|
||||||
|
t.Fatal("Package is empty")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestAPIMarshalAndUnmarshal(t *testing.T) {
|
||||||
|
type payload struct {
|
||||||
|
Name string `json:"name"`
|
||||||
|
}
|
||||||
|
|
||||||
|
data, err := API.Marshal(payload{Name: "gin"})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if string(data) != `{"name":"gin"}` {
|
||||||
|
t.Fatalf("unexpected marshal output: %s", data)
|
||||||
|
}
|
||||||
|
|
||||||
|
var decoded payload
|
||||||
|
if err := API.Unmarshal(data, &decoded); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if decoded.Name != "gin" {
|
||||||
|
t.Fatalf("unexpected decoded payload: %#v", decoded)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestAPIMarshalIndent(t *testing.T) {
|
||||||
|
data, err := API.MarshalIndent(map[string]string{"name": "gin"}, "", " ")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if !bytes.Contains(data, []byte("\n ")) {
|
||||||
|
t.Fatalf("expected indented JSON, got %q", data)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestAPIEncoder(t *testing.T) {
|
||||||
|
var buf bytes.Buffer
|
||||||
|
encoder := API.NewEncoder(&buf)
|
||||||
|
encoder.SetEscapeHTML(false)
|
||||||
|
|
||||||
|
if err := encoder.Encode("<gin>"); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if got := buf.String(); got != "\"<gin>\"\n" {
|
||||||
|
t.Fatalf("unexpected encoded JSON: %q", got)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestAPIDecoder(t *testing.T) {
|
||||||
|
decoder := API.NewDecoder(strings.NewReader(`{"known": 1, "extra": 2}`))
|
||||||
|
decoder.UseNumber()
|
||||||
|
decoder.DisallowUnknownFields()
|
||||||
|
|
||||||
|
var dst struct {
|
||||||
|
Known json.Number `json:"known"`
|
||||||
|
}
|
||||||
|
if err := decoder.Decode(&dst); err == nil {
|
||||||
|
t.Fatal("expected unknown field error")
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -8,10 +8,13 @@ import (
|
|||||||
"html/template"
|
"html/template"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/http/httptest"
|
"net/http/httptest"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
@ -214,6 +217,24 @@ func TestSetHTMLTemplate(t *testing.T) {
|
|||||||
assert.NotNil(t, engine())
|
assert.NotNil(t, engine())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestLoadHTMLGlob(t *testing.T) {
|
||||||
|
LoadHTMLGlob("../testdata/template/*")
|
||||||
|
|
||||||
|
assert.NotNil(t, engine())
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestLoadHTMLFiles(t *testing.T) {
|
||||||
|
LoadHTMLFiles("../testdata/template/hello.tmpl", "../testdata/template/raw.tmpl")
|
||||||
|
|
||||||
|
assert.NotNil(t, engine())
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestLoadHTMLFS(t *testing.T) {
|
||||||
|
LoadHTMLFS(http.Dir("../testdata"), "template/hello.tmpl", "template/raw.tmpl")
|
||||||
|
|
||||||
|
assert.NotNil(t, engine())
|
||||||
|
}
|
||||||
|
|
||||||
func TestStaticFile(t *testing.T) {
|
func TestStaticFile(t *testing.T) {
|
||||||
StaticFile("/static-file", "../testdata/test_file.txt")
|
StaticFile("/static-file", "../testdata/test_file.txt")
|
||||||
|
|
||||||
@ -224,6 +245,33 @@ func TestStaticFile(t *testing.T) {
|
|||||||
assert.Equal(t, http.StatusOK, w.Code)
|
assert.Equal(t, http.StatusOK, w.Code)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestRunReturnsListenError(t *testing.T) {
|
||||||
|
err := Run("127.0.0.1:bad-port")
|
||||||
|
|
||||||
|
require.Error(t, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestRunTLSReturnsListenError(t *testing.T) {
|
||||||
|
err := RunTLS("127.0.0.1:bad-port", "../testdata/certificate/cert.pem", "../testdata/certificate/key.pem")
|
||||||
|
|
||||||
|
require.Error(t, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestRunUnixReturnsListenError(t *testing.T) {
|
||||||
|
err := RunUnix(filepath.Join(t.TempDir(), "missing", "gin.sock"))
|
||||||
|
|
||||||
|
require.Error(t, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestRunFdReturnsListenerError(t *testing.T) {
|
||||||
|
file, err := os.CreateTemp(t.TempDir(), "gin-fd")
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
err = RunFd(int(file.Fd()))
|
||||||
|
|
||||||
|
require.Error(t, err)
|
||||||
|
}
|
||||||
|
|
||||||
func TestStatic(t *testing.T) {
|
func TestStatic(t *testing.T) {
|
||||||
Static("/static-dir", "../testdata")
|
Static("/static-dir", "../testdata")
|
||||||
|
|
||||||
|
|||||||
20
tree.go
20
tree.go
@ -417,16 +417,19 @@ type skippedNode struct {
|
|||||||
// given path.
|
// given path.
|
||||||
func (n *node) getValue(path string, params *Params, skippedNodes *[]skippedNode, unescape bool) (value nodeValue) {
|
func (n *node) getValue(path string, params *Params, skippedNodes *[]skippedNode, unescape bool) (value nodeValue) {
|
||||||
var globalParamsCount int16
|
var globalParamsCount int16
|
||||||
|
var skipStatic bool
|
||||||
|
|
||||||
walk: // Outer loop for walking the tree
|
walk: // Outer loop for walking the tree
|
||||||
for {
|
for {
|
||||||
prefix := n.path
|
prefix := n.path
|
||||||
if len(path) > len(prefix) {
|
if len(path) > len(prefix) {
|
||||||
if path[:len(prefix)] == prefix {
|
if path[:len(prefix)] == prefix {
|
||||||
|
fullPath := path
|
||||||
path = path[len(prefix):]
|
path = path[len(prefix):]
|
||||||
|
|
||||||
// Try all the non-wildcard children first by matching the indices
|
// Try all the non-wildcard children first by matching the indices
|
||||||
idxc := path[0]
|
idxc := path[0]
|
||||||
|
if !skipStatic {
|
||||||
for i, c := range []byte(n.indices) {
|
for i, c := range []byte(n.indices) {
|
||||||
if c == idxc {
|
if c == idxc {
|
||||||
// strings.HasPrefix(n.children[len(n.children)-1].path, ":") == n.wildChild
|
// strings.HasPrefix(n.children[len(n.children)-1].path, ":") == n.wildChild
|
||||||
@ -434,16 +437,8 @@ walk: // Outer loop for walking the tree
|
|||||||
index := len(*skippedNodes)
|
index := len(*skippedNodes)
|
||||||
*skippedNodes = (*skippedNodes)[:index+1]
|
*skippedNodes = (*skippedNodes)[:index+1]
|
||||||
(*skippedNodes)[index] = skippedNode{
|
(*skippedNodes)[index] = skippedNode{
|
||||||
path: prefix + path,
|
path: fullPath,
|
||||||
node: &node{
|
node: n,
|
||||||
path: n.path,
|
|
||||||
wildChild: n.wildChild,
|
|
||||||
nType: n.nType,
|
|
||||||
priority: n.priority,
|
|
||||||
children: n.children,
|
|
||||||
handlers: n.handlers,
|
|
||||||
fullPath: n.fullPath,
|
|
||||||
},
|
|
||||||
paramsCount: globalParamsCount,
|
paramsCount: globalParamsCount,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -452,6 +447,8 @@ walk: // Outer loop for walking the tree
|
|||||||
continue walk
|
continue walk
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
skipStatic = false
|
||||||
|
|
||||||
if !n.wildChild {
|
if !n.wildChild {
|
||||||
// If the path at the end of the loop is not equal to '/' and the current node has no child nodes
|
// If the path at the end of the loop is not equal to '/' and the current node has no child nodes
|
||||||
@ -467,6 +464,7 @@ walk: // Outer loop for walking the tree
|
|||||||
*value.params = (*value.params)[:skippedNode.paramsCount]
|
*value.params = (*value.params)[:skippedNode.paramsCount]
|
||||||
}
|
}
|
||||||
globalParamsCount = skippedNode.paramsCount
|
globalParamsCount = skippedNode.paramsCount
|
||||||
|
skipStatic = true
|
||||||
continue walk
|
continue walk
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -598,6 +596,7 @@ walk: // Outer loop for walking the tree
|
|||||||
*value.params = (*value.params)[:skippedNode.paramsCount]
|
*value.params = (*value.params)[:skippedNode.paramsCount]
|
||||||
}
|
}
|
||||||
globalParamsCount = skippedNode.paramsCount
|
globalParamsCount = skippedNode.paramsCount
|
||||||
|
skipStatic = true
|
||||||
continue walk
|
continue walk
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -655,6 +654,7 @@ walk: // Outer loop for walking the tree
|
|||||||
*value.params = (*value.params)[:skippedNode.paramsCount]
|
*value.params = (*value.params)[:skippedNode.paramsCount]
|
||||||
}
|
}
|
||||||
globalParamsCount = skippedNode.paramsCount
|
globalParamsCount = skippedNode.paramsCount
|
||||||
|
skipStatic = true
|
||||||
continue walk
|
continue walk
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
34
tree_test.go
34
tree_test.go
@ -939,6 +939,40 @@ func TestTreeExpandParamsCapacity(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestTreeFindCaseInsensitivePathWithWildcardChildAndBufferedRune(t *testing.T) {
|
||||||
|
tree := &node{
|
||||||
|
path: "/",
|
||||||
|
indices: "x",
|
||||||
|
wildChild: true,
|
||||||
|
children: []*node{
|
||||||
|
{path: "x", handlers: fakeHandler("/x"), fullPath: "/x"},
|
||||||
|
{path: ":id", nType: param, handlers: fakeHandler("/:id"), fullPath: "/:id"},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
out := tree.findCaseInsensitivePathRec("/x", nil, [4]byte{0, 'x'}, false)
|
||||||
|
if string(out) != "/x" {
|
||||||
|
t.Fatalf("Wrong result: got %s, want /x", string(out))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestTreeFindCaseInsensitivePathWithWildcardChildAndUppercaseStatic(t *testing.T) {
|
||||||
|
tree := &node{
|
||||||
|
path: "/",
|
||||||
|
indices: "A",
|
||||||
|
wildChild: true,
|
||||||
|
children: []*node{
|
||||||
|
{path: "A", handlers: fakeHandler("/A"), fullPath: "/A"},
|
||||||
|
{path: ":id", nType: param, handlers: fakeHandler("/:id"), fullPath: "/:id"},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
out := tree.findCaseInsensitivePathRec("/a", nil, [4]byte{}, false)
|
||||||
|
if string(out) != "/A" {
|
||||||
|
t.Fatalf("Wrong result: got %s, want /A", string(out))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestTreeWildcardConflictEx(t *testing.T) {
|
func TestTreeWildcardConflictEx(t *testing.T) {
|
||||||
conflicts := [...]struct {
|
conflicts := [...]struct {
|
||||||
route string
|
route string
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user