Files
2026-07-13 11:58:46 +08:00

285 lines
6.4 KiB
Go

// Copyright 2025 The Hugo Authors. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package hreflect
import (
"math"
"reflect"
"testing"
qt "github.com/frankban/quicktest"
"github.com/gohugoio/hugo/htesting/hqt"
)
func TestToFuncs(t *testing.T) {
c := qt.New(t)
c.Assert(ToInt64(reflect.ValueOf(int(42))), qt.Equals, int64(42))
c.Assert(ToFloat64(reflect.ValueOf(float32(3.14))), hqt.IsSameFloat64, float64(3.14))
c.Assert(ToString(reflect.ValueOf("hello")), qt.Equals, "hello")
}
func TestConvertIfPossible(t *testing.T) {
c := qt.New(t)
for _, test := range []struct {
name string
value any
typ any
expected any
ok bool
}{
// From uint to int.
{
name: "uint64(math.MaxUint64) to int16",
value: uint64(math.MaxUint64),
typ: int16(0),
ok: false, // overflow
},
{
name: "uint64(math.MaxUint64) to int64",
value: uint64(math.MaxUint64),
typ: int64(0),
ok: false, // overflow
},
{
name: "uint64(math.MaxInt16) to int16",
value: uint64(math.MaxInt16),
typ: int64(0),
ok: true,
expected: int64(math.MaxInt16),
},
// From int to int.
{
name: "int64(math.MaxInt64) to int16",
value: int64(math.MaxInt64),
typ: int16(0),
ok: false, // overflow
},
{
name: "int64(math.MaxInt16) to int",
value: int64(math.MaxInt16),
typ: int(0),
ok: true,
expected: int(math.MaxInt16),
},
{
name: "int64(math.MaxInt16) to int",
value: int64(math.MaxInt16),
typ: int(0),
ok: true,
expected: int(math.MaxInt16),
},
// From float64 to int.
{
name: "float64(1.5) to int",
value: float64(1.5),
typ: int(0),
ok: false, // loss of precision
},
{
name: "float64(1.0) to int",
value: float64(1.0),
typ: int(0),
ok: true,
expected: int(1),
},
{
name: "float64(math.MaxInt16+1) to int16",
value: float64(math.MaxInt16 + 1),
typ: int16(0),
ok: false, // overflow
},
{
name: "float64(math.MaxFloat64) to int64",
value: float64(math.MaxFloat64),
typ: int64(0),
ok: false, // overflow
},
{
name: "float64(32767) to int16",
value: float64(32767),
typ: int16(0),
ok: true,
expected: int16(32767),
},
// From float32 to int.
{
name: "float32(1.5) to int",
value: float32(1.5),
typ: int(0),
ok: false, // loss of precision
},
{
name: "float32(1.0) to int",
value: float32(1.0),
typ: int(0),
ok: true,
expected: int(1),
},
{
name: "float32(math.MaxFloat32) to int16",
value: float32(math.MaxFloat32),
typ: int16(0),
ok: false, // overflow
},
{
name: "float32(math.MaxFloat32) to int64",
value: float32(math.MaxFloat32),
typ: int64(0),
ok: false, // overflow
},
{
name: "float32(math.MaxInt16) to int16",
value: float32(math.MaxInt16),
typ: int16(0),
ok: true,
expected: int16(32767),
},
{
name: "float32(math.MaxInt16+1) to int16",
value: float32(math.MaxInt16 + 1),
typ: int16(0),
ok: false, // overflow
},
// Int to float.
{
name: "int16(32767) to float32",
value: int16(32767),
typ: float32(0),
ok: true,
expected: float32(32767),
},
{
name: "int64(32767) to float32",
value: int64(32767),
typ: float32(0),
ok: true,
expected: float32(32767),
},
{
name: "int64(math.MaxInt64) to float32",
value: int64(math.MaxInt64),
typ: float32(0),
ok: true,
expected: float32(math.MaxInt64),
},
{
name: "int64(math.MaxInt64) to float64",
value: int64(math.MaxInt64),
typ: float64(0),
ok: true,
expected: float64(math.MaxInt64),
},
// Int to uint.
{
name: "int16(32767) to uint16",
value: int16(32767),
typ: uint16(0),
ok: true,
expected: uint16(32767),
},
{
name: "int16(32767) to uint8",
value: int16(32767),
typ: uint8(0),
ok: false,
},
{
name: "float64(3.14) to uint64",
value: float64(3.14),
typ: uint64(0),
ok: false,
},
{
name: "float64(3.0) to uint64",
value: float64(3.0),
typ: uint64(0),
ok: true,
expected: uint64(3),
},
// From uint to float.
{
name: "uint64(math.MaxInt16) to float64",
value: uint64(math.MaxInt16),
typ: float64(0),
ok: true,
expected: float64(math.MaxInt16),
},
// Float to float.
{
name: "float64(3.14) to float32",
value: float64(3.14),
typ: float32(0),
ok: true,
expected: float32(3.14),
},
{
name: "float32(3.14) to float64",
value: float32(3.14),
typ: float64(0),
ok: true,
expected: float64(3.14),
},
{
name: "float64(3.14) to float64",
value: float64(3.14),
typ: float64(0),
ok: true,
expected: float64(3.14),
},
} {
v, ok := ConvertIfPossible(reflect.ValueOf(test.value), reflect.TypeOf(test.typ))
c.Assert(ok, qt.Equals, test.ok, qt.Commentf("test case: %s", test.name))
if test.ok {
c.Assert(v.Interface(), hqt.IsSameNumber, test.expected, qt.Commentf("test case: %s", test.name))
}
}
}
func TestConvertIfPossibleMisc(t *testing.T) {
c := qt.New(t)
type s string
var (
i = int32(42)
i64 = int64(i)
iv any = i
ip = &i
inil any = (*int32)(nil)
shello = s("hello")
)
convertOK := func(v any, typ any) any {
rv, ok := ConvertIfPossible(reflect.ValueOf(v), reflect.TypeOf(typ))
c.Assert(ok, qt.IsTrue)
return rv.Interface()
}
c.Assert(convertOK(shello, ""), qt.Equals, "hello")
c.Assert(convertOK(ip, int64(0)), qt.Equals, i64)
c.Assert(convertOK(iv, int64(0)), qt.Equals, i64)
c.Assert(convertOK(inil, int64(0)), qt.Equals, int64(0))
}
func BenchmarkToInt64(b *testing.B) {
v := reflect.ValueOf(int(42))
for b.Loop() {
ToInt64(v)
}
}