github.com/bakjos/protoreflect@v1.9.2/internal/codec/encode_test.go (about)

     1  package codec_test
     2  
     3  import (
     4  	"testing"
     5  
     6  	"github.com/golang/protobuf/proto"
     7  
     8  	"github.com/bakjos/protoreflect/codec"
     9  	"github.com/bakjos/protoreflect/desc"
    10  	"github.com/bakjos/protoreflect/dynamic"
    11  	"github.com/bakjos/protoreflect/internal/testprotos"
    12  	"github.com/bakjos/protoreflect/internal/testutil"
    13  )
    14  
    15  func TestEncodeMessage(t *testing.T) {
    16  	// A generated message will be encoded using its XXX_Size and XXX_Marshal
    17  	// methods
    18  	pm := &testprotos.Test{
    19  		Foo:   proto.String("bar"),
    20  		Array: []int32{0, 1, 2, 3},
    21  		S: &testprotos.Simple{
    22  			Name: proto.String("baz"),
    23  			Id:   proto.Uint64(12345),
    24  		},
    25  		M: map[string]int32{
    26  			"a": 1,
    27  			"b": 2,
    28  			"c": 3,
    29  			"d": 4,
    30  		},
    31  		B: []byte{3, 2, 1, 0},
    32  	}
    33  
    34  	// A generated message will be encoded using its MarshalAppend and
    35  	// MarshalAppendDeterministic methods
    36  	md, err := desc.LoadMessageDescriptorForMessage(pm)
    37  	testutil.Ok(t, err)
    38  	dm := dynamic.NewMessage(md)
    39  	err = dm.ConvertFrom(pm)
    40  	testutil.Ok(t, err)
    41  
    42  	// This custom message will use MarshalDeterministic method or fall back to
    43  	// old proto.Marshal implementation for non-deterministic marshaling
    44  	cm := (*TestMessage)(pm)
    45  
    46  	testCases := []struct {
    47  		Name string
    48  		Msg  proto.Message
    49  	}{
    50  		{Name: "generated", Msg: pm},
    51  		{Name: "dynamic", Msg: dm},
    52  		{Name: "custom", Msg: cm},
    53  	}
    54  	dels := []struct {
    55  		Name      string
    56  		Delimited bool
    57  	}{
    58  		{Name: "not delimited", Delimited: false},
    59  		{Name: "delimited", Delimited: true},
    60  	}
    61  
    62  	var bytes []byte
    63  
    64  	for _, dl := range dels {
    65  		t.Run(dl.Name, func(t *testing.T) {
    66  			t.Run("deterministic", func(t *testing.T) {
    67  				for _, tc := range testCases {
    68  					t.Run(tc.Name, func(t *testing.T) {
    69  						var cb codec.Buffer
    70  						cb.SetDeterministic(true)
    71  						if dl.Delimited {
    72  							err := cb.EncodeDelimitedMessage(tc.Msg)
    73  							testutil.Ok(t, err)
    74  						} else {
    75  							err := cb.EncodeMessage(tc.Msg)
    76  							testutil.Ok(t, err)
    77  						}
    78  						b := cb.Bytes()
    79  						if bytes == nil {
    80  							bytes = b
    81  						} else if dl.Delimited {
    82  							// delimited writes have varint-encoded length prefix
    83  							var lenBuf codec.Buffer
    84  							err := lenBuf.EncodeVarint(uint64(len(bytes)))
    85  							testutil.Ok(t, err)
    86  							testutil.Eq(t, append(lenBuf.Bytes(), bytes...), b)
    87  						} else {
    88  							// The generated proto message is the benchmark.
    89  							// Ensure that the others match its output.
    90  							testutil.Eq(t, bytes, b)
    91  						}
    92  					})
    93  				}
    94  			})
    95  
    96  			t.Run("non-deterministic", func(t *testing.T) {
    97  				for _, tc := range testCases {
    98  					t.Run(tc.Name, func(t *testing.T) {
    99  						var cb codec.Buffer
   100  						if dl.Delimited {
   101  							err := cb.EncodeDelimitedMessage(tc.Msg)
   102  							testutil.Ok(t, err)
   103  						} else {
   104  							err := cb.EncodeMessage(tc.Msg)
   105  							testutil.Ok(t, err)
   106  						}
   107  
   108  						var b []byte
   109  						if dl.Delimited {
   110  							// delimited writes have varint-encoded length prefix
   111  							l, err := cb.DecodeVarint()
   112  							testutil.Ok(t, err)
   113  							b = cb.Bytes()
   114  							testutil.Eq(t, int(l), len(b))
   115  						} else {
   116  							b = cb.Bytes()
   117  						}
   118  						// we can't compare byte slices to benchmark since the
   119  						// message contains a map and we are not using deterministic
   120  						// marshal method; so verify that unmarshaling the bytes
   121  						// results in an equal message as the original
   122  						var pm2 testprotos.Test
   123  						err = proto.Unmarshal(b, &pm2)
   124  						testutil.Ok(t, err)
   125  
   126  						testutil.Require(t, proto.Equal(pm, &pm2))
   127  					})
   128  				}
   129  			})
   130  		})
   131  	}
   132  }
   133  
   134  // NB: other field types are well-exercised by dynamic.Message serialization tests
   135  // So we focus on serialization of groups and the various kinds of proto.Message
   136  // implementations that can back them (similar to TestEncodeMessage above).
   137  func TestEncodeFieldValue_Group(t *testing.T) {
   138  	atmMd, err := desc.LoadMessageDescriptorForMessage((*testprotos.AnotherTestMessage)(nil))
   139  	testutil.Ok(t, err)
   140  
   141  	rrFd := atmMd.FindFieldByNumber(6) // tag 6 is the group
   142  
   143  	// A generated message will be encoded using proto.Marshal function
   144  	// or the proto.Buffer type (for deterministic output)
   145  	pm := &testprotos.AnotherTestMessage_RockNRoll{
   146  		Beatles: proto.String("Sgt. Pepper's Lonely Hearts Club Band"),
   147  		Stones:  proto.String("Exile on Main St."),
   148  		Doors:   proto.String("Strange Days"),
   149  	}
   150  
   151  	// A generated message will be encoded using its MarshalAppend and
   152  	// MarshalAppendDeterministic methods
   153  	md, err := desc.LoadMessageDescriptorForMessage(pm)
   154  	testutil.Ok(t, err)
   155  	dm := dynamic.NewMessage(md)
   156  	err = dm.ConvertFrom(pm)
   157  	testutil.Ok(t, err)
   158  
   159  	// This custom message will use MarshalDeterministic method or fall back to
   160  	// old proto.Marshal implementation for non-deterministic marshaling
   161  	cm := (*TestGroup)(pm)
   162  
   163  	testCases := []struct {
   164  		Name string
   165  		Msg  proto.Message
   166  	}{
   167  		{Name: "generated", Msg: pm},
   168  		{Name: "dynamic", Msg: dm},
   169  		{Name: "custom", Msg: cm},
   170  	}
   171  
   172  	dets := []struct {
   173  		Name          string
   174  		Deterministic bool
   175  	}{
   176  		{Name: "deterministic", Deterministic: true},
   177  		{Name: "non-deterministic", Deterministic: false},
   178  	}
   179  
   180  	var bytes []byte
   181  
   182  	for _, det := range dets {
   183  		t.Run(det.Name, func(t *testing.T) {
   184  			for _, tc := range testCases {
   185  				t.Run(tc.Name, func(t *testing.T) {
   186  					var cb codec.Buffer
   187  					cb.SetDeterministic(det.Deterministic)
   188  					err := cb.EncodeFieldValue(rrFd, tc.Msg)
   189  					testutil.Ok(t, err)
   190  					b := cb.Bytes()
   191  					if bytes == nil {
   192  						bytes = b
   193  						// make sure that the bytes are valid
   194  						expected := &testprotos.AnotherTestMessage{Rocknroll: pm}
   195  						var actual testprotos.AnotherTestMessage
   196  						err := proto.Unmarshal(b, &actual)
   197  						testutil.Ok(t, err)
   198  						testutil.Require(t, proto.Equal(expected, &actual))
   199  					} else {
   200  						// The generated proto message is the benchmark.
   201  						// Ensure that all others match its output.
   202  						// (We can do this even for non-deterministic
   203  						// method because the actual data being marshaled
   204  						// has no map values, so will always be the same)
   205  						testutil.Eq(t, bytes, b)
   206  					}
   207  				})
   208  			}
   209  		})
   210  	}
   211  }
   212  
   213  type TestMessage testprotos.Test
   214  
   215  func (m *TestMessage) Reset() {
   216  	(*testprotos.Test)(m).Reset()
   217  }
   218  
   219  func (m *TestMessage) String() string {
   220  	return (*testprotos.Test)(m).String()
   221  }
   222  
   223  func (m *TestMessage) ProtoMessage() {
   224  }
   225  
   226  func (m *TestMessage) MarshalDeterministic() ([]byte, error) {
   227  	var buf proto.Buffer
   228  	buf.SetDeterministic(true)
   229  	if err := buf.Marshal(m); err != nil {
   230  		return nil, err
   231  	}
   232  	return buf.Bytes(), nil
   233  }
   234  
   235  type TestGroup testprotos.AnotherTestMessage_RockNRoll
   236  
   237  func (m *TestGroup) Reset() {
   238  	(*testprotos.AnotherTestMessage_RockNRoll)(m).Reset()
   239  }
   240  
   241  func (m *TestGroup) String() string {
   242  	return (*testprotos.AnotherTestMessage_RockNRoll)(m).String()
   243  }
   244  
   245  func (m *TestGroup) ProtoMessage() {
   246  }
   247  
   248  func (m *TestGroup) MarshalDeterministic() ([]byte, error) {
   249  	var buf proto.Buffer
   250  	buf.SetDeterministic(true)
   251  	if err := buf.Marshal(m); err != nil {
   252  		return nil, err
   253  	}
   254  	return buf.Bytes(), nil
   255  }
   256  
   257  func init() {
   258  	proto.RegisterType((*TestMessage)(nil), "foo.bar.v2.TestMessage")
   259  	proto.RegisterType((*TestGroup)(nil), "foo.bar.v2.TestGroup")
   260  }