v0.1.0 initial commit
This commit is contained in:
@@ -0,0 +1,200 @@
|
||||
package toml
|
||||
|
||||
import (
|
||||
"math"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestDecode_NumericOverflow(t *testing.T) {
|
||||
type T struct {
|
||||
I8 int8 `toml:"i8"`
|
||||
U8 uint8 `toml:"u8"`
|
||||
F32 float32 `toml:"f32"`
|
||||
}
|
||||
var tgt T
|
||||
if err := Decode(map[string]any{"i8": 300}, &tgt); err == nil {
|
||||
t.Errorf("300 into int8 must error, got %d", tgt.I8)
|
||||
}
|
||||
if err := Decode(map[string]any{"u8": 256}, &tgt); err == nil {
|
||||
t.Errorf("256 into uint8 must error, got %d", tgt.U8)
|
||||
}
|
||||
if err := Decode(map[string]any{"f32": 1e300}, &tgt); err == nil {
|
||||
t.Errorf("1e300 into float32 must error, got %g", tgt.F32)
|
||||
}
|
||||
// Boundaries remain valid
|
||||
if err := Decode(map[string]any{"i8": 127, "u8": 255}, &tgt); err != nil {
|
||||
t.Fatalf("boundary decode failed: %v", err)
|
||||
}
|
||||
if tgt.I8 != 127 || tgt.U8 != 255 {
|
||||
t.Errorf("boundary values: %d %d", tgt.I8, tgt.U8)
|
||||
}
|
||||
}
|
||||
|
||||
func TestDecode_UnsupportedKinds(t *testing.T) {
|
||||
type T struct {
|
||||
C complex128 `toml:"v"`
|
||||
}
|
||||
var c T
|
||||
if err := Decode(map[string]any{"v": 1}, &c); err == nil {
|
||||
t.Error("complex128 target must error, not zero-fill")
|
||||
}
|
||||
type T2 struct {
|
||||
Ch chan int `toml:"v"`
|
||||
}
|
||||
var c2 T2
|
||||
if err := Decode(map[string]any{"v": 1}, &c2); err == nil {
|
||||
t.Error("chan target must error")
|
||||
}
|
||||
}
|
||||
|
||||
func TestDecode_NonEmptyInterface(t *testing.T) {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
t.Errorf("input-triggered panic: %v", r)
|
||||
}
|
||||
}()
|
||||
type T struct {
|
||||
R interface{ Read([]byte) (int, error) } `toml:"r"`
|
||||
}
|
||||
var tgt T
|
||||
if err := Decode(map[string]any{"r": map[string]any{}}, &tgt); err == nil {
|
||||
t.Error("map into non-empty interface must error")
|
||||
}
|
||||
}
|
||||
|
||||
func TestDecode_Uint64Wrap(t *testing.T) {
|
||||
type T struct {
|
||||
V int64 `toml:"v"`
|
||||
}
|
||||
var tgt T
|
||||
if err := Decode(map[string]any{"v": uint64(math.MaxUint64)}, &tgt); err == nil {
|
||||
t.Errorf("MaxUint64 into int64 must error, got %d", tgt.V)
|
||||
}
|
||||
if err := Decode(map[string]any{"v": uint(math.MaxUint64)}, &tgt); err == nil {
|
||||
t.Errorf("uint wrap into int64 must error, got %d", tgt.V)
|
||||
}
|
||||
if err := Decode(map[string]any{"v": uint64(42)}, &tgt); err != nil || tgt.V != 42 {
|
||||
t.Errorf("in-range uint64 failed: %v %d", err, tgt.V)
|
||||
}
|
||||
}
|
||||
|
||||
func TestMarshal_FloatFormatting(t *testing.T) {
|
||||
b, err := Marshal(map[string]any{"f": float32(3.14)})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if got := strings.TrimSpace(string(b)); got != "f = 3.14" {
|
||||
t.Errorf("float32 noise digits: %s", got)
|
||||
}
|
||||
|
||||
b, err = Marshal(map[string]any{"f": 1e100})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if got := strings.TrimSpace(string(b)); got != "f = 1e+100" {
|
||||
t.Errorf("digit expansion instead of exponent: %s", got)
|
||||
}
|
||||
var m map[string]any
|
||||
if err := Unmarshal(b, &m); err != nil {
|
||||
t.Fatalf("re-parse of exponent form failed: %v", err)
|
||||
}
|
||||
if m["f"] != 1e100 {
|
||||
t.Errorf("round trip mismatch: %v", m["f"])
|
||||
}
|
||||
|
||||
// Integral floats keep the .0 fixup
|
||||
b, _ = Marshal(map[string]any{"f": 100.0})
|
||||
if got := strings.TrimSpace(string(b)); got != "f = 100.0" {
|
||||
t.Errorf(".0 fixup lost: %s", got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestMarshal_NaNInf(t *testing.T) {
|
||||
for _, v := range []float64{math.NaN(), math.Inf(1), math.Inf(-1)} {
|
||||
if _, err := Marshal(map[string]any{"f": v}); err == nil {
|
||||
t.Errorf("%v must fail to encode", v)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestMarshal_SliceHomogeneity(t *testing.T) {
|
||||
if _, err := Marshal(map[string]any{"x": []any{map[string]any{"a": 1}, 2}}); err == nil {
|
||||
t.Error("mixed table/scalar slice must error")
|
||||
}
|
||||
if _, err := Marshal(map[string]any{"x": []any{1, nil, 2}}); err == nil {
|
||||
t.Error("nil interface element must error")
|
||||
}
|
||||
|
||||
type Item struct {
|
||||
N int `toml:"n"`
|
||||
}
|
||||
b, err := Marshal(map[string]any{"items": []*Item{nil, {N: 1}}})
|
||||
if err != nil {
|
||||
t.Fatalf("nil pointer in table slice must be skipped, not error: %v", err)
|
||||
}
|
||||
if strings.Count(string(b), "[[items]]") != 1 {
|
||||
t.Errorf("expected exactly one [[items]]:\n%s", b)
|
||||
}
|
||||
}
|
||||
|
||||
func TestMarshal_TagSortedKeys(t *testing.T) {
|
||||
type T struct {
|
||||
B int `toml:"z"`
|
||||
Z int `toml:"a"`
|
||||
}
|
||||
b, err := Marshal(T{B: 1, Z: 2})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if got := strings.TrimSpace(string(b)); got != "a = 2\nz = 1" {
|
||||
t.Errorf("keys not sorted by emitted name:\n%s", got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestLexer_UnicodeEscapes(t *testing.T) {
|
||||
var m map[string]any
|
||||
if err := Unmarshal([]byte(`s = "caf\u00E9 \U0001F600"`), &m); err != nil {
|
||||
t.Fatalf("unicode escape parse failed: %v", err)
|
||||
}
|
||||
if m["s"] != "café 😀" {
|
||||
t.Errorf("got %q", m["s"])
|
||||
}
|
||||
|
||||
for _, in := range []string{
|
||||
`s = "\uD800"`, // surrogate
|
||||
`s = "\u12"`, // too few digits
|
||||
`s = "\U00110000"`, // > MaxRune
|
||||
`s = "\uZZZZ"`, // non-hex
|
||||
} {
|
||||
if err := Unmarshal([]byte(in), &m); err == nil {
|
||||
t.Errorf("%s should fail", in)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestParser_InlineTableImmutable(t *testing.T) {
|
||||
for _, in := range []string{
|
||||
"t = { a = 1 }\nt.b = 2",
|
||||
"t = { a = 1 }\n[t]\nb = 2",
|
||||
"t = { a = 1 }\n[t.sub]\nb = 2",
|
||||
} {
|
||||
p := NewParser([]byte(in))
|
||||
if _, err := p.Parse(); err == nil {
|
||||
t.Errorf("inline table extension should fail:\n%s", in)
|
||||
}
|
||||
}
|
||||
// Intra-table dotted keys remain valid
|
||||
var m map[string]any
|
||||
if err := Unmarshal([]byte(`t = { a.b = 1, a.c = 2 }`), &m); err != nil {
|
||||
t.Errorf("dotted keys within inline table must parse: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestParser_ValueDepthLimit(t *testing.T) {
|
||||
input := "x = " + strings.Repeat("[", 5000) + strings.Repeat("]", 5000)
|
||||
p := NewParser([]byte(input))
|
||||
if _, err := p.Parse(); err == nil {
|
||||
t.Error("expected nesting depth error")
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user