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 }