blob: 2aa61dd6f02f05dedca1aa14926ecb0adc1814fa [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"
19 "google.golang.org/protobuf/runtime/protoiface"
Herbie Ong7b828bc2019-02-08 19:56:24 -080020
Damien Neile89e6242019-05-13 23:55:40 -070021 "google.golang.org/protobuf/encoding/testprotos/pb2"
22 "google.golang.org/protobuf/encoding/testprotos/pb3"
Joe Tsaia95b29f2019-05-16 12:47:20 -070023 "google.golang.org/protobuf/types/known/anypb"
24 "google.golang.org/protobuf/types/known/durationpb"
25 "google.golang.org/protobuf/types/known/emptypb"
26 "google.golang.org/protobuf/types/known/fieldmaskpb"
27 "google.golang.org/protobuf/types/known/structpb"
28 "google.golang.org/protobuf/types/known/timestamppb"
29 "google.golang.org/protobuf/types/known/wrapperspb"
Herbie Ong7b828bc2019-02-08 19:56:24 -080030)
31
Joe Tsai378c1322019-04-25 23:48:08 -070032// TODO: Replace this with proto.SetExtension.
Joe Tsai4fddeba2019-03-20 18:29:32 -070033func setExtension(m proto.Message, xd *protoiface.ExtensionDescV1, val interface{}) {
Joe Tsai378c1322019-04-25 23:48:08 -070034 m.ProtoReflect().Set(xd.Type, xd.Type.ValueOf(val))
Herbie Ongf83d5bb2019-03-14 00:01:27 -070035}
36
Herbie Ong7b828bc2019-02-08 19:56:24 -080037func TestMarshal(t *testing.T) {
38 tests := []struct {
Herbie Ong0b0f4032019-03-18 19:06:15 -070039 desc string
Damien Neil5c5b5312019-05-14 12:44:37 -070040 mo protojson.MarshalOptions
Herbie Ong0b0f4032019-03-18 19:06:15 -070041 input proto.Message
42 want string
43 wantErr bool // TODO: Verify error message substring.
Joe Tsai5ae10aa2019-07-11 18:23:08 -070044 skip bool
Herbie Ong7b828bc2019-02-08 19:56:24 -080045 }{{
46 desc: "proto2 optional scalars not set",
47 input: &pb2.Scalars{},
48 want: "{}",
49 }, {
50 desc: "proto3 scalars not set",
51 input: &pb3.Scalars{},
52 want: "{}",
53 }, {
54 desc: "proto2 optional scalars set to zero values",
55 input: &pb2.Scalars{
Damien Neila8a2cea2019-07-10 16:17:16 -070056 OptBool: proto.Bool(false),
57 OptInt32: proto.Int32(0),
58 OptInt64: proto.Int64(0),
59 OptUint32: proto.Uint32(0),
60 OptUint64: proto.Uint64(0),
61 OptSint32: proto.Int32(0),
62 OptSint64: proto.Int64(0),
63 OptFixed32: proto.Uint32(0),
64 OptFixed64: proto.Uint64(0),
65 OptSfixed32: proto.Int32(0),
66 OptSfixed64: proto.Int64(0),
67 OptFloat: proto.Float32(0),
68 OptDouble: proto.Float64(0),
Herbie Ong7b828bc2019-02-08 19:56:24 -080069 OptBytes: []byte{},
Damien Neila8a2cea2019-07-10 16:17:16 -070070 OptString: proto.String(""),
Herbie Ong7b828bc2019-02-08 19:56:24 -080071 },
72 want: `{
73 "optBool": false,
74 "optInt32": 0,
75 "optInt64": "0",
76 "optUint32": 0,
77 "optUint64": "0",
78 "optSint32": 0,
79 "optSint64": "0",
80 "optFixed32": 0,
81 "optFixed64": "0",
82 "optSfixed32": 0,
83 "optSfixed64": "0",
84 "optFloat": 0,
85 "optDouble": 0,
86 "optBytes": "",
87 "optString": ""
88}`,
89 }, {
90 desc: "proto2 optional scalars set to some values",
91 input: &pb2.Scalars{
Damien Neila8a2cea2019-07-10 16:17:16 -070092 OptBool: proto.Bool(true),
93 OptInt32: proto.Int32(0xff),
94 OptInt64: proto.Int64(0xdeadbeef),
95 OptUint32: proto.Uint32(47),
96 OptUint64: proto.Uint64(0xdeadbeef),
97 OptSint32: proto.Int32(-1001),
98 OptSint64: proto.Int64(-0xffff),
99 OptFixed64: proto.Uint64(64),
100 OptSfixed32: proto.Int32(-32),
101 OptFloat: proto.Float32(1.02),
102 OptDouble: proto.Float64(1.234),
Herbie Ong87608a72019-03-06 14:32:24 -0800103 OptBytes: []byte("谷歌"),
Damien Neila8a2cea2019-07-10 16:17:16 -0700104 OptString: proto.String("谷歌"),
Herbie Ong7b828bc2019-02-08 19:56:24 -0800105 },
106 want: `{
107 "optBool": true,
108 "optInt32": 255,
109 "optInt64": "3735928559",
110 "optUint32": 47,
111 "optUint64": "3735928559",
112 "optSint32": -1001,
113 "optSint64": "-65535",
114 "optFixed64": "64",
115 "optSfixed32": -32,
116 "optFloat": 1.02,
117 "optDouble": 1.234,
118 "optBytes": "6LC35q2M",
119 "optString": "谷歌"
120}`,
121 }, {
Herbie Ong0b0f4032019-03-18 19:06:15 -0700122 desc: "string",
123 input: &pb3.Scalars{
124 SString: "谷歌",
125 },
126 want: `{
127 "sString": "谷歌"
128}`,
129 }, {
130 desc: "string with invalid UTF8",
131 input: &pb3.Scalars{
132 SString: "abc\xff",
133 },
Herbie Ong0b0f4032019-03-18 19:06:15 -0700134 wantErr: true,
135 }, {
Herbie Ong7b828bc2019-02-08 19:56:24 -0800136 desc: "float nan",
137 input: &pb3.Scalars{
138 SFloat: float32(math.NaN()),
139 },
140 want: `{
141 "sFloat": "NaN"
142}`,
143 }, {
144 desc: "float positive infinity",
145 input: &pb3.Scalars{
146 SFloat: float32(math.Inf(1)),
147 },
148 want: `{
149 "sFloat": "Infinity"
150}`,
151 }, {
152 desc: "float negative infinity",
153 input: &pb3.Scalars{
154 SFloat: float32(math.Inf(-1)),
155 },
156 want: `{
157 "sFloat": "-Infinity"
158}`,
159 }, {
160 desc: "double nan",
161 input: &pb3.Scalars{
162 SDouble: math.NaN(),
163 },
164 want: `{
165 "sDouble": "NaN"
166}`,
167 }, {
168 desc: "double positive infinity",
169 input: &pb3.Scalars{
170 SDouble: math.Inf(1),
171 },
172 want: `{
173 "sDouble": "Infinity"
174}`,
175 }, {
176 desc: "double negative infinity",
177 input: &pb3.Scalars{
178 SDouble: math.Inf(-1),
179 },
180 want: `{
181 "sDouble": "-Infinity"
182}`,
183 }, {
184 desc: "proto2 enum not set",
185 input: &pb2.Enums{},
186 want: "{}",
187 }, {
188 desc: "proto2 enum set to zero value",
189 input: &pb2.Enums{
Joe Tsai6dc168e2019-07-09 23:11:13 -0700190 OptEnum: pb2.Enum(0).Enum(),
191 OptNestedEnum: pb2.Enums_NestedEnum(0).Enum(),
Herbie Ong7b828bc2019-02-08 19:56:24 -0800192 },
193 want: `{
194 "optEnum": 0,
195 "optNestedEnum": 0
196}`,
197 }, {
198 desc: "proto2 enum",
199 input: &pb2.Enums{
200 OptEnum: pb2.Enum_ONE.Enum(),
201 OptNestedEnum: pb2.Enums_UNO.Enum(),
202 },
203 want: `{
204 "optEnum": "ONE",
205 "optNestedEnum": "UNO"
206}`,
207 }, {
208 desc: "proto2 enum set to numeric values",
209 input: &pb2.Enums{
Joe Tsai6dc168e2019-07-09 23:11:13 -0700210 OptEnum: pb2.Enum(2).Enum(),
211 OptNestedEnum: pb2.Enums_NestedEnum(2).Enum(),
Herbie Ong7b828bc2019-02-08 19:56:24 -0800212 },
213 want: `{
214 "optEnum": "TWO",
215 "optNestedEnum": "DOS"
216}`,
217 }, {
218 desc: "proto2 enum set to unnamed numeric values",
219 input: &pb2.Enums{
Joe Tsai6dc168e2019-07-09 23:11:13 -0700220 OptEnum: pb2.Enum(101).Enum(),
221 OptNestedEnum: pb2.Enums_NestedEnum(-101).Enum(),
Herbie Ong7b828bc2019-02-08 19:56:24 -0800222 },
223 want: `{
224 "optEnum": 101,
225 "optNestedEnum": -101
226}`,
227 }, {
228 desc: "proto3 enum not set",
229 input: &pb3.Enums{},
230 want: "{}",
231 }, {
232 desc: "proto3 enum set to zero value",
233 input: &pb3.Enums{
234 SEnum: pb3.Enum_ZERO,
235 SNestedEnum: pb3.Enums_CERO,
236 },
237 want: "{}",
238 }, {
239 desc: "proto3 enum",
240 input: &pb3.Enums{
241 SEnum: pb3.Enum_ONE,
242 SNestedEnum: pb3.Enums_UNO,
243 },
244 want: `{
245 "sEnum": "ONE",
246 "sNestedEnum": "UNO"
247}`,
248 }, {
249 desc: "proto3 enum set to numeric values",
250 input: &pb3.Enums{
251 SEnum: 2,
252 SNestedEnum: 2,
253 },
254 want: `{
255 "sEnum": "TWO",
256 "sNestedEnum": "DOS"
257}`,
258 }, {
259 desc: "proto3 enum set to unnamed numeric values",
260 input: &pb3.Enums{
261 SEnum: -47,
262 SNestedEnum: 47,
263 },
264 want: `{
265 "sEnum": -47,
266 "sNestedEnum": 47
267}`,
268 }, {
269 desc: "proto2 nested message not set",
270 input: &pb2.Nests{},
271 want: "{}",
272 }, {
273 desc: "proto2 nested message set to empty",
274 input: &pb2.Nests{
275 OptNested: &pb2.Nested{},
276 Optgroup: &pb2.Nests_OptGroup{},
277 },
278 want: `{
279 "optNested": {},
280 "optgroup": {}
281}`,
282 }, {
283 desc: "proto2 nested messages",
284 input: &pb2.Nests{
285 OptNested: &pb2.Nested{
Damien Neila8a2cea2019-07-10 16:17:16 -0700286 OptString: proto.String("nested message"),
Herbie Ong7b828bc2019-02-08 19:56:24 -0800287 OptNested: &pb2.Nested{
Damien Neila8a2cea2019-07-10 16:17:16 -0700288 OptString: proto.String("another nested message"),
Herbie Ong7b828bc2019-02-08 19:56:24 -0800289 },
290 },
291 },
292 want: `{
293 "optNested": {
294 "optString": "nested message",
295 "optNested": {
296 "optString": "another nested message"
297 }
298 }
299}`,
300 }, {
301 desc: "proto2 groups",
302 input: &pb2.Nests{
303 Optgroup: &pb2.Nests_OptGroup{
Damien Neila8a2cea2019-07-10 16:17:16 -0700304 OptString: proto.String("inside a group"),
Herbie Ong7b828bc2019-02-08 19:56:24 -0800305 OptNested: &pb2.Nested{
Damien Neila8a2cea2019-07-10 16:17:16 -0700306 OptString: proto.String("nested message inside a group"),
Herbie Ong7b828bc2019-02-08 19:56:24 -0800307 },
308 Optnestedgroup: &pb2.Nests_OptGroup_OptNestedGroup{
Damien Neila8a2cea2019-07-10 16:17:16 -0700309 OptFixed32: proto.Uint32(47),
Herbie Ong7b828bc2019-02-08 19:56:24 -0800310 },
311 },
312 },
313 want: `{
314 "optgroup": {
315 "optString": "inside a group",
316 "optNested": {
317 "optString": "nested message inside a group"
318 },
319 "optnestedgroup": {
320 "optFixed32": 47
321 }
322 }
323}`,
324 }, {
325 desc: "proto3 nested message not set",
326 input: &pb3.Nests{},
327 want: "{}",
328 }, {
329 desc: "proto3 nested message set to empty",
330 input: &pb3.Nests{
331 SNested: &pb3.Nested{},
332 },
333 want: `{
334 "sNested": {}
335}`,
336 }, {
337 desc: "proto3 nested message",
338 input: &pb3.Nests{
339 SNested: &pb3.Nested{
340 SString: "nested message",
341 SNested: &pb3.Nested{
342 SString: "another nested message",
343 },
344 },
345 },
346 want: `{
347 "sNested": {
348 "sString": "nested message",
349 "sNested": {
350 "sString": "another nested message"
351 }
352 }
353}`,
354 }, {
355 desc: "oneof not set",
356 input: &pb3.Oneofs{},
357 want: "{}",
358 }, {
359 desc: "oneof set to empty string",
360 input: &pb3.Oneofs{
361 Union: &pb3.Oneofs_OneofString{},
362 },
363 want: `{
364 "oneofString": ""
365}`,
366 }, {
367 desc: "oneof set to string",
368 input: &pb3.Oneofs{
369 Union: &pb3.Oneofs_OneofString{
370 OneofString: "hello",
371 },
372 },
373 want: `{
374 "oneofString": "hello"
375}`,
376 }, {
377 desc: "oneof set to enum",
378 input: &pb3.Oneofs{
379 Union: &pb3.Oneofs_OneofEnum{
380 OneofEnum: pb3.Enum_ZERO,
381 },
382 },
383 want: `{
384 "oneofEnum": "ZERO"
385}`,
386 }, {
387 desc: "oneof set to empty message",
388 input: &pb3.Oneofs{
389 Union: &pb3.Oneofs_OneofNested{
390 OneofNested: &pb3.Nested{},
391 },
392 },
393 want: `{
394 "oneofNested": {}
395}`,
396 }, {
397 desc: "oneof set to message",
398 input: &pb3.Oneofs{
399 Union: &pb3.Oneofs_OneofNested{
400 OneofNested: &pb3.Nested{
401 SString: "nested message",
402 },
403 },
404 },
405 want: `{
406 "oneofNested": {
407 "sString": "nested message"
408 }
409}`,
410 }, {
411 desc: "repeated fields not set",
412 input: &pb2.Repeats{},
413 want: "{}",
414 }, {
415 desc: "repeated fields set to empty slices",
416 input: &pb2.Repeats{
417 RptBool: []bool{},
418 RptInt32: []int32{},
419 RptInt64: []int64{},
420 RptUint32: []uint32{},
421 RptUint64: []uint64{},
422 RptFloat: []float32{},
423 RptDouble: []float64{},
424 RptBytes: [][]byte{},
425 },
426 want: "{}",
427 }, {
428 desc: "repeated fields set to some values",
429 input: &pb2.Repeats{
430 RptBool: []bool{true, false, true, true},
431 RptInt32: []int32{1, 6, 0, 0},
432 RptInt64: []int64{-64, 47},
433 RptUint32: []uint32{0xff, 0xffff},
434 RptUint64: []uint64{0xdeadbeef},
435 RptFloat: []float32{float32(math.NaN()), float32(math.Inf(1)), float32(math.Inf(-1)), 1.034},
436 RptDouble: []float64{math.NaN(), math.Inf(1), math.Inf(-1), 1.23e-308},
437 RptString: []string{"hello", "世界"},
438 RptBytes: [][]byte{
439 []byte("hello"),
440 []byte("\xe4\xb8\x96\xe7\x95\x8c"),
441 },
442 },
443 want: `{
444 "rptBool": [
445 true,
446 false,
447 true,
448 true
449 ],
450 "rptInt32": [
451 1,
452 6,
453 0,
454 0
455 ],
456 "rptInt64": [
457 "-64",
458 "47"
459 ],
460 "rptUint32": [
461 255,
462 65535
463 ],
464 "rptUint64": [
465 "3735928559"
466 ],
467 "rptFloat": [
468 "NaN",
469 "Infinity",
470 "-Infinity",
471 1.034
472 ],
473 "rptDouble": [
474 "NaN",
475 "Infinity",
476 "-Infinity",
477 1.23e-308
478 ],
479 "rptString": [
480 "hello",
481 "世界"
482 ],
483 "rptBytes": [
484 "aGVsbG8=",
485 "5LiW55WM"
486 ]
487}`,
488 }, {
489 desc: "repeated enums",
490 input: &pb2.Enums{
491 RptEnum: []pb2.Enum{pb2.Enum_ONE, 2, pb2.Enum_TEN, 42},
492 RptNestedEnum: []pb2.Enums_NestedEnum{2, 47, 10},
493 },
494 want: `{
495 "rptEnum": [
496 "ONE",
497 "TWO",
498 "TEN",
499 42
500 ],
501 "rptNestedEnum": [
502 "DOS",
503 47,
504 "DIEZ"
505 ]
506}`,
507 }, {
508 desc: "repeated messages set to empty",
509 input: &pb2.Nests{
510 RptNested: []*pb2.Nested{},
511 Rptgroup: []*pb2.Nests_RptGroup{},
512 },
513 want: "{}",
514 }, {
515 desc: "repeated messages",
516 input: &pb2.Nests{
517 RptNested: []*pb2.Nested{
518 {
Damien Neila8a2cea2019-07-10 16:17:16 -0700519 OptString: proto.String("repeat nested one"),
Herbie Ong7b828bc2019-02-08 19:56:24 -0800520 },
521 {
Damien Neila8a2cea2019-07-10 16:17:16 -0700522 OptString: proto.String("repeat nested two"),
Herbie Ong7b828bc2019-02-08 19:56:24 -0800523 OptNested: &pb2.Nested{
Damien Neila8a2cea2019-07-10 16:17:16 -0700524 OptString: proto.String("inside repeat nested two"),
Herbie Ong7b828bc2019-02-08 19:56:24 -0800525 },
526 },
527 {},
528 },
529 },
530 want: `{
531 "rptNested": [
532 {
533 "optString": "repeat nested one"
534 },
535 {
536 "optString": "repeat nested two",
537 "optNested": {
538 "optString": "inside repeat nested two"
539 }
540 },
541 {}
542 ]
543}`,
544 }, {
545 desc: "repeated messages contains nil value",
546 input: &pb2.Nests{
547 RptNested: []*pb2.Nested{nil, {}},
548 },
549 want: `{
550 "rptNested": [
551 {},
552 {}
553 ]
554}`,
555 }, {
556 desc: "repeated groups",
557 input: &pb2.Nests{
558 Rptgroup: []*pb2.Nests_RptGroup{
559 {
560 RptString: []string{"hello", "world"},
561 },
562 {},
563 nil,
564 },
565 },
566 want: `{
567 "rptgroup": [
568 {
569 "rptString": [
570 "hello",
571 "world"
572 ]
573 },
574 {},
575 {}
576 ]
577}`,
578 }, {
579 desc: "map fields not set",
580 input: &pb3.Maps{},
581 want: "{}",
582 }, {
583 desc: "map fields set to empty",
584 input: &pb3.Maps{
585 Int32ToStr: map[int32]string{},
586 BoolToUint32: map[bool]uint32{},
587 Uint64ToEnum: map[uint64]pb3.Enum{},
588 StrToNested: map[string]*pb3.Nested{},
589 StrToOneofs: map[string]*pb3.Oneofs{},
590 },
591 want: "{}",
592 }, {
593 desc: "map fields 1",
594 input: &pb3.Maps{
595 BoolToUint32: map[bool]uint32{
596 true: 42,
597 false: 101,
598 },
599 },
600 want: `{
601 "boolToUint32": {
602 "false": 101,
603 "true": 42
604 }
605}`,
606 }, {
607 desc: "map fields 2",
608 input: &pb3.Maps{
609 Int32ToStr: map[int32]string{
610 -101: "-101",
611 0xff: "0xff",
612 0: "zero",
613 },
614 },
615 want: `{
616 "int32ToStr": {
617 "-101": "-101",
618 "0": "zero",
619 "255": "0xff"
620 }
621}`,
622 }, {
623 desc: "map fields 3",
624 input: &pb3.Maps{
625 Uint64ToEnum: map[uint64]pb3.Enum{
626 1: pb3.Enum_ONE,
627 2: pb3.Enum_TWO,
628 10: pb3.Enum_TEN,
629 47: 47,
630 },
631 },
632 want: `{
633 "uint64ToEnum": {
634 "1": "ONE",
635 "2": "TWO",
636 "10": "TEN",
637 "47": 47
638 }
639}`,
640 }, {
641 desc: "map fields 4",
642 input: &pb3.Maps{
643 StrToNested: map[string]*pb3.Nested{
644 "nested": &pb3.Nested{
645 SString: "nested in a map",
646 },
647 },
648 },
649 want: `{
650 "strToNested": {
651 "nested": {
652 "sString": "nested in a map"
653 }
654 }
655}`,
656 }, {
657 desc: "map fields 5",
658 input: &pb3.Maps{
659 StrToOneofs: map[string]*pb3.Oneofs{
660 "string": &pb3.Oneofs{
661 Union: &pb3.Oneofs_OneofString{
662 OneofString: "hello",
663 },
664 },
665 "nested": &pb3.Oneofs{
666 Union: &pb3.Oneofs_OneofNested{
667 OneofNested: &pb3.Nested{
668 SString: "nested oneof in map field value",
669 },
670 },
671 },
672 },
673 },
674 want: `{
675 "strToOneofs": {
676 "nested": {
677 "oneofNested": {
678 "sString": "nested oneof in map field value"
679 }
680 },
681 "string": {
682 "oneofString": "hello"
683 }
684 }
685}`,
686 }, {
687 desc: "map field contains nil value",
688 input: &pb3.Maps{
689 StrToNested: map[string]*pb3.Nested{
690 "nil": nil,
691 },
692 },
693 want: `{
694 "strToNested": {
695 "nil": {}
696 }
697}`,
698 }, {
Herbie Ong329be5b2019-03-27 14:47:59 -0700699 desc: "required fields not set",
700 input: &pb2.Requireds{},
701 want: `{}`,
702 wantErr: true,
703 }, {
704 desc: "required fields partially set",
705 input: &pb2.Requireds{
Damien Neila8a2cea2019-07-10 16:17:16 -0700706 ReqBool: proto.Bool(false),
707 ReqSfixed64: proto.Int64(0),
708 ReqDouble: proto.Float64(1.23),
709 ReqString: proto.String("hello"),
Herbie Ong329be5b2019-03-27 14:47:59 -0700710 ReqEnum: pb2.Enum_ONE.Enum(),
711 },
712 want: `{
713 "reqBool": false,
714 "reqSfixed64": "0",
715 "reqDouble": 1.23,
716 "reqString": "hello",
717 "reqEnum": "ONE"
718}`,
719 wantErr: true,
720 }, {
721 desc: "required fields not set with AllowPartial",
Damien Neil5c5b5312019-05-14 12:44:37 -0700722 mo: protojson.MarshalOptions{AllowPartial: true},
Herbie Ong329be5b2019-03-27 14:47:59 -0700723 input: &pb2.Requireds{
Damien Neila8a2cea2019-07-10 16:17:16 -0700724 ReqBool: proto.Bool(false),
725 ReqSfixed64: proto.Int64(0),
726 ReqDouble: proto.Float64(1.23),
727 ReqString: proto.String("hello"),
Herbie Ong329be5b2019-03-27 14:47:59 -0700728 ReqEnum: pb2.Enum_ONE.Enum(),
729 },
730 want: `{
731 "reqBool": false,
732 "reqSfixed64": "0",
733 "reqDouble": 1.23,
734 "reqString": "hello",
735 "reqEnum": "ONE"
736}`,
737 }, {
738 desc: "required fields all set",
739 input: &pb2.Requireds{
Damien Neila8a2cea2019-07-10 16:17:16 -0700740 ReqBool: proto.Bool(false),
741 ReqSfixed64: proto.Int64(0),
742 ReqDouble: proto.Float64(1.23),
743 ReqString: proto.String("hello"),
Herbie Ong329be5b2019-03-27 14:47:59 -0700744 ReqEnum: pb2.Enum_ONE.Enum(),
745 ReqNested: &pb2.Nested{},
746 },
747 want: `{
748 "reqBool": false,
749 "reqSfixed64": "0",
750 "reqDouble": 1.23,
751 "reqString": "hello",
752 "reqEnum": "ONE",
753 "reqNested": {}
754}`,
755 }, {
756 desc: "indirect required field",
757 input: &pb2.IndirectRequired{
758 OptNested: &pb2.NestedWithRequired{},
759 },
760 want: `{
761 "optNested": {}
762}`,
763 wantErr: true,
764 }, {
765 desc: "indirect required field with AllowPartial",
Damien Neil5c5b5312019-05-14 12:44:37 -0700766 mo: protojson.MarshalOptions{AllowPartial: true},
Herbie Ong329be5b2019-03-27 14:47:59 -0700767 input: &pb2.IndirectRequired{
768 OptNested: &pb2.NestedWithRequired{},
769 },
770 want: `{
771 "optNested": {}
772}`,
773 }, {
774 desc: "indirect required field in empty repeated",
775 input: &pb2.IndirectRequired{
776 RptNested: []*pb2.NestedWithRequired{},
777 },
778 want: `{}`,
779 }, {
780 desc: "indirect required field in repeated",
781 input: &pb2.IndirectRequired{
782 RptNested: []*pb2.NestedWithRequired{
783 &pb2.NestedWithRequired{},
784 },
785 },
786 want: `{
787 "rptNested": [
788 {}
789 ]
790}`,
791 wantErr: true,
792 }, {
793 desc: "indirect required field in repeated with AllowPartial",
Damien Neil5c5b5312019-05-14 12:44:37 -0700794 mo: protojson.MarshalOptions{AllowPartial: true},
Herbie Ong329be5b2019-03-27 14:47:59 -0700795 input: &pb2.IndirectRequired{
796 RptNested: []*pb2.NestedWithRequired{
797 &pb2.NestedWithRequired{},
798 },
799 },
800 want: `{
801 "rptNested": [
802 {}
803 ]
804}`,
805 }, {
806 desc: "indirect required field in empty map",
807 input: &pb2.IndirectRequired{
808 StrToNested: map[string]*pb2.NestedWithRequired{},
809 },
810 want: "{}",
811 }, {
812 desc: "indirect required field in map",
813 input: &pb2.IndirectRequired{
814 StrToNested: map[string]*pb2.NestedWithRequired{
815 "fail": &pb2.NestedWithRequired{},
816 },
817 },
818 want: `{
819 "strToNested": {
820 "fail": {}
821 }
822}`,
823 wantErr: true,
824 }, {
825 desc: "indirect required field in map with AllowPartial",
Damien Neil5c5b5312019-05-14 12:44:37 -0700826 mo: protojson.MarshalOptions{AllowPartial: true},
Herbie Ong329be5b2019-03-27 14:47:59 -0700827 input: &pb2.IndirectRequired{
828 StrToNested: map[string]*pb2.NestedWithRequired{
829 "fail": &pb2.NestedWithRequired{},
830 },
831 },
832 want: `{
833 "strToNested": {
834 "fail": {}
835 }
836}`,
837 }, {
838 desc: "indirect required field in oneof",
839 input: &pb2.IndirectRequired{
840 Union: &pb2.IndirectRequired_OneofNested{
841 OneofNested: &pb2.NestedWithRequired{},
842 },
843 },
844 want: `{
845 "oneofNested": {}
846}`,
847 wantErr: true,
848 }, {
849 desc: "indirect required field in oneof with AllowPartial",
Damien Neil5c5b5312019-05-14 12:44:37 -0700850 mo: protojson.MarshalOptions{AllowPartial: true},
Herbie Ong329be5b2019-03-27 14:47:59 -0700851 input: &pb2.IndirectRequired{
852 Union: &pb2.IndirectRequired_OneofNested{
853 OneofNested: &pb2.NestedWithRequired{},
854 },
855 },
856 want: `{
857 "oneofNested": {}
858}`,
859 }, {
Herbie Ong7b828bc2019-02-08 19:56:24 -0800860 desc: "unknown fields are ignored",
Joe Tsai28216c72019-06-22 13:20:09 -0700861 input: func() proto.Message {
862 m := &pb2.Scalars{
Damien Neila8a2cea2019-07-10 16:17:16 -0700863 OptString: proto.String("no unknowns"),
Joe Tsai28216c72019-06-22 13:20:09 -0700864 }
865 m.ProtoReflect().SetUnknown(pack.Message{
Herbie Ong7b828bc2019-02-08 19:56:24 -0800866 pack.Tag{101, pack.BytesType}, pack.String("hello world"),
Joe Tsai28216c72019-06-22 13:20:09 -0700867 }.Marshal())
868 return m
869 }(),
Herbie Ong7b828bc2019-02-08 19:56:24 -0800870 want: `{
871 "optString": "no unknowns"
872}`,
873 }, {
874 desc: "json_name",
875 input: &pb3.JSONNames{
876 SString: "json_name",
877 },
878 want: `{
879 "foo_bar": "json_name"
880}`,
Herbie Ongf83d5bb2019-03-14 00:01:27 -0700881 }, {
882 desc: "extensions of non-repeated fields",
883 input: func() proto.Message {
884 m := &pb2.Extensions{
Damien Neila8a2cea2019-07-10 16:17:16 -0700885 OptString: proto.String("non-extension field"),
886 OptBool: proto.Bool(true),
887 OptInt32: proto.Int32(42),
Herbie Ongf83d5bb2019-03-14 00:01:27 -0700888 }
889 setExtension(m, pb2.E_OptExtBool, true)
890 setExtension(m, pb2.E_OptExtString, "extension field")
891 setExtension(m, pb2.E_OptExtEnum, pb2.Enum_TEN)
892 setExtension(m, pb2.E_OptExtNested, &pb2.Nested{
Damien Neila8a2cea2019-07-10 16:17:16 -0700893 OptString: proto.String("nested in an extension"),
Herbie Ongf83d5bb2019-03-14 00:01:27 -0700894 OptNested: &pb2.Nested{
Damien Neila8a2cea2019-07-10 16:17:16 -0700895 OptString: proto.String("another nested in an extension"),
Herbie Ongf83d5bb2019-03-14 00:01:27 -0700896 },
897 })
898 return m
899 }(),
900 want: `{
901 "optString": "non-extension field",
902 "optBool": true,
903 "optInt32": 42,
904 "[pb2.opt_ext_bool]": true,
905 "[pb2.opt_ext_enum]": "TEN",
906 "[pb2.opt_ext_nested]": {
907 "optString": "nested in an extension",
908 "optNested": {
909 "optString": "another nested in an extension"
910 }
911 },
912 "[pb2.opt_ext_string]": "extension field"
913}`,
914 }, {
Herbie Ongf83d5bb2019-03-14 00:01:27 -0700915 desc: "extensions of repeated fields",
916 input: func() proto.Message {
917 m := &pb2.Extensions{}
918 setExtension(m, pb2.E_RptExtEnum, &[]pb2.Enum{pb2.Enum_TEN, 101, pb2.Enum_ONE})
919 setExtension(m, pb2.E_RptExtFixed32, &[]uint32{42, 47})
920 setExtension(m, pb2.E_RptExtNested, &[]*pb2.Nested{
Damien Neila8a2cea2019-07-10 16:17:16 -0700921 &pb2.Nested{OptString: proto.String("one")},
922 &pb2.Nested{OptString: proto.String("two")},
923 &pb2.Nested{OptString: proto.String("three")},
Herbie Ongf83d5bb2019-03-14 00:01:27 -0700924 })
925 return m
926 }(),
927 want: `{
928 "[pb2.rpt_ext_enum]": [
929 "TEN",
930 101,
931 "ONE"
932 ],
933 "[pb2.rpt_ext_fixed32]": [
934 42,
935 47
936 ],
937 "[pb2.rpt_ext_nested]": [
938 {
939 "optString": "one"
940 },
941 {
942 "optString": "two"
943 },
944 {
945 "optString": "three"
946 }
947 ]
948}`,
949 }, {
950 desc: "extensions of non-repeated fields in another message",
951 input: func() proto.Message {
952 m := &pb2.Extensions{}
953 setExtension(m, pb2.E_ExtensionsContainer_OptExtBool, true)
954 setExtension(m, pb2.E_ExtensionsContainer_OptExtString, "extension field")
955 setExtension(m, pb2.E_ExtensionsContainer_OptExtEnum, pb2.Enum_TEN)
956 setExtension(m, pb2.E_ExtensionsContainer_OptExtNested, &pb2.Nested{
Damien Neila8a2cea2019-07-10 16:17:16 -0700957 OptString: proto.String("nested in an extension"),
Herbie Ongf83d5bb2019-03-14 00:01:27 -0700958 OptNested: &pb2.Nested{
Damien Neila8a2cea2019-07-10 16:17:16 -0700959 OptString: proto.String("another nested in an extension"),
Herbie Ongf83d5bb2019-03-14 00:01:27 -0700960 },
961 })
962 return m
963 }(),
964 want: `{
965 "[pb2.ExtensionsContainer.opt_ext_bool]": true,
966 "[pb2.ExtensionsContainer.opt_ext_enum]": "TEN",
967 "[pb2.ExtensionsContainer.opt_ext_nested]": {
968 "optString": "nested in an extension",
969 "optNested": {
970 "optString": "another nested in an extension"
971 }
972 },
973 "[pb2.ExtensionsContainer.opt_ext_string]": "extension field"
974}`,
975 }, {
976 desc: "extensions of repeated fields in another message",
977 input: func() proto.Message {
978 m := &pb2.Extensions{
Damien Neila8a2cea2019-07-10 16:17:16 -0700979 OptString: proto.String("non-extension field"),
980 OptBool: proto.Bool(true),
981 OptInt32: proto.Int32(42),
Herbie Ongf83d5bb2019-03-14 00:01:27 -0700982 }
983 setExtension(m, pb2.E_ExtensionsContainer_RptExtEnum, &[]pb2.Enum{pb2.Enum_TEN, 101, pb2.Enum_ONE})
984 setExtension(m, pb2.E_ExtensionsContainer_RptExtString, &[]string{"hello", "world"})
985 setExtension(m, pb2.E_ExtensionsContainer_RptExtNested, &[]*pb2.Nested{
Damien Neila8a2cea2019-07-10 16:17:16 -0700986 &pb2.Nested{OptString: proto.String("one")},
987 &pb2.Nested{OptString: proto.String("two")},
988 &pb2.Nested{OptString: proto.String("three")},
Herbie Ongf83d5bb2019-03-14 00:01:27 -0700989 })
990 return m
991 }(),
992 want: `{
993 "optString": "non-extension field",
994 "optBool": true,
995 "optInt32": 42,
996 "[pb2.ExtensionsContainer.rpt_ext_enum]": [
997 "TEN",
998 101,
999 "ONE"
1000 ],
1001 "[pb2.ExtensionsContainer.rpt_ext_nested]": [
1002 {
1003 "optString": "one"
1004 },
1005 {
1006 "optString": "two"
1007 },
1008 {
1009 "optString": "three"
1010 }
1011 ],
1012 "[pb2.ExtensionsContainer.rpt_ext_string]": [
1013 "hello",
1014 "world"
1015 ]
1016}`,
1017 }, {
1018 desc: "MessageSet",
1019 input: func() proto.Message {
1020 m := &pb2.MessageSet{}
1021 setExtension(m, pb2.E_MessageSetExtension_MessageSetExtension, &pb2.MessageSetExtension{
Damien Neila8a2cea2019-07-10 16:17:16 -07001022 OptString: proto.String("a messageset extension"),
Herbie Ongf83d5bb2019-03-14 00:01:27 -07001023 })
1024 setExtension(m, pb2.E_MessageSetExtension_NotMessageSetExtension, &pb2.MessageSetExtension{
Damien Neila8a2cea2019-07-10 16:17:16 -07001025 OptString: proto.String("not a messageset extension"),
Herbie Ongf83d5bb2019-03-14 00:01:27 -07001026 })
1027 setExtension(m, pb2.E_MessageSetExtension_ExtNested, &pb2.Nested{
Damien Neila8a2cea2019-07-10 16:17:16 -07001028 OptString: proto.String("just a regular extension"),
Herbie Ongf83d5bb2019-03-14 00:01:27 -07001029 })
1030 return m
1031 }(),
1032 want: `{
1033 "[pb2.MessageSetExtension]": {
1034 "optString": "a messageset extension"
1035 },
1036 "[pb2.MessageSetExtension.ext_nested]": {
1037 "optString": "just a regular extension"
1038 },
1039 "[pb2.MessageSetExtension.not_message_set_extension]": {
1040 "optString": "not a messageset extension"
1041 }
1042}`,
Joe Tsai5ae10aa2019-07-11 18:23:08 -07001043 skip: !flags.Proto1Legacy,
Herbie Ongf83d5bb2019-03-14 00:01:27 -07001044 }, {
1045 desc: "not real MessageSet 1",
1046 input: func() proto.Message {
1047 m := &pb2.FakeMessageSet{}
1048 setExtension(m, pb2.E_FakeMessageSetExtension_MessageSetExtension, &pb2.FakeMessageSetExtension{
Damien Neila8a2cea2019-07-10 16:17:16 -07001049 OptString: proto.String("not a messageset extension"),
Herbie Ongf83d5bb2019-03-14 00:01:27 -07001050 })
1051 return m
1052 }(),
1053 want: `{
1054 "[pb2.FakeMessageSetExtension.message_set_extension]": {
1055 "optString": "not a messageset extension"
1056 }
1057}`,
Joe Tsai5ae10aa2019-07-11 18:23:08 -07001058 skip: !flags.Proto1Legacy,
Herbie Ongf83d5bb2019-03-14 00:01:27 -07001059 }, {
1060 desc: "not real MessageSet 2",
1061 input: func() proto.Message {
1062 m := &pb2.MessageSet{}
1063 setExtension(m, pb2.E_MessageSetExtension, &pb2.FakeMessageSetExtension{
Damien Neila8a2cea2019-07-10 16:17:16 -07001064 OptString: proto.String("another not a messageset extension"),
Herbie Ongf83d5bb2019-03-14 00:01:27 -07001065 })
1066 return m
1067 }(),
1068 want: `{
1069 "[pb2.message_set_extension]": {
1070 "optString": "another not a messageset extension"
1071 }
1072}`,
Joe Tsai5ae10aa2019-07-11 18:23:08 -07001073 skip: !flags.Proto1Legacy,
Herbie Ong0b0f4032019-03-18 19:06:15 -07001074 }, {
1075 desc: "BoolValue empty",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001076 input: &wrapperspb.BoolValue{},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001077 want: `false`,
1078 }, {
1079 desc: "BoolValue",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001080 input: &wrapperspb.BoolValue{Value: true},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001081 want: `true`,
1082 }, {
1083 desc: "Int32Value empty",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001084 input: &wrapperspb.Int32Value{},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001085 want: `0`,
1086 }, {
1087 desc: "Int32Value",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001088 input: &wrapperspb.Int32Value{Value: 42},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001089 want: `42`,
1090 }, {
1091 desc: "Int64Value",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001092 input: &wrapperspb.Int64Value{Value: 42},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001093 want: `"42"`,
1094 }, {
1095 desc: "UInt32Value",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001096 input: &wrapperspb.UInt32Value{Value: 42},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001097 want: `42`,
1098 }, {
1099 desc: "UInt64Value",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001100 input: &wrapperspb.UInt64Value{Value: 42},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001101 want: `"42"`,
1102 }, {
1103 desc: "FloatValue",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001104 input: &wrapperspb.FloatValue{Value: 1.02},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001105 want: `1.02`,
1106 }, {
Herbie Onge63c4c42019-03-22 22:20:22 -07001107 desc: "FloatValue Infinity",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001108 input: &wrapperspb.FloatValue{Value: float32(math.Inf(-1))},
Herbie Onge63c4c42019-03-22 22:20:22 -07001109 want: `"-Infinity"`,
1110 }, {
Herbie Ong0b0f4032019-03-18 19:06:15 -07001111 desc: "DoubleValue",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001112 input: &wrapperspb.DoubleValue{Value: 1.02},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001113 want: `1.02`,
1114 }, {
Herbie Onge63c4c42019-03-22 22:20:22 -07001115 desc: "DoubleValue NaN",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001116 input: &wrapperspb.DoubleValue{Value: math.NaN()},
Herbie Onge63c4c42019-03-22 22:20:22 -07001117 want: `"NaN"`,
1118 }, {
Herbie Ong0b0f4032019-03-18 19:06:15 -07001119 desc: "StringValue empty",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001120 input: &wrapperspb.StringValue{},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001121 want: `""`,
1122 }, {
1123 desc: "StringValue",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001124 input: &wrapperspb.StringValue{Value: "谷歌"},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001125 want: `"谷歌"`,
1126 }, {
1127 desc: "StringValue with invalid UTF8 error",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001128 input: &wrapperspb.StringValue{Value: "abc\xff"},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001129 wantErr: true,
1130 }, {
1131 desc: "StringValue field with invalid UTF8 error",
1132 input: &pb2.KnownTypes{
Joe Tsaia95b29f2019-05-16 12:47:20 -07001133 OptString: &wrapperspb.StringValue{Value: "abc\xff"},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001134 },
Herbie Ong0b0f4032019-03-18 19:06:15 -07001135 wantErr: true,
1136 }, {
1137 desc: "BytesValue",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001138 input: &wrapperspb.BytesValue{Value: []byte("hello")},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001139 want: `"aGVsbG8="`,
1140 }, {
1141 desc: "Empty",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001142 input: &emptypb.Empty{},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001143 want: `{}`,
1144 }, {
Herbie Ong300b9fe2019-03-29 15:42:20 -07001145 desc: "NullValue field",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001146 input: &pb2.KnownTypes{OptNull: new(structpb.NullValue)},
Herbie Ong300b9fe2019-03-29 15:42:20 -07001147 want: `{
1148 "optNull": null
1149}`,
1150 }, {
Herbie Ong1c7462c2019-03-22 17:56:55 -07001151 desc: "Value empty",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001152 input: &structpb.Value{},
Herbie Ong1c7462c2019-03-22 17:56:55 -07001153 wantErr: true,
Herbie Ong0b0f4032019-03-18 19:06:15 -07001154 }, {
1155 desc: "Value empty field",
1156 input: &pb2.KnownTypes{
Joe Tsaia95b29f2019-05-16 12:47:20 -07001157 OptValue: &structpb.Value{},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001158 },
Herbie Ong1c7462c2019-03-22 17:56:55 -07001159 wantErr: true,
Herbie Ong0b0f4032019-03-18 19:06:15 -07001160 }, {
1161 desc: "Value contains NullValue",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001162 input: &structpb.Value{Kind: &structpb.Value_NullValue{}},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001163 want: `null`,
1164 }, {
1165 desc: "Value contains BoolValue",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001166 input: &structpb.Value{Kind: &structpb.Value_BoolValue{}},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001167 want: `false`,
1168 }, {
1169 desc: "Value contains NumberValue",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001170 input: &structpb.Value{Kind: &structpb.Value_NumberValue{1.02}},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001171 want: `1.02`,
1172 }, {
1173 desc: "Value contains StringValue",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001174 input: &structpb.Value{Kind: &structpb.Value_StringValue{"hello"}},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001175 want: `"hello"`,
1176 }, {
1177 desc: "Value contains StringValue with invalid UTF8",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001178 input: &structpb.Value{Kind: &structpb.Value_StringValue{"\xff"}},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001179 wantErr: true,
1180 }, {
1181 desc: "Value contains Struct",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001182 input: &structpb.Value{
1183 Kind: &structpb.Value_StructValue{
1184 &structpb.Struct{
1185 Fields: map[string]*structpb.Value{
1186 "null": {Kind: &structpb.Value_NullValue{}},
1187 "number": {Kind: &structpb.Value_NumberValue{}},
1188 "string": {Kind: &structpb.Value_StringValue{}},
1189 "struct": {Kind: &structpb.Value_StructValue{}},
1190 "list": {Kind: &structpb.Value_ListValue{}},
1191 "bool": {Kind: &structpb.Value_BoolValue{}},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001192 },
1193 },
1194 },
1195 },
1196 want: `{
1197 "bool": false,
1198 "list": [],
1199 "null": null,
1200 "number": 0,
1201 "string": "",
1202 "struct": {}
1203}`,
1204 }, {
1205 desc: "Value contains ListValue",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001206 input: &structpb.Value{
1207 Kind: &structpb.Value_ListValue{
1208 &structpb.ListValue{
1209 Values: []*structpb.Value{
1210 {Kind: &structpb.Value_BoolValue{}},
1211 {Kind: &structpb.Value_NullValue{}},
1212 {Kind: &structpb.Value_NumberValue{}},
1213 {Kind: &structpb.Value_StringValue{}},
1214 {Kind: &structpb.Value_StructValue{}},
1215 {Kind: &structpb.Value_ListValue{}},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001216 },
1217 },
1218 },
1219 },
1220 want: `[
1221 false,
1222 null,
1223 0,
1224 "",
1225 {},
1226 []
1227]`,
1228 }, {
1229 desc: "Struct with nil map",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001230 input: &structpb.Struct{},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001231 want: `{}`,
1232 }, {
1233 desc: "Struct with empty map",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001234 input: &structpb.Struct{
1235 Fields: map[string]*structpb.Value{},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001236 },
1237 want: `{}`,
1238 }, {
1239 desc: "Struct",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001240 input: &structpb.Struct{
1241 Fields: map[string]*structpb.Value{
1242 "bool": {Kind: &structpb.Value_BoolValue{true}},
1243 "null": {Kind: &structpb.Value_NullValue{}},
1244 "number": {Kind: &structpb.Value_NumberValue{3.1415}},
1245 "string": {Kind: &structpb.Value_StringValue{"hello"}},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001246 "struct": {
Joe Tsaia95b29f2019-05-16 12:47:20 -07001247 Kind: &structpb.Value_StructValue{
1248 &structpb.Struct{
1249 Fields: map[string]*structpb.Value{
1250 "string": {Kind: &structpb.Value_StringValue{"world"}},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001251 },
1252 },
1253 },
1254 },
1255 "list": {
Joe Tsaia95b29f2019-05-16 12:47:20 -07001256 Kind: &structpb.Value_ListValue{
1257 &structpb.ListValue{
1258 Values: []*structpb.Value{
1259 {Kind: &structpb.Value_BoolValue{}},
1260 {Kind: &structpb.Value_NullValue{}},
1261 {Kind: &structpb.Value_NumberValue{}},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001262 },
1263 },
1264 },
1265 },
1266 },
1267 },
1268 want: `{
1269 "bool": true,
1270 "list": [
1271 false,
1272 null,
1273 0
1274 ],
1275 "null": null,
1276 "number": 3.1415,
1277 "string": "hello",
1278 "struct": {
1279 "string": "world"
1280 }
1281}`,
1282 }, {
1283 desc: "Struct message with invalid UTF8 string",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001284 input: &structpb.Struct{
1285 Fields: map[string]*structpb.Value{
1286 "string": {Kind: &structpb.Value_StringValue{"\xff"}},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001287 },
1288 },
Herbie Ong0b0f4032019-03-18 19:06:15 -07001289 wantErr: true,
1290 }, {
1291 desc: "ListValue with nil values",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001292 input: &structpb.ListValue{},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001293 want: `[]`,
1294 }, {
1295 desc: "ListValue with empty values",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001296 input: &structpb.ListValue{
1297 Values: []*structpb.Value{},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001298 },
1299 want: `[]`,
1300 }, {
1301 desc: "ListValue",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001302 input: &structpb.ListValue{
1303 Values: []*structpb.Value{
1304 {Kind: &structpb.Value_BoolValue{true}},
1305 {Kind: &structpb.Value_NullValue{}},
1306 {Kind: &structpb.Value_NumberValue{3.1415}},
1307 {Kind: &structpb.Value_StringValue{"hello"}},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001308 {
Joe Tsaia95b29f2019-05-16 12:47:20 -07001309 Kind: &structpb.Value_ListValue{
1310 &structpb.ListValue{
1311 Values: []*structpb.Value{
1312 {Kind: &structpb.Value_BoolValue{}},
1313 {Kind: &structpb.Value_NullValue{}},
1314 {Kind: &structpb.Value_NumberValue{}},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001315 },
1316 },
1317 },
1318 },
1319 {
Joe Tsaia95b29f2019-05-16 12:47:20 -07001320 Kind: &structpb.Value_StructValue{
1321 &structpb.Struct{
1322 Fields: map[string]*structpb.Value{
1323 "string": {Kind: &structpb.Value_StringValue{"world"}},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001324 },
1325 },
1326 },
1327 },
1328 },
1329 },
1330 want: `[
1331 true,
1332 null,
1333 3.1415,
1334 "hello",
1335 [
1336 false,
1337 null,
1338 0
1339 ],
1340 {
1341 "string": "world"
1342 }
1343]`,
1344 }, {
1345 desc: "ListValue with invalid UTF8 string",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001346 input: &structpb.ListValue{
1347 Values: []*structpb.Value{
1348 {Kind: &structpb.Value_StringValue{"\xff"}},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001349 },
1350 },
Herbie Ong0b0f4032019-03-18 19:06:15 -07001351 wantErr: true,
1352 }, {
1353 desc: "Duration empty",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001354 input: &durationpb.Duration{},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001355 want: `"0s"`,
1356 }, {
1357 desc: "Duration with secs",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001358 input: &durationpb.Duration{Seconds: 3},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001359 want: `"3s"`,
1360 }, {
1361 desc: "Duration with -secs",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001362 input: &durationpb.Duration{Seconds: -3},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001363 want: `"-3s"`,
1364 }, {
1365 desc: "Duration with nanos",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001366 input: &durationpb.Duration{Nanos: 1e6},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001367 want: `"0.001s"`,
1368 }, {
1369 desc: "Duration with -nanos",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001370 input: &durationpb.Duration{Nanos: -1e6},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001371 want: `"-0.001s"`,
1372 }, {
1373 desc: "Duration with large secs",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001374 input: &durationpb.Duration{Seconds: 1e10, Nanos: 1},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001375 want: `"10000000000.000000001s"`,
1376 }, {
1377 desc: "Duration with 6-digit nanos",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001378 input: &durationpb.Duration{Nanos: 1e4},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001379 want: `"0.000010s"`,
1380 }, {
1381 desc: "Duration with 3-digit nanos",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001382 input: &durationpb.Duration{Nanos: 1e6},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001383 want: `"0.001s"`,
1384 }, {
1385 desc: "Duration with -secs -nanos",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001386 input: &durationpb.Duration{Seconds: -123, Nanos: -450},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001387 want: `"-123.000000450s"`,
1388 }, {
Herbie Ongad9c1252019-04-24 20:51:28 -07001389 desc: "Duration max value",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001390 input: &durationpb.Duration{Seconds: 315576000000, Nanos: 999999999},
Herbie Ongad9c1252019-04-24 20:51:28 -07001391 want: `"315576000000.999999999s"`,
1392 }, {
1393 desc: "Duration min value",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001394 input: &durationpb.Duration{Seconds: -315576000000, Nanos: -999999999},
Herbie Ongad9c1252019-04-24 20:51:28 -07001395 want: `"-315576000000.999999999s"`,
1396 }, {
Herbie Ong0b0f4032019-03-18 19:06:15 -07001397 desc: "Duration with +secs -nanos",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001398 input: &durationpb.Duration{Seconds: 1, Nanos: -1},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001399 wantErr: true,
1400 }, {
1401 desc: "Duration with -secs +nanos",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001402 input: &durationpb.Duration{Seconds: -1, Nanos: 1},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001403 wantErr: true,
1404 }, {
1405 desc: "Duration with +secs out of range",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001406 input: &durationpb.Duration{Seconds: 315576000001},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001407 wantErr: true,
1408 }, {
1409 desc: "Duration with -secs out of range",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001410 input: &durationpb.Duration{Seconds: -315576000001},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001411 wantErr: true,
1412 }, {
1413 desc: "Duration with +nanos out of range",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001414 input: &durationpb.Duration{Seconds: 0, Nanos: 1e9},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001415 wantErr: true,
1416 }, {
1417 desc: "Duration with -nanos out of range",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001418 input: &durationpb.Duration{Seconds: 0, Nanos: -1e9},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001419 wantErr: true,
1420 }, {
1421 desc: "Timestamp zero",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001422 input: &timestamppb.Timestamp{},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001423 want: `"1970-01-01T00:00:00Z"`,
1424 }, {
1425 desc: "Timestamp",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001426 input: &timestamppb.Timestamp{Seconds: 1553036601},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001427 want: `"2019-03-19T23:03:21Z"`,
1428 }, {
1429 desc: "Timestamp with nanos",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001430 input: &timestamppb.Timestamp{Seconds: 1553036601, Nanos: 1},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001431 want: `"2019-03-19T23:03:21.000000001Z"`,
1432 }, {
1433 desc: "Timestamp with 6-digit nanos",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001434 input: &timestamppb.Timestamp{Nanos: 1e3},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001435 want: `"1970-01-01T00:00:00.000001Z"`,
1436 }, {
1437 desc: "Timestamp with 3-digit nanos",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001438 input: &timestamppb.Timestamp{Nanos: 1e7},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001439 want: `"1970-01-01T00:00:00.010Z"`,
1440 }, {
Herbie Ongad9c1252019-04-24 20:51:28 -07001441 desc: "Timestamp max value",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001442 input: &timestamppb.Timestamp{Seconds: 253402300799, Nanos: 999999999},
Herbie Ongad9c1252019-04-24 20:51:28 -07001443 want: `"9999-12-31T23:59:59.999999999Z"`,
1444 }, {
1445 desc: "Timestamp min value",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001446 input: &timestamppb.Timestamp{Seconds: -62135596800},
Herbie Ongad9c1252019-04-24 20:51:28 -07001447 want: `"0001-01-01T00:00:00Z"`,
1448 }, {
Herbie Ong0b0f4032019-03-18 19:06:15 -07001449 desc: "Timestamp with +secs out of range",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001450 input: &timestamppb.Timestamp{Seconds: 253402300800},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001451 wantErr: true,
1452 }, {
1453 desc: "Timestamp with -secs out of range",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001454 input: &timestamppb.Timestamp{Seconds: -62135596801},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001455 wantErr: true,
1456 }, {
1457 desc: "Timestamp with -nanos",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001458 input: &timestamppb.Timestamp{Nanos: -1},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001459 wantErr: true,
1460 }, {
1461 desc: "Timestamp with +nanos out of range",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001462 input: &timestamppb.Timestamp{Nanos: 1e9},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001463 wantErr: true,
1464 }, {
1465 desc: "FieldMask empty",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001466 input: &fieldmaskpb.FieldMask{},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001467 want: `""`,
1468 }, {
1469 desc: "FieldMask",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001470 input: &fieldmaskpb.FieldMask{
Herbie Ong0b0f4032019-03-18 19:06:15 -07001471 Paths: []string{
1472 "foo",
1473 "foo_bar",
1474 "foo.bar_qux",
1475 "_foo",
1476 },
1477 },
1478 want: `"foo,fooBar,foo.barQux,Foo"`,
1479 }, {
1480 desc: "FieldMask error 1",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001481 input: &fieldmaskpb.FieldMask{
Herbie Ong0b0f4032019-03-18 19:06:15 -07001482 Paths: []string{"foo_"},
1483 },
1484 wantErr: true,
1485 }, {
1486 desc: "FieldMask error 2",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001487 input: &fieldmaskpb.FieldMask{
Herbie Ong0b0f4032019-03-18 19:06:15 -07001488 Paths: []string{"foo__bar"},
1489 },
1490 wantErr: true,
1491 }, {
1492 desc: "Any empty",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001493 input: &anypb.Any{},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001494 want: `{}`,
1495 }, {
Herbie Ong8ac9dd22019-03-27 12:20:50 -07001496 desc: "Any with non-custom message",
Damien Neil5c5b5312019-05-14 12:44:37 -07001497 mo: protojson.MarshalOptions{
Joe Tsai0fc49f82019-05-01 12:29:25 -07001498 Resolver: preg.NewTypes(pimpl.Export{}.MessageTypeOf(&pb2.Nested{})),
Herbie Ong0b0f4032019-03-18 19:06:15 -07001499 },
1500 input: func() proto.Message {
1501 m := &pb2.Nested{
Damien Neila8a2cea2019-07-10 16:17:16 -07001502 OptString: proto.String("embedded inside Any"),
Herbie Ong0b0f4032019-03-18 19:06:15 -07001503 OptNested: &pb2.Nested{
Damien Neila8a2cea2019-07-10 16:17:16 -07001504 OptString: proto.String("inception"),
Herbie Ong0b0f4032019-03-18 19:06:15 -07001505 },
1506 }
1507 b, err := proto.MarshalOptions{Deterministic: true}.Marshal(m)
1508 if err != nil {
1509 t.Fatalf("error in binary marshaling message for Any.value: %v", err)
1510 }
Joe Tsaia95b29f2019-05-16 12:47:20 -07001511 return &anypb.Any{
Herbie Ong0b0f4032019-03-18 19:06:15 -07001512 TypeUrl: "foo/pb2.Nested",
1513 Value: b,
1514 }
1515 }(),
1516 want: `{
1517 "@type": "foo/pb2.Nested",
1518 "optString": "embedded inside Any",
1519 "optNested": {
1520 "optString": "inception"
1521 }
1522}`,
1523 }, {
Herbie Ong8ac9dd22019-03-27 12:20:50 -07001524 desc: "Any with empty embedded message",
Damien Neil5c5b5312019-05-14 12:44:37 -07001525 mo: protojson.MarshalOptions{
Joe Tsai0fc49f82019-05-01 12:29:25 -07001526 Resolver: preg.NewTypes(pimpl.Export{}.MessageTypeOf(&pb2.Nested{})),
Herbie Ong0b0f4032019-03-18 19:06:15 -07001527 },
Joe Tsaia95b29f2019-05-16 12:47:20 -07001528 input: &anypb.Any{TypeUrl: "foo/pb2.Nested"},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001529 want: `{
1530 "@type": "foo/pb2.Nested"
1531}`,
1532 }, {
Herbie Ong8ac9dd22019-03-27 12:20:50 -07001533 desc: "Any without registered type",
Damien Neil5c5b5312019-05-14 12:44:37 -07001534 mo: protojson.MarshalOptions{Resolver: preg.NewTypes()},
Joe Tsaia95b29f2019-05-16 12:47:20 -07001535 input: &anypb.Any{TypeUrl: "foo/pb2.Nested"},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001536 wantErr: true,
1537 }, {
Damien Neil0c9f0a92019-06-19 10:41:09 -07001538 desc: "Any with missing required",
Damien Neil5c5b5312019-05-14 12:44:37 -07001539 mo: protojson.MarshalOptions{
Joe Tsai0fc49f82019-05-01 12:29:25 -07001540 Resolver: preg.NewTypes(pimpl.Export{}.MessageTypeOf(&pb2.PartialRequired{})),
Herbie Ong0b0f4032019-03-18 19:06:15 -07001541 },
1542 input: func() proto.Message {
1543 m := &pb2.PartialRequired{
Damien Neila8a2cea2019-07-10 16:17:16 -07001544 OptString: proto.String("embedded inside Any"),
Herbie Ong0b0f4032019-03-18 19:06:15 -07001545 }
Damien Neil96c229a2019-04-03 12:17:24 -07001546 b, err := proto.MarshalOptions{
1547 AllowPartial: true,
1548 Deterministic: true,
1549 }.Marshal(m)
Herbie Ong0b0f4032019-03-18 19:06:15 -07001550 if err != nil {
1551 t.Fatalf("error in binary marshaling message for Any.value: %v", err)
1552 }
Joe Tsaia95b29f2019-05-16 12:47:20 -07001553 return &anypb.Any{
Joe Tsai0fc49f82019-05-01 12:29:25 -07001554 TypeUrl: string(m.ProtoReflect().Descriptor().FullName()),
Herbie Ong0b0f4032019-03-18 19:06:15 -07001555 Value: b,
1556 }
1557 }(),
1558 want: `{
1559 "@type": "pb2.PartialRequired",
1560 "optString": "embedded inside Any"
1561}`,
Herbie Ong0b0f4032019-03-18 19:06:15 -07001562 }, {
Herbie Ong8ac9dd22019-03-27 12:20:50 -07001563 desc: "Any with partial required and AllowPartial",
Damien Neil5c5b5312019-05-14 12:44:37 -07001564 mo: protojson.MarshalOptions{
Herbie Ong8ac9dd22019-03-27 12:20:50 -07001565 AllowPartial: true,
Joe Tsai0fc49f82019-05-01 12:29:25 -07001566 Resolver: preg.NewTypes(pimpl.Export{}.MessageTypeOf(&pb2.PartialRequired{})),
Herbie Ong8ac9dd22019-03-27 12:20:50 -07001567 },
1568 input: func() proto.Message {
1569 m := &pb2.PartialRequired{
Damien Neila8a2cea2019-07-10 16:17:16 -07001570 OptString: proto.String("embedded inside Any"),
Herbie Ong8ac9dd22019-03-27 12:20:50 -07001571 }
Damien Neil96c229a2019-04-03 12:17:24 -07001572 b, err := proto.MarshalOptions{
1573 AllowPartial: true,
1574 Deterministic: true,
1575 }.Marshal(m)
Herbie Ong8ac9dd22019-03-27 12:20:50 -07001576 if err != nil {
1577 t.Fatalf("error in binary marshaling message for Any.value: %v", err)
1578 }
Joe Tsaia95b29f2019-05-16 12:47:20 -07001579 return &anypb.Any{
Joe Tsai0fc49f82019-05-01 12:29:25 -07001580 TypeUrl: string(m.ProtoReflect().Descriptor().FullName()),
Herbie Ong8ac9dd22019-03-27 12:20:50 -07001581 Value: b,
1582 }
1583 }(),
1584 want: `{
1585 "@type": "pb2.PartialRequired",
1586 "optString": "embedded inside Any"
1587}`,
1588 }, {
Herbie Ong0b0f4032019-03-18 19:06:15 -07001589 desc: "Any with invalid UTF8",
Damien Neil5c5b5312019-05-14 12:44:37 -07001590 mo: protojson.MarshalOptions{
Joe Tsai0fc49f82019-05-01 12:29:25 -07001591 Resolver: preg.NewTypes(pimpl.Export{}.MessageTypeOf(&pb2.Nested{})),
Herbie Ong0b0f4032019-03-18 19:06:15 -07001592 },
1593 input: func() proto.Message {
1594 m := &pb2.Nested{
Damien Neila8a2cea2019-07-10 16:17:16 -07001595 OptString: proto.String("abc\xff"),
Herbie Ong0b0f4032019-03-18 19:06:15 -07001596 }
1597 b, err := proto.MarshalOptions{Deterministic: true}.Marshal(m)
1598 if err != nil {
1599 t.Fatalf("error in binary marshaling message for Any.value: %v", err)
1600 }
Joe Tsaia95b29f2019-05-16 12:47:20 -07001601 return &anypb.Any{
Herbie Ong0b0f4032019-03-18 19:06:15 -07001602 TypeUrl: "foo/pb2.Nested",
1603 Value: b,
1604 }
1605 }(),
Herbie Ong0b0f4032019-03-18 19:06:15 -07001606 wantErr: true,
1607 }, {
1608 desc: "Any with invalid value",
Damien Neil5c5b5312019-05-14 12:44:37 -07001609 mo: protojson.MarshalOptions{
Joe Tsai0fc49f82019-05-01 12:29:25 -07001610 Resolver: preg.NewTypes(pimpl.Export{}.MessageTypeOf(&pb2.Nested{})),
Herbie Ong0b0f4032019-03-18 19:06:15 -07001611 },
Joe Tsaia95b29f2019-05-16 12:47:20 -07001612 input: &anypb.Any{
Herbie Ong0b0f4032019-03-18 19:06:15 -07001613 TypeUrl: "foo/pb2.Nested",
Joe Tsai6dc168e2019-07-09 23:11:13 -07001614 Value: []byte("\x80"),
Herbie Ong0b0f4032019-03-18 19:06:15 -07001615 },
1616 wantErr: true,
1617 }, {
1618 desc: "Any with BoolValue",
Damien Neil5c5b5312019-05-14 12:44:37 -07001619 mo: protojson.MarshalOptions{
Joe Tsaia95b29f2019-05-16 12:47:20 -07001620 Resolver: preg.NewTypes(pimpl.Export{}.MessageTypeOf(&wrapperspb.BoolValue{})),
Herbie Ong0b0f4032019-03-18 19:06:15 -07001621 },
1622 input: func() proto.Message {
Joe Tsaia95b29f2019-05-16 12:47:20 -07001623 m := &wrapperspb.BoolValue{Value: true}
Herbie Ong0b0f4032019-03-18 19:06:15 -07001624 b, err := proto.MarshalOptions{Deterministic: true}.Marshal(m)
1625 if err != nil {
1626 t.Fatalf("error in binary marshaling message for Any.value: %v", err)
1627 }
Joe Tsaia95b29f2019-05-16 12:47:20 -07001628 return &anypb.Any{
Herbie Ong0b0f4032019-03-18 19:06:15 -07001629 TypeUrl: "type.googleapis.com/google.protobuf.BoolValue",
1630 Value: b,
1631 }
1632 }(),
1633 want: `{
1634 "@type": "type.googleapis.com/google.protobuf.BoolValue",
1635 "value": true
1636}`,
1637 }, {
Herbie Ong0b0f4032019-03-18 19:06:15 -07001638 desc: "Any with Empty",
Damien Neil5c5b5312019-05-14 12:44:37 -07001639 mo: protojson.MarshalOptions{
Joe Tsaia95b29f2019-05-16 12:47:20 -07001640 Resolver: preg.NewTypes(pimpl.Export{}.MessageTypeOf(&emptypb.Empty{})),
Herbie Ong0b0f4032019-03-18 19:06:15 -07001641 },
1642 input: func() proto.Message {
Joe Tsaia95b29f2019-05-16 12:47:20 -07001643 m := &emptypb.Empty{}
Herbie Ong0b0f4032019-03-18 19:06:15 -07001644 b, err := proto.MarshalOptions{Deterministic: true}.Marshal(m)
1645 if err != nil {
1646 t.Fatalf("error in binary marshaling message for Any.value: %v", err)
1647 }
Joe Tsaia95b29f2019-05-16 12:47:20 -07001648 return &anypb.Any{
Herbie Ong0b0f4032019-03-18 19:06:15 -07001649 TypeUrl: "type.googleapis.com/google.protobuf.Empty",
1650 Value: b,
1651 }
1652 }(),
1653 want: `{
Herbie Ong82014a52019-03-27 15:21:43 -07001654 "@type": "type.googleapis.com/google.protobuf.Empty",
1655 "value": {}
Herbie Ong0b0f4032019-03-18 19:06:15 -07001656}`,
1657 }, {
1658 desc: "Any with StringValue containing invalid UTF8",
Damien Neil5c5b5312019-05-14 12:44:37 -07001659 mo: protojson.MarshalOptions{
Joe Tsaia95b29f2019-05-16 12:47:20 -07001660 Resolver: preg.NewTypes(pimpl.Export{}.MessageTypeOf(&wrapperspb.StringValue{})),
Herbie Ong0b0f4032019-03-18 19:06:15 -07001661 },
1662 input: func() proto.Message {
Joe Tsaia95b29f2019-05-16 12:47:20 -07001663 m := &wrapperspb.StringValue{Value: "abcd"}
Herbie Ong0b0f4032019-03-18 19:06:15 -07001664 b, err := proto.MarshalOptions{Deterministic: true}.Marshal(m)
1665 if err != nil {
1666 t.Fatalf("error in binary marshaling message for Any.value: %v", err)
1667 }
Joe Tsaia95b29f2019-05-16 12:47:20 -07001668 return &anypb.Any{
Herbie Ong0b0f4032019-03-18 19:06:15 -07001669 TypeUrl: "google.protobuf.StringValue",
Damien Neilbc310b52019-04-11 11:46:55 -07001670 Value: bytes.Replace(b, []byte("abcd"), []byte("abc\xff"), -1),
Herbie Ong0b0f4032019-03-18 19:06:15 -07001671 }
1672 }(),
Herbie Ong0b0f4032019-03-18 19:06:15 -07001673 wantErr: true,
1674 }, {
Herbie Ong8ac9dd22019-03-27 12:20:50 -07001675 desc: "Any with Int64Value",
Damien Neil5c5b5312019-05-14 12:44:37 -07001676 mo: protojson.MarshalOptions{
Joe Tsaia95b29f2019-05-16 12:47:20 -07001677 Resolver: preg.NewTypes(pimpl.Export{}.MessageTypeOf(&wrapperspb.Int64Value{})),
Herbie Ong8ac9dd22019-03-27 12:20:50 -07001678 },
1679 input: func() proto.Message {
Joe Tsaia95b29f2019-05-16 12:47:20 -07001680 m := &wrapperspb.Int64Value{Value: 42}
Herbie Ong8ac9dd22019-03-27 12:20:50 -07001681 b, err := proto.MarshalOptions{Deterministic: true}.Marshal(m)
1682 if err != nil {
1683 t.Fatalf("error in binary marshaling message for Any.value: %v", err)
1684 }
Joe Tsaia95b29f2019-05-16 12:47:20 -07001685 return &anypb.Any{
Herbie Ong8ac9dd22019-03-27 12:20:50 -07001686 TypeUrl: "google.protobuf.Int64Value",
1687 Value: b,
1688 }
1689 }(),
1690 want: `{
1691 "@type": "google.protobuf.Int64Value",
1692 "value": "42"
1693}`,
1694 }, {
1695 desc: "Any with Duration",
Damien Neil5c5b5312019-05-14 12:44:37 -07001696 mo: protojson.MarshalOptions{
Joe Tsaia95b29f2019-05-16 12:47:20 -07001697 Resolver: preg.NewTypes(pimpl.Export{}.MessageTypeOf(&durationpb.Duration{})),
Herbie Ong8ac9dd22019-03-27 12:20:50 -07001698 },
1699 input: func() proto.Message {
Joe Tsaia95b29f2019-05-16 12:47:20 -07001700 m := &durationpb.Duration{}
Herbie Ong8ac9dd22019-03-27 12:20:50 -07001701 b, err := proto.MarshalOptions{Deterministic: true}.Marshal(m)
1702 if err != nil {
1703 t.Fatalf("error in binary marshaling message for Any.value: %v", err)
1704 }
Joe Tsaia95b29f2019-05-16 12:47:20 -07001705 return &anypb.Any{
Herbie Ong8ac9dd22019-03-27 12:20:50 -07001706 TypeUrl: "type.googleapis.com/google.protobuf.Duration",
1707 Value: b,
1708 }
1709 }(),
1710 want: `{
1711 "@type": "type.googleapis.com/google.protobuf.Duration",
1712 "value": "0s"
1713}`,
1714 }, {
1715 desc: "Any with empty Value",
Damien Neil5c5b5312019-05-14 12:44:37 -07001716 mo: protojson.MarshalOptions{
Joe Tsaia95b29f2019-05-16 12:47:20 -07001717 Resolver: preg.NewTypes(pimpl.Export{}.MessageTypeOf(&structpb.Value{})),
Herbie Ong8ac9dd22019-03-27 12:20:50 -07001718 },
1719 input: func() proto.Message {
Joe Tsaia95b29f2019-05-16 12:47:20 -07001720 m := &structpb.Value{}
Herbie Ong8ac9dd22019-03-27 12:20:50 -07001721 b, err := proto.Marshal(m)
1722 if err != nil {
1723 t.Fatalf("error in binary marshaling message for Any.value: %v", err)
1724 }
Joe Tsaia95b29f2019-05-16 12:47:20 -07001725 return &anypb.Any{
Herbie Ong8ac9dd22019-03-27 12:20:50 -07001726 TypeUrl: "type.googleapis.com/google.protobuf.Value",
1727 Value: b,
1728 }
1729 }(),
1730 wantErr: true,
1731 }, {
Herbie Ong0b0f4032019-03-18 19:06:15 -07001732 desc: "Any with Value of StringValue",
Damien Neil5c5b5312019-05-14 12:44:37 -07001733 mo: protojson.MarshalOptions{
Joe Tsaia95b29f2019-05-16 12:47:20 -07001734 Resolver: preg.NewTypes(pimpl.Export{}.MessageTypeOf(&structpb.Value{})),
Herbie Ong0b0f4032019-03-18 19:06:15 -07001735 },
1736 input: func() proto.Message {
Joe Tsaia95b29f2019-05-16 12:47:20 -07001737 m := &structpb.Value{Kind: &structpb.Value_StringValue{"abcd"}}
Herbie Ong0b0f4032019-03-18 19:06:15 -07001738 b, err := proto.MarshalOptions{Deterministic: true}.Marshal(m)
1739 if err != nil {
1740 t.Fatalf("error in binary marshaling message for Any.value: %v", err)
1741 }
Joe Tsaia95b29f2019-05-16 12:47:20 -07001742 return &anypb.Any{
Herbie Ong0b0f4032019-03-18 19:06:15 -07001743 TypeUrl: "type.googleapis.com/google.protobuf.Value",
Damien Neilbc310b52019-04-11 11:46:55 -07001744 Value: bytes.Replace(b, []byte("abcd"), []byte("abc\xff"), -1),
Herbie Ong0b0f4032019-03-18 19:06:15 -07001745 }
1746 }(),
Herbie Ong0b0f4032019-03-18 19:06:15 -07001747 wantErr: true,
1748 }, {
Herbie Ong8ac9dd22019-03-27 12:20:50 -07001749 desc: "Any with Value of NullValue",
Damien Neil5c5b5312019-05-14 12:44:37 -07001750 mo: protojson.MarshalOptions{
Joe Tsaia95b29f2019-05-16 12:47:20 -07001751 Resolver: preg.NewTypes(pimpl.Export{}.MessageTypeOf(&structpb.Value{})),
Herbie Ong0b0f4032019-03-18 19:06:15 -07001752 },
1753 input: func() proto.Message {
Joe Tsaia95b29f2019-05-16 12:47:20 -07001754 m := &structpb.Value{Kind: &structpb.Value_NullValue{}}
Herbie Ong8ac9dd22019-03-27 12:20:50 -07001755 b, err := proto.MarshalOptions{Deterministic: true}.Marshal(m)
Herbie Ong0b0f4032019-03-18 19:06:15 -07001756 if err != nil {
1757 t.Fatalf("error in binary marshaling message for Any.value: %v", err)
1758 }
Joe Tsaia95b29f2019-05-16 12:47:20 -07001759 return &anypb.Any{
Herbie Ong0b0f4032019-03-18 19:06:15 -07001760 TypeUrl: "type.googleapis.com/google.protobuf.Value",
1761 Value: b,
1762 }
1763 }(),
Herbie Ong0b0f4032019-03-18 19:06:15 -07001764 want: `{
Herbie Ong8ac9dd22019-03-27 12:20:50 -07001765 "@type": "type.googleapis.com/google.protobuf.Value",
1766 "value": null
Herbie Ong0b0f4032019-03-18 19:06:15 -07001767}`,
1768 }, {
1769 desc: "Any with Struct",
Damien Neil5c5b5312019-05-14 12:44:37 -07001770 mo: protojson.MarshalOptions{
Herbie Ong0b0f4032019-03-18 19:06:15 -07001771 Resolver: preg.NewTypes(
Joe Tsaia95b29f2019-05-16 12:47:20 -07001772 pimpl.Export{}.MessageTypeOf(&structpb.Struct{}),
1773 pimpl.Export{}.MessageTypeOf(&structpb.Value{}),
1774 pimpl.Export{}.MessageTypeOf(&wrapperspb.BoolValue{}),
1775 pimpl.Export{}.EnumTypeOf(structpb.NullValue_NULL_VALUE),
1776 pimpl.Export{}.MessageTypeOf(&wrapperspb.StringValue{}),
Herbie Ong0b0f4032019-03-18 19:06:15 -07001777 ),
1778 },
1779 input: func() proto.Message {
Joe Tsaia95b29f2019-05-16 12:47:20 -07001780 m := &structpb.Struct{
1781 Fields: map[string]*structpb.Value{
1782 "bool": {Kind: &structpb.Value_BoolValue{true}},
1783 "null": {Kind: &structpb.Value_NullValue{}},
1784 "string": {Kind: &structpb.Value_StringValue{"hello"}},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001785 "struct": {
Joe Tsaia95b29f2019-05-16 12:47:20 -07001786 Kind: &structpb.Value_StructValue{
1787 &structpb.Struct{
1788 Fields: map[string]*structpb.Value{
1789 "string": {Kind: &structpb.Value_StringValue{"world"}},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001790 },
1791 },
1792 },
1793 },
1794 },
1795 }
1796 b, err := proto.MarshalOptions{Deterministic: true}.Marshal(m)
1797 if err != nil {
1798 t.Fatalf("error in binary marshaling message for Any.value: %v", err)
1799 }
Joe Tsaia95b29f2019-05-16 12:47:20 -07001800 return &anypb.Any{
Herbie Ong0b0f4032019-03-18 19:06:15 -07001801 TypeUrl: "google.protobuf.Struct",
1802 Value: b,
1803 }
1804 }(),
1805 want: `{
1806 "@type": "google.protobuf.Struct",
1807 "value": {
1808 "bool": true,
1809 "null": null,
1810 "string": "hello",
1811 "struct": {
1812 "string": "world"
1813 }
1814 }
1815}`,
1816 }, {
Herbie Ong8ac9dd22019-03-27 12:20:50 -07001817 desc: "Any with missing type_url",
Damien Neil5c5b5312019-05-14 12:44:37 -07001818 mo: protojson.MarshalOptions{
Joe Tsaia95b29f2019-05-16 12:47:20 -07001819 Resolver: preg.NewTypes(pimpl.Export{}.MessageTypeOf(&wrapperspb.BoolValue{})),
Herbie Ong8ac9dd22019-03-27 12:20:50 -07001820 },
1821 input: func() proto.Message {
Joe Tsaia95b29f2019-05-16 12:47:20 -07001822 m := &wrapperspb.BoolValue{Value: true}
Herbie Ong8ac9dd22019-03-27 12:20:50 -07001823 b, err := proto.MarshalOptions{Deterministic: true}.Marshal(m)
1824 if err != nil {
1825 t.Fatalf("error in binary marshaling message for Any.value: %v", err)
1826 }
Joe Tsaia95b29f2019-05-16 12:47:20 -07001827 return &anypb.Any{
Herbie Ong8ac9dd22019-03-27 12:20:50 -07001828 Value: b,
1829 }
1830 }(),
1831 wantErr: true,
1832 }, {
Herbie Ong0b0f4032019-03-18 19:06:15 -07001833 desc: "well known types as field values",
Damien Neil5c5b5312019-05-14 12:44:37 -07001834 mo: protojson.MarshalOptions{
Joe Tsaia95b29f2019-05-16 12:47:20 -07001835 Resolver: preg.NewTypes(pimpl.Export{}.MessageTypeOf(&emptypb.Empty{})),
Herbie Ong0b0f4032019-03-18 19:06:15 -07001836 },
1837 input: &pb2.KnownTypes{
Joe Tsaia95b29f2019-05-16 12:47:20 -07001838 OptBool: &wrapperspb.BoolValue{Value: false},
1839 OptInt32: &wrapperspb.Int32Value{Value: 42},
1840 OptInt64: &wrapperspb.Int64Value{Value: 42},
1841 OptUint32: &wrapperspb.UInt32Value{Value: 42},
1842 OptUint64: &wrapperspb.UInt64Value{Value: 42},
1843 OptFloat: &wrapperspb.FloatValue{Value: 1.23},
1844 OptDouble: &wrapperspb.DoubleValue{Value: 3.1415},
1845 OptString: &wrapperspb.StringValue{Value: "hello"},
1846 OptBytes: &wrapperspb.BytesValue{Value: []byte("hello")},
1847 OptDuration: &durationpb.Duration{Seconds: 123},
1848 OptTimestamp: &timestamppb.Timestamp{Seconds: 1553036601},
1849 OptStruct: &structpb.Struct{
1850 Fields: map[string]*structpb.Value{
1851 "string": {Kind: &structpb.Value_StringValue{"hello"}},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001852 },
1853 },
Joe Tsaia95b29f2019-05-16 12:47:20 -07001854 OptList: &structpb.ListValue{
1855 Values: []*structpb.Value{
1856 {Kind: &structpb.Value_NullValue{}},
1857 {Kind: &structpb.Value_StringValue{}},
1858 {Kind: &structpb.Value_StructValue{}},
1859 {Kind: &structpb.Value_ListValue{}},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001860 },
1861 },
Joe Tsaia95b29f2019-05-16 12:47:20 -07001862 OptValue: &structpb.Value{
1863 Kind: &structpb.Value_StringValue{"world"},
Herbie Ong1c7462c2019-03-22 17:56:55 -07001864 },
Joe Tsaia95b29f2019-05-16 12:47:20 -07001865 OptEmpty: &emptypb.Empty{},
1866 OptAny: &anypb.Any{
Herbie Ong0b0f4032019-03-18 19:06:15 -07001867 TypeUrl: "google.protobuf.Empty",
1868 },
Joe Tsaia95b29f2019-05-16 12:47:20 -07001869 OptFieldmask: &fieldmaskpb.FieldMask{
Herbie Ong0b0f4032019-03-18 19:06:15 -07001870 Paths: []string{"foo_bar", "bar_foo"},
1871 },
1872 },
1873 want: `{
1874 "optBool": false,
1875 "optInt32": 42,
1876 "optInt64": "42",
1877 "optUint32": 42,
1878 "optUint64": "42",
1879 "optFloat": 1.23,
1880 "optDouble": 3.1415,
1881 "optString": "hello",
1882 "optBytes": "aGVsbG8=",
1883 "optDuration": "123s",
1884 "optTimestamp": "2019-03-19T23:03:21Z",
1885 "optStruct": {
1886 "string": "hello"
1887 },
1888 "optList": [
1889 null,
1890 "",
1891 {},
1892 []
1893 ],
Herbie Ong1c7462c2019-03-22 17:56:55 -07001894 "optValue": "world",
Herbie Ong0b0f4032019-03-18 19:06:15 -07001895 "optEmpty": {},
1896 "optAny": {
Herbie Ong82014a52019-03-27 15:21:43 -07001897 "@type": "google.protobuf.Empty",
1898 "value": {}
Herbie Ong0b0f4032019-03-18 19:06:15 -07001899 },
1900 "optFieldmask": "fooBar,barFoo"
1901}`,
Herbie Ong7b828bc2019-02-08 19:56:24 -08001902 }}
1903
1904 for _, tt := range tests {
1905 tt := tt
Joe Tsai5ae10aa2019-07-11 18:23:08 -07001906 if tt.skip {
1907 continue
1908 }
Herbie Ong7b828bc2019-02-08 19:56:24 -08001909 t.Run(tt.desc, func(t *testing.T) {
Herbie Ong0b0f4032019-03-18 19:06:15 -07001910 // Use 2-space indentation on all MarshalOptions.
1911 tt.mo.Indent = " "
Herbie Ong7b828bc2019-02-08 19:56:24 -08001912 b, err := tt.mo.Marshal(tt.input)
Herbie Ong0b0f4032019-03-18 19:06:15 -07001913 if err != nil && !tt.wantErr {
Herbie Ong7b828bc2019-02-08 19:56:24 -08001914 t.Errorf("Marshal() returned error: %v\n", err)
1915 }
Herbie Ong0b0f4032019-03-18 19:06:15 -07001916 if err == nil && tt.wantErr {
1917 t.Errorf("Marshal() got nil error, want error\n")
1918 }
Herbie Ong7b828bc2019-02-08 19:56:24 -08001919 got := string(b)
1920 if got != tt.want {
1921 t.Errorf("Marshal()\n<got>\n%v\n<want>\n%v\n", got, tt.want)
Joe Tsai6dc168e2019-07-09 23:11:13 -07001922 if diff := cmp.Diff(tt.want, got); diff != "" {
Herbie Ong7b828bc2019-02-08 19:56:24 -08001923 t.Errorf("Marshal() diff -want +got\n%v\n", diff)
1924 }
1925 }
1926 })
1927 }
1928}