github.com/bakjos/protoreflect@v1.9.2/dynamic/marshal_test.go (about) 1 package dynamic 2 3 import ( 4 "fmt" 5 "math" 6 "reflect" 7 "testing" 8 9 "github.com/golang/protobuf/protoc-gen-go/descriptor" 10 11 "github.com/golang/protobuf/proto" 12 13 "github.com/bakjos/protoreflect/desc" 14 "github.com/bakjos/protoreflect/internal/testprotos" 15 "github.com/bakjos/protoreflect/internal/testutil" 16 ) 17 18 // Shared stuff for marshalling and unmarshalling tests. This is used for the binary format, the text 19 // format, and the JSON format. 20 21 var unaryFieldsPosMsg = &testprotos.UnaryFields{ 22 I: proto.Int32(1), 23 J: proto.Int64(2), 24 K: proto.Int32(3), 25 L: proto.Int64(4), 26 M: proto.Uint32(5), 27 N: proto.Uint64(6), 28 O: proto.Uint32(7), 29 P: proto.Uint64(8), 30 Q: proto.Int32(9), 31 R: proto.Int64(10), 32 S: proto.Float32(11), 33 T: proto.Float64(12), 34 U: []byte{0, 1, 2, 3, 4, 5, 6, 7}, 35 V: proto.String("foobar"), 36 W: proto.Bool(true), 37 X: &testprotos.RepeatedFields{ 38 I: []int32{3}, 39 V: []string{"baz"}, 40 }, 41 Groupy: &testprotos.UnaryFields_GroupY{ 42 Ya: proto.String("bedazzle"), 43 Yb: proto.Int32(42), 44 }, 45 Z: testprotos.TestEnum_SECOND.Enum(), 46 } 47 48 var unaryFieldsNegMsg = &testprotos.UnaryFields{ 49 I: proto.Int32(-1), 50 J: proto.Int64(-2), 51 K: proto.Int32(-3), 52 L: proto.Int64(-4), 53 M: proto.Uint32(5), 54 N: proto.Uint64(6), 55 O: proto.Uint32(7), 56 P: proto.Uint64(8), 57 Q: proto.Int32(-9), 58 R: proto.Int64(-10), 59 S: proto.Float32(-11), 60 T: proto.Float64(-12), 61 U: []byte{0, 1, 2, 3, 4, 5, 6, 7}, 62 V: proto.String("foobar"), 63 W: proto.Bool(true), 64 X: &testprotos.RepeatedFields{ 65 I: []int32{-3}, 66 V: []string{"baz"}, 67 }, 68 Groupy: &testprotos.UnaryFields_GroupY{ 69 Ya: proto.String("bedazzle"), 70 Yb: proto.Int32(-42), 71 }, 72 Z: testprotos.TestEnum_SECOND.Enum(), 73 } 74 75 var unaryFieldsPosInfMsg = &testprotos.UnaryFields{ 76 S: proto.Float32(float32(math.Inf(1))), 77 T: proto.Float64(math.Inf(1)), 78 } 79 80 var unaryFieldsNegInfMsg = &testprotos.UnaryFields{ 81 S: proto.Float32(float32(math.Inf(-1))), 82 T: proto.Float64(math.Inf(-1)), 83 } 84 85 var unaryFieldsNanMsg = &testprotos.UnaryFields{ 86 S: proto.Float32(float32(math.NaN())), 87 T: proto.Float64(math.NaN()), 88 } 89 90 var repeatedFieldsMsg = &testprotos.RepeatedFields{ 91 I: []int32{1, -2, 3}, 92 J: []int64{-4, 5, -6}, 93 K: []int32{7, -8, 9}, 94 L: []int64{-10, 11, -12}, 95 M: []uint32{13, 14, 15}, 96 N: []uint64{16, 17, 18}, 97 O: []uint32{19, 20, 21}, 98 P: []uint64{22, 23, 24}, 99 Q: []int32{25, 26, 27}, 100 R: []int64{28, 29, 30}, 101 S: []float32{31, 32, 33}, 102 T: []float64{34, 35, 36}, 103 U: [][]byte{{0, 1, 2, 3}, {4, 5, 6, 7}, {8, 9, 10, 11}}, 104 V: []string{"foo", "bar", "baz"}, 105 W: []bool{true, false, true}, 106 X: []*testprotos.UnaryFields{ 107 {I: proto.Int32(-32), V: proto.String("baz")}, 108 {I: proto.Int32(-64), V: proto.String("bozo")}, 109 }, 110 Groupy: []*testprotos.RepeatedFields_GroupY{ 111 {Ya: proto.String("bedazzle"), Yb: proto.Int32(42)}, 112 {Ya: proto.String("buzzard"), Yb: proto.Int32(-421)}, 113 }, 114 Z: []testprotos.TestEnum{testprotos.TestEnum_SECOND, testprotos.TestEnum_THIRD, testprotos.TestEnum_FIRST}, 115 } 116 117 var repeatedFieldsInfNanMsg = &testprotos.RepeatedFields{ 118 S: []float32{float32(math.Inf(1)), float32(math.Inf(-1)), float32(math.NaN())}, 119 T: []float64{math.Inf(1), math.Inf(-1), math.NaN()}, 120 } 121 122 var repeatedPackedFieldsMsg = &testprotos.RepeatedPackedFields{ 123 I: []int32{1, -2, 3}, 124 J: []int64{-4, 5, -6}, 125 K: []int32{7, -8, 9}, 126 L: []int64{-10, 11, -12}, 127 M: []uint32{13, 14, 15}, 128 N: []uint64{16, 17, 18}, 129 O: []uint32{19, 20, 21}, 130 P: []uint64{22, 23, 24}, 131 Q: []int32{25, 26, 27}, 132 R: []int64{28, 29, 30}, 133 S: []float32{31, 32, 33}, 134 T: []float64{34, 35, 36}, 135 U: []bool{true, false, true}, 136 Groupy: []*testprotos.RepeatedPackedFields_GroupY{ 137 {Yb: []int32{42, 84, 126, 168, 210}}, 138 {Yb: []int32{-210, -168, -126, -84, -42}}, 139 }, 140 V: []testprotos.TestEnum{testprotos.TestEnum_SECOND, testprotos.TestEnum_THIRD, testprotos.TestEnum_FIRST}, 141 } 142 143 var repeatedPackedFieldsInfNanMsg = &testprotos.RepeatedPackedFields{ 144 S: []float32{float32(math.Inf(1)), float32(math.Inf(-1)), float32(math.NaN())}, 145 T: []float64{math.Inf(1), math.Inf(-1), math.NaN()}, 146 } 147 148 var mapKeyFieldsMsg = &testprotos.MapKeyFields{ 149 I: map[int32]string{1: "foo", -2: "bar", 3: "baz"}, 150 J: map[int64]string{-4: "foo", 5: "bar", -6: "baz"}, 151 K: map[int32]string{7: "foo", -8: "bar", 9: "baz"}, 152 L: map[int64]string{-10: "foo", 11: "bar", -12: "baz"}, 153 M: map[uint32]string{13: "foo", 14: "bar", 15: "baz"}, 154 N: map[uint64]string{16: "foo", 17: "bar", 18: "baz"}, 155 O: map[uint32]string{19: "foo", 20: "bar", 21: "baz"}, 156 P: map[uint64]string{22: "foo", 23: "bar", 24: "baz"}, 157 Q: map[int32]string{25: "foo", 26: "bar", 27: "baz"}, 158 R: map[int64]string{28: "foo", 29: "bar", 30: "baz"}, 159 S: map[string]string{"a": "foo", "b": "bar", "c": "baz"}, 160 T: map[bool]string{true: "foo", false: "bar"}, 161 } 162 163 var mapValueFieldsMsg = &testprotos.MapValFields{ 164 I: map[string]int32{"a": 1, "b": -2, "c": 3}, 165 J: map[string]int64{"a": -4, "b": 5, "c": -6}, 166 K: map[string]int32{"a": 7, "b": -8, "c": 9}, 167 L: map[string]int64{"a": -10, "b": 11, "c": -12}, 168 M: map[string]uint32{"a": 13, "b": 14, "c": 15}, 169 N: map[string]uint64{"a": 16, "b": 17, "c": 18}, 170 O: map[string]uint32{"a": 19, "b": 20, "c": 21}, 171 P: map[string]uint64{"a": 22, "b": 23, "c": 24}, 172 Q: map[string]int32{"a": 25, "b": 26, "c": 27}, 173 R: map[string]int64{"a": 28, "b": 29, "c": 30}, 174 S: map[string]float32{"a": 31, "b": 32, "c": 33}, 175 T: map[string]float64{"a": 34, "b": 35, "c": 36}, 176 U: map[string][]byte{"a": {0, 1, 2, 3}, "b": {4, 5, 6, 7}, "c": {8, 9, 10, 11}}, 177 V: map[string]string{"a": "foo", "b": "bar", "c": "baz"}, 178 W: map[string]bool{"a": true, "b": false, "c": true}, 179 X: map[string]*testprotos.UnaryFields{ 180 "a": {I: proto.Int32(-32), V: proto.String("baz")}, 181 "b": {I: proto.Int32(-64), V: proto.String("bozo")}, 182 }, 183 Y: map[string]testprotos.TestEnum{"a": testprotos.TestEnum_SECOND, "b": testprotos.TestEnum_THIRD, "c": testprotos.TestEnum_FIRST}, 184 } 185 186 var mapValueFieldsInfNanMsg = &testprotos.MapValFields{ 187 S: map[string]float32{"a": float32(math.Inf(1)), "b": float32(math.Inf(-1)), "c": float32(math.NaN())}, 188 T: map[string]float64{"a": math.Inf(1), "b": math.Inf(-1), "c": math.NaN()}, 189 } 190 191 var mapValueFieldsNilMsg = &testprotos.TestRequest{ 192 Others: map[string]*testprotos.TestMessage{"a": nil, "b": nil}, 193 } 194 195 var mapValueFieldsNilUnknownMsg proto.Message 196 var mdForUnknownMsg *desc.MessageDescriptor 197 198 func init() { 199 // NB: can't use desc/builder package because that would cause dependency cycle :( 200 fdp := &descriptor.FileDescriptorProto{ 201 Name: proto.String("foo.proto"), 202 Syntax: proto.String("proto3"), 203 Package: proto.String("example"), 204 MessageType: []*descriptor.DescriptorProto{ 205 { 206 Name: proto.String("Message"), 207 Field: []*descriptor.FieldDescriptorProto{ 208 { 209 Name: proto.String("vals"), 210 Type: descriptor.FieldDescriptorProto_TYPE_MESSAGE.Enum(), 211 Number: proto.Int32(1), 212 Label: descriptor.FieldDescriptorProto_LABEL_REPEATED.Enum(), 213 TypeName: proto.String(".example.Message.ValsEntry"), 214 }, 215 }, 216 NestedType: []*descriptor.DescriptorProto{ 217 { 218 Name: proto.String("ValsEntry"), 219 Options: &descriptor.MessageOptions{ 220 MapEntry: proto.Bool(true), 221 }, 222 Field: []*descriptor.FieldDescriptorProto{ 223 { 224 Name: proto.String("key"), 225 Number: proto.Int32(1), 226 Type: descriptor.FieldDescriptorProto_TYPE_STRING.Enum(), 227 Label: descriptor.FieldDescriptorProto_LABEL_OPTIONAL.Enum(), 228 }, 229 { 230 Name: proto.String("value"), 231 Number: proto.Int32(2), 232 Type: descriptor.FieldDescriptorProto_TYPE_MESSAGE.Enum(), 233 Label: descriptor.FieldDescriptorProto_LABEL_OPTIONAL.Enum(), 234 TypeName: proto.String(".example.Message"), 235 }, 236 }, 237 }, 238 }, 239 }, 240 }, 241 } 242 fd, err := desc.CreateFileDescriptor(fdp) 243 if err != nil { 244 panic(err) 245 } 246 mdForUnknownMsg = fd.GetMessageTypes()[0] 247 mapValueFieldsNilUnknownMsg = &unknownMsg{ 248 Vals: map[string]*unknownMsg{"a": nil, "b": nil}, 249 } 250 } 251 252 // This message looks and acts like a proto but is NOT in the registry, so proto.MessageType 253 // returns nil, which forces us to fallback to a nil *dynamic.Message when representing a 254 // nil map value in a dynamic message, allowing tests to check that strange edge case. 255 type unknownMsg struct { 256 Vals map[string]*unknownMsg `protobuf:"bytes,1,rep,name=vals,proto3" json:"vals,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` 257 } 258 259 func (m *unknownMsg) XXX_MessageName() string { 260 return "example.Message" 261 } 262 func (m *unknownMsg) Reset() { 263 m.Vals = nil 264 } 265 func (m *unknownMsg) String() string { 266 return fmt.Sprintf("%#v", m.Vals) 267 } 268 func (m *unknownMsg) ProtoMessage() { 269 } 270 func (m *unknownMsg) GetMessageDescriptor() *desc.MessageDescriptor { 271 return mdForUnknownMsg 272 } 273 274 func doTranslationParty(t *testing.T, msg proto.Message, 275 marshalPm func(proto.Message) ([]byte, error), unmarshalPm func([]byte, proto.Message) error, 276 marshalDm func(*Message) ([]byte, error), unmarshalDm func(*Message, []byte) error, 277 includesNaN, compareBytes, outputIsString bool) { 278 279 md, err := desc.LoadMessageDescriptorForMessage(msg) 280 testutil.Ok(t, err) 281 dm := NewMessage(md) 282 283 b, err := marshalPm(msg) 284 testutil.Ok(t, err) 285 err = unmarshalDm(dm, b) 286 testutil.Ok(t, err, "failed to unmarshal from: %s", b) 287 288 // both techniques to marshal do the same thing 289 b2a, err := marshalPm(dm) 290 testutil.Ok(t, err) 291 b2b, err := marshalDm(dm) 292 testutil.Ok(t, err) 293 testutil.Eq(t, b2a, b2b) 294 295 // round trip back to proto.Message 296 msg2 := reflect.New(reflect.TypeOf(msg).Elem()).Interface().(proto.Message) 297 err = unmarshalPm(b2a, msg2) 298 testutil.Ok(t, err, "failed to unmarshal from: %s", b2a) 299 300 if !includesNaN { 301 // NaN fields are never equal so this would always be false 302 testutil.Ceq(t, msg, msg2, eqpm) 303 } 304 if compareBytes { 305 if outputIsString { 306 testutil.Eq(t, string(b), string(b2a)) 307 } else { 308 testutil.Eq(t, b, b2a) 309 } 310 } 311 312 // and back again 313 b3, err := marshalPm(msg2) 314 testutil.Ok(t, err) 315 dm2 := NewMessage(md) 316 err = unmarshalDm(dm2, b3) 317 testutil.Ok(t, err, "failed to unmarshal from: %s", b3) 318 319 if !includesNaN { 320 testutil.Ceq(t, dm, dm2, eqdm) 321 } 322 323 // dynamic message -> (bytes) -> dynamic message 324 // both techniques to unmarshal are equivalent 325 dm3 := NewMessage(md) 326 err = unmarshalPm(b2a, dm3) 327 testutil.Ok(t, err, "failed to unmarshal from: %s", b2a) 328 dm4 := NewMessage(md) 329 err = unmarshalDm(dm4, b2a) 330 testutil.Ok(t, err, "failed to unmarshal from: %s", b2a) 331 332 if !includesNaN { 333 testutil.Ceq(t, dm, dm3, eqdm) 334 testutil.Ceq(t, dm, dm4, eqdm) 335 } 336 }