ugorji/go

Further Canonical / MissingFielder issue

jim-minter opened this issue · 2 comments

Following #354, I think there's still a lurking issue (tested running against codec commit a4afc2b). With Canonical enabled, I'd expect to see root map keys sorted lexicographically, but they don't appear to be.

package test

import (
	"bytes"
	"testing"

	"github.com/ugorji/go/codec"
)

type missingFields struct {
	m map[string]interface{}
}

func (mf *missingFields) CodecMissingField(field []byte, value interface{}) bool {
	if mf.m == nil {
		mf.m = map[string]interface{}{}
	}

	(mf.m)[string(field)] = value

	return true
}

func (mf *missingFields) CodecMissingFields() map[string]interface{} {
	return mf.m
}

var _ codec.MissingFielder = (*missingFields)(nil)

type s1 struct {
	missingFields
	B int
}

type s2 struct {
	A int
	B int
}

func TestCanonicalMissingFielder2(t *testing.T) {
	h := codec.JsonHandle{
		BasicHandle: codec.BasicHandle{
			EncodeOptions: codec.EncodeOptions{
				Canonical: true,
			},
		},
	}

	var b1 []byte
	err := codec.NewEncoderBytes(&b1, &h).Encode(&s1{
		missingFields: missingFields{
			m: map[string]interface{}{
				"A": 1,
			},
		},
		B: 2,
	})
	if err != nil {
		t.Fatal(err)
	}

	var b2 []byte
	err = codec.NewEncoderBytes(&b2, &h).Encode(&s2{
		A: 1,
		B: 2,
	})
	if err != nil {
		t.Fatal(err)
	}

	if !bytes.Equal(b1, b2) {
		t.Errorf("bytes differ:\n%s\n%s", string(b1), string(b2))
	}
}

I would expect the two byte slices above to be equal, but they are not:

=== RUN   TestCanonicalMissingFielder
    main_test.go:72: bytes differ:
        {"B":2,"A":1}
        {"A":1,"B":2}
--- FAIL: TestCanonicalMissingFielder (0.00s)

Good catch. I missed this scenario, and initially thought it was sufficient to just sort the missing fields independently. I now collate both missing fields and struct fields together, sort them, and then encode as one. This should resolve the issue. I updated the tests also.

Thanks for diligently following up on it.

Thanks again - all looks good now.