fix(encode): pointer table arrays, whole-minute offsets and emission checks
Assisted-by: GLM 5.3
This commit is contained in:
+113
-1
@@ -67,7 +67,7 @@ func TestMarshalFloatSpecials(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestMarshalFloatNormalizesNegativeZero(t *testing.T) {
|
||||
func TestMarshalFloatNormalisesNegativeZero(t *testing.T) {
|
||||
// The output contract normalises negative zero to "0.0".
|
||||
type Cfg struct {
|
||||
Z float64 `toml:"z"`
|
||||
@@ -2284,3 +2284,115 @@ func TestEmitFieldComments(t *testing.T) {
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// errWriter fails every write with a fixed error.
|
||||
type errWriter struct{ err error }
|
||||
|
||||
func (w errWriter) Write([]byte) (int, error) { return 0, w.err }
|
||||
|
||||
// TestMarshalWrite covers the streaming entry: the happy path with options
|
||||
// and a failing writer.
|
||||
func TestMarshalWrite(t *testing.T) {
|
||||
var buf bytes.Buffer
|
||||
err := MarshalWrite(&buf, map[string]any{"b": 2, "a": 1})
|
||||
if err != nil {
|
||||
t.Fatalf("MarshalWrite: %v", err)
|
||||
}
|
||||
// A map carries no order, so the writer uses the sorted one.
|
||||
if buf.String() != "a = 1\nb = 2\n" {
|
||||
t.Errorf("output = %q", buf.String())
|
||||
}
|
||||
writeErr := errors.New("boom")
|
||||
if err := MarshalWrite(errWriter{writeErr}, map[string]any{"a": 1}); !errors.Is(err, writeErr) {
|
||||
t.Errorf("err = %v, want the write error wrapped", err)
|
||||
}
|
||||
}
|
||||
|
||||
// TestMarshalRejectsUnsupportedKinds pins the clear error a field of a kind
|
||||
// TOML cannot carry raises, through the struct walk.
|
||||
func TestMarshalRejectsUnsupportedKinds(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
value any
|
||||
}{
|
||||
{"func", struct {
|
||||
F func() `toml:"f"`
|
||||
}{}},
|
||||
{"chan", struct {
|
||||
C chan int `toml:"c"`
|
||||
}{}},
|
||||
{"complex", struct {
|
||||
Z complex128 `toml:"z"`
|
||||
}{}},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
_, err := Marshal(tt.value)
|
||||
if err == nil {
|
||||
t.Fatalf("Marshal accepted %#v", tt.value)
|
||||
}
|
||||
if !strings.Contains(err.Error(), "cannot encode") {
|
||||
t.Errorf("err = %v, want the cannot-encode complaint", err)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// TestMarshalOmitsEmptyPointerTableSlice pins that an empty slice of pointer
|
||||
// tables is omitted, the rule its non-pointer form already follows.
|
||||
func TestMarshalOmitsEmptyPointerTableSlice(t *testing.T) {
|
||||
type item struct {
|
||||
N int `toml:"n"`
|
||||
}
|
||||
out, err := Marshal(struct {
|
||||
Items []*item `toml:"items"`
|
||||
}{})
|
||||
if err != nil {
|
||||
t.Fatalf("Marshal: %v", err)
|
||||
}
|
||||
if len(out) != 0 {
|
||||
t.Errorf("output = %q, want the empty array of tables omitted", out)
|
||||
}
|
||||
}
|
||||
|
||||
// TestMarshalRejectsNonWholeMinuteOffset pins that a zone offset carrying
|
||||
// seconds is refused instead of silently losing them.
|
||||
func TestMarshalRejectsNonWholeMinuteOffset(t *testing.T) {
|
||||
z := time.FixedZone("", 57*60+44)
|
||||
_, err := Marshal(struct {
|
||||
Stamp time.Time `toml:"stamp"`
|
||||
}{Stamp: time.Date(1890, 1, 1, 12, 0, 0, 0, z)})
|
||||
if err == nil || !strings.Contains(err.Error(), "not a whole number of minutes") {
|
||||
t.Errorf("err = %v, want the whole-minute offset complaint", err)
|
||||
}
|
||||
_, err = Marshal(struct {
|
||||
Stamp OffsetDateTime `toml:"stamp"`
|
||||
}{Stamp: OffsetDateTime{time.Date(1890, 1, 1, 12, 0, 0, 0, z)}})
|
||||
if err == nil || !strings.Contains(err.Error(), "not a whole number of minutes") {
|
||||
t.Errorf("err = %v, want the whole-minute offset complaint for the wrapper", err)
|
||||
}
|
||||
}
|
||||
|
||||
// cancelOnMarshal cancels the context the encode runs under, the moment its
|
||||
// method is called, so the emission that follows is already past the walk's
|
||||
// own checks.
|
||||
type cancelOnMarshal struct {
|
||||
cancel context.CancelFunc
|
||||
}
|
||||
|
||||
func (c cancelOnMarshal) MarshalTOML() (any, error) {
|
||||
c.cancel()
|
||||
return int64(1), nil
|
||||
}
|
||||
|
||||
// TestMarshalContextCancelsDuringEmission pins that a context cancelled
|
||||
// between the walk and the emission stops the encode instead of writing the
|
||||
// whole document out.
|
||||
func TestMarshalContextCancelsDuringEmission(t *testing.T) {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
value := map[string]any{"k": cancelOnMarshal{cancel}}
|
||||
if _, err := MarshalContext(ctx, value); !errors.Is(err, context.Canceled) {
|
||||
t.Errorf("err = %v, want the cancellation", err)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user