142 lines
4.2 KiB
Go
142 lines
4.2 KiB
Go
package toml
|
|
|
|
import (
|
|
"math"
|
|
"reflect"
|
|
"strconv"
|
|
"strings"
|
|
"testing"
|
|
)
|
|
|
|
func TestDecodeFixedArrayRoundTrip(t *testing.T) {
|
|
type Item struct {
|
|
Value uint16 `toml:"value"`
|
|
}
|
|
type Config struct {
|
|
Numbers [3]int64 `toml:"numbers"`
|
|
Items [2]*Item `toml:"items"`
|
|
Matrix [2][2]int8 `toml:"matrix"`
|
|
Empty [0]string `toml:"empty"`
|
|
}
|
|
want := Config{
|
|
Numbers: [3]int64{math.MinInt64, 0, math.MaxInt64},
|
|
Items: [2]*Item{{1}, {math.MaxUint16}},
|
|
Matrix: [2][2]int8{{-128, 127}, {0, 1}},
|
|
}
|
|
data, err := Marshal(want)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
var got Config
|
|
if err := Unmarshal(data, &got); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if !reflect.DeepEqual(got, want) {
|
|
t.Fatalf("got %#v, want %#v", got, want)
|
|
}
|
|
for _, input := range []any{[]any{1}, []any{1, 2, 3}, []any{1, 256}} {
|
|
dst := [2]uint8{7, 8}
|
|
if err := Decode(input, &dst); err == nil || dst != [2]uint8{7, 8} {
|
|
t.Fatalf("invalid array changed target: %v (%v)", dst, err)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestDecodePointerDefaultsAndRollback(t *testing.T) {
|
|
type Settings struct {
|
|
Port uint16 `toml:"port"`
|
|
Name string `toml:"name"`
|
|
}
|
|
original := &Settings{Port: 8080, Name: "default"}
|
|
dst := original
|
|
if err := Decode(map[string]any{"name": "updated"}, &dst); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if dst.Port != 8080 || dst.Name != "updated" || dst == original || original.Name != "default" {
|
|
t.Fatalf("defaults or original pointer changed: dst=%+v original=%+v", dst, original)
|
|
}
|
|
type Config struct {
|
|
Settings *Settings
|
|
Map map[string]int
|
|
Slice []int
|
|
Limit uint8
|
|
}
|
|
cfg := Config{original, map[string]int{"old": 1}, []int{2}, 3}
|
|
err := Decode(map[string]any{
|
|
"Settings": map[string]any{"port": 9090},
|
|
"Map": map[string]any{"new": 4},
|
|
"Slice": []any{5},
|
|
"Limit": 256,
|
|
}, &cfg)
|
|
if err == nil || cfg.Settings != original || *original != (Settings{8080, "default"}) ||
|
|
!reflect.DeepEqual(cfg.Map, map[string]int{"old": 1}) || !reflect.DeepEqual(cfg.Slice, []int{2}) || cfg.Limit != 3 {
|
|
t.Fatalf("partial update: %+v (%v)", cfg, err)
|
|
}
|
|
}
|
|
|
|
func TestDecodeCyclicInputErrors(t *testing.T) {
|
|
cycle := map[string]any{}
|
|
cycle["self"] = cycle
|
|
var number uint64
|
|
if err := Decode(cycle, &number); err == nil {
|
|
t.Fatal("accepted cyclic map as unsigned number")
|
|
}
|
|
type Node struct {
|
|
Next *Node `toml:"self"`
|
|
}
|
|
var node Node
|
|
if err := Decode(cycle, &node); err == nil || node.Next != nil {
|
|
t.Fatalf("recursive decode must fail without publication: %v", err)
|
|
}
|
|
}
|
|
|
|
func TestNumericKeyRangeIndependence(t *testing.T) {
|
|
for _, key := range []string{"0", "-1", "+1", "9223372036854775808", "-9223372036854775809", strings.Repeat("9", 100)} {
|
|
if _, err := Marshal(map[string]any{key: 1}); err == nil {
|
|
t.Errorf("encoded numeric key %q", key)
|
|
}
|
|
if _, err := NewParser([]byte(strconv.Quote(key) + " = 1")).Parse(); err == nil {
|
|
t.Errorf("parsed numeric key %q", key)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestDecodeIntegerWidths(t *testing.T) {
|
|
for _, typ := range []reflect.Type{
|
|
reflect.TypeFor[int](), reflect.TypeFor[int8](), reflect.TypeFor[int16](), reflect.TypeFor[int32](), reflect.TypeFor[int64](),
|
|
reflect.TypeFor[uint](), reflect.TypeFor[uint8](), reflect.TypeFor[uint16](), reflect.TypeFor[uint32](), reflect.TypeFor[uint64](),
|
|
} {
|
|
t.Run(typ.String(), func(t *testing.T) {
|
|
bits := typ.Bits()
|
|
signed := typ.Kind() >= reflect.Int && typ.Kind() <= reflect.Int64
|
|
max := uint64(math.MaxUint64) >> (64 - bits)
|
|
if signed {
|
|
max >>= 1
|
|
}
|
|
dst := reflect.New(typ)
|
|
if err := Decode(max, dst.Interface()); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if max < math.MaxUint64 {
|
|
before := dst.Elem().Interface()
|
|
if err := Decode(max+1, dst.Interface()); err == nil || dst.Elem().Interface() != before {
|
|
t.Fatalf("overflow was accepted or mutated destination: %v", err)
|
|
}
|
|
}
|
|
if signed {
|
|
min := -int64(max) - 1
|
|
if err := Decode(min, dst.Interface()); err != nil || dst.Elem().Int() != min {
|
|
t.Fatalf("minimum changed: %v", err)
|
|
}
|
|
if min > math.MinInt64 {
|
|
if err := Decode(min-1, dst.Interface()); err == nil || dst.Elem().Int() != min {
|
|
t.Fatalf("underflow accepted: %v", err)
|
|
}
|
|
}
|
|
} else if err := Decode(int64(-1), dst.Interface()); err == nil {
|
|
t.Fatal("negative signed integer accepted")
|
|
}
|
|
})
|
|
}
|
|
}
|