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 }