blob: 3a56ebbab355b784f6a82d5ddb60b1192b9068c6 [file] [log] [blame]
Joe Tsai3ab648c2018-08-15 14:41:30 -07001// 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 pack
6
7import (
8 "bytes"
9 "encoding/hex"
10 "fmt"
11 "math"
12 "testing"
13
Joe Tsai01ab2962018-09-21 17:44:00 -070014 pref "github.com/golang/protobuf/v2/reflect/protoreflect"
15 ptype "github.com/golang/protobuf/v2/reflect/prototype"
Joe Tsaie1f8d502018-11-26 18:55:29 -080016 cmp "github.com/google/go-cmp/cmp"
17
18 descriptorpb "github.com/golang/protobuf/v2/types/descriptor"
Joe Tsai3ab648c2018-08-15 14:41:30 -070019)
20
21var msgDesc = func() pref.MessageDescriptor {
22 mtyp, err := ptype.NewMessage(&ptype.StandaloneMessage{
23 Syntax: pref.Proto2,
24 FullName: "Message",
25 Fields: []ptype.Field{
Damien Neil204f1c02018-10-23 15:03:38 -070026 {Name: "F1", Number: 1, Cardinality: pref.Repeated, Kind: pref.BoolKind, Options: packedOpt(true)},
27 {Name: "F2", Number: 2, Cardinality: pref.Repeated, Kind: pref.Int64Kind, Options: packedOpt(true)},
28 {Name: "F3", Number: 3, Cardinality: pref.Repeated, Kind: pref.Sint64Kind, Options: packedOpt(true)},
29 {Name: "F4", Number: 4, Cardinality: pref.Repeated, Kind: pref.Uint64Kind, Options: packedOpt(true)},
30 {Name: "F5", Number: 5, Cardinality: pref.Repeated, Kind: pref.Fixed32Kind, Options: packedOpt(true)},
31 {Name: "F6", Number: 6, Cardinality: pref.Repeated, Kind: pref.Sfixed32Kind, Options: packedOpt(true)},
32 {Name: "F7", Number: 7, Cardinality: pref.Repeated, Kind: pref.FloatKind, Options: packedOpt(true)},
33 {Name: "F8", Number: 8, Cardinality: pref.Repeated, Kind: pref.Fixed64Kind, Options: packedOpt(true)},
34 {Name: "F9", Number: 9, Cardinality: pref.Repeated, Kind: pref.Sfixed64Kind, Options: packedOpt(true)},
35 {Name: "F10", Number: 10, Cardinality: pref.Repeated, Kind: pref.DoubleKind, Options: packedOpt(true)},
Joe Tsai3ab648c2018-08-15 14:41:30 -070036 {Name: "F11", Number: 11, Cardinality: pref.Optional, Kind: pref.StringKind},
37 {Name: "F12", Number: 12, Cardinality: pref.Optional, Kind: pref.BytesKind},
38 {Name: "F13", Number: 13, Cardinality: pref.Optional, Kind: pref.MessageKind, MessageType: ptype.PlaceholderMessage("Message")},
39 {Name: "F14", Number: 14, Cardinality: pref.Optional, Kind: pref.GroupKind, MessageType: ptype.PlaceholderMessage("Message")}},
40 })
41 if err != nil {
42 panic(err)
43 }
44 return mtyp
45}()
46
Joe Tsaie1f8d502018-11-26 18:55:29 -080047func packedOpt(b bool) *descriptorpb.FieldOptions {
48 return &descriptorpb.FieldOptions{Packed: &b}
Damien Neil204f1c02018-10-23 15:03:38 -070049}
50
Joe Tsai3ab648c2018-08-15 14:41:30 -070051// dhex decodes a hex-string and returns the bytes and panics if s is invalid.
52func dhex(s string) []byte {
53 b, err := hex.DecodeString(s)
54 if err != nil {
55 panic(err)
56 }
57 return b
58}
59
60func TestPack(t *testing.T) {
61 tests := []struct {
62 raw []byte
63 msg Message
64
65 wantOutCompact string
66 wantOutMulti string
67 wantOutSource string
68 }{{
69 raw: dhex("080088808080800002088280808080000a09010002828080808000"),
70 msg: Message{
71 Tag{1, VarintType}, Bool(false),
72 Denormalized{5, Tag{1, VarintType}}, Uvarint(2),
73 Tag{1, VarintType}, Denormalized{5, Uvarint(2)},
74 Tag{1, BytesType}, LengthPrefix{Bool(true), Bool(false), Uvarint(2), Denormalized{5, Uvarint(2)}},
75 },
76 wantOutSource: `pack.Message{
77 pack.Tag{1, pack.VarintType}, pack.Bool(false),
78 pack.Denormalized{+5, pack.Tag{1, pack.VarintType}}, pack.Uvarint(2),
79 pack.Tag{1, pack.VarintType}, pack.Denormalized{+5, pack.Uvarint(2)},
80 pack.Tag{1, pack.BytesType}, pack.LengthPrefix{pack.Bool(true), pack.Bool(false), pack.Uvarint(2), pack.Denormalized{+5, pack.Uvarint(2)}},
81}`,
82 }, {
83 raw: dhex("100010828080808000121980808080808080808001ffffffffffffffff7f828080808000"),
84 msg: Message{
85 Tag{2, VarintType}, Varint(0),
86 Tag{2, VarintType}, Denormalized{5, Varint(2)},
87 Tag{2, BytesType}, LengthPrefix{Varint(math.MinInt64), Varint(math.MaxInt64), Denormalized{5, Varint(2)}},
88 },
89 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)}}}`,
90 }, {
91 raw: dhex("1801188180808080001a1affffffffffffffffff01feffffffffffffffff01818080808000"),
92 msg: Message{
93 Tag{3, VarintType}, Svarint(-1),
94 Tag{3, VarintType}, Denormalized{5, Svarint(-1)},
95 Tag{3, BytesType}, LengthPrefix{Svarint(math.MinInt64), Svarint(math.MaxInt64), Denormalized{5, Svarint(-1)}},
96 },
97 wantOutMulti: `Message{
98 Tag{3, Varint}, Svarint(-1),
99 Tag{3, Varint}, Denormalized{+5, Svarint(-1)},
100 Tag{3, Bytes}, LengthPrefix{Svarint(-9223372036854775808), Svarint(9223372036854775807), Denormalized{+5, Svarint(-1)}},
101}`,
102 }, {
103 raw: dhex("200120818080808000221100ffffffffffffffffff01818080808000"),
104 msg: Message{
105 Tag{4, VarintType}, Uvarint(+1),
106 Tag{4, VarintType}, Denormalized{5, Uvarint(+1)},
107 Tag{4, BytesType}, LengthPrefix{Uvarint(0), Uvarint(math.MaxUint64), Denormalized{5, Uvarint(+1)}},
108 },
109 wantOutSource: `pack.Message{
110 pack.Tag{4, pack.VarintType}, pack.Uvarint(1),
111 pack.Tag{4, pack.VarintType}, pack.Denormalized{+5, pack.Uvarint(1)},
112 pack.Tag{4, pack.BytesType}, pack.LengthPrefix{pack.Uvarint(0), pack.Uvarint(18446744073709551615), pack.Denormalized{+5, pack.Uvarint(1)}},
113}`,
114 }, {
115 raw: dhex("2d010000002a0800000000ffffffff"),
116 msg: Message{
117 Tag{5, Fixed32Type}, Uint32(+1),
118 Tag{5, BytesType}, LengthPrefix{Uint32(0), Uint32(math.MaxUint32)},
119 },
120 wantOutCompact: `Message{Tag{5, Fixed32}, Uint32(1), Tag{5, Bytes}, LengthPrefix{Uint32(0), Uint32(4294967295)}}`,
121 }, {
122 raw: dhex("35ffffffff320800000080ffffff7f"),
123 msg: Message{
124 Tag{6, Fixed32Type}, Int32(-1),
125 Tag{6, BytesType}, LengthPrefix{Int32(math.MinInt32), Int32(math.MaxInt32)},
126 },
127 wantOutMulti: `Message{
128 Tag{6, Fixed32}, Int32(-1),
129 Tag{6, Bytes}, LengthPrefix{Int32(-2147483648), Int32(2147483647)},
130}`,
131 }, {
132 raw: dhex("3ddb0f49403a1401000000ffff7f7f0000c07f0000807f000080ff"),
133 msg: Message{
134 Tag{7, Fixed32Type}, Float32(math.Pi),
135 Tag{7, BytesType}, LengthPrefix{Float32(math.SmallestNonzeroFloat32), Float32(math.MaxFloat32), Float32(math.NaN()), Float32(math.Inf(+1)), Float32(math.Inf(-1))},
136 },
137 wantOutSource: `pack.Message{
138 pack.Tag{7, pack.Fixed32Type}, pack.Float32(3.1415927),
139 pack.Tag{7, pack.BytesType}, pack.LengthPrefix{pack.Float32(1e-45), pack.Float32(3.4028235e+38), pack.Float32(math.NaN()), pack.Float32(math.Inf(+1)), pack.Float32(math.Inf(-1))},
140}`,
141 }, {
142 raw: dhex("41010000000000000042100000000000000000ffffffffffffffff"),
143 msg: Message{
144 Tag{8, Fixed64Type}, Uint64(+1),
145 Tag{8, BytesType}, LengthPrefix{Uint64(0), Uint64(math.MaxUint64)},
146 },
147 wantOutCompact: `Message{Tag{8, Fixed64}, Uint64(1), Tag{8, Bytes}, LengthPrefix{Uint64(0), Uint64(18446744073709551615)}}`,
148 }, {
149 raw: dhex("49ffffffffffffffff4a100000000000000080ffffffffffffff7f"),
150 msg: Message{
151 Tag{9, Fixed64Type}, Int64(-1),
152 Tag{9, BytesType}, LengthPrefix{Int64(math.MinInt64), Int64(math.MaxInt64)},
153 },
154 wantOutMulti: `Message{
155 Tag{9, Fixed64}, Int64(-1),
156 Tag{9, Bytes}, LengthPrefix{Int64(-9223372036854775808), Int64(9223372036854775807)},
157}`,
158 }, {
159 raw: dhex("51182d4454fb21094052280100000000000000ffffffffffffef7f010000000000f87f000000000000f07f000000000000f0ff"),
160 msg: Message{
161 Tag{10, Fixed64Type}, Float64(math.Pi),
162 Tag{10, BytesType}, LengthPrefix{Float64(math.SmallestNonzeroFloat64), Float64(math.MaxFloat64), Float64(math.NaN()), Float64(math.Inf(+1)), Float64(math.Inf(-1))},
163 },
164 wantOutMulti: `Message{
165 Tag{10, Fixed64}, Float64(3.141592653589793),
166 Tag{10, Bytes}, LengthPrefix{Float64(5e-324), Float64(1.7976931348623157e+308), Float64(NaN), Float64(+Inf), Float64(-Inf)},
167}`,
168 }, {
169 raw: dhex("5a06737472696e675a868080808000737472696e67"),
170 msg: Message{
171 Tag{11, BytesType}, String("string"),
172 Tag{11, BytesType}, Denormalized{+5, String("string")},
173 },
174 wantOutCompact: `Message{Tag{11, Bytes}, String("string"), Tag{11, Bytes}, Denormalized{+5, String("string")}}`,
175 }, {
176 raw: dhex("62056279746573628580808080006279746573"),
177 msg: Message{
178 Tag{12, BytesType}, Bytes("bytes"),
179 Tag{12, BytesType}, Denormalized{+5, Bytes("bytes")},
180 },
181 wantOutMulti: `Message{
182 Tag{12, Bytes}, Bytes("bytes"),
183 Tag{12, Bytes}, Denormalized{+5, Bytes("bytes")},
184}`,
185 }, {
186 raw: dhex("6a28a006ffffffffffffffffff01a506ffffffffa106ffffffffffffffffa206056279746573a306a406"),
187 msg: Message{
188 Tag{13, BytesType}, LengthPrefix(Message{
189 Tag{100, VarintType}, Uvarint(math.MaxUint64),
190 Tag{100, Fixed32Type}, Uint32(math.MaxUint32),
191 Tag{100, Fixed64Type}, Uint64(math.MaxUint64),
192 Tag{100, BytesType}, Bytes("bytes"),
193 Tag{100, StartGroupType}, Tag{100, EndGroupType},
194 }),
195 },
196 wantOutSource: `pack.Message{
197 pack.Tag{13, pack.BytesType}, pack.LengthPrefix(pack.Message{
198 pack.Tag{100, pack.VarintType}, pack.Uvarint(18446744073709551615),
199 pack.Tag{100, pack.Fixed32Type}, pack.Uint32(4294967295),
200 pack.Tag{100, pack.Fixed64Type}, pack.Uint64(18446744073709551615),
201 pack.Tag{100, pack.BytesType}, pack.Bytes("bytes"),
202 pack.Tag{100, pack.StartGroupType},
203 pack.Tag{100, pack.EndGroupType},
204 }),
205}`,
206 }, {
207 raw: dhex("6aa88080808000a006ffffffffffffffffff01a506ffffffffa106ffffffffffffffffa206056279746573a306a406"),
208 msg: Message{
209 Tag{13, BytesType}, Denormalized{5, LengthPrefix(Message{
210 Tag{100, VarintType}, Uvarint(math.MaxUint64),
211 Tag{100, Fixed32Type}, Uint32(math.MaxUint32),
212 Tag{100, Fixed64Type}, Uint64(math.MaxUint64),
213 Tag{100, BytesType}, Bytes("bytes"),
214 Tag{100, StartGroupType}, Tag{100, EndGroupType},
215 })},
216 },
217 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}})}}`,
218 }, {
219 raw: dhex("73a006ffffffffffffffffff01a506ffffffffa106ffffffffffffffffa206056279746573a306a40674"),
220 msg: Message{
221 Tag{14, StartGroupType}, Message{
222 Tag{100, VarintType}, Uvarint(math.MaxUint64),
223 Tag{100, Fixed32Type}, Uint32(math.MaxUint32),
224 Tag{100, Fixed64Type}, Uint64(math.MaxUint64),
225 Tag{100, BytesType}, Bytes("bytes"),
226 Tag{100, StartGroupType}, Tag{100, EndGroupType},
227 },
228 Tag{14, EndGroupType},
229 },
230 wantOutMulti: `Message{
231 Tag{14, StartGroup},
232 Message{
233 Tag{100, Varint}, Uvarint(18446744073709551615),
234 Tag{100, Fixed32}, Uint32(4294967295),
235 Tag{100, Fixed64}, Uint64(18446744073709551615),
236 Tag{100, Bytes}, Bytes("bytes"),
237 Tag{100, StartGroup},
238 Tag{100, EndGroup},
239 },
240 Tag{14, EndGroup},
241}`,
242 }, {
243 raw: dhex("d0faa972cd02a5f09051c2d8aa0d6a26a89c311eddef024b423c0f6f47b64227a1600db56e3f73d4113096c9a88e2b99f2d847516853d76a1a6e9811c85a2ab3"),
244 msg: Message{
245 Tag{29970346, VarintType}, Uvarint(333),
246 Tag{21268228, Fixed32Type}, Uint32(229300418),
247 Tag{13, BytesType}, LengthPrefix(Message{
248 Tag{100805, VarintType}, Uvarint(30),
249 Tag{5883, Fixed32Type}, Uint32(255607371),
250 Tag{13, Type(7)},
251 Raw("G\xb6B'\xa1`\r\xb5n?s\xd4\x110\x96ɨ\x8e+\x99\xf2\xd8GQhS"),
252 }),
253 Tag{1706, Type(7)},
254 Raw("\x1an\x98\x11\xc8Z*\xb3"),
255 },
256 }, {
257 raw: dhex("3d08d0e57f"),
258 msg: Message{
259 Tag{7, Fixed32Type}, Float32(math.Float32frombits(
260 // TODO: Remove workaround for compiler bug (see https://golang.org/issues/27193).
261 func() uint32 { return 0x7fe5d008 }(),
262 )),
263 },
264 wantOutSource: `pack.Message{
265 pack.Tag{7, pack.Fixed32Type}, pack.Float32(math.Float32frombits(0x7fe5d008)),
266}`,
267 }, {
268 raw: dhex("51a8d65110771bf97f"),
269 msg: Message{
270 Tag{10, Fixed64Type}, Float64(math.Float64frombits(0x7ff91b771051d6a8)),
271 },
272 wantOutSource: `pack.Message{
273 pack.Tag{10, pack.Fixed64Type}, pack.Float64(math.Float64frombits(0x7ff91b771051d6a8)),
274}`,
275 }, {
276 raw: dhex("ab2c14481ab3e9a76d937fb4dd5e6c616ef311f62b7fe888785fca5609ffe81c1064e50dd7a9edb408d317e2891c0d54c719446938d41ab0ccf8e61dc28b0ebb"),
277 msg: Message{
278 Tag{709, StartGroupType},
279 Tag{2, EndGroupType},
280 Tag{9, VarintType}, Uvarint(26),
281 Tag{28655254, StartGroupType},
282 Message{
283 Tag{2034, StartGroupType},
284 Tag{194006, EndGroupType},
285 },
286 Tag{13, EndGroupType},
287 Tag{12, Fixed64Type}, Uint64(9865274810543764334),
288 Tag{15, VarintType}, Uvarint(95),
289 Tag{1385, BytesType}, Bytes("\xff\xe8\x1c\x10d\xe5\rש"),
290 Tag{17229, Fixed32Type}, Uint32(2313295827),
291 Tag{3, EndGroupType},
292 Tag{1, Fixed32Type}, Uint32(1142540116),
293 Tag{13, Fixed64Type}, Uint64(2154683029754926136),
294 Tag{28856, BytesType},
295 Raw("\xbb"),
296 },
297 }, {
298 raw: dhex("29baa4ac1c1e0a20183393bac434b8d3559337ec940050038770eaa9937f98e4"),
299 msg: Message{
300 Tag{5, Fixed64Type}, Uint64(1738400580611384506),
301 Tag{6, StartGroupType},
302 Message{
303 Tag{13771682, StartGroupType},
304 Message{
305 Tag{175415, VarintType}, Uvarint(7059),
306 },
307 Denormalized{+1, Tag{333, EndGroupType}},
308 Tag{10, VarintType}, Uvarint(3),
309 Tag{1792, Type(7)},
310 Raw("꩓\u007f\x98\xe4"),
311 },
312 },
313 }}
314
315 equateFloatBits := cmp.Options{
316 cmp.Comparer(func(x, y Float32) bool {
317 return math.Float32bits(float32(x)) == math.Float32bits(float32(y))
318 }),
319 cmp.Comparer(func(x, y Float64) bool {
320 return math.Float64bits(float64(x)) == math.Float64bits(float64(y))
321 }),
322 }
323 for _, tt := range tests {
324 t.Run("", func(t *testing.T) {
325 var msg Message
326 raw := tt.msg.Marshal()
327 msg.UnmarshalDescriptor(tt.raw, msgDesc)
328
329 if !bytes.Equal(raw, tt.raw) {
330 t.Errorf("Marshal() mismatch:\ngot %x\nwant %x", raw, tt.raw)
331 }
332 if !cmp.Equal(msg, tt.msg, equateFloatBits) {
333 t.Errorf("Unmarshal() mismatch:\ngot %+v\nwant %+v", msg, tt.msg)
334 }
335 if got, want := tt.msg.Size(), len(tt.raw); got != want {
336 t.Errorf("Size() = %v, want %v", got, want)
337 }
338 if tt.wantOutCompact != "" {
339 gotOut := fmt.Sprintf("%v", tt.msg)
340 if string(gotOut) != tt.wantOutCompact {
341 t.Errorf("fmt.Sprintf(%q, msg):\ngot: %s\nwant: %s", "%v", gotOut, tt.wantOutCompact)
342 }
343 }
344 if tt.wantOutMulti != "" {
345 gotOut := fmt.Sprintf("%+v", tt.msg)
346 if string(gotOut) != tt.wantOutMulti {
347 t.Errorf("fmt.Sprintf(%q, msg):\ngot: %s\nwant: %s", "%+v", gotOut, tt.wantOutMulti)
348 }
349 }
350 if tt.wantOutSource != "" {
351 gotOut := fmt.Sprintf("%#v", tt.msg)
352 if string(gotOut) != tt.wantOutSource {
353 t.Errorf("fmt.Sprintf(%q, msg):\ngot: %s\nwant: %s", "%#v", gotOut, tt.wantOutSource)
354 }
355 }
356 })
357 }
358}