• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1// Copyright 2018 The Go Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style
3// license that can be found in the LICENSE file.
4
5package protopack
6
7import (
8	"bytes"
9	"encoding/hex"
10	"fmt"
11	"math"
12	"testing"
13
14	"github.com/google/go-cmp/cmp"
15
16	"google.golang.org/protobuf/encoding/prototext"
17	"google.golang.org/protobuf/reflect/protodesc"
18	"google.golang.org/protobuf/reflect/protoreflect"
19
20	"google.golang.org/protobuf/types/descriptorpb"
21)
22
23var msgDesc = func() protoreflect.MessageDescriptor {
24	const s = `
25		name:   "test.proto"
26		syntax: "proto2"
27		message_type: [{
28			name: "Message"
29			field: [
30				{name:"f1"  number:1  label:LABEL_REPEATED type:TYPE_BOOL     options:{packed:true}},
31				{name:"f2"  number:2  label:LABEL_REPEATED type:TYPE_INT64    options:{packed:true}},
32				{name:"f3"  number:3  label:LABEL_REPEATED type:TYPE_SINT64   options:{packed:true}},
33				{name:"f4"  number:4  label:LABEL_REPEATED type:TYPE_UINT64   options:{packed:true}},
34				{name:"f5"  number:5  label:LABEL_REPEATED type:TYPE_FIXED32  options:{packed:true}},
35				{name:"f6"  number:6  label:LABEL_REPEATED type:TYPE_SFIXED32 options:{packed:true}},
36				{name:"f7"  number:7  label:LABEL_REPEATED type:TYPE_FLOAT    options:{packed:true}},
37				{name:"f8"  number:8  label:LABEL_REPEATED type:TYPE_FIXED64  options:{packed:true}},
38				{name:"f9"  number:9  label:LABEL_REPEATED type:TYPE_SFIXED64 options:{packed:true}},
39				{name:"f10" number:10 label:LABEL_REPEATED type:TYPE_DOUBLE   options:{packed:true}},
40				{name:"f11" number:11 label:LABEL_OPTIONAL type:TYPE_STRING},
41				{name:"f12" number:12 label:LABEL_OPTIONAL type:TYPE_BYTES},
42				{name:"f13" number:13 label:LABEL_OPTIONAL type:TYPE_MESSAGE type_name:".Message"},
43				{name:"f14" number:14 label:LABEL_OPTIONAL type:TYPE_GROUP   type_name:".Message.F14"}
44			]
45			nested_type: [{name: "F14"}]
46		}]
47	`
48	pb := new(descriptorpb.FileDescriptorProto)
49	if err := prototext.Unmarshal([]byte(s), pb); err != nil {
50		panic(err)
51	}
52	fd, err := protodesc.NewFile(pb, nil)
53	if err != nil {
54		panic(err)
55	}
56	return fd.Messages().Get(0)
57}()
58
59// dhex decodes a hex-string and returns the bytes and panics if s is invalid.
60func dhex(s string) []byte {
61	b, err := hex.DecodeString(s)
62	if err != nil {
63		panic(err)
64	}
65	return b
66}
67
68func TestPack(t *testing.T) {
69	tests := []struct {
70		raw      []byte
71		msg      Message
72		msgDesc  protoreflect.MessageDescriptor
73		inferMsg bool
74
75		wantOutCompact string
76		wantOutMulti   string
77		wantOutSource  string
78	}{{
79		raw: dhex("080088808080800002088280808080000a09010002828080808000"),
80		msg: Message{
81			Tag{1, VarintType}, Bool(false),
82			Denormalized{5, Tag{1, VarintType}}, Uvarint(2),
83			Tag{1, VarintType}, Denormalized{5, Uvarint(2)},
84			Tag{1, BytesType}, LengthPrefix{Bool(true), Bool(false), Uvarint(2), Denormalized{5, Uvarint(2)}},
85		},
86		msgDesc: msgDesc,
87		wantOutSource: `protopack.Message{
88	protopack.Tag{1, protopack.VarintType}, protopack.Bool(false),
89	protopack.Denormalized{+5, protopack.Tag{1, protopack.VarintType}}, protopack.Uvarint(2),
90	protopack.Tag{1, protopack.VarintType}, protopack.Denormalized{+5, protopack.Uvarint(2)},
91	protopack.Tag{1, protopack.BytesType}, protopack.LengthPrefix{protopack.Bool(true), protopack.Bool(false), protopack.Uvarint(2), protopack.Denormalized{+5, protopack.Uvarint(2)}},
92}`,
93	}, {
94		raw: dhex("080088808080800002088280808080000a09010002828080808000"),
95		msg: Message{
96			Tag{1, VarintType}, Uvarint(0),
97			Denormalized{5, Tag{1, VarintType}}, Uvarint(2),
98			Tag{1, VarintType}, Denormalized{5, Uvarint(2)},
99			Tag{1, BytesType}, Bytes(Message{Bool(true), Bool(false), Uvarint(2), Denormalized{5, Uvarint(2)}}.Marshal()),
100		},
101		inferMsg: true,
102	}, {
103		raw: dhex("100010828080808000121980808080808080808001ffffffffffffffff7f828080808000"),
104		msg: Message{
105			Tag{2, VarintType}, Varint(0),
106			Tag{2, VarintType}, Denormalized{5, Varint(2)},
107			Tag{2, BytesType}, LengthPrefix{Varint(math.MinInt64), Varint(math.MaxInt64), Denormalized{5, Varint(2)}},
108		},
109		msgDesc:        msgDesc,
110		wantOutCompact: `Message{Tag{2, Varint}, Varint(0), Tag{2, Varint}, Denormalized{+5, Varint(2)}, Tag{2, Bytes}, LengthPrefix{Varint(-9223372036854775808), Varint(9223372036854775807), Denormalized{+5, Varint(2)}}}`,
111	}, {
112		raw: dhex("1801188180808080001a1affffffffffffffffff01feffffffffffffffff01818080808000"),
113		msg: Message{
114			Tag{3, VarintType}, Svarint(-1),
115			Tag{3, VarintType}, Denormalized{5, Svarint(-1)},
116			Tag{3, BytesType}, LengthPrefix{Svarint(math.MinInt64), Svarint(math.MaxInt64), Denormalized{5, Svarint(-1)}},
117		},
118		msgDesc: msgDesc,
119		wantOutMulti: `Message{
120	Tag{3, Varint}, Svarint(-1),
121	Tag{3, Varint}, Denormalized{+5, Svarint(-1)},
122	Tag{3, Bytes}, LengthPrefix{Svarint(-9223372036854775808), Svarint(9223372036854775807), Denormalized{+5, Svarint(-1)}},
123}`,
124	}, {
125		raw: dhex("200120818080808000221100ffffffffffffffffff01818080808000"),
126		msg: Message{
127			Tag{4, VarintType}, Uvarint(+1),
128			Tag{4, VarintType}, Denormalized{5, Uvarint(+1)},
129			Tag{4, BytesType}, LengthPrefix{Uvarint(0), Uvarint(math.MaxUint64), Denormalized{5, Uvarint(+1)}},
130		},
131		msgDesc: msgDesc,
132		wantOutSource: `protopack.Message{
133	protopack.Tag{4, protopack.VarintType}, protopack.Uvarint(1),
134	protopack.Tag{4, protopack.VarintType}, protopack.Denormalized{+5, protopack.Uvarint(1)},
135	protopack.Tag{4, protopack.BytesType}, protopack.LengthPrefix{protopack.Uvarint(0), protopack.Uvarint(18446744073709551615), protopack.Denormalized{+5, protopack.Uvarint(1)}},
136}`,
137	}, {
138		raw: dhex("2d010000002a0800000000ffffffff"),
139		msg: Message{
140			Tag{5, Fixed32Type}, Uint32(+1),
141			Tag{5, BytesType}, LengthPrefix{Uint32(0), Uint32(math.MaxUint32)},
142		},
143		msgDesc:        msgDesc,
144		wantOutCompact: `Message{Tag{5, Fixed32}, Uint32(1), Tag{5, Bytes}, LengthPrefix{Uint32(0), Uint32(4294967295)}}`,
145	}, {
146		raw: dhex("35ffffffff320800000080ffffff7f"),
147		msg: Message{
148			Tag{6, Fixed32Type}, Int32(-1),
149			Tag{6, BytesType}, LengthPrefix{Int32(math.MinInt32), Int32(math.MaxInt32)},
150		},
151		msgDesc: msgDesc,
152		wantOutMulti: `Message{
153	Tag{6, Fixed32}, Int32(-1),
154	Tag{6, Bytes}, LengthPrefix{Int32(-2147483648), Int32(2147483647)},
155}`,
156	}, {
157		raw: dhex("3ddb0f49403a1001000000ffff7f7f0000807f000080ff"),
158		msg: Message{
159			Tag{7, Fixed32Type}, Float32(math.Pi),
160			Tag{7, BytesType}, LengthPrefix{Float32(math.SmallestNonzeroFloat32), Float32(math.MaxFloat32), Float32(math.Inf(+1)), Float32(math.Inf(-1))},
161		},
162		msgDesc: msgDesc,
163		wantOutSource: `protopack.Message{
164	protopack.Tag{7, protopack.Fixed32Type}, protopack.Float32(3.1415927),
165	protopack.Tag{7, protopack.BytesType}, protopack.LengthPrefix{protopack.Float32(1e-45), protopack.Float32(3.4028235e+38), protopack.Float32(math.Inf(+1)), protopack.Float32(math.Inf(-1))},
166}`,
167	}, {
168		raw: dhex("41010000000000000042100000000000000000ffffffffffffffff"),
169		msg: Message{
170			Tag{8, Fixed64Type}, Uint64(+1),
171			Tag{8, BytesType}, LengthPrefix{Uint64(0), Uint64(math.MaxUint64)},
172		},
173		msgDesc:        msgDesc,
174		wantOutCompact: `Message{Tag{8, Fixed64}, Uint64(1), Tag{8, Bytes}, LengthPrefix{Uint64(0), Uint64(18446744073709551615)}}`,
175	}, {
176		raw: dhex("49ffffffffffffffff4a100000000000000080ffffffffffffff7f"),
177		msg: Message{
178			Tag{9, Fixed64Type}, Int64(-1),
179			Tag{9, BytesType}, LengthPrefix{Int64(math.MinInt64), Int64(math.MaxInt64)},
180		},
181		msgDesc: msgDesc,
182		wantOutMulti: `Message{
183	Tag{9, Fixed64}, Int64(-1),
184	Tag{9, Bytes}, LengthPrefix{Int64(-9223372036854775808), Int64(9223372036854775807)},
185}`,
186	}, {
187		raw: dhex("51182d4454fb21094052200100000000000000ffffffffffffef7f000000000000f07f000000000000f0ff"),
188		msg: Message{
189			Tag{10, Fixed64Type}, Float64(math.Pi),
190			Tag{10, BytesType}, LengthPrefix{Float64(math.SmallestNonzeroFloat64), Float64(math.MaxFloat64), Float64(math.Inf(+1)), Float64(math.Inf(-1))},
191		},
192		msgDesc: msgDesc,
193		wantOutMulti: `Message{
194	Tag{10, Fixed64}, Float64(3.141592653589793),
195	Tag{10, Bytes}, LengthPrefix{Float64(5e-324), Float64(1.7976931348623157e+308), Float64(+Inf), Float64(-Inf)},
196}`,
197	}, {
198		raw: dhex("5a06737472696e675a868080808000737472696e67"),
199		msg: Message{
200			Tag{11, BytesType}, String("string"),
201			Tag{11, BytesType}, Denormalized{+5, String("string")},
202		},
203		msgDesc:        msgDesc,
204		wantOutCompact: `Message{Tag{11, Bytes}, String("string"), Tag{11, Bytes}, Denormalized{+5, String("string")}}`,
205	}, {
206		raw: dhex("62056279746573628580808080006279746573"),
207		msg: Message{
208			Tag{12, BytesType}, Bytes("bytes"),
209			Tag{12, BytesType}, Denormalized{+5, Bytes("bytes")},
210		},
211		msgDesc: msgDesc,
212		wantOutMulti: `Message{
213	Tag{12, Bytes}, Bytes("bytes"),
214	Tag{12, Bytes}, Denormalized{+5, Bytes("bytes")},
215}`,
216	}, {
217		raw: dhex("6a28a006ffffffffffffffffff01a506ffffffffa106ffffffffffffffffa206056279746573a306a406"),
218		msg: Message{
219			Tag{13, BytesType}, LengthPrefix(Message{
220				Tag{100, VarintType}, Uvarint(math.MaxUint64),
221				Tag{100, Fixed32Type}, Uint32(math.MaxUint32),
222				Tag{100, Fixed64Type}, Uint64(math.MaxUint64),
223				Tag{100, BytesType}, Bytes("bytes"),
224				Tag{100, StartGroupType}, Tag{100, EndGroupType},
225			}),
226		},
227		msgDesc: msgDesc,
228		wantOutSource: `protopack.Message{
229	protopack.Tag{13, protopack.BytesType}, protopack.LengthPrefix(protopack.Message{
230		protopack.Tag{100, protopack.VarintType}, protopack.Uvarint(18446744073709551615),
231		protopack.Tag{100, protopack.Fixed32Type}, protopack.Uint32(4294967295),
232		protopack.Tag{100, protopack.Fixed64Type}, protopack.Uint64(18446744073709551615),
233		protopack.Tag{100, protopack.BytesType}, protopack.Bytes("bytes"),
234		protopack.Tag{100, protopack.StartGroupType},
235		protopack.Tag{100, protopack.EndGroupType},
236	}),
237}`,
238	}, {
239		raw: dhex("6a28a006ffffffffffffffffff01a506ffffffffa106ffffffffffffffffa206056279746573a306a406"),
240		msg: Message{
241			Tag{13, BytesType}, LengthPrefix(Message{
242				Tag{100, VarintType}, Uvarint(math.MaxUint64),
243				Tag{100, Fixed32Type}, Uint32(math.MaxUint32),
244				Tag{100, Fixed64Type}, Uint64(math.MaxUint64),
245				Tag{100, BytesType}, Bytes("bytes"),
246				Tag{100, StartGroupType}, Tag{100, EndGroupType},
247			}),
248		},
249		inferMsg: true,
250	}, {
251		raw: dhex("6a28a006ffffffffffffffffff01a506ffffffffa106ffffffffffffffffa206056279746573a306ac06"),
252		msg: Message{
253			Tag{13, BytesType}, Bytes(Message{
254				Tag{100, VarintType}, Uvarint(math.MaxUint64),
255				Tag{100, Fixed32Type}, Uint32(math.MaxUint32),
256				Tag{100, Fixed64Type}, Uint64(math.MaxUint64),
257				Tag{100, BytesType}, Bytes("bytes"),
258				Tag{100, StartGroupType}, Tag{101, EndGroupType},
259			}.Marshal()),
260		},
261		inferMsg: true,
262	}, {
263		raw: dhex("6aa88080808000a006ffffffffffffffffff01a506ffffffffa106ffffffffffffffffa206056279746573a306a406"),
264		msg: Message{
265			Tag{13, BytesType}, Denormalized{5, LengthPrefix(Message{
266				Tag{100, VarintType}, Uvarint(math.MaxUint64),
267				Tag{100, Fixed32Type}, Uint32(math.MaxUint32),
268				Tag{100, Fixed64Type}, Uint64(math.MaxUint64),
269				Tag{100, BytesType}, Bytes("bytes"),
270				Tag{100, StartGroupType}, Tag{100, EndGroupType},
271			})},
272		},
273		msgDesc:        msgDesc,
274		wantOutCompact: `Message{Tag{13, Bytes}, Denormalized{+5, LengthPrefix(Message{Tag{100, Varint}, Uvarint(18446744073709551615), Tag{100, Fixed32}, Uint32(4294967295), Tag{100, Fixed64}, Uint64(18446744073709551615), Tag{100, Bytes}, Bytes("bytes"), Tag{100, StartGroup}, Tag{100, EndGroup}})}}`,
275	}, {
276		raw: dhex("73a006ffffffffffffffffff01a506ffffffffa106ffffffffffffffffa206056279746573a306a40674"),
277		msg: Message{
278			Tag{14, StartGroupType}, Message{
279				Tag{100, VarintType}, Uvarint(math.MaxUint64),
280				Tag{100, Fixed32Type}, Uint32(math.MaxUint32),
281				Tag{100, Fixed64Type}, Uint64(math.MaxUint64),
282				Tag{100, BytesType}, Bytes("bytes"),
283				Tag{100, StartGroupType}, Tag{100, EndGroupType},
284			},
285			Tag{14, EndGroupType},
286		},
287		msgDesc: msgDesc,
288		wantOutMulti: `Message{
289	Tag{14, StartGroup},
290	Message{
291		Tag{100, Varint}, Uvarint(18446744073709551615),
292		Tag{100, Fixed32}, Uint32(4294967295),
293		Tag{100, Fixed64}, Uint64(18446744073709551615),
294		Tag{100, Bytes}, Bytes("bytes"),
295		Tag{100, StartGroup},
296		Tag{100, EndGroup},
297	},
298	Tag{14, EndGroup},
299}`,
300	}, {
301		raw: dhex("d0faa972cd02a5f09051c2d8aa0d6a26a89c311eddef024b423c0f6f47b64227a1600db56e3f73d4113096c9a88e2b99f2d847516853d76a1a6e9811c85a2ab3"),
302		msg: Message{
303			Tag{29970346, VarintType}, Uvarint(333),
304			Tag{21268228, Fixed32Type}, Uint32(229300418),
305			Tag{13, BytesType}, LengthPrefix(Message{
306				Tag{100805, VarintType}, Uvarint(30),
307				Tag{5883, Fixed32Type}, Uint32(255607371),
308				Tag{13, Type(7)},
309				Raw("G\xb6B'\xa1`\r\xb5n?s\xd4\x110\x96ɨ\x8e+\x99\xf2\xd8GQhS"),
310			}),
311			Tag{1706, Type(7)},
312			Raw("\x1an\x98\x11\xc8Z*\xb3"),
313		},
314		msgDesc: msgDesc,
315	}, {
316		raw: dhex("3d08d0e57f"),
317		msg: Message{
318			Tag{7, Fixed32Type}, Float32(math.Float32frombits(
319				// TODO: Remove workaround for compiler bug (see https://golang.org/issues/27193).
320				func() uint32 { return 0x7fe5d008 }(),
321			)),
322		},
323		msgDesc: msgDesc,
324		wantOutSource: `protopack.Message{
325	protopack.Tag{7, protopack.Fixed32Type}, protopack.Float32(math.Float32frombits(0x7fe5d008)),
326}`,
327	}, {
328		raw: dhex("51a8d65110771bf97f"),
329		msg: Message{
330			Tag{10, Fixed64Type}, Float64(math.Float64frombits(0x7ff91b771051d6a8)),
331		},
332		msgDesc: msgDesc,
333		wantOutSource: `protopack.Message{
334	protopack.Tag{10, protopack.Fixed64Type}, protopack.Float64(math.Float64frombits(0x7ff91b771051d6a8)),
335}`,
336	}, {
337		raw: dhex("ab2c14481ab3e9a76d937fb4dd5e6c616ef311f62b7fe888785fca5609ffe81c1064e50dd7a9edb408d317e2891c0d54c719446938d41ab0ccf8e61dc28b0ebb"),
338		msg: Message{
339			Tag{709, StartGroupType},
340			Tag{2, EndGroupType},
341			Tag{9, VarintType}, Uvarint(26),
342			Tag{28655254, StartGroupType},
343			Message{
344				Tag{2034, StartGroupType},
345				Tag{194006, EndGroupType},
346			},
347			Tag{13, EndGroupType},
348			Tag{12, Fixed64Type}, Uint64(9865274810543764334),
349			Tag{15, VarintType}, Uvarint(95),
350			Tag{1385, BytesType}, Bytes("\xff\xe8\x1c\x10d\xe5\rש"),
351			Tag{17229, Fixed32Type}, Uint32(2313295827),
352			Tag{3, EndGroupType},
353			Tag{1, Fixed32Type}, Uint32(1142540116),
354			Tag{13, Fixed64Type}, Uint64(2154683029754926136),
355			Tag{28856, BytesType},
356			Raw("\xbb"),
357		},
358		msgDesc: msgDesc,
359	}, {
360		raw: dhex("29baa4ac1c1e0a20183393bac434b8d3559337ec940050038770eaa9937f98e4"),
361		msg: Message{
362			Tag{5, Fixed64Type}, Uint64(1738400580611384506),
363			Tag{6, StartGroupType},
364			Message{
365				Tag{13771682, StartGroupType},
366				Message{
367					Tag{175415, VarintType}, Uvarint(7059),
368				},
369				Denormalized{+1, Tag{333, EndGroupType}},
370				Tag{10, VarintType}, Uvarint(3),
371				Tag{1792, Type(7)},
372				Raw("꩓\u007f\x98\xe4"),
373			},
374		},
375		msgDesc: msgDesc,
376	}}
377
378	equateFloatBits := cmp.Options{
379		cmp.Comparer(func(x, y Float32) bool {
380			return math.Float32bits(float32(x)) == math.Float32bits(float32(y))
381		}),
382		cmp.Comparer(func(x, y Float64) bool {
383			return math.Float64bits(float64(x)) == math.Float64bits(float64(y))
384		}),
385	}
386	for _, tt := range tests {
387		t.Run("", func(t *testing.T) {
388			var msg Message
389			raw := tt.msg.Marshal()
390			msg.unmarshal(tt.raw, tt.msgDesc, tt.inferMsg)
391
392			if !bytes.Equal(raw, tt.raw) {
393				t.Errorf("Marshal() mismatch:\ngot  %x\nwant %x", raw, tt.raw)
394			}
395			if diff := cmp.Diff(tt.msg, msg, equateFloatBits); diff != "" {
396				t.Errorf("Unmarshal() mismatch (-want +got):\n%s", diff)
397			}
398			if got, want := tt.msg.Size(), len(tt.raw); got != want {
399				t.Errorf("Size() = %v, want %v", got, want)
400			}
401			if tt.wantOutCompact != "" {
402				gotOut := fmt.Sprintf("%v", tt.msg)
403				if string(gotOut) != tt.wantOutCompact {
404					t.Errorf("fmt.Sprintf(%q, msg):\ngot:  %s\nwant: %s", "%v", gotOut, tt.wantOutCompact)
405				}
406			}
407			if tt.wantOutMulti != "" {
408				gotOut := fmt.Sprintf("%+v", tt.msg)
409				if string(gotOut) != tt.wantOutMulti {
410					t.Errorf("fmt.Sprintf(%q, msg):\ngot:  %s\nwant: %s", "%+v", gotOut, tt.wantOutMulti)
411				}
412			}
413			if tt.wantOutSource != "" {
414				gotOut := fmt.Sprintf("%#v", tt.msg)
415				if string(gotOut) != tt.wantOutSource {
416					t.Errorf("fmt.Sprintf(%q, msg):\ngot:  %s\nwant: %s", "%#v", gotOut, tt.wantOutSource)
417				}
418			}
419		})
420	}
421}
422