blob: a439df0179f8c8282d0c9ee1f09272aa36f52ecb [file] [log] [blame]
Herbie Ongcddf8192018-11-28 18:25:20 -08001// 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 textpb_test
6
7import (
8 "math"
9 "strings"
10 "testing"
11
Herbie Ongf42b55f2019-01-02 15:46:07 -080012 protoV1 "github.com/golang/protobuf/proto"
Herbie Ongcf253082018-12-17 17:13:07 -080013 "github.com/golang/protobuf/protoapi"
Herbie Ongcddf8192018-11-28 18:25:20 -080014 "github.com/golang/protobuf/v2/encoding/textpb"
Herbie Ongcddf8192018-11-28 18:25:20 -080015 "github.com/golang/protobuf/v2/internal/detrand"
Herbie Ong20a1d312018-12-11 21:08:58 -080016 "github.com/golang/protobuf/v2/internal/encoding/pack"
Herbie Ongcf253082018-12-17 17:13:07 -080017 "github.com/golang/protobuf/v2/internal/encoding/wire"
Herbie Ongf42b55f2019-01-02 15:46:07 -080018 "github.com/golang/protobuf/v2/internal/impl"
Herbie Ongcf253082018-12-17 17:13:07 -080019 "github.com/golang/protobuf/v2/internal/legacy"
Herbie Ongcddf8192018-11-28 18:25:20 -080020 "github.com/golang/protobuf/v2/internal/scalar"
21 "github.com/golang/protobuf/v2/proto"
Herbie Ongf42b55f2019-01-02 15:46:07 -080022 preg "github.com/golang/protobuf/v2/reflect/protoregistry"
Herbie Ongcddf8192018-11-28 18:25:20 -080023 "github.com/google/go-cmp/cmp"
24 "github.com/google/go-cmp/cmp/cmpopts"
25
Joe Tsai08e00302018-11-26 22:32:06 -080026 // The legacy package must be imported prior to use of any legacy messages.
27 // TODO: Remove this when protoV1 registers these hooks for you.
28 _ "github.com/golang/protobuf/v2/internal/legacy"
29
Herbie Ongf42b55f2019-01-02 15:46:07 -080030 anypb "github.com/golang/protobuf/ptypes/any"
Joe Tsai08e00302018-11-26 22:32:06 -080031 "github.com/golang/protobuf/v2/encoding/textpb/testprotos/pb2"
32 "github.com/golang/protobuf/v2/encoding/textpb/testprotos/pb3"
Herbie Ongcddf8192018-11-28 18:25:20 -080033)
34
35func init() {
36 // Disable detrand to enable direct comparisons on outputs.
37 detrand.Disable()
38}
39
Herbie Ongcddf8192018-11-28 18:25:20 -080040// splitLines is a cmpopts.Option for comparing strings with line breaks.
41var splitLines = cmpopts.AcyclicTransformer("SplitLines", func(s string) []string {
42 return strings.Split(s, "\n")
43})
44
Herbie Ong800c9902018-12-06 15:28:53 -080045func pb2Enum(i int32) *pb2.Enum {
46 p := new(pb2.Enum)
47 *p = pb2.Enum(i)
48 return p
49}
50
51func pb2Enums_NestedEnum(i int32) *pb2.Enums_NestedEnum {
52 p := new(pb2.Enums_NestedEnum)
53 *p = pb2.Enums_NestedEnum(i)
54 return p
55}
56
Herbie Ongcf253082018-12-17 17:13:07 -080057func setExtension(m proto.Message, xd *protoapi.ExtensionDesc, val interface{}) {
58 xt := legacy.Export{}.ExtensionTypeFromDesc(xd)
59 knownFields := m.ProtoReflect().KnownFields()
60 extTypes := knownFields.ExtensionTypes()
61 extTypes.Register(xt)
62 if val == nil {
63 return
64 }
65 pval := xt.ValueOf(val)
66 knownFields.Set(wire.Number(xd.Field), pval)
67}
68
Herbie Ongcddf8192018-11-28 18:25:20 -080069func TestMarshal(t *testing.T) {
70 tests := []struct {
71 desc string
Herbie Ongf42b55f2019-01-02 15:46:07 -080072 mo textpb.MarshalOptions
Herbie Ong70651952018-12-13 14:19:50 -080073 input proto.Message
Herbie Ongcddf8192018-11-28 18:25:20 -080074 want string
75 wantErr bool
76 }{{
Herbie Ongcddf8192018-11-28 18:25:20 -080077 desc: "proto2 optional scalar fields not set",
Herbie Ong800c9902018-12-06 15:28:53 -080078 input: &pb2.Scalars{},
Herbie Ongcddf8192018-11-28 18:25:20 -080079 want: "\n",
80 }, {
81 desc: "proto3 scalar fields not set",
Herbie Ong800c9902018-12-06 15:28:53 -080082 input: &pb3.Scalars{},
Herbie Ongcddf8192018-11-28 18:25:20 -080083 want: "\n",
84 }, {
85 desc: "proto2 optional scalar fields set to zero values",
Herbie Ong800c9902018-12-06 15:28:53 -080086 input: &pb2.Scalars{
Herbie Ongcddf8192018-11-28 18:25:20 -080087 OptBool: scalar.Bool(false),
88 OptInt32: scalar.Int32(0),
89 OptInt64: scalar.Int64(0),
90 OptUint32: scalar.Uint32(0),
91 OptUint64: scalar.Uint64(0),
92 OptSint32: scalar.Int32(0),
93 OptSint64: scalar.Int64(0),
94 OptFixed32: scalar.Uint32(0),
95 OptFixed64: scalar.Uint64(0),
96 OptSfixed32: scalar.Int32(0),
97 OptSfixed64: scalar.Int64(0),
98 OptFloat: scalar.Float32(0),
99 OptDouble: scalar.Float64(0),
100 OptBytes: []byte{},
101 OptString: scalar.String(""),
Herbie Ong800c9902018-12-06 15:28:53 -0800102 },
Herbie Ongcddf8192018-11-28 18:25:20 -0800103 want: `opt_bool: false
104opt_int32: 0
105opt_int64: 0
106opt_uint32: 0
107opt_uint64: 0
108opt_sint32: 0
109opt_sint64: 0
110opt_fixed32: 0
111opt_fixed64: 0
112opt_sfixed32: 0
113opt_sfixed64: 0
114opt_float: 0
115opt_double: 0
116opt_bytes: ""
117opt_string: ""
118`,
119 }, {
120 desc: "proto3 scalar fields set to zero values",
Herbie Ong800c9902018-12-06 15:28:53 -0800121 input: &pb3.Scalars{
Herbie Ongcddf8192018-11-28 18:25:20 -0800122 SBool: false,
123 SInt32: 0,
124 SInt64: 0,
125 SUint32: 0,
126 SUint64: 0,
127 SSint32: 0,
128 SSint64: 0,
129 SFixed32: 0,
130 SFixed64: 0,
131 SSfixed32: 0,
132 SSfixed64: 0,
133 SFloat: 0,
134 SDouble: 0,
135 SBytes: []byte{},
136 SString: "",
Herbie Ong800c9902018-12-06 15:28:53 -0800137 },
Herbie Ongcddf8192018-11-28 18:25:20 -0800138 want: "\n",
139 }, {
140 desc: "proto2 optional scalar fields set to some values",
Herbie Ong800c9902018-12-06 15:28:53 -0800141 input: &pb2.Scalars{
Herbie Ongcddf8192018-11-28 18:25:20 -0800142 OptBool: scalar.Bool(true),
143 OptInt32: scalar.Int32(0xff),
144 OptInt64: scalar.Int64(0xdeadbeef),
145 OptUint32: scalar.Uint32(47),
146 OptUint64: scalar.Uint64(0xdeadbeef),
147 OptSint32: scalar.Int32(-1001),
148 OptSint64: scalar.Int64(-0xffff),
149 OptFixed64: scalar.Uint64(64),
150 OptSfixed32: scalar.Int32(-32),
151 // TODO: Update encoder to output same decimals.
152 OptFloat: scalar.Float32(1.02),
153 OptDouble: scalar.Float64(1.23e100),
154 // TODO: Update encoder to not output UTF8 for bytes.
155 OptBytes: []byte("\xe8\xb0\xb7\xe6\xad\x8c"),
156 OptString: scalar.String("谷歌"),
Herbie Ong800c9902018-12-06 15:28:53 -0800157 },
Herbie Ongcddf8192018-11-28 18:25:20 -0800158 want: `opt_bool: true
159opt_int32: 255
160opt_int64: 3735928559
161opt_uint32: 47
162opt_uint64: 3735928559
163opt_sint32: -1001
164opt_sint64: -65535
165opt_fixed64: 64
166opt_sfixed32: -32
167opt_float: 1.0199999809265137
168opt_double: 1.23e+100
169opt_bytes: "谷歌"
170opt_string: "谷歌"
171`,
172 }, {
Herbie Ongcddf8192018-11-28 18:25:20 -0800173 desc: "float32 nan",
Herbie Ong800c9902018-12-06 15:28:53 -0800174 input: &pb3.Scalars{
Herbie Ongcddf8192018-11-28 18:25:20 -0800175 SFloat: float32(math.NaN()),
Herbie Ong800c9902018-12-06 15:28:53 -0800176 },
Herbie Ongcddf8192018-11-28 18:25:20 -0800177 want: "s_float: nan\n",
178 }, {
179 desc: "float32 positive infinity",
Herbie Ong800c9902018-12-06 15:28:53 -0800180 input: &pb3.Scalars{
Herbie Ongcddf8192018-11-28 18:25:20 -0800181 SFloat: float32(math.Inf(1)),
Herbie Ong800c9902018-12-06 15:28:53 -0800182 },
Herbie Ongcddf8192018-11-28 18:25:20 -0800183 want: "s_float: inf\n",
184 }, {
185 desc: "float32 negative infinity",
Herbie Ong800c9902018-12-06 15:28:53 -0800186 input: &pb3.Scalars{
Herbie Ongcddf8192018-11-28 18:25:20 -0800187 SFloat: float32(math.Inf(-1)),
Herbie Ong800c9902018-12-06 15:28:53 -0800188 },
Herbie Ongcddf8192018-11-28 18:25:20 -0800189 want: "s_float: -inf\n",
190 }, {
191 desc: "float64 nan",
Herbie Ong800c9902018-12-06 15:28:53 -0800192 input: &pb3.Scalars{
Herbie Ongcddf8192018-11-28 18:25:20 -0800193 SDouble: math.NaN(),
Herbie Ong800c9902018-12-06 15:28:53 -0800194 },
Herbie Ongcddf8192018-11-28 18:25:20 -0800195 want: "s_double: nan\n",
196 }, {
197 desc: "float64 positive infinity",
Herbie Ong800c9902018-12-06 15:28:53 -0800198 input: &pb3.Scalars{
Herbie Ongcddf8192018-11-28 18:25:20 -0800199 SDouble: math.Inf(1),
Herbie Ong800c9902018-12-06 15:28:53 -0800200 },
Herbie Ongcddf8192018-11-28 18:25:20 -0800201 want: "s_double: inf\n",
202 }, {
203 desc: "float64 negative infinity",
Herbie Ong800c9902018-12-06 15:28:53 -0800204 input: &pb3.Scalars{
Herbie Ongcddf8192018-11-28 18:25:20 -0800205 SDouble: math.Inf(-1),
Herbie Ong800c9902018-12-06 15:28:53 -0800206 },
Herbie Ongcddf8192018-11-28 18:25:20 -0800207 want: "s_double: -inf\n",
208 }, {
209 desc: "proto2 bytes set to empty string",
Herbie Ong800c9902018-12-06 15:28:53 -0800210 input: &pb2.Scalars{
Herbie Ongcddf8192018-11-28 18:25:20 -0800211 OptBytes: []byte(""),
Herbie Ong800c9902018-12-06 15:28:53 -0800212 },
Herbie Ongcddf8192018-11-28 18:25:20 -0800213 want: "opt_bytes: \"\"\n",
214 }, {
215 desc: "proto3 bytes set to empty string",
Herbie Ong800c9902018-12-06 15:28:53 -0800216 input: &pb3.Scalars{
Herbie Ongcddf8192018-11-28 18:25:20 -0800217 SBytes: []byte(""),
Herbie Ong800c9902018-12-06 15:28:53 -0800218 },
Herbie Ongcddf8192018-11-28 18:25:20 -0800219 want: "\n",
220 }, {
Herbie Ong800c9902018-12-06 15:28:53 -0800221 desc: "proto2 enum not set",
222 input: &pb2.Enums{},
Herbie Ongcddf8192018-11-28 18:25:20 -0800223 want: "\n",
224 }, {
Herbie Ong800c9902018-12-06 15:28:53 -0800225 desc: "proto2 enum set to zero value",
226 input: &pb2.Enums{
227 OptEnum: pb2.Enum_UNKNOWN.Enum(),
228 OptNestedEnum: pb2Enums_NestedEnum(0),
229 },
230 want: `opt_enum: UNKNOWN
231opt_nested_enum: 0
232`,
233 }, {
234 desc: "proto2 enum",
235 input: &pb2.Enums{
236 OptEnum: pb2.Enum_FIRST.Enum(),
237 OptNestedEnum: pb2.Enums_UNO.Enum(),
238 },
239 want: `opt_enum: FIRST
240opt_nested_enum: UNO
241`,
242 }, {
243 desc: "proto2 enum set to numeric values",
244 input: &pb2.Enums{
245 OptEnum: pb2Enum(1),
246 OptNestedEnum: pb2Enums_NestedEnum(2),
247 },
248 want: `opt_enum: FIRST
249opt_nested_enum: DOS
250`,
251 }, {
252 desc: "proto2 enum set to unnamed numeric values",
253 input: &pb2.Enums{
254 OptEnum: pb2Enum(101),
255 OptNestedEnum: pb2Enums_NestedEnum(-101),
256 },
257 want: `opt_enum: 101
258opt_nested_enum: -101
259`,
260 }, {
261 desc: "proto3 enum not set",
262 input: &pb3.Enums{},
263 want: "\n",
264 }, {
265 desc: "proto3 enum set to zero value",
266 input: &pb3.Enums{
267 SEnum: pb3.Enum_ZERO,
268 SNestedEnum: pb3.Enums_CERO,
269 },
270 want: "\n",
271 }, {
272 desc: "proto3 enum",
273 input: &pb3.Enums{
274 SEnum: pb3.Enum_ONE,
275 SNestedEnum: pb3.Enums_DIEZ,
276 },
277 want: `s_enum: ONE
278s_nested_enum: DIEZ
279`,
280 }, {
281 desc: "proto3 enum set to numeric values",
282 input: &pb3.Enums{
283 SEnum: 2,
284 SNestedEnum: 1,
285 },
286 want: `s_enum: TWO
287s_nested_enum: UNO
288`,
289 }, {
290 desc: "proto3 enum set to unnamed numeric values",
291 input: &pb3.Enums{
292 SEnum: -47,
293 SNestedEnum: 47,
294 },
295 want: `s_enum: -47
296s_nested_enum: 47
297`,
298 }, {
299 desc: "proto2 nested message not set",
300 input: &pb2.Nests{},
301 want: "\n",
302 }, {
303 desc: "proto2 nested message set to empty",
304 input: &pb2.Nests{
305 OptNested: &pb2.Nested{},
306 Optgroup: &pb2.Nests_OptGroup{},
307 },
308 want: `opt_nested: {}
309optgroup: {}
310`,
311 }, {
312 desc: "proto2 nested messages",
313 input: &pb2.Nests{
314 OptNested: &pb2.Nested{
315 OptString: scalar.String("nested message"),
316 OptNested: &pb2.Nested{
317 OptString: scalar.String("another nested message"),
318 },
319 },
320 },
321 want: `opt_nested: {
322 opt_string: "nested message"
323 opt_nested: {
324 opt_string: "another nested message"
325 }
326}
327`,
328 }, {
329 desc: "proto2 group fields",
330 input: &pb2.Nests{
331 Optgroup: &pb2.Nests_OptGroup{
332 OptBool: scalar.Bool(true),
333 OptString: scalar.String("inside a group"),
334 OptNested: &pb2.Nested{
335 OptString: scalar.String("nested message inside a group"),
336 },
337 Optnestedgroup: &pb2.Nests_OptGroup_OptNestedGroup{
338 OptEnum: pb2.Enum_TENTH.Enum(),
339 },
340 },
341 },
342 want: `optgroup: {
343 opt_bool: true
344 opt_string: "inside a group"
345 opt_nested: {
346 opt_string: "nested message inside a group"
347 }
348 optnestedgroup: {
349 opt_enum: TENTH
350 }
351}
352`,
353 }, {
354 desc: "proto3 nested message not set",
355 input: &pb3.Nests{},
356 want: "\n",
357 }, {
358 desc: "proto3 nested message",
359 input: &pb3.Nests{
360 SNested: &pb3.Nested{
361 SString: "nested message",
362 SNested: &pb3.Nested{
363 SString: "another nested message",
364 },
365 },
366 },
367 want: `s_nested: {
368 s_string: "nested message"
369 s_nested: {
370 s_string: "another nested message"
371 }
372}
373`,
374 }, {
375 desc: "oneof fields",
376 input: &pb2.Oneofs{},
377 want: "\n",
378 }, {
379 desc: "oneof field set to empty string",
380 input: &pb2.Oneofs{
381 Union: &pb2.Oneofs_Str{},
382 },
383 want: "str: \"\"\n",
384 }, {
385 desc: "oneof field set to string",
386 input: &pb2.Oneofs{
387 Union: &pb2.Oneofs_Str{
388 Str: "hello",
389 },
390 },
391 want: "str: \"hello\"\n",
392 }, {
393 desc: "oneof field set to empty message",
394 input: &pb2.Oneofs{
395 Union: &pb2.Oneofs_Msg{
396 Msg: &pb2.Nested{},
397 },
398 },
399 want: "msg: {}\n",
400 }, {
401 desc: "oneof field set to message",
402 input: &pb2.Oneofs{
403 Union: &pb2.Oneofs_Msg{
404 Msg: &pb2.Nested{
405 OptString: scalar.String("nested message"),
406 },
407 },
408 },
409 want: `msg: {
410 opt_string: "nested message"
411}
412`,
413 }, {
414 desc: "repeated not set",
415 input: &pb2.Repeats{},
416 want: "\n",
417 }, {
418 desc: "repeated set to empty slices",
419 input: &pb2.Repeats{
Herbie Ongcddf8192018-11-28 18:25:20 -0800420 RptBool: []bool{},
421 RptInt32: []int32{},
422 RptInt64: []int64{},
423 RptUint32: []uint32{},
424 RptUint64: []uint64{},
425 RptFloat: []float32{},
426 RptDouble: []float64{},
427 RptBytes: [][]byte{},
Herbie Ong800c9902018-12-06 15:28:53 -0800428 },
Herbie Ongcddf8192018-11-28 18:25:20 -0800429 want: "\n",
430 }, {
Herbie Ong800c9902018-12-06 15:28:53 -0800431 desc: "repeated set to some values",
432 input: &pb2.Repeats{
Herbie Ongcddf8192018-11-28 18:25:20 -0800433 RptBool: []bool{true, false, true, true},
434 RptInt32: []int32{1, 6, 0, 0},
435 RptInt64: []int64{-64, 47},
436 RptUint32: []uint32{0xff, 0xffff},
437 RptUint64: []uint64{0xdeadbeef},
438 // TODO: add float32 examples.
439 RptDouble: []float64{math.NaN(), math.Inf(1), math.Inf(-1), 1.23e-308},
440 RptString: []string{"hello", "世界"},
441 RptBytes: [][]byte{
442 []byte("hello"),
443 []byte("\xe4\xb8\x96\xe7\x95\x8c"),
444 },
Herbie Ong800c9902018-12-06 15:28:53 -0800445 },
Herbie Ongcddf8192018-11-28 18:25:20 -0800446 want: `rpt_bool: true
447rpt_bool: false
448rpt_bool: true
449rpt_bool: true
450rpt_int32: 1
451rpt_int32: 6
452rpt_int32: 0
453rpt_int32: 0
454rpt_int64: -64
455rpt_int64: 47
456rpt_uint32: 255
457rpt_uint32: 65535
458rpt_uint64: 3735928559
459rpt_double: nan
460rpt_double: inf
461rpt_double: -inf
462rpt_double: 1.23e-308
463rpt_string: "hello"
464rpt_string: "世界"
465rpt_bytes: "hello"
466rpt_bytes: "世界"
467`,
468 }, {
Herbie Ong800c9902018-12-06 15:28:53 -0800469 desc: "repeated enum",
470 input: &pb2.Enums{
Herbie Ongcddf8192018-11-28 18:25:20 -0800471 RptEnum: []pb2.Enum{pb2.Enum_FIRST, 2, pb2.Enum_TENTH, 42},
Herbie Ongcddf8192018-11-28 18:25:20 -0800472 RptNestedEnum: []pb2.Enums_NestedEnum{2, 47, 10},
Herbie Ong800c9902018-12-06 15:28:53 -0800473 },
474 want: `rpt_enum: FIRST
Herbie Ongcddf8192018-11-28 18:25:20 -0800475rpt_enum: SECOND
476rpt_enum: TENTH
477rpt_enum: 42
Herbie Ongcddf8192018-11-28 18:25:20 -0800478rpt_nested_enum: DOS
479rpt_nested_enum: 47
480rpt_nested_enum: DIEZ
481`,
482 }, {
Herbie Ong800c9902018-12-06 15:28:53 -0800483 desc: "repeated nested message set to empty",
484 input: &pb2.Nests{
Herbie Ongcddf8192018-11-28 18:25:20 -0800485 RptNested: []*pb2.Nested{},
486 Rptgroup: []*pb2.Nests_RptGroup{},
Herbie Ong800c9902018-12-06 15:28:53 -0800487 },
488 want: "\n",
Herbie Ongcddf8192018-11-28 18:25:20 -0800489 }, {
Herbie Ong800c9902018-12-06 15:28:53 -0800490 desc: "repeated nested messages",
491 input: &pb2.Nests{
Herbie Ongcddf8192018-11-28 18:25:20 -0800492 RptNested: []*pb2.Nested{
493 {
494 OptString: scalar.String("repeat nested one"),
495 },
496 {
497 OptString: scalar.String("repeat nested two"),
498 OptNested: &pb2.Nested{
499 OptString: scalar.String("inside repeat nested two"),
500 },
501 },
502 {},
503 },
Herbie Ong800c9902018-12-06 15:28:53 -0800504 },
505 want: `rpt_nested: {
Herbie Ongcddf8192018-11-28 18:25:20 -0800506 opt_string: "repeat nested one"
507}
508rpt_nested: {
509 opt_string: "repeat nested two"
510 opt_nested: {
511 opt_string: "inside repeat nested two"
512 }
513}
514rpt_nested: {}
515`,
516 }, {
Herbie Ong800c9902018-12-06 15:28:53 -0800517 desc: "repeated group fields",
518 input: &pb2.Nests{
Herbie Ongcddf8192018-11-28 18:25:20 -0800519 Rptgroup: []*pb2.Nests_RptGroup{
520 {
521 RptBool: []bool{true, false},
522 },
523 {},
524 },
Herbie Ong800c9902018-12-06 15:28:53 -0800525 },
526 want: `rptgroup: {
Herbie Ongcddf8192018-11-28 18:25:20 -0800527 rpt_bool: true
528 rpt_bool: false
529}
530rptgroup: {}
531`,
532 }, {
Herbie Ongcddf8192018-11-28 18:25:20 -0800533 desc: "map fields empty",
Herbie Ong800c9902018-12-06 15:28:53 -0800534 input: &pb2.Maps{},
Herbie Ongcddf8192018-11-28 18:25:20 -0800535 want: "\n",
536 }, {
537 desc: "map fields set to empty maps",
Herbie Ong800c9902018-12-06 15:28:53 -0800538 input: &pb2.Maps{
Herbie Ongcddf8192018-11-28 18:25:20 -0800539 Int32ToStr: map[int32]string{},
540 Sfixed64ToBool: map[int64]bool{},
541 BoolToUint32: map[bool]uint32{},
542 Uint64ToEnum: map[uint64]pb2.Enum{},
543 StrToNested: map[string]*pb2.Nested{},
544 StrToOneofs: map[string]*pb2.Oneofs{},
Herbie Ong800c9902018-12-06 15:28:53 -0800545 },
Herbie Ongcddf8192018-11-28 18:25:20 -0800546 want: "\n",
547 }, {
548 desc: "map fields 1",
Herbie Ong800c9902018-12-06 15:28:53 -0800549 input: &pb2.Maps{
Herbie Ongcddf8192018-11-28 18:25:20 -0800550 Int32ToStr: map[int32]string{
551 -101: "-101",
552 0xff: "0xff",
553 0: "zero",
554 },
555 Sfixed64ToBool: map[int64]bool{
556 0xcafe: true,
557 0: false,
558 },
559 BoolToUint32: map[bool]uint32{
560 true: 42,
561 false: 101,
562 },
Herbie Ong800c9902018-12-06 15:28:53 -0800563 },
Herbie Ongcddf8192018-11-28 18:25:20 -0800564 want: `int32_to_str: {
565 key: -101
566 value: "-101"
567}
568int32_to_str: {
569 key: 0
570 value: "zero"
571}
572int32_to_str: {
573 key: 255
574 value: "0xff"
575}
576sfixed64_to_bool: {
577 key: 0
578 value: false
579}
580sfixed64_to_bool: {
581 key: 51966
582 value: true
583}
584bool_to_uint32: {
585 key: false
586 value: 101
587}
588bool_to_uint32: {
589 key: true
590 value: 42
591}
592`,
593 }, {
594 desc: "map fields 2",
Herbie Ong800c9902018-12-06 15:28:53 -0800595 input: &pb2.Maps{
Herbie Ongcddf8192018-11-28 18:25:20 -0800596 Uint64ToEnum: map[uint64]pb2.Enum{
597 1: pb2.Enum_FIRST,
598 2: pb2.Enum_SECOND,
599 10: pb2.Enum_TENTH,
600 },
Herbie Ong800c9902018-12-06 15:28:53 -0800601 },
Herbie Ongcddf8192018-11-28 18:25:20 -0800602 want: `uint64_to_enum: {
603 key: 1
604 value: FIRST
605}
606uint64_to_enum: {
607 key: 2
608 value: SECOND
609}
610uint64_to_enum: {
611 key: 10
612 value: TENTH
613}
614`,
615 }, {
616 desc: "map fields 3",
Herbie Ong800c9902018-12-06 15:28:53 -0800617 input: &pb2.Maps{
Herbie Ongcddf8192018-11-28 18:25:20 -0800618 StrToNested: map[string]*pb2.Nested{
619 "nested_one": &pb2.Nested{
620 OptString: scalar.String("nested in a map"),
621 },
622 },
Herbie Ong800c9902018-12-06 15:28:53 -0800623 },
Herbie Ongcddf8192018-11-28 18:25:20 -0800624 want: `str_to_nested: {
625 key: "nested_one"
626 value: {
627 opt_string: "nested in a map"
628 }
629}
630`,
631 }, {
632 desc: "map fields 4",
Herbie Ong800c9902018-12-06 15:28:53 -0800633 input: &pb2.Maps{
Herbie Ongcddf8192018-11-28 18:25:20 -0800634 StrToOneofs: map[string]*pb2.Oneofs{
635 "string": &pb2.Oneofs{
636 Union: &pb2.Oneofs_Str{
637 Str: "hello",
638 },
639 },
640 "nested": &pb2.Oneofs{
641 Union: &pb2.Oneofs_Msg{
642 Msg: &pb2.Nested{
643 OptString: scalar.String("nested oneof in map field value"),
644 },
645 },
646 },
647 },
Herbie Ong800c9902018-12-06 15:28:53 -0800648 },
Herbie Ongcddf8192018-11-28 18:25:20 -0800649 want: `str_to_oneofs: {
650 key: "nested"
651 value: {
652 msg: {
653 opt_string: "nested oneof in map field value"
654 }
655 }
656}
657str_to_oneofs: {
658 key: "string"
659 value: {
660 str: "hello"
661 }
662}
663`,
664 }, {
Herbie Ong800c9902018-12-06 15:28:53 -0800665 desc: "proto2 required fields not set",
666 input: &pb2.Requireds{},
667 want: "\n",
668 wantErr: true,
Herbie Ongcddf8192018-11-28 18:25:20 -0800669 }, {
Herbie Ong800c9902018-12-06 15:28:53 -0800670 desc: "proto2 required fields partially set",
671 input: &pb2.Requireds{
672 ReqBool: scalar.Bool(false),
673 ReqFixed32: scalar.Uint32(47),
674 ReqSfixed64: scalar.Int64(0xbeefcafe),
675 ReqDouble: scalar.Float64(math.NaN()),
676 ReqString: scalar.String("hello"),
677 ReqEnum: pb2.Enum_FIRST.Enum(),
678 },
679 want: `req_bool: false
680req_fixed32: 47
681req_sfixed64: 3203386110
682req_double: nan
683req_string: "hello"
684req_enum: FIRST
685`,
686 wantErr: true,
687 }, {
688 desc: "proto2 required fields all set",
689 input: &pb2.Requireds{
690 ReqBool: scalar.Bool(false),
691 ReqFixed32: scalar.Uint32(0),
692 ReqFixed64: scalar.Uint64(0),
693 ReqSfixed32: scalar.Int32(0),
694 ReqSfixed64: scalar.Int64(0),
695 ReqFloat: scalar.Float32(0),
696 ReqDouble: scalar.Float64(0),
697 ReqString: scalar.String(""),
698 ReqEnum: pb2.Enum_UNKNOWN.Enum(),
699 ReqBytes: []byte{},
700 ReqNested: &pb2.Nested{},
701 },
702 want: `req_bool: false
703req_fixed32: 0
704req_fixed64: 0
705req_sfixed32: 0
706req_sfixed64: 0
707req_float: 0
708req_double: 0
709req_string: ""
710req_bytes: ""
711req_enum: UNKNOWN
712req_nested: {}
Herbie Ongcddf8192018-11-28 18:25:20 -0800713`,
714 }, {
Herbie Ong800c9902018-12-06 15:28:53 -0800715 desc: "indirect required field",
716 input: &pb2.IndirectRequired{
717 OptNested: &pb2.NestedWithRequired{},
718 },
719 want: "opt_nested: {}\n",
720 wantErr: true,
Herbie Ongcddf8192018-11-28 18:25:20 -0800721 }, {
Herbie Ong800c9902018-12-06 15:28:53 -0800722 desc: "indirect required field in empty repeated",
723 input: &pb2.IndirectRequired{
724 RptNested: []*pb2.NestedWithRequired{},
725 },
726 want: "\n",
Herbie Ongcddf8192018-11-28 18:25:20 -0800727 }, {
Herbie Ong800c9902018-12-06 15:28:53 -0800728 desc: "indirect required field in repeated",
729 input: &pb2.IndirectRequired{
730 RptNested: []*pb2.NestedWithRequired{
731 &pb2.NestedWithRequired{},
Herbie Ongcddf8192018-11-28 18:25:20 -0800732 },
Herbie Ong800c9902018-12-06 15:28:53 -0800733 },
734 want: "rpt_nested: {}\n",
735 wantErr: true,
736 }, {
737 desc: "indirect required field in empty map",
738 input: &pb2.IndirectRequired{
739 StrToNested: map[string]*pb2.NestedWithRequired{},
740 },
741 want: "\n",
742 }, {
743 desc: "indirect required field in map",
744 input: &pb2.IndirectRequired{
745 StrToNested: map[string]*pb2.NestedWithRequired{
746 "fail": &pb2.NestedWithRequired{},
747 },
748 },
749 want: `str_to_nested: {
750 key: "fail"
751 value: {}
Herbie Ongcddf8192018-11-28 18:25:20 -0800752}
753`,
Herbie Ong800c9902018-12-06 15:28:53 -0800754 wantErr: true,
Herbie Ong20a1d312018-12-11 21:08:58 -0800755 }, {
756 desc: "unknown varint and fixed types",
757 input: &pb2.Scalars{
758 OptString: scalar.String("this message contains unknown fields"),
759 XXX_unrecognized: pack.Message{
760 pack.Tag{101, pack.VarintType}, pack.Bool(true),
761 pack.Tag{102, pack.VarintType}, pack.Varint(0xff),
762 pack.Tag{103, pack.Fixed32Type}, pack.Uint32(47),
763 pack.Tag{104, pack.Fixed64Type}, pack.Int64(0xdeadbeef),
764 }.Marshal(),
765 },
766 want: `opt_string: "this message contains unknown fields"
767101: 1
768102: 255
769103: 47
770104: 3735928559
771`,
772 }, {
773 desc: "unknown length-delimited",
774 input: &pb2.Scalars{
775 XXX_unrecognized: pack.Message{
776 pack.Tag{101, pack.BytesType}, pack.LengthPrefix{pack.Bool(true), pack.Bool(false)},
777 pack.Tag{102, pack.BytesType}, pack.String("hello world"),
778 pack.Tag{103, pack.BytesType}, pack.Bytes("\xe4\xb8\x96\xe7\x95\x8c"),
779 }.Marshal(),
780 },
781 want: `101: "\x01\x00"
782102: "hello world"
783103: "世界"
784`,
785 }, {
786 desc: "unknown group type",
787 input: &pb2.Scalars{
788 XXX_unrecognized: pack.Message{
789 pack.Tag{101, pack.StartGroupType}, pack.Tag{101, pack.EndGroupType},
790 pack.Tag{102, pack.StartGroupType},
791 pack.Tag{101, pack.VarintType}, pack.Bool(false),
792 pack.Tag{102, pack.BytesType}, pack.String("inside a group"),
793 pack.Tag{102, pack.EndGroupType},
794 }.Marshal(),
795 },
796 want: `101: {}
797102: {
798 101: 0
799 102: "inside a group"
800}
801`,
802 }, {
803 desc: "unknown unpack repeated field",
804 input: &pb2.Scalars{
805 XXX_unrecognized: pack.Message{
806 pack.Tag{101, pack.BytesType}, pack.LengthPrefix{pack.Bool(true), pack.Bool(false), pack.Bool(true)},
807 pack.Tag{102, pack.BytesType}, pack.String("hello"),
808 pack.Tag{101, pack.VarintType}, pack.Bool(true),
809 pack.Tag{102, pack.BytesType}, pack.String("世界"),
810 }.Marshal(),
811 },
812 want: `101: "\x01\x00\x01"
813101: 1
814102: "hello"
815102: "世界"
816`,
Herbie Ongcf253082018-12-17 17:13:07 -0800817 }, {
818 desc: "extensions of non-repeated fields",
819 input: func() proto.Message {
820 m := &pb2.Extensions{
821 OptString: scalar.String("non-extension field"),
822 OptBool: scalar.Bool(true),
823 OptInt32: scalar.Int32(42),
824 }
825 setExtension(m, pb2.E_OptExtBool, true)
826 setExtension(m, pb2.E_OptExtString, "extension field")
827 setExtension(m, pb2.E_OptExtEnum, pb2.Enum_TENTH)
828 setExtension(m, pb2.E_OptExtNested, &pb2.Nested{
829 OptString: scalar.String("nested in an extension"),
830 OptNested: &pb2.Nested{
831 OptString: scalar.String("another nested in an extension"),
832 },
833 })
834 return m
835 }(),
836 want: `opt_string: "non-extension field"
837opt_bool: true
838opt_int32: 42
839[pb2.opt_ext_bool]: true
840[pb2.opt_ext_enum]: TENTH
841[pb2.opt_ext_nested]: {
842 opt_string: "nested in an extension"
843 opt_nested: {
844 opt_string: "another nested in an extension"
845 }
846}
847[pb2.opt_ext_string]: "extension field"
848`,
849 }, {
850 desc: "registered extension but not set",
851 input: func() proto.Message {
852 m := &pb2.Extensions{}
853 setExtension(m, pb2.E_OptExtNested, nil)
854 return m
855 }(),
856 want: "\n",
857 }, {
858 desc: "extensions of repeated fields",
859 input: func() proto.Message {
860 m := &pb2.Extensions{}
861 setExtension(m, pb2.E_RptExtEnum, &[]pb2.Enum{pb2.Enum_TENTH, 101, pb2.Enum_FIRST})
862 setExtension(m, pb2.E_RptExtFixed32, &[]uint32{42, 47})
863 setExtension(m, pb2.E_RptExtNested, &[]*pb2.Nested{
864 &pb2.Nested{OptString: scalar.String("one")},
865 &pb2.Nested{OptString: scalar.String("two")},
866 &pb2.Nested{OptString: scalar.String("three")},
867 })
868 return m
869 }(),
870 want: `[pb2.rpt_ext_enum]: TENTH
871[pb2.rpt_ext_enum]: 101
872[pb2.rpt_ext_enum]: FIRST
873[pb2.rpt_ext_fixed32]: 42
874[pb2.rpt_ext_fixed32]: 47
875[pb2.rpt_ext_nested]: {
876 opt_string: "one"
877}
878[pb2.rpt_ext_nested]: {
879 opt_string: "two"
880}
881[pb2.rpt_ext_nested]: {
882 opt_string: "three"
883}
884`,
885 }, {
886 desc: "extensions of non-repeated fields in another message",
887 input: func() proto.Message {
888 m := &pb2.Extensions{}
889 setExtension(m, pb2.E_ExtensionsContainer_OptExtBool, true)
890 setExtension(m, pb2.E_ExtensionsContainer_OptExtString, "extension field")
891 setExtension(m, pb2.E_ExtensionsContainer_OptExtEnum, pb2.Enum_TENTH)
892 setExtension(m, pb2.E_ExtensionsContainer_OptExtNested, &pb2.Nested{
893 OptString: scalar.String("nested in an extension"),
894 OptNested: &pb2.Nested{
895 OptString: scalar.String("another nested in an extension"),
896 },
897 })
898 return m
899 }(),
900 want: `[pb2.ExtensionsContainer.opt_ext_bool]: true
901[pb2.ExtensionsContainer.opt_ext_enum]: TENTH
902[pb2.ExtensionsContainer.opt_ext_nested]: {
903 opt_string: "nested in an extension"
904 opt_nested: {
905 opt_string: "another nested in an extension"
906 }
907}
908[pb2.ExtensionsContainer.opt_ext_string]: "extension field"
909`,
910 }, {
911 desc: "extensions of repeated fields in another message",
912 input: func() proto.Message {
913 m := &pb2.Extensions{
914 OptString: scalar.String("non-extension field"),
915 OptBool: scalar.Bool(true),
916 OptInt32: scalar.Int32(42),
917 }
918 setExtension(m, pb2.E_ExtensionsContainer_RptExtEnum, &[]pb2.Enum{pb2.Enum_TENTH, 101, pb2.Enum_FIRST})
919 setExtension(m, pb2.E_ExtensionsContainer_RptExtString, &[]string{"hello", "world"})
920 setExtension(m, pb2.E_ExtensionsContainer_RptExtNested, &[]*pb2.Nested{
921 &pb2.Nested{OptString: scalar.String("one")},
922 &pb2.Nested{OptString: scalar.String("two")},
923 &pb2.Nested{OptString: scalar.String("three")},
924 })
925 return m
926 }(),
927 want: `opt_string: "non-extension field"
928opt_bool: true
929opt_int32: 42
930[pb2.ExtensionsContainer.rpt_ext_enum]: TENTH
931[pb2.ExtensionsContainer.rpt_ext_enum]: 101
932[pb2.ExtensionsContainer.rpt_ext_enum]: FIRST
933[pb2.ExtensionsContainer.rpt_ext_nested]: {
934 opt_string: "one"
935}
936[pb2.ExtensionsContainer.rpt_ext_nested]: {
937 opt_string: "two"
938}
939[pb2.ExtensionsContainer.rpt_ext_nested]: {
940 opt_string: "three"
941}
942[pb2.ExtensionsContainer.rpt_ext_string]: "hello"
943[pb2.ExtensionsContainer.rpt_ext_string]: "world"
944`,
945 /* TODO: test for MessageSet
946 }, {
947 desc: "MessageSet",
948 input: func() proto.Message {
949 m := &pb2.MessageSet{}
950 setExtension(m, pb2.E_MessageSetExtension_MessageSetExtension, &pb2.MessageSetExtension{
951 OptString: scalar.String("a messageset extension"),
952 })
953 setExtension(m, pb2.E_MessageSetExtension_NotMessageSetExtension, &pb2.MessageSetExtension{
954 OptString: scalar.String("not a messageset extension"),
955 })
956 setExtension(m, pb2.E_MessageSetExtension_ExtNested, &pb2.Nested{
957 OptString: scalar.String("just a regular extension"),
958 })
959 return m
960 }(),
961 want: `[pb2.MessageSetExtension]: {
962 opt_string: "a messageset extension"
963 }
964 [pb2.MessageSetExtension.ext_nested]: {
965 opt_string: "just a regular extension"
966 }
967 [pb2.MessageSetExtension.not_message_set_extension]: {
968 opt_string: "not a messageset extension"
969 }
970 `,
971 */
Herbie Ongf42b55f2019-01-02 15:46:07 -0800972 }, {
973 desc: "google.protobuf.Any message not expanded",
974 mo: textpb.MarshalOptions{Resolver: preg.NewTypes()},
975 input: func() proto.Message {
976 m := &pb2.Nested{
977 OptString: scalar.String("embedded inside Any"),
978 OptNested: &pb2.Nested{
979 OptString: scalar.String("inception"),
980 },
981 }
982 // TODO: Switch to V2 marshal when ready.
983 b, err := protoV1.Marshal(m)
984 if err != nil {
985 t.Fatalf("error in binary marshaling message for Any.value: %v", err)
986 }
987 return impl.Export{}.MessageOf(&anypb.Any{
988 TypeUrl: string(m.ProtoReflect().Type().FullName()),
989 Value: b,
990 }).Interface()
991 }(),
992 want: `type_url: "pb2.Nested"
993value: "\n\x13embedded inside Any\x12\x0b\n\tinception"
994`,
995 }, {
996 desc: "google.protobuf.Any message expanded",
997 mo: func() textpb.MarshalOptions {
998 m := &pb2.Nested{}
999 resolver := preg.NewTypes(m.ProtoReflect().Type())
1000 return textpb.MarshalOptions{Resolver: resolver}
1001 }(),
1002 input: func() proto.Message {
1003 m := &pb2.Nested{
1004 OptString: scalar.String("embedded inside Any"),
1005 OptNested: &pb2.Nested{
1006 OptString: scalar.String("inception"),
1007 },
1008 }
1009 // TODO: Switch to V2 marshal when ready.
1010 b, err := protoV1.Marshal(m)
1011 if err != nil {
1012 t.Fatalf("error in binary marshaling message for Any.value: %v", err)
1013 }
1014 return impl.Export{}.MessageOf(&anypb.Any{
1015 TypeUrl: string(m.ProtoReflect().Type().FullName()),
1016 Value: b,
1017 }).Interface()
1018 }(),
1019 want: `[pb2.Nested]: {
1020 opt_string: "embedded inside Any"
1021 opt_nested: {
1022 opt_string: "inception"
1023 }
1024}
1025`,
1026 }, {
1027 desc: "google.protobuf.Any message expanded with missing required error",
1028 mo: func() textpb.MarshalOptions {
1029 m := &pb2.PartialRequired{}
1030 resolver := preg.NewTypes(m.ProtoReflect().Type())
1031 return textpb.MarshalOptions{Resolver: resolver}
1032 }(),
1033 input: func() proto.Message {
1034 m := &pb2.PartialRequired{
1035 OptString: scalar.String("embedded inside Any"),
1036 }
1037 // TODO: Switch to V2 marshal when ready.
1038 b, err := protoV1.Marshal(m)
1039 // Ignore required not set error.
1040 if _, ok := err.(*protoV1.RequiredNotSetError); !ok {
1041 t.Fatalf("error in binary marshaling message for Any.value: %v", err)
1042 }
1043 return impl.Export{}.MessageOf(&anypb.Any{
1044 TypeUrl: string(m.ProtoReflect().Type().FullName()),
1045 Value: b,
1046 }).Interface()
1047 }(),
1048 want: `[pb2.PartialRequired]: {
1049 opt_string: "embedded inside Any"
1050}
1051`,
1052 wantErr: true,
1053 }, {
1054 desc: "google.protobuf.Any field",
1055 mo: textpb.MarshalOptions{Resolver: preg.NewTypes()},
1056 input: func() proto.Message {
1057 m := &pb2.Nested{
1058 OptString: scalar.String("embedded inside Any"),
1059 OptNested: &pb2.Nested{
1060 OptString: scalar.String("inception"),
1061 },
1062 }
1063 // TODO: Switch to V2 marshal when ready.
1064 b, err := protoV1.Marshal(m)
1065 if err != nil {
1066 t.Fatalf("error in binary marshaling message for Any.value: %v", err)
1067 }
1068 return &pb2.KnownTypes{
1069 OptAny: &anypb.Any{
1070 TypeUrl: string(m.ProtoReflect().Type().FullName()),
1071 Value: b,
1072 },
1073 }
1074 }(),
1075 want: `opt_any: {
1076 type_url: "pb2.Nested"
1077 value: "\n\x13embedded inside Any\x12\x0b\n\tinception"
1078}
1079`,
1080 }, {
1081 desc: "google.protobuf.Any field expanded using given types registry",
1082 mo: func() textpb.MarshalOptions {
1083 m := &pb2.Nested{}
1084 resolver := preg.NewTypes(m.ProtoReflect().Type())
1085 return textpb.MarshalOptions{Resolver: resolver}
1086 }(),
1087 input: func() proto.Message {
1088 m := &pb2.Nested{
1089 OptString: scalar.String("embedded inside Any"),
1090 OptNested: &pb2.Nested{
1091 OptString: scalar.String("inception"),
1092 },
1093 }
1094 // TODO: Switch to V2 marshal when ready.
1095 b, err := protoV1.Marshal(m)
1096 if err != nil {
1097 t.Fatalf("error in binary marshaling message for Any.value: %v", err)
1098 }
1099 return &pb2.KnownTypes{
1100 OptAny: &anypb.Any{
1101 TypeUrl: string(m.ProtoReflect().Type().FullName()),
1102 Value: b,
1103 },
1104 }
1105 }(),
1106 want: `opt_any: {
1107 [pb2.Nested]: {
1108 opt_string: "embedded inside Any"
1109 opt_nested: {
1110 opt_string: "inception"
1111 }
1112 }
1113}
1114`,
Herbie Ongcddf8192018-11-28 18:25:20 -08001115 }}
1116
1117 for _, tt := range tests {
1118 tt := tt
1119 t.Run(tt.desc, func(t *testing.T) {
1120 t.Parallel()
Herbie Ongf42b55f2019-01-02 15:46:07 -08001121 b, err := tt.mo.Marshal(tt.input)
Herbie Ongcddf8192018-11-28 18:25:20 -08001122 if err != nil && !tt.wantErr {
Herbie Ongf42b55f2019-01-02 15:46:07 -08001123 t.Errorf("Marshal() returned error: %v\n", err)
Herbie Ongcddf8192018-11-28 18:25:20 -08001124 }
Herbie Ong800c9902018-12-06 15:28:53 -08001125 if err == nil && tt.wantErr {
Herbie Ongf42b55f2019-01-02 15:46:07 -08001126 t.Error("Marshal() got nil error, want error\n")
Herbie Ongcddf8192018-11-28 18:25:20 -08001127 }
Herbie Ong800c9902018-12-06 15:28:53 -08001128 got := string(b)
1129 if tt.want != "" && got != tt.want {
1130 t.Errorf("Marshal()\n<got>\n%v\n<want>\n%v\n", got, tt.want)
1131 if diff := cmp.Diff(tt.want, got, splitLines); diff != "" {
Herbie Ongcddf8192018-11-28 18:25:20 -08001132 t.Errorf("Marshal() diff -want +got\n%v\n", diff)
1133 }
1134 }
1135 })
1136 }
1137}