1 // Protocol Buffers - Google's data interchange format
2 // Copyright 2008 Google Inc. All rights reserved.
3 // https://developers.google.com/protocol-buffers/
4 //
5 // Redistribution and use in source and binary forms, with or without
6 // modification, are permitted provided that the following conditions are
7 // met:
8 //
9 // * Redistributions of source code must retain the above copyright
10 // notice, this list of conditions and the following disclaimer.
11 // * Redistributions in binary form must reproduce the above
12 // copyright notice, this list of conditions and the following disclaimer
13 // in the documentation and/or other materials provided with the
14 // distribution.
15 // * Neither the name of Google Inc. nor the names of its
16 // contributors may be used to endorse or promote products derived from
17 // this software without specific prior written permission.
18 //
19 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
22 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
23 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
25 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30
31 #include <google/protobuf/util/internal/protostream_objectsource.h>
32
33 #include <memory>
34 #include <sstream>
35
36 #include <google/protobuf/any.pb.h>
37 #include <google/protobuf/io/coded_stream.h>
38 #include <google/protobuf/io/zero_copy_stream_impl_lite.h>
39 #include <google/protobuf/descriptor.h>
40 #include <google/protobuf/util/internal/expecting_objectwriter.h>
41 #include <google/protobuf/util/internal/testdata/anys.pb.h>
42 #include <google/protobuf/util/internal/testdata/books.pb.h>
43 #include <google/protobuf/util/internal/testdata/field_mask.pb.h>
44 #include <google/protobuf/util/internal/testdata/maps.pb.h>
45 #include <google/protobuf/util/internal/testdata/proto3.pb.h>
46 #include <google/protobuf/util/internal/testdata/struct.pb.h>
47 #include <google/protobuf/util/internal/testdata/timestamp_duration.pb.h>
48 #include <google/protobuf/util/internal/type_info_test_helper.h>
49 #include <google/protobuf/util/internal/constants.h>
50 #include <gtest/gtest.h>
51 #include <google/protobuf/stubs/casts.h>
52
53
54 namespace google {
55 namespace protobuf {
56 namespace util {
57 namespace converter {
58
59 using ::google::protobuf::Any;
60 using io::ArrayInputStream;
61 using io::CodedInputStream;
62 using proto_util_converter::testing::AnyM;
63 using proto_util_converter::testing::AnyOut;
64 using proto_util_converter::testing::Author;
65 using proto_util_converter::testing::BadAuthor;
66 using proto_util_converter::testing::BadNestedBook;
67 using proto_util_converter::testing::Book;
68 using proto_util_converter::testing::Book_Label;
69 using proto_util_converter::testing::Cyclic;
70 using proto_util_converter::testing::FieldMaskTest;
71 using proto_util_converter::testing::MapOut;
72 using proto_util_converter::testing::MapOutWireFormat;
73 using proto_util_converter::testing::NestedBook;
74 using proto_util_converter::testing::NestedFieldMask;
75 using proto_util_converter::testing::PackedPrimitive;
76 using proto_util_converter::testing::Primitive;
77 using proto_util_converter::testing::Proto3Message;
78 using proto_util_converter::testing::StructType;
79 using proto_util_converter::testing::TimestampDuration;
80 using ::testing::_;
81 using util::Status;
82
83
84 namespace {
GetTypeUrl(const Descriptor * descriptor)85 std::string GetTypeUrl(const Descriptor* descriptor) {
86 return std::string(kTypeServiceBaseUrl) + "/" + descriptor->full_name();
87 }
88 } // namespace
89
90 class ProtostreamObjectSourceTest
91 : public ::testing::TestWithParam<testing::TypeInfoSource> {
92 protected:
ProtostreamObjectSourceTest()93 ProtostreamObjectSourceTest()
94 : helper_(GetParam()),
95 mock_(),
96 ow_(&mock_),
97 use_lower_camel_for_enums_(false),
98 use_ints_for_enums_(false),
99 use_preserve_proto_field_names_(false),
100 add_trailing_zeros_(false),
101 render_unknown_enum_values_(true) {
102 helper_.ResetTypeInfo(Book::descriptor(), Proto3Message::descriptor());
103 }
104
~ProtostreamObjectSourceTest()105 virtual ~ProtostreamObjectSourceTest() {}
106
DoTest(const Message & msg,const Descriptor * descriptor)107 void DoTest(const Message& msg, const Descriptor* descriptor) {
108 Status status = ExecuteTest(msg, descriptor);
109 EXPECT_EQ(util::Status(), status);
110 }
111
ExecuteTest(const Message & msg,const Descriptor * descriptor)112 Status ExecuteTest(const Message& msg, const Descriptor* descriptor) {
113 std::ostringstream oss;
114 msg.SerializePartialToOstream(&oss);
115 std::string proto = oss.str();
116 ArrayInputStream arr_stream(proto.data(), proto.size());
117 CodedInputStream in_stream(&arr_stream);
118
119 std::unique_ptr<ProtoStreamObjectSource> os(
120 helper_.NewProtoSource(&in_stream, GetTypeUrl(descriptor)));
121 if (use_lower_camel_for_enums_) os->set_use_lower_camel_for_enums(true);
122 if (use_ints_for_enums_) os->set_use_ints_for_enums(true);
123 if (use_preserve_proto_field_names_)
124 os->set_preserve_proto_field_names(true);
125 os->set_max_recursion_depth(64);
126 return os->WriteTo(&mock_);
127 }
128
PrepareExpectingObjectWriterForRepeatedPrimitive()129 void PrepareExpectingObjectWriterForRepeatedPrimitive() {
130 ow_.StartObject("")
131 ->StartList("repFix32")
132 ->RenderUint32("", bit_cast<uint32>(3201))
133 ->RenderUint32("", bit_cast<uint32>(0))
134 ->RenderUint32("", bit_cast<uint32>(3202))
135 ->EndList()
136 ->StartList("repU32")
137 ->RenderUint32("", bit_cast<uint32>(3203))
138 ->RenderUint32("", bit_cast<uint32>(0))
139 ->EndList()
140 ->StartList("repI32")
141 ->RenderInt32("", 0)
142 ->RenderInt32("", 3204)
143 ->RenderInt32("", 3205)
144 ->EndList()
145 ->StartList("repSf32")
146 ->RenderInt32("", 3206)
147 ->RenderInt32("", 0)
148 ->EndList()
149 ->StartList("repS32")
150 ->RenderInt32("", 0)
151 ->RenderInt32("", 3207)
152 ->RenderInt32("", 3208)
153 ->EndList()
154 ->StartList("repFix64")
155 ->RenderUint64("", bit_cast<uint64>(int64{6401}))
156 ->RenderUint64("", bit_cast<uint64>(int64{0}))
157 ->EndList()
158 ->StartList("repU64")
159 ->RenderUint64("", bit_cast<uint64>(int64{0}))
160 ->RenderUint64("", bit_cast<uint64>(int64{6402}))
161 ->RenderUint64("", bit_cast<uint64>(int64{6403}))
162 ->EndList()
163 ->StartList("repI64")
164 ->RenderInt64("", 6404L)
165 ->RenderInt64("", 0L)
166 ->EndList()
167 ->StartList("repSf64")
168 ->RenderInt64("", 0L)
169 ->RenderInt64("", 6405L)
170 ->RenderInt64("", 6406L)
171 ->EndList()
172 ->StartList("repS64")
173 ->RenderInt64("", 6407L)
174 ->RenderInt64("", 0L)
175 ->EndList()
176 ->StartList("repFloat")
177 ->RenderFloat("", 0.0f)
178 ->RenderFloat("", 32.1f)
179 ->RenderFloat("", 32.2f)
180 ->EndList()
181 ->StartList("repDouble")
182 ->RenderDouble("", 64.1L)
183 ->RenderDouble("", 0.0L)
184 ->EndList()
185 ->StartList("repBool")
186 ->RenderBool("", true)
187 ->RenderBool("", false)
188 ->EndList()
189 ->EndObject();
190 }
191
PrepareRepeatedPrimitive()192 Primitive PrepareRepeatedPrimitive() {
193 Primitive primitive;
194 primitive.add_rep_fix32(3201);
195 primitive.add_rep_fix32(0);
196 primitive.add_rep_fix32(3202);
197 primitive.add_rep_u32(3203);
198 primitive.add_rep_u32(0);
199 primitive.add_rep_i32(0);
200 primitive.add_rep_i32(3204);
201 primitive.add_rep_i32(3205);
202 primitive.add_rep_sf32(3206);
203 primitive.add_rep_sf32(0);
204 primitive.add_rep_s32(0);
205 primitive.add_rep_s32(3207);
206 primitive.add_rep_s32(3208);
207 primitive.add_rep_fix64(6401L);
208 primitive.add_rep_fix64(0L);
209 primitive.add_rep_u64(0L);
210 primitive.add_rep_u64(6402L);
211 primitive.add_rep_u64(6403L);
212 primitive.add_rep_i64(6404L);
213 primitive.add_rep_i64(0L);
214 primitive.add_rep_sf64(0L);
215 primitive.add_rep_sf64(6405L);
216 primitive.add_rep_sf64(6406L);
217 primitive.add_rep_s64(6407L);
218 primitive.add_rep_s64(0L);
219 primitive.add_rep_float(0.0f);
220 primitive.add_rep_float(32.1f);
221 primitive.add_rep_float(32.2f);
222 primitive.add_rep_double(64.1L);
223 primitive.add_rep_double(0.0);
224 primitive.add_rep_bool(true);
225 primitive.add_rep_bool(false);
226
227 PrepareExpectingObjectWriterForRepeatedPrimitive();
228 return primitive;
229 }
230
PreparePackedPrimitive()231 PackedPrimitive PreparePackedPrimitive() {
232 PackedPrimitive primitive;
233 primitive.add_rep_fix32(3201);
234 primitive.add_rep_fix32(0);
235 primitive.add_rep_fix32(3202);
236 primitive.add_rep_u32(3203);
237 primitive.add_rep_u32(0);
238 primitive.add_rep_i32(0);
239 primitive.add_rep_i32(3204);
240 primitive.add_rep_i32(3205);
241 primitive.add_rep_sf32(3206);
242 primitive.add_rep_sf32(0);
243 primitive.add_rep_s32(0);
244 primitive.add_rep_s32(3207);
245 primitive.add_rep_s32(3208);
246 primitive.add_rep_fix64(6401L);
247 primitive.add_rep_fix64(0L);
248 primitive.add_rep_u64(0L);
249 primitive.add_rep_u64(6402L);
250 primitive.add_rep_u64(6403L);
251 primitive.add_rep_i64(6404L);
252 primitive.add_rep_i64(0L);
253 primitive.add_rep_sf64(0L);
254 primitive.add_rep_sf64(6405L);
255 primitive.add_rep_sf64(6406L);
256 primitive.add_rep_s64(6407L);
257 primitive.add_rep_s64(0L);
258 primitive.add_rep_float(0.0f);
259 primitive.add_rep_float(32.1f);
260 primitive.add_rep_float(32.2f);
261 primitive.add_rep_double(64.1L);
262 primitive.add_rep_double(0.0);
263 primitive.add_rep_bool(true);
264 primitive.add_rep_bool(false);
265
266 PrepareExpectingObjectWriterForRepeatedPrimitive();
267 return primitive;
268 }
269
UseLowerCamelForEnums()270 void UseLowerCamelForEnums() { use_lower_camel_for_enums_ = true; }
271
UseIntsForEnums()272 void UseIntsForEnums() { use_ints_for_enums_ = true; }
273
UsePreserveProtoFieldNames()274 void UsePreserveProtoFieldNames() { use_preserve_proto_field_names_ = true; }
275
AddTrailingZeros()276 void AddTrailingZeros() { add_trailing_zeros_ = true; }
277
SetRenderUnknownEnumValues(bool value)278 void SetRenderUnknownEnumValues(bool value) {
279 render_unknown_enum_values_ = value;
280 }
281
282 testing::TypeInfoTestHelper helper_;
283
284 ::testing::NiceMock<MockObjectWriter> mock_;
285 ExpectingObjectWriter ow_;
286 bool use_lower_camel_for_enums_;
287 bool use_ints_for_enums_;
288 bool use_preserve_proto_field_names_;
289 bool add_trailing_zeros_;
290 bool render_unknown_enum_values_;
291 };
292
293 INSTANTIATE_TEST_SUITE_P(DifferentTypeInfoSourceTest,
294 ProtostreamObjectSourceTest,
295 ::testing::Values(
296 testing::USE_TYPE_RESOLVER));
297
TEST_P(ProtostreamObjectSourceTest,EmptyMessage)298 TEST_P(ProtostreamObjectSourceTest, EmptyMessage) {
299 Book empty;
300 ow_.StartObject("")->EndObject();
301 DoTest(empty, Book::descriptor());
302 }
303
TEST_P(ProtostreamObjectSourceTest,Primitives)304 TEST_P(ProtostreamObjectSourceTest, Primitives) {
305 Primitive primitive;
306 primitive.set_fix32(3201);
307 primitive.set_u32(3202);
308 primitive.set_i32(3203);
309 primitive.set_sf32(3204);
310 primitive.set_s32(3205);
311 primitive.set_fix64(6401L);
312 primitive.set_u64(6402L);
313 primitive.set_i64(6403L);
314 primitive.set_sf64(6404L);
315 primitive.set_s64(6405L);
316 primitive.set_str("String Value");
317 primitive.set_bytes("Some Bytes");
318 primitive.set_float_(32.1f);
319 primitive.set_double_(64.1L);
320 primitive.set_bool_(true);
321
322 ow_.StartObject("")
323 ->RenderUint32("fix32", bit_cast<uint32>(3201))
324 ->RenderUint32("u32", bit_cast<uint32>(3202))
325 ->RenderInt32("i32", 3203)
326 ->RenderInt32("sf32", 3204)
327 ->RenderInt32("s32", 3205)
328 ->RenderUint64("fix64", bit_cast<uint64>(int64{6401}))
329 ->RenderUint64("u64", bit_cast<uint64>(int64{6402}))
330 ->RenderInt64("i64", 6403L)
331 ->RenderInt64("sf64", 6404L)
332 ->RenderInt64("s64", 6405L)
333 ->RenderString("str", "String Value")
334 ->RenderBytes("bytes", "Some Bytes")
335 ->RenderFloat("float", 32.1f)
336 ->RenderDouble("double", 64.1L)
337 ->RenderBool("bool", true)
338 ->EndObject();
339 DoTest(primitive, Primitive::descriptor());
340 }
341
TEST_P(ProtostreamObjectSourceTest,RepeatingPrimitives)342 TEST_P(ProtostreamObjectSourceTest, RepeatingPrimitives) {
343 Primitive primitive = PrepareRepeatedPrimitive();
344 primitive.add_rep_str("String One");
345 primitive.add_rep_str("String Two");
346 primitive.add_rep_bytes("Some Bytes");
347
348 ow_.StartList("repStr")
349 ->RenderString("", "String One")
350 ->RenderString("", "String Two")
351 ->EndList()
352 ->StartList("repBytes")
353 ->RenderBytes("", "Some Bytes")
354 ->EndList();
355 DoTest(primitive, Primitive::descriptor());
356 }
357
TEST_P(ProtostreamObjectSourceTest,CustomJsonName)358 TEST_P(ProtostreamObjectSourceTest, CustomJsonName) {
359 Author author;
360 author.set_id(12345);
361
362 ow_.StartObject("")->RenderUint64("@id", 12345)->EndObject();
363 DoTest(author, Author::descriptor());
364 }
365
TEST_P(ProtostreamObjectSourceTest,NestedMessage)366 TEST_P(ProtostreamObjectSourceTest, NestedMessage) {
367 Author* author = new Author();
368 author->set_name("Tolstoy");
369 Book book;
370 book.set_title("My Book");
371 book.set_allocated_author(author);
372
373 ow_.StartObject("")
374 ->RenderString("title", "My Book")
375 ->StartObject("author")
376 ->RenderString("name", "Tolstoy")
377 ->EndObject()
378 ->EndObject();
379 DoTest(book, Book::descriptor());
380 }
381
TEST_P(ProtostreamObjectSourceTest,RepeatingField)382 TEST_P(ProtostreamObjectSourceTest, RepeatingField) {
383 Author author;
384 author.set_alive(false);
385 author.set_name("john");
386 author.add_pseudonym("phil");
387 author.add_pseudonym("bob");
388
389 ow_.StartObject("")
390 ->RenderBool("alive", false)
391 ->RenderString("name", "john")
392 ->StartList("pseudonym")
393 ->RenderString("", "phil")
394 ->RenderString("", "bob")
395 ->EndList()
396 ->EndObject();
397 DoTest(author, Author::descriptor());
398 }
399
TEST_P(ProtostreamObjectSourceTest,PackedRepeatingFields)400 TEST_P(ProtostreamObjectSourceTest, PackedRepeatingFields) {
401 DoTest(PreparePackedPrimitive(), PackedPrimitive::descriptor());
402 }
403
TEST_P(ProtostreamObjectSourceTest,NonPackedPackableFieldsActuallyPacked)404 TEST_P(ProtostreamObjectSourceTest, NonPackedPackableFieldsActuallyPacked) {
405 // Protostream is packed, but parse with non-packed Primitive.
406 DoTest(PreparePackedPrimitive(), Primitive::descriptor());
407 }
408
TEST_P(ProtostreamObjectSourceTest,PackedPackableFieldNotActuallyPacked)409 TEST_P(ProtostreamObjectSourceTest, PackedPackableFieldNotActuallyPacked) {
410 // Protostream is not packed, but parse with PackedPrimitive.
411 DoTest(PrepareRepeatedPrimitive(), PackedPrimitive::descriptor());
412 }
413
TEST_P(ProtostreamObjectSourceTest,BadAuthor)414 TEST_P(ProtostreamObjectSourceTest, BadAuthor) {
415 Author author;
416 author.set_alive(false);
417 author.set_name("john");
418 author.set_id(1234L);
419 author.add_pseudonym("phil");
420 author.add_pseudonym("bob");
421
422 ow_.StartObject("")
423 ->StartList("alive")
424 ->RenderBool("", false)
425 ->EndList()
426 ->StartList("name")
427 ->RenderUint64("", static_cast<uint64>('j'))
428 ->RenderUint64("", static_cast<uint64>('o'))
429 ->RenderUint64("", static_cast<uint64>('h'))
430 ->RenderUint64("", static_cast<uint64>('n'))
431 ->EndList()
432 ->RenderString("pseudonym", "phil")
433 ->RenderString("pseudonym", "bob")
434 ->EndObject();
435 // Protostream created with Author, but parsed with BadAuthor.
436 DoTest(author, BadAuthor::descriptor());
437 }
438
TEST_P(ProtostreamObjectSourceTest,NestedBookToBadNestedBook)439 TEST_P(ProtostreamObjectSourceTest, NestedBookToBadNestedBook) {
440 Book* book = new Book();
441 book->set_length(250);
442 book->set_published(2014L);
443 NestedBook nested;
444 nested.set_allocated_book(book);
445
446 ow_.StartObject("")
447 ->StartList("book")
448 ->RenderUint32("", 24) // tag for field length (3 << 3)
449 ->RenderUint32("", 250)
450 ->RenderUint32("", 32) // tag for field published (4 << 3)
451 ->RenderUint32("", 2014)
452 ->EndList()
453 ->EndObject();
454 // Protostream created with NestedBook, but parsed with BadNestedBook.
455 DoTest(nested, BadNestedBook::descriptor());
456 }
457
TEST_P(ProtostreamObjectSourceTest,BadNestedBookToNestedBook)458 TEST_P(ProtostreamObjectSourceTest, BadNestedBookToNestedBook) {
459 BadNestedBook nested;
460 nested.add_book(1);
461 nested.add_book(2);
462 nested.add_book(3);
463 nested.add_book(4);
464 nested.add_book(5);
465 nested.add_book(6);
466 nested.add_book(7);
467
468 ow_.StartObject("")->StartObject("book")->EndObject()->EndObject();
469 // Protostream created with BadNestedBook, but parsed with NestedBook.
470 DoTest(nested, NestedBook::descriptor());
471 }
472
TEST_P(ProtostreamObjectSourceTest,LongRepeatedListDoesNotBreakIntoMultipleJsonLists)473 TEST_P(ProtostreamObjectSourceTest,
474 LongRepeatedListDoesNotBreakIntoMultipleJsonLists) {
475 Book book;
476
477 int repeat = 10000;
478 for (int i = 0; i < repeat; ++i) {
479 Book_Label* label = book.add_labels();
480 label->set_key(StrCat("i", i));
481 label->set_value(StrCat("v", i));
482 }
483
484 // Make sure StartList and EndList are called exactly once (see b/18227499 for
485 // problems when this doesn't happen)
486 EXPECT_CALL(mock_, StartList(_)).Times(1);
487 EXPECT_CALL(mock_, EndList()).Times(1);
488
489 DoTest(book, Book::descriptor());
490 }
491
TEST_P(ProtostreamObjectSourceTest,LowerCamelEnumOutputMacroCase)492 TEST_P(ProtostreamObjectSourceTest, LowerCamelEnumOutputMacroCase) {
493 Book book;
494 book.set_type(Book::ACTION_AND_ADVENTURE);
495
496 UseLowerCamelForEnums();
497
498 ow_.StartObject("")->RenderString("type", "actionAndAdventure")->EndObject();
499 DoTest(book, Book::descriptor());
500 }
501
TEST_P(ProtostreamObjectSourceTest,LowerCamelEnumOutputSnakeCase)502 TEST_P(ProtostreamObjectSourceTest, LowerCamelEnumOutputSnakeCase) {
503 Book book;
504 book.set_type(Book::arts_and_photography);
505
506 UseLowerCamelForEnums();
507
508 ow_.StartObject("")->RenderString("type", "artsAndPhotography")->EndObject();
509 DoTest(book, Book::descriptor());
510 }
511
TEST_P(ProtostreamObjectSourceTest,LowerCamelEnumOutputWithNumber)512 TEST_P(ProtostreamObjectSourceTest, LowerCamelEnumOutputWithNumber) {
513 Book book;
514 book.set_type(Book::I18N_Tech);
515
516 UseLowerCamelForEnums();
517
518 ow_.StartObject("")->RenderString("type", "i18nTech")->EndObject();
519 DoTest(book, Book::descriptor());
520 }
521
TEST_P(ProtostreamObjectSourceTest,EnumCaseIsUnchangedByDefault)522 TEST_P(ProtostreamObjectSourceTest, EnumCaseIsUnchangedByDefault) {
523 Book book;
524 book.set_type(Book::ACTION_AND_ADVENTURE);
525 ow_.StartObject("")
526 ->RenderString("type", "ACTION_AND_ADVENTURE")
527 ->EndObject();
528 DoTest(book, Book::descriptor());
529 }
530
TEST_P(ProtostreamObjectSourceTest,UseIntsForEnumsTest)531 TEST_P(ProtostreamObjectSourceTest, UseIntsForEnumsTest) {
532 Book book;
533 book.set_type(Book::ACTION_AND_ADVENTURE);
534
535 UseIntsForEnums();
536
537 ow_.StartObject("")->RenderInt32("type", 3)->EndObject();
538 DoTest(book, Book::descriptor());
539 }
540
TEST_P(ProtostreamObjectSourceTest,UsePreserveProtoFieldNames)541 TEST_P(ProtostreamObjectSourceTest, UsePreserveProtoFieldNames) {
542 Book book;
543 book.set_snake_field("foo");
544
545 UsePreserveProtoFieldNames();
546
547 ow_.StartObject("")->RenderString("snake_field", "foo")->EndObject();
548 DoTest(book, Book::descriptor());
549 }
550
TEST_P(ProtostreamObjectSourceTest,UnknownEnumAreDroppedWhenRenderUnknownEnumValuesIsUnset)551 TEST_P(ProtostreamObjectSourceTest,
552 UnknownEnumAreDroppedWhenRenderUnknownEnumValuesIsUnset) {
553 Proto3Message message;
554 message.set_enum_value(static_cast<Proto3Message::NestedEnum>(1234));
555
556 SetRenderUnknownEnumValues(false);
557
558 // Unknown enum values are not output.
559 ow_.StartObject("")->EndObject();
560 DoTest(message, Proto3Message::descriptor());
561 }
562
TEST_P(ProtostreamObjectSourceTest,UnknownEnumAreOutputWhenRenderUnknownEnumValuesIsSet)563 TEST_P(ProtostreamObjectSourceTest,
564 UnknownEnumAreOutputWhenRenderUnknownEnumValuesIsSet) {
565 Proto3Message message;
566 message.set_enum_value(static_cast<Proto3Message::NestedEnum>(1234));
567
568 SetRenderUnknownEnumValues(true);
569
570 // Unknown enum values are output.
571 ow_.StartObject("")->RenderInt32("enumValue", 1234)->EndObject();
572 DoTest(message, Proto3Message::descriptor());
573 }
574
TEST_P(ProtostreamObjectSourceTest,CyclicMessageDepthTest)575 TEST_P(ProtostreamObjectSourceTest, CyclicMessageDepthTest) {
576 Cyclic cyclic;
577 cyclic.set_m_int(123);
578
579 Book* book = cyclic.mutable_m_book();
580 book->set_title("book title");
581 Cyclic* current = cyclic.mutable_m_cyclic();
582 Author* current_author = cyclic.add_m_author();
583 for (int i = 0; i < 63; ++i) {
584 Author* next = current_author->add_friend_();
585 next->set_id(i);
586 next->set_name(StrCat("author_name_", i));
587 next->set_alive(true);
588 current_author = next;
589 }
590
591 // Recursive message with depth (65) > max (max is 64).
592 for (int i = 0; i < 64; ++i) {
593 Cyclic* next = current->mutable_m_cyclic();
594 next->set_m_str(StrCat("count_", i));
595 current = next;
596 }
597
598 Status status = ExecuteTest(cyclic, Cyclic::descriptor());
599 EXPECT_EQ(util::error::INVALID_ARGUMENT, status.code());
600 }
601
602 class ProtostreamObjectSourceMapsTest : public ProtostreamObjectSourceTest {
603 protected:
ProtostreamObjectSourceMapsTest()604 ProtostreamObjectSourceMapsTest() {
605 helper_.ResetTypeInfo(MapOut::descriptor());
606 }
607 };
608
609 INSTANTIATE_TEST_SUITE_P(DifferentTypeInfoSourceTest,
610 ProtostreamObjectSourceMapsTest,
611 ::testing::Values(
612 testing::USE_TYPE_RESOLVER));
613
614 // Tests JSON map.
615 //
616 // This is the example expected output.
617 // {
618 // "map1": {
619 // "key1": {
620 // "foo": "foovalue"
621 // },
622 // "key2": {
623 // "foo": "barvalue"
624 // }
625 // },
626 // "map2": {
627 // "nestedself": {
628 // "map1": {
629 // "nested_key1": {
630 // "foo": "nested_foo"
631 // }
632 // },
633 // "bar": "nested_bar_string"
634 // }
635 // },
636 // "map3": {
637 // "111": "one one one"
638 // },
639 // "bar": "top bar"
640 // }
TEST_P(ProtostreamObjectSourceMapsTest,MapsTest)641 TEST_P(ProtostreamObjectSourceMapsTest, MapsTest) {
642 MapOut out;
643 (*out.mutable_map1())["key1"].set_foo("foovalue");
644 (*out.mutable_map1())["key2"].set_foo("barvalue");
645
646 MapOut* nested_value = &(*out.mutable_map2())["nestedself"];
647 (*nested_value->mutable_map1())["nested_key1"].set_foo("nested_foo");
648 nested_value->set_bar("nested_bar_string");
649
650 (*out.mutable_map3())[111] = "one one one";
651
652 out.set_bar("top bar");
653
654 ow_.StartObject("")
655 ->StartObject("map1")
656 ->StartObject("key1")
657 ->RenderString("foo", "foovalue")
658 ->EndObject()
659 ->StartObject("key2")
660 ->RenderString("foo", "barvalue")
661 ->EndObject()
662 ->StartObject("map2")
663 ->StartObject("nestedself")
664 ->StartObject("map1")
665 ->StartObject("nested_key1")
666 ->RenderString("foo", "nested_foo")
667 ->EndObject()
668 ->EndObject()
669 ->RenderString("bar", "nested_bar_string")
670 ->EndObject()
671 ->EndObject()
672 ->StartObject("map3")
673 ->RenderString("111", "one one one")
674 ->EndObject()
675 ->EndObject()
676 ->RenderString("bar", "top bar")
677 ->EndObject();
678
679 DoTest(out, MapOut::descriptor());
680 }
681
TEST_P(ProtostreamObjectSourceMapsTest,MissingKeysTest)682 TEST_P(ProtostreamObjectSourceMapsTest, MissingKeysTest) {
683 // MapOutWireFormat has the same wire representation with MapOut but uses
684 // repeated message fields to represent map fields so we can intentionally
685 // leave out the key field or the value field of a map entry.
686 MapOutWireFormat out;
687 // Create some map entries without keys. They will be rendered with the
688 // default values ("" for strings, "0" for integers, etc.).
689 // {
690 // "map1": {
691 // "": {
692 // "foo": "foovalue"
693 // }
694 // },
695 // "map2": {
696 // "": {
697 // "map1": {
698 // "nested_key1": {
699 // "foo": "nested_foo"
700 // }
701 // }
702 // }
703 // },
704 // "map3": {
705 // "0": "one one one"
706 // },
707 // "map4": {
708 // "false": "bool"
709 // }
710 // }
711 out.add_map1()->mutable_value()->set_foo("foovalue");
712 MapOut* nested = out.add_map2()->mutable_value();
713 (*nested->mutable_map1())["nested_key1"].set_foo("nested_foo");
714 out.add_map3()->set_value("one one one");
715 out.add_map4()->set_value("bool");
716
717 ow_.StartObject("")
718 ->StartObject("map1")
719 ->StartObject("")
720 ->RenderString("foo", "foovalue")
721 ->EndObject()
722 ->EndObject()
723 ->StartObject("map2")
724 ->StartObject("")
725 ->StartObject("map1")
726 ->StartObject("nested_key1")
727 ->RenderString("foo", "nested_foo")
728 ->EndObject()
729 ->EndObject()
730 ->EndObject()
731 ->EndObject()
732 ->StartObject("map3")
733 ->RenderString("0", "one one one")
734 ->EndObject()
735 ->StartObject("map4")
736 ->RenderString("false", "bool")
737 ->EndObject()
738 ->EndObject();
739
740 DoTest(out, MapOut::descriptor());
741 }
742
743 class ProtostreamObjectSourceAnysTest : public ProtostreamObjectSourceTest {
744 protected:
ProtostreamObjectSourceAnysTest()745 ProtostreamObjectSourceAnysTest() {
746 helper_.ResetTypeInfo({AnyOut::descriptor(), Book::descriptor(),
747 google::protobuf::Any::descriptor()});
748 }
749 };
750
751 INSTANTIATE_TEST_SUITE_P(DifferentTypeInfoSourceTest,
752 ProtostreamObjectSourceAnysTest,
753 ::testing::Values(
754 testing::USE_TYPE_RESOLVER));
755
756 // Tests JSON any support.
757 //
758 // This is the example expected output.
759 // {
760 // "any": {
761 // "@type": "type.googleapis.com/google.protobuf.testing.AnyM"
762 // "foo": "foovalue"
763 // }
764 // }
TEST_P(ProtostreamObjectSourceAnysTest,BasicAny)765 TEST_P(ProtostreamObjectSourceAnysTest, BasicAny) {
766 AnyOut out;
767 ::google::protobuf::Any* any = out.mutable_any();
768
769 AnyM m;
770 m.set_foo("foovalue");
771 any->PackFrom(m);
772
773 ow_.StartObject("")
774 ->StartObject("any")
775 ->RenderString("@type",
776 "type.googleapis.com/proto_util_converter.testing.AnyM")
777 ->RenderString("foo", "foovalue")
778 ->EndObject()
779 ->EndObject();
780
781 DoTest(out, AnyOut::descriptor());
782 }
783
TEST_P(ProtostreamObjectSourceAnysTest,LowerCamelEnumOutputSnakeCase)784 TEST_P(ProtostreamObjectSourceAnysTest, LowerCamelEnumOutputSnakeCase) {
785 AnyOut out;
786 ::google::protobuf::Any* any = out.mutable_any();
787
788 Book book;
789 book.set_type(Book::arts_and_photography);
790 any->PackFrom(book);
791
792 UseLowerCamelForEnums();
793
794 ow_.StartObject("")
795 ->StartObject("any")
796 ->RenderString("@type",
797 "type.googleapis.com/proto_util_converter.testing.Book")
798 ->RenderString("type", "artsAndPhotography")
799 ->EndObject()
800 ->EndObject();
801
802 DoTest(out, AnyOut::descriptor());
803 }
804
TEST_P(ProtostreamObjectSourceAnysTest,UseIntsForEnumsTest)805 TEST_P(ProtostreamObjectSourceAnysTest, UseIntsForEnumsTest) {
806 AnyOut out;
807 ::google::protobuf::Any* any = out.mutable_any();
808
809 Book book;
810 book.set_type(Book::ACTION_AND_ADVENTURE);
811 any->PackFrom(book);
812
813 UseIntsForEnums();
814
815 ow_.StartObject("")
816 ->StartObject("any")
817 ->RenderString("@type",
818 "type.googleapis.com/proto_util_converter.testing.Book")
819 ->RenderInt32("type", 3)
820 ->EndObject()
821 ->EndObject();
822
823 DoTest(out, AnyOut::descriptor());
824 }
825
TEST_P(ProtostreamObjectSourceAnysTest,UsePreserveProtoFieldNames)826 TEST_P(ProtostreamObjectSourceAnysTest, UsePreserveProtoFieldNames) {
827 AnyOut out;
828 ::google::protobuf::Any* any = out.mutable_any();
829
830 Book book;
831 book.set_snake_field("foo");
832 any->PackFrom(book);
833
834 UsePreserveProtoFieldNames();
835
836 ow_.StartObject("")
837 ->StartObject("any")
838 ->RenderString("@type",
839 "type.googleapis.com/proto_util_converter.testing.Book")
840 ->RenderString("snake_field", "foo")
841 ->EndObject()
842 ->EndObject();
843
844 DoTest(out, AnyOut::descriptor());
845 }
846
TEST_P(ProtostreamObjectSourceAnysTest,RecursiveAny)847 TEST_P(ProtostreamObjectSourceAnysTest, RecursiveAny) {
848 AnyOut out;
849 ::google::protobuf::Any* any = out.mutable_any();
850 any->set_type_url("type.googleapis.com/google.protobuf.Any");
851
852 ::google::protobuf::Any nested_any;
853 nested_any.set_type_url(
854 "type.googleapis.com/proto_util_converter.testing.AnyM");
855
856 AnyM m;
857 m.set_foo("foovalue");
858 nested_any.set_value(m.SerializeAsString());
859
860 any->set_value(nested_any.SerializeAsString());
861
862 ow_.StartObject("")
863 ->StartObject("any")
864 ->RenderString("@type", "type.googleapis.com/google.protobuf.Any")
865 ->StartObject("value")
866 ->RenderString("@type",
867 "type.googleapis.com/proto_util_converter.testing.AnyM")
868 ->RenderString("foo", "foovalue")
869 ->EndObject()
870 ->EndObject()
871 ->EndObject();
872
873 DoTest(out, AnyOut::descriptor());
874 }
875
TEST_P(ProtostreamObjectSourceAnysTest,DoubleRecursiveAny)876 TEST_P(ProtostreamObjectSourceAnysTest, DoubleRecursiveAny) {
877 AnyOut out;
878 ::google::protobuf::Any* any = out.mutable_any();
879 any->set_type_url("type.googleapis.com/google.protobuf.Any");
880
881 ::google::protobuf::Any nested_any;
882 nested_any.set_type_url("type.googleapis.com/google.protobuf.Any");
883
884 ::google::protobuf::Any second_nested_any;
885 second_nested_any.set_type_url(
886 "type.googleapis.com/proto_util_converter.testing.AnyM");
887
888 AnyM m;
889 m.set_foo("foovalue");
890 second_nested_any.set_value(m.SerializeAsString());
891 nested_any.set_value(second_nested_any.SerializeAsString());
892 any->set_value(nested_any.SerializeAsString());
893
894 ow_.StartObject("")
895 ->StartObject("any")
896 ->RenderString("@type", "type.googleapis.com/google.protobuf.Any")
897 ->StartObject("value")
898 ->RenderString("@type", "type.googleapis.com/google.protobuf.Any")
899 ->StartObject("value")
900 ->RenderString("@type",
901 "type.googleapis.com/proto_util_converter.testing.AnyM")
902 ->RenderString("foo", "foovalue")
903 ->EndObject()
904 ->EndObject()
905 ->EndObject()
906 ->EndObject();
907
908 DoTest(out, AnyOut::descriptor());
909 }
910
TEST_P(ProtostreamObjectSourceAnysTest,EmptyAnyOutputsEmptyObject)911 TEST_P(ProtostreamObjectSourceAnysTest, EmptyAnyOutputsEmptyObject) {
912 AnyOut out;
913 out.mutable_any();
914
915 ow_.StartObject("")->StartObject("any")->EndObject()->EndObject();
916
917 DoTest(out, AnyOut::descriptor());
918 }
919
TEST_P(ProtostreamObjectSourceAnysTest,EmptyWithTypeAndNoValueOutputsType)920 TEST_P(ProtostreamObjectSourceAnysTest, EmptyWithTypeAndNoValueOutputsType) {
921 AnyOut out;
922 out.mutable_any()->set_type_url("foo.googleapis.com/my.Type");
923
924 ow_.StartObject("")
925 ->StartObject("any")
926 ->RenderString("@type", "foo.googleapis.com/my.Type")
927 ->EndObject()
928 ->EndObject();
929
930 DoTest(out, AnyOut::descriptor());
931 }
932
TEST_P(ProtostreamObjectSourceAnysTest,MissingTypeUrlError)933 TEST_P(ProtostreamObjectSourceAnysTest, MissingTypeUrlError) {
934 AnyOut out;
935 ::google::protobuf::Any* any = out.mutable_any();
936
937 AnyM m;
938 m.set_foo("foovalue");
939 any->set_value(m.SerializeAsString());
940
941 // We start the "AnyOut" part and then fail when we hit the Any object.
942 ow_.StartObject("");
943
944 Status status = ExecuteTest(out, AnyOut::descriptor());
945 EXPECT_EQ(util::error::INTERNAL, status.code());
946 }
947
TEST_P(ProtostreamObjectSourceAnysTest,UnknownTypeServiceError)948 TEST_P(ProtostreamObjectSourceAnysTest, UnknownTypeServiceError) {
949 AnyOut out;
950 ::google::protobuf::Any* any = out.mutable_any();
951 any->set_type_url("foo.googleapis.com/my.own.Type");
952
953 AnyM m;
954 m.set_foo("foovalue");
955 any->set_value(m.SerializeAsString());
956
957 // We start the "AnyOut" part and then fail when we hit the Any object.
958 ow_.StartObject("");
959
960 Status status = ExecuteTest(out, AnyOut::descriptor());
961 EXPECT_EQ(util::error::INTERNAL, status.code());
962 }
963
TEST_P(ProtostreamObjectSourceAnysTest,UnknownTypeError)964 TEST_P(ProtostreamObjectSourceAnysTest, UnknownTypeError) {
965 AnyOut out;
966 ::google::protobuf::Any* any = out.mutable_any();
967 any->set_type_url("type.googleapis.com/unknown.Type");
968
969 AnyM m;
970 m.set_foo("foovalue");
971 any->set_value(m.SerializeAsString());
972
973 // We start the "AnyOut" part and then fail when we hit the Any object.
974 ow_.StartObject("");
975
976 Status status = ExecuteTest(out, AnyOut::descriptor());
977 EXPECT_EQ(util::error::INTERNAL, status.code());
978 }
979
980 class ProtostreamObjectSourceStructTest : public ProtostreamObjectSourceTest {
981 protected:
ProtostreamObjectSourceStructTest()982 ProtostreamObjectSourceStructTest() {
983 helper_.ResetTypeInfo(StructType::descriptor(),
984 google::protobuf::Struct::descriptor());
985 }
986 };
987
988 INSTANTIATE_TEST_SUITE_P(DifferentTypeInfoSourceTest,
989 ProtostreamObjectSourceStructTest,
990 ::testing::Values(
991 testing::USE_TYPE_RESOLVER));
992
993 // Tests struct
994 //
995 // "object": {
996 // "k1": 123,
997 // "k2": true
998 // }
TEST_P(ProtostreamObjectSourceStructTest,StructRenderSuccess)999 TEST_P(ProtostreamObjectSourceStructTest, StructRenderSuccess) {
1000 StructType out;
1001 google::protobuf::Struct* s = out.mutable_object();
1002 s->mutable_fields()->operator[]("k1").set_number_value(123);
1003 s->mutable_fields()->operator[]("k2").set_bool_value(true);
1004
1005 ow_.StartObject("")
1006 ->StartObject("object")
1007 ->RenderDouble("k1", 123)
1008 ->RenderBool("k2", true)
1009 ->EndObject()
1010 ->EndObject();
1011
1012 DoTest(out, StructType::descriptor());
1013 }
1014
TEST_P(ProtostreamObjectSourceStructTest,MissingValueSkipsField)1015 TEST_P(ProtostreamObjectSourceStructTest, MissingValueSkipsField) {
1016 StructType out;
1017 google::protobuf::Struct* s = out.mutable_object();
1018 s->mutable_fields()->operator[]("k1");
1019
1020 ow_.StartObject("")->StartObject("object")->EndObject()->EndObject();
1021
1022 DoTest(out, StructType::descriptor());
1023 }
1024
1025 class ProtostreamObjectSourceFieldMaskTest
1026 : public ProtostreamObjectSourceTest {
1027 protected:
ProtostreamObjectSourceFieldMaskTest()1028 ProtostreamObjectSourceFieldMaskTest() {
1029 helper_.ResetTypeInfo(FieldMaskTest::descriptor(),
1030 google::protobuf::FieldMask::descriptor());
1031 }
1032 };
1033
1034 INSTANTIATE_TEST_SUITE_P(DifferentTypeInfoSourceTest,
1035 ProtostreamObjectSourceFieldMaskTest,
1036 ::testing::Values(
1037 testing::USE_TYPE_RESOLVER));
1038
TEST_P(ProtostreamObjectSourceFieldMaskTest,FieldMaskRenderSuccess)1039 TEST_P(ProtostreamObjectSourceFieldMaskTest, FieldMaskRenderSuccess) {
1040 FieldMaskTest out;
1041 out.set_id("1");
1042 out.mutable_single_mask()->add_paths("path1");
1043 out.mutable_single_mask()->add_paths("snake_case_path2");
1044 ::google::protobuf::FieldMask* mask = out.add_repeated_mask();
1045 mask->add_paths("path3");
1046 mask = out.add_repeated_mask();
1047 mask->add_paths("snake_case_path4");
1048 mask->add_paths("path5");
1049 NestedFieldMask* nested = out.add_nested_mask();
1050 nested->set_data("data");
1051 nested->mutable_single_mask()->add_paths("nested.path1");
1052 nested->mutable_single_mask()->add_paths("nested_field.snake_case_path2");
1053 mask = nested->add_repeated_mask();
1054 mask->add_paths("nested_field.path3");
1055 mask->add_paths("nested.snake_case_path4");
1056 mask = nested->add_repeated_mask();
1057 mask->add_paths("nested.path5");
1058 mask = nested->add_repeated_mask();
1059 mask->add_paths(
1060 "snake_case.map_field[\"map_key_should_be_ignored\"].nested_snake_case."
1061 "map_field[\"map_key_sho\\\"uld_be_ignored\"]");
1062
1063 ow_.StartObject("")
1064 ->RenderString("id", "1")
1065 ->RenderString("singleMask", "path1,snakeCasePath2")
1066 ->StartList("repeatedMask")
1067 ->RenderString("", "path3")
1068 ->RenderString("", "snakeCasePath4,path5")
1069 ->EndList()
1070 ->StartList("nestedMask")
1071 ->StartObject("")
1072 ->RenderString("data", "data")
1073 ->RenderString("singleMask", "nested.path1,nestedField.snakeCasePath2")
1074 ->StartList("repeatedMask")
1075 ->RenderString("", "nestedField.path3,nested.snakeCasePath4")
1076 ->RenderString("", "nested.path5")
1077 ->RenderString("",
1078 "snakeCase.mapField[\"map_key_should_be_ignored\"]."
1079 "nestedSnakeCase.mapField[\"map_key_sho\\\"uld_be_"
1080 "ignored\"]")
1081 ->EndList()
1082 ->EndObject()
1083 ->EndList()
1084 ->EndObject();
1085
1086 DoTest(out, FieldMaskTest::descriptor());
1087 }
1088
1089 class ProtostreamObjectSourceTimestampTest
1090 : public ProtostreamObjectSourceTest {
1091 protected:
ProtostreamObjectSourceTimestampTest()1092 ProtostreamObjectSourceTimestampTest() {
1093 helper_.ResetTypeInfo(TimestampDuration::descriptor());
1094 }
1095 };
1096
1097 INSTANTIATE_TEST_SUITE_P(DifferentTypeInfoSourceTest,
1098 ProtostreamObjectSourceTimestampTest,
1099 ::testing::Values(
1100 testing::USE_TYPE_RESOLVER));
1101
TEST_P(ProtostreamObjectSourceTimestampTest,InvalidTimestampBelowMinTest)1102 TEST_P(ProtostreamObjectSourceTimestampTest, InvalidTimestampBelowMinTest) {
1103 TimestampDuration out;
1104 google::protobuf::Timestamp* ts = out.mutable_ts();
1105 // Min allowed seconds - 1
1106 ts->set_seconds(kTimestampMinSeconds - 1);
1107 ow_.StartObject("");
1108
1109 Status status = ExecuteTest(out, TimestampDuration::descriptor());
1110 EXPECT_EQ(util::error::INTERNAL, status.code());
1111 }
1112
TEST_P(ProtostreamObjectSourceTimestampTest,InvalidTimestampAboveMaxTest)1113 TEST_P(ProtostreamObjectSourceTimestampTest, InvalidTimestampAboveMaxTest) {
1114 TimestampDuration out;
1115 google::protobuf::Timestamp* ts = out.mutable_ts();
1116 // Max allowed seconds + 1
1117 ts->set_seconds(kTimestampMaxSeconds + 1);
1118 ow_.StartObject("");
1119
1120 Status status = ExecuteTest(out, TimestampDuration::descriptor());
1121 EXPECT_EQ(util::error::INTERNAL, status.code());
1122 }
1123
TEST_P(ProtostreamObjectSourceTimestampTest,InvalidDurationBelowMinTest)1124 TEST_P(ProtostreamObjectSourceTimestampTest, InvalidDurationBelowMinTest) {
1125 TimestampDuration out;
1126 google::protobuf::Duration* dur = out.mutable_dur();
1127 // Min allowed seconds - 1
1128 dur->set_seconds(kDurationMinSeconds - 1);
1129 ow_.StartObject("");
1130
1131 Status status = ExecuteTest(out, TimestampDuration::descriptor());
1132 EXPECT_EQ(util::error::INTERNAL, status.code());
1133 }
1134
TEST_P(ProtostreamObjectSourceTimestampTest,InvalidDurationAboveMaxTest)1135 TEST_P(ProtostreamObjectSourceTimestampTest, InvalidDurationAboveMaxTest) {
1136 TimestampDuration out;
1137 google::protobuf::Duration* dur = out.mutable_dur();
1138 // Min allowed seconds + 1
1139 dur->set_seconds(kDurationMaxSeconds + 1);
1140 ow_.StartObject("");
1141
1142 Status status = ExecuteTest(out, TimestampDuration::descriptor());
1143 EXPECT_EQ(util::error::INTERNAL, status.code());
1144 }
1145
TEST_P(ProtostreamObjectSourceTimestampTest,TimestampDurationDefaultValue)1146 TEST_P(ProtostreamObjectSourceTimestampTest, TimestampDurationDefaultValue) {
1147 TimestampDuration out;
1148 out.mutable_ts()->set_seconds(0);
1149 out.mutable_dur()->set_seconds(0);
1150
1151 ow_.StartObject("")
1152 ->RenderString("ts", "1970-01-01T00:00:00Z")
1153 ->RenderString("dur", "0s")
1154 ->EndObject();
1155
1156 DoTest(out, TimestampDuration::descriptor());
1157 }
1158
1159
1160
1161 } // namespace converter
1162 } // namespace util
1163 } // namespace protobuf
1164 } // namespace google
1165