pack_test.go 14 KB

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