blob: 42c3a966e2d73de2b38879c79e05b15e39c2ca91 [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"
Damien Neile89e6242019-05-13 23:55:40 -070014 "google.golang.org/protobuf/internal/encoding/pack"
Joe Tsai5ae10aa2019-07-11 18:23:08 -070015 "google.golang.org/protobuf/internal/flags"
Damien Neile89e6242019-05-13 23:55:40 -070016 pimpl "google.golang.org/protobuf/internal/impl"
Damien Neile89e6242019-05-13 23:55:40 -070017 "google.golang.org/protobuf/proto"
18 preg "google.golang.org/protobuf/reflect/protoregistry"
Herbie Ong7b828bc2019-02-08 19:56:24 -080019
Damien Neile89e6242019-05-13 23:55:40 -070020 "google.golang.org/protobuf/encoding/testprotos/pb2"
21 "google.golang.org/protobuf/encoding/testprotos/pb3"
Joe Tsaia95b29f2019-05-16 12:47:20 -070022 "google.golang.org/protobuf/types/known/anypb"
23 "google.golang.org/protobuf/types/known/durationpb"
24 "google.golang.org/protobuf/types/known/emptypb"
25 "google.golang.org/protobuf/types/known/fieldmaskpb"
26 "google.golang.org/protobuf/types/known/structpb"
27 "google.golang.org/protobuf/types/known/timestamppb"
28 "google.golang.org/protobuf/types/known/wrapperspb"
Herbie Ong7b828bc2019-02-08 19:56:24 -080029)
30
Herbie Ong7b828bc2019-02-08 19:56:24 -080031func TestMarshal(t *testing.T) {
32 tests := []struct {
Herbie Ong0b0f4032019-03-18 19:06:15 -070033 desc string
Damien Neil5c5b5312019-05-14 12:44:37 -070034 mo protojson.MarshalOptions
Herbie Ong0b0f4032019-03-18 19:06:15 -070035 input proto.Message
36 want string
37 wantErr bool // TODO: Verify error message substring.
Joe Tsai5ae10aa2019-07-11 18:23:08 -070038 skip bool
Herbie Ong7b828bc2019-02-08 19:56:24 -080039 }{{
40 desc: "proto2 optional scalars not set",
41 input: &pb2.Scalars{},
42 want: "{}",
43 }, {
44 desc: "proto3 scalars not set",
45 input: &pb3.Scalars{},
46 want: "{}",
47 }, {
48 desc: "proto2 optional scalars set to zero values",
49 input: &pb2.Scalars{
Damien Neila8a2cea2019-07-10 16:17:16 -070050 OptBool: proto.Bool(false),
51 OptInt32: proto.Int32(0),
52 OptInt64: proto.Int64(0),
53 OptUint32: proto.Uint32(0),
54 OptUint64: proto.Uint64(0),
55 OptSint32: proto.Int32(0),
56 OptSint64: proto.Int64(0),
57 OptFixed32: proto.Uint32(0),
58 OptFixed64: proto.Uint64(0),
59 OptSfixed32: proto.Int32(0),
60 OptSfixed64: proto.Int64(0),
61 OptFloat: proto.Float32(0),
62 OptDouble: proto.Float64(0),
Herbie Ong7b828bc2019-02-08 19:56:24 -080063 OptBytes: []byte{},
Damien Neila8a2cea2019-07-10 16:17:16 -070064 OptString: proto.String(""),
Herbie Ong7b828bc2019-02-08 19:56:24 -080065 },
66 want: `{
67 "optBool": false,
68 "optInt32": 0,
69 "optInt64": "0",
70 "optUint32": 0,
71 "optUint64": "0",
72 "optSint32": 0,
73 "optSint64": "0",
74 "optFixed32": 0,
75 "optFixed64": "0",
76 "optSfixed32": 0,
77 "optSfixed64": "0",
78 "optFloat": 0,
79 "optDouble": 0,
80 "optBytes": "",
81 "optString": ""
82}`,
83 }, {
84 desc: "proto2 optional scalars set to some values",
85 input: &pb2.Scalars{
Damien Neila8a2cea2019-07-10 16:17:16 -070086 OptBool: proto.Bool(true),
87 OptInt32: proto.Int32(0xff),
88 OptInt64: proto.Int64(0xdeadbeef),
89 OptUint32: proto.Uint32(47),
90 OptUint64: proto.Uint64(0xdeadbeef),
91 OptSint32: proto.Int32(-1001),
92 OptSint64: proto.Int64(-0xffff),
93 OptFixed64: proto.Uint64(64),
94 OptSfixed32: proto.Int32(-32),
95 OptFloat: proto.Float32(1.02),
96 OptDouble: proto.Float64(1.234),
Herbie Ong87608a72019-03-06 14:32:24 -080097 OptBytes: []byte("谷歌"),
Damien Neila8a2cea2019-07-10 16:17:16 -070098 OptString: proto.String("谷歌"),
Herbie Ong7b828bc2019-02-08 19:56:24 -080099 },
100 want: `{
101 "optBool": true,
102 "optInt32": 255,
103 "optInt64": "3735928559",
104 "optUint32": 47,
105 "optUint64": "3735928559",
106 "optSint32": -1001,
107 "optSint64": "-65535",
108 "optFixed64": "64",
109 "optSfixed32": -32,
110 "optFloat": 1.02,
111 "optDouble": 1.234,
112 "optBytes": "6LC35q2M",
113 "optString": "谷歌"
114}`,
115 }, {
Herbie Ong0b0f4032019-03-18 19:06:15 -0700116 desc: "string",
117 input: &pb3.Scalars{
118 SString: "谷歌",
119 },
120 want: `{
121 "sString": "谷歌"
122}`,
123 }, {
124 desc: "string with invalid UTF8",
125 input: &pb3.Scalars{
126 SString: "abc\xff",
127 },
Herbie Ong0b0f4032019-03-18 19:06:15 -0700128 wantErr: true,
129 }, {
Herbie Ong7b828bc2019-02-08 19:56:24 -0800130 desc: "float nan",
131 input: &pb3.Scalars{
132 SFloat: float32(math.NaN()),
133 },
134 want: `{
135 "sFloat": "NaN"
136}`,
137 }, {
138 desc: "float positive infinity",
139 input: &pb3.Scalars{
140 SFloat: float32(math.Inf(1)),
141 },
142 want: `{
143 "sFloat": "Infinity"
144}`,
145 }, {
146 desc: "float negative infinity",
147 input: &pb3.Scalars{
148 SFloat: float32(math.Inf(-1)),
149 },
150 want: `{
151 "sFloat": "-Infinity"
152}`,
153 }, {
154 desc: "double nan",
155 input: &pb3.Scalars{
156 SDouble: math.NaN(),
157 },
158 want: `{
159 "sDouble": "NaN"
160}`,
161 }, {
162 desc: "double positive infinity",
163 input: &pb3.Scalars{
164 SDouble: math.Inf(1),
165 },
166 want: `{
167 "sDouble": "Infinity"
168}`,
169 }, {
170 desc: "double negative infinity",
171 input: &pb3.Scalars{
172 SDouble: math.Inf(-1),
173 },
174 want: `{
175 "sDouble": "-Infinity"
176}`,
177 }, {
178 desc: "proto2 enum not set",
179 input: &pb2.Enums{},
180 want: "{}",
181 }, {
182 desc: "proto2 enum set to zero value",
183 input: &pb2.Enums{
Joe Tsai6dc168e2019-07-09 23:11:13 -0700184 OptEnum: pb2.Enum(0).Enum(),
185 OptNestedEnum: pb2.Enums_NestedEnum(0).Enum(),
Herbie Ong7b828bc2019-02-08 19:56:24 -0800186 },
187 want: `{
188 "optEnum": 0,
189 "optNestedEnum": 0
190}`,
191 }, {
192 desc: "proto2 enum",
193 input: &pb2.Enums{
194 OptEnum: pb2.Enum_ONE.Enum(),
195 OptNestedEnum: pb2.Enums_UNO.Enum(),
196 },
197 want: `{
198 "optEnum": "ONE",
199 "optNestedEnum": "UNO"
200}`,
201 }, {
202 desc: "proto2 enum set to numeric values",
203 input: &pb2.Enums{
Joe Tsai6dc168e2019-07-09 23:11:13 -0700204 OptEnum: pb2.Enum(2).Enum(),
205 OptNestedEnum: pb2.Enums_NestedEnum(2).Enum(),
Herbie Ong7b828bc2019-02-08 19:56:24 -0800206 },
207 want: `{
208 "optEnum": "TWO",
209 "optNestedEnum": "DOS"
210}`,
211 }, {
212 desc: "proto2 enum set to unnamed numeric values",
213 input: &pb2.Enums{
Joe Tsai6dc168e2019-07-09 23:11:13 -0700214 OptEnum: pb2.Enum(101).Enum(),
215 OptNestedEnum: pb2.Enums_NestedEnum(-101).Enum(),
Herbie Ong7b828bc2019-02-08 19:56:24 -0800216 },
217 want: `{
218 "optEnum": 101,
219 "optNestedEnum": -101
220}`,
221 }, {
222 desc: "proto3 enum not set",
223 input: &pb3.Enums{},
224 want: "{}",
225 }, {
226 desc: "proto3 enum set to zero value",
227 input: &pb3.Enums{
228 SEnum: pb3.Enum_ZERO,
229 SNestedEnum: pb3.Enums_CERO,
230 },
231 want: "{}",
232 }, {
233 desc: "proto3 enum",
234 input: &pb3.Enums{
235 SEnum: pb3.Enum_ONE,
236 SNestedEnum: pb3.Enums_UNO,
237 },
238 want: `{
239 "sEnum": "ONE",
240 "sNestedEnum": "UNO"
241}`,
242 }, {
243 desc: "proto3 enum set to numeric values",
244 input: &pb3.Enums{
245 SEnum: 2,
246 SNestedEnum: 2,
247 },
248 want: `{
249 "sEnum": "TWO",
250 "sNestedEnum": "DOS"
251}`,
252 }, {
253 desc: "proto3 enum set to unnamed numeric values",
254 input: &pb3.Enums{
255 SEnum: -47,
256 SNestedEnum: 47,
257 },
258 want: `{
259 "sEnum": -47,
260 "sNestedEnum": 47
261}`,
262 }, {
263 desc: "proto2 nested message not set",
264 input: &pb2.Nests{},
265 want: "{}",
266 }, {
267 desc: "proto2 nested message set to empty",
268 input: &pb2.Nests{
269 OptNested: &pb2.Nested{},
270 Optgroup: &pb2.Nests_OptGroup{},
271 },
272 want: `{
273 "optNested": {},
274 "optgroup": {}
275}`,
276 }, {
277 desc: "proto2 nested messages",
278 input: &pb2.Nests{
279 OptNested: &pb2.Nested{
Damien Neila8a2cea2019-07-10 16:17:16 -0700280 OptString: proto.String("nested message"),
Herbie Ong7b828bc2019-02-08 19:56:24 -0800281 OptNested: &pb2.Nested{
Damien Neila8a2cea2019-07-10 16:17:16 -0700282 OptString: proto.String("another nested message"),
Herbie Ong7b828bc2019-02-08 19:56:24 -0800283 },
284 },
285 },
286 want: `{
287 "optNested": {
288 "optString": "nested message",
289 "optNested": {
290 "optString": "another nested message"
291 }
292 }
293}`,
294 }, {
295 desc: "proto2 groups",
296 input: &pb2.Nests{
297 Optgroup: &pb2.Nests_OptGroup{
Damien Neila8a2cea2019-07-10 16:17:16 -0700298 OptString: proto.String("inside a group"),
Herbie Ong7b828bc2019-02-08 19:56:24 -0800299 OptNested: &pb2.Nested{
Damien Neila8a2cea2019-07-10 16:17:16 -0700300 OptString: proto.String("nested message inside a group"),
Herbie Ong7b828bc2019-02-08 19:56:24 -0800301 },
302 Optnestedgroup: &pb2.Nests_OptGroup_OptNestedGroup{
Damien Neila8a2cea2019-07-10 16:17:16 -0700303 OptFixed32: proto.Uint32(47),
Herbie Ong7b828bc2019-02-08 19:56:24 -0800304 },
305 },
306 },
307 want: `{
308 "optgroup": {
309 "optString": "inside a group",
310 "optNested": {
311 "optString": "nested message inside a group"
312 },
313 "optnestedgroup": {
314 "optFixed32": 47
315 }
316 }
317}`,
318 }, {
319 desc: "proto3 nested message not set",
320 input: &pb3.Nests{},
321 want: "{}",
322 }, {
323 desc: "proto3 nested message set to empty",
324 input: &pb3.Nests{
325 SNested: &pb3.Nested{},
326 },
327 want: `{
328 "sNested": {}
329}`,
330 }, {
331 desc: "proto3 nested message",
332 input: &pb3.Nests{
333 SNested: &pb3.Nested{
334 SString: "nested message",
335 SNested: &pb3.Nested{
336 SString: "another nested message",
337 },
338 },
339 },
340 want: `{
341 "sNested": {
342 "sString": "nested message",
343 "sNested": {
344 "sString": "another nested message"
345 }
346 }
347}`,
348 }, {
349 desc: "oneof not set",
350 input: &pb3.Oneofs{},
351 want: "{}",
352 }, {
353 desc: "oneof set to empty string",
354 input: &pb3.Oneofs{
355 Union: &pb3.Oneofs_OneofString{},
356 },
357 want: `{
358 "oneofString": ""
359}`,
360 }, {
361 desc: "oneof set to string",
362 input: &pb3.Oneofs{
363 Union: &pb3.Oneofs_OneofString{
364 OneofString: "hello",
365 },
366 },
367 want: `{
368 "oneofString": "hello"
369}`,
370 }, {
371 desc: "oneof set to enum",
372 input: &pb3.Oneofs{
373 Union: &pb3.Oneofs_OneofEnum{
374 OneofEnum: pb3.Enum_ZERO,
375 },
376 },
377 want: `{
378 "oneofEnum": "ZERO"
379}`,
380 }, {
381 desc: "oneof set to empty message",
382 input: &pb3.Oneofs{
383 Union: &pb3.Oneofs_OneofNested{
384 OneofNested: &pb3.Nested{},
385 },
386 },
387 want: `{
388 "oneofNested": {}
389}`,
390 }, {
391 desc: "oneof set to message",
392 input: &pb3.Oneofs{
393 Union: &pb3.Oneofs_OneofNested{
394 OneofNested: &pb3.Nested{
395 SString: "nested message",
396 },
397 },
398 },
399 want: `{
400 "oneofNested": {
401 "sString": "nested message"
402 }
403}`,
404 }, {
405 desc: "repeated fields not set",
406 input: &pb2.Repeats{},
407 want: "{}",
408 }, {
409 desc: "repeated fields set to empty slices",
410 input: &pb2.Repeats{
411 RptBool: []bool{},
412 RptInt32: []int32{},
413 RptInt64: []int64{},
414 RptUint32: []uint32{},
415 RptUint64: []uint64{},
416 RptFloat: []float32{},
417 RptDouble: []float64{},
418 RptBytes: [][]byte{},
419 },
420 want: "{}",
421 }, {
422 desc: "repeated fields set to some values",
423 input: &pb2.Repeats{
424 RptBool: []bool{true, false, true, true},
425 RptInt32: []int32{1, 6, 0, 0},
426 RptInt64: []int64{-64, 47},
427 RptUint32: []uint32{0xff, 0xffff},
428 RptUint64: []uint64{0xdeadbeef},
429 RptFloat: []float32{float32(math.NaN()), float32(math.Inf(1)), float32(math.Inf(-1)), 1.034},
430 RptDouble: []float64{math.NaN(), math.Inf(1), math.Inf(-1), 1.23e-308},
431 RptString: []string{"hello", "世界"},
432 RptBytes: [][]byte{
433 []byte("hello"),
434 []byte("\xe4\xb8\x96\xe7\x95\x8c"),
435 },
436 },
437 want: `{
438 "rptBool": [
439 true,
440 false,
441 true,
442 true
443 ],
444 "rptInt32": [
445 1,
446 6,
447 0,
448 0
449 ],
450 "rptInt64": [
451 "-64",
452 "47"
453 ],
454 "rptUint32": [
455 255,
456 65535
457 ],
458 "rptUint64": [
459 "3735928559"
460 ],
461 "rptFloat": [
462 "NaN",
463 "Infinity",
464 "-Infinity",
465 1.034
466 ],
467 "rptDouble": [
468 "NaN",
469 "Infinity",
470 "-Infinity",
471 1.23e-308
472 ],
473 "rptString": [
474 "hello",
475 "世界"
476 ],
477 "rptBytes": [
478 "aGVsbG8=",
479 "5LiW55WM"
480 ]
481}`,
482 }, {
483 desc: "repeated enums",
484 input: &pb2.Enums{
485 RptEnum: []pb2.Enum{pb2.Enum_ONE, 2, pb2.Enum_TEN, 42},
486 RptNestedEnum: []pb2.Enums_NestedEnum{2, 47, 10},
487 },
488 want: `{
489 "rptEnum": [
490 "ONE",
491 "TWO",
492 "TEN",
493 42
494 ],
495 "rptNestedEnum": [
496 "DOS",
497 47,
498 "DIEZ"
499 ]
500}`,
501 }, {
502 desc: "repeated messages set to empty",
503 input: &pb2.Nests{
504 RptNested: []*pb2.Nested{},
505 Rptgroup: []*pb2.Nests_RptGroup{},
506 },
507 want: "{}",
508 }, {
509 desc: "repeated messages",
510 input: &pb2.Nests{
511 RptNested: []*pb2.Nested{
512 {
Damien Neila8a2cea2019-07-10 16:17:16 -0700513 OptString: proto.String("repeat nested one"),
Herbie Ong7b828bc2019-02-08 19:56:24 -0800514 },
515 {
Damien Neila8a2cea2019-07-10 16:17:16 -0700516 OptString: proto.String("repeat nested two"),
Herbie Ong7b828bc2019-02-08 19:56:24 -0800517 OptNested: &pb2.Nested{
Damien Neila8a2cea2019-07-10 16:17:16 -0700518 OptString: proto.String("inside repeat nested two"),
Herbie Ong7b828bc2019-02-08 19:56:24 -0800519 },
520 },
521 {},
522 },
523 },
524 want: `{
525 "rptNested": [
526 {
527 "optString": "repeat nested one"
528 },
529 {
530 "optString": "repeat nested two",
531 "optNested": {
532 "optString": "inside repeat nested two"
533 }
534 },
535 {}
536 ]
537}`,
538 }, {
539 desc: "repeated messages contains nil value",
540 input: &pb2.Nests{
541 RptNested: []*pb2.Nested{nil, {}},
542 },
543 want: `{
544 "rptNested": [
545 {},
546 {}
547 ]
548}`,
549 }, {
550 desc: "repeated groups",
551 input: &pb2.Nests{
552 Rptgroup: []*pb2.Nests_RptGroup{
553 {
554 RptString: []string{"hello", "world"},
555 },
556 {},
557 nil,
558 },
559 },
560 want: `{
561 "rptgroup": [
562 {
563 "rptString": [
564 "hello",
565 "world"
566 ]
567 },
568 {},
569 {}
570 ]
571}`,
572 }, {
573 desc: "map fields not set",
574 input: &pb3.Maps{},
575 want: "{}",
576 }, {
577 desc: "map fields set to empty",
578 input: &pb3.Maps{
579 Int32ToStr: map[int32]string{},
580 BoolToUint32: map[bool]uint32{},
581 Uint64ToEnum: map[uint64]pb3.Enum{},
582 StrToNested: map[string]*pb3.Nested{},
583 StrToOneofs: map[string]*pb3.Oneofs{},
584 },
585 want: "{}",
586 }, {
587 desc: "map fields 1",
588 input: &pb3.Maps{
589 BoolToUint32: map[bool]uint32{
590 true: 42,
591 false: 101,
592 },
593 },
594 want: `{
595 "boolToUint32": {
596 "false": 101,
597 "true": 42
598 }
599}`,
600 }, {
601 desc: "map fields 2",
602 input: &pb3.Maps{
603 Int32ToStr: map[int32]string{
604 -101: "-101",
605 0xff: "0xff",
606 0: "zero",
607 },
608 },
609 want: `{
610 "int32ToStr": {
611 "-101": "-101",
612 "0": "zero",
613 "255": "0xff"
614 }
615}`,
616 }, {
617 desc: "map fields 3",
618 input: &pb3.Maps{
619 Uint64ToEnum: map[uint64]pb3.Enum{
620 1: pb3.Enum_ONE,
621 2: pb3.Enum_TWO,
622 10: pb3.Enum_TEN,
623 47: 47,
624 },
625 },
626 want: `{
627 "uint64ToEnum": {
628 "1": "ONE",
629 "2": "TWO",
630 "10": "TEN",
631 "47": 47
632 }
633}`,
634 }, {
635 desc: "map fields 4",
636 input: &pb3.Maps{
637 StrToNested: map[string]*pb3.Nested{
638 "nested": &pb3.Nested{
639 SString: "nested in a map",
640 },
641 },
642 },
643 want: `{
644 "strToNested": {
645 "nested": {
646 "sString": "nested in a map"
647 }
648 }
649}`,
650 }, {
651 desc: "map fields 5",
652 input: &pb3.Maps{
653 StrToOneofs: map[string]*pb3.Oneofs{
654 "string": &pb3.Oneofs{
655 Union: &pb3.Oneofs_OneofString{
656 OneofString: "hello",
657 },
658 },
659 "nested": &pb3.Oneofs{
660 Union: &pb3.Oneofs_OneofNested{
661 OneofNested: &pb3.Nested{
662 SString: "nested oneof in map field value",
663 },
664 },
665 },
666 },
667 },
668 want: `{
669 "strToOneofs": {
670 "nested": {
671 "oneofNested": {
672 "sString": "nested oneof in map field value"
673 }
674 },
675 "string": {
676 "oneofString": "hello"
677 }
678 }
679}`,
680 }, {
681 desc: "map field contains nil value",
682 input: &pb3.Maps{
683 StrToNested: map[string]*pb3.Nested{
684 "nil": nil,
685 },
686 },
687 want: `{
688 "strToNested": {
689 "nil": {}
690 }
691}`,
692 }, {
Herbie Ong329be5b2019-03-27 14:47:59 -0700693 desc: "required fields not set",
694 input: &pb2.Requireds{},
695 want: `{}`,
696 wantErr: true,
697 }, {
698 desc: "required fields partially set",
699 input: &pb2.Requireds{
Damien Neila8a2cea2019-07-10 16:17:16 -0700700 ReqBool: proto.Bool(false),
701 ReqSfixed64: proto.Int64(0),
702 ReqDouble: proto.Float64(1.23),
703 ReqString: proto.String("hello"),
Herbie Ong329be5b2019-03-27 14:47:59 -0700704 ReqEnum: pb2.Enum_ONE.Enum(),
705 },
706 want: `{
707 "reqBool": false,
708 "reqSfixed64": "0",
709 "reqDouble": 1.23,
710 "reqString": "hello",
711 "reqEnum": "ONE"
712}`,
713 wantErr: true,
714 }, {
715 desc: "required fields not set with AllowPartial",
Damien Neil5c5b5312019-05-14 12:44:37 -0700716 mo: protojson.MarshalOptions{AllowPartial: true},
Herbie Ong329be5b2019-03-27 14:47:59 -0700717 input: &pb2.Requireds{
Damien Neila8a2cea2019-07-10 16:17:16 -0700718 ReqBool: proto.Bool(false),
719 ReqSfixed64: proto.Int64(0),
720 ReqDouble: proto.Float64(1.23),
721 ReqString: proto.String("hello"),
Herbie Ong329be5b2019-03-27 14:47:59 -0700722 ReqEnum: pb2.Enum_ONE.Enum(),
723 },
724 want: `{
725 "reqBool": false,
726 "reqSfixed64": "0",
727 "reqDouble": 1.23,
728 "reqString": "hello",
729 "reqEnum": "ONE"
730}`,
731 }, {
732 desc: "required fields all set",
733 input: &pb2.Requireds{
Damien Neila8a2cea2019-07-10 16:17:16 -0700734 ReqBool: proto.Bool(false),
735 ReqSfixed64: proto.Int64(0),
736 ReqDouble: proto.Float64(1.23),
737 ReqString: proto.String("hello"),
Herbie Ong329be5b2019-03-27 14:47:59 -0700738 ReqEnum: pb2.Enum_ONE.Enum(),
739 ReqNested: &pb2.Nested{},
740 },
741 want: `{
742 "reqBool": false,
743 "reqSfixed64": "0",
744 "reqDouble": 1.23,
745 "reqString": "hello",
746 "reqEnum": "ONE",
747 "reqNested": {}
748}`,
749 }, {
750 desc: "indirect required field",
751 input: &pb2.IndirectRequired{
752 OptNested: &pb2.NestedWithRequired{},
753 },
754 want: `{
755 "optNested": {}
756}`,
757 wantErr: true,
758 }, {
759 desc: "indirect required field with AllowPartial",
Damien Neil5c5b5312019-05-14 12:44:37 -0700760 mo: protojson.MarshalOptions{AllowPartial: true},
Herbie Ong329be5b2019-03-27 14:47:59 -0700761 input: &pb2.IndirectRequired{
762 OptNested: &pb2.NestedWithRequired{},
763 },
764 want: `{
765 "optNested": {}
766}`,
767 }, {
768 desc: "indirect required field in empty repeated",
769 input: &pb2.IndirectRequired{
770 RptNested: []*pb2.NestedWithRequired{},
771 },
772 want: `{}`,
773 }, {
774 desc: "indirect required field in repeated",
775 input: &pb2.IndirectRequired{
776 RptNested: []*pb2.NestedWithRequired{
777 &pb2.NestedWithRequired{},
778 },
779 },
780 want: `{
781 "rptNested": [
782 {}
783 ]
784}`,
785 wantErr: true,
786 }, {
787 desc: "indirect required field in repeated with AllowPartial",
Damien Neil5c5b5312019-05-14 12:44:37 -0700788 mo: protojson.MarshalOptions{AllowPartial: true},
Herbie Ong329be5b2019-03-27 14:47:59 -0700789 input: &pb2.IndirectRequired{
790 RptNested: []*pb2.NestedWithRequired{
791 &pb2.NestedWithRequired{},
792 },
793 },
794 want: `{
795 "rptNested": [
796 {}
797 ]
798}`,
799 }, {
800 desc: "indirect required field in empty map",
801 input: &pb2.IndirectRequired{
802 StrToNested: map[string]*pb2.NestedWithRequired{},
803 },
804 want: "{}",
805 }, {
806 desc: "indirect required field in map",
807 input: &pb2.IndirectRequired{
808 StrToNested: map[string]*pb2.NestedWithRequired{
809 "fail": &pb2.NestedWithRequired{},
810 },
811 },
812 want: `{
813 "strToNested": {
814 "fail": {}
815 }
816}`,
817 wantErr: true,
818 }, {
819 desc: "indirect required field in map with AllowPartial",
Damien Neil5c5b5312019-05-14 12:44:37 -0700820 mo: protojson.MarshalOptions{AllowPartial: true},
Herbie Ong329be5b2019-03-27 14:47:59 -0700821 input: &pb2.IndirectRequired{
822 StrToNested: map[string]*pb2.NestedWithRequired{
823 "fail": &pb2.NestedWithRequired{},
824 },
825 },
826 want: `{
827 "strToNested": {
828 "fail": {}
829 }
830}`,
831 }, {
832 desc: "indirect required field in oneof",
833 input: &pb2.IndirectRequired{
834 Union: &pb2.IndirectRequired_OneofNested{
835 OneofNested: &pb2.NestedWithRequired{},
836 },
837 },
838 want: `{
839 "oneofNested": {}
840}`,
841 wantErr: true,
842 }, {
843 desc: "indirect required field in oneof with AllowPartial",
Damien Neil5c5b5312019-05-14 12:44:37 -0700844 mo: protojson.MarshalOptions{AllowPartial: true},
Herbie Ong329be5b2019-03-27 14:47:59 -0700845 input: &pb2.IndirectRequired{
846 Union: &pb2.IndirectRequired_OneofNested{
847 OneofNested: &pb2.NestedWithRequired{},
848 },
849 },
850 want: `{
851 "oneofNested": {}
852}`,
853 }, {
Herbie Ong7b828bc2019-02-08 19:56:24 -0800854 desc: "unknown fields are ignored",
Joe Tsai28216c72019-06-22 13:20:09 -0700855 input: func() proto.Message {
856 m := &pb2.Scalars{
Damien Neila8a2cea2019-07-10 16:17:16 -0700857 OptString: proto.String("no unknowns"),
Joe Tsai28216c72019-06-22 13:20:09 -0700858 }
859 m.ProtoReflect().SetUnknown(pack.Message{
Herbie Ong7b828bc2019-02-08 19:56:24 -0800860 pack.Tag{101, pack.BytesType}, pack.String("hello world"),
Joe Tsai28216c72019-06-22 13:20:09 -0700861 }.Marshal())
862 return m
863 }(),
Herbie Ong7b828bc2019-02-08 19:56:24 -0800864 want: `{
865 "optString": "no unknowns"
866}`,
867 }, {
868 desc: "json_name",
869 input: &pb3.JSONNames{
870 SString: "json_name",
871 },
872 want: `{
873 "foo_bar": "json_name"
874}`,
Herbie Ongf83d5bb2019-03-14 00:01:27 -0700875 }, {
876 desc: "extensions of non-repeated fields",
877 input: func() proto.Message {
878 m := &pb2.Extensions{
Damien Neila8a2cea2019-07-10 16:17:16 -0700879 OptString: proto.String("non-extension field"),
880 OptBool: proto.Bool(true),
881 OptInt32: proto.Int32(42),
Herbie Ongf83d5bb2019-03-14 00:01:27 -0700882 }
Damien Neil92f76182019-08-02 16:58:08 -0700883 proto.SetExtension(m, pb2.E_OptExtBool, true)
884 proto.SetExtension(m, pb2.E_OptExtString, "extension field")
885 proto.SetExtension(m, pb2.E_OptExtEnum, pb2.Enum_TEN)
886 proto.SetExtension(m, pb2.E_OptExtNested, &pb2.Nested{
Damien Neila8a2cea2019-07-10 16:17:16 -0700887 OptString: proto.String("nested in an extension"),
Herbie Ongf83d5bb2019-03-14 00:01:27 -0700888 OptNested: &pb2.Nested{
Damien Neila8a2cea2019-07-10 16:17:16 -0700889 OptString: proto.String("another nested in an extension"),
Herbie Ongf83d5bb2019-03-14 00:01:27 -0700890 },
891 })
892 return m
893 }(),
894 want: `{
895 "optString": "non-extension field",
896 "optBool": true,
897 "optInt32": 42,
898 "[pb2.opt_ext_bool]": true,
899 "[pb2.opt_ext_enum]": "TEN",
900 "[pb2.opt_ext_nested]": {
901 "optString": "nested in an extension",
902 "optNested": {
903 "optString": "another nested in an extension"
904 }
905 },
906 "[pb2.opt_ext_string]": "extension field"
907}`,
908 }, {
Herbie Ongf83d5bb2019-03-14 00:01:27 -0700909 desc: "extensions of repeated fields",
910 input: func() proto.Message {
911 m := &pb2.Extensions{}
Damien Neil293dc762019-08-29 11:42:57 -0700912 proto.SetExtension(m, pb2.E_RptExtEnum, []pb2.Enum{pb2.Enum_TEN, 101, pb2.Enum_ONE})
913 proto.SetExtension(m, pb2.E_RptExtFixed32, []uint32{42, 47})
914 proto.SetExtension(m, pb2.E_RptExtNested, []*pb2.Nested{
Damien Neila8a2cea2019-07-10 16:17:16 -0700915 &pb2.Nested{OptString: proto.String("one")},
916 &pb2.Nested{OptString: proto.String("two")},
917 &pb2.Nested{OptString: proto.String("three")},
Herbie Ongf83d5bb2019-03-14 00:01:27 -0700918 })
919 return m
920 }(),
921 want: `{
922 "[pb2.rpt_ext_enum]": [
923 "TEN",
924 101,
925 "ONE"
926 ],
927 "[pb2.rpt_ext_fixed32]": [
928 42,
929 47
930 ],
931 "[pb2.rpt_ext_nested]": [
932 {
933 "optString": "one"
934 },
935 {
936 "optString": "two"
937 },
938 {
939 "optString": "three"
940 }
941 ]
942}`,
943 }, {
944 desc: "extensions of non-repeated fields in another message",
945 input: func() proto.Message {
946 m := &pb2.Extensions{}
Damien Neil92f76182019-08-02 16:58:08 -0700947 proto.SetExtension(m, pb2.E_ExtensionsContainer_OptExtBool, true)
948 proto.SetExtension(m, pb2.E_ExtensionsContainer_OptExtString, "extension field")
949 proto.SetExtension(m, pb2.E_ExtensionsContainer_OptExtEnum, pb2.Enum_TEN)
950 proto.SetExtension(m, pb2.E_ExtensionsContainer_OptExtNested, &pb2.Nested{
Damien Neila8a2cea2019-07-10 16:17:16 -0700951 OptString: proto.String("nested in an extension"),
Herbie Ongf83d5bb2019-03-14 00:01:27 -0700952 OptNested: &pb2.Nested{
Damien Neila8a2cea2019-07-10 16:17:16 -0700953 OptString: proto.String("another nested in an extension"),
Herbie Ongf83d5bb2019-03-14 00:01:27 -0700954 },
955 })
956 return m
957 }(),
958 want: `{
959 "[pb2.ExtensionsContainer.opt_ext_bool]": true,
960 "[pb2.ExtensionsContainer.opt_ext_enum]": "TEN",
961 "[pb2.ExtensionsContainer.opt_ext_nested]": {
962 "optString": "nested in an extension",
963 "optNested": {
964 "optString": "another nested in an extension"
965 }
966 },
967 "[pb2.ExtensionsContainer.opt_ext_string]": "extension field"
968}`,
969 }, {
970 desc: "extensions of repeated fields in another message",
971 input: func() proto.Message {
972 m := &pb2.Extensions{
Damien Neila8a2cea2019-07-10 16:17:16 -0700973 OptString: proto.String("non-extension field"),
974 OptBool: proto.Bool(true),
975 OptInt32: proto.Int32(42),
Herbie Ongf83d5bb2019-03-14 00:01:27 -0700976 }
Damien Neil293dc762019-08-29 11:42:57 -0700977 proto.SetExtension(m, pb2.E_ExtensionsContainer_RptExtEnum, []pb2.Enum{pb2.Enum_TEN, 101, pb2.Enum_ONE})
978 proto.SetExtension(m, pb2.E_ExtensionsContainer_RptExtString, []string{"hello", "world"})
979 proto.SetExtension(m, pb2.E_ExtensionsContainer_RptExtNested, []*pb2.Nested{
Damien Neila8a2cea2019-07-10 16:17:16 -0700980 &pb2.Nested{OptString: proto.String("one")},
981 &pb2.Nested{OptString: proto.String("two")},
982 &pb2.Nested{OptString: proto.String("three")},
Herbie Ongf83d5bb2019-03-14 00:01:27 -0700983 })
984 return m
985 }(),
986 want: `{
987 "optString": "non-extension field",
988 "optBool": true,
989 "optInt32": 42,
990 "[pb2.ExtensionsContainer.rpt_ext_enum]": [
991 "TEN",
992 101,
993 "ONE"
994 ],
995 "[pb2.ExtensionsContainer.rpt_ext_nested]": [
996 {
997 "optString": "one"
998 },
999 {
1000 "optString": "two"
1001 },
1002 {
1003 "optString": "three"
1004 }
1005 ],
1006 "[pb2.ExtensionsContainer.rpt_ext_string]": [
1007 "hello",
1008 "world"
1009 ]
1010}`,
1011 }, {
1012 desc: "MessageSet",
1013 input: func() proto.Message {
1014 m := &pb2.MessageSet{}
Damien Neil92f76182019-08-02 16:58:08 -07001015 proto.SetExtension(m, pb2.E_MessageSetExtension_MessageSetExtension, &pb2.MessageSetExtension{
Damien Neila8a2cea2019-07-10 16:17:16 -07001016 OptString: proto.String("a messageset extension"),
Herbie Ongf83d5bb2019-03-14 00:01:27 -07001017 })
Damien Neil92f76182019-08-02 16:58:08 -07001018 proto.SetExtension(m, pb2.E_MessageSetExtension_NotMessageSetExtension, &pb2.MessageSetExtension{
Damien Neila8a2cea2019-07-10 16:17:16 -07001019 OptString: proto.String("not a messageset extension"),
Herbie Ongf83d5bb2019-03-14 00:01:27 -07001020 })
Damien Neil92f76182019-08-02 16:58:08 -07001021 proto.SetExtension(m, pb2.E_MessageSetExtension_ExtNested, &pb2.Nested{
Damien Neila8a2cea2019-07-10 16:17:16 -07001022 OptString: proto.String("just a regular extension"),
Herbie Ongf83d5bb2019-03-14 00:01:27 -07001023 })
1024 return m
1025 }(),
1026 want: `{
1027 "[pb2.MessageSetExtension]": {
1028 "optString": "a messageset extension"
1029 },
1030 "[pb2.MessageSetExtension.ext_nested]": {
1031 "optString": "just a regular extension"
1032 },
1033 "[pb2.MessageSetExtension.not_message_set_extension]": {
1034 "optString": "not a messageset extension"
1035 }
1036}`,
Joe Tsai1799d112019-08-08 13:31:59 -07001037 skip: !flags.ProtoLegacy,
Herbie Ongf83d5bb2019-03-14 00:01:27 -07001038 }, {
1039 desc: "not real MessageSet 1",
1040 input: func() proto.Message {
1041 m := &pb2.FakeMessageSet{}
Damien Neil92f76182019-08-02 16:58:08 -07001042 proto.SetExtension(m, pb2.E_FakeMessageSetExtension_MessageSetExtension, &pb2.FakeMessageSetExtension{
Damien Neila8a2cea2019-07-10 16:17:16 -07001043 OptString: proto.String("not a messageset extension"),
Herbie Ongf83d5bb2019-03-14 00:01:27 -07001044 })
1045 return m
1046 }(),
1047 want: `{
1048 "[pb2.FakeMessageSetExtension.message_set_extension]": {
1049 "optString": "not a messageset extension"
1050 }
1051}`,
Joe Tsai1799d112019-08-08 13:31:59 -07001052 skip: !flags.ProtoLegacy,
Herbie Ongf83d5bb2019-03-14 00:01:27 -07001053 }, {
1054 desc: "not real MessageSet 2",
1055 input: func() proto.Message {
1056 m := &pb2.MessageSet{}
Damien Neil92f76182019-08-02 16:58:08 -07001057 proto.SetExtension(m, pb2.E_MessageSetExtension, &pb2.FakeMessageSetExtension{
Damien Neila8a2cea2019-07-10 16:17:16 -07001058 OptString: proto.String("another not a messageset extension"),
Herbie Ongf83d5bb2019-03-14 00:01:27 -07001059 })
1060 return m
1061 }(),
1062 want: `{
1063 "[pb2.message_set_extension]": {
1064 "optString": "another not a messageset extension"
1065 }
1066}`,
Joe Tsai1799d112019-08-08 13:31:59 -07001067 skip: !flags.ProtoLegacy,
Herbie Ong0b0f4032019-03-18 19:06:15 -07001068 }, {
1069 desc: "BoolValue empty",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001070 input: &wrapperspb.BoolValue{},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001071 want: `false`,
1072 }, {
1073 desc: "BoolValue",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001074 input: &wrapperspb.BoolValue{Value: true},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001075 want: `true`,
1076 }, {
1077 desc: "Int32Value empty",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001078 input: &wrapperspb.Int32Value{},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001079 want: `0`,
1080 }, {
1081 desc: "Int32Value",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001082 input: &wrapperspb.Int32Value{Value: 42},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001083 want: `42`,
1084 }, {
1085 desc: "Int64Value",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001086 input: &wrapperspb.Int64Value{Value: 42},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001087 want: `"42"`,
1088 }, {
1089 desc: "UInt32Value",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001090 input: &wrapperspb.UInt32Value{Value: 42},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001091 want: `42`,
1092 }, {
1093 desc: "UInt64Value",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001094 input: &wrapperspb.UInt64Value{Value: 42},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001095 want: `"42"`,
1096 }, {
1097 desc: "FloatValue",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001098 input: &wrapperspb.FloatValue{Value: 1.02},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001099 want: `1.02`,
1100 }, {
Herbie Onge63c4c42019-03-22 22:20:22 -07001101 desc: "FloatValue Infinity",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001102 input: &wrapperspb.FloatValue{Value: float32(math.Inf(-1))},
Herbie Onge63c4c42019-03-22 22:20:22 -07001103 want: `"-Infinity"`,
1104 }, {
Herbie Ong0b0f4032019-03-18 19:06:15 -07001105 desc: "DoubleValue",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001106 input: &wrapperspb.DoubleValue{Value: 1.02},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001107 want: `1.02`,
1108 }, {
Herbie Onge63c4c42019-03-22 22:20:22 -07001109 desc: "DoubleValue NaN",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001110 input: &wrapperspb.DoubleValue{Value: math.NaN()},
Herbie Onge63c4c42019-03-22 22:20:22 -07001111 want: `"NaN"`,
1112 }, {
Herbie Ong0b0f4032019-03-18 19:06:15 -07001113 desc: "StringValue empty",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001114 input: &wrapperspb.StringValue{},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001115 want: `""`,
1116 }, {
1117 desc: "StringValue",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001118 input: &wrapperspb.StringValue{Value: "谷歌"},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001119 want: `"谷歌"`,
1120 }, {
1121 desc: "StringValue with invalid UTF8 error",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001122 input: &wrapperspb.StringValue{Value: "abc\xff"},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001123 wantErr: true,
1124 }, {
1125 desc: "StringValue field with invalid UTF8 error",
1126 input: &pb2.KnownTypes{
Joe Tsaia95b29f2019-05-16 12:47:20 -07001127 OptString: &wrapperspb.StringValue{Value: "abc\xff"},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001128 },
Herbie Ong0b0f4032019-03-18 19:06:15 -07001129 wantErr: true,
1130 }, {
1131 desc: "BytesValue",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001132 input: &wrapperspb.BytesValue{Value: []byte("hello")},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001133 want: `"aGVsbG8="`,
1134 }, {
1135 desc: "Empty",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001136 input: &emptypb.Empty{},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001137 want: `{}`,
1138 }, {
Herbie Ong300b9fe2019-03-29 15:42:20 -07001139 desc: "NullValue field",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001140 input: &pb2.KnownTypes{OptNull: new(structpb.NullValue)},
Herbie Ong300b9fe2019-03-29 15:42:20 -07001141 want: `{
1142 "optNull": null
1143}`,
1144 }, {
Herbie Ong1c7462c2019-03-22 17:56:55 -07001145 desc: "Value empty",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001146 input: &structpb.Value{},
Herbie Ong1c7462c2019-03-22 17:56:55 -07001147 wantErr: true,
Herbie Ong0b0f4032019-03-18 19:06:15 -07001148 }, {
1149 desc: "Value empty field",
1150 input: &pb2.KnownTypes{
Joe Tsaia95b29f2019-05-16 12:47:20 -07001151 OptValue: &structpb.Value{},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001152 },
Herbie Ong1c7462c2019-03-22 17:56:55 -07001153 wantErr: true,
Herbie Ong0b0f4032019-03-18 19:06:15 -07001154 }, {
1155 desc: "Value contains NullValue",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001156 input: &structpb.Value{Kind: &structpb.Value_NullValue{}},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001157 want: `null`,
1158 }, {
1159 desc: "Value contains BoolValue",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001160 input: &structpb.Value{Kind: &structpb.Value_BoolValue{}},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001161 want: `false`,
1162 }, {
1163 desc: "Value contains NumberValue",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001164 input: &structpb.Value{Kind: &structpb.Value_NumberValue{1.02}},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001165 want: `1.02`,
1166 }, {
1167 desc: "Value contains StringValue",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001168 input: &structpb.Value{Kind: &structpb.Value_StringValue{"hello"}},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001169 want: `"hello"`,
1170 }, {
1171 desc: "Value contains StringValue with invalid UTF8",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001172 input: &structpb.Value{Kind: &structpb.Value_StringValue{"\xff"}},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001173 wantErr: true,
1174 }, {
1175 desc: "Value contains Struct",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001176 input: &structpb.Value{
1177 Kind: &structpb.Value_StructValue{
1178 &structpb.Struct{
1179 Fields: map[string]*structpb.Value{
1180 "null": {Kind: &structpb.Value_NullValue{}},
1181 "number": {Kind: &structpb.Value_NumberValue{}},
1182 "string": {Kind: &structpb.Value_StringValue{}},
1183 "struct": {Kind: &structpb.Value_StructValue{}},
1184 "list": {Kind: &structpb.Value_ListValue{}},
1185 "bool": {Kind: &structpb.Value_BoolValue{}},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001186 },
1187 },
1188 },
1189 },
1190 want: `{
1191 "bool": false,
1192 "list": [],
1193 "null": null,
1194 "number": 0,
1195 "string": "",
1196 "struct": {}
1197}`,
1198 }, {
1199 desc: "Value contains ListValue",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001200 input: &structpb.Value{
1201 Kind: &structpb.Value_ListValue{
1202 &structpb.ListValue{
1203 Values: []*structpb.Value{
1204 {Kind: &structpb.Value_BoolValue{}},
1205 {Kind: &structpb.Value_NullValue{}},
1206 {Kind: &structpb.Value_NumberValue{}},
1207 {Kind: &structpb.Value_StringValue{}},
1208 {Kind: &structpb.Value_StructValue{}},
1209 {Kind: &structpb.Value_ListValue{}},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001210 },
1211 },
1212 },
1213 },
1214 want: `[
1215 false,
1216 null,
1217 0,
1218 "",
1219 {},
1220 []
1221]`,
1222 }, {
1223 desc: "Struct with nil map",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001224 input: &structpb.Struct{},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001225 want: `{}`,
1226 }, {
1227 desc: "Struct with empty map",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001228 input: &structpb.Struct{
1229 Fields: map[string]*structpb.Value{},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001230 },
1231 want: `{}`,
1232 }, {
1233 desc: "Struct",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001234 input: &structpb.Struct{
1235 Fields: map[string]*structpb.Value{
1236 "bool": {Kind: &structpb.Value_BoolValue{true}},
1237 "null": {Kind: &structpb.Value_NullValue{}},
1238 "number": {Kind: &structpb.Value_NumberValue{3.1415}},
1239 "string": {Kind: &structpb.Value_StringValue{"hello"}},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001240 "struct": {
Joe Tsaia95b29f2019-05-16 12:47:20 -07001241 Kind: &structpb.Value_StructValue{
1242 &structpb.Struct{
1243 Fields: map[string]*structpb.Value{
1244 "string": {Kind: &structpb.Value_StringValue{"world"}},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001245 },
1246 },
1247 },
1248 },
1249 "list": {
Joe Tsaia95b29f2019-05-16 12:47:20 -07001250 Kind: &structpb.Value_ListValue{
1251 &structpb.ListValue{
1252 Values: []*structpb.Value{
1253 {Kind: &structpb.Value_BoolValue{}},
1254 {Kind: &structpb.Value_NullValue{}},
1255 {Kind: &structpb.Value_NumberValue{}},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001256 },
1257 },
1258 },
1259 },
1260 },
1261 },
1262 want: `{
1263 "bool": true,
1264 "list": [
1265 false,
1266 null,
1267 0
1268 ],
1269 "null": null,
1270 "number": 3.1415,
1271 "string": "hello",
1272 "struct": {
1273 "string": "world"
1274 }
1275}`,
1276 }, {
1277 desc: "Struct message with invalid UTF8 string",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001278 input: &structpb.Struct{
1279 Fields: map[string]*structpb.Value{
1280 "string": {Kind: &structpb.Value_StringValue{"\xff"}},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001281 },
1282 },
Herbie Ong0b0f4032019-03-18 19:06:15 -07001283 wantErr: true,
1284 }, {
1285 desc: "ListValue with nil values",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001286 input: &structpb.ListValue{},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001287 want: `[]`,
1288 }, {
1289 desc: "ListValue with empty values",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001290 input: &structpb.ListValue{
1291 Values: []*structpb.Value{},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001292 },
1293 want: `[]`,
1294 }, {
1295 desc: "ListValue",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001296 input: &structpb.ListValue{
1297 Values: []*structpb.Value{
1298 {Kind: &structpb.Value_BoolValue{true}},
1299 {Kind: &structpb.Value_NullValue{}},
1300 {Kind: &structpb.Value_NumberValue{3.1415}},
1301 {Kind: &structpb.Value_StringValue{"hello"}},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001302 {
Joe Tsaia95b29f2019-05-16 12:47:20 -07001303 Kind: &structpb.Value_ListValue{
1304 &structpb.ListValue{
1305 Values: []*structpb.Value{
1306 {Kind: &structpb.Value_BoolValue{}},
1307 {Kind: &structpb.Value_NullValue{}},
1308 {Kind: &structpb.Value_NumberValue{}},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001309 },
1310 },
1311 },
1312 },
1313 {
Joe Tsaia95b29f2019-05-16 12:47:20 -07001314 Kind: &structpb.Value_StructValue{
1315 &structpb.Struct{
1316 Fields: map[string]*structpb.Value{
1317 "string": {Kind: &structpb.Value_StringValue{"world"}},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001318 },
1319 },
1320 },
1321 },
1322 },
1323 },
1324 want: `[
1325 true,
1326 null,
1327 3.1415,
1328 "hello",
1329 [
1330 false,
1331 null,
1332 0
1333 ],
1334 {
1335 "string": "world"
1336 }
1337]`,
1338 }, {
1339 desc: "ListValue with invalid UTF8 string",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001340 input: &structpb.ListValue{
1341 Values: []*structpb.Value{
1342 {Kind: &structpb.Value_StringValue{"\xff"}},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001343 },
1344 },
Herbie Ong0b0f4032019-03-18 19:06:15 -07001345 wantErr: true,
1346 }, {
1347 desc: "Duration empty",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001348 input: &durationpb.Duration{},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001349 want: `"0s"`,
1350 }, {
1351 desc: "Duration with secs",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001352 input: &durationpb.Duration{Seconds: 3},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001353 want: `"3s"`,
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 nanos",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001360 input: &durationpb.Duration{Nanos: 1e6},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001361 want: `"0.001s"`,
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 large secs",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001368 input: &durationpb.Duration{Seconds: 1e10, Nanos: 1},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001369 want: `"10000000000.000000001s"`,
1370 }, {
1371 desc: "Duration with 6-digit nanos",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001372 input: &durationpb.Duration{Nanos: 1e4},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001373 want: `"0.000010s"`,
1374 }, {
1375 desc: "Duration with 3-digit nanos",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001376 input: &durationpb.Duration{Nanos: 1e6},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001377 want: `"0.001s"`,
1378 }, {
1379 desc: "Duration with -secs -nanos",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001380 input: &durationpb.Duration{Seconds: -123, Nanos: -450},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001381 want: `"-123.000000450s"`,
1382 }, {
Herbie Ongad9c1252019-04-24 20:51:28 -07001383 desc: "Duration max value",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001384 input: &durationpb.Duration{Seconds: 315576000000, Nanos: 999999999},
Herbie Ongad9c1252019-04-24 20:51:28 -07001385 want: `"315576000000.999999999s"`,
1386 }, {
1387 desc: "Duration min 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 }, {
Herbie Ong0b0f4032019-03-18 19:06:15 -07001391 desc: "Duration with +secs -nanos",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001392 input: &durationpb.Duration{Seconds: 1, Nanos: -1},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001393 wantErr: true,
1394 }, {
1395 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 out of range",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001400 input: &durationpb.Duration{Seconds: 315576000001},
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 +nanos out of range",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001408 input: &durationpb.Duration{Seconds: 0, Nanos: 1e9},
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: "Timestamp zero",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001416 input: &timestamppb.Timestamp{},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001417 want: `"1970-01-01T00:00:00Z"`,
1418 }, {
1419 desc: "Timestamp",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001420 input: &timestamppb.Timestamp{Seconds: 1553036601},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001421 want: `"2019-03-19T23:03:21Z"`,
1422 }, {
1423 desc: "Timestamp with nanos",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001424 input: &timestamppb.Timestamp{Seconds: 1553036601, Nanos: 1},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001425 want: `"2019-03-19T23:03:21.000000001Z"`,
1426 }, {
1427 desc: "Timestamp with 6-digit nanos",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001428 input: &timestamppb.Timestamp{Nanos: 1e3},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001429 want: `"1970-01-01T00:00:00.000001Z"`,
1430 }, {
1431 desc: "Timestamp with 3-digit nanos",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001432 input: &timestamppb.Timestamp{Nanos: 1e7},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001433 want: `"1970-01-01T00:00:00.010Z"`,
1434 }, {
Herbie Ongad9c1252019-04-24 20:51:28 -07001435 desc: "Timestamp max value",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001436 input: &timestamppb.Timestamp{Seconds: 253402300799, Nanos: 999999999},
Herbie Ongad9c1252019-04-24 20:51:28 -07001437 want: `"9999-12-31T23:59:59.999999999Z"`,
1438 }, {
1439 desc: "Timestamp min value",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001440 input: &timestamppb.Timestamp{Seconds: -62135596800},
Herbie Ongad9c1252019-04-24 20:51:28 -07001441 want: `"0001-01-01T00:00:00Z"`,
1442 }, {
Herbie Ong0b0f4032019-03-18 19:06:15 -07001443 desc: "Timestamp with +secs out of range",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001444 input: &timestamppb.Timestamp{Seconds: 253402300800},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001445 wantErr: true,
1446 }, {
1447 desc: "Timestamp with -secs out of range",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001448 input: &timestamppb.Timestamp{Seconds: -62135596801},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001449 wantErr: true,
1450 }, {
1451 desc: "Timestamp with -nanos",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001452 input: &timestamppb.Timestamp{Nanos: -1},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001453 wantErr: true,
1454 }, {
1455 desc: "Timestamp with +nanos out of range",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001456 input: &timestamppb.Timestamp{Nanos: 1e9},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001457 wantErr: true,
1458 }, {
1459 desc: "FieldMask empty",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001460 input: &fieldmaskpb.FieldMask{},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001461 want: `""`,
1462 }, {
1463 desc: "FieldMask",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001464 input: &fieldmaskpb.FieldMask{
Herbie Ong0b0f4032019-03-18 19:06:15 -07001465 Paths: []string{
1466 "foo",
1467 "foo_bar",
1468 "foo.bar_qux",
1469 "_foo",
1470 },
1471 },
1472 want: `"foo,fooBar,foo.barQux,Foo"`,
1473 }, {
1474 desc: "FieldMask error 1",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001475 input: &fieldmaskpb.FieldMask{
Herbie Ong0b0f4032019-03-18 19:06:15 -07001476 Paths: []string{"foo_"},
1477 },
1478 wantErr: true,
1479 }, {
1480 desc: "FieldMask error 2",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001481 input: &fieldmaskpb.FieldMask{
Herbie Ong0b0f4032019-03-18 19:06:15 -07001482 Paths: []string{"foo__bar"},
1483 },
1484 wantErr: true,
1485 }, {
1486 desc: "Any empty",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001487 input: &anypb.Any{},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001488 want: `{}`,
1489 }, {
Herbie Ong8ac9dd22019-03-27 12:20:50 -07001490 desc: "Any with non-custom message",
Damien Neil5c5b5312019-05-14 12:44:37 -07001491 mo: protojson.MarshalOptions{
Joe Tsai0fc49f82019-05-01 12:29:25 -07001492 Resolver: preg.NewTypes(pimpl.Export{}.MessageTypeOf(&pb2.Nested{})),
Herbie Ong0b0f4032019-03-18 19:06:15 -07001493 },
1494 input: func() proto.Message {
1495 m := &pb2.Nested{
Damien Neila8a2cea2019-07-10 16:17:16 -07001496 OptString: proto.String("embedded inside Any"),
Herbie Ong0b0f4032019-03-18 19:06:15 -07001497 OptNested: &pb2.Nested{
Damien Neila8a2cea2019-07-10 16:17:16 -07001498 OptString: proto.String("inception"),
Herbie Ong0b0f4032019-03-18 19:06:15 -07001499 },
1500 }
1501 b, err := proto.MarshalOptions{Deterministic: true}.Marshal(m)
1502 if err != nil {
1503 t.Fatalf("error in binary marshaling message for Any.value: %v", err)
1504 }
Joe Tsaia95b29f2019-05-16 12:47:20 -07001505 return &anypb.Any{
Herbie Ong0b0f4032019-03-18 19:06:15 -07001506 TypeUrl: "foo/pb2.Nested",
1507 Value: b,
1508 }
1509 }(),
1510 want: `{
1511 "@type": "foo/pb2.Nested",
1512 "optString": "embedded inside Any",
1513 "optNested": {
1514 "optString": "inception"
1515 }
1516}`,
1517 }, {
Herbie Ong8ac9dd22019-03-27 12:20:50 -07001518 desc: "Any with empty embedded message",
Damien Neil5c5b5312019-05-14 12:44:37 -07001519 mo: protojson.MarshalOptions{
Joe Tsai0fc49f82019-05-01 12:29:25 -07001520 Resolver: preg.NewTypes(pimpl.Export{}.MessageTypeOf(&pb2.Nested{})),
Herbie Ong0b0f4032019-03-18 19:06:15 -07001521 },
Joe Tsaia95b29f2019-05-16 12:47:20 -07001522 input: &anypb.Any{TypeUrl: "foo/pb2.Nested"},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001523 want: `{
1524 "@type": "foo/pb2.Nested"
1525}`,
1526 }, {
Herbie Ong8ac9dd22019-03-27 12:20:50 -07001527 desc: "Any without registered type",
Damien Neil5c5b5312019-05-14 12:44:37 -07001528 mo: protojson.MarshalOptions{Resolver: preg.NewTypes()},
Joe Tsaia95b29f2019-05-16 12:47:20 -07001529 input: &anypb.Any{TypeUrl: "foo/pb2.Nested"},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001530 wantErr: true,
1531 }, {
Damien Neil0c9f0a92019-06-19 10:41:09 -07001532 desc: "Any with missing required",
Damien Neil5c5b5312019-05-14 12:44:37 -07001533 mo: protojson.MarshalOptions{
Joe Tsai0fc49f82019-05-01 12:29:25 -07001534 Resolver: preg.NewTypes(pimpl.Export{}.MessageTypeOf(&pb2.PartialRequired{})),
Herbie Ong0b0f4032019-03-18 19:06:15 -07001535 },
1536 input: func() proto.Message {
1537 m := &pb2.PartialRequired{
Damien Neila8a2cea2019-07-10 16:17:16 -07001538 OptString: proto.String("embedded inside Any"),
Herbie Ong0b0f4032019-03-18 19:06:15 -07001539 }
Damien Neil96c229a2019-04-03 12:17:24 -07001540 b, err := proto.MarshalOptions{
1541 AllowPartial: true,
1542 Deterministic: true,
1543 }.Marshal(m)
Herbie Ong0b0f4032019-03-18 19:06:15 -07001544 if err != nil {
1545 t.Fatalf("error in binary marshaling message for Any.value: %v", err)
1546 }
Joe Tsaia95b29f2019-05-16 12:47:20 -07001547 return &anypb.Any{
Joe Tsai0fc49f82019-05-01 12:29:25 -07001548 TypeUrl: string(m.ProtoReflect().Descriptor().FullName()),
Herbie Ong0b0f4032019-03-18 19:06:15 -07001549 Value: b,
1550 }
1551 }(),
1552 want: `{
1553 "@type": "pb2.PartialRequired",
1554 "optString": "embedded inside Any"
1555}`,
Herbie Ong0b0f4032019-03-18 19:06:15 -07001556 }, {
Herbie Ong8ac9dd22019-03-27 12:20:50 -07001557 desc: "Any with partial required and AllowPartial",
Damien Neil5c5b5312019-05-14 12:44:37 -07001558 mo: protojson.MarshalOptions{
Herbie Ong8ac9dd22019-03-27 12:20:50 -07001559 AllowPartial: true,
Joe Tsai0fc49f82019-05-01 12:29:25 -07001560 Resolver: preg.NewTypes(pimpl.Export{}.MessageTypeOf(&pb2.PartialRequired{})),
Herbie Ong8ac9dd22019-03-27 12:20:50 -07001561 },
1562 input: func() proto.Message {
1563 m := &pb2.PartialRequired{
Damien Neila8a2cea2019-07-10 16:17:16 -07001564 OptString: proto.String("embedded inside Any"),
Herbie Ong8ac9dd22019-03-27 12:20:50 -07001565 }
Damien Neil96c229a2019-04-03 12:17:24 -07001566 b, err := proto.MarshalOptions{
1567 AllowPartial: true,
1568 Deterministic: true,
1569 }.Marshal(m)
Herbie Ong8ac9dd22019-03-27 12:20:50 -07001570 if err != nil {
1571 t.Fatalf("error in binary marshaling message for Any.value: %v", err)
1572 }
Joe Tsaia95b29f2019-05-16 12:47:20 -07001573 return &anypb.Any{
Joe Tsai0fc49f82019-05-01 12:29:25 -07001574 TypeUrl: string(m.ProtoReflect().Descriptor().FullName()),
Herbie Ong8ac9dd22019-03-27 12:20:50 -07001575 Value: b,
1576 }
1577 }(),
1578 want: `{
1579 "@type": "pb2.PartialRequired",
1580 "optString": "embedded inside Any"
1581}`,
1582 }, {
Herbie Ong0b0f4032019-03-18 19:06:15 -07001583 desc: "Any with invalid UTF8",
Damien Neil5c5b5312019-05-14 12:44:37 -07001584 mo: protojson.MarshalOptions{
Joe Tsai0fc49f82019-05-01 12:29:25 -07001585 Resolver: preg.NewTypes(pimpl.Export{}.MessageTypeOf(&pb2.Nested{})),
Herbie Ong0b0f4032019-03-18 19:06:15 -07001586 },
1587 input: func() proto.Message {
1588 m := &pb2.Nested{
Damien Neila8a2cea2019-07-10 16:17:16 -07001589 OptString: proto.String("abc\xff"),
Herbie Ong0b0f4032019-03-18 19:06:15 -07001590 }
1591 b, err := proto.MarshalOptions{Deterministic: true}.Marshal(m)
1592 if err != nil {
1593 t.Fatalf("error in binary marshaling message for Any.value: %v", err)
1594 }
Joe Tsaia95b29f2019-05-16 12:47:20 -07001595 return &anypb.Any{
Herbie Ong0b0f4032019-03-18 19:06:15 -07001596 TypeUrl: "foo/pb2.Nested",
1597 Value: b,
1598 }
1599 }(),
Herbie Ong0b0f4032019-03-18 19:06:15 -07001600 wantErr: true,
1601 }, {
1602 desc: "Any with invalid value",
Damien Neil5c5b5312019-05-14 12:44:37 -07001603 mo: protojson.MarshalOptions{
Joe Tsai0fc49f82019-05-01 12:29:25 -07001604 Resolver: preg.NewTypes(pimpl.Export{}.MessageTypeOf(&pb2.Nested{})),
Herbie Ong0b0f4032019-03-18 19:06:15 -07001605 },
Joe Tsaia95b29f2019-05-16 12:47:20 -07001606 input: &anypb.Any{
Herbie Ong0b0f4032019-03-18 19:06:15 -07001607 TypeUrl: "foo/pb2.Nested",
Joe Tsai6dc168e2019-07-09 23:11:13 -07001608 Value: []byte("\x80"),
Herbie Ong0b0f4032019-03-18 19:06:15 -07001609 },
1610 wantErr: true,
1611 }, {
1612 desc: "Any with BoolValue",
Damien Neil5c5b5312019-05-14 12:44:37 -07001613 mo: protojson.MarshalOptions{
Joe Tsaia95b29f2019-05-16 12:47:20 -07001614 Resolver: preg.NewTypes(pimpl.Export{}.MessageTypeOf(&wrapperspb.BoolValue{})),
Herbie Ong0b0f4032019-03-18 19:06:15 -07001615 },
1616 input: func() proto.Message {
Joe Tsaia95b29f2019-05-16 12:47:20 -07001617 m := &wrapperspb.BoolValue{Value: true}
Herbie Ong0b0f4032019-03-18 19:06:15 -07001618 b, err := proto.MarshalOptions{Deterministic: true}.Marshal(m)
1619 if err != nil {
1620 t.Fatalf("error in binary marshaling message for Any.value: %v", err)
1621 }
Joe Tsaia95b29f2019-05-16 12:47:20 -07001622 return &anypb.Any{
Herbie Ong0b0f4032019-03-18 19:06:15 -07001623 TypeUrl: "type.googleapis.com/google.protobuf.BoolValue",
1624 Value: b,
1625 }
1626 }(),
1627 want: `{
1628 "@type": "type.googleapis.com/google.protobuf.BoolValue",
1629 "value": true
1630}`,
1631 }, {
Herbie Ong0b0f4032019-03-18 19:06:15 -07001632 desc: "Any with Empty",
Damien Neil5c5b5312019-05-14 12:44:37 -07001633 mo: protojson.MarshalOptions{
Joe Tsaia95b29f2019-05-16 12:47:20 -07001634 Resolver: preg.NewTypes(pimpl.Export{}.MessageTypeOf(&emptypb.Empty{})),
Herbie Ong0b0f4032019-03-18 19:06:15 -07001635 },
1636 input: func() proto.Message {
Joe Tsaia95b29f2019-05-16 12:47:20 -07001637 m := &emptypb.Empty{}
Herbie Ong0b0f4032019-03-18 19:06:15 -07001638 b, err := proto.MarshalOptions{Deterministic: true}.Marshal(m)
1639 if err != nil {
1640 t.Fatalf("error in binary marshaling message for Any.value: %v", err)
1641 }
Joe Tsaia95b29f2019-05-16 12:47:20 -07001642 return &anypb.Any{
Herbie Ong0b0f4032019-03-18 19:06:15 -07001643 TypeUrl: "type.googleapis.com/google.protobuf.Empty",
1644 Value: b,
1645 }
1646 }(),
1647 want: `{
Herbie Ong82014a52019-03-27 15:21:43 -07001648 "@type": "type.googleapis.com/google.protobuf.Empty",
1649 "value": {}
Herbie Ong0b0f4032019-03-18 19:06:15 -07001650}`,
1651 }, {
1652 desc: "Any with StringValue containing invalid UTF8",
Damien Neil5c5b5312019-05-14 12:44:37 -07001653 mo: protojson.MarshalOptions{
Joe Tsaia95b29f2019-05-16 12:47:20 -07001654 Resolver: preg.NewTypes(pimpl.Export{}.MessageTypeOf(&wrapperspb.StringValue{})),
Herbie Ong0b0f4032019-03-18 19:06:15 -07001655 },
1656 input: func() proto.Message {
Joe Tsaia95b29f2019-05-16 12:47:20 -07001657 m := &wrapperspb.StringValue{Value: "abcd"}
Herbie Ong0b0f4032019-03-18 19:06:15 -07001658 b, err := proto.MarshalOptions{Deterministic: true}.Marshal(m)
1659 if err != nil {
1660 t.Fatalf("error in binary marshaling message for Any.value: %v", err)
1661 }
Joe Tsaia95b29f2019-05-16 12:47:20 -07001662 return &anypb.Any{
Herbie Ong0b0f4032019-03-18 19:06:15 -07001663 TypeUrl: "google.protobuf.StringValue",
Damien Neilbc310b52019-04-11 11:46:55 -07001664 Value: bytes.Replace(b, []byte("abcd"), []byte("abc\xff"), -1),
Herbie Ong0b0f4032019-03-18 19:06:15 -07001665 }
1666 }(),
Herbie Ong0b0f4032019-03-18 19:06:15 -07001667 wantErr: true,
1668 }, {
Herbie Ong8ac9dd22019-03-27 12:20:50 -07001669 desc: "Any with Int64Value",
Damien Neil5c5b5312019-05-14 12:44:37 -07001670 mo: protojson.MarshalOptions{
Joe Tsaia95b29f2019-05-16 12:47:20 -07001671 Resolver: preg.NewTypes(pimpl.Export{}.MessageTypeOf(&wrapperspb.Int64Value{})),
Herbie Ong8ac9dd22019-03-27 12:20:50 -07001672 },
1673 input: func() proto.Message {
Joe Tsaia95b29f2019-05-16 12:47:20 -07001674 m := &wrapperspb.Int64Value{Value: 42}
Herbie Ong8ac9dd22019-03-27 12:20:50 -07001675 b, err := proto.MarshalOptions{Deterministic: true}.Marshal(m)
1676 if err != nil {
1677 t.Fatalf("error in binary marshaling message for Any.value: %v", err)
1678 }
Joe Tsaia95b29f2019-05-16 12:47:20 -07001679 return &anypb.Any{
Herbie Ong8ac9dd22019-03-27 12:20:50 -07001680 TypeUrl: "google.protobuf.Int64Value",
1681 Value: b,
1682 }
1683 }(),
1684 want: `{
1685 "@type": "google.protobuf.Int64Value",
1686 "value": "42"
1687}`,
1688 }, {
1689 desc: "Any with Duration",
Damien Neil5c5b5312019-05-14 12:44:37 -07001690 mo: protojson.MarshalOptions{
Joe Tsaia95b29f2019-05-16 12:47:20 -07001691 Resolver: preg.NewTypes(pimpl.Export{}.MessageTypeOf(&durationpb.Duration{})),
Herbie Ong8ac9dd22019-03-27 12:20:50 -07001692 },
1693 input: func() proto.Message {
Joe Tsaia95b29f2019-05-16 12:47:20 -07001694 m := &durationpb.Duration{}
Herbie Ong8ac9dd22019-03-27 12:20:50 -07001695 b, err := proto.MarshalOptions{Deterministic: true}.Marshal(m)
1696 if err != nil {
1697 t.Fatalf("error in binary marshaling message for Any.value: %v", err)
1698 }
Joe Tsaia95b29f2019-05-16 12:47:20 -07001699 return &anypb.Any{
Herbie Ong8ac9dd22019-03-27 12:20:50 -07001700 TypeUrl: "type.googleapis.com/google.protobuf.Duration",
1701 Value: b,
1702 }
1703 }(),
1704 want: `{
1705 "@type": "type.googleapis.com/google.protobuf.Duration",
1706 "value": "0s"
1707}`,
1708 }, {
1709 desc: "Any with empty Value",
Damien Neil5c5b5312019-05-14 12:44:37 -07001710 mo: protojson.MarshalOptions{
Joe Tsaia95b29f2019-05-16 12:47:20 -07001711 Resolver: preg.NewTypes(pimpl.Export{}.MessageTypeOf(&structpb.Value{})),
Herbie Ong8ac9dd22019-03-27 12:20:50 -07001712 },
1713 input: func() proto.Message {
Joe Tsaia95b29f2019-05-16 12:47:20 -07001714 m := &structpb.Value{}
Herbie Ong8ac9dd22019-03-27 12:20:50 -07001715 b, err := proto.Marshal(m)
1716 if err != nil {
1717 t.Fatalf("error in binary marshaling message for Any.value: %v", err)
1718 }
Joe Tsaia95b29f2019-05-16 12:47:20 -07001719 return &anypb.Any{
Herbie Ong8ac9dd22019-03-27 12:20:50 -07001720 TypeUrl: "type.googleapis.com/google.protobuf.Value",
1721 Value: b,
1722 }
1723 }(),
1724 wantErr: true,
1725 }, {
Herbie Ong0b0f4032019-03-18 19:06:15 -07001726 desc: "Any with Value of StringValue",
Damien Neil5c5b5312019-05-14 12:44:37 -07001727 mo: protojson.MarshalOptions{
Joe Tsaia95b29f2019-05-16 12:47:20 -07001728 Resolver: preg.NewTypes(pimpl.Export{}.MessageTypeOf(&structpb.Value{})),
Herbie Ong0b0f4032019-03-18 19:06:15 -07001729 },
1730 input: func() proto.Message {
Joe Tsaia95b29f2019-05-16 12:47:20 -07001731 m := &structpb.Value{Kind: &structpb.Value_StringValue{"abcd"}}
Herbie Ong0b0f4032019-03-18 19:06:15 -07001732 b, err := proto.MarshalOptions{Deterministic: true}.Marshal(m)
1733 if err != nil {
1734 t.Fatalf("error in binary marshaling message for Any.value: %v", err)
1735 }
Joe Tsaia95b29f2019-05-16 12:47:20 -07001736 return &anypb.Any{
Herbie Ong0b0f4032019-03-18 19:06:15 -07001737 TypeUrl: "type.googleapis.com/google.protobuf.Value",
Damien Neilbc310b52019-04-11 11:46:55 -07001738 Value: bytes.Replace(b, []byte("abcd"), []byte("abc\xff"), -1),
Herbie Ong0b0f4032019-03-18 19:06:15 -07001739 }
1740 }(),
Herbie Ong0b0f4032019-03-18 19:06:15 -07001741 wantErr: true,
1742 }, {
Herbie Ong8ac9dd22019-03-27 12:20:50 -07001743 desc: "Any with Value of NullValue",
Damien Neil5c5b5312019-05-14 12:44:37 -07001744 mo: protojson.MarshalOptions{
Joe Tsaia95b29f2019-05-16 12:47:20 -07001745 Resolver: preg.NewTypes(pimpl.Export{}.MessageTypeOf(&structpb.Value{})),
Herbie Ong0b0f4032019-03-18 19:06:15 -07001746 },
1747 input: func() proto.Message {
Joe Tsaia95b29f2019-05-16 12:47:20 -07001748 m := &structpb.Value{Kind: &structpb.Value_NullValue{}}
Herbie Ong8ac9dd22019-03-27 12:20:50 -07001749 b, err := proto.MarshalOptions{Deterministic: true}.Marshal(m)
Herbie Ong0b0f4032019-03-18 19:06:15 -07001750 if err != nil {
1751 t.Fatalf("error in binary marshaling message for Any.value: %v", err)
1752 }
Joe Tsaia95b29f2019-05-16 12:47:20 -07001753 return &anypb.Any{
Herbie Ong0b0f4032019-03-18 19:06:15 -07001754 TypeUrl: "type.googleapis.com/google.protobuf.Value",
1755 Value: b,
1756 }
1757 }(),
Herbie Ong0b0f4032019-03-18 19:06:15 -07001758 want: `{
Herbie Ong8ac9dd22019-03-27 12:20:50 -07001759 "@type": "type.googleapis.com/google.protobuf.Value",
1760 "value": null
Herbie Ong0b0f4032019-03-18 19:06:15 -07001761}`,
1762 }, {
1763 desc: "Any with Struct",
Damien Neil5c5b5312019-05-14 12:44:37 -07001764 mo: protojson.MarshalOptions{
Herbie Ong0b0f4032019-03-18 19:06:15 -07001765 Resolver: preg.NewTypes(
Joe Tsaia95b29f2019-05-16 12:47:20 -07001766 pimpl.Export{}.MessageTypeOf(&structpb.Struct{}),
1767 pimpl.Export{}.MessageTypeOf(&structpb.Value{}),
1768 pimpl.Export{}.MessageTypeOf(&wrapperspb.BoolValue{}),
1769 pimpl.Export{}.EnumTypeOf(structpb.NullValue_NULL_VALUE),
1770 pimpl.Export{}.MessageTypeOf(&wrapperspb.StringValue{}),
Herbie Ong0b0f4032019-03-18 19:06:15 -07001771 ),
1772 },
1773 input: func() proto.Message {
Joe Tsaia95b29f2019-05-16 12:47:20 -07001774 m := &structpb.Struct{
1775 Fields: map[string]*structpb.Value{
1776 "bool": {Kind: &structpb.Value_BoolValue{true}},
1777 "null": {Kind: &structpb.Value_NullValue{}},
1778 "string": {Kind: &structpb.Value_StringValue{"hello"}},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001779 "struct": {
Joe Tsaia95b29f2019-05-16 12:47:20 -07001780 Kind: &structpb.Value_StructValue{
1781 &structpb.Struct{
1782 Fields: map[string]*structpb.Value{
1783 "string": {Kind: &structpb.Value_StringValue{"world"}},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001784 },
1785 },
1786 },
1787 },
1788 },
1789 }
1790 b, err := proto.MarshalOptions{Deterministic: true}.Marshal(m)
1791 if err != nil {
1792 t.Fatalf("error in binary marshaling message for Any.value: %v", err)
1793 }
Joe Tsaia95b29f2019-05-16 12:47:20 -07001794 return &anypb.Any{
Herbie Ong0b0f4032019-03-18 19:06:15 -07001795 TypeUrl: "google.protobuf.Struct",
1796 Value: b,
1797 }
1798 }(),
1799 want: `{
1800 "@type": "google.protobuf.Struct",
1801 "value": {
1802 "bool": true,
1803 "null": null,
1804 "string": "hello",
1805 "struct": {
1806 "string": "world"
1807 }
1808 }
1809}`,
1810 }, {
Herbie Ong8ac9dd22019-03-27 12:20:50 -07001811 desc: "Any with missing type_url",
Damien Neil5c5b5312019-05-14 12:44:37 -07001812 mo: protojson.MarshalOptions{
Joe Tsaia95b29f2019-05-16 12:47:20 -07001813 Resolver: preg.NewTypes(pimpl.Export{}.MessageTypeOf(&wrapperspb.BoolValue{})),
Herbie Ong8ac9dd22019-03-27 12:20:50 -07001814 },
1815 input: func() proto.Message {
Joe Tsaia95b29f2019-05-16 12:47:20 -07001816 m := &wrapperspb.BoolValue{Value: true}
Herbie Ong8ac9dd22019-03-27 12:20:50 -07001817 b, err := proto.MarshalOptions{Deterministic: true}.Marshal(m)
1818 if err != nil {
1819 t.Fatalf("error in binary marshaling message for Any.value: %v", err)
1820 }
Joe Tsaia95b29f2019-05-16 12:47:20 -07001821 return &anypb.Any{
Herbie Ong8ac9dd22019-03-27 12:20:50 -07001822 Value: b,
1823 }
1824 }(),
1825 wantErr: true,
1826 }, {
Herbie Ong0b0f4032019-03-18 19:06:15 -07001827 desc: "well known types as field values",
Damien Neil5c5b5312019-05-14 12:44:37 -07001828 mo: protojson.MarshalOptions{
Joe Tsaia95b29f2019-05-16 12:47:20 -07001829 Resolver: preg.NewTypes(pimpl.Export{}.MessageTypeOf(&emptypb.Empty{})),
Herbie Ong0b0f4032019-03-18 19:06:15 -07001830 },
1831 input: &pb2.KnownTypes{
Joe Tsaia95b29f2019-05-16 12:47:20 -07001832 OptBool: &wrapperspb.BoolValue{Value: false},
1833 OptInt32: &wrapperspb.Int32Value{Value: 42},
1834 OptInt64: &wrapperspb.Int64Value{Value: 42},
1835 OptUint32: &wrapperspb.UInt32Value{Value: 42},
1836 OptUint64: &wrapperspb.UInt64Value{Value: 42},
1837 OptFloat: &wrapperspb.FloatValue{Value: 1.23},
1838 OptDouble: &wrapperspb.DoubleValue{Value: 3.1415},
1839 OptString: &wrapperspb.StringValue{Value: "hello"},
1840 OptBytes: &wrapperspb.BytesValue{Value: []byte("hello")},
1841 OptDuration: &durationpb.Duration{Seconds: 123},
1842 OptTimestamp: &timestamppb.Timestamp{Seconds: 1553036601},
1843 OptStruct: &structpb.Struct{
1844 Fields: map[string]*structpb.Value{
1845 "string": {Kind: &structpb.Value_StringValue{"hello"}},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001846 },
1847 },
Joe Tsaia95b29f2019-05-16 12:47:20 -07001848 OptList: &structpb.ListValue{
1849 Values: []*structpb.Value{
1850 {Kind: &structpb.Value_NullValue{}},
1851 {Kind: &structpb.Value_StringValue{}},
1852 {Kind: &structpb.Value_StructValue{}},
1853 {Kind: &structpb.Value_ListValue{}},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001854 },
1855 },
Joe Tsaia95b29f2019-05-16 12:47:20 -07001856 OptValue: &structpb.Value{
1857 Kind: &structpb.Value_StringValue{"world"},
Herbie Ong1c7462c2019-03-22 17:56:55 -07001858 },
Joe Tsaia95b29f2019-05-16 12:47:20 -07001859 OptEmpty: &emptypb.Empty{},
1860 OptAny: &anypb.Any{
Herbie Ong0b0f4032019-03-18 19:06:15 -07001861 TypeUrl: "google.protobuf.Empty",
1862 },
Joe Tsaia95b29f2019-05-16 12:47:20 -07001863 OptFieldmask: &fieldmaskpb.FieldMask{
Herbie Ong0b0f4032019-03-18 19:06:15 -07001864 Paths: []string{"foo_bar", "bar_foo"},
1865 },
1866 },
1867 want: `{
1868 "optBool": false,
1869 "optInt32": 42,
1870 "optInt64": "42",
1871 "optUint32": 42,
1872 "optUint64": "42",
1873 "optFloat": 1.23,
1874 "optDouble": 3.1415,
1875 "optString": "hello",
1876 "optBytes": "aGVsbG8=",
1877 "optDuration": "123s",
1878 "optTimestamp": "2019-03-19T23:03:21Z",
1879 "optStruct": {
1880 "string": "hello"
1881 },
1882 "optList": [
1883 null,
1884 "",
1885 {},
1886 []
1887 ],
Herbie Ong1c7462c2019-03-22 17:56:55 -07001888 "optValue": "world",
Herbie Ong0b0f4032019-03-18 19:06:15 -07001889 "optEmpty": {},
1890 "optAny": {
Herbie Ong82014a52019-03-27 15:21:43 -07001891 "@type": "google.protobuf.Empty",
1892 "value": {}
Herbie Ong0b0f4032019-03-18 19:06:15 -07001893 },
1894 "optFieldmask": "fooBar,barFoo"
1895}`,
Herbie Ong7b828bc2019-02-08 19:56:24 -08001896 }}
1897
1898 for _, tt := range tests {
1899 tt := tt
Joe Tsai5ae10aa2019-07-11 18:23:08 -07001900 if tt.skip {
1901 continue
1902 }
Herbie Ong7b828bc2019-02-08 19:56:24 -08001903 t.Run(tt.desc, func(t *testing.T) {
Herbie Ong0b0f4032019-03-18 19:06:15 -07001904 // Use 2-space indentation on all MarshalOptions.
1905 tt.mo.Indent = " "
Herbie Ong7b828bc2019-02-08 19:56:24 -08001906 b, err := tt.mo.Marshal(tt.input)
Herbie Ong0b0f4032019-03-18 19:06:15 -07001907 if err != nil && !tt.wantErr {
Herbie Ong7b828bc2019-02-08 19:56:24 -08001908 t.Errorf("Marshal() returned error: %v\n", err)
1909 }
Herbie Ong0b0f4032019-03-18 19:06:15 -07001910 if err == nil && tt.wantErr {
1911 t.Errorf("Marshal() got nil error, want error\n")
1912 }
Herbie Ong7b828bc2019-02-08 19:56:24 -08001913 got := string(b)
1914 if got != tt.want {
1915 t.Errorf("Marshal()\n<got>\n%v\n<want>\n%v\n", got, tt.want)
Joe Tsai6dc168e2019-07-09 23:11:13 -07001916 if diff := cmp.Diff(tt.want, got); diff != "" {
Herbie Ong7b828bc2019-02-08 19:56:24 -08001917 t.Errorf("Marshal() diff -want +got\n%v\n", diff)
1918 }
1919 }
1920 })
1921 }
1922}