blob: 42980b0f53fa05dde0175389d8df901707d0f0db [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"
Damien Neile89e6242019-05-13 23:55:40 -070015 pimpl "google.golang.org/protobuf/internal/impl"
Damien Neile89e6242019-05-13 23:55:40 -070016 "google.golang.org/protobuf/proto"
17 preg "google.golang.org/protobuf/reflect/protoregistry"
18 "google.golang.org/protobuf/runtime/protoiface"
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
Joe Tsai378c1322019-04-25 23:48:08 -070031// TODO: Replace this with proto.SetExtension.
Joe Tsai4fddeba2019-03-20 18:29:32 -070032func setExtension(m proto.Message, xd *protoiface.ExtensionDescV1, val interface{}) {
Joe Tsai378c1322019-04-25 23:48:08 -070033 m.ProtoReflect().Set(xd.Type, xd.Type.ValueOf(val))
Herbie Ongf83d5bb2019-03-14 00:01:27 -070034}
35
Herbie Ong7b828bc2019-02-08 19:56:24 -080036func TestMarshal(t *testing.T) {
37 tests := []struct {
Herbie Ong0b0f4032019-03-18 19:06:15 -070038 desc string
Damien Neil5c5b5312019-05-14 12:44:37 -070039 mo protojson.MarshalOptions
Herbie Ong0b0f4032019-03-18 19:06:15 -070040 input proto.Message
41 want string
42 wantErr bool // TODO: Verify error message substring.
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 }
887 setExtension(m, pb2.E_OptExtBool, true)
888 setExtension(m, pb2.E_OptExtString, "extension field")
889 setExtension(m, pb2.E_OptExtEnum, pb2.Enum_TEN)
890 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{}
916 setExtension(m, pb2.E_RptExtEnum, &[]pb2.Enum{pb2.Enum_TEN, 101, pb2.Enum_ONE})
917 setExtension(m, pb2.E_RptExtFixed32, &[]uint32{42, 47})
918 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{}
951 setExtension(m, pb2.E_ExtensionsContainer_OptExtBool, true)
952 setExtension(m, pb2.E_ExtensionsContainer_OptExtString, "extension field")
953 setExtension(m, pb2.E_ExtensionsContainer_OptExtEnum, pb2.Enum_TEN)
954 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 }
981 setExtension(m, pb2.E_ExtensionsContainer_RptExtEnum, &[]pb2.Enum{pb2.Enum_TEN, 101, pb2.Enum_ONE})
982 setExtension(m, pb2.E_ExtensionsContainer_RptExtString, &[]string{"hello", "world"})
983 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{}
1019 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 })
1022 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 })
1025 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}`,
1041 }, {
1042 desc: "not real MessageSet 1",
1043 input: func() proto.Message {
1044 m := &pb2.FakeMessageSet{}
1045 setExtension(m, pb2.E_FakeMessageSetExtension_MessageSetExtension, &pb2.FakeMessageSetExtension{
Damien Neila8a2cea2019-07-10 16:17:16 -07001046 OptString: proto.String("not a messageset extension"),
Herbie Ongf83d5bb2019-03-14 00:01:27 -07001047 })
1048 return m
1049 }(),
1050 want: `{
1051 "[pb2.FakeMessageSetExtension.message_set_extension]": {
1052 "optString": "not a messageset extension"
1053 }
1054}`,
1055 }, {
1056 desc: "not real MessageSet 2",
1057 input: func() proto.Message {
1058 m := &pb2.MessageSet{}
1059 setExtension(m, pb2.E_MessageSetExtension, &pb2.FakeMessageSetExtension{
Damien Neila8a2cea2019-07-10 16:17:16 -07001060 OptString: proto.String("another not a messageset extension"),
Herbie Ongf83d5bb2019-03-14 00:01:27 -07001061 })
1062 return m
1063 }(),
1064 want: `{
1065 "[pb2.message_set_extension]": {
1066 "optString": "another not a messageset extension"
1067 }
1068}`,
Herbie Ong0b0f4032019-03-18 19:06:15 -07001069 }, {
1070 desc: "BoolValue empty",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001071 input: &wrapperspb.BoolValue{},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001072 want: `false`,
1073 }, {
1074 desc: "BoolValue",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001075 input: &wrapperspb.BoolValue{Value: true},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001076 want: `true`,
1077 }, {
1078 desc: "Int32Value empty",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001079 input: &wrapperspb.Int32Value{},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001080 want: `0`,
1081 }, {
1082 desc: "Int32Value",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001083 input: &wrapperspb.Int32Value{Value: 42},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001084 want: `42`,
1085 }, {
1086 desc: "Int64Value",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001087 input: &wrapperspb.Int64Value{Value: 42},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001088 want: `"42"`,
1089 }, {
1090 desc: "UInt32Value",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001091 input: &wrapperspb.UInt32Value{Value: 42},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001092 want: `42`,
1093 }, {
1094 desc: "UInt64Value",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001095 input: &wrapperspb.UInt64Value{Value: 42},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001096 want: `"42"`,
1097 }, {
1098 desc: "FloatValue",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001099 input: &wrapperspb.FloatValue{Value: 1.02},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001100 want: `1.02`,
1101 }, {
Herbie Onge63c4c42019-03-22 22:20:22 -07001102 desc: "FloatValue Infinity",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001103 input: &wrapperspb.FloatValue{Value: float32(math.Inf(-1))},
Herbie Onge63c4c42019-03-22 22:20:22 -07001104 want: `"-Infinity"`,
1105 }, {
Herbie Ong0b0f4032019-03-18 19:06:15 -07001106 desc: "DoubleValue",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001107 input: &wrapperspb.DoubleValue{Value: 1.02},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001108 want: `1.02`,
1109 }, {
Herbie Onge63c4c42019-03-22 22:20:22 -07001110 desc: "DoubleValue NaN",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001111 input: &wrapperspb.DoubleValue{Value: math.NaN()},
Herbie Onge63c4c42019-03-22 22:20:22 -07001112 want: `"NaN"`,
1113 }, {
Herbie Ong0b0f4032019-03-18 19:06:15 -07001114 desc: "StringValue empty",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001115 input: &wrapperspb.StringValue{},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001116 want: `""`,
1117 }, {
1118 desc: "StringValue",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001119 input: &wrapperspb.StringValue{Value: "谷歌"},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001120 want: `"谷歌"`,
1121 }, {
1122 desc: "StringValue with invalid UTF8 error",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001123 input: &wrapperspb.StringValue{Value: "abc\xff"},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001124 wantErr: true,
1125 }, {
1126 desc: "StringValue field with invalid UTF8 error",
1127 input: &pb2.KnownTypes{
Joe Tsaia95b29f2019-05-16 12:47:20 -07001128 OptString: &wrapperspb.StringValue{Value: "abc\xff"},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001129 },
Herbie Ong0b0f4032019-03-18 19:06:15 -07001130 wantErr: true,
1131 }, {
1132 desc: "BytesValue",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001133 input: &wrapperspb.BytesValue{Value: []byte("hello")},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001134 want: `"aGVsbG8="`,
1135 }, {
1136 desc: "Empty",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001137 input: &emptypb.Empty{},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001138 want: `{}`,
1139 }, {
Herbie Ong300b9fe2019-03-29 15:42:20 -07001140 desc: "NullValue field",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001141 input: &pb2.KnownTypes{OptNull: new(structpb.NullValue)},
Herbie Ong300b9fe2019-03-29 15:42:20 -07001142 want: `{
1143 "optNull": null
1144}`,
1145 }, {
Herbie Ong1c7462c2019-03-22 17:56:55 -07001146 desc: "Value empty",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001147 input: &structpb.Value{},
Herbie Ong1c7462c2019-03-22 17:56:55 -07001148 wantErr: true,
Herbie Ong0b0f4032019-03-18 19:06:15 -07001149 }, {
1150 desc: "Value empty field",
1151 input: &pb2.KnownTypes{
Joe Tsaia95b29f2019-05-16 12:47:20 -07001152 OptValue: &structpb.Value{},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001153 },
Herbie Ong1c7462c2019-03-22 17:56:55 -07001154 wantErr: true,
Herbie Ong0b0f4032019-03-18 19:06:15 -07001155 }, {
1156 desc: "Value contains NullValue",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001157 input: &structpb.Value{Kind: &structpb.Value_NullValue{}},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001158 want: `null`,
1159 }, {
1160 desc: "Value contains BoolValue",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001161 input: &structpb.Value{Kind: &structpb.Value_BoolValue{}},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001162 want: `false`,
1163 }, {
1164 desc: "Value contains NumberValue",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001165 input: &structpb.Value{Kind: &structpb.Value_NumberValue{1.02}},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001166 want: `1.02`,
1167 }, {
1168 desc: "Value contains StringValue",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001169 input: &structpb.Value{Kind: &structpb.Value_StringValue{"hello"}},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001170 want: `"hello"`,
1171 }, {
1172 desc: "Value contains StringValue with invalid UTF8",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001173 input: &structpb.Value{Kind: &structpb.Value_StringValue{"\xff"}},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001174 wantErr: true,
1175 }, {
1176 desc: "Value contains Struct",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001177 input: &structpb.Value{
1178 Kind: &structpb.Value_StructValue{
1179 &structpb.Struct{
1180 Fields: map[string]*structpb.Value{
1181 "null": {Kind: &structpb.Value_NullValue{}},
1182 "number": {Kind: &structpb.Value_NumberValue{}},
1183 "string": {Kind: &structpb.Value_StringValue{}},
1184 "struct": {Kind: &structpb.Value_StructValue{}},
1185 "list": {Kind: &structpb.Value_ListValue{}},
1186 "bool": {Kind: &structpb.Value_BoolValue{}},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001187 },
1188 },
1189 },
1190 },
1191 want: `{
1192 "bool": false,
1193 "list": [],
1194 "null": null,
1195 "number": 0,
1196 "string": "",
1197 "struct": {}
1198}`,
1199 }, {
1200 desc: "Value contains ListValue",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001201 input: &structpb.Value{
1202 Kind: &structpb.Value_ListValue{
1203 &structpb.ListValue{
1204 Values: []*structpb.Value{
1205 {Kind: &structpb.Value_BoolValue{}},
1206 {Kind: &structpb.Value_NullValue{}},
1207 {Kind: &structpb.Value_NumberValue{}},
1208 {Kind: &structpb.Value_StringValue{}},
1209 {Kind: &structpb.Value_StructValue{}},
1210 {Kind: &structpb.Value_ListValue{}},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001211 },
1212 },
1213 },
1214 },
1215 want: `[
1216 false,
1217 null,
1218 0,
1219 "",
1220 {},
1221 []
1222]`,
1223 }, {
1224 desc: "Struct with nil map",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001225 input: &structpb.Struct{},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001226 want: `{}`,
1227 }, {
1228 desc: "Struct with empty map",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001229 input: &structpb.Struct{
1230 Fields: map[string]*structpb.Value{},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001231 },
1232 want: `{}`,
1233 }, {
1234 desc: "Struct",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001235 input: &structpb.Struct{
1236 Fields: map[string]*structpb.Value{
1237 "bool": {Kind: &structpb.Value_BoolValue{true}},
1238 "null": {Kind: &structpb.Value_NullValue{}},
1239 "number": {Kind: &structpb.Value_NumberValue{3.1415}},
1240 "string": {Kind: &structpb.Value_StringValue{"hello"}},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001241 "struct": {
Joe Tsaia95b29f2019-05-16 12:47:20 -07001242 Kind: &structpb.Value_StructValue{
1243 &structpb.Struct{
1244 Fields: map[string]*structpb.Value{
1245 "string": {Kind: &structpb.Value_StringValue{"world"}},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001246 },
1247 },
1248 },
1249 },
1250 "list": {
Joe Tsaia95b29f2019-05-16 12:47:20 -07001251 Kind: &structpb.Value_ListValue{
1252 &structpb.ListValue{
1253 Values: []*structpb.Value{
1254 {Kind: &structpb.Value_BoolValue{}},
1255 {Kind: &structpb.Value_NullValue{}},
1256 {Kind: &structpb.Value_NumberValue{}},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001257 },
1258 },
1259 },
1260 },
1261 },
1262 },
1263 want: `{
1264 "bool": true,
1265 "list": [
1266 false,
1267 null,
1268 0
1269 ],
1270 "null": null,
1271 "number": 3.1415,
1272 "string": "hello",
1273 "struct": {
1274 "string": "world"
1275 }
1276}`,
1277 }, {
1278 desc: "Struct message with invalid UTF8 string",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001279 input: &structpb.Struct{
1280 Fields: map[string]*structpb.Value{
1281 "string": {Kind: &structpb.Value_StringValue{"\xff"}},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001282 },
1283 },
Herbie Ong0b0f4032019-03-18 19:06:15 -07001284 wantErr: true,
1285 }, {
1286 desc: "ListValue with nil values",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001287 input: &structpb.ListValue{},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001288 want: `[]`,
1289 }, {
1290 desc: "ListValue with empty values",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001291 input: &structpb.ListValue{
1292 Values: []*structpb.Value{},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001293 },
1294 want: `[]`,
1295 }, {
1296 desc: "ListValue",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001297 input: &structpb.ListValue{
1298 Values: []*structpb.Value{
1299 {Kind: &structpb.Value_BoolValue{true}},
1300 {Kind: &structpb.Value_NullValue{}},
1301 {Kind: &structpb.Value_NumberValue{3.1415}},
1302 {Kind: &structpb.Value_StringValue{"hello"}},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001303 {
Joe Tsaia95b29f2019-05-16 12:47:20 -07001304 Kind: &structpb.Value_ListValue{
1305 &structpb.ListValue{
1306 Values: []*structpb.Value{
1307 {Kind: &structpb.Value_BoolValue{}},
1308 {Kind: &structpb.Value_NullValue{}},
1309 {Kind: &structpb.Value_NumberValue{}},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001310 },
1311 },
1312 },
1313 },
1314 {
Joe Tsaia95b29f2019-05-16 12:47:20 -07001315 Kind: &structpb.Value_StructValue{
1316 &structpb.Struct{
1317 Fields: map[string]*structpb.Value{
1318 "string": {Kind: &structpb.Value_StringValue{"world"}},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001319 },
1320 },
1321 },
1322 },
1323 },
1324 },
1325 want: `[
1326 true,
1327 null,
1328 3.1415,
1329 "hello",
1330 [
1331 false,
1332 null,
1333 0
1334 ],
1335 {
1336 "string": "world"
1337 }
1338]`,
1339 }, {
1340 desc: "ListValue with invalid UTF8 string",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001341 input: &structpb.ListValue{
1342 Values: []*structpb.Value{
1343 {Kind: &structpb.Value_StringValue{"\xff"}},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001344 },
1345 },
Herbie Ong0b0f4032019-03-18 19:06:15 -07001346 wantErr: true,
1347 }, {
1348 desc: "Duration empty",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001349 input: &durationpb.Duration{},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001350 want: `"0s"`,
1351 }, {
1352 desc: "Duration with secs",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001353 input: &durationpb.Duration{Seconds: 3},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001354 want: `"3s"`,
1355 }, {
1356 desc: "Duration with -secs",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001357 input: &durationpb.Duration{Seconds: -3},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001358 want: `"-3s"`,
1359 }, {
1360 desc: "Duration with nanos",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001361 input: &durationpb.Duration{Nanos: 1e6},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001362 want: `"0.001s"`,
1363 }, {
1364 desc: "Duration with -nanos",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001365 input: &durationpb.Duration{Nanos: -1e6},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001366 want: `"-0.001s"`,
1367 }, {
1368 desc: "Duration with large secs",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001369 input: &durationpb.Duration{Seconds: 1e10, Nanos: 1},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001370 want: `"10000000000.000000001s"`,
1371 }, {
1372 desc: "Duration with 6-digit nanos",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001373 input: &durationpb.Duration{Nanos: 1e4},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001374 want: `"0.000010s"`,
1375 }, {
1376 desc: "Duration with 3-digit nanos",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001377 input: &durationpb.Duration{Nanos: 1e6},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001378 want: `"0.001s"`,
1379 }, {
1380 desc: "Duration with -secs -nanos",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001381 input: &durationpb.Duration{Seconds: -123, Nanos: -450},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001382 want: `"-123.000000450s"`,
1383 }, {
Herbie Ongad9c1252019-04-24 20:51:28 -07001384 desc: "Duration max value",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001385 input: &durationpb.Duration{Seconds: 315576000000, Nanos: 999999999},
Herbie Ongad9c1252019-04-24 20:51:28 -07001386 want: `"315576000000.999999999s"`,
1387 }, {
1388 desc: "Duration min value",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001389 input: &durationpb.Duration{Seconds: -315576000000, Nanos: -999999999},
Herbie Ongad9c1252019-04-24 20:51:28 -07001390 want: `"-315576000000.999999999s"`,
1391 }, {
Herbie Ong0b0f4032019-03-18 19:06:15 -07001392 desc: "Duration with +secs -nanos",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001393 input: &durationpb.Duration{Seconds: 1, Nanos: -1},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001394 wantErr: true,
1395 }, {
1396 desc: "Duration with -secs +nanos",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001397 input: &durationpb.Duration{Seconds: -1, Nanos: 1},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001398 wantErr: true,
1399 }, {
1400 desc: "Duration with +secs out of range",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001401 input: &durationpb.Duration{Seconds: 315576000001},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001402 wantErr: true,
1403 }, {
1404 desc: "Duration with -secs out of range",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001405 input: &durationpb.Duration{Seconds: -315576000001},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001406 wantErr: true,
1407 }, {
1408 desc: "Duration with +nanos out of range",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001409 input: &durationpb.Duration{Seconds: 0, Nanos: 1e9},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001410 wantErr: true,
1411 }, {
1412 desc: "Duration with -nanos out of range",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001413 input: &durationpb.Duration{Seconds: 0, Nanos: -1e9},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001414 wantErr: true,
1415 }, {
1416 desc: "Timestamp zero",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001417 input: &timestamppb.Timestamp{},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001418 want: `"1970-01-01T00:00:00Z"`,
1419 }, {
1420 desc: "Timestamp",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001421 input: &timestamppb.Timestamp{Seconds: 1553036601},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001422 want: `"2019-03-19T23:03:21Z"`,
1423 }, {
1424 desc: "Timestamp with nanos",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001425 input: &timestamppb.Timestamp{Seconds: 1553036601, Nanos: 1},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001426 want: `"2019-03-19T23:03:21.000000001Z"`,
1427 }, {
1428 desc: "Timestamp with 6-digit nanos",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001429 input: &timestamppb.Timestamp{Nanos: 1e3},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001430 want: `"1970-01-01T00:00:00.000001Z"`,
1431 }, {
1432 desc: "Timestamp with 3-digit nanos",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001433 input: &timestamppb.Timestamp{Nanos: 1e7},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001434 want: `"1970-01-01T00:00:00.010Z"`,
1435 }, {
Herbie Ongad9c1252019-04-24 20:51:28 -07001436 desc: "Timestamp max value",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001437 input: &timestamppb.Timestamp{Seconds: 253402300799, Nanos: 999999999},
Herbie Ongad9c1252019-04-24 20:51:28 -07001438 want: `"9999-12-31T23:59:59.999999999Z"`,
1439 }, {
1440 desc: "Timestamp min value",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001441 input: &timestamppb.Timestamp{Seconds: -62135596800},
Herbie Ongad9c1252019-04-24 20:51:28 -07001442 want: `"0001-01-01T00:00:00Z"`,
1443 }, {
Herbie Ong0b0f4032019-03-18 19:06:15 -07001444 desc: "Timestamp with +secs out of range",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001445 input: &timestamppb.Timestamp{Seconds: 253402300800},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001446 wantErr: true,
1447 }, {
1448 desc: "Timestamp with -secs out of range",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001449 input: &timestamppb.Timestamp{Seconds: -62135596801},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001450 wantErr: true,
1451 }, {
1452 desc: "Timestamp with -nanos",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001453 input: &timestamppb.Timestamp{Nanos: -1},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001454 wantErr: true,
1455 }, {
1456 desc: "Timestamp with +nanos out of range",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001457 input: &timestamppb.Timestamp{Nanos: 1e9},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001458 wantErr: true,
1459 }, {
1460 desc: "FieldMask empty",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001461 input: &fieldmaskpb.FieldMask{},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001462 want: `""`,
1463 }, {
1464 desc: "FieldMask",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001465 input: &fieldmaskpb.FieldMask{
Herbie Ong0b0f4032019-03-18 19:06:15 -07001466 Paths: []string{
1467 "foo",
1468 "foo_bar",
1469 "foo.bar_qux",
1470 "_foo",
1471 },
1472 },
1473 want: `"foo,fooBar,foo.barQux,Foo"`,
1474 }, {
1475 desc: "FieldMask error 1",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001476 input: &fieldmaskpb.FieldMask{
Herbie Ong0b0f4032019-03-18 19:06:15 -07001477 Paths: []string{"foo_"},
1478 },
1479 wantErr: true,
1480 }, {
1481 desc: "FieldMask error 2",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001482 input: &fieldmaskpb.FieldMask{
Herbie Ong0b0f4032019-03-18 19:06:15 -07001483 Paths: []string{"foo__bar"},
1484 },
1485 wantErr: true,
1486 }, {
1487 desc: "Any empty",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001488 input: &anypb.Any{},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001489 want: `{}`,
1490 }, {
Herbie Ong8ac9dd22019-03-27 12:20:50 -07001491 desc: "Any with non-custom message",
Damien Neil5c5b5312019-05-14 12:44:37 -07001492 mo: protojson.MarshalOptions{
Joe Tsai0fc49f82019-05-01 12:29:25 -07001493 Resolver: preg.NewTypes(pimpl.Export{}.MessageTypeOf(&pb2.Nested{})),
Herbie Ong0b0f4032019-03-18 19:06:15 -07001494 },
1495 input: func() proto.Message {
1496 m := &pb2.Nested{
Damien Neila8a2cea2019-07-10 16:17:16 -07001497 OptString: proto.String("embedded inside Any"),
Herbie Ong0b0f4032019-03-18 19:06:15 -07001498 OptNested: &pb2.Nested{
Damien Neila8a2cea2019-07-10 16:17:16 -07001499 OptString: proto.String("inception"),
Herbie Ong0b0f4032019-03-18 19:06:15 -07001500 },
1501 }
1502 b, err := proto.MarshalOptions{Deterministic: true}.Marshal(m)
1503 if err != nil {
1504 t.Fatalf("error in binary marshaling message for Any.value: %v", err)
1505 }
Joe Tsaia95b29f2019-05-16 12:47:20 -07001506 return &anypb.Any{
Herbie Ong0b0f4032019-03-18 19:06:15 -07001507 TypeUrl: "foo/pb2.Nested",
1508 Value: b,
1509 }
1510 }(),
1511 want: `{
1512 "@type": "foo/pb2.Nested",
1513 "optString": "embedded inside Any",
1514 "optNested": {
1515 "optString": "inception"
1516 }
1517}`,
1518 }, {
Herbie Ong8ac9dd22019-03-27 12:20:50 -07001519 desc: "Any with empty embedded message",
Damien Neil5c5b5312019-05-14 12:44:37 -07001520 mo: protojson.MarshalOptions{
Joe Tsai0fc49f82019-05-01 12:29:25 -07001521 Resolver: preg.NewTypes(pimpl.Export{}.MessageTypeOf(&pb2.Nested{})),
Herbie Ong0b0f4032019-03-18 19:06:15 -07001522 },
Joe Tsaia95b29f2019-05-16 12:47:20 -07001523 input: &anypb.Any{TypeUrl: "foo/pb2.Nested"},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001524 want: `{
1525 "@type": "foo/pb2.Nested"
1526}`,
1527 }, {
Herbie Ong8ac9dd22019-03-27 12:20:50 -07001528 desc: "Any without registered type",
Damien Neil5c5b5312019-05-14 12:44:37 -07001529 mo: protojson.MarshalOptions{Resolver: preg.NewTypes()},
Joe Tsaia95b29f2019-05-16 12:47:20 -07001530 input: &anypb.Any{TypeUrl: "foo/pb2.Nested"},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001531 wantErr: true,
1532 }, {
Damien Neil0c9f0a92019-06-19 10:41:09 -07001533 desc: "Any with missing required",
Damien Neil5c5b5312019-05-14 12:44:37 -07001534 mo: protojson.MarshalOptions{
Joe Tsai0fc49f82019-05-01 12:29:25 -07001535 Resolver: preg.NewTypes(pimpl.Export{}.MessageTypeOf(&pb2.PartialRequired{})),
Herbie Ong0b0f4032019-03-18 19:06:15 -07001536 },
1537 input: func() proto.Message {
1538 m := &pb2.PartialRequired{
Damien Neila8a2cea2019-07-10 16:17:16 -07001539 OptString: proto.String("embedded inside Any"),
Herbie Ong0b0f4032019-03-18 19:06:15 -07001540 }
Damien Neil96c229a2019-04-03 12:17:24 -07001541 b, err := proto.MarshalOptions{
1542 AllowPartial: true,
1543 Deterministic: true,
1544 }.Marshal(m)
Herbie Ong0b0f4032019-03-18 19:06:15 -07001545 if err != nil {
1546 t.Fatalf("error in binary marshaling message for Any.value: %v", err)
1547 }
Joe Tsaia95b29f2019-05-16 12:47:20 -07001548 return &anypb.Any{
Joe Tsai0fc49f82019-05-01 12:29:25 -07001549 TypeUrl: string(m.ProtoReflect().Descriptor().FullName()),
Herbie Ong0b0f4032019-03-18 19:06:15 -07001550 Value: b,
1551 }
1552 }(),
1553 want: `{
1554 "@type": "pb2.PartialRequired",
1555 "optString": "embedded inside Any"
1556}`,
Herbie Ong0b0f4032019-03-18 19:06:15 -07001557 }, {
Herbie Ong8ac9dd22019-03-27 12:20:50 -07001558 desc: "Any with partial required and AllowPartial",
Damien Neil5c5b5312019-05-14 12:44:37 -07001559 mo: protojson.MarshalOptions{
Herbie Ong8ac9dd22019-03-27 12:20:50 -07001560 AllowPartial: true,
Joe Tsai0fc49f82019-05-01 12:29:25 -07001561 Resolver: preg.NewTypes(pimpl.Export{}.MessageTypeOf(&pb2.PartialRequired{})),
Herbie Ong8ac9dd22019-03-27 12:20:50 -07001562 },
1563 input: func() proto.Message {
1564 m := &pb2.PartialRequired{
Damien Neila8a2cea2019-07-10 16:17:16 -07001565 OptString: proto.String("embedded inside Any"),
Herbie Ong8ac9dd22019-03-27 12:20:50 -07001566 }
Damien Neil96c229a2019-04-03 12:17:24 -07001567 b, err := proto.MarshalOptions{
1568 AllowPartial: true,
1569 Deterministic: true,
1570 }.Marshal(m)
Herbie Ong8ac9dd22019-03-27 12:20:50 -07001571 if err != nil {
1572 t.Fatalf("error in binary marshaling message for Any.value: %v", err)
1573 }
Joe Tsaia95b29f2019-05-16 12:47:20 -07001574 return &anypb.Any{
Joe Tsai0fc49f82019-05-01 12:29:25 -07001575 TypeUrl: string(m.ProtoReflect().Descriptor().FullName()),
Herbie Ong8ac9dd22019-03-27 12:20:50 -07001576 Value: b,
1577 }
1578 }(),
1579 want: `{
1580 "@type": "pb2.PartialRequired",
1581 "optString": "embedded inside Any"
1582}`,
1583 }, {
Herbie Ong0b0f4032019-03-18 19:06:15 -07001584 desc: "Any with invalid UTF8",
Damien Neil5c5b5312019-05-14 12:44:37 -07001585 mo: protojson.MarshalOptions{
Joe Tsai0fc49f82019-05-01 12:29:25 -07001586 Resolver: preg.NewTypes(pimpl.Export{}.MessageTypeOf(&pb2.Nested{})),
Herbie Ong0b0f4032019-03-18 19:06:15 -07001587 },
1588 input: func() proto.Message {
1589 m := &pb2.Nested{
Damien Neila8a2cea2019-07-10 16:17:16 -07001590 OptString: proto.String("abc\xff"),
Herbie Ong0b0f4032019-03-18 19:06:15 -07001591 }
1592 b, err := proto.MarshalOptions{Deterministic: true}.Marshal(m)
1593 if err != nil {
1594 t.Fatalf("error in binary marshaling message for Any.value: %v", err)
1595 }
Joe Tsaia95b29f2019-05-16 12:47:20 -07001596 return &anypb.Any{
Herbie Ong0b0f4032019-03-18 19:06:15 -07001597 TypeUrl: "foo/pb2.Nested",
1598 Value: b,
1599 }
1600 }(),
Herbie Ong0b0f4032019-03-18 19:06:15 -07001601 wantErr: true,
1602 }, {
1603 desc: "Any with invalid value",
Damien Neil5c5b5312019-05-14 12:44:37 -07001604 mo: protojson.MarshalOptions{
Joe Tsai0fc49f82019-05-01 12:29:25 -07001605 Resolver: preg.NewTypes(pimpl.Export{}.MessageTypeOf(&pb2.Nested{})),
Herbie Ong0b0f4032019-03-18 19:06:15 -07001606 },
Joe Tsaia95b29f2019-05-16 12:47:20 -07001607 input: &anypb.Any{
Herbie Ong0b0f4032019-03-18 19:06:15 -07001608 TypeUrl: "foo/pb2.Nested",
Joe Tsai6dc168e2019-07-09 23:11:13 -07001609 Value: []byte("\x80"),
Herbie Ong0b0f4032019-03-18 19:06:15 -07001610 },
1611 wantErr: true,
1612 }, {
1613 desc: "Any with BoolValue",
Damien Neil5c5b5312019-05-14 12:44:37 -07001614 mo: protojson.MarshalOptions{
Joe Tsaia95b29f2019-05-16 12:47:20 -07001615 Resolver: preg.NewTypes(pimpl.Export{}.MessageTypeOf(&wrapperspb.BoolValue{})),
Herbie Ong0b0f4032019-03-18 19:06:15 -07001616 },
1617 input: func() proto.Message {
Joe Tsaia95b29f2019-05-16 12:47:20 -07001618 m := &wrapperspb.BoolValue{Value: true}
Herbie Ong0b0f4032019-03-18 19:06:15 -07001619 b, err := proto.MarshalOptions{Deterministic: true}.Marshal(m)
1620 if err != nil {
1621 t.Fatalf("error in binary marshaling message for Any.value: %v", err)
1622 }
Joe Tsaia95b29f2019-05-16 12:47:20 -07001623 return &anypb.Any{
Herbie Ong0b0f4032019-03-18 19:06:15 -07001624 TypeUrl: "type.googleapis.com/google.protobuf.BoolValue",
1625 Value: b,
1626 }
1627 }(),
1628 want: `{
1629 "@type": "type.googleapis.com/google.protobuf.BoolValue",
1630 "value": true
1631}`,
1632 }, {
Herbie Ong0b0f4032019-03-18 19:06:15 -07001633 desc: "Any with Empty",
Damien Neil5c5b5312019-05-14 12:44:37 -07001634 mo: protojson.MarshalOptions{
Joe Tsaia95b29f2019-05-16 12:47:20 -07001635 Resolver: preg.NewTypes(pimpl.Export{}.MessageTypeOf(&emptypb.Empty{})),
Herbie Ong0b0f4032019-03-18 19:06:15 -07001636 },
1637 input: func() proto.Message {
Joe Tsaia95b29f2019-05-16 12:47:20 -07001638 m := &emptypb.Empty{}
Herbie Ong0b0f4032019-03-18 19:06:15 -07001639 b, err := proto.MarshalOptions{Deterministic: true}.Marshal(m)
1640 if err != nil {
1641 t.Fatalf("error in binary marshaling message for Any.value: %v", err)
1642 }
Joe Tsaia95b29f2019-05-16 12:47:20 -07001643 return &anypb.Any{
Herbie Ong0b0f4032019-03-18 19:06:15 -07001644 TypeUrl: "type.googleapis.com/google.protobuf.Empty",
1645 Value: b,
1646 }
1647 }(),
1648 want: `{
Herbie Ong82014a52019-03-27 15:21:43 -07001649 "@type": "type.googleapis.com/google.protobuf.Empty",
1650 "value": {}
Herbie Ong0b0f4032019-03-18 19:06:15 -07001651}`,
1652 }, {
1653 desc: "Any with StringValue containing invalid UTF8",
Damien Neil5c5b5312019-05-14 12:44:37 -07001654 mo: protojson.MarshalOptions{
Joe Tsaia95b29f2019-05-16 12:47:20 -07001655 Resolver: preg.NewTypes(pimpl.Export{}.MessageTypeOf(&wrapperspb.StringValue{})),
Herbie Ong0b0f4032019-03-18 19:06:15 -07001656 },
1657 input: func() proto.Message {
Joe Tsaia95b29f2019-05-16 12:47:20 -07001658 m := &wrapperspb.StringValue{Value: "abcd"}
Herbie Ong0b0f4032019-03-18 19:06:15 -07001659 b, err := proto.MarshalOptions{Deterministic: true}.Marshal(m)
1660 if err != nil {
1661 t.Fatalf("error in binary marshaling message for Any.value: %v", err)
1662 }
Joe Tsaia95b29f2019-05-16 12:47:20 -07001663 return &anypb.Any{
Herbie Ong0b0f4032019-03-18 19:06:15 -07001664 TypeUrl: "google.protobuf.StringValue",
Damien Neilbc310b52019-04-11 11:46:55 -07001665 Value: bytes.Replace(b, []byte("abcd"), []byte("abc\xff"), -1),
Herbie Ong0b0f4032019-03-18 19:06:15 -07001666 }
1667 }(),
Herbie Ong0b0f4032019-03-18 19:06:15 -07001668 wantErr: true,
1669 }, {
Herbie Ong8ac9dd22019-03-27 12:20:50 -07001670 desc: "Any with Int64Value",
Damien Neil5c5b5312019-05-14 12:44:37 -07001671 mo: protojson.MarshalOptions{
Joe Tsaia95b29f2019-05-16 12:47:20 -07001672 Resolver: preg.NewTypes(pimpl.Export{}.MessageTypeOf(&wrapperspb.Int64Value{})),
Herbie Ong8ac9dd22019-03-27 12:20:50 -07001673 },
1674 input: func() proto.Message {
Joe Tsaia95b29f2019-05-16 12:47:20 -07001675 m := &wrapperspb.Int64Value{Value: 42}
Herbie Ong8ac9dd22019-03-27 12:20:50 -07001676 b, err := proto.MarshalOptions{Deterministic: true}.Marshal(m)
1677 if err != nil {
1678 t.Fatalf("error in binary marshaling message for Any.value: %v", err)
1679 }
Joe Tsaia95b29f2019-05-16 12:47:20 -07001680 return &anypb.Any{
Herbie Ong8ac9dd22019-03-27 12:20:50 -07001681 TypeUrl: "google.protobuf.Int64Value",
1682 Value: b,
1683 }
1684 }(),
1685 want: `{
1686 "@type": "google.protobuf.Int64Value",
1687 "value": "42"
1688}`,
1689 }, {
1690 desc: "Any with Duration",
Damien Neil5c5b5312019-05-14 12:44:37 -07001691 mo: protojson.MarshalOptions{
Joe Tsaia95b29f2019-05-16 12:47:20 -07001692 Resolver: preg.NewTypes(pimpl.Export{}.MessageTypeOf(&durationpb.Duration{})),
Herbie Ong8ac9dd22019-03-27 12:20:50 -07001693 },
1694 input: func() proto.Message {
Joe Tsaia95b29f2019-05-16 12:47:20 -07001695 m := &durationpb.Duration{}
Herbie Ong8ac9dd22019-03-27 12:20:50 -07001696 b, err := proto.MarshalOptions{Deterministic: true}.Marshal(m)
1697 if err != nil {
1698 t.Fatalf("error in binary marshaling message for Any.value: %v", err)
1699 }
Joe Tsaia95b29f2019-05-16 12:47:20 -07001700 return &anypb.Any{
Herbie Ong8ac9dd22019-03-27 12:20:50 -07001701 TypeUrl: "type.googleapis.com/google.protobuf.Duration",
1702 Value: b,
1703 }
1704 }(),
1705 want: `{
1706 "@type": "type.googleapis.com/google.protobuf.Duration",
1707 "value": "0s"
1708}`,
1709 }, {
1710 desc: "Any with empty Value",
Damien Neil5c5b5312019-05-14 12:44:37 -07001711 mo: protojson.MarshalOptions{
Joe Tsaia95b29f2019-05-16 12:47:20 -07001712 Resolver: preg.NewTypes(pimpl.Export{}.MessageTypeOf(&structpb.Value{})),
Herbie Ong8ac9dd22019-03-27 12:20:50 -07001713 },
1714 input: func() proto.Message {
Joe Tsaia95b29f2019-05-16 12:47:20 -07001715 m := &structpb.Value{}
Herbie Ong8ac9dd22019-03-27 12:20:50 -07001716 b, err := proto.Marshal(m)
1717 if err != nil {
1718 t.Fatalf("error in binary marshaling message for Any.value: %v", err)
1719 }
Joe Tsaia95b29f2019-05-16 12:47:20 -07001720 return &anypb.Any{
Herbie Ong8ac9dd22019-03-27 12:20:50 -07001721 TypeUrl: "type.googleapis.com/google.protobuf.Value",
1722 Value: b,
1723 }
1724 }(),
1725 wantErr: true,
1726 }, {
Herbie Ong0b0f4032019-03-18 19:06:15 -07001727 desc: "Any with Value of StringValue",
Damien Neil5c5b5312019-05-14 12:44:37 -07001728 mo: protojson.MarshalOptions{
Joe Tsaia95b29f2019-05-16 12:47:20 -07001729 Resolver: preg.NewTypes(pimpl.Export{}.MessageTypeOf(&structpb.Value{})),
Herbie Ong0b0f4032019-03-18 19:06:15 -07001730 },
1731 input: func() proto.Message {
Joe Tsaia95b29f2019-05-16 12:47:20 -07001732 m := &structpb.Value{Kind: &structpb.Value_StringValue{"abcd"}}
Herbie Ong0b0f4032019-03-18 19:06:15 -07001733 b, err := proto.MarshalOptions{Deterministic: true}.Marshal(m)
1734 if err != nil {
1735 t.Fatalf("error in binary marshaling message for Any.value: %v", err)
1736 }
Joe Tsaia95b29f2019-05-16 12:47:20 -07001737 return &anypb.Any{
Herbie Ong0b0f4032019-03-18 19:06:15 -07001738 TypeUrl: "type.googleapis.com/google.protobuf.Value",
Damien Neilbc310b52019-04-11 11:46:55 -07001739 Value: bytes.Replace(b, []byte("abcd"), []byte("abc\xff"), -1),
Herbie Ong0b0f4032019-03-18 19:06:15 -07001740 }
1741 }(),
Herbie Ong0b0f4032019-03-18 19:06:15 -07001742 wantErr: true,
1743 }, {
Herbie Ong8ac9dd22019-03-27 12:20:50 -07001744 desc: "Any with Value of NullValue",
Damien Neil5c5b5312019-05-14 12:44:37 -07001745 mo: protojson.MarshalOptions{
Joe Tsaia95b29f2019-05-16 12:47:20 -07001746 Resolver: preg.NewTypes(pimpl.Export{}.MessageTypeOf(&structpb.Value{})),
Herbie Ong0b0f4032019-03-18 19:06:15 -07001747 },
1748 input: func() proto.Message {
Joe Tsaia95b29f2019-05-16 12:47:20 -07001749 m := &structpb.Value{Kind: &structpb.Value_NullValue{}}
Herbie Ong8ac9dd22019-03-27 12:20:50 -07001750 b, err := proto.MarshalOptions{Deterministic: true}.Marshal(m)
Herbie Ong0b0f4032019-03-18 19:06:15 -07001751 if err != nil {
1752 t.Fatalf("error in binary marshaling message for Any.value: %v", err)
1753 }
Joe Tsaia95b29f2019-05-16 12:47:20 -07001754 return &anypb.Any{
Herbie Ong0b0f4032019-03-18 19:06:15 -07001755 TypeUrl: "type.googleapis.com/google.protobuf.Value",
1756 Value: b,
1757 }
1758 }(),
Herbie Ong0b0f4032019-03-18 19:06:15 -07001759 want: `{
Herbie Ong8ac9dd22019-03-27 12:20:50 -07001760 "@type": "type.googleapis.com/google.protobuf.Value",
1761 "value": null
Herbie Ong0b0f4032019-03-18 19:06:15 -07001762}`,
1763 }, {
1764 desc: "Any with Struct",
Damien Neil5c5b5312019-05-14 12:44:37 -07001765 mo: protojson.MarshalOptions{
Herbie Ong0b0f4032019-03-18 19:06:15 -07001766 Resolver: preg.NewTypes(
Joe Tsaia95b29f2019-05-16 12:47:20 -07001767 pimpl.Export{}.MessageTypeOf(&structpb.Struct{}),
1768 pimpl.Export{}.MessageTypeOf(&structpb.Value{}),
1769 pimpl.Export{}.MessageTypeOf(&wrapperspb.BoolValue{}),
1770 pimpl.Export{}.EnumTypeOf(structpb.NullValue_NULL_VALUE),
1771 pimpl.Export{}.MessageTypeOf(&wrapperspb.StringValue{}),
Herbie Ong0b0f4032019-03-18 19:06:15 -07001772 ),
1773 },
1774 input: func() proto.Message {
Joe Tsaia95b29f2019-05-16 12:47:20 -07001775 m := &structpb.Struct{
1776 Fields: map[string]*structpb.Value{
1777 "bool": {Kind: &structpb.Value_BoolValue{true}},
1778 "null": {Kind: &structpb.Value_NullValue{}},
1779 "string": {Kind: &structpb.Value_StringValue{"hello"}},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001780 "struct": {
Joe Tsaia95b29f2019-05-16 12:47:20 -07001781 Kind: &structpb.Value_StructValue{
1782 &structpb.Struct{
1783 Fields: map[string]*structpb.Value{
1784 "string": {Kind: &structpb.Value_StringValue{"world"}},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001785 },
1786 },
1787 },
1788 },
1789 },
1790 }
1791 b, err := proto.MarshalOptions{Deterministic: true}.Marshal(m)
1792 if err != nil {
1793 t.Fatalf("error in binary marshaling message for Any.value: %v", err)
1794 }
Joe Tsaia95b29f2019-05-16 12:47:20 -07001795 return &anypb.Any{
Herbie Ong0b0f4032019-03-18 19:06:15 -07001796 TypeUrl: "google.protobuf.Struct",
1797 Value: b,
1798 }
1799 }(),
1800 want: `{
1801 "@type": "google.protobuf.Struct",
1802 "value": {
1803 "bool": true,
1804 "null": null,
1805 "string": "hello",
1806 "struct": {
1807 "string": "world"
1808 }
1809 }
1810}`,
1811 }, {
Herbie Ong8ac9dd22019-03-27 12:20:50 -07001812 desc: "Any with missing type_url",
Damien Neil5c5b5312019-05-14 12:44:37 -07001813 mo: protojson.MarshalOptions{
Joe Tsaia95b29f2019-05-16 12:47:20 -07001814 Resolver: preg.NewTypes(pimpl.Export{}.MessageTypeOf(&wrapperspb.BoolValue{})),
Herbie Ong8ac9dd22019-03-27 12:20:50 -07001815 },
1816 input: func() proto.Message {
Joe Tsaia95b29f2019-05-16 12:47:20 -07001817 m := &wrapperspb.BoolValue{Value: true}
Herbie Ong8ac9dd22019-03-27 12:20:50 -07001818 b, err := proto.MarshalOptions{Deterministic: true}.Marshal(m)
1819 if err != nil {
1820 t.Fatalf("error in binary marshaling message for Any.value: %v", err)
1821 }
Joe Tsaia95b29f2019-05-16 12:47:20 -07001822 return &anypb.Any{
Herbie Ong8ac9dd22019-03-27 12:20:50 -07001823 Value: b,
1824 }
1825 }(),
1826 wantErr: true,
1827 }, {
Herbie Ong0b0f4032019-03-18 19:06:15 -07001828 desc: "well known types as field values",
Damien Neil5c5b5312019-05-14 12:44:37 -07001829 mo: protojson.MarshalOptions{
Joe Tsaia95b29f2019-05-16 12:47:20 -07001830 Resolver: preg.NewTypes(pimpl.Export{}.MessageTypeOf(&emptypb.Empty{})),
Herbie Ong0b0f4032019-03-18 19:06:15 -07001831 },
1832 input: &pb2.KnownTypes{
Joe Tsaia95b29f2019-05-16 12:47:20 -07001833 OptBool: &wrapperspb.BoolValue{Value: false},
1834 OptInt32: &wrapperspb.Int32Value{Value: 42},
1835 OptInt64: &wrapperspb.Int64Value{Value: 42},
1836 OptUint32: &wrapperspb.UInt32Value{Value: 42},
1837 OptUint64: &wrapperspb.UInt64Value{Value: 42},
1838 OptFloat: &wrapperspb.FloatValue{Value: 1.23},
1839 OptDouble: &wrapperspb.DoubleValue{Value: 3.1415},
1840 OptString: &wrapperspb.StringValue{Value: "hello"},
1841 OptBytes: &wrapperspb.BytesValue{Value: []byte("hello")},
1842 OptDuration: &durationpb.Duration{Seconds: 123},
1843 OptTimestamp: &timestamppb.Timestamp{Seconds: 1553036601},
1844 OptStruct: &structpb.Struct{
1845 Fields: map[string]*structpb.Value{
1846 "string": {Kind: &structpb.Value_StringValue{"hello"}},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001847 },
1848 },
Joe Tsaia95b29f2019-05-16 12:47:20 -07001849 OptList: &structpb.ListValue{
1850 Values: []*structpb.Value{
1851 {Kind: &structpb.Value_NullValue{}},
1852 {Kind: &structpb.Value_StringValue{}},
1853 {Kind: &structpb.Value_StructValue{}},
1854 {Kind: &structpb.Value_ListValue{}},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001855 },
1856 },
Joe Tsaia95b29f2019-05-16 12:47:20 -07001857 OptValue: &structpb.Value{
1858 Kind: &structpb.Value_StringValue{"world"},
Herbie Ong1c7462c2019-03-22 17:56:55 -07001859 },
Joe Tsaia95b29f2019-05-16 12:47:20 -07001860 OptEmpty: &emptypb.Empty{},
1861 OptAny: &anypb.Any{
Herbie Ong0b0f4032019-03-18 19:06:15 -07001862 TypeUrl: "google.protobuf.Empty",
1863 },
Joe Tsaia95b29f2019-05-16 12:47:20 -07001864 OptFieldmask: &fieldmaskpb.FieldMask{
Herbie Ong0b0f4032019-03-18 19:06:15 -07001865 Paths: []string{"foo_bar", "bar_foo"},
1866 },
1867 },
1868 want: `{
1869 "optBool": false,
1870 "optInt32": 42,
1871 "optInt64": "42",
1872 "optUint32": 42,
1873 "optUint64": "42",
1874 "optFloat": 1.23,
1875 "optDouble": 3.1415,
1876 "optString": "hello",
1877 "optBytes": "aGVsbG8=",
1878 "optDuration": "123s",
1879 "optTimestamp": "2019-03-19T23:03:21Z",
1880 "optStruct": {
1881 "string": "hello"
1882 },
1883 "optList": [
1884 null,
1885 "",
1886 {},
1887 []
1888 ],
Herbie Ong1c7462c2019-03-22 17:56:55 -07001889 "optValue": "world",
Herbie Ong0b0f4032019-03-18 19:06:15 -07001890 "optEmpty": {},
1891 "optAny": {
Herbie Ong82014a52019-03-27 15:21:43 -07001892 "@type": "google.protobuf.Empty",
1893 "value": {}
Herbie Ong0b0f4032019-03-18 19:06:15 -07001894 },
1895 "optFieldmask": "fooBar,barFoo"
1896}`,
Herbie Ong7b828bc2019-02-08 19:56:24 -08001897 }}
1898
1899 for _, tt := range tests {
1900 tt := tt
1901 t.Run(tt.desc, func(t *testing.T) {
Herbie Ong0b0f4032019-03-18 19:06:15 -07001902 // Use 2-space indentation on all MarshalOptions.
1903 tt.mo.Indent = " "
Herbie Ong7b828bc2019-02-08 19:56:24 -08001904 b, err := tt.mo.Marshal(tt.input)
Herbie Ong0b0f4032019-03-18 19:06:15 -07001905 if err != nil && !tt.wantErr {
Herbie Ong7b828bc2019-02-08 19:56:24 -08001906 t.Errorf("Marshal() returned error: %v\n", err)
1907 }
Herbie Ong0b0f4032019-03-18 19:06:15 -07001908 if err == nil && tt.wantErr {
1909 t.Errorf("Marshal() got nil error, want error\n")
1910 }
Herbie Ong7b828bc2019-02-08 19:56:24 -08001911 got := string(b)
1912 if got != tt.want {
1913 t.Errorf("Marshal()\n<got>\n%v\n<want>\n%v\n", got, tt.want)
Joe Tsai6dc168e2019-07-09 23:11:13 -07001914 if diff := cmp.Diff(tt.want, got); diff != "" {
Herbie Ong7b828bc2019-02-08 19:56:24 -08001915 t.Errorf("Marshal() diff -want +got\n%v\n", diff)
1916 }
1917 }
1918 })
1919 }
1920}