blob: 38e60e7b15934a4ecc0f5d656b6d72d23eb0cd15 [file] [log] [blame]
Herbie Ong7b828bc2019-02-08 19:56:24 -08001// Copyright 2019 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
Damien Neil5c5b5312019-05-14 12:44:37 -07005package protojson_test
Herbie Ong7b828bc2019-02-08 19:56:24 -08006
7import (
Damien Neilbc310b52019-04-11 11:46:55 -07008 "bytes"
Herbie Ong7b828bc2019-02-08 19:56:24 -08009 "math"
Herbie Ong7b828bc2019-02-08 19:56:24 -080010 "testing"
11
Herbie Ong7b828bc2019-02-08 19:56:24 -080012 "github.com/google/go-cmp/cmp"
Damien Neil5c5b5312019-05-14 12:44:37 -070013 "google.golang.org/protobuf/encoding/protojson"
Herbie Ong582ab3d2019-09-06 15:56:09 -070014 "google.golang.org/protobuf/internal/detrand"
Damien Neile89e6242019-05-13 23:55:40 -070015 "google.golang.org/protobuf/internal/encoding/pack"
Joe Tsai5ae10aa2019-07-11 18:23:08 -070016 "google.golang.org/protobuf/internal/flags"
Damien Neile89e6242019-05-13 23:55:40 -070017 pimpl "google.golang.org/protobuf/internal/impl"
Damien Neile89e6242019-05-13 23:55:40 -070018 "google.golang.org/protobuf/proto"
19 preg "google.golang.org/protobuf/reflect/protoregistry"
Herbie Ong7b828bc2019-02-08 19:56:24 -080020
Damien Neile89e6242019-05-13 23:55:40 -070021 "google.golang.org/protobuf/encoding/testprotos/pb2"
22 "google.golang.org/protobuf/encoding/testprotos/pb3"
Joe Tsaia95b29f2019-05-16 12:47:20 -070023 "google.golang.org/protobuf/types/known/anypb"
24 "google.golang.org/protobuf/types/known/durationpb"
25 "google.golang.org/protobuf/types/known/emptypb"
26 "google.golang.org/protobuf/types/known/fieldmaskpb"
27 "google.golang.org/protobuf/types/known/structpb"
28 "google.golang.org/protobuf/types/known/timestamppb"
29 "google.golang.org/protobuf/types/known/wrapperspb"
Herbie Ong7b828bc2019-02-08 19:56:24 -080030)
31
Herbie Ong582ab3d2019-09-06 15:56:09 -070032// Disable detrand to enable direct comparisons on outputs.
33func init() { detrand.Disable() }
34
Herbie Ong7b828bc2019-02-08 19:56:24 -080035func TestMarshal(t *testing.T) {
36 tests := []struct {
Herbie Ong0b0f4032019-03-18 19:06:15 -070037 desc string
Damien Neil5c5b5312019-05-14 12:44:37 -070038 mo protojson.MarshalOptions
Herbie Ong0b0f4032019-03-18 19:06:15 -070039 input proto.Message
40 want string
41 wantErr bool // TODO: Verify error message substring.
Joe Tsai5ae10aa2019-07-11 18:23:08 -070042 skip bool
Herbie Ong7b828bc2019-02-08 19:56:24 -080043 }{{
44 desc: "proto2 optional scalars not set",
45 input: &pb2.Scalars{},
46 want: "{}",
47 }, {
48 desc: "proto3 scalars not set",
49 input: &pb3.Scalars{},
50 want: "{}",
51 }, {
52 desc: "proto2 optional scalars set to zero values",
53 input: &pb2.Scalars{
Damien Neila8a2cea2019-07-10 16:17:16 -070054 OptBool: proto.Bool(false),
55 OptInt32: proto.Int32(0),
56 OptInt64: proto.Int64(0),
57 OptUint32: proto.Uint32(0),
58 OptUint64: proto.Uint64(0),
59 OptSint32: proto.Int32(0),
60 OptSint64: proto.Int64(0),
61 OptFixed32: proto.Uint32(0),
62 OptFixed64: proto.Uint64(0),
63 OptSfixed32: proto.Int32(0),
64 OptSfixed64: proto.Int64(0),
65 OptFloat: proto.Float32(0),
66 OptDouble: proto.Float64(0),
Herbie Ong7b828bc2019-02-08 19:56:24 -080067 OptBytes: []byte{},
Damien Neila8a2cea2019-07-10 16:17:16 -070068 OptString: proto.String(""),
Herbie Ong7b828bc2019-02-08 19:56:24 -080069 },
70 want: `{
71 "optBool": false,
72 "optInt32": 0,
73 "optInt64": "0",
74 "optUint32": 0,
75 "optUint64": "0",
76 "optSint32": 0,
77 "optSint64": "0",
78 "optFixed32": 0,
79 "optFixed64": "0",
80 "optSfixed32": 0,
81 "optSfixed64": "0",
82 "optFloat": 0,
83 "optDouble": 0,
84 "optBytes": "",
85 "optString": ""
86}`,
87 }, {
88 desc: "proto2 optional scalars set to some values",
89 input: &pb2.Scalars{
Damien Neila8a2cea2019-07-10 16:17:16 -070090 OptBool: proto.Bool(true),
91 OptInt32: proto.Int32(0xff),
92 OptInt64: proto.Int64(0xdeadbeef),
93 OptUint32: proto.Uint32(47),
94 OptUint64: proto.Uint64(0xdeadbeef),
95 OptSint32: proto.Int32(-1001),
96 OptSint64: proto.Int64(-0xffff),
97 OptFixed64: proto.Uint64(64),
98 OptSfixed32: proto.Int32(-32),
99 OptFloat: proto.Float32(1.02),
100 OptDouble: proto.Float64(1.234),
Herbie Ong87608a72019-03-06 14:32:24 -0800101 OptBytes: []byte("谷歌"),
Damien Neila8a2cea2019-07-10 16:17:16 -0700102 OptString: proto.String("谷歌"),
Herbie Ong7b828bc2019-02-08 19:56:24 -0800103 },
104 want: `{
105 "optBool": true,
106 "optInt32": 255,
107 "optInt64": "3735928559",
108 "optUint32": 47,
109 "optUint64": "3735928559",
110 "optSint32": -1001,
111 "optSint64": "-65535",
112 "optFixed64": "64",
113 "optSfixed32": -32,
114 "optFloat": 1.02,
115 "optDouble": 1.234,
116 "optBytes": "6LC35q2M",
117 "optString": "谷歌"
118}`,
119 }, {
Herbie Ong0b0f4032019-03-18 19:06:15 -0700120 desc: "string",
121 input: &pb3.Scalars{
122 SString: "谷歌",
123 },
124 want: `{
125 "sString": "谷歌"
126}`,
127 }, {
128 desc: "string with invalid UTF8",
129 input: &pb3.Scalars{
130 SString: "abc\xff",
131 },
Herbie Ong0b0f4032019-03-18 19:06:15 -0700132 wantErr: true,
133 }, {
Herbie Ong7b828bc2019-02-08 19:56:24 -0800134 desc: "float nan",
135 input: &pb3.Scalars{
136 SFloat: float32(math.NaN()),
137 },
138 want: `{
139 "sFloat": "NaN"
140}`,
141 }, {
142 desc: "float positive infinity",
143 input: &pb3.Scalars{
144 SFloat: float32(math.Inf(1)),
145 },
146 want: `{
147 "sFloat": "Infinity"
148}`,
149 }, {
150 desc: "float negative infinity",
151 input: &pb3.Scalars{
152 SFloat: float32(math.Inf(-1)),
153 },
154 want: `{
155 "sFloat": "-Infinity"
156}`,
157 }, {
158 desc: "double nan",
159 input: &pb3.Scalars{
160 SDouble: math.NaN(),
161 },
162 want: `{
163 "sDouble": "NaN"
164}`,
165 }, {
166 desc: "double positive infinity",
167 input: &pb3.Scalars{
168 SDouble: math.Inf(1),
169 },
170 want: `{
171 "sDouble": "Infinity"
172}`,
173 }, {
174 desc: "double negative infinity",
175 input: &pb3.Scalars{
176 SDouble: math.Inf(-1),
177 },
178 want: `{
179 "sDouble": "-Infinity"
180}`,
181 }, {
182 desc: "proto2 enum not set",
183 input: &pb2.Enums{},
184 want: "{}",
185 }, {
186 desc: "proto2 enum set to zero value",
187 input: &pb2.Enums{
Joe Tsai6dc168e2019-07-09 23:11:13 -0700188 OptEnum: pb2.Enum(0).Enum(),
189 OptNestedEnum: pb2.Enums_NestedEnum(0).Enum(),
Herbie Ong7b828bc2019-02-08 19:56:24 -0800190 },
191 want: `{
192 "optEnum": 0,
193 "optNestedEnum": 0
194}`,
195 }, {
196 desc: "proto2 enum",
197 input: &pb2.Enums{
198 OptEnum: pb2.Enum_ONE.Enum(),
199 OptNestedEnum: pb2.Enums_UNO.Enum(),
200 },
201 want: `{
202 "optEnum": "ONE",
203 "optNestedEnum": "UNO"
204}`,
205 }, {
206 desc: "proto2 enum set to numeric values",
207 input: &pb2.Enums{
Joe Tsai6dc168e2019-07-09 23:11:13 -0700208 OptEnum: pb2.Enum(2).Enum(),
209 OptNestedEnum: pb2.Enums_NestedEnum(2).Enum(),
Herbie Ong7b828bc2019-02-08 19:56:24 -0800210 },
211 want: `{
212 "optEnum": "TWO",
213 "optNestedEnum": "DOS"
214}`,
215 }, {
216 desc: "proto2 enum set to unnamed numeric values",
217 input: &pb2.Enums{
Joe Tsai6dc168e2019-07-09 23:11:13 -0700218 OptEnum: pb2.Enum(101).Enum(),
219 OptNestedEnum: pb2.Enums_NestedEnum(-101).Enum(),
Herbie Ong7b828bc2019-02-08 19:56:24 -0800220 },
221 want: `{
222 "optEnum": 101,
223 "optNestedEnum": -101
224}`,
225 }, {
226 desc: "proto3 enum not set",
227 input: &pb3.Enums{},
228 want: "{}",
229 }, {
230 desc: "proto3 enum set to zero value",
231 input: &pb3.Enums{
232 SEnum: pb3.Enum_ZERO,
233 SNestedEnum: pb3.Enums_CERO,
234 },
235 want: "{}",
236 }, {
237 desc: "proto3 enum",
238 input: &pb3.Enums{
239 SEnum: pb3.Enum_ONE,
240 SNestedEnum: pb3.Enums_UNO,
241 },
242 want: `{
243 "sEnum": "ONE",
244 "sNestedEnum": "UNO"
245}`,
246 }, {
247 desc: "proto3 enum set to numeric values",
248 input: &pb3.Enums{
249 SEnum: 2,
250 SNestedEnum: 2,
251 },
252 want: `{
253 "sEnum": "TWO",
254 "sNestedEnum": "DOS"
255}`,
256 }, {
257 desc: "proto3 enum set to unnamed numeric values",
258 input: &pb3.Enums{
259 SEnum: -47,
260 SNestedEnum: 47,
261 },
262 want: `{
263 "sEnum": -47,
264 "sNestedEnum": 47
265}`,
266 }, {
267 desc: "proto2 nested message not set",
268 input: &pb2.Nests{},
269 want: "{}",
270 }, {
271 desc: "proto2 nested message set to empty",
272 input: &pb2.Nests{
273 OptNested: &pb2.Nested{},
274 Optgroup: &pb2.Nests_OptGroup{},
275 },
276 want: `{
277 "optNested": {},
278 "optgroup": {}
279}`,
280 }, {
281 desc: "proto2 nested messages",
282 input: &pb2.Nests{
283 OptNested: &pb2.Nested{
Damien Neila8a2cea2019-07-10 16:17:16 -0700284 OptString: proto.String("nested message"),
Herbie Ong7b828bc2019-02-08 19:56:24 -0800285 OptNested: &pb2.Nested{
Damien Neila8a2cea2019-07-10 16:17:16 -0700286 OptString: proto.String("another nested message"),
Herbie Ong7b828bc2019-02-08 19:56:24 -0800287 },
288 },
289 },
290 want: `{
291 "optNested": {
292 "optString": "nested message",
293 "optNested": {
294 "optString": "another nested message"
295 }
296 }
297}`,
298 }, {
299 desc: "proto2 groups",
300 input: &pb2.Nests{
301 Optgroup: &pb2.Nests_OptGroup{
Damien Neila8a2cea2019-07-10 16:17:16 -0700302 OptString: proto.String("inside a group"),
Herbie Ong7b828bc2019-02-08 19:56:24 -0800303 OptNested: &pb2.Nested{
Damien Neila8a2cea2019-07-10 16:17:16 -0700304 OptString: proto.String("nested message inside a group"),
Herbie Ong7b828bc2019-02-08 19:56:24 -0800305 },
306 Optnestedgroup: &pb2.Nests_OptGroup_OptNestedGroup{
Damien Neila8a2cea2019-07-10 16:17:16 -0700307 OptFixed32: proto.Uint32(47),
Herbie Ong7b828bc2019-02-08 19:56:24 -0800308 },
309 },
310 },
311 want: `{
312 "optgroup": {
313 "optString": "inside a group",
314 "optNested": {
315 "optString": "nested message inside a group"
316 },
317 "optnestedgroup": {
318 "optFixed32": 47
319 }
320 }
321}`,
322 }, {
323 desc: "proto3 nested message not set",
324 input: &pb3.Nests{},
325 want: "{}",
326 }, {
327 desc: "proto3 nested message set to empty",
328 input: &pb3.Nests{
329 SNested: &pb3.Nested{},
330 },
331 want: `{
332 "sNested": {}
333}`,
334 }, {
335 desc: "proto3 nested message",
336 input: &pb3.Nests{
337 SNested: &pb3.Nested{
338 SString: "nested message",
339 SNested: &pb3.Nested{
340 SString: "another nested message",
341 },
342 },
343 },
344 want: `{
345 "sNested": {
346 "sString": "nested message",
347 "sNested": {
348 "sString": "another nested message"
349 }
350 }
351}`,
352 }, {
353 desc: "oneof not set",
354 input: &pb3.Oneofs{},
355 want: "{}",
356 }, {
357 desc: "oneof set to empty string",
358 input: &pb3.Oneofs{
359 Union: &pb3.Oneofs_OneofString{},
360 },
361 want: `{
362 "oneofString": ""
363}`,
364 }, {
365 desc: "oneof set to string",
366 input: &pb3.Oneofs{
367 Union: &pb3.Oneofs_OneofString{
368 OneofString: "hello",
369 },
370 },
371 want: `{
372 "oneofString": "hello"
373}`,
374 }, {
375 desc: "oneof set to enum",
376 input: &pb3.Oneofs{
377 Union: &pb3.Oneofs_OneofEnum{
378 OneofEnum: pb3.Enum_ZERO,
379 },
380 },
381 want: `{
382 "oneofEnum": "ZERO"
383}`,
384 }, {
385 desc: "oneof set to empty message",
386 input: &pb3.Oneofs{
387 Union: &pb3.Oneofs_OneofNested{
388 OneofNested: &pb3.Nested{},
389 },
390 },
391 want: `{
392 "oneofNested": {}
393}`,
394 }, {
395 desc: "oneof set to message",
396 input: &pb3.Oneofs{
397 Union: &pb3.Oneofs_OneofNested{
398 OneofNested: &pb3.Nested{
399 SString: "nested message",
400 },
401 },
402 },
403 want: `{
404 "oneofNested": {
405 "sString": "nested message"
406 }
407}`,
408 }, {
409 desc: "repeated fields not set",
410 input: &pb2.Repeats{},
411 want: "{}",
412 }, {
413 desc: "repeated fields set to empty slices",
414 input: &pb2.Repeats{
415 RptBool: []bool{},
416 RptInt32: []int32{},
417 RptInt64: []int64{},
418 RptUint32: []uint32{},
419 RptUint64: []uint64{},
420 RptFloat: []float32{},
421 RptDouble: []float64{},
422 RptBytes: [][]byte{},
423 },
424 want: "{}",
425 }, {
426 desc: "repeated fields set to some values",
427 input: &pb2.Repeats{
428 RptBool: []bool{true, false, true, true},
429 RptInt32: []int32{1, 6, 0, 0},
430 RptInt64: []int64{-64, 47},
431 RptUint32: []uint32{0xff, 0xffff},
432 RptUint64: []uint64{0xdeadbeef},
433 RptFloat: []float32{float32(math.NaN()), float32(math.Inf(1)), float32(math.Inf(-1)), 1.034},
434 RptDouble: []float64{math.NaN(), math.Inf(1), math.Inf(-1), 1.23e-308},
435 RptString: []string{"hello", "世界"},
436 RptBytes: [][]byte{
437 []byte("hello"),
438 []byte("\xe4\xb8\x96\xe7\x95\x8c"),
439 },
440 },
441 want: `{
442 "rptBool": [
443 true,
444 false,
445 true,
446 true
447 ],
448 "rptInt32": [
449 1,
450 6,
451 0,
452 0
453 ],
454 "rptInt64": [
455 "-64",
456 "47"
457 ],
458 "rptUint32": [
459 255,
460 65535
461 ],
462 "rptUint64": [
463 "3735928559"
464 ],
465 "rptFloat": [
466 "NaN",
467 "Infinity",
468 "-Infinity",
469 1.034
470 ],
471 "rptDouble": [
472 "NaN",
473 "Infinity",
474 "-Infinity",
475 1.23e-308
476 ],
477 "rptString": [
478 "hello",
479 "世界"
480 ],
481 "rptBytes": [
482 "aGVsbG8=",
483 "5LiW55WM"
484 ]
485}`,
486 }, {
487 desc: "repeated enums",
488 input: &pb2.Enums{
489 RptEnum: []pb2.Enum{pb2.Enum_ONE, 2, pb2.Enum_TEN, 42},
490 RptNestedEnum: []pb2.Enums_NestedEnum{2, 47, 10},
491 },
492 want: `{
493 "rptEnum": [
494 "ONE",
495 "TWO",
496 "TEN",
497 42
498 ],
499 "rptNestedEnum": [
500 "DOS",
501 47,
502 "DIEZ"
503 ]
504}`,
505 }, {
506 desc: "repeated messages set to empty",
507 input: &pb2.Nests{
508 RptNested: []*pb2.Nested{},
509 Rptgroup: []*pb2.Nests_RptGroup{},
510 },
511 want: "{}",
512 }, {
513 desc: "repeated messages",
514 input: &pb2.Nests{
515 RptNested: []*pb2.Nested{
516 {
Damien Neila8a2cea2019-07-10 16:17:16 -0700517 OptString: proto.String("repeat nested one"),
Herbie Ong7b828bc2019-02-08 19:56:24 -0800518 },
519 {
Damien Neila8a2cea2019-07-10 16:17:16 -0700520 OptString: proto.String("repeat nested two"),
Herbie Ong7b828bc2019-02-08 19:56:24 -0800521 OptNested: &pb2.Nested{
Damien Neila8a2cea2019-07-10 16:17:16 -0700522 OptString: proto.String("inside repeat nested two"),
Herbie Ong7b828bc2019-02-08 19:56:24 -0800523 },
524 },
525 {},
526 },
527 },
528 want: `{
529 "rptNested": [
530 {
531 "optString": "repeat nested one"
532 },
533 {
534 "optString": "repeat nested two",
535 "optNested": {
536 "optString": "inside repeat nested two"
537 }
538 },
539 {}
540 ]
541}`,
542 }, {
543 desc: "repeated messages contains nil value",
544 input: &pb2.Nests{
545 RptNested: []*pb2.Nested{nil, {}},
546 },
547 want: `{
548 "rptNested": [
549 {},
550 {}
551 ]
552}`,
553 }, {
554 desc: "repeated groups",
555 input: &pb2.Nests{
556 Rptgroup: []*pb2.Nests_RptGroup{
557 {
558 RptString: []string{"hello", "world"},
559 },
560 {},
561 nil,
562 },
563 },
564 want: `{
565 "rptgroup": [
566 {
567 "rptString": [
568 "hello",
569 "world"
570 ]
571 },
572 {},
573 {}
574 ]
575}`,
576 }, {
577 desc: "map fields not set",
578 input: &pb3.Maps{},
579 want: "{}",
580 }, {
581 desc: "map fields set to empty",
582 input: &pb3.Maps{
583 Int32ToStr: map[int32]string{},
584 BoolToUint32: map[bool]uint32{},
585 Uint64ToEnum: map[uint64]pb3.Enum{},
586 StrToNested: map[string]*pb3.Nested{},
587 StrToOneofs: map[string]*pb3.Oneofs{},
588 },
589 want: "{}",
590 }, {
591 desc: "map fields 1",
592 input: &pb3.Maps{
593 BoolToUint32: map[bool]uint32{
594 true: 42,
595 false: 101,
596 },
597 },
598 want: `{
599 "boolToUint32": {
600 "false": 101,
601 "true": 42
602 }
603}`,
604 }, {
605 desc: "map fields 2",
606 input: &pb3.Maps{
607 Int32ToStr: map[int32]string{
608 -101: "-101",
609 0xff: "0xff",
610 0: "zero",
611 },
612 },
613 want: `{
614 "int32ToStr": {
615 "-101": "-101",
616 "0": "zero",
617 "255": "0xff"
618 }
619}`,
620 }, {
621 desc: "map fields 3",
622 input: &pb3.Maps{
623 Uint64ToEnum: map[uint64]pb3.Enum{
624 1: pb3.Enum_ONE,
625 2: pb3.Enum_TWO,
626 10: pb3.Enum_TEN,
627 47: 47,
628 },
629 },
630 want: `{
631 "uint64ToEnum": {
632 "1": "ONE",
633 "2": "TWO",
634 "10": "TEN",
635 "47": 47
636 }
637}`,
638 }, {
639 desc: "map fields 4",
640 input: &pb3.Maps{
641 StrToNested: map[string]*pb3.Nested{
642 "nested": &pb3.Nested{
643 SString: "nested in a map",
644 },
645 },
646 },
647 want: `{
648 "strToNested": {
649 "nested": {
650 "sString": "nested in a map"
651 }
652 }
653}`,
654 }, {
655 desc: "map fields 5",
656 input: &pb3.Maps{
657 StrToOneofs: map[string]*pb3.Oneofs{
658 "string": &pb3.Oneofs{
659 Union: &pb3.Oneofs_OneofString{
660 OneofString: "hello",
661 },
662 },
663 "nested": &pb3.Oneofs{
664 Union: &pb3.Oneofs_OneofNested{
665 OneofNested: &pb3.Nested{
666 SString: "nested oneof in map field value",
667 },
668 },
669 },
670 },
671 },
672 want: `{
673 "strToOneofs": {
674 "nested": {
675 "oneofNested": {
676 "sString": "nested oneof in map field value"
677 }
678 },
679 "string": {
680 "oneofString": "hello"
681 }
682 }
683}`,
684 }, {
685 desc: "map field contains nil value",
686 input: &pb3.Maps{
687 StrToNested: map[string]*pb3.Nested{
688 "nil": nil,
689 },
690 },
691 want: `{
692 "strToNested": {
693 "nil": {}
694 }
695}`,
696 }, {
Herbie Ong329be5b2019-03-27 14:47:59 -0700697 desc: "required fields not set",
698 input: &pb2.Requireds{},
699 want: `{}`,
700 wantErr: true,
701 }, {
702 desc: "required fields partially set",
703 input: &pb2.Requireds{
Damien Neila8a2cea2019-07-10 16:17:16 -0700704 ReqBool: proto.Bool(false),
705 ReqSfixed64: proto.Int64(0),
706 ReqDouble: proto.Float64(1.23),
707 ReqString: proto.String("hello"),
Herbie Ong329be5b2019-03-27 14:47:59 -0700708 ReqEnum: pb2.Enum_ONE.Enum(),
709 },
710 want: `{
711 "reqBool": false,
712 "reqSfixed64": "0",
713 "reqDouble": 1.23,
714 "reqString": "hello",
715 "reqEnum": "ONE"
716}`,
717 wantErr: true,
718 }, {
719 desc: "required fields not set with AllowPartial",
Damien Neil5c5b5312019-05-14 12:44:37 -0700720 mo: protojson.MarshalOptions{AllowPartial: true},
Herbie Ong329be5b2019-03-27 14:47:59 -0700721 input: &pb2.Requireds{
Damien Neila8a2cea2019-07-10 16:17:16 -0700722 ReqBool: proto.Bool(false),
723 ReqSfixed64: proto.Int64(0),
724 ReqDouble: proto.Float64(1.23),
725 ReqString: proto.String("hello"),
Herbie Ong329be5b2019-03-27 14:47:59 -0700726 ReqEnum: pb2.Enum_ONE.Enum(),
727 },
728 want: `{
729 "reqBool": false,
730 "reqSfixed64": "0",
731 "reqDouble": 1.23,
732 "reqString": "hello",
733 "reqEnum": "ONE"
734}`,
735 }, {
736 desc: "required fields all set",
737 input: &pb2.Requireds{
Damien Neila8a2cea2019-07-10 16:17:16 -0700738 ReqBool: proto.Bool(false),
739 ReqSfixed64: proto.Int64(0),
740 ReqDouble: proto.Float64(1.23),
741 ReqString: proto.String("hello"),
Herbie Ong329be5b2019-03-27 14:47:59 -0700742 ReqEnum: pb2.Enum_ONE.Enum(),
743 ReqNested: &pb2.Nested{},
744 },
745 want: `{
746 "reqBool": false,
747 "reqSfixed64": "0",
748 "reqDouble": 1.23,
749 "reqString": "hello",
750 "reqEnum": "ONE",
751 "reqNested": {}
752}`,
753 }, {
754 desc: "indirect required field",
755 input: &pb2.IndirectRequired{
756 OptNested: &pb2.NestedWithRequired{},
757 },
758 want: `{
759 "optNested": {}
760}`,
761 wantErr: true,
762 }, {
763 desc: "indirect required field with AllowPartial",
Damien Neil5c5b5312019-05-14 12:44:37 -0700764 mo: protojson.MarshalOptions{AllowPartial: true},
Herbie Ong329be5b2019-03-27 14:47:59 -0700765 input: &pb2.IndirectRequired{
766 OptNested: &pb2.NestedWithRequired{},
767 },
768 want: `{
769 "optNested": {}
770}`,
771 }, {
772 desc: "indirect required field in empty repeated",
773 input: &pb2.IndirectRequired{
774 RptNested: []*pb2.NestedWithRequired{},
775 },
776 want: `{}`,
777 }, {
778 desc: "indirect required field in repeated",
779 input: &pb2.IndirectRequired{
780 RptNested: []*pb2.NestedWithRequired{
781 &pb2.NestedWithRequired{},
782 },
783 },
784 want: `{
785 "rptNested": [
786 {}
787 ]
788}`,
789 wantErr: true,
790 }, {
791 desc: "indirect required field in repeated with AllowPartial",
Damien Neil5c5b5312019-05-14 12:44:37 -0700792 mo: protojson.MarshalOptions{AllowPartial: true},
Herbie Ong329be5b2019-03-27 14:47:59 -0700793 input: &pb2.IndirectRequired{
794 RptNested: []*pb2.NestedWithRequired{
795 &pb2.NestedWithRequired{},
796 },
797 },
798 want: `{
799 "rptNested": [
800 {}
801 ]
802}`,
803 }, {
804 desc: "indirect required field in empty map",
805 input: &pb2.IndirectRequired{
806 StrToNested: map[string]*pb2.NestedWithRequired{},
807 },
808 want: "{}",
809 }, {
810 desc: "indirect required field in map",
811 input: &pb2.IndirectRequired{
812 StrToNested: map[string]*pb2.NestedWithRequired{
813 "fail": &pb2.NestedWithRequired{},
814 },
815 },
816 want: `{
817 "strToNested": {
818 "fail": {}
819 }
820}`,
821 wantErr: true,
822 }, {
823 desc: "indirect required field in map with AllowPartial",
Damien Neil5c5b5312019-05-14 12:44:37 -0700824 mo: protojson.MarshalOptions{AllowPartial: true},
Herbie Ong329be5b2019-03-27 14:47:59 -0700825 input: &pb2.IndirectRequired{
826 StrToNested: map[string]*pb2.NestedWithRequired{
827 "fail": &pb2.NestedWithRequired{},
828 },
829 },
830 want: `{
831 "strToNested": {
832 "fail": {}
833 }
834}`,
835 }, {
836 desc: "indirect required field in oneof",
837 input: &pb2.IndirectRequired{
838 Union: &pb2.IndirectRequired_OneofNested{
839 OneofNested: &pb2.NestedWithRequired{},
840 },
841 },
842 want: `{
843 "oneofNested": {}
844}`,
845 wantErr: true,
846 }, {
847 desc: "indirect required field in oneof with AllowPartial",
Damien Neil5c5b5312019-05-14 12:44:37 -0700848 mo: protojson.MarshalOptions{AllowPartial: true},
Herbie Ong329be5b2019-03-27 14:47:59 -0700849 input: &pb2.IndirectRequired{
850 Union: &pb2.IndirectRequired_OneofNested{
851 OneofNested: &pb2.NestedWithRequired{},
852 },
853 },
854 want: `{
855 "oneofNested": {}
856}`,
857 }, {
Herbie Ong7b828bc2019-02-08 19:56:24 -0800858 desc: "unknown fields are ignored",
Joe Tsai28216c72019-06-22 13:20:09 -0700859 input: func() proto.Message {
860 m := &pb2.Scalars{
Damien Neila8a2cea2019-07-10 16:17:16 -0700861 OptString: proto.String("no unknowns"),
Joe Tsai28216c72019-06-22 13:20:09 -0700862 }
863 m.ProtoReflect().SetUnknown(pack.Message{
Herbie Ong7b828bc2019-02-08 19:56:24 -0800864 pack.Tag{101, pack.BytesType}, pack.String("hello world"),
Joe Tsai28216c72019-06-22 13:20:09 -0700865 }.Marshal())
866 return m
867 }(),
Herbie Ong7b828bc2019-02-08 19:56:24 -0800868 want: `{
869 "optString": "no unknowns"
870}`,
871 }, {
872 desc: "json_name",
873 input: &pb3.JSONNames{
874 SString: "json_name",
875 },
876 want: `{
877 "foo_bar": "json_name"
878}`,
Herbie Ongf83d5bb2019-03-14 00:01:27 -0700879 }, {
880 desc: "extensions of non-repeated fields",
881 input: func() proto.Message {
882 m := &pb2.Extensions{
Damien Neila8a2cea2019-07-10 16:17:16 -0700883 OptString: proto.String("non-extension field"),
884 OptBool: proto.Bool(true),
885 OptInt32: proto.Int32(42),
Herbie Ongf83d5bb2019-03-14 00:01:27 -0700886 }
Damien Neil92f76182019-08-02 16:58:08 -0700887 proto.SetExtension(m, pb2.E_OptExtBool, true)
888 proto.SetExtension(m, pb2.E_OptExtString, "extension field")
889 proto.SetExtension(m, pb2.E_OptExtEnum, pb2.Enum_TEN)
890 proto.SetExtension(m, pb2.E_OptExtNested, &pb2.Nested{
Damien Neila8a2cea2019-07-10 16:17:16 -0700891 OptString: proto.String("nested in an extension"),
Herbie Ongf83d5bb2019-03-14 00:01:27 -0700892 OptNested: &pb2.Nested{
Damien Neila8a2cea2019-07-10 16:17:16 -0700893 OptString: proto.String("another nested in an extension"),
Herbie Ongf83d5bb2019-03-14 00:01:27 -0700894 },
895 })
896 return m
897 }(),
898 want: `{
899 "optString": "non-extension field",
900 "optBool": true,
901 "optInt32": 42,
902 "[pb2.opt_ext_bool]": true,
903 "[pb2.opt_ext_enum]": "TEN",
904 "[pb2.opt_ext_nested]": {
905 "optString": "nested in an extension",
906 "optNested": {
907 "optString": "another nested in an extension"
908 }
909 },
910 "[pb2.opt_ext_string]": "extension field"
911}`,
912 }, {
Herbie Ongf83d5bb2019-03-14 00:01:27 -0700913 desc: "extensions of repeated fields",
914 input: func() proto.Message {
915 m := &pb2.Extensions{}
Damien Neil293dc762019-08-29 11:42:57 -0700916 proto.SetExtension(m, pb2.E_RptExtEnum, []pb2.Enum{pb2.Enum_TEN, 101, pb2.Enum_ONE})
917 proto.SetExtension(m, pb2.E_RptExtFixed32, []uint32{42, 47})
918 proto.SetExtension(m, pb2.E_RptExtNested, []*pb2.Nested{
Damien Neila8a2cea2019-07-10 16:17:16 -0700919 &pb2.Nested{OptString: proto.String("one")},
920 &pb2.Nested{OptString: proto.String("two")},
921 &pb2.Nested{OptString: proto.String("three")},
Herbie Ongf83d5bb2019-03-14 00:01:27 -0700922 })
923 return m
924 }(),
925 want: `{
926 "[pb2.rpt_ext_enum]": [
927 "TEN",
928 101,
929 "ONE"
930 ],
931 "[pb2.rpt_ext_fixed32]": [
932 42,
933 47
934 ],
935 "[pb2.rpt_ext_nested]": [
936 {
937 "optString": "one"
938 },
939 {
940 "optString": "two"
941 },
942 {
943 "optString": "three"
944 }
945 ]
946}`,
947 }, {
948 desc: "extensions of non-repeated fields in another message",
949 input: func() proto.Message {
950 m := &pb2.Extensions{}
Damien Neil92f76182019-08-02 16:58:08 -0700951 proto.SetExtension(m, pb2.E_ExtensionsContainer_OptExtBool, true)
952 proto.SetExtension(m, pb2.E_ExtensionsContainer_OptExtString, "extension field")
953 proto.SetExtension(m, pb2.E_ExtensionsContainer_OptExtEnum, pb2.Enum_TEN)
954 proto.SetExtension(m, pb2.E_ExtensionsContainer_OptExtNested, &pb2.Nested{
Damien Neila8a2cea2019-07-10 16:17:16 -0700955 OptString: proto.String("nested in an extension"),
Herbie Ongf83d5bb2019-03-14 00:01:27 -0700956 OptNested: &pb2.Nested{
Damien Neila8a2cea2019-07-10 16:17:16 -0700957 OptString: proto.String("another nested in an extension"),
Herbie Ongf83d5bb2019-03-14 00:01:27 -0700958 },
959 })
960 return m
961 }(),
962 want: `{
963 "[pb2.ExtensionsContainer.opt_ext_bool]": true,
964 "[pb2.ExtensionsContainer.opt_ext_enum]": "TEN",
965 "[pb2.ExtensionsContainer.opt_ext_nested]": {
966 "optString": "nested in an extension",
967 "optNested": {
968 "optString": "another nested in an extension"
969 }
970 },
971 "[pb2.ExtensionsContainer.opt_ext_string]": "extension field"
972}`,
973 }, {
974 desc: "extensions of repeated fields in another message",
975 input: func() proto.Message {
976 m := &pb2.Extensions{
Damien Neila8a2cea2019-07-10 16:17:16 -0700977 OptString: proto.String("non-extension field"),
978 OptBool: proto.Bool(true),
979 OptInt32: proto.Int32(42),
Herbie Ongf83d5bb2019-03-14 00:01:27 -0700980 }
Damien Neil293dc762019-08-29 11:42:57 -0700981 proto.SetExtension(m, pb2.E_ExtensionsContainer_RptExtEnum, []pb2.Enum{pb2.Enum_TEN, 101, pb2.Enum_ONE})
982 proto.SetExtension(m, pb2.E_ExtensionsContainer_RptExtString, []string{"hello", "world"})
983 proto.SetExtension(m, pb2.E_ExtensionsContainer_RptExtNested, []*pb2.Nested{
Damien Neila8a2cea2019-07-10 16:17:16 -0700984 &pb2.Nested{OptString: proto.String("one")},
985 &pb2.Nested{OptString: proto.String("two")},
986 &pb2.Nested{OptString: proto.String("three")},
Herbie Ongf83d5bb2019-03-14 00:01:27 -0700987 })
988 return m
989 }(),
990 want: `{
991 "optString": "non-extension field",
992 "optBool": true,
993 "optInt32": 42,
994 "[pb2.ExtensionsContainer.rpt_ext_enum]": [
995 "TEN",
996 101,
997 "ONE"
998 ],
999 "[pb2.ExtensionsContainer.rpt_ext_nested]": [
1000 {
1001 "optString": "one"
1002 },
1003 {
1004 "optString": "two"
1005 },
1006 {
1007 "optString": "three"
1008 }
1009 ],
1010 "[pb2.ExtensionsContainer.rpt_ext_string]": [
1011 "hello",
1012 "world"
1013 ]
1014}`,
1015 }, {
1016 desc: "MessageSet",
1017 input: func() proto.Message {
1018 m := &pb2.MessageSet{}
Damien Neil92f76182019-08-02 16:58:08 -07001019 proto.SetExtension(m, pb2.E_MessageSetExtension_MessageSetExtension, &pb2.MessageSetExtension{
Damien Neila8a2cea2019-07-10 16:17:16 -07001020 OptString: proto.String("a messageset extension"),
Herbie Ongf83d5bb2019-03-14 00:01:27 -07001021 })
Damien Neil92f76182019-08-02 16:58:08 -07001022 proto.SetExtension(m, pb2.E_MessageSetExtension_NotMessageSetExtension, &pb2.MessageSetExtension{
Damien Neila8a2cea2019-07-10 16:17:16 -07001023 OptString: proto.String("not a messageset extension"),
Herbie Ongf83d5bb2019-03-14 00:01:27 -07001024 })
Damien Neil92f76182019-08-02 16:58:08 -07001025 proto.SetExtension(m, pb2.E_MessageSetExtension_ExtNested, &pb2.Nested{
Damien Neila8a2cea2019-07-10 16:17:16 -07001026 OptString: proto.String("just a regular extension"),
Herbie Ongf83d5bb2019-03-14 00:01:27 -07001027 })
1028 return m
1029 }(),
1030 want: `{
1031 "[pb2.MessageSetExtension]": {
1032 "optString": "a messageset extension"
1033 },
1034 "[pb2.MessageSetExtension.ext_nested]": {
1035 "optString": "just a regular extension"
1036 },
1037 "[pb2.MessageSetExtension.not_message_set_extension]": {
1038 "optString": "not a messageset extension"
1039 }
1040}`,
Joe Tsai1799d112019-08-08 13:31:59 -07001041 skip: !flags.ProtoLegacy,
Herbie Ongf83d5bb2019-03-14 00:01:27 -07001042 }, {
1043 desc: "not real MessageSet 1",
1044 input: func() proto.Message {
1045 m := &pb2.FakeMessageSet{}
Damien Neil92f76182019-08-02 16:58:08 -07001046 proto.SetExtension(m, pb2.E_FakeMessageSetExtension_MessageSetExtension, &pb2.FakeMessageSetExtension{
Damien Neila8a2cea2019-07-10 16:17:16 -07001047 OptString: proto.String("not a messageset extension"),
Herbie Ongf83d5bb2019-03-14 00:01:27 -07001048 })
1049 return m
1050 }(),
1051 want: `{
1052 "[pb2.FakeMessageSetExtension.message_set_extension]": {
1053 "optString": "not a messageset extension"
1054 }
1055}`,
Joe Tsai1799d112019-08-08 13:31:59 -07001056 skip: !flags.ProtoLegacy,
Herbie Ongf83d5bb2019-03-14 00:01:27 -07001057 }, {
1058 desc: "not real MessageSet 2",
1059 input: func() proto.Message {
1060 m := &pb2.MessageSet{}
Damien Neil92f76182019-08-02 16:58:08 -07001061 proto.SetExtension(m, pb2.E_MessageSetExtension, &pb2.FakeMessageSetExtension{
Damien Neila8a2cea2019-07-10 16:17:16 -07001062 OptString: proto.String("another not a messageset extension"),
Herbie Ongf83d5bb2019-03-14 00:01:27 -07001063 })
1064 return m
1065 }(),
1066 want: `{
1067 "[pb2.message_set_extension]": {
1068 "optString": "another not a messageset extension"
1069 }
1070}`,
Joe Tsai1799d112019-08-08 13:31:59 -07001071 skip: !flags.ProtoLegacy,
Herbie Ong0b0f4032019-03-18 19:06:15 -07001072 }, {
1073 desc: "BoolValue empty",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001074 input: &wrapperspb.BoolValue{},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001075 want: `false`,
1076 }, {
1077 desc: "BoolValue",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001078 input: &wrapperspb.BoolValue{Value: true},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001079 want: `true`,
1080 }, {
1081 desc: "Int32Value empty",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001082 input: &wrapperspb.Int32Value{},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001083 want: `0`,
1084 }, {
1085 desc: "Int32Value",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001086 input: &wrapperspb.Int32Value{Value: 42},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001087 want: `42`,
1088 }, {
1089 desc: "Int64Value",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001090 input: &wrapperspb.Int64Value{Value: 42},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001091 want: `"42"`,
1092 }, {
1093 desc: "UInt32Value",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001094 input: &wrapperspb.UInt32Value{Value: 42},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001095 want: `42`,
1096 }, {
1097 desc: "UInt64Value",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001098 input: &wrapperspb.UInt64Value{Value: 42},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001099 want: `"42"`,
1100 }, {
1101 desc: "FloatValue",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001102 input: &wrapperspb.FloatValue{Value: 1.02},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001103 want: `1.02`,
1104 }, {
Herbie Onge63c4c42019-03-22 22:20:22 -07001105 desc: "FloatValue Infinity",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001106 input: &wrapperspb.FloatValue{Value: float32(math.Inf(-1))},
Herbie Onge63c4c42019-03-22 22:20:22 -07001107 want: `"-Infinity"`,
1108 }, {
Herbie Ong0b0f4032019-03-18 19:06:15 -07001109 desc: "DoubleValue",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001110 input: &wrapperspb.DoubleValue{Value: 1.02},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001111 want: `1.02`,
1112 }, {
Herbie Onge63c4c42019-03-22 22:20:22 -07001113 desc: "DoubleValue NaN",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001114 input: &wrapperspb.DoubleValue{Value: math.NaN()},
Herbie Onge63c4c42019-03-22 22:20:22 -07001115 want: `"NaN"`,
1116 }, {
Herbie Ong0b0f4032019-03-18 19:06:15 -07001117 desc: "StringValue empty",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001118 input: &wrapperspb.StringValue{},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001119 want: `""`,
1120 }, {
1121 desc: "StringValue",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001122 input: &wrapperspb.StringValue{Value: "谷歌"},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001123 want: `"谷歌"`,
1124 }, {
1125 desc: "StringValue with invalid UTF8 error",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001126 input: &wrapperspb.StringValue{Value: "abc\xff"},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001127 wantErr: true,
1128 }, {
1129 desc: "StringValue field with invalid UTF8 error",
1130 input: &pb2.KnownTypes{
Joe Tsaia95b29f2019-05-16 12:47:20 -07001131 OptString: &wrapperspb.StringValue{Value: "abc\xff"},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001132 },
Herbie Ong0b0f4032019-03-18 19:06:15 -07001133 wantErr: true,
1134 }, {
1135 desc: "BytesValue",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001136 input: &wrapperspb.BytesValue{Value: []byte("hello")},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001137 want: `"aGVsbG8="`,
1138 }, {
1139 desc: "Empty",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001140 input: &emptypb.Empty{},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001141 want: `{}`,
1142 }, {
Herbie Ong300b9fe2019-03-29 15:42:20 -07001143 desc: "NullValue field",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001144 input: &pb2.KnownTypes{OptNull: new(structpb.NullValue)},
Herbie Ong300b9fe2019-03-29 15:42:20 -07001145 want: `{
1146 "optNull": null
1147}`,
1148 }, {
Herbie Ong1c7462c2019-03-22 17:56:55 -07001149 desc: "Value empty",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001150 input: &structpb.Value{},
Herbie Ong1c7462c2019-03-22 17:56:55 -07001151 wantErr: true,
Herbie Ong0b0f4032019-03-18 19:06:15 -07001152 }, {
1153 desc: "Value empty field",
1154 input: &pb2.KnownTypes{
Joe Tsaia95b29f2019-05-16 12:47:20 -07001155 OptValue: &structpb.Value{},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001156 },
Herbie Ong1c7462c2019-03-22 17:56:55 -07001157 wantErr: true,
Herbie Ong0b0f4032019-03-18 19:06:15 -07001158 }, {
1159 desc: "Value contains NullValue",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001160 input: &structpb.Value{Kind: &structpb.Value_NullValue{}},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001161 want: `null`,
1162 }, {
1163 desc: "Value contains BoolValue",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001164 input: &structpb.Value{Kind: &structpb.Value_BoolValue{}},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001165 want: `false`,
1166 }, {
1167 desc: "Value contains NumberValue",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001168 input: &structpb.Value{Kind: &structpb.Value_NumberValue{1.02}},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001169 want: `1.02`,
1170 }, {
1171 desc: "Value contains StringValue",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001172 input: &structpb.Value{Kind: &structpb.Value_StringValue{"hello"}},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001173 want: `"hello"`,
1174 }, {
1175 desc: "Value contains StringValue with invalid UTF8",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001176 input: &structpb.Value{Kind: &structpb.Value_StringValue{"\xff"}},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001177 wantErr: true,
1178 }, {
1179 desc: "Value contains Struct",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001180 input: &structpb.Value{
1181 Kind: &structpb.Value_StructValue{
1182 &structpb.Struct{
1183 Fields: map[string]*structpb.Value{
1184 "null": {Kind: &structpb.Value_NullValue{}},
1185 "number": {Kind: &structpb.Value_NumberValue{}},
1186 "string": {Kind: &structpb.Value_StringValue{}},
1187 "struct": {Kind: &structpb.Value_StructValue{}},
1188 "list": {Kind: &structpb.Value_ListValue{}},
1189 "bool": {Kind: &structpb.Value_BoolValue{}},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001190 },
1191 },
1192 },
1193 },
1194 want: `{
1195 "bool": false,
1196 "list": [],
1197 "null": null,
1198 "number": 0,
1199 "string": "",
1200 "struct": {}
1201}`,
1202 }, {
1203 desc: "Value contains ListValue",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001204 input: &structpb.Value{
1205 Kind: &structpb.Value_ListValue{
1206 &structpb.ListValue{
1207 Values: []*structpb.Value{
1208 {Kind: &structpb.Value_BoolValue{}},
1209 {Kind: &structpb.Value_NullValue{}},
1210 {Kind: &structpb.Value_NumberValue{}},
1211 {Kind: &structpb.Value_StringValue{}},
1212 {Kind: &structpb.Value_StructValue{}},
1213 {Kind: &structpb.Value_ListValue{}},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001214 },
1215 },
1216 },
1217 },
1218 want: `[
1219 false,
1220 null,
1221 0,
1222 "",
1223 {},
1224 []
1225]`,
1226 }, {
1227 desc: "Struct with nil map",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001228 input: &structpb.Struct{},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001229 want: `{}`,
1230 }, {
1231 desc: "Struct with empty map",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001232 input: &structpb.Struct{
1233 Fields: map[string]*structpb.Value{},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001234 },
1235 want: `{}`,
1236 }, {
1237 desc: "Struct",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001238 input: &structpb.Struct{
1239 Fields: map[string]*structpb.Value{
1240 "bool": {Kind: &structpb.Value_BoolValue{true}},
1241 "null": {Kind: &structpb.Value_NullValue{}},
1242 "number": {Kind: &structpb.Value_NumberValue{3.1415}},
1243 "string": {Kind: &structpb.Value_StringValue{"hello"}},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001244 "struct": {
Joe Tsaia95b29f2019-05-16 12:47:20 -07001245 Kind: &structpb.Value_StructValue{
1246 &structpb.Struct{
1247 Fields: map[string]*structpb.Value{
1248 "string": {Kind: &structpb.Value_StringValue{"world"}},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001249 },
1250 },
1251 },
1252 },
1253 "list": {
Joe Tsaia95b29f2019-05-16 12:47:20 -07001254 Kind: &structpb.Value_ListValue{
1255 &structpb.ListValue{
1256 Values: []*structpb.Value{
1257 {Kind: &structpb.Value_BoolValue{}},
1258 {Kind: &structpb.Value_NullValue{}},
1259 {Kind: &structpb.Value_NumberValue{}},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001260 },
1261 },
1262 },
1263 },
1264 },
1265 },
1266 want: `{
1267 "bool": true,
1268 "list": [
1269 false,
1270 null,
1271 0
1272 ],
1273 "null": null,
1274 "number": 3.1415,
1275 "string": "hello",
1276 "struct": {
1277 "string": "world"
1278 }
1279}`,
1280 }, {
1281 desc: "Struct message with invalid UTF8 string",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001282 input: &structpb.Struct{
1283 Fields: map[string]*structpb.Value{
1284 "string": {Kind: &structpb.Value_StringValue{"\xff"}},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001285 },
1286 },
Herbie Ong0b0f4032019-03-18 19:06:15 -07001287 wantErr: true,
1288 }, {
1289 desc: "ListValue with nil values",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001290 input: &structpb.ListValue{},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001291 want: `[]`,
1292 }, {
1293 desc: "ListValue with empty values",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001294 input: &structpb.ListValue{
1295 Values: []*structpb.Value{},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001296 },
1297 want: `[]`,
1298 }, {
1299 desc: "ListValue",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001300 input: &structpb.ListValue{
1301 Values: []*structpb.Value{
1302 {Kind: &structpb.Value_BoolValue{true}},
1303 {Kind: &structpb.Value_NullValue{}},
1304 {Kind: &structpb.Value_NumberValue{3.1415}},
1305 {Kind: &structpb.Value_StringValue{"hello"}},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001306 {
Joe Tsaia95b29f2019-05-16 12:47:20 -07001307 Kind: &structpb.Value_ListValue{
1308 &structpb.ListValue{
1309 Values: []*structpb.Value{
1310 {Kind: &structpb.Value_BoolValue{}},
1311 {Kind: &structpb.Value_NullValue{}},
1312 {Kind: &structpb.Value_NumberValue{}},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001313 },
1314 },
1315 },
1316 },
1317 {
Joe Tsaia95b29f2019-05-16 12:47:20 -07001318 Kind: &structpb.Value_StructValue{
1319 &structpb.Struct{
1320 Fields: map[string]*structpb.Value{
1321 "string": {Kind: &structpb.Value_StringValue{"world"}},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001322 },
1323 },
1324 },
1325 },
1326 },
1327 },
1328 want: `[
1329 true,
1330 null,
1331 3.1415,
1332 "hello",
1333 [
1334 false,
1335 null,
1336 0
1337 ],
1338 {
1339 "string": "world"
1340 }
1341]`,
1342 }, {
1343 desc: "ListValue with invalid UTF8 string",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001344 input: &structpb.ListValue{
1345 Values: []*structpb.Value{
1346 {Kind: &structpb.Value_StringValue{"\xff"}},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001347 },
1348 },
Herbie Ong0b0f4032019-03-18 19:06:15 -07001349 wantErr: true,
1350 }, {
1351 desc: "Duration empty",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001352 input: &durationpb.Duration{},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001353 want: `"0s"`,
1354 }, {
1355 desc: "Duration with secs",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001356 input: &durationpb.Duration{Seconds: 3},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001357 want: `"3s"`,
1358 }, {
1359 desc: "Duration with -secs",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001360 input: &durationpb.Duration{Seconds: -3},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001361 want: `"-3s"`,
1362 }, {
1363 desc: "Duration with nanos",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001364 input: &durationpb.Duration{Nanos: 1e6},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001365 want: `"0.001s"`,
1366 }, {
1367 desc: "Duration with -nanos",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001368 input: &durationpb.Duration{Nanos: -1e6},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001369 want: `"-0.001s"`,
1370 }, {
1371 desc: "Duration with large secs",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001372 input: &durationpb.Duration{Seconds: 1e10, Nanos: 1},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001373 want: `"10000000000.000000001s"`,
1374 }, {
1375 desc: "Duration with 6-digit nanos",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001376 input: &durationpb.Duration{Nanos: 1e4},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001377 want: `"0.000010s"`,
1378 }, {
1379 desc: "Duration with 3-digit nanos",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001380 input: &durationpb.Duration{Nanos: 1e6},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001381 want: `"0.001s"`,
1382 }, {
1383 desc: "Duration with -secs -nanos",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001384 input: &durationpb.Duration{Seconds: -123, Nanos: -450},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001385 want: `"-123.000000450s"`,
1386 }, {
Herbie Ongad9c1252019-04-24 20:51:28 -07001387 desc: "Duration max value",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001388 input: &durationpb.Duration{Seconds: 315576000000, Nanos: 999999999},
Herbie Ongad9c1252019-04-24 20:51:28 -07001389 want: `"315576000000.999999999s"`,
1390 }, {
1391 desc: "Duration min value",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001392 input: &durationpb.Duration{Seconds: -315576000000, Nanos: -999999999},
Herbie Ongad9c1252019-04-24 20:51:28 -07001393 want: `"-315576000000.999999999s"`,
1394 }, {
Herbie Ong0b0f4032019-03-18 19:06:15 -07001395 desc: "Duration with +secs -nanos",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001396 input: &durationpb.Duration{Seconds: 1, Nanos: -1},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001397 wantErr: true,
1398 }, {
1399 desc: "Duration with -secs +nanos",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001400 input: &durationpb.Duration{Seconds: -1, Nanos: 1},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001401 wantErr: true,
1402 }, {
1403 desc: "Duration with +secs out of range",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001404 input: &durationpb.Duration{Seconds: 315576000001},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001405 wantErr: true,
1406 }, {
1407 desc: "Duration with -secs out of range",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001408 input: &durationpb.Duration{Seconds: -315576000001},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001409 wantErr: true,
1410 }, {
1411 desc: "Duration with +nanos out of range",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001412 input: &durationpb.Duration{Seconds: 0, Nanos: 1e9},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001413 wantErr: true,
1414 }, {
1415 desc: "Duration with -nanos out of range",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001416 input: &durationpb.Duration{Seconds: 0, Nanos: -1e9},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001417 wantErr: true,
1418 }, {
1419 desc: "Timestamp zero",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001420 input: &timestamppb.Timestamp{},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001421 want: `"1970-01-01T00:00:00Z"`,
1422 }, {
1423 desc: "Timestamp",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001424 input: &timestamppb.Timestamp{Seconds: 1553036601},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001425 want: `"2019-03-19T23:03:21Z"`,
1426 }, {
1427 desc: "Timestamp with nanos",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001428 input: &timestamppb.Timestamp{Seconds: 1553036601, Nanos: 1},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001429 want: `"2019-03-19T23:03:21.000000001Z"`,
1430 }, {
1431 desc: "Timestamp with 6-digit nanos",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001432 input: &timestamppb.Timestamp{Nanos: 1e3},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001433 want: `"1970-01-01T00:00:00.000001Z"`,
1434 }, {
1435 desc: "Timestamp with 3-digit nanos",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001436 input: &timestamppb.Timestamp{Nanos: 1e7},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001437 want: `"1970-01-01T00:00:00.010Z"`,
1438 }, {
Herbie Ongad9c1252019-04-24 20:51:28 -07001439 desc: "Timestamp max value",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001440 input: &timestamppb.Timestamp{Seconds: 253402300799, Nanos: 999999999},
Herbie Ongad9c1252019-04-24 20:51:28 -07001441 want: `"9999-12-31T23:59:59.999999999Z"`,
1442 }, {
1443 desc: "Timestamp min value",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001444 input: &timestamppb.Timestamp{Seconds: -62135596800},
Herbie Ongad9c1252019-04-24 20:51:28 -07001445 want: `"0001-01-01T00:00:00Z"`,
1446 }, {
Herbie Ong0b0f4032019-03-18 19:06:15 -07001447 desc: "Timestamp with +secs out of range",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001448 input: &timestamppb.Timestamp{Seconds: 253402300800},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001449 wantErr: true,
1450 }, {
1451 desc: "Timestamp with -secs out of range",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001452 input: &timestamppb.Timestamp{Seconds: -62135596801},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001453 wantErr: true,
1454 }, {
1455 desc: "Timestamp with -nanos",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001456 input: &timestamppb.Timestamp{Nanos: -1},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001457 wantErr: true,
1458 }, {
1459 desc: "Timestamp with +nanos out of range",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001460 input: &timestamppb.Timestamp{Nanos: 1e9},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001461 wantErr: true,
1462 }, {
1463 desc: "FieldMask empty",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001464 input: &fieldmaskpb.FieldMask{},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001465 want: `""`,
1466 }, {
1467 desc: "FieldMask",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001468 input: &fieldmaskpb.FieldMask{
Herbie Ong0b0f4032019-03-18 19:06:15 -07001469 Paths: []string{
1470 "foo",
1471 "foo_bar",
1472 "foo.bar_qux",
1473 "_foo",
1474 },
1475 },
1476 want: `"foo,fooBar,foo.barQux,Foo"`,
1477 }, {
1478 desc: "FieldMask error 1",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001479 input: &fieldmaskpb.FieldMask{
Herbie Ong0b0f4032019-03-18 19:06:15 -07001480 Paths: []string{"foo_"},
1481 },
1482 wantErr: true,
1483 }, {
1484 desc: "FieldMask error 2",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001485 input: &fieldmaskpb.FieldMask{
Herbie Ong0b0f4032019-03-18 19:06:15 -07001486 Paths: []string{"foo__bar"},
1487 },
1488 wantErr: true,
1489 }, {
1490 desc: "Any empty",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001491 input: &anypb.Any{},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001492 want: `{}`,
1493 }, {
Herbie Ong8ac9dd22019-03-27 12:20:50 -07001494 desc: "Any with non-custom message",
Damien Neil5c5b5312019-05-14 12:44:37 -07001495 mo: protojson.MarshalOptions{
Joe Tsai0fc49f82019-05-01 12:29:25 -07001496 Resolver: preg.NewTypes(pimpl.Export{}.MessageTypeOf(&pb2.Nested{})),
Herbie Ong0b0f4032019-03-18 19:06:15 -07001497 },
1498 input: func() proto.Message {
1499 m := &pb2.Nested{
Damien Neila8a2cea2019-07-10 16:17:16 -07001500 OptString: proto.String("embedded inside Any"),
Herbie Ong0b0f4032019-03-18 19:06:15 -07001501 OptNested: &pb2.Nested{
Damien Neila8a2cea2019-07-10 16:17:16 -07001502 OptString: proto.String("inception"),
Herbie Ong0b0f4032019-03-18 19:06:15 -07001503 },
1504 }
1505 b, err := proto.MarshalOptions{Deterministic: true}.Marshal(m)
1506 if err != nil {
1507 t.Fatalf("error in binary marshaling message for Any.value: %v", err)
1508 }
Joe Tsaia95b29f2019-05-16 12:47:20 -07001509 return &anypb.Any{
Herbie Ong0b0f4032019-03-18 19:06:15 -07001510 TypeUrl: "foo/pb2.Nested",
1511 Value: b,
1512 }
1513 }(),
1514 want: `{
1515 "@type": "foo/pb2.Nested",
1516 "optString": "embedded inside Any",
1517 "optNested": {
1518 "optString": "inception"
1519 }
1520}`,
1521 }, {
Herbie Ong8ac9dd22019-03-27 12:20:50 -07001522 desc: "Any with empty embedded message",
Damien Neil5c5b5312019-05-14 12:44:37 -07001523 mo: protojson.MarshalOptions{
Joe Tsai0fc49f82019-05-01 12:29:25 -07001524 Resolver: preg.NewTypes(pimpl.Export{}.MessageTypeOf(&pb2.Nested{})),
Herbie Ong0b0f4032019-03-18 19:06:15 -07001525 },
Joe Tsaia95b29f2019-05-16 12:47:20 -07001526 input: &anypb.Any{TypeUrl: "foo/pb2.Nested"},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001527 want: `{
1528 "@type": "foo/pb2.Nested"
1529}`,
1530 }, {
Herbie Ong8ac9dd22019-03-27 12:20:50 -07001531 desc: "Any without registered type",
Damien Neil5c5b5312019-05-14 12:44:37 -07001532 mo: protojson.MarshalOptions{Resolver: preg.NewTypes()},
Joe Tsaia95b29f2019-05-16 12:47:20 -07001533 input: &anypb.Any{TypeUrl: "foo/pb2.Nested"},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001534 wantErr: true,
1535 }, {
Damien Neil0c9f0a92019-06-19 10:41:09 -07001536 desc: "Any with missing required",
Damien Neil5c5b5312019-05-14 12:44:37 -07001537 mo: protojson.MarshalOptions{
Joe Tsai0fc49f82019-05-01 12:29:25 -07001538 Resolver: preg.NewTypes(pimpl.Export{}.MessageTypeOf(&pb2.PartialRequired{})),
Herbie Ong0b0f4032019-03-18 19:06:15 -07001539 },
1540 input: func() proto.Message {
1541 m := &pb2.PartialRequired{
Damien Neila8a2cea2019-07-10 16:17:16 -07001542 OptString: proto.String("embedded inside Any"),
Herbie Ong0b0f4032019-03-18 19:06:15 -07001543 }
Damien Neil96c229a2019-04-03 12:17:24 -07001544 b, err := proto.MarshalOptions{
1545 AllowPartial: true,
1546 Deterministic: true,
1547 }.Marshal(m)
Herbie Ong0b0f4032019-03-18 19:06:15 -07001548 if err != nil {
1549 t.Fatalf("error in binary marshaling message for Any.value: %v", err)
1550 }
Joe Tsaia95b29f2019-05-16 12:47:20 -07001551 return &anypb.Any{
Joe Tsai0fc49f82019-05-01 12:29:25 -07001552 TypeUrl: string(m.ProtoReflect().Descriptor().FullName()),
Herbie Ong0b0f4032019-03-18 19:06:15 -07001553 Value: b,
1554 }
1555 }(),
1556 want: `{
1557 "@type": "pb2.PartialRequired",
1558 "optString": "embedded inside Any"
1559}`,
Herbie Ong0b0f4032019-03-18 19:06:15 -07001560 }, {
Herbie Ong8ac9dd22019-03-27 12:20:50 -07001561 desc: "Any with partial required and AllowPartial",
Damien Neil5c5b5312019-05-14 12:44:37 -07001562 mo: protojson.MarshalOptions{
Herbie Ong8ac9dd22019-03-27 12:20:50 -07001563 AllowPartial: true,
Joe Tsai0fc49f82019-05-01 12:29:25 -07001564 Resolver: preg.NewTypes(pimpl.Export{}.MessageTypeOf(&pb2.PartialRequired{})),
Herbie Ong8ac9dd22019-03-27 12:20:50 -07001565 },
1566 input: func() proto.Message {
1567 m := &pb2.PartialRequired{
Damien Neila8a2cea2019-07-10 16:17:16 -07001568 OptString: proto.String("embedded inside Any"),
Herbie Ong8ac9dd22019-03-27 12:20:50 -07001569 }
Damien Neil96c229a2019-04-03 12:17:24 -07001570 b, err := proto.MarshalOptions{
1571 AllowPartial: true,
1572 Deterministic: true,
1573 }.Marshal(m)
Herbie Ong8ac9dd22019-03-27 12:20:50 -07001574 if err != nil {
1575 t.Fatalf("error in binary marshaling message for Any.value: %v", err)
1576 }
Joe Tsaia95b29f2019-05-16 12:47:20 -07001577 return &anypb.Any{
Joe Tsai0fc49f82019-05-01 12:29:25 -07001578 TypeUrl: string(m.ProtoReflect().Descriptor().FullName()),
Herbie Ong8ac9dd22019-03-27 12:20:50 -07001579 Value: b,
1580 }
1581 }(),
1582 want: `{
1583 "@type": "pb2.PartialRequired",
1584 "optString": "embedded inside Any"
1585}`,
1586 }, {
Herbie Ong0b0f4032019-03-18 19:06:15 -07001587 desc: "Any with invalid UTF8",
Damien Neil5c5b5312019-05-14 12:44:37 -07001588 mo: protojson.MarshalOptions{
Joe Tsai0fc49f82019-05-01 12:29:25 -07001589 Resolver: preg.NewTypes(pimpl.Export{}.MessageTypeOf(&pb2.Nested{})),
Herbie Ong0b0f4032019-03-18 19:06:15 -07001590 },
1591 input: func() proto.Message {
1592 m := &pb2.Nested{
Damien Neila8a2cea2019-07-10 16:17:16 -07001593 OptString: proto.String("abc\xff"),
Herbie Ong0b0f4032019-03-18 19:06:15 -07001594 }
1595 b, err := proto.MarshalOptions{Deterministic: true}.Marshal(m)
1596 if err != nil {
1597 t.Fatalf("error in binary marshaling message for Any.value: %v", err)
1598 }
Joe Tsaia95b29f2019-05-16 12:47:20 -07001599 return &anypb.Any{
Herbie Ong0b0f4032019-03-18 19:06:15 -07001600 TypeUrl: "foo/pb2.Nested",
1601 Value: b,
1602 }
1603 }(),
Herbie Ong0b0f4032019-03-18 19:06:15 -07001604 wantErr: true,
1605 }, {
1606 desc: "Any with invalid value",
Damien Neil5c5b5312019-05-14 12:44:37 -07001607 mo: protojson.MarshalOptions{
Joe Tsai0fc49f82019-05-01 12:29:25 -07001608 Resolver: preg.NewTypes(pimpl.Export{}.MessageTypeOf(&pb2.Nested{})),
Herbie Ong0b0f4032019-03-18 19:06:15 -07001609 },
Joe Tsaia95b29f2019-05-16 12:47:20 -07001610 input: &anypb.Any{
Herbie Ong0b0f4032019-03-18 19:06:15 -07001611 TypeUrl: "foo/pb2.Nested",
Joe Tsai6dc168e2019-07-09 23:11:13 -07001612 Value: []byte("\x80"),
Herbie Ong0b0f4032019-03-18 19:06:15 -07001613 },
1614 wantErr: true,
1615 }, {
1616 desc: "Any with BoolValue",
Damien Neil5c5b5312019-05-14 12:44:37 -07001617 mo: protojson.MarshalOptions{
Joe Tsaia95b29f2019-05-16 12:47:20 -07001618 Resolver: preg.NewTypes(pimpl.Export{}.MessageTypeOf(&wrapperspb.BoolValue{})),
Herbie Ong0b0f4032019-03-18 19:06:15 -07001619 },
1620 input: func() proto.Message {
Joe Tsaia95b29f2019-05-16 12:47:20 -07001621 m := &wrapperspb.BoolValue{Value: true}
Herbie Ong0b0f4032019-03-18 19:06:15 -07001622 b, err := proto.MarshalOptions{Deterministic: true}.Marshal(m)
1623 if err != nil {
1624 t.Fatalf("error in binary marshaling message for Any.value: %v", err)
1625 }
Joe Tsaia95b29f2019-05-16 12:47:20 -07001626 return &anypb.Any{
Herbie Ong0b0f4032019-03-18 19:06:15 -07001627 TypeUrl: "type.googleapis.com/google.protobuf.BoolValue",
1628 Value: b,
1629 }
1630 }(),
1631 want: `{
1632 "@type": "type.googleapis.com/google.protobuf.BoolValue",
1633 "value": true
1634}`,
1635 }, {
Herbie Ong0b0f4032019-03-18 19:06:15 -07001636 desc: "Any with Empty",
Damien Neil5c5b5312019-05-14 12:44:37 -07001637 mo: protojson.MarshalOptions{
Joe Tsaia95b29f2019-05-16 12:47:20 -07001638 Resolver: preg.NewTypes(pimpl.Export{}.MessageTypeOf(&emptypb.Empty{})),
Herbie Ong0b0f4032019-03-18 19:06:15 -07001639 },
1640 input: func() proto.Message {
Joe Tsaia95b29f2019-05-16 12:47:20 -07001641 m := &emptypb.Empty{}
Herbie Ong0b0f4032019-03-18 19:06:15 -07001642 b, err := proto.MarshalOptions{Deterministic: true}.Marshal(m)
1643 if err != nil {
1644 t.Fatalf("error in binary marshaling message for Any.value: %v", err)
1645 }
Joe Tsaia95b29f2019-05-16 12:47:20 -07001646 return &anypb.Any{
Herbie Ong0b0f4032019-03-18 19:06:15 -07001647 TypeUrl: "type.googleapis.com/google.protobuf.Empty",
1648 Value: b,
1649 }
1650 }(),
1651 want: `{
Herbie Ong82014a52019-03-27 15:21:43 -07001652 "@type": "type.googleapis.com/google.protobuf.Empty",
1653 "value": {}
Herbie Ong0b0f4032019-03-18 19:06:15 -07001654}`,
1655 }, {
1656 desc: "Any with StringValue containing invalid UTF8",
Damien Neil5c5b5312019-05-14 12:44:37 -07001657 mo: protojson.MarshalOptions{
Joe Tsaia95b29f2019-05-16 12:47:20 -07001658 Resolver: preg.NewTypes(pimpl.Export{}.MessageTypeOf(&wrapperspb.StringValue{})),
Herbie Ong0b0f4032019-03-18 19:06:15 -07001659 },
1660 input: func() proto.Message {
Joe Tsaia95b29f2019-05-16 12:47:20 -07001661 m := &wrapperspb.StringValue{Value: "abcd"}
Herbie Ong0b0f4032019-03-18 19:06:15 -07001662 b, err := proto.MarshalOptions{Deterministic: true}.Marshal(m)
1663 if err != nil {
1664 t.Fatalf("error in binary marshaling message for Any.value: %v", err)
1665 }
Joe Tsaia95b29f2019-05-16 12:47:20 -07001666 return &anypb.Any{
Herbie Ong0b0f4032019-03-18 19:06:15 -07001667 TypeUrl: "google.protobuf.StringValue",
Damien Neilbc310b52019-04-11 11:46:55 -07001668 Value: bytes.Replace(b, []byte("abcd"), []byte("abc\xff"), -1),
Herbie Ong0b0f4032019-03-18 19:06:15 -07001669 }
1670 }(),
Herbie Ong0b0f4032019-03-18 19:06:15 -07001671 wantErr: true,
1672 }, {
Herbie Ong8ac9dd22019-03-27 12:20:50 -07001673 desc: "Any with Int64Value",
Damien Neil5c5b5312019-05-14 12:44:37 -07001674 mo: protojson.MarshalOptions{
Joe Tsaia95b29f2019-05-16 12:47:20 -07001675 Resolver: preg.NewTypes(pimpl.Export{}.MessageTypeOf(&wrapperspb.Int64Value{})),
Herbie Ong8ac9dd22019-03-27 12:20:50 -07001676 },
1677 input: func() proto.Message {
Joe Tsaia95b29f2019-05-16 12:47:20 -07001678 m := &wrapperspb.Int64Value{Value: 42}
Herbie Ong8ac9dd22019-03-27 12:20:50 -07001679 b, err := proto.MarshalOptions{Deterministic: true}.Marshal(m)
1680 if err != nil {
1681 t.Fatalf("error in binary marshaling message for Any.value: %v", err)
1682 }
Joe Tsaia95b29f2019-05-16 12:47:20 -07001683 return &anypb.Any{
Herbie Ong8ac9dd22019-03-27 12:20:50 -07001684 TypeUrl: "google.protobuf.Int64Value",
1685 Value: b,
1686 }
1687 }(),
1688 want: `{
1689 "@type": "google.protobuf.Int64Value",
1690 "value": "42"
1691}`,
1692 }, {
1693 desc: "Any with Duration",
Damien Neil5c5b5312019-05-14 12:44:37 -07001694 mo: protojson.MarshalOptions{
Joe Tsaia95b29f2019-05-16 12:47:20 -07001695 Resolver: preg.NewTypes(pimpl.Export{}.MessageTypeOf(&durationpb.Duration{})),
Herbie Ong8ac9dd22019-03-27 12:20:50 -07001696 },
1697 input: func() proto.Message {
Joe Tsaia95b29f2019-05-16 12:47:20 -07001698 m := &durationpb.Duration{}
Herbie Ong8ac9dd22019-03-27 12:20:50 -07001699 b, err := proto.MarshalOptions{Deterministic: true}.Marshal(m)
1700 if err != nil {
1701 t.Fatalf("error in binary marshaling message for Any.value: %v", err)
1702 }
Joe Tsaia95b29f2019-05-16 12:47:20 -07001703 return &anypb.Any{
Herbie Ong8ac9dd22019-03-27 12:20:50 -07001704 TypeUrl: "type.googleapis.com/google.protobuf.Duration",
1705 Value: b,
1706 }
1707 }(),
1708 want: `{
1709 "@type": "type.googleapis.com/google.protobuf.Duration",
1710 "value": "0s"
1711}`,
1712 }, {
1713 desc: "Any with empty Value",
Damien Neil5c5b5312019-05-14 12:44:37 -07001714 mo: protojson.MarshalOptions{
Joe Tsaia95b29f2019-05-16 12:47:20 -07001715 Resolver: preg.NewTypes(pimpl.Export{}.MessageTypeOf(&structpb.Value{})),
Herbie Ong8ac9dd22019-03-27 12:20:50 -07001716 },
1717 input: func() proto.Message {
Joe Tsaia95b29f2019-05-16 12:47:20 -07001718 m := &structpb.Value{}
Herbie Ong8ac9dd22019-03-27 12:20:50 -07001719 b, err := proto.Marshal(m)
1720 if err != nil {
1721 t.Fatalf("error in binary marshaling message for Any.value: %v", err)
1722 }
Joe Tsaia95b29f2019-05-16 12:47:20 -07001723 return &anypb.Any{
Herbie Ong8ac9dd22019-03-27 12:20:50 -07001724 TypeUrl: "type.googleapis.com/google.protobuf.Value",
1725 Value: b,
1726 }
1727 }(),
1728 wantErr: true,
1729 }, {
Herbie Ong0b0f4032019-03-18 19:06:15 -07001730 desc: "Any with Value of StringValue",
Damien Neil5c5b5312019-05-14 12:44:37 -07001731 mo: protojson.MarshalOptions{
Joe Tsaia95b29f2019-05-16 12:47:20 -07001732 Resolver: preg.NewTypes(pimpl.Export{}.MessageTypeOf(&structpb.Value{})),
Herbie Ong0b0f4032019-03-18 19:06:15 -07001733 },
1734 input: func() proto.Message {
Joe Tsaia95b29f2019-05-16 12:47:20 -07001735 m := &structpb.Value{Kind: &structpb.Value_StringValue{"abcd"}}
Herbie Ong0b0f4032019-03-18 19:06:15 -07001736 b, err := proto.MarshalOptions{Deterministic: true}.Marshal(m)
1737 if err != nil {
1738 t.Fatalf("error in binary marshaling message for Any.value: %v", err)
1739 }
Joe Tsaia95b29f2019-05-16 12:47:20 -07001740 return &anypb.Any{
Herbie Ong0b0f4032019-03-18 19:06:15 -07001741 TypeUrl: "type.googleapis.com/google.protobuf.Value",
Damien Neilbc310b52019-04-11 11:46:55 -07001742 Value: bytes.Replace(b, []byte("abcd"), []byte("abc\xff"), -1),
Herbie Ong0b0f4032019-03-18 19:06:15 -07001743 }
1744 }(),
Herbie Ong0b0f4032019-03-18 19:06:15 -07001745 wantErr: true,
1746 }, {
Herbie Ong8ac9dd22019-03-27 12:20:50 -07001747 desc: "Any with Value of NullValue",
Damien Neil5c5b5312019-05-14 12:44:37 -07001748 mo: protojson.MarshalOptions{
Joe Tsaia95b29f2019-05-16 12:47:20 -07001749 Resolver: preg.NewTypes(pimpl.Export{}.MessageTypeOf(&structpb.Value{})),
Herbie Ong0b0f4032019-03-18 19:06:15 -07001750 },
1751 input: func() proto.Message {
Joe Tsaia95b29f2019-05-16 12:47:20 -07001752 m := &structpb.Value{Kind: &structpb.Value_NullValue{}}
Herbie Ong8ac9dd22019-03-27 12:20:50 -07001753 b, err := proto.MarshalOptions{Deterministic: true}.Marshal(m)
Herbie Ong0b0f4032019-03-18 19:06:15 -07001754 if err != nil {
1755 t.Fatalf("error in binary marshaling message for Any.value: %v", err)
1756 }
Joe Tsaia95b29f2019-05-16 12:47:20 -07001757 return &anypb.Any{
Herbie Ong0b0f4032019-03-18 19:06:15 -07001758 TypeUrl: "type.googleapis.com/google.protobuf.Value",
1759 Value: b,
1760 }
1761 }(),
Herbie Ong0b0f4032019-03-18 19:06:15 -07001762 want: `{
Herbie Ong8ac9dd22019-03-27 12:20:50 -07001763 "@type": "type.googleapis.com/google.protobuf.Value",
1764 "value": null
Herbie Ong0b0f4032019-03-18 19:06:15 -07001765}`,
1766 }, {
1767 desc: "Any with Struct",
Damien Neil5c5b5312019-05-14 12:44:37 -07001768 mo: protojson.MarshalOptions{
Herbie Ong0b0f4032019-03-18 19:06:15 -07001769 Resolver: preg.NewTypes(
Joe Tsaia95b29f2019-05-16 12:47:20 -07001770 pimpl.Export{}.MessageTypeOf(&structpb.Struct{}),
1771 pimpl.Export{}.MessageTypeOf(&structpb.Value{}),
1772 pimpl.Export{}.MessageTypeOf(&wrapperspb.BoolValue{}),
1773 pimpl.Export{}.EnumTypeOf(structpb.NullValue_NULL_VALUE),
1774 pimpl.Export{}.MessageTypeOf(&wrapperspb.StringValue{}),
Herbie Ong0b0f4032019-03-18 19:06:15 -07001775 ),
1776 },
1777 input: func() proto.Message {
Joe Tsaia95b29f2019-05-16 12:47:20 -07001778 m := &structpb.Struct{
1779 Fields: map[string]*structpb.Value{
1780 "bool": {Kind: &structpb.Value_BoolValue{true}},
1781 "null": {Kind: &structpb.Value_NullValue{}},
1782 "string": {Kind: &structpb.Value_StringValue{"hello"}},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001783 "struct": {
Joe Tsaia95b29f2019-05-16 12:47:20 -07001784 Kind: &structpb.Value_StructValue{
1785 &structpb.Struct{
1786 Fields: map[string]*structpb.Value{
1787 "string": {Kind: &structpb.Value_StringValue{"world"}},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001788 },
1789 },
1790 },
1791 },
1792 },
1793 }
1794 b, err := proto.MarshalOptions{Deterministic: true}.Marshal(m)
1795 if err != nil {
1796 t.Fatalf("error in binary marshaling message for Any.value: %v", err)
1797 }
Joe Tsaia95b29f2019-05-16 12:47:20 -07001798 return &anypb.Any{
Herbie Ong0b0f4032019-03-18 19:06:15 -07001799 TypeUrl: "google.protobuf.Struct",
1800 Value: b,
1801 }
1802 }(),
1803 want: `{
1804 "@type": "google.protobuf.Struct",
1805 "value": {
1806 "bool": true,
1807 "null": null,
1808 "string": "hello",
1809 "struct": {
1810 "string": "world"
1811 }
1812 }
1813}`,
1814 }, {
Herbie Ong8ac9dd22019-03-27 12:20:50 -07001815 desc: "Any with missing type_url",
Damien Neil5c5b5312019-05-14 12:44:37 -07001816 mo: protojson.MarshalOptions{
Joe Tsaia95b29f2019-05-16 12:47:20 -07001817 Resolver: preg.NewTypes(pimpl.Export{}.MessageTypeOf(&wrapperspb.BoolValue{})),
Herbie Ong8ac9dd22019-03-27 12:20:50 -07001818 },
1819 input: func() proto.Message {
Joe Tsaia95b29f2019-05-16 12:47:20 -07001820 m := &wrapperspb.BoolValue{Value: true}
Herbie Ong8ac9dd22019-03-27 12:20:50 -07001821 b, err := proto.MarshalOptions{Deterministic: true}.Marshal(m)
1822 if err != nil {
1823 t.Fatalf("error in binary marshaling message for Any.value: %v", err)
1824 }
Joe Tsaia95b29f2019-05-16 12:47:20 -07001825 return &anypb.Any{
Herbie Ong8ac9dd22019-03-27 12:20:50 -07001826 Value: b,
1827 }
1828 }(),
1829 wantErr: true,
1830 }, {
Herbie Ong0b0f4032019-03-18 19:06:15 -07001831 desc: "well known types as field values",
Damien Neil5c5b5312019-05-14 12:44:37 -07001832 mo: protojson.MarshalOptions{
Joe Tsaia95b29f2019-05-16 12:47:20 -07001833 Resolver: preg.NewTypes(pimpl.Export{}.MessageTypeOf(&emptypb.Empty{})),
Herbie Ong0b0f4032019-03-18 19:06:15 -07001834 },
1835 input: &pb2.KnownTypes{
Joe Tsaia95b29f2019-05-16 12:47:20 -07001836 OptBool: &wrapperspb.BoolValue{Value: false},
1837 OptInt32: &wrapperspb.Int32Value{Value: 42},
1838 OptInt64: &wrapperspb.Int64Value{Value: 42},
1839 OptUint32: &wrapperspb.UInt32Value{Value: 42},
1840 OptUint64: &wrapperspb.UInt64Value{Value: 42},
1841 OptFloat: &wrapperspb.FloatValue{Value: 1.23},
1842 OptDouble: &wrapperspb.DoubleValue{Value: 3.1415},
1843 OptString: &wrapperspb.StringValue{Value: "hello"},
1844 OptBytes: &wrapperspb.BytesValue{Value: []byte("hello")},
1845 OptDuration: &durationpb.Duration{Seconds: 123},
1846 OptTimestamp: &timestamppb.Timestamp{Seconds: 1553036601},
1847 OptStruct: &structpb.Struct{
1848 Fields: map[string]*structpb.Value{
1849 "string": {Kind: &structpb.Value_StringValue{"hello"}},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001850 },
1851 },
Joe Tsaia95b29f2019-05-16 12:47:20 -07001852 OptList: &structpb.ListValue{
1853 Values: []*structpb.Value{
1854 {Kind: &structpb.Value_NullValue{}},
1855 {Kind: &structpb.Value_StringValue{}},
1856 {Kind: &structpb.Value_StructValue{}},
1857 {Kind: &structpb.Value_ListValue{}},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001858 },
1859 },
Joe Tsaia95b29f2019-05-16 12:47:20 -07001860 OptValue: &structpb.Value{
1861 Kind: &structpb.Value_StringValue{"world"},
Herbie Ong1c7462c2019-03-22 17:56:55 -07001862 },
Joe Tsaia95b29f2019-05-16 12:47:20 -07001863 OptEmpty: &emptypb.Empty{},
1864 OptAny: &anypb.Any{
Herbie Ong0b0f4032019-03-18 19:06:15 -07001865 TypeUrl: "google.protobuf.Empty",
1866 },
Joe Tsaia95b29f2019-05-16 12:47:20 -07001867 OptFieldmask: &fieldmaskpb.FieldMask{
Herbie Ong0b0f4032019-03-18 19:06:15 -07001868 Paths: []string{"foo_bar", "bar_foo"},
1869 },
1870 },
1871 want: `{
1872 "optBool": false,
1873 "optInt32": 42,
1874 "optInt64": "42",
1875 "optUint32": 42,
1876 "optUint64": "42",
1877 "optFloat": 1.23,
1878 "optDouble": 3.1415,
1879 "optString": "hello",
1880 "optBytes": "aGVsbG8=",
1881 "optDuration": "123s",
1882 "optTimestamp": "2019-03-19T23:03:21Z",
1883 "optStruct": {
1884 "string": "hello"
1885 },
1886 "optList": [
1887 null,
1888 "",
1889 {},
1890 []
1891 ],
Herbie Ong1c7462c2019-03-22 17:56:55 -07001892 "optValue": "world",
Herbie Ong0b0f4032019-03-18 19:06:15 -07001893 "optEmpty": {},
1894 "optAny": {
Herbie Ong82014a52019-03-27 15:21:43 -07001895 "@type": "google.protobuf.Empty",
1896 "value": {}
Herbie Ong0b0f4032019-03-18 19:06:15 -07001897 },
1898 "optFieldmask": "fooBar,barFoo"
1899}`,
Herbie Ong984e5282019-09-06 00:29:48 -07001900 }, {
1901 desc: "EmitUnpopulated: proto2 optional scalars",
1902 mo: protojson.MarshalOptions{EmitUnpopulated: true},
1903 input: &pb2.Scalars{},
1904 want: `{
1905 "optBool": null,
1906 "optInt32": null,
1907 "optInt64": null,
1908 "optUint32": null,
1909 "optUint64": null,
1910 "optSint32": null,
1911 "optSint64": null,
1912 "optFixed32": null,
1913 "optFixed64": null,
1914 "optSfixed32": null,
1915 "optSfixed64": null,
1916 "optFloat": null,
1917 "optDouble": null,
1918 "optBytes": null,
1919 "optString": null
1920}`,
1921 }, {
1922 desc: "EmitUnpopulated: proto3 scalars",
1923 mo: protojson.MarshalOptions{EmitUnpopulated: true},
1924 input: &pb3.Scalars{},
1925 want: `{
1926 "sBool": false,
1927 "sInt32": 0,
1928 "sInt64": "0",
1929 "sUint32": 0,
1930 "sUint64": "0",
1931 "sSint32": 0,
1932 "sSint64": "0",
1933 "sFixed32": 0,
1934 "sFixed64": "0",
1935 "sSfixed32": 0,
1936 "sSfixed64": "0",
1937 "sFloat": 0,
1938 "sDouble": 0,
1939 "sBytes": "",
1940 "sString": ""
1941}`,
1942 }, {
1943 desc: "EmitUnpopulated: proto2 enum",
1944 mo: protojson.MarshalOptions{EmitUnpopulated: true},
1945 input: &pb2.Enums{},
1946 want: `{
1947 "optEnum": null,
1948 "rptEnum": [],
1949 "optNestedEnum": null,
1950 "rptNestedEnum": []
1951}`,
1952 }, {
1953 desc: "EmitUnpopulated: proto3 enum",
1954 mo: protojson.MarshalOptions{EmitUnpopulated: true},
1955 input: &pb3.Enums{},
1956 want: `{
1957 "sEnum": "ZERO",
1958 "sNestedEnum": "CERO"
1959}`,
1960 }, {
1961 desc: "EmitUnpopulated: proto2 message and group fields",
1962 mo: protojson.MarshalOptions{EmitUnpopulated: true},
1963 input: &pb2.Nests{},
1964 want: `{
1965 "optNested": null,
1966 "optgroup": null,
1967 "rptNested": [],
1968 "rptgroup": []
1969}`,
1970 }, {
1971 desc: "EmitUnpopulated: proto3 message field",
1972 mo: protojson.MarshalOptions{EmitUnpopulated: true},
1973 input: &pb3.Nests{},
1974 want: `{
1975 "sNested": null
1976}`,
1977 }, {
1978 desc: "EmitUnpopulated: proto2 empty message and group fields",
1979 mo: protojson.MarshalOptions{EmitUnpopulated: true},
1980 input: &pb2.Nests{
1981 OptNested: &pb2.Nested{},
1982 Optgroup: &pb2.Nests_OptGroup{},
1983 },
1984 want: `{
1985 "optNested": {
1986 "optString": null,
1987 "optNested": null
1988 },
1989 "optgroup": {
1990 "optString": null,
1991 "optNested": null,
1992 "optnestedgroup": null
1993 },
1994 "rptNested": [],
1995 "rptgroup": []
1996}`,
1997 }, {
1998 desc: "EmitUnpopulated: proto3 empty message field",
1999 mo: protojson.MarshalOptions{EmitUnpopulated: true},
2000 input: &pb3.Nests{
2001 SNested: &pb3.Nested{},
2002 },
2003 want: `{
2004 "sNested": {
2005 "sString": "",
2006 "sNested": null
2007 }
2008}`,
2009 }, {
2010 desc: "EmitUnpopulated: proto2 required fields",
2011 mo: protojson.MarshalOptions{
2012 AllowPartial: true,
2013 EmitUnpopulated: true,
2014 },
2015 input: &pb2.Requireds{},
2016 want: `{
2017 "reqBool": null,
2018 "reqSfixed64": null,
2019 "reqDouble": null,
2020 "reqString": null,
2021 "reqEnum": null,
2022 "reqNested": null
2023}`,
2024 }, {
2025 desc: "EmitUnpopulated: repeated fields",
2026 mo: protojson.MarshalOptions{EmitUnpopulated: true},
2027 input: &pb2.Repeats{},
2028 want: `{
2029 "rptBool": [],
2030 "rptInt32": [],
2031 "rptInt64": [],
2032 "rptUint32": [],
2033 "rptUint64": [],
2034 "rptFloat": [],
2035 "rptDouble": [],
2036 "rptString": [],
2037 "rptBytes": []
2038}`,
2039 }, {
2040 desc: "EmitUnpopulated: repeated containing empty message",
2041 mo: protojson.MarshalOptions{EmitUnpopulated: true},
2042 input: &pb2.Nests{
2043 RptNested: []*pb2.Nested{nil, {}},
2044 },
2045 want: `{
2046 "optNested": null,
2047 "optgroup": null,
2048 "rptNested": [
2049 {
2050 "optString": null,
2051 "optNested": null
2052 },
2053 {
2054 "optString": null,
2055 "optNested": null
2056 }
2057 ],
2058 "rptgroup": []
2059}`,
2060 }, {
2061 desc: "EmitUnpopulated: map fields",
2062 mo: protojson.MarshalOptions{EmitUnpopulated: true},
2063 input: &pb3.Maps{},
2064 want: `{
2065 "int32ToStr": {},
2066 "boolToUint32": {},
2067 "uint64ToEnum": {},
2068 "strToNested": {},
2069 "strToOneofs": {}
2070}`,
2071 }, {
2072 desc: "EmitUnpopulated: map containing empty message",
2073 mo: protojson.MarshalOptions{EmitUnpopulated: true},
2074 input: &pb3.Maps{
2075 StrToNested: map[string]*pb3.Nested{
2076 "nested": &pb3.Nested{},
2077 },
2078 StrToOneofs: map[string]*pb3.Oneofs{
2079 "nested": &pb3.Oneofs{},
2080 },
2081 },
2082 want: `{
2083 "int32ToStr": {},
2084 "boolToUint32": {},
2085 "uint64ToEnum": {},
2086 "strToNested": {
2087 "nested": {
2088 "sString": "",
2089 "sNested": null
2090 }
2091 },
2092 "strToOneofs": {
2093 "nested": {}
2094 }
2095}`,
2096 }, {
2097 desc: "EmitUnpopulated: oneof fields",
2098 mo: protojson.MarshalOptions{EmitUnpopulated: true},
2099 input: &pb3.Oneofs{},
2100 want: `{}`,
2101 }, {
2102 desc: "EmitUnpopulated: extensions",
2103 mo: protojson.MarshalOptions{EmitUnpopulated: true},
2104 input: func() proto.Message {
2105 m := &pb2.Extensions{}
2106 proto.SetExtension(m, pb2.E_OptExtNested, &pb2.Nested{})
2107 proto.SetExtension(m, pb2.E_RptExtNested, []*pb2.Nested{
2108 nil,
2109 {},
2110 })
2111 return m
2112 }(),
2113 want: `{
2114 "optString": null,
2115 "optBool": null,
2116 "optInt32": null,
2117 "[pb2.opt_ext_nested]": {
2118 "optString": null,
2119 "optNested": null
2120 },
2121 "[pb2.rpt_ext_nested]": [
2122 {
2123 "optString": null,
2124 "optNested": null
2125 },
2126 {
2127 "optString": null,
2128 "optNested": null
2129 }
2130 ]
2131}`,
Herbie Ong9111f3b2019-09-06 14:35:09 -07002132 }, {
2133 desc: "UseEnumNumbers in singular field",
2134 mo: protojson.MarshalOptions{UseEnumNumbers: true},
2135 input: &pb2.Enums{
2136 OptEnum: pb2.Enum_ONE.Enum(),
2137 OptNestedEnum: pb2.Enums_UNO.Enum(),
2138 },
2139 want: `{
2140 "optEnum": 1,
2141 "optNestedEnum": 1
2142}`,
2143 }, {
2144 desc: "UseEnumNumbers in repeated field",
2145 mo: protojson.MarshalOptions{UseEnumNumbers: true},
2146 input: &pb2.Enums{
2147 RptEnum: []pb2.Enum{pb2.Enum_ONE, 2, pb2.Enum_TEN, 42},
2148 RptNestedEnum: []pb2.Enums_NestedEnum{pb2.Enums_UNO, pb2.Enums_DOS, 47},
2149 },
2150 want: `{
2151 "rptEnum": [
2152 1,
2153 2,
2154 10,
2155 42
2156 ],
2157 "rptNestedEnum": [
2158 1,
2159 2,
2160 47
2161 ]
2162}`,
2163 }, {
2164 desc: "UseEnumNumbers in map field",
2165 mo: protojson.MarshalOptions{UseEnumNumbers: true},
2166 input: &pb3.Maps{
2167 Uint64ToEnum: map[uint64]pb3.Enum{
2168 1: pb3.Enum_ONE,
2169 2: pb3.Enum_TWO,
2170 10: pb3.Enum_TEN,
2171 47: 47,
2172 },
2173 },
2174 want: `{
2175 "uint64ToEnum": {
2176 "1": 1,
2177 "2": 2,
2178 "10": 10,
2179 "47": 47
2180 }
2181}`,
Herbie Ong956cd6d2019-09-06 15:17:22 -07002182 }, {
2183 desc: "UseProtoNames",
2184 mo: protojson.MarshalOptions{UseProtoNames: true},
2185 input: &pb2.Nests{
2186 OptNested: &pb2.Nested{},
2187 Optgroup: &pb2.Nests_OptGroup{
2188 OptString: proto.String("inside a group"),
2189 OptNested: &pb2.Nested{
2190 OptString: proto.String("nested message inside a group"),
2191 },
2192 Optnestedgroup: &pb2.Nests_OptGroup_OptNestedGroup{
2193 OptFixed32: proto.Uint32(47),
2194 },
2195 },
2196 Rptgroup: []*pb2.Nests_RptGroup{
2197 {
2198 RptString: []string{"hello", "world"},
2199 },
2200 },
2201 },
2202 want: `{
2203 "opt_nested": {},
2204 "OptGroup": {
2205 "opt_string": "inside a group",
2206 "opt_nested": {
2207 "opt_string": "nested message inside a group"
2208 },
2209 "OptNestedGroup": {
2210 "opt_fixed32": 47
2211 }
2212 },
2213 "RptGroup": [
2214 {
2215 "rpt_string": [
2216 "hello",
2217 "world"
2218 ]
2219 }
2220 ]
2221}`,
Herbie Ong7b828bc2019-02-08 19:56:24 -08002222 }}
2223
2224 for _, tt := range tests {
2225 tt := tt
Joe Tsai5ae10aa2019-07-11 18:23:08 -07002226 if tt.skip {
2227 continue
2228 }
Herbie Ong7b828bc2019-02-08 19:56:24 -08002229 t.Run(tt.desc, func(t *testing.T) {
Herbie Ong0b0f4032019-03-18 19:06:15 -07002230 // Use 2-space indentation on all MarshalOptions.
2231 tt.mo.Indent = " "
Herbie Ong7b828bc2019-02-08 19:56:24 -08002232 b, err := tt.mo.Marshal(tt.input)
Herbie Ong0b0f4032019-03-18 19:06:15 -07002233 if err != nil && !tt.wantErr {
Herbie Ong7b828bc2019-02-08 19:56:24 -08002234 t.Errorf("Marshal() returned error: %v\n", err)
2235 }
Herbie Ong0b0f4032019-03-18 19:06:15 -07002236 if err == nil && tt.wantErr {
2237 t.Errorf("Marshal() got nil error, want error\n")
2238 }
Herbie Ong7b828bc2019-02-08 19:56:24 -08002239 got := string(b)
2240 if got != tt.want {
2241 t.Errorf("Marshal()\n<got>\n%v\n<want>\n%v\n", got, tt.want)
Joe Tsai6dc168e2019-07-09 23:11:13 -07002242 if diff := cmp.Diff(tt.want, got); diff != "" {
Herbie Ong7b828bc2019-02-08 19:56:24 -08002243 t.Errorf("Marshal() diff -want +got\n%v\n", diff)
2244 }
2245 }
2246 })
2247 }
2248}