From 600943fdf3198a3cf7e52bc4f98e62fbc3eab643 Mon Sep 17 00:00:00 2001 From: Kashiwa <13825170+ksw2000@users.noreply.github.com> Date: Sat, 24 May 2025 07:15:57 +0000 Subject: [PATCH 1/6] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20refact:=20make=20gener?= =?UTF-8?q?icParseType=20return=20error?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ctx.go | 26 +- ctx_test.go | 1451 +++++++++++++++++---------------------------------- helpers.go | 140 ++--- 3 files changed, 578 insertions(+), 1039 deletions(-) diff --git a/ctx.go b/ctx.go index 5849501c260..a7b47c925fd 100644 --- a/ctx.go +++ b/ctx.go @@ -644,9 +644,14 @@ func (c *DefaultCtx) Get(key string, defaultValue ...string) string { // GetReqHeader returns the HTTP request header specified by filed. // This function is generic and can handle different headers type values. +// If the generic type cannot be matched to a supported type, the function +// returns the default value (if provided) or the zero value of type V. func GetReqHeader[V GenericType](c Ctx, key string, defaultValue ...V) V { - var v V - return genericParseType[V](c.App().getString(c.Request().Header.Peek(key)), v, defaultValue...) + v, err := genericParseType[V](c.App().getString(c.Request().Header.Peek(key))) + if err != nil && len(defaultValue) > 0 { + return defaultValue[0] + } + return v } // GetRespHeader returns the HTTP response header specified by field. @@ -1103,6 +1108,8 @@ func (c *DefaultCtx) Params(key string, defaultValue ...string) string { // Params is used to get the route parameters. // This function is generic and can handle different route parameters type values. +// If the generic type cannot be matched to a supported type, the function +// returns the default value (if provided) or the zero value of type V. // // Example: // @@ -1115,8 +1122,11 @@ func (c *DefaultCtx) Params(key string, defaultValue ...string) string { // http://example.com/id/:number -> http://example.com/id/john // Params[int](c, "number", 0) -> returns 0 because can't parse 'john' as integer. func Params[V GenericType](c Ctx, key string, defaultValue ...V) V { - var v V - return genericParseType(c.Params(key), v, defaultValue...) + v, err := genericParseType[V](c.Params(key)) + if err != nil && len(defaultValue) > 0 { + return defaultValue[0] + } + return v } // Path returns the path part of the request URL. @@ -1238,10 +1248,12 @@ func (c *DefaultCtx) Queries() map[string]string { // age := Query[int](c, "age") // Returns 8 // unknown := Query[string](c, "unknown", "default") // Returns "default" since the query parameter "unknown" is not found func Query[V GenericType](c Ctx, key string, defaultValue ...V) V { - var v V q := c.App().getString(c.RequestCtx().QueryArgs().Peek(key)) - - return genericParseType[V](q, v, defaultValue...) + v, err := genericParseType[V](q) + if err != nil && len(defaultValue) > 0 { + return defaultValue[0] + } + return v } // Range returns a struct containing the type and a slice of ranges. diff --git a/ctx_test.go b/ctx_test.go index e314f81d32b..16ee10f1413 100644 --- a/ctx_test.go +++ b/ctx_test.go @@ -28,6 +28,7 @@ import ( "testing" "text/template" "time" + "unsafe" "github.com/gofiber/fiber/v3/internal/storage/memory" "github.com/gofiber/utils/v2" @@ -5253,574 +5254,614 @@ func Benchmark_Ctx_GetReqHeaders(b *testing.B) { // go test -run Test_GenericParseTypeInts func Test_GenericParseTypeInts(t *testing.T) { t.Parallel() - type genericTypes[v GenericType] struct { - value v - str string - } - - ints := []genericTypes[int]{ + ints := []struct { + value int64 + bits int + }{ { value: 0, - str: "0", + bits: 8, }, { value: 1, - str: "1", + bits: 8, }, { value: 2, - str: "2", + bits: 8, }, { value: 3, - str: "3", + bits: 8, }, { value: 4, - str: "4", - }, - { - value: 2147483647, - str: "2147483647", + bits: 8, }, { - value: -2147483648, - str: "-2147483648", + value: -1, + bits: 8, }, { - value: -1, - str: "-1", + value: math.MaxInt8, + bits: 8, }, - } - - for _, test := range ints { - var v int - tt := test - t.Run("test_genericParseTypeInts", func(t *testing.T) { - t.Parallel() - require.Equal(t, tt.value, genericParseType(tt.str, v)) - require.Equal(t, tt.value, genericParseType[int](tt.str, v)) - }) - } -} - -// go test -run Test_GenericParseTypeInt8s -func Test_GenericParseTypeInt8s(t *testing.T) { - t.Parallel() - - type genericTypes[v GenericType] struct { - value v - str string - } - - int8s := []genericTypes[int8]{ { - value: int8(0), - str: "0", + value: math.MinInt8, + bits: 8, }, { - value: int8(1), - str: "1", + value: math.MaxInt16, + bits: 16, }, { - value: int8(2), - str: "2", + value: math.MinInt16, + bits: 16, }, { - value: int8(3), - str: "3", + value: math.MaxInt32, + bits: 32, }, { - value: int8(4), - str: "4", + value: math.MinInt32, + bits: 32, }, { - value: int8(math.MaxInt8), - str: strconv.Itoa(math.MaxInt8), + value: math.MaxInt64, + bits: 64, }, { - value: int8(math.MinInt8), - str: strconv.Itoa(math.MinInt8), + value: math.MinInt64, + bits: 64, }, } - for _, test := range int8s { - var v int8 - tt := test - t.Run("test_genericParseTypeInt8s", func(t *testing.T) { - t.Parallel() - require.Equal(t, tt.value, genericParseType(tt.str, v)) - require.Equal(t, tt.value, genericParseType[int8](tt.str, v)) - }) - } + t.Run("test_genericParseTypeInt8s", func(t *testing.T) { + t.Parallel() + for _, test := range ints { + v, err := genericParseType[int8](strconv.FormatInt(test.value, 10)) + if test.bits <= 8 { + require.NoError(t, err) + require.Equal(t, int8(test.value), v) //nolint:gosec // casting here is not a concern + } else { + require.ErrorIs(t, err, strconv.ErrRange) + } + } + }) + t.Run("test_genericParseTypeInt16s", func(t *testing.T) { + t.Parallel() + for _, test := range ints { + v, err := genericParseType[int16](strconv.FormatInt(test.value, 10)) + if test.bits <= 16 { + require.NoError(t, err) + require.Equal(t, int16(test.value), v) //nolint:gosec // casting here is not a concern + } else { + require.ErrorIs(t, err, strconv.ErrRange) + } + } + }) + t.Run("test_genericParseTypeInt32s", func(t *testing.T) { + t.Parallel() + for _, test := range ints { + v, err := genericParseType[int32](strconv.FormatInt(test.value, 10)) + if test.bits <= 32 { + require.NoError(t, err) + require.Equal(t, int32(test.value), v) //nolint:gosec // casting here is not a concern + } else { + require.ErrorIs(t, err, strconv.ErrRange) + } + } + }) + t.Run("test_genericParseTypeInt64s", func(t *testing.T) { + t.Parallel() + for _, test := range ints { + v, err := genericParseType[int64](strconv.FormatInt(test.value, 10)) + if test.bits <= 64 { + require.NoError(t, err) + require.Equal(t, test.value, v) + } else { + require.ErrorIs(t, err, strconv.ErrRange) + } + } + }) + + t.Run("test_genericParseTypeInts", func(t *testing.T) { + t.Parallel() + for _, test := range ints { + v, err := genericParseType[int](strconv.FormatInt(test.value, 10)) + if test.bits <= int(unsafe.Sizeof(int(0)))*8 { + require.NoError(t, err) + require.Equal(t, int(test.value), v) + } else { + require.ErrorIs(t, err, strconv.ErrRange) + } + } + }) } -// go test -run Test_GenericParseTypeInt16s -func Test_GenericParseTypeInt16s(t *testing.T) { +// go test -run Test_GenericParseTypeUints +func Test_GenericParseTypeUints(t *testing.T) { t.Parallel() - type genericTypes[v GenericType] struct { - value v - str string - } - - int16s := []genericTypes[int16]{ - { - value: int16(0), - str: "0", - }, - { - value: int16(1), - str: "1", - }, - { - value: int16(2), - str: "2", - }, - { - value: int16(3), - str: "3", - }, + uints := []struct { + value uint64 + bits int + }{ { - value: int16(4), - str: "4", + value: 0, + bits: 8, }, { - value: int16(math.MaxInt16), - str: strconv.Itoa(math.MaxInt16), + value: 1, + bits: 8, }, { - value: int16(math.MinInt16), - str: strconv.Itoa(math.MinInt16), + value: 2, + bits: 8, }, - } - - for _, test := range int16s { - var v int16 - tt := test - t.Run("test_genericParseTypeInt16s", func(t *testing.T) { - t.Parallel() - require.Equal(t, tt.value, genericParseType(tt.str, v)) - require.Equal(t, tt.value, genericParseType[int16](tt.str, v)) - }) - } -} - -// go test -run Test_GenericParseTypeInt32s -func Test_GenericParseTypeInt32s(t *testing.T) { - t.Parallel() - type genericTypes[v GenericType] struct { - value v - str string - } - - int32s := []genericTypes[int32]{ { - value: int32(0), - str: "0", + value: 3, + bits: 8, }, { - value: int32(1), - str: "1", + value: 4, + bits: 8, }, { - value: int32(2), - str: "2", + value: math.MaxUint8, + bits: 8, }, { - value: int32(3), - str: "3", + value: math.MaxUint16, + bits: 16, }, { - value: int32(4), - str: "4", + value: math.MaxUint16, + bits: 16, }, { - value: int32(math.MaxInt32), - str: strconv.Itoa(math.MaxInt32), + value: math.MaxUint32, + bits: 32, }, { - value: int32(math.MinInt32), - str: strconv.Itoa(math.MinInt32), + value: math.MaxUint64, + bits: 64, }, } - for _, test := range int32s { - var v int32 - tt := test - t.Run("test_genericParseTypeInt32s", func(t *testing.T) { - t.Parallel() - require.Equal(t, tt.value, genericParseType(tt.str, v)) - require.Equal(t, tt.value, genericParseType[int32](tt.str, v)) - }) - } + t.Run("test_genericParseTypeUint8s", func(t *testing.T) { + t.Parallel() + for _, test := range uints { + v, err := genericParseType[uint8](strconv.FormatUint(test.value, 10)) + if test.bits <= 8 { + require.NoError(t, err) + require.Equal(t, uint8(test.value), v) //nolint:gosec // casting here is not a concern + } else { + require.ErrorIs(t, err, strconv.ErrRange) + } + } + }) + t.Run("test_genericParseTypeUint16s", func(t *testing.T) { + t.Parallel() + for _, test := range uints { + v, err := genericParseType[uint16](strconv.FormatUint(test.value, 10)) + if test.bits <= 16 { + require.NoError(t, err) + require.Equal(t, uint16(test.value), v) //nolint:gosec // casting here is not a concern + } else { + require.ErrorIs(t, err, strconv.ErrRange) + } + } + }) + t.Run("test_genericParseTypeUint32s", func(t *testing.T) { + t.Parallel() + for _, test := range uints { + v, err := genericParseType[uint32](strconv.FormatUint(test.value, 10)) + if test.bits <= 32 { + require.NoError(t, err) + require.Equal(t, uint32(test.value), v) //nolint:gosec // casting here is not a concern + } else { + require.ErrorIs(t, err, strconv.ErrRange) + } + } + }) + + t.Run("test_genericParseTypeUint64s", func(t *testing.T) { + t.Parallel() + for _, test := range uints { + v, err := genericParseType[uint64](strconv.FormatUint(test.value, 10)) + if test.bits <= 64 { + require.NoError(t, err) + require.Equal(t, test.value, v) + } else { + require.ErrorIs(t, err, strconv.ErrRange) + } + } + }) + + t.Run("test_genericParseTypeUints", func(t *testing.T) { + t.Parallel() + for _, test := range uints { + v, err := genericParseType[uint](strconv.FormatUint(test.value, 10)) + if test.bits <= int(unsafe.Sizeof(uint(0)))*8 { + require.NoError(t, err) + require.Equal(t, uint(test.value), v) + } else { + require.ErrorIs(t, err, strconv.ErrRange) + } + } + }) } -// go test -run Test_GenericParseTypeInt64s -func Test_GenericParseTypeInt64s(t *testing.T) { +// go test -run Test_GenericParseTypeFloats +func Test_GenericParseTypeFloats(t *testing.T) { t.Parallel() - type genericTypes[v GenericType] struct { - value v - str string - } - int64s := []genericTypes[int64]{ + floats := []struct { + str string + value float64 + }{ { - value: int64(0), - str: "0", + value: 3.1415, + str: "3.1415", }, { - value: int64(1), - str: "1", + value: 1.234, + str: "1.234", }, { - value: int64(2), + value: 2, str: "2", }, { - value: int64(3), + value: 3, str: "3", }, - { - value: int64(4), - str: "4", - }, - { - value: int64(math.MaxInt64), - str: strconv.Itoa(math.MaxInt64), - }, - { - value: int64(math.MinInt64), - str: strconv.Itoa(math.MinInt64), - }, } - for _, test := range int64s { - var v int64 - tt := test - t.Run("test_genericParseTypeInt64s", func(t *testing.T) { - t.Parallel() - require.Equal(t, tt.value, genericParseType(tt.str, v)) - require.Equal(t, tt.value, genericParseType[int64](tt.str, v)) - }) - } + t.Run("test_genericParseTypeFloat32s", func(t *testing.T) { + t.Parallel() + for _, test := range floats { + v, err := genericParseType[float32](test.str) + require.NoError(t, err) + require.InEpsilon(t, float32(test.value), v, epsilon) + } + }) + + t.Run("test_genericParseTypeFloat64s", func(t *testing.T) { + t.Parallel() + for _, test := range floats { + v, err := genericParseType[float64](test.str) + require.NoError(t, err) + require.InEpsilon(t, test.value, v, epsilon) + } + }) } -// go test -run Test_GenericParseTypeUints -func Test_GenericParseTypeUints(t *testing.T) { +// go test -run Test_GenericParseTypeBytes +func Test_GenericParseTypeBytes(t *testing.T) { t.Parallel() - type genericTypes[v GenericType] struct { - value v - str string - } - uints := []genericTypes[uint]{ - { - value: uint(0), - str: "0", - }, - { - value: uint(1), - str: "1", - }, + cases := []struct { + str string + err error + value []byte + }{ { - value: uint(2), - str: "2", + value: []byte("alex"), + str: "alex", }, { - value: uint(3), - str: "3", + value: []byte("32.23"), + str: "32.23", }, { - value: uint(4), - str: "4", + value: []byte("john"), + str: "john", }, { - value: ^uint(0), - str: strconv.FormatUint(uint64(^uint(0)), 10), + value: []byte(nil), + str: "", + err: errParsedEmptyBytes, }, } - for _, test := range uints { - var v uint - tt := test - t.Run("test_genericParseTypeUints", func(t *testing.T) { + t.Run("test_genericParseTypeBytes", func(t *testing.T) { + t.Parallel() + for _, test := range cases { + v, err := genericParseType[[]byte](test.str) + if test.err == nil { + require.NoError(t, err) + } else { + require.ErrorIs(t, err, test.err) + } + require.Equal(t, test.value, v) + } + }) +} + +// go test -run Test_GenericParseTypeString +func Test_GenericParseTypeString(t *testing.T) { + t.Parallel() + + tests := []string{"john", "doe", "hello", "fiber"} + + for _, test := range tests { + t.Run("test_genericParseTypeString", func(t *testing.T) { t.Parallel() - require.Equal(t, tt.value, genericParseType(tt.str, v)) - require.Equal(t, tt.value, genericParseType[uint](tt.str, v)) + v, err := genericParseType[string](test) + require.NoError(t, err) + require.Equal(t, test, v) }) } } -// go test -run Test_GenericParseTypeUints -func Test_GenericParseTypeUint8s(t *testing.T) { +// go test -run Test_GenericParseTypeBoolean +func Test_GenericParseTypeBoolean(t *testing.T) { t.Parallel() - type genericTypes[v GenericType] struct { - value v - str string - } - uint8s := []genericTypes[uint8]{ - { - value: uint8(0), - str: "0", - }, - { - value: uint8(1), - str: "1", - }, + bools := []struct { + str string + value bool + }{ { - value: uint8(2), - str: "2", + str: "True", + value: true, }, { - value: uint8(3), - str: "3", + str: "False", + value: false, }, { - value: uint8(4), - str: "4", + str: "true", + value: true, }, { - value: uint8(math.MaxUint8), - str: strconv.Itoa(math.MaxUint8), + str: "false", + value: false, }, } - for _, test := range uint8s { - var v uint8 - tt := test - t.Run("test_genericParseTypeUint8s", func(t *testing.T) { - t.Parallel() - require.Equal(t, tt.value, genericParseType(tt.str, v)) - require.Equal(t, tt.value, genericParseType[uint8](tt.str, v)) - }) - } + t.Run("test_genericParseTypeBoolean", func(t *testing.T) { + t.Parallel() + for _, test := range bools { + v, err := genericParseType[bool](test.str) + require.NoError(t, err) + if test.value { + require.True(t, v) + } else { + require.False(t, v) + } + } + }) } -// go test -run Test_GenericParseTypeUint16s -func Test_GenericParseTypeUint16s(t *testing.T) { - t.Parallel() - - type genericTypes[v GenericType] struct { - value v - str string - } - - uint16s := []genericTypes[uint16]{ +// go test -v -run=^$ -bench=Benchmark_GenericParseTypeInts -benchmem -count=4 +func Benchmark_GenericParseTypeInts(b *testing.B) { + ints := []struct { + value int64 + bits int + }{ { - value: uint16(0), - str: "0", + value: 0, + bits: 8, }, { - value: uint16(1), - str: "1", + value: 1, + bits: 8, }, { - value: uint16(2), - str: "2", + value: 2, + bits: 8, }, { - value: uint16(3), - str: "3", + value: 3, + bits: 8, }, { - value: uint16(4), - str: "4", + value: 4, + bits: 8, }, { - value: uint16(math.MaxUint16), - str: strconv.Itoa(math.MaxUint16), + value: -1, + bits: 8, }, - } - - for _, test := range uint16s { - var v uint16 - tt := test - t.Run("test_genericParseTypeUint16s", func(t *testing.T) { - t.Parallel() - require.Equal(t, tt.value, genericParseType(tt.str, v)) - require.Equal(t, tt.value, genericParseType[uint16](tt.str, v)) - }) - } -} - -// go test -run Test_GenericParseTypeUint32s -func Test_GenericParseTypeUint32s(t *testing.T) { - t.Parallel() - - type genericTypes[v GenericType] struct { - value v - str string - } - - uint32s := []genericTypes[uint32]{ { - value: uint32(0), - str: "0", + value: math.MaxInt8, + bits: 8, }, { - value: uint32(1), - str: "1", + value: math.MinInt8, + bits: 8, }, { - value: uint32(2), - str: "2", + value: math.MaxInt16, + bits: 16, }, { - value: uint32(3), - str: "3", + value: math.MinInt16, + bits: 16, }, { - value: uint32(4), - str: "4", + value: math.MaxInt32, + bits: 32, }, { - value: uint32(math.MaxUint32), - str: strconv.Itoa(math.MaxUint32), + value: math.MinInt32, + bits: 32, }, - } - - for _, test := range uint32s { - var v uint32 - tt := test - t.Run("test_genericParseTypeUint32s", func(t *testing.T) { - t.Parallel() - require.Equal(t, tt.value, genericParseType(tt.str, v)) - require.Equal(t, tt.value, genericParseType[uint32](tt.str, v)) - }) + { + value: math.MaxInt64, + bits: 64, + }, + { + value: math.MinInt64, + bits: 64, + }, + } + for _, test := range ints { + benchGenericParseTypeInt[int8](b, "bench_genericParseTypeInt8s", 8, test.bits, test.value) + benchGenericParseTypeInt[int16](b, "bench_genericParseTypeInt16s", 16, test.bits, test.value) + benchGenericParseTypeInt[int32](b, "bench_genericParseTypeInt32s", 32, test.bits, test.value) + benchGenericParseTypeInt[int64](b, "bench_genericParseTypeInt64s", 64, test.bits, test.value) + benchGenericParseTypeInt[int](b, "bench_genericParseTypeInts", int(unsafe.Sizeof(int(0)))*8, test.bits, test.value) } } -// go test -run Test_GenericParseTypeUint64s -func Test_GenericParseTypeUint64s(t *testing.T) { - t.Parallel() - type genericTypes[v GenericType] struct { - value v - str string - } +func benchGenericParseTypeInt[V GenericTypeInteger](b *testing.B, name string, bits, expectedBits int, expectedVal int64) { + b.Helper() + b.Run(name, func(t *testing.B) { + var v V + var err error + b.ReportAllocs() + b.ResetTimer() + for n := 0; n < b.N; n++ { + v, err = genericParseType[V](strconv.FormatInt(expectedVal, 10)) + } + if expectedBits <= bits { + require.NoError(t, err) + require.Equal(t, V(expectedVal), v) + } else { + require.ErrorIs(t, err, strconv.ErrRange) + } + }) +} - uint64s := []genericTypes[uint64]{ +// go test -v -run=^$ -bench=Benchmark_GenericParseTypeUints -benchmem -count=4 +func Benchmark_GenericParseTypeUints(b *testing.B) { + uints := []struct { + value uint64 + bits int + }{ { - value: uint64(0), - str: "0", + value: 0, + bits: 8, }, { - value: uint64(1), - str: "1", + value: 1, + bits: 8, }, { - value: uint64(2), - str: "2", + value: 2, + bits: 8, }, { - value: uint64(3), - str: "3", + value: 3, + bits: 8, }, { - value: uint64(4), - str: "4", + value: 4, + bits: 8, }, - } - - for _, test := range uint64s { - var v uint64 - tt := test - t.Run("test_genericParseTypeUint64s", func(t *testing.T) { - t.Parallel() - require.Equal(t, tt.value, genericParseType(tt.str, v)) - require.Equal(t, tt.value, genericParseType[uint64](tt.str, v)) - }) - } -} - -// go test -run Test_GenericParseTypeFloat32s -func Test_GenericParseTypeFloat32s(t *testing.T) { - t.Parallel() - - type genericTypes[v GenericType] struct { - value v - str string - } - - float32s := []genericTypes[float32]{ { - value: float32(3.1415), - str: "3.1415", + value: math.MaxUint8, + bits: 8, }, { - value: float32(1.234), - str: "1.234", + value: math.MaxUint16, + bits: 16, }, { - value: float32(2), - str: "2", + value: math.MaxUint16, + bits: 16, }, { - value: float32(3), - str: "3", + value: math.MaxUint32, + bits: 32, + }, + { + value: math.MaxUint64, + bits: 64, }, } - for _, test := range float32s { - var v float32 - tt := test - t.Run("test_genericParseTypeFloat32s", func(t *testing.T) { - t.Parallel() - require.InEpsilon(t, tt.value, genericParseType(tt.str, v), epsilon) - require.InEpsilon(t, tt.value, genericParseType[float32](tt.str, v), epsilon) - }) + for _, test := range uints { + benchGenericParseTypeUInt[uint8](b, "benchmark_genericParseTypeUint8s", 8, test.bits, test.value) + benchGenericParseTypeUInt[uint16](b, "benchmark_genericParseTypeUint16s", 16, test.bits, test.value) + benchGenericParseTypeUInt[uint32](b, "benchmark_genericParseTypeUint32s", 32, test.bits, test.value) + benchGenericParseTypeUInt[uint64](b, "benchmark_genericParseTypeUint64s", 64, test.bits, test.value) + benchGenericParseTypeUInt[uint](b, "benchmark_genericParseTypeUints", int(unsafe.Sizeof(uint(0)))*8, test.bits, test.value) } } -// go test -run Test_GenericParseTypeFloat64s -func Test_GenericParseTypeFloat64s(t *testing.T) { - t.Parallel() +func benchGenericParseTypeUInt[V GenericTypeInteger](b *testing.B, name string, bits, expectedBits int, expectedVal uint64) { + b.Helper() + b.Run(name, func(t *testing.B) { + var v V + var err error + b.ReportAllocs() + b.ResetTimer() + for n := 0; n < b.N; n++ { + v, err = genericParseType[V](strconv.FormatUint(expectedVal, 10)) + } + if expectedBits <= bits { + require.NoError(t, err) + require.Equal(t, V(expectedVal), v) + } else { + require.ErrorIs(t, err, strconv.ErrRange) + } + }) +} - type genericTypes[v GenericType] struct { - value v +// go test -v -run=^$ -bench=Benchmark_GenericParseTypeFloats -benchmem -count=4 +func Benchmark_GenericParseTypeFloats(b *testing.B) { + floats := []struct { str string - } - - float64s := []genericTypes[float64]{ + value float64 + }{ { - value: float64(3.1415), + value: 3.1415, str: "3.1415", }, { - value: float64(1.234), + value: 1.234, str: "1.234", }, { - value: float64(2), + value: 2, str: "2", }, { - value: float64(3), + value: 3, str: "3", }, } - for _, test := range float64s { - var v float64 - tt := test - t.Run("test_genericParseTypeFloat64s", func(t *testing.T) { - t.Parallel() - require.InEpsilon(t, tt.value, genericParseType(tt.str, v), epsilon) - require.InEpsilon(t, tt.value, genericParseType[float64](tt.str, v), epsilon) + for _, test := range floats { + b.Run("benchmark_genericParseTypeFloat32s", func(t *testing.B) { + var v float32 + var err error + b.ReportAllocs() + b.ResetTimer() + for n := 0; n < b.N; n++ { + v, err = genericParseType[float32](test.str) + } + require.NoError(t, err) + require.InEpsilon(t, float32(test.value), v, epsilon) }) } -} - -// go test -run Test_GenericParseTypeArrayBytes -func Test_GenericParseTypeArrayBytes(t *testing.T) { - t.Parallel() - type genericTypes[v GenericType] struct { - value v - str string + for _, test := range floats { + b.Run("test_genericParseTypeFloat64s", func(t *testing.B) { + var v float64 + var err error + b.ReportAllocs() + b.ResetTimer() + for n := 0; n < b.N; n++ { + v, err = genericParseType[float64](test.str) + } + require.NoError(t, err) + require.InEpsilon(t, test.value, v, epsilon) + }) } +} - arrBytes := []genericTypes[[]byte]{ +// go test -v -run=^$ -bench=Benchmark_GenericParseTypeBytes -benchmem -count=4 +func Benchmark_GenericParseTypeBytes(b *testing.B) { + cases := []struct { + str string + err error + value []byte + }{ { value: []byte("alex"), str: "alex", @@ -5829,37 +5870,61 @@ func Test_GenericParseTypeArrayBytes(t *testing.T) { value: []byte("32.23"), str: "32.23", }, - { - value: []byte(nil), - str: "", - }, { value: []byte("john"), str: "john", }, + { + value: []byte(nil), + str: "", + err: errParsedEmptyBytes, + }, } - for _, test := range arrBytes { - var v []byte - tt := test - t.Run("test_genericParseTypeArrayBytes", func(t *testing.T) { - t.Parallel() - require.Equal(t, tt.value, genericParseType(tt.str, v, []byte(nil))) - require.Equal(t, tt.value, genericParseType[[]byte](tt.str, v, []byte(nil))) + for _, test := range cases { + b.Run("benchmark_genericParseTypeBytes", func(b *testing.B) { + var v []byte + var err error + b.ReportAllocs() + b.ResetTimer() + for n := 0; n < b.N; n++ { + v, err = genericParseType[[]byte](test.str) + } + if test.err == nil { + require.NoError(b, err) + } else { + require.ErrorIs(b, err, test.err) + } + require.Equal(b, test.value, v) }) } } -// go test -run Test_GenericParseTypeBoolean -func Test_GenericParseTypeBoolean(t *testing.T) { - t.Parallel() +// go test -v -run=^$ -bench=Benchmark_GenericParseTypeString -benchmem -count=4 +func Benchmark_GenericParseTypeString(b *testing.B) { + tests := []string{"john", "doe", "hello", "fiber"} - type genericTypes[v GenericType] struct { - value v - str string + for _, test := range tests { + b.Run("benchmark_genericParseTypeString", func(b *testing.B) { + var v string + var err error + b.ReportAllocs() + b.ResetTimer() + for n := 0; n < b.N; n++ { + v, err = genericParseType[string](test) + } + require.NoError(b, err) + require.Equal(b, test, v) + }) } +} - bools := []genericTypes[bool]{ +// go test -v -run=^$ -bench=Benchmark_GenericParseTypeBoolean -benchmem -count=4 +func Benchmark_GenericParseTypeBoolean(b *testing.B) { + bools := []struct { + str string + value bool + }{ { str: "True", value: true, @@ -5879,16 +5944,19 @@ func Test_GenericParseTypeBoolean(t *testing.T) { } for _, test := range bools { - var v bool - tt := test - t.Run("test_genericParseTypeBoolean", func(t *testing.T) { - t.Parallel() - if tt.value { - require.True(t, genericParseType(tt.str, v)) - require.True(t, genericParseType[bool](tt.str, v)) + b.Run("benchmark_genericParseTypeBoolean", func(b *testing.B) { + var v bool + var err error + b.ReportAllocs() + b.ResetTimer() + for n := 0; n < b.N; n++ { + v, err = genericParseType[bool](test.str) + } + require.NoError(b, err) + if test.value { + require.True(b, v) } else { - require.False(t, genericParseType(tt.str, v)) - require.False(t, genericParseType[bool](tt.str, v)) + require.False(b, v) } }) } @@ -6024,561 +6092,6 @@ func Test_Ctx_End_after_drop(t *testing.T) { require.Nil(t, resp) } -// go test -run Test_GenericParseTypeString -func Test_GenericParseTypeString(t *testing.T) { - t.Parallel() - - tests := []string{"john", "doe", "hello", "fiber"} - - for _, test := range tests { - var v string - tt := test - t.Run("test_genericParseTypeString", func(t *testing.T) { - t.Parallel() - require.Equal(t, tt, genericParseType(tt, v)) - require.Equal(t, tt, genericParseType[string](tt, v)) - }) - } -} - -// go test -v -run=^$ -bench=Benchmark_GenericParseTypeInts -benchmem -count=4 -func Benchmark_GenericParseTypeInts(b *testing.B) { - type genericTypes[v GenericType] struct { - value v - str string - } - - ints := []genericTypes[int]{ - { - value: 0, - str: "0", - }, - { - value: 1, - str: "1", - }, - { - value: 2, - str: "2", - }, - { - value: 3, - str: "3", - }, - { - value: 4, - str: "4", - }, - } - - int8s := []genericTypes[int8]{ - { - value: int8(0), - str: "0", - }, - { - value: int8(1), - str: "1", - }, - { - value: int8(2), - str: "2", - }, - { - value: int8(3), - str: "3", - }, - { - value: int8(4), - str: "4", - }, - } - - int16s := []genericTypes[int16]{ - { - value: int16(0), - str: "0", - }, - { - value: int16(1), - str: "1", - }, - { - value: int16(2), - str: "2", - }, - { - value: int16(3), - str: "3", - }, - { - value: int16(4), - str: "4", - }, - } - - int32s := []genericTypes[int32]{ - { - value: int32(0), - str: "0", - }, - { - value: int32(1), - str: "1", - }, - { - value: int32(2), - str: "2", - }, - { - value: int32(3), - str: "3", - }, - { - value: int32(4), - str: "4", - }, - } - - int64s := []genericTypes[int64]{ - { - value: int64(0), - str: "0", - }, - { - value: int64(1), - str: "1", - }, - { - value: int64(2), - str: "2", - }, - { - value: int64(3), - str: "3", - }, - { - value: int64(4), - str: "4", - }, - } - - for _, test := range ints { - b.Run("bench_genericParseTypeInts", func(b *testing.B) { - var res int - b.ReportAllocs() - b.ResetTimer() - for n := 0; n < b.N; n++ { - res = genericParseType(test.str, res) - } - require.Equal(b, test.value, res) - }) - } - - for _, test := range int8s { - b.Run("benchmark_genericParseTypeInt8s", func(b *testing.B) { - var res int8 - b.ReportAllocs() - b.ResetTimer() - for n := 0; n < b.N; n++ { - res = genericParseType(test.str, res) - } - require.Equal(b, test.value, res) - }) - } - - for _, test := range int16s { - b.Run("benchmark_genericParseTypeInt16s", func(b *testing.B) { - var res int16 - b.ReportAllocs() - b.ResetTimer() - for n := 0; n < b.N; n++ { - res = genericParseType(test.str, res) - } - require.Equal(b, test.value, res) - }) - } - - for _, test := range int32s { - b.Run("benchmark_genericParseType32Ints", func(b *testing.B) { - var res int32 - b.ReportAllocs() - b.ResetTimer() - for n := 0; n < b.N; n++ { - res = genericParseType(test.str, res) - } - require.Equal(b, test.value, res) - }) - } - - for _, test := range int64s { - b.Run("benchmark_genericParseTypeInt64s", func(b *testing.B) { - var res int64 - b.ReportAllocs() - b.ResetTimer() - for n := 0; n < b.N; n++ { - res = genericParseType(test.str, res) - } - require.Equal(b, test.value, res) - }) - } -} - -// go test -v -run=^$ -bench=Benchmark_GenericParseTypeUints -benchmem -count=4 -func Benchmark_GenericParseTypeUints(b *testing.B) { - type genericTypes[v GenericType] struct { - value v - str string - } - - uints := []genericTypes[uint]{ - { - value: uint(0), - str: "0", - }, - { - value: uint(1), - str: "1", - }, - { - value: uint(2), - str: "2", - }, - { - value: uint(3), - str: "3", - }, - { - value: uint(4), - str: "4", - }, - } - - uint8s := []genericTypes[uint8]{ - { - value: uint8(0), - str: "0", - }, - { - value: uint8(1), - str: "1", - }, - { - value: uint8(2), - str: "2", - }, - { - value: uint8(3), - str: "3", - }, - { - value: uint8(4), - str: "4", - }, - } - - uint16s := []genericTypes[uint16]{ - { - value: uint16(0), - str: "0", - }, - { - value: uint16(1), - str: "1", - }, - { - value: uint16(2), - str: "2", - }, - { - value: uint16(3), - str: "3", - }, - { - value: uint16(4), - str: "4", - }, - } - - uint32s := []genericTypes[uint32]{ - { - value: uint32(0), - str: "0", - }, - { - value: uint32(1), - str: "1", - }, - { - value: uint32(2), - str: "2", - }, - { - value: uint32(3), - str: "3", - }, - { - value: uint32(4), - str: "4", - }, - } - - uint64s := []genericTypes[uint64]{ - { - value: uint64(0), - str: "0", - }, - { - value: uint64(1), - str: "1", - }, - { - value: uint64(2), - str: "2", - }, - { - value: uint64(3), - str: "3", - }, - { - value: uint64(4), - str: "4", - }, - } - - for _, test := range uints { - b.Run("benchamark_genericParseTypeUints", func(b *testing.B) { - var res uint - b.ReportAllocs() - b.ResetTimer() - for n := 0; n < b.N; n++ { - res = genericParseType(test.str, res) - } - require.Equal(b, test.value, res) - }) - } - - for _, test := range uint8s { - b.Run("benchamark_genericParseTypeUint8s", func(b *testing.B) { - var res uint8 - b.ReportAllocs() - b.ResetTimer() - for n := 0; n < b.N; n++ { - res = genericParseType(test.str, res) - } - require.Equal(b, test.value, res) - }) - } - - for _, test := range uint16s { - b.Run("benchamark_genericParseTypeUint16s", func(b *testing.B) { - var res uint16 - b.ReportAllocs() - b.ResetTimer() - for n := 0; n < b.N; n++ { - res = genericParseType(test.str, res) - } - require.Equal(b, test.value, res) - }) - } - - for _, test := range uint32s { - b.Run("benchamark_genericParseTypeUint32s", func(b *testing.B) { - var res uint32 - b.ReportAllocs() - b.ResetTimer() - for n := 0; n < b.N; n++ { - res = genericParseType(test.str, res) - } - require.Equal(b, test.value, res) - }) - } - - for _, test := range uint64s { - b.Run("benchamark_genericParseTypeUint64s", func(b *testing.B) { - var res uint64 - b.ReportAllocs() - b.ResetTimer() - for n := 0; n < b.N; n++ { - res = genericParseType(test.str, res) - } - require.Equal(b, test.value, res) - }) - } -} - -// go test -v -run=^$ -bench=Benchmark_GenericParseTypeFloats -benchmem -count=4 -func Benchmark_GenericParseTypeFloats(b *testing.B) { - type genericTypes[v GenericType] struct { - value v - str string - } - - float32s := []genericTypes[float32]{ - { - value: float32(3.1415), - str: "3.1415", - }, - { - value: float32(1.234), - str: "1.234", - }, - { - value: float32(2), - str: "2", - }, - { - value: float32(3), - str: "3", - }, - } - - float64s := []genericTypes[float64]{ - { - value: float64(3.1415), - str: "3.1415", - }, - { - value: float64(1.234), - str: "1.234", - }, - { - value: float64(2), - str: "2", - }, - { - value: float64(3), - str: "3", - }, - } - - for _, test := range float32s { - b.Run("benchmark_genericParseTypeFloat32s", func(b *testing.B) { - var res float32 - b.ReportAllocs() - b.ResetTimer() - for n := 0; n < b.N; n++ { - res = genericParseType(test.str, res) - } - require.InEpsilon(b, test.value, res, epsilon) - }) - } - - for _, test := range float64s { - b.Run("benchmark_genericParseTypeFloat32s", func(b *testing.B) { - var res float64 - b.ReportAllocs() - b.ResetTimer() - for n := 0; n < b.N; n++ { - res = genericParseType(test.str, res) - } - require.InEpsilon(b, test.value, res, epsilon) - }) - } -} - -// go test -v -run=^$ -bench=Benchmark_GenericParseTypeArrayBytes -benchmem -count=4 -func Benchmark_GenericParseTypeArrayBytes(b *testing.B) { - type genericTypes[v GenericType] struct { - value v - str string - } - - arrBytes := []genericTypes[[]byte]{ - { - value: []byte("alex"), - str: "alex", - }, - { - value: []byte("32.23"), - str: "32.23", - }, - { - value: []byte(nil), - str: "", - }, - { - value: []byte("john"), - str: "john", - }, - } - - for _, test := range arrBytes { - b.Run("Benchmark_GenericParseTypeArrayBytes", func(b *testing.B) { - var res []byte - b.ReportAllocs() - b.ResetTimer() - for n := 0; n < b.N; n++ { - res = genericParseType(test.str, res, []byte(nil)) - } - require.Equal(b, test.value, res) - }) - } -} - -// go test -v -run=^$ -bench=Benchmark_GenericParseTypeBoolean -benchmem -count=4 -func Benchmark_GenericParseTypeBoolean(b *testing.B) { - type genericTypes[v GenericType] struct { - value v - str string - } - - bools := []genericTypes[bool]{ - { - str: "True", - value: true, - }, - { - str: "False", - value: false, - }, - { - str: "true", - value: true, - }, - { - str: "false", - value: false, - }, - } - - for _, test := range bools { - b.Run("Benchmark_GenericParseTypeBoolean", func(b *testing.B) { - var res bool - b.ReportAllocs() - b.ResetTimer() - for n := 0; n < b.N; n++ { - res = genericParseType(test.str, res) - } - if test.value { - require.True(b, res) - } else { - require.False(b, res) - } - }) - } -} - -// go test -v -run=^$ -bench=Benchmark_GenericParseTypeString -benchmem -count=4 -func Benchmark_GenericParseTypeString(b *testing.B) { - tests := []string{"john", "doe", "hello", "fiber"} - - b.ReportAllocs() - b.ResetTimer() - for _, test := range tests { - b.Run("benchmark_genericParseTypeString", func(b *testing.B) { - var res string - b.ReportAllocs() - b.ResetTimer() - for n := 0; n < b.N; n++ { - res = genericParseType(test, res) - } - - require.Equal(b, test, res) - }) - } -} - // go test -v -run=^$ -bench=Benchmark_Ctx_IsProxyTrusted -benchmem -count=4 func Benchmark_Ctx_IsProxyTrusted(b *testing.B) { // Scenario without trusted proxy check diff --git a/helpers.go b/helpers.go index e80e89c1e6a..5cc52ee2740 100644 --- a/helpers.go +++ b/helpers.go @@ -753,90 +753,104 @@ func Convert[T any](value string, convertor func(string) (T, error), defaultValu return converted, nil } -// assertValueType asserts the type of the result to the type of the value -func assertValueType[V GenericType, T any](result T) V { - v, ok := any(result).(V) - if !ok { - panic(fmt.Errorf("failed to type-assert to %T", v)) - } - return v -} +var ( + errParsedEmptyString = errors.New("parsed result is empty string") + errParsedEmptyBytes = errors.New("parsed result is empty bytes") +) -func genericParseDefault[V GenericType](err error, parser func() V, defaultValue ...V) V { +func genericParseType[V GenericType](str string) (V, error) { var v V - if err != nil { - if len(defaultValue) > 0 { - return defaultValue[0] - } - return v - } - return parser() -} - -func genericParseInt[V GenericType](str string, bitSize int, parser func(int64) V, defaultValue ...V) V { - result, err := strconv.ParseInt(str, 10, bitSize) - return genericParseDefault[V](err, func() V { return parser(result) }, defaultValue...) -} - -func genericParseUint[V GenericType](str string, bitSize int, parser func(uint64) V, defaultValue ...V) V { - result, err := strconv.ParseUint(str, 10, bitSize) - return genericParseDefault[V](err, func() V { return parser(result) }, defaultValue...) -} - -func genericParseFloat[V GenericType](str string, bitSize int, parser func(float64) V, defaultValue ...V) V { - result, err := strconv.ParseFloat(str, bitSize) - return genericParseDefault[V](err, func() V { return parser(result) }, defaultValue...) -} - -func genericParseBool[V GenericType](str string, parser func(bool) V, defaultValue ...V) V { - result, err := strconv.ParseBool(str) - return genericParseDefault[V](err, func() V { return parser(result) }, defaultValue...) -} - -//nolint:gosec // Casting in this function is not a concern -func genericParseType[V GenericType](str string, v V, defaultValue ...V) V { switch any(v).(type) { case int: - return genericParseInt[V](str, 0, func(i int64) V { return assertValueType[V, int](int(i)) }, defaultValue...) + result, err := strconv.ParseInt(str, 10, 0) + if err != nil { + return v, fmt.Errorf("failed to parse int: %w", err) + } + return any(int(result)).(V), nil //nolint:errcheck,forcetypeassert // not needed case int8: - return genericParseInt[V](str, 8, func(i int64) V { return assertValueType[V, int8](int8(i)) }, defaultValue...) + result, err := strconv.ParseInt(str, 10, 8) + if err != nil { + return v, fmt.Errorf("failed to parse int8: %w", err) + } + return any(int8(result)).(V), nil //nolint:errcheck,forcetypeassert // not needed case int16: - return genericParseInt[V](str, 16, func(i int64) V { return assertValueType[V, int16](int16(i)) }, defaultValue...) + result, err := strconv.ParseInt(str, 10, 16) + if err != nil { + return v, fmt.Errorf("failed to parse int16: %w", err) + } + return any(int16(result)).(V), nil //nolint:errcheck,forcetypeassert // not needed case int32: - return genericParseInt[V](str, 32, func(i int64) V { return assertValueType[V, int32](int32(i)) }, defaultValue...) + result, err := strconv.ParseInt(str, 10, 32) + if err != nil { + return v, fmt.Errorf("failed to parse int32: %w", err) + } + return any(int32(result)).(V), nil //nolint:errcheck,forcetypeassert // not needed case int64: - return genericParseInt[V](str, 64, func(i int64) V { return assertValueType[V, int64](i) }, defaultValue...) + result, err := strconv.ParseInt(str, 10, 64) + if err != nil { + return v, fmt.Errorf("failed to parse int64: %w", err) + } + return any(result).(V), nil //nolint:errcheck,forcetypeassert // not needed case uint: - return genericParseUint[V](str, 0, func(i uint64) V { return assertValueType[V, uint](uint(i)) }, defaultValue...) + result, err := strconv.ParseUint(str, 10, 0) + if err != nil { + return v, fmt.Errorf("failed to parse uint: %w", err) + } + return any(uint(result)).(V), nil //nolint:errcheck,forcetypeassert // not needed case uint8: - return genericParseUint[V](str, 8, func(i uint64) V { return assertValueType[V, uint8](uint8(i)) }, defaultValue...) + result, err := strconv.ParseUint(str, 10, 8) + if err != nil { + return v, fmt.Errorf("failed to parse uint8: %w", err) + } + return any(uint8(result)).(V), nil //nolint:errcheck,forcetypeassert // not needed case uint16: - return genericParseUint[V](str, 16, func(i uint64) V { return assertValueType[V, uint16](uint16(i)) }, defaultValue...) + result, err := strconv.ParseUint(str, 10, 16) + if err != nil { + return v, fmt.Errorf("failed to parse uint16: %w", err) + } + return any(uint16(result)).(V), nil //nolint:errcheck,forcetypeassert // not needed case uint32: - return genericParseUint[V](str, 32, func(i uint64) V { return assertValueType[V, uint32](uint32(i)) }, defaultValue...) + result, err := strconv.ParseUint(str, 10, 32) + if err != nil { + return v, fmt.Errorf("failed to parse uint32: %w", err) + } + return any(uint32(result)).(V), nil //nolint:errcheck,forcetypeassert // not needed case uint64: - return genericParseUint[V](str, 64, func(i uint64) V { return assertValueType[V, uint64](i) }, defaultValue...) + result, err := strconv.ParseUint(str, 10, 64) + if err != nil { + return v, fmt.Errorf("failed to parse uint64: %w", err) + } + return any(result).(V), nil //nolint:errcheck,forcetypeassert // not needed case float32: - return genericParseFloat[V](str, 32, func(i float64) V { return assertValueType[V, float32](float32(i)) }, defaultValue...) + result, err := strconv.ParseFloat(str, 32) + if err != nil { + return v, fmt.Errorf("failed to parse float32: %w", err) + } + return any(float32(result)).(V), nil //nolint:errcheck,forcetypeassert // not needed case float64: - return genericParseFloat[V](str, 64, func(i float64) V { return assertValueType[V, float64](i) }, defaultValue...) + result, err := strconv.ParseFloat(str, 64) + if err != nil { + return v, fmt.Errorf("failed to parse float64: %w", err) + } + return any(result).(V), nil //nolint:errcheck,forcetypeassert // not needed case bool: - return genericParseBool[V](str, func(b bool) V { return assertValueType[V, bool](b) }, defaultValue...) + result, err := strconv.ParseBool(str) + if err != nil { + return v, fmt.Errorf("failed to parse bool: %w", err) + } + return any(result).(V), nil //nolint:errcheck,forcetypeassert // not needed case string: - if str == "" && len(defaultValue) > 0 { - return defaultValue[0] + if str == "" { + return v, errParsedEmptyString } - return assertValueType[V, string](str) + return any(str).(V), nil //nolint:errcheck,forcetypeassert // not needed case []byte: - if str == "" && len(defaultValue) > 0 { - return defaultValue[0] + if str == "" { + return v, errParsedEmptyBytes } - return assertValueType[V, []byte]([]byte(str)) + return any([]byte(str)).(V), nil //nolint:errcheck,forcetypeassert // not needed default: - if len(defaultValue) > 0 { - return defaultValue[0] - } - return v + return v, nil } } From 6f5d786149b57e0644a2608ee32fda4ba4c4e278 Mon Sep 17 00:00:00 2001 From: Kashiwa <13825170+ksw2000@users.noreply.github.com> Date: Sat, 24 May 2025 16:36:38 +0800 Subject: [PATCH 2/6] =?UTF-8?q?=F0=9F=90=9B=20fix:=20return=20error=20when?= =?UTF-8?q?=20parsing=20unsupported=20type?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- helpers.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/helpers.go b/helpers.go index 5cc52ee2740..2cfb09c9fca 100644 --- a/helpers.go +++ b/helpers.go @@ -756,6 +756,7 @@ func Convert[T any](value string, convertor func(string) (T, error), defaultValu var ( errParsedEmptyString = errors.New("parsed result is empty string") errParsedEmptyBytes = errors.New("parsed result is empty bytes") + errParsedType = errors.New("unsupported generic type") ) func genericParseType[V GenericType](str string) (V, error) { @@ -850,7 +851,7 @@ func genericParseType[V GenericType](str string) (V, error) { } return any([]byte(str)).(V), nil //nolint:errcheck,forcetypeassert // not needed default: - return v, nil + return v, errParsedType } } From 2bb95eee26f91b581cad671a16eac72653a3bc19 Mon Sep 17 00:00:00 2001 From: Kashiwa <13825170+ksw2000@users.noreply.github.com> Date: Sun, 25 May 2025 16:13:47 +0800 Subject: [PATCH 3/6] =?UTF-8?q?=F0=9F=9A=A8=20test:=20cover=20the=20defaul?= =?UTF-8?q?t=20value=20for=20Params?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ctx_test.go | 1 + 1 file changed, 1 insertion(+) diff --git a/ctx_test.go b/ctx_test.go index 16ee10f1413..ffdfbe7aa58 100644 --- a/ctx_test.go +++ b/ctx_test.go @@ -2495,6 +2495,7 @@ func Test_Ctx_Params(t *testing.T) { }) app.Get("/test4/:optional?", func(c Ctx) error { require.Equal(t, "", c.Params("optional")) + require.Equal(t, "default", Params(c, "optional", "default")) return nil }) app.Get("/test5/:id/:Id", func(c Ctx) error { From ef40a2f870c0ab11038031d3547ac81c522c4797 Mon Sep 17 00:00:00 2001 From: Kashiwa <13825170+ksw2000@users.noreply.github.com> Date: Mon, 26 May 2025 00:16:00 +0800 Subject: [PATCH 4/6] =?UTF-8?q?=F0=9F=9A=A8=20test:=20cover=20default=20va?= =?UTF-8?q?lue=20on=20parsing=20error?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ctx_test.go | 713 ------------------------------------------------ helpers_test.go | 647 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 647 insertions(+), 713 deletions(-) diff --git a/ctx_test.go b/ctx_test.go index ffdfbe7aa58..044f2826b5d 100644 --- a/ctx_test.go +++ b/ctx_test.go @@ -17,7 +17,6 @@ import ( "errors" "fmt" "io" - "math" "mime/multipart" "net" "net/http/httptest" @@ -28,7 +27,6 @@ import ( "testing" "text/template" "time" - "unsafe" "github.com/gofiber/fiber/v3/internal/storage/memory" "github.com/gofiber/utils/v2" @@ -5252,717 +5250,6 @@ func Benchmark_Ctx_GetReqHeaders(b *testing.B) { }, headers) } -// go test -run Test_GenericParseTypeInts -func Test_GenericParseTypeInts(t *testing.T) { - t.Parallel() - ints := []struct { - value int64 - bits int - }{ - { - value: 0, - bits: 8, - }, - { - value: 1, - bits: 8, - }, - { - value: 2, - bits: 8, - }, - { - value: 3, - bits: 8, - }, - { - value: 4, - bits: 8, - }, - { - value: -1, - bits: 8, - }, - { - value: math.MaxInt8, - bits: 8, - }, - { - value: math.MinInt8, - bits: 8, - }, - { - value: math.MaxInt16, - bits: 16, - }, - { - value: math.MinInt16, - bits: 16, - }, - { - value: math.MaxInt32, - bits: 32, - }, - { - value: math.MinInt32, - bits: 32, - }, - { - value: math.MaxInt64, - bits: 64, - }, - { - value: math.MinInt64, - bits: 64, - }, - } - - t.Run("test_genericParseTypeInt8s", func(t *testing.T) { - t.Parallel() - for _, test := range ints { - v, err := genericParseType[int8](strconv.FormatInt(test.value, 10)) - if test.bits <= 8 { - require.NoError(t, err) - require.Equal(t, int8(test.value), v) //nolint:gosec // casting here is not a concern - } else { - require.ErrorIs(t, err, strconv.ErrRange) - } - } - }) - t.Run("test_genericParseTypeInt16s", func(t *testing.T) { - t.Parallel() - for _, test := range ints { - v, err := genericParseType[int16](strconv.FormatInt(test.value, 10)) - if test.bits <= 16 { - require.NoError(t, err) - require.Equal(t, int16(test.value), v) //nolint:gosec // casting here is not a concern - } else { - require.ErrorIs(t, err, strconv.ErrRange) - } - } - }) - t.Run("test_genericParseTypeInt32s", func(t *testing.T) { - t.Parallel() - for _, test := range ints { - v, err := genericParseType[int32](strconv.FormatInt(test.value, 10)) - if test.bits <= 32 { - require.NoError(t, err) - require.Equal(t, int32(test.value), v) //nolint:gosec // casting here is not a concern - } else { - require.ErrorIs(t, err, strconv.ErrRange) - } - } - }) - t.Run("test_genericParseTypeInt64s", func(t *testing.T) { - t.Parallel() - for _, test := range ints { - v, err := genericParseType[int64](strconv.FormatInt(test.value, 10)) - if test.bits <= 64 { - require.NoError(t, err) - require.Equal(t, test.value, v) - } else { - require.ErrorIs(t, err, strconv.ErrRange) - } - } - }) - - t.Run("test_genericParseTypeInts", func(t *testing.T) { - t.Parallel() - for _, test := range ints { - v, err := genericParseType[int](strconv.FormatInt(test.value, 10)) - if test.bits <= int(unsafe.Sizeof(int(0)))*8 { - require.NoError(t, err) - require.Equal(t, int(test.value), v) - } else { - require.ErrorIs(t, err, strconv.ErrRange) - } - } - }) -} - -// go test -run Test_GenericParseTypeUints -func Test_GenericParseTypeUints(t *testing.T) { - t.Parallel() - uints := []struct { - value uint64 - bits int - }{ - { - value: 0, - bits: 8, - }, - { - value: 1, - bits: 8, - }, - { - value: 2, - bits: 8, - }, - { - value: 3, - bits: 8, - }, - { - value: 4, - bits: 8, - }, - { - value: math.MaxUint8, - bits: 8, - }, - { - value: math.MaxUint16, - bits: 16, - }, - { - value: math.MaxUint16, - bits: 16, - }, - { - value: math.MaxUint32, - bits: 32, - }, - { - value: math.MaxUint64, - bits: 64, - }, - } - - t.Run("test_genericParseTypeUint8s", func(t *testing.T) { - t.Parallel() - for _, test := range uints { - v, err := genericParseType[uint8](strconv.FormatUint(test.value, 10)) - if test.bits <= 8 { - require.NoError(t, err) - require.Equal(t, uint8(test.value), v) //nolint:gosec // casting here is not a concern - } else { - require.ErrorIs(t, err, strconv.ErrRange) - } - } - }) - t.Run("test_genericParseTypeUint16s", func(t *testing.T) { - t.Parallel() - for _, test := range uints { - v, err := genericParseType[uint16](strconv.FormatUint(test.value, 10)) - if test.bits <= 16 { - require.NoError(t, err) - require.Equal(t, uint16(test.value), v) //nolint:gosec // casting here is not a concern - } else { - require.ErrorIs(t, err, strconv.ErrRange) - } - } - }) - t.Run("test_genericParseTypeUint32s", func(t *testing.T) { - t.Parallel() - for _, test := range uints { - v, err := genericParseType[uint32](strconv.FormatUint(test.value, 10)) - if test.bits <= 32 { - require.NoError(t, err) - require.Equal(t, uint32(test.value), v) //nolint:gosec // casting here is not a concern - } else { - require.ErrorIs(t, err, strconv.ErrRange) - } - } - }) - - t.Run("test_genericParseTypeUint64s", func(t *testing.T) { - t.Parallel() - for _, test := range uints { - v, err := genericParseType[uint64](strconv.FormatUint(test.value, 10)) - if test.bits <= 64 { - require.NoError(t, err) - require.Equal(t, test.value, v) - } else { - require.ErrorIs(t, err, strconv.ErrRange) - } - } - }) - - t.Run("test_genericParseTypeUints", func(t *testing.T) { - t.Parallel() - for _, test := range uints { - v, err := genericParseType[uint](strconv.FormatUint(test.value, 10)) - if test.bits <= int(unsafe.Sizeof(uint(0)))*8 { - require.NoError(t, err) - require.Equal(t, uint(test.value), v) - } else { - require.ErrorIs(t, err, strconv.ErrRange) - } - } - }) -} - -// go test -run Test_GenericParseTypeFloats -func Test_GenericParseTypeFloats(t *testing.T) { - t.Parallel() - - floats := []struct { - str string - value float64 - }{ - { - value: 3.1415, - str: "3.1415", - }, - { - value: 1.234, - str: "1.234", - }, - { - value: 2, - str: "2", - }, - { - value: 3, - str: "3", - }, - } - - t.Run("test_genericParseTypeFloat32s", func(t *testing.T) { - t.Parallel() - for _, test := range floats { - v, err := genericParseType[float32](test.str) - require.NoError(t, err) - require.InEpsilon(t, float32(test.value), v, epsilon) - } - }) - - t.Run("test_genericParseTypeFloat64s", func(t *testing.T) { - t.Parallel() - for _, test := range floats { - v, err := genericParseType[float64](test.str) - require.NoError(t, err) - require.InEpsilon(t, test.value, v, epsilon) - } - }) -} - -// go test -run Test_GenericParseTypeBytes -func Test_GenericParseTypeBytes(t *testing.T) { - t.Parallel() - - cases := []struct { - str string - err error - value []byte - }{ - { - value: []byte("alex"), - str: "alex", - }, - { - value: []byte("32.23"), - str: "32.23", - }, - { - value: []byte("john"), - str: "john", - }, - { - value: []byte(nil), - str: "", - err: errParsedEmptyBytes, - }, - } - - t.Run("test_genericParseTypeBytes", func(t *testing.T) { - t.Parallel() - for _, test := range cases { - v, err := genericParseType[[]byte](test.str) - if test.err == nil { - require.NoError(t, err) - } else { - require.ErrorIs(t, err, test.err) - } - require.Equal(t, test.value, v) - } - }) -} - -// go test -run Test_GenericParseTypeString -func Test_GenericParseTypeString(t *testing.T) { - t.Parallel() - - tests := []string{"john", "doe", "hello", "fiber"} - - for _, test := range tests { - t.Run("test_genericParseTypeString", func(t *testing.T) { - t.Parallel() - v, err := genericParseType[string](test) - require.NoError(t, err) - require.Equal(t, test, v) - }) - } -} - -// go test -run Test_GenericParseTypeBoolean -func Test_GenericParseTypeBoolean(t *testing.T) { - t.Parallel() - - bools := []struct { - str string - value bool - }{ - { - str: "True", - value: true, - }, - { - str: "False", - value: false, - }, - { - str: "true", - value: true, - }, - { - str: "false", - value: false, - }, - } - - t.Run("test_genericParseTypeBoolean", func(t *testing.T) { - t.Parallel() - for _, test := range bools { - v, err := genericParseType[bool](test.str) - require.NoError(t, err) - if test.value { - require.True(t, v) - } else { - require.False(t, v) - } - } - }) -} - -// go test -v -run=^$ -bench=Benchmark_GenericParseTypeInts -benchmem -count=4 -func Benchmark_GenericParseTypeInts(b *testing.B) { - ints := []struct { - value int64 - bits int - }{ - { - value: 0, - bits: 8, - }, - { - value: 1, - bits: 8, - }, - { - value: 2, - bits: 8, - }, - { - value: 3, - bits: 8, - }, - { - value: 4, - bits: 8, - }, - { - value: -1, - bits: 8, - }, - { - value: math.MaxInt8, - bits: 8, - }, - { - value: math.MinInt8, - bits: 8, - }, - { - value: math.MaxInt16, - bits: 16, - }, - { - value: math.MinInt16, - bits: 16, - }, - { - value: math.MaxInt32, - bits: 32, - }, - { - value: math.MinInt32, - bits: 32, - }, - { - value: math.MaxInt64, - bits: 64, - }, - { - value: math.MinInt64, - bits: 64, - }, - } - for _, test := range ints { - benchGenericParseTypeInt[int8](b, "bench_genericParseTypeInt8s", 8, test.bits, test.value) - benchGenericParseTypeInt[int16](b, "bench_genericParseTypeInt16s", 16, test.bits, test.value) - benchGenericParseTypeInt[int32](b, "bench_genericParseTypeInt32s", 32, test.bits, test.value) - benchGenericParseTypeInt[int64](b, "bench_genericParseTypeInt64s", 64, test.bits, test.value) - benchGenericParseTypeInt[int](b, "bench_genericParseTypeInts", int(unsafe.Sizeof(int(0)))*8, test.bits, test.value) - } -} - -func benchGenericParseTypeInt[V GenericTypeInteger](b *testing.B, name string, bits, expectedBits int, expectedVal int64) { - b.Helper() - b.Run(name, func(t *testing.B) { - var v V - var err error - b.ReportAllocs() - b.ResetTimer() - for n := 0; n < b.N; n++ { - v, err = genericParseType[V](strconv.FormatInt(expectedVal, 10)) - } - if expectedBits <= bits { - require.NoError(t, err) - require.Equal(t, V(expectedVal), v) - } else { - require.ErrorIs(t, err, strconv.ErrRange) - } - }) -} - -// go test -v -run=^$ -bench=Benchmark_GenericParseTypeUints -benchmem -count=4 -func Benchmark_GenericParseTypeUints(b *testing.B) { - uints := []struct { - value uint64 - bits int - }{ - { - value: 0, - bits: 8, - }, - { - value: 1, - bits: 8, - }, - { - value: 2, - bits: 8, - }, - { - value: 3, - bits: 8, - }, - { - value: 4, - bits: 8, - }, - { - value: math.MaxUint8, - bits: 8, - }, - { - value: math.MaxUint16, - bits: 16, - }, - { - value: math.MaxUint16, - bits: 16, - }, - { - value: math.MaxUint32, - bits: 32, - }, - { - value: math.MaxUint64, - bits: 64, - }, - } - - for _, test := range uints { - benchGenericParseTypeUInt[uint8](b, "benchmark_genericParseTypeUint8s", 8, test.bits, test.value) - benchGenericParseTypeUInt[uint16](b, "benchmark_genericParseTypeUint16s", 16, test.bits, test.value) - benchGenericParseTypeUInt[uint32](b, "benchmark_genericParseTypeUint32s", 32, test.bits, test.value) - benchGenericParseTypeUInt[uint64](b, "benchmark_genericParseTypeUint64s", 64, test.bits, test.value) - benchGenericParseTypeUInt[uint](b, "benchmark_genericParseTypeUints", int(unsafe.Sizeof(uint(0)))*8, test.bits, test.value) - } -} - -func benchGenericParseTypeUInt[V GenericTypeInteger](b *testing.B, name string, bits, expectedBits int, expectedVal uint64) { - b.Helper() - b.Run(name, func(t *testing.B) { - var v V - var err error - b.ReportAllocs() - b.ResetTimer() - for n := 0; n < b.N; n++ { - v, err = genericParseType[V](strconv.FormatUint(expectedVal, 10)) - } - if expectedBits <= bits { - require.NoError(t, err) - require.Equal(t, V(expectedVal), v) - } else { - require.ErrorIs(t, err, strconv.ErrRange) - } - }) -} - -// go test -v -run=^$ -bench=Benchmark_GenericParseTypeFloats -benchmem -count=4 -func Benchmark_GenericParseTypeFloats(b *testing.B) { - floats := []struct { - str string - value float64 - }{ - { - value: 3.1415, - str: "3.1415", - }, - { - value: 1.234, - str: "1.234", - }, - { - value: 2, - str: "2", - }, - { - value: 3, - str: "3", - }, - } - - for _, test := range floats { - b.Run("benchmark_genericParseTypeFloat32s", func(t *testing.B) { - var v float32 - var err error - b.ReportAllocs() - b.ResetTimer() - for n := 0; n < b.N; n++ { - v, err = genericParseType[float32](test.str) - } - require.NoError(t, err) - require.InEpsilon(t, float32(test.value), v, epsilon) - }) - } - - for _, test := range floats { - b.Run("test_genericParseTypeFloat64s", func(t *testing.B) { - var v float64 - var err error - b.ReportAllocs() - b.ResetTimer() - for n := 0; n < b.N; n++ { - v, err = genericParseType[float64](test.str) - } - require.NoError(t, err) - require.InEpsilon(t, test.value, v, epsilon) - }) - } -} - -// go test -v -run=^$ -bench=Benchmark_GenericParseTypeBytes -benchmem -count=4 -func Benchmark_GenericParseTypeBytes(b *testing.B) { - cases := []struct { - str string - err error - value []byte - }{ - { - value: []byte("alex"), - str: "alex", - }, - { - value: []byte("32.23"), - str: "32.23", - }, - { - value: []byte("john"), - str: "john", - }, - { - value: []byte(nil), - str: "", - err: errParsedEmptyBytes, - }, - } - - for _, test := range cases { - b.Run("benchmark_genericParseTypeBytes", func(b *testing.B) { - var v []byte - var err error - b.ReportAllocs() - b.ResetTimer() - for n := 0; n < b.N; n++ { - v, err = genericParseType[[]byte](test.str) - } - if test.err == nil { - require.NoError(b, err) - } else { - require.ErrorIs(b, err, test.err) - } - require.Equal(b, test.value, v) - }) - } -} - -// go test -v -run=^$ -bench=Benchmark_GenericParseTypeString -benchmem -count=4 -func Benchmark_GenericParseTypeString(b *testing.B) { - tests := []string{"john", "doe", "hello", "fiber"} - - for _, test := range tests { - b.Run("benchmark_genericParseTypeString", func(b *testing.B) { - var v string - var err error - b.ReportAllocs() - b.ResetTimer() - for n := 0; n < b.N; n++ { - v, err = genericParseType[string](test) - } - require.NoError(b, err) - require.Equal(b, test, v) - }) - } -} - -// go test -v -run=^$ -bench=Benchmark_GenericParseTypeBoolean -benchmem -count=4 -func Benchmark_GenericParseTypeBoolean(b *testing.B) { - bools := []struct { - str string - value bool - }{ - { - str: "True", - value: true, - }, - { - str: "False", - value: false, - }, - { - str: "true", - value: true, - }, - { - str: "false", - value: false, - }, - } - - for _, test := range bools { - b.Run("benchmark_genericParseTypeBoolean", func(b *testing.B) { - var v bool - var err error - b.ReportAllocs() - b.ResetTimer() - for n := 0; n < b.N; n++ { - v, err = genericParseType[bool](test.str) - } - require.NoError(b, err) - if test.value { - require.True(b, v) - } else { - require.False(b, v) - } - }) - } -} - // go test -run Test_Ctx_Drop -v func Test_Ctx_Drop(t *testing.T) { t.Parallel() diff --git a/helpers_test.go b/helpers_test.go index ee2c25ab4b0..6e89c9e04a7 100644 --- a/helpers_test.go +++ b/helpers_test.go @@ -5,9 +5,12 @@ package fiber import ( + "math" + "strconv" "strings" "testing" "time" + "unsafe" "github.com/gofiber/utils/v2" "github.com/stretchr/testify/require" @@ -666,3 +669,647 @@ func Benchmark_SlashRecognition(b *testing.B) { require.True(b, result) }) } + +type testGenericParseTypeIntCase struct { + value int64 + bits int +} + +// go test -run Test_GenericParseTypeInts +func Test_GenericParseTypeInts(t *testing.T) { + t.Parallel() + ints := []testGenericParseTypeIntCase{ + { + value: 0, + bits: 8, + }, + { + value: 1, + bits: 8, + }, + { + value: 2, + bits: 8, + }, + { + value: 3, + bits: 8, + }, + { + value: 4, + bits: 8, + }, + { + value: -1, + bits: 8, + }, + { + value: math.MaxInt8, + bits: 8, + }, + { + value: math.MinInt8, + bits: 8, + }, + { + value: math.MaxInt16, + bits: 16, + }, + { + value: math.MinInt16, + bits: 16, + }, + { + value: math.MaxInt32, + bits: 32, + }, + { + value: math.MinInt32, + bits: 32, + }, + { + value: math.MaxInt64, + bits: 64, + }, + { + value: math.MinInt64, + bits: 64, + }, + } + + testGenericTypeInt[int8](t, "test_genericParseTypeInt8s", ints) + testGenericTypeInt[int16](t, "test_genericParseTypeInt16s", ints) + testGenericTypeInt[int32](t, "test_genericParseTypeInt32s", ints) + testGenericTypeInt[int64](t, "test_genericParseTypeInt64s", ints) + testGenericTypeInt[int](t, "test_genericParseTypeInts", ints) +} + +func testGenericTypeInt[V GenericTypeInteger](t *testing.T, name string, cases []testGenericParseTypeIntCase) { + t.Helper() + t.Run(name, func(t *testing.T) { + t.Parallel() + for _, test := range cases { + v, err := genericParseType[V](strconv.FormatInt(test.value, 10)) + if test.bits <= int(unsafe.Sizeof(V(0)))*8 { + require.NoError(t, err) + require.Equal(t, V(test.value), v) + } else { + require.ErrorIs(t, err, strconv.ErrRange) + } + } + testGenericParseError[V](t) + }) +} + +type testGenericParseTypeUintCase struct { + value uint64 + bits int +} + +// go test -run Test_GenericParseTypeUints +func Test_GenericParseTypeUints(t *testing.T) { + t.Parallel() + uints := []testGenericParseTypeUintCase{ + { + value: 0, + bits: 8, + }, + { + value: 1, + bits: 8, + }, + { + value: 2, + bits: 8, + }, + { + value: 3, + bits: 8, + }, + { + value: 4, + bits: 8, + }, + { + value: math.MaxUint8, + bits: 8, + }, + { + value: math.MaxUint16, + bits: 16, + }, + { + value: math.MaxUint16, + bits: 16, + }, + { + value: math.MaxUint32, + bits: 32, + }, + { + value: math.MaxUint64, + bits: 64, + }, + } + + testGenericTypeUint[uint8](t, "test_genericParseTypeUint8s", uints) + testGenericTypeUint[uint16](t, "test_genericParseTypeUint16s", uints) + testGenericTypeUint[uint32](t, "test_genericParseTypeUint32s", uints) + testGenericTypeUint[uint64](t, "test_genericParseTypeUint64s", uints) + testGenericTypeUint[uint](t, "test_genericParseTypeUints", uints) +} + +func testGenericTypeUint[V GenericTypeInteger](t *testing.T, name string, cases []testGenericParseTypeUintCase) { + t.Helper() + t.Run(name, func(t *testing.T) { + t.Parallel() + for _, test := range cases { + v, err := genericParseType[V](strconv.FormatUint(test.value, 10)) + if test.bits <= int(unsafe.Sizeof(V(0)))*8 { + require.NoError(t, err) + require.Equal(t, V(test.value), v) + } else { + require.ErrorIs(t, err, strconv.ErrRange) + } + } + testGenericParseError[V](t) + }) +} + +// go test -run Test_GenericParseTypeFloats +func Test_GenericParseTypeFloats(t *testing.T) { + t.Parallel() + + floats := []struct { + str string + value float64 + }{ + { + value: 3.1415, + str: "3.1415", + }, + { + value: 1.234, + str: "1.234", + }, + { + value: 2, + str: "2", + }, + { + value: 3, + str: "3", + }, + } + + t.Run("test_genericParseTypeFloat32s", func(t *testing.T) { + t.Parallel() + for _, test := range floats { + v, err := genericParseType[float32](test.str) + require.NoError(t, err) + require.InEpsilon(t, float32(test.value), v, epsilon) + } + testGenericParseError[float32](t) + }) + + t.Run("test_genericParseTypeFloat64s", func(t *testing.T) { + t.Parallel() + for _, test := range floats { + v, err := genericParseType[float64](test.str) + require.NoError(t, err) + require.InEpsilon(t, test.value, v, epsilon) + } + testGenericParseError[float64](t) + }) +} + +// go test -run Test_GenericParseTypeBytes +func Test_GenericParseTypeBytes(t *testing.T) { + t.Parallel() + + cases := []struct { + str string + err error + value []byte + }{ + { + value: []byte("alex"), + str: "alex", + }, + { + value: []byte("32.23"), + str: "32.23", + }, + { + value: []byte("john"), + str: "john", + }, + { + value: []byte(nil), + str: "", + err: errParsedEmptyBytes, + }, + } + + t.Run("test_genericParseTypeBytes", func(t *testing.T) { + t.Parallel() + for _, test := range cases { + v, err := genericParseType[[]byte](test.str) + if test.err == nil { + require.NoError(t, err) + } else { + require.ErrorIs(t, err, test.err) + } + require.Equal(t, test.value, v) + } + }) +} + +// go test -run Test_GenericParseTypeString +func Test_GenericParseTypeString(t *testing.T) { + t.Parallel() + + tests := []string{"john", "doe", "hello", "fiber"} + + for _, test := range tests { + t.Run("test_genericParseTypeString", func(t *testing.T) { + t.Parallel() + v, err := genericParseType[string](test) + require.NoError(t, err) + require.Equal(t, test, v) + }) + } +} + +// go test -run Test_GenericParseTypeBoolean +func Test_GenericParseTypeBoolean(t *testing.T) { + t.Parallel() + + bools := []struct { + str string + value bool + }{ + { + str: "True", + value: true, + }, + { + str: "False", + value: false, + }, + { + str: "true", + value: true, + }, + { + str: "false", + value: false, + }, + } + + t.Run("test_genericParseTypeBoolean", func(t *testing.T) { + t.Parallel() + for _, test := range bools { + v, err := genericParseType[bool](test.str) + require.NoError(t, err) + if test.value { + require.True(t, v) + } else { + require.False(t, v) + } + } + testGenericParseError[bool](t) + }) +} + +func testGenericParseError[V GenericType](t *testing.T) { + t.Helper() + var expected V + v, err := genericParseType[V]("invalid-string") + require.Error(t, err) + require.Equal(t, expected, v) +} + +// go test -v -run=^$ -bench=Benchmark_GenericParseTypeInts -benchmem -count=4 +func Benchmark_GenericParseTypeInts(b *testing.B) { + ints := []testGenericParseTypeIntCase{ + { + value: 0, + bits: 8, + }, + { + value: 1, + bits: 8, + }, + { + value: 2, + bits: 8, + }, + { + value: 3, + bits: 8, + }, + { + value: 4, + bits: 8, + }, + { + value: -1, + bits: 8, + }, + { + value: math.MaxInt8, + bits: 8, + }, + { + value: math.MinInt8, + bits: 8, + }, + { + value: math.MaxInt16, + bits: 16, + }, + { + value: math.MinInt16, + bits: 16, + }, + { + value: math.MaxInt32, + bits: 32, + }, + { + value: math.MinInt32, + bits: 32, + }, + { + value: math.MaxInt64, + bits: 64, + }, + { + value: math.MinInt64, + bits: 64, + }, + } + for _, test := range ints { + benchGenericParseTypeInt[int8](b, "bench_genericParseTypeInt8s", test) + benchGenericParseTypeInt[int16](b, "bench_genericParseTypeInt16s", test) + benchGenericParseTypeInt[int32](b, "bench_genericParseTypeInt32s", test) + benchGenericParseTypeInt[int64](b, "bench_genericParseTypeInt64s", test) + benchGenericParseTypeInt[int](b, "bench_genericParseTypeInts", test) + } +} + +func benchGenericParseTypeInt[V GenericTypeInteger](b *testing.B, name string, test testGenericParseTypeIntCase) { + b.Helper() + b.Run(name, func(t *testing.B) { + var v V + var err error + b.ReportAllocs() + b.ResetTimer() + for n := 0; n < b.N; n++ { + v, err = genericParseType[V](strconv.FormatInt(test.value, 10)) + } + if test.bits <= int(unsafe.Sizeof(V(0)))*8 { + require.NoError(t, err) + require.Equal(t, V(test.value), v) + } else { + require.ErrorIs(t, err, strconv.ErrRange) + } + }) +} + +// go test -v -run=^$ -bench=Benchmark_GenericParseTypeUints -benchmem -count=4 +func Benchmark_GenericParseTypeUints(b *testing.B) { + uints := []struct { + value uint64 + bits int + }{ + { + value: 0, + bits: 8, + }, + { + value: 1, + bits: 8, + }, + { + value: 2, + bits: 8, + }, + { + value: 3, + bits: 8, + }, + { + value: 4, + bits: 8, + }, + { + value: math.MaxUint8, + bits: 8, + }, + { + value: math.MaxUint16, + bits: 16, + }, + { + value: math.MaxUint16, + bits: 16, + }, + { + value: math.MaxUint32, + bits: 32, + }, + { + value: math.MaxUint64, + bits: 64, + }, + } + + for _, test := range uints { + benchGenericParseTypeUInt[uint8](b, "benchmark_genericParseTypeUint8s", test) + benchGenericParseTypeUInt[uint16](b, "benchmark_genericParseTypeUint16s", test) + benchGenericParseTypeUInt[uint32](b, "benchmark_genericParseTypeUint32s", test) + benchGenericParseTypeUInt[uint64](b, "benchmark_genericParseTypeUint64s", test) + benchGenericParseTypeUInt[uint](b, "benchmark_genericParseTypeUints", test) + } +} + +func benchGenericParseTypeUInt[V GenericTypeInteger](b *testing.B, name string, test testGenericParseTypeUintCase) { + b.Helper() + b.Run(name, func(t *testing.B) { + var v V + var err error + b.ReportAllocs() + b.ResetTimer() + for n := 0; n < b.N; n++ { + v, err = genericParseType[V](strconv.FormatUint(test.value, 10)) + } + if test.bits <= int(unsafe.Sizeof(V(0)))*8 { + require.NoError(t, err) + require.Equal(t, V(test.value), v) + } else { + require.ErrorIs(t, err, strconv.ErrRange) + } + }) +} + +// go test -v -run=^$ -bench=Benchmark_GenericParseTypeFloats -benchmem -count=4 +func Benchmark_GenericParseTypeFloats(b *testing.B) { + floats := []struct { + str string + value float64 + }{ + { + value: 3.1415, + str: "3.1415", + }, + { + value: 1.234, + str: "1.234", + }, + { + value: 2, + str: "2", + }, + { + value: 3, + str: "3", + }, + } + + for _, test := range floats { + b.Run("benchmark_genericParseTypeFloat32s", func(t *testing.B) { + var v float32 + var err error + b.ReportAllocs() + b.ResetTimer() + for n := 0; n < b.N; n++ { + v, err = genericParseType[float32](test.str) + } + require.NoError(t, err) + require.InEpsilon(t, float32(test.value), v, epsilon) + }) + } + + for _, test := range floats { + b.Run("test_genericParseTypeFloat64s", func(t *testing.B) { + var v float64 + var err error + b.ReportAllocs() + b.ResetTimer() + for n := 0; n < b.N; n++ { + v, err = genericParseType[float64](test.str) + } + require.NoError(t, err) + require.InEpsilon(t, test.value, v, epsilon) + }) + } +} + +// go test -v -run=^$ -bench=Benchmark_GenericParseTypeBytes -benchmem -count=4 +func Benchmark_GenericParseTypeBytes(b *testing.B) { + cases := []struct { + str string + err error + value []byte + }{ + { + value: []byte("alex"), + str: "alex", + }, + { + value: []byte("32.23"), + str: "32.23", + }, + { + value: []byte("john"), + str: "john", + }, + { + value: []byte(nil), + str: "", + err: errParsedEmptyBytes, + }, + } + + for _, test := range cases { + b.Run("benchmark_genericParseTypeBytes", func(b *testing.B) { + var v []byte + var err error + b.ReportAllocs() + b.ResetTimer() + for n := 0; n < b.N; n++ { + v, err = genericParseType[[]byte](test.str) + } + if test.err == nil { + require.NoError(b, err) + } else { + require.ErrorIs(b, err, test.err) + } + require.Equal(b, test.value, v) + }) + } +} + +// go test -v -run=^$ -bench=Benchmark_GenericParseTypeString -benchmem -count=4 +func Benchmark_GenericParseTypeString(b *testing.B) { + tests := []string{"john", "doe", "hello", "fiber"} + + for _, test := range tests { + b.Run("benchmark_genericParseTypeString", func(b *testing.B) { + var v string + var err error + b.ReportAllocs() + b.ResetTimer() + for n := 0; n < b.N; n++ { + v, err = genericParseType[string](test) + } + require.NoError(b, err) + require.Equal(b, test, v) + }) + } +} + +// go test -v -run=^$ -bench=Benchmark_GenericParseTypeBoolean -benchmem -count=4 +func Benchmark_GenericParseTypeBoolean(b *testing.B) { + bools := []struct { + str string + value bool + }{ + { + str: "True", + value: true, + }, + { + str: "False", + value: false, + }, + { + str: "true", + value: true, + }, + { + str: "false", + value: false, + }, + } + + for _, test := range bools { + b.Run("benchmark_genericParseTypeBoolean", func(b *testing.B) { + var v bool + var err error + b.ReportAllocs() + b.ResetTimer() + for n := 0; n < b.N; n++ { + v, err = genericParseType[bool](test.str) + } + require.NoError(b, err) + if test.value { + require.True(b, v) + } else { + require.False(b, v) + } + }) + } +} From c6392688dcd72fbe609c2e6980cc22ab014d8feb Mon Sep 17 00:00:00 2001 From: Kashiwa <13825170+ksw2000@users.noreply.github.com> Date: Mon, 26 May 2025 00:28:18 +0800 Subject: [PATCH 5/6] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20refact:=20change=20the?= =?UTF-8?q?=20benchmark=20name?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- helpers_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/helpers_test.go b/helpers_test.go index 6e89c9e04a7..e2d5ac2b420 100644 --- a/helpers_test.go +++ b/helpers_test.go @@ -1193,7 +1193,7 @@ func Benchmark_GenericParseTypeFloats(b *testing.B) { } for _, test := range floats { - b.Run("test_genericParseTypeFloat64s", func(t *testing.B) { + b.Run("benchmark_genericParseTypeFloat64s", func(t *testing.B) { var v float64 var err error b.ReportAllocs() From 97e5d83585d380b818aec3e63ded4cd30a238b3a Mon Sep 17 00:00:00 2001 From: Kashiwa <13825170+ksw2000@users.noreply.github.com> Date: Mon, 26 May 2025 00:29:29 +0800 Subject: [PATCH 6/6] =?UTF-8?q?=F0=9F=9A=A8=20test:=20remove=20the=20dupli?= =?UTF-8?q?cated=20maxUint16=20test=20case?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- helpers_test.go | 4 ---- 1 file changed, 4 deletions(-) diff --git a/helpers_test.go b/helpers_test.go index e2d5ac2b420..0e222a5ffe5 100644 --- a/helpers_test.go +++ b/helpers_test.go @@ -798,10 +798,6 @@ func Test_GenericParseTypeUints(t *testing.T) { value: math.MaxUint16, bits: 16, }, - { - value: math.MaxUint16, - bits: 16, - }, { value: math.MaxUint32, bits: 32,