From 7c1f8f3e334bb79b232b7bedcb261305f98a8dc5 Mon Sep 17 00:00:00 2001 From: Ben Luddy Date: Mon, 29 Jan 2024 23:11:49 -0500 Subject: [PATCH] Fix EncOption/DecOption unset fields on mode regurgitation. Signed-off-by: Ben Luddy --- decode.go | 1 + decode_test.go | 36 +++++++++++++++++++++++++----------- encode.go | 1 + encode_test.go | 38 ++++++++++++++++++++++++++++++++++++-- 4 files changed, 63 insertions(+), 13 deletions(-) diff --git a/decode.go b/decode.go index 015ac9d1..5986d061 100644 --- a/decode.go +++ b/decode.go @@ -741,6 +741,7 @@ func (dm *decMode) DecOptions() DecOptions { IntDec: dm.intDec, MapKeyByteString: dm.mapKeyByteString, ExtraReturnErrors: dm.extraReturnErrors, + DefaultMapType: dm.defaultMapType, UTF8: dm.utf8, FieldNameMatching: dm.fieldNameMatching, BigIntDec: dm.bigIntDec, diff --git a/decode_test.go b/decode_test.go index 99265b63..92cb675f 100644 --- a/decode_test.go +++ b/decode_test.go @@ -4861,16 +4861,30 @@ func TestUnmarshalToNotNilInterface(t *testing.T) { func TestDecOptions(t *testing.T) { opts1 := DecOptions{ - DupMapKey: DupMapKeyEnforcedAPF, - TimeTag: DecTagRequired, - MaxNestedLevels: 100, - MaxArrayElements: 102, - MaxMapPairs: 101, - IndefLength: IndefLengthForbidden, - TagsMd: TagsForbidden, - IntDec: IntDecConvertSigned, - ExtraReturnErrors: ExtraDecErrorUnknownField, - UTF8: UTF8DecodeInvalid, + DupMapKey: DupMapKeyEnforcedAPF, + TimeTag: DecTagRequired, + MaxNestedLevels: 100, + MaxArrayElements: 102, + MaxMapPairs: 101, + IndefLength: IndefLengthForbidden, + TagsMd: TagsForbidden, + IntDec: IntDecConvertSigned, + MapKeyByteString: MapKeyByteStringForbidden, + ExtraReturnErrors: ExtraDecErrorUnknownField, + DefaultMapType: reflect.TypeOf(map[string]interface{}(nil)), + UTF8: UTF8DecodeInvalid, + FieldNameMatching: FieldNameMatchingCaseSensitive, + BigIntDec: BigIntDecodePointer, + DefaultByteStringType: reflect.TypeOf(""), + ByteStringToString: ByteStringToStringAllowed, + FieldNameByteString: FieldNameByteStringAllowed, + } + ov := reflect.ValueOf(opts1) + for i := 0; i < ov.NumField(); i++ { + fv := ov.Field(i) + if fv.IsZero() { + t.Errorf("options field %q is unset or set to the zero value for its type", ov.Type().Field(i).Name) + } } dm, err := opts1.DecMode() if err != nil { @@ -4878,7 +4892,7 @@ func TestDecOptions(t *testing.T) { } else { opts2 := dm.DecOptions() if !reflect.DeepEqual(opts1, opts2) { - t.Errorf("DecOptions->DecMode->DecOptions returned different values: %v, %v", opts1, opts2) + t.Errorf("DecOptions->DecMode->DecOptions returned different values: %#v, %#v", opts1, opts2) } } } diff --git a/encode.go b/encode.go index 087f95f3..86cc47ab 100644 --- a/encode.go +++ b/encode.go @@ -643,6 +643,7 @@ func (em *encMode) EncOptions() EncOptions { Time: em.time, TimeTag: em.timeTag, IndefLength: em.indefLength, + NilContainers: em.nilContainers, TagsMd: em.tagsMd, OmitEmpty: em.omitEmpty, String: em.stringType, diff --git a/encode_test.go b/encode_test.go index 516a248c..bb4bdade 100644 --- a/encode_test.go +++ b/encode_test.go @@ -3623,6 +3623,23 @@ func TestEncModeInvalidBigIntConvertMode(t *testing.T) { } } +func TestEncOptionsTagsForbidden(t *testing.T) { + // It's not valid to set both TagsMd and TimeTag to a non-zero value in the same EncOptions, + // so this exercises the options-mode-options roundtrip for non-zero TagsMd. + opts1 := EncOptions{ + TagsMd: TagsForbidden, + } + em, err := opts1.EncMode() + if err != nil { + t.Errorf("EncMode() returned an error %v", err) + } else { + opts2 := em.EncOptions() + if !reflect.DeepEqual(opts1, opts2) { + t.Errorf("EncOptions->EncMode->EncOptions returned different values: %#v, %#v", opts1, opts2) + } + } +} + func TestEncOptions(t *testing.T) { opts1 := EncOptions{ Sort: SortBytewiseLexical, @@ -3633,8 +3650,25 @@ func TestEncOptions(t *testing.T) { Time: TimeRFC3339Nano, TimeTag: EncTagRequired, IndefLength: IndefLengthForbidden, - NilContainers: NilContainerAsNull, + NilContainers: NilContainerAsEmpty, TagsMd: TagsAllowed, + OmitEmpty: OmitEmptyGoValue, + String: StringToByteString, + FieldName: FieldNameToByteString, + } + ov := reflect.ValueOf(opts1) + for i := 0; i < ov.NumField(); i++ { + fv := ov.Field(i) + if fv.IsZero() { + fn := ov.Type().Field(i).Name + if fn == "TagsMd" { + // Roundtripping non-zero values for TagsMd is tested separately + // since the non-zero value (TagsForbidden) is incompatible with the + // non-zero value for other options (e.g. TimeTag). + continue + } + t.Errorf("options field %q is unset or set to the zero value for its type", fn) + } } em, err := opts1.EncMode() if err != nil { @@ -3642,7 +3676,7 @@ func TestEncOptions(t *testing.T) { } else { opts2 := em.EncOptions() if !reflect.DeepEqual(opts1, opts2) { - t.Errorf("EncOptions->EncMode->EncOptions returned different values: %v, %v", opts1, opts2) + t.Errorf("EncOptions->EncMode->EncOptions returned different values: %#v, %#v", opts1, opts2) } } }