2026-04-22 01:52:30 +08:00
|
|
|
|
package cast_test
|
|
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
|
"math"
|
|
|
|
|
|
"testing"
|
|
|
|
|
|
|
|
|
|
|
|
"apigo.cc/go/cast"
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
// --- 黑盒测试 (Black-box / Edge Cases) ---
|
|
|
|
|
|
|
|
|
|
|
|
func TestEdgeCases(t *testing.T) {
|
|
|
|
|
|
// 1. 极端数值
|
2026-05-01 00:11:40 +08:00
|
|
|
|
if cast.Int64(math.MaxInt64) != math.MaxInt64 {
|
|
|
|
|
|
t.Error("MaxInt64 failed")
|
|
|
|
|
|
}
|
|
|
|
|
|
if cast.Float64("1.7976931348623157e+308") == 0 {
|
|
|
|
|
|
t.Error("MaxFloat64 string failed")
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-04-22 01:52:30 +08:00
|
|
|
|
// 2. 各种非法格式字符串
|
2026-05-01 00:11:40 +08:00
|
|
|
|
if cast.Int("abc") != 0 {
|
|
|
|
|
|
t.Error("Invalid string to int should be 0")
|
|
|
|
|
|
}
|
|
|
|
|
|
if cast.Float("1.2.3.4") != 0 {
|
|
|
|
|
|
t.Error("Invalid float string should be 0")
|
|
|
|
|
|
}
|
|
|
|
|
|
if cast.Bool("not_a_bool") != false {
|
|
|
|
|
|
t.Error("Invalid bool string should be false")
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-04-22 01:52:30 +08:00
|
|
|
|
// 3. 深度嵌套与空指针
|
|
|
|
|
|
var p ***int
|
2026-05-01 00:11:40 +08:00
|
|
|
|
if cast.Int(p) != 0 {
|
|
|
|
|
|
t.Error("Deep nil pointer to int failed")
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-04-22 01:52:30 +08:00
|
|
|
|
// 4. JSON 异常输入
|
2026-05-04 09:22:34 +08:00
|
|
|
|
if err := cast.UnmarshalJSON("{invalid_json}", nil); err == nil {
|
2026-05-01 00:41:23 +08:00
|
|
|
|
t.Log("UnmarshalJSON handled invalid input")
|
2026-05-01 00:11:40 +08:00
|
|
|
|
}
|
2026-04-22 01:52:30 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// --- 性能测试 (Performance / Benchmarks) ---
|
|
|
|
|
|
|
|
|
|
|
|
func BenchmarkInt(b *testing.B) {
|
|
|
|
|
|
val := "123456"
|
|
|
|
|
|
for i := 0; i < b.N; i++ {
|
|
|
|
|
|
_ = cast.Int(val)
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
func BenchmarkIntFromInterface(b *testing.B) {
|
|
|
|
|
|
var val interface{} = 123456
|
|
|
|
|
|
for i := 0; i < b.N; i++ {
|
|
|
|
|
|
_ = cast.Int(val)
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
func BenchmarkString(b *testing.B) {
|
|
|
|
|
|
val := 123456.789
|
|
|
|
|
|
for i := 0; i < b.N; i++ {
|
|
|
|
|
|
_ = cast.String(val)
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-05-01 00:41:23 +08:00
|
|
|
|
func BenchmarkJSON(b *testing.B) {
|
2026-04-22 01:52:30 +08:00
|
|
|
|
type User struct {
|
|
|
|
|
|
ID int `json:"id"`
|
|
|
|
|
|
Name string `json:"name"`
|
|
|
|
|
|
}
|
|
|
|
|
|
u := User{ID: 1, Name: "Benchmark User"}
|
|
|
|
|
|
for i := 0; i < b.N; i++ {
|
2026-05-04 11:11:37 +08:00
|
|
|
|
_ = cast.To[string](u)
|
2026-04-22 01:52:30 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
func BenchmarkIf(b *testing.B) {
|
|
|
|
|
|
for i := 0; i < b.N; i++ {
|
|
|
|
|
|
_ = cast.If(i%2 == 0, "A", "B")
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-05-01 00:11:40 +08:00
|
|
|
|
func BenchmarkSplit(b *testing.B) {
|
2026-04-22 01:52:30 +08:00
|
|
|
|
s := " a, b, c, d, e, f, g "
|
|
|
|
|
|
for i := 0; i < b.N; i++ {
|
2026-05-01 00:11:40 +08:00
|
|
|
|
_ = cast.Split(s, ",")
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-05-04 09:22:34 +08:00
|
|
|
|
func BenchmarkToMap(b *testing.B) {
|
|
|
|
|
|
m := make(map[string]int)
|
|
|
|
|
|
kv := []any{"a", 1, "b", 2, "c", 3, "d", 4, "e", 5}
|
|
|
|
|
|
b.ResetTimer()
|
|
|
|
|
|
for i := 0; i < b.N; i++ {
|
2026-05-04 10:41:34 +08:00
|
|
|
|
cast.FillMap(m, kv)
|
2026-05-04 09:22:34 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
func BenchmarkToSlice(b *testing.B) {
|
|
|
|
|
|
m := map[string]int{"a": 1, "b": 2, "c": 3, "d": 4, "e": 5}
|
|
|
|
|
|
var s []any
|
|
|
|
|
|
b.ResetTimer()
|
|
|
|
|
|
for i := 0; i < b.N; i++ {
|
|
|
|
|
|
s = s[:0]
|
2026-05-04 10:41:34 +08:00
|
|
|
|
cast.FillSlice(&s, m)
|
2026-05-04 09:22:34 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-05-01 00:11:40 +08:00
|
|
|
|
// 1. 测试 Int 转换的快速路径 (命中 switch case)
|
|
|
|
|
|
func BenchmarkInt_FastPath(b *testing.B) {
|
|
|
|
|
|
val := "123456"
|
|
|
|
|
|
b.ResetTimer()
|
|
|
|
|
|
for i := 0; i < b.N; i++ {
|
|
|
|
|
|
_ = cast.Int(val)
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 2. 测试 Int 转换的慢速路径 (退化为反射)
|
|
|
|
|
|
type customInt int
|
|
|
|
|
|
|
|
|
|
|
|
func BenchmarkInt_SlowPath(b *testing.B) {
|
|
|
|
|
|
var val customInt = 123456
|
|
|
|
|
|
b.ResetTimer()
|
|
|
|
|
|
for i := 0; i < b.N; i++ {
|
|
|
|
|
|
// 自定义类型无法命中 switch 的基础类型,会走 reflect.ValueOf
|
|
|
|
|
|
_ = cast.Int(val)
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 3. 测试 String 转换的快速路径
|
|
|
|
|
|
func BenchmarkString_FastPath(b *testing.B) {
|
|
|
|
|
|
val := 123456.789
|
|
|
|
|
|
b.ResetTimer()
|
|
|
|
|
|
for i := 0; i < b.N; i++ {
|
|
|
|
|
|
_ = cast.String(val) // 命中 strconv.FormatFloat,极快
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-05-01 00:41:23 +08:00
|
|
|
|
// 4. 测试 ToJSON 性能 (对比自定义逻辑与标准库)
|
|
|
|
|
|
func BenchmarkToJSON_SimpleStruct(b *testing.B) {
|
2026-05-01 00:11:40 +08:00
|
|
|
|
type User struct {
|
|
|
|
|
|
ID int `json:"id"`
|
|
|
|
|
|
Name string `json:"name"`
|
|
|
|
|
|
}
|
|
|
|
|
|
u := User{ID: 1, Name: "Benchmark User"}
|
|
|
|
|
|
b.ResetTimer()
|
|
|
|
|
|
for i := 0; i < b.N; i++ {
|
2026-05-04 11:11:37 +08:00
|
|
|
|
_ = cast.To[string](u)
|
2026-05-01 00:11:40 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 5. 测试 Goja Map 的清洗性能
|
2026-05-01 00:41:23 +08:00
|
|
|
|
func BenchmarkToJSON_DirtyMap(b *testing.B) {
|
2026-05-01 00:11:40 +08:00
|
|
|
|
// 模拟恶劣数据:包含 any key 的 map
|
|
|
|
|
|
val := map[any]any{
|
|
|
|
|
|
0: "A",
|
|
|
|
|
|
1: map[any]any{"key": "B"},
|
|
|
|
|
|
}
|
|
|
|
|
|
b.ResetTimer()
|
|
|
|
|
|
for i := 0; i < b.N; i++ {
|
2026-05-04 11:11:37 +08:00
|
|
|
|
_ = cast.To[string](val)
|
2026-04-22 01:52:30 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
2026-05-04 14:10:18 +08:00
|
|
|
|
|
|
|
|
|
|
func BenchmarkTo_StructToStruct(b *testing.B) {
|
|
|
|
|
|
type Source struct {
|
|
|
|
|
|
ID int
|
|
|
|
|
|
Name string
|
|
|
|
|
|
Age string
|
|
|
|
|
|
}
|
|
|
|
|
|
type Target struct {
|
|
|
|
|
|
ID int64
|
|
|
|
|
|
Name string
|
|
|
|
|
|
Age int
|
|
|
|
|
|
}
|
|
|
|
|
|
src := Source{ID: 1, Name: "Tom", Age: "18"}
|
|
|
|
|
|
b.ResetTimer()
|
|
|
|
|
|
for i := 0; i < b.N; i++ {
|
|
|
|
|
|
_ = cast.To[Target](src)
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|