github.com/jhump/protoreflect@v1.16.0/internal/codec/encode_test.go (about)

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