| // Protocol Buffers - Google's data interchange format |
| // Copyright 2008 Google Inc. All rights reserved. |
| // https://developers.google.com/protocol-buffers/ |
| // |
| // Redistribution and use in source and binary forms, with or without |
| // modification, are permitted provided that the following conditions are |
| // met: |
| // |
| // * Redistributions of source code must retain the above copyright |
| // notice, this list of conditions and the following disclaimer. |
| // * Redistributions in binary form must reproduce the above |
| // copyright notice, this list of conditions and the following disclaimer |
| // in the documentation and/or other materials provided with the |
| // distribution. |
| // * Neither the name of Google Inc. nor the names of its |
| // contributors may be used to endorse or promote products derived from |
| // this software without specific prior written permission. |
| // |
| // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
| // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
| // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
| // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
| // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
| // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
| // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
| // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
| // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
| // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| |
| #include <google/protobuf/util/internal/protostream_objectsource.h> |
| |
| #include <memory> |
| #ifndef _SHARED_PTR_H |
| #include <google/protobuf/stubs/shared_ptr.h> |
| #endif |
| #include <sstream> |
| |
| #include <google/protobuf/stubs/casts.h> |
| #include <google/protobuf/any.pb.h> |
| #include <google/protobuf/io/coded_stream.h> |
| #include <google/protobuf/io/zero_copy_stream_impl_lite.h> |
| #include <google/protobuf/descriptor.h> |
| #include <google/protobuf/util/internal/expecting_objectwriter.h> |
| #include <google/protobuf/util/internal/testdata/books.pb.h> |
| #include <google/protobuf/util/internal/testdata/field_mask.pb.h> |
| #include <google/protobuf/util/internal/type_info_test_helper.h> |
| #include <google/protobuf/util/internal/constants.h> |
| #include <google/protobuf/stubs/strutil.h> |
| #include <google/protobuf/util/internal/testdata/anys.pb.h> |
| #include <google/protobuf/util/internal/testdata/maps.pb.h> |
| #include <google/protobuf/util/internal/testdata/struct.pb.h> |
| #include <google/protobuf/util/internal/testdata/timestamp_duration.pb.h> |
| #include <gtest/gtest.h> |
| |
| |
| namespace google { |
| namespace protobuf { |
| namespace util { |
| namespace converter { |
| |
| using google::protobuf::Descriptor; |
| using google::protobuf::DescriptorPool; |
| using google::protobuf::FileDescriptorProto; |
| using google::protobuf::Message; |
| using google::protobuf::io::ArrayInputStream; |
| using google::protobuf::io::CodedInputStream; |
| using util::Status; |
| using google::protobuf::testing::Author; |
| using google::protobuf::testing::BadAuthor; |
| using google::protobuf::testing::BadNestedBook; |
| using google::protobuf::testing::Book; |
| using google::protobuf::testing::Cyclic; |
| using google::protobuf::testing::Book_Label; |
| using google::protobuf::testing::NestedBook; |
| using google::protobuf::testing::PackedPrimitive; |
| using google::protobuf::testing::Primitive; |
| using google::protobuf::testing::more_author; |
| using google::protobuf::testing::maps::MapOut; |
| using google::protobuf::testing::maps::MapOutWireFormat; |
| using google::protobuf::testing::timestampduration::TimestampDuration; |
| using google::protobuf::testing::anys::AnyOut; |
| using google::protobuf::testing::anys::AnyM; |
| using google::protobuf::testing::FieldMaskTest; |
| using google::protobuf::testing::NestedFieldMask; |
| using google::protobuf::testing::structs::StructType; |
| using ::testing::_; |
| |
| |
| namespace { |
| string GetTypeUrl(const Descriptor* descriptor) { |
| return string(kTypeServiceBaseUrl) + "/" + descriptor->full_name(); |
| } |
| } // namespace |
| |
| class ProtostreamObjectSourceTest |
| : public ::testing::TestWithParam<testing::TypeInfoSource> { |
| protected: |
| ProtostreamObjectSourceTest() |
| : helper_(GetParam()), |
| mock_(), |
| ow_(&mock_), |
| use_lower_camel_for_enums_(false) { |
| helper_.ResetTypeInfo(Book::descriptor()); |
| } |
| |
| virtual ~ProtostreamObjectSourceTest() {} |
| |
| void DoTest(const Message& msg, const Descriptor* descriptor) { |
| Status status = ExecuteTest(msg, descriptor); |
| EXPECT_EQ(Status::OK, status); |
| } |
| |
| Status ExecuteTest(const Message& msg, const Descriptor* descriptor) { |
| ostringstream oss; |
| msg.SerializePartialToOstream(&oss); |
| string proto = oss.str(); |
| ArrayInputStream arr_stream(proto.data(), proto.size()); |
| CodedInputStream in_stream(&arr_stream); |
| |
| google::protobuf::scoped_ptr<ProtoStreamObjectSource> os( |
| helper_.NewProtoSource(&in_stream, GetTypeUrl(descriptor))); |
| if (use_lower_camel_for_enums_) os->set_use_lower_camel_for_enums(true); |
| os->set_max_recursion_depth(64); |
| return os->WriteTo(&mock_); |
| } |
| |
| void PrepareExpectingObjectWriterForRepeatedPrimitive() { |
| ow_.StartObject("") |
| ->StartList("repFix32") |
| ->RenderUint32("", bit_cast<uint32>(3201)) |
| ->RenderUint32("", bit_cast<uint32>(0)) |
| ->RenderUint32("", bit_cast<uint32>(3202)) |
| ->EndList() |
| ->StartList("repU32") |
| ->RenderUint32("", bit_cast<uint32>(3203)) |
| ->RenderUint32("", bit_cast<uint32>(0)) |
| ->EndList() |
| ->StartList("repI32") |
| ->RenderInt32("", 0) |
| ->RenderInt32("", 3204) |
| ->RenderInt32("", 3205) |
| ->EndList() |
| ->StartList("repSf32") |
| ->RenderInt32("", 3206) |
| ->RenderInt32("", 0) |
| ->EndList() |
| ->StartList("repS32") |
| ->RenderInt32("", 0) |
| ->RenderInt32("", 3207) |
| ->RenderInt32("", 3208) |
| ->EndList() |
| ->StartList("repFix64") |
| ->RenderUint64("", bit_cast<uint64>(6401LL)) |
| ->RenderUint64("", bit_cast<uint64>(0LL)) |
| ->EndList() |
| ->StartList("repU64") |
| ->RenderUint64("", bit_cast<uint64>(0LL)) |
| ->RenderUint64("", bit_cast<uint64>(6402LL)) |
| ->RenderUint64("", bit_cast<uint64>(6403LL)) |
| ->EndList() |
| ->StartList("repI64") |
| ->RenderInt64("", 6404L) |
| ->RenderInt64("", 0L) |
| ->EndList() |
| ->StartList("repSf64") |
| ->RenderInt64("", 0L) |
| ->RenderInt64("", 6405L) |
| ->RenderInt64("", 6406L) |
| ->EndList() |
| ->StartList("repS64") |
| ->RenderInt64("", 6407L) |
| ->RenderInt64("", 0L) |
| ->EndList() |
| ->StartList("repFloat") |
| ->RenderFloat("", 0.0f) |
| ->RenderFloat("", 32.1f) |
| ->RenderFloat("", 32.2f) |
| ->EndList() |
| ->StartList("repDouble") |
| ->RenderDouble("", 64.1L) |
| ->RenderDouble("", 0.0L) |
| ->EndList() |
| ->StartList("repBool") |
| ->RenderBool("", true) |
| ->RenderBool("", false) |
| ->EndList() |
| ->EndObject(); |
| } |
| |
| Primitive PrepareRepeatedPrimitive() { |
| Primitive primitive; |
| primitive.add_rep_fix32(3201); |
| primitive.add_rep_fix32(0); |
| primitive.add_rep_fix32(3202); |
| primitive.add_rep_u32(3203); |
| primitive.add_rep_u32(0); |
| primitive.add_rep_i32(0); |
| primitive.add_rep_i32(3204); |
| primitive.add_rep_i32(3205); |
| primitive.add_rep_sf32(3206); |
| primitive.add_rep_sf32(0); |
| primitive.add_rep_s32(0); |
| primitive.add_rep_s32(3207); |
| primitive.add_rep_s32(3208); |
| primitive.add_rep_fix64(6401L); |
| primitive.add_rep_fix64(0L); |
| primitive.add_rep_u64(0L); |
| primitive.add_rep_u64(6402L); |
| primitive.add_rep_u64(6403L); |
| primitive.add_rep_i64(6404L); |
| primitive.add_rep_i64(0L); |
| primitive.add_rep_sf64(0L); |
| primitive.add_rep_sf64(6405L); |
| primitive.add_rep_sf64(6406L); |
| primitive.add_rep_s64(6407L); |
| primitive.add_rep_s64(0L); |
| primitive.add_rep_float(0.0f); |
| primitive.add_rep_float(32.1f); |
| primitive.add_rep_float(32.2f); |
| primitive.add_rep_double(64.1L); |
| primitive.add_rep_double(0.0); |
| primitive.add_rep_bool(true); |
| primitive.add_rep_bool(false); |
| |
| PrepareExpectingObjectWriterForRepeatedPrimitive(); |
| return primitive; |
| } |
| |
| PackedPrimitive PreparePackedPrimitive() { |
| PackedPrimitive primitive; |
| primitive.add_rep_fix32(3201); |
| primitive.add_rep_fix32(0); |
| primitive.add_rep_fix32(3202); |
| primitive.add_rep_u32(3203); |
| primitive.add_rep_u32(0); |
| primitive.add_rep_i32(0); |
| primitive.add_rep_i32(3204); |
| primitive.add_rep_i32(3205); |
| primitive.add_rep_sf32(3206); |
| primitive.add_rep_sf32(0); |
| primitive.add_rep_s32(0); |
| primitive.add_rep_s32(3207); |
| primitive.add_rep_s32(3208); |
| primitive.add_rep_fix64(6401L); |
| primitive.add_rep_fix64(0L); |
| primitive.add_rep_u64(0L); |
| primitive.add_rep_u64(6402L); |
| primitive.add_rep_u64(6403L); |
| primitive.add_rep_i64(6404L); |
| primitive.add_rep_i64(0L); |
| primitive.add_rep_sf64(0L); |
| primitive.add_rep_sf64(6405L); |
| primitive.add_rep_sf64(6406L); |
| primitive.add_rep_s64(6407L); |
| primitive.add_rep_s64(0L); |
| primitive.add_rep_float(0.0f); |
| primitive.add_rep_float(32.1f); |
| primitive.add_rep_float(32.2f); |
| primitive.add_rep_double(64.1L); |
| primitive.add_rep_double(0.0); |
| primitive.add_rep_bool(true); |
| primitive.add_rep_bool(false); |
| |
| PrepareExpectingObjectWriterForRepeatedPrimitive(); |
| return primitive; |
| } |
| |
| void UseLowerCamelForEnums() { use_lower_camel_for_enums_ = true; } |
| |
| testing::TypeInfoTestHelper helper_; |
| |
| ::testing::NiceMock<MockObjectWriter> mock_; |
| ExpectingObjectWriter ow_; |
| bool use_lower_camel_for_enums_; |
| }; |
| |
| INSTANTIATE_TEST_CASE_P(DifferentTypeInfoSourceTest, |
| ProtostreamObjectSourceTest, |
| ::testing::Values( |
| testing::USE_TYPE_RESOLVER)); |
| |
| TEST_P(ProtostreamObjectSourceTest, EmptyMessage) { |
| Book empty; |
| ow_.StartObject("")->EndObject(); |
| DoTest(empty, Book::descriptor()); |
| } |
| |
| TEST_P(ProtostreamObjectSourceTest, Primitives) { |
| Primitive primitive; |
| primitive.set_fix32(3201); |
| primitive.set_u32(3202); |
| primitive.set_i32(3203); |
| primitive.set_sf32(3204); |
| primitive.set_s32(3205); |
| primitive.set_fix64(6401L); |
| primitive.set_u64(6402L); |
| primitive.set_i64(6403L); |
| primitive.set_sf64(6404L); |
| primitive.set_s64(6405L); |
| primitive.set_str("String Value"); |
| primitive.set_bytes("Some Bytes"); |
| primitive.set_float_(32.1f); |
| primitive.set_double_(64.1L); |
| primitive.set_bool_(true); |
| |
| ow_.StartObject("") |
| ->RenderUint32("fix32", bit_cast<uint32>(3201)) |
| ->RenderUint32("u32", bit_cast<uint32>(3202)) |
| ->RenderInt32("i32", 3203) |
| ->RenderInt32("sf32", 3204) |
| ->RenderInt32("s32", 3205) |
| ->RenderUint64("fix64", bit_cast<uint64>(6401LL)) |
| ->RenderUint64("u64", bit_cast<uint64>(6402LL)) |
| ->RenderInt64("i64", 6403L) |
| ->RenderInt64("sf64", 6404L) |
| ->RenderInt64("s64", 6405L) |
| ->RenderString("str", "String Value") |
| ->RenderBytes("bytes", "Some Bytes") |
| ->RenderFloat("float", 32.1f) |
| ->RenderDouble("double", 64.1L) |
| ->RenderBool("bool", true) |
| ->EndObject(); |
| DoTest(primitive, Primitive::descriptor()); |
| } |
| |
| TEST_P(ProtostreamObjectSourceTest, RepeatingPrimitives) { |
| Primitive primitive = PrepareRepeatedPrimitive(); |
| primitive.add_rep_str("String One"); |
| primitive.add_rep_str("String Two"); |
| primitive.add_rep_bytes("Some Bytes"); |
| |
| ow_.StartList("repStr") |
| ->RenderString("", "String One") |
| ->RenderString("", "String Two") |
| ->EndList() |
| ->StartList("repBytes") |
| ->RenderBytes("", "Some Bytes") |
| ->EndList(); |
| DoTest(primitive, Primitive::descriptor()); |
| } |
| |
| TEST_P(ProtostreamObjectSourceTest, CustomJsonName) { |
| Author author; |
| author.set_id(12345); |
| |
| ow_.StartObject("")->RenderUint64("@id", 12345)->EndObject(); |
| DoTest(author, Author::descriptor()); |
| } |
| |
| TEST_P(ProtostreamObjectSourceTest, NestedMessage) { |
| Author* author = new Author(); |
| author->set_name("Tolstoy"); |
| Book book; |
| book.set_title("My Book"); |
| book.set_allocated_author(author); |
| |
| ow_.StartObject("") |
| ->RenderString("title", "My Book") |
| ->StartObject("author") |
| ->RenderString("name", "Tolstoy") |
| ->EndObject() |
| ->EndObject(); |
| DoTest(book, Book::descriptor()); |
| } |
| |
| TEST_P(ProtostreamObjectSourceTest, RepeatingField) { |
| Author author; |
| author.set_alive(false); |
| author.set_name("john"); |
| author.add_pseudonym("phil"); |
| author.add_pseudonym("bob"); |
| |
| ow_.StartObject("") |
| ->RenderBool("alive", false) |
| ->RenderString("name", "john") |
| ->StartList("pseudonym") |
| ->RenderString("", "phil") |
| ->RenderString("", "bob") |
| ->EndList() |
| ->EndObject(); |
| DoTest(author, Author::descriptor()); |
| } |
| |
| TEST_P(ProtostreamObjectSourceTest, PackedRepeatingFields) { |
| DoTest(PreparePackedPrimitive(), PackedPrimitive::descriptor()); |
| } |
| |
| TEST_P(ProtostreamObjectSourceTest, NonPackedPackableFieldsActuallyPacked) { |
| // Protostream is packed, but parse with non-packed Primitive. |
| DoTest(PreparePackedPrimitive(), Primitive::descriptor()); |
| } |
| |
| TEST_P(ProtostreamObjectSourceTest, PackedPackableFieldNotActuallyPacked) { |
| // Protostream is not packed, but parse with PackedPrimitive. |
| DoTest(PrepareRepeatedPrimitive(), PackedPrimitive::descriptor()); |
| } |
| |
| TEST_P(ProtostreamObjectSourceTest, BadAuthor) { |
| Author author; |
| author.set_alive(false); |
| author.set_name("john"); |
| author.set_id(1234L); |
| author.add_pseudonym("phil"); |
| author.add_pseudonym("bob"); |
| |
| ow_.StartObject("") |
| ->StartList("alive") |
| ->RenderBool("", false) |
| ->EndList() |
| ->StartList("name") |
| ->RenderUint64("", static_cast<uint64>('j')) |
| ->RenderUint64("", static_cast<uint64>('o')) |
| ->RenderUint64("", static_cast<uint64>('h')) |
| ->RenderUint64("", static_cast<uint64>('n')) |
| ->EndList() |
| ->RenderString("pseudonym", "phil") |
| ->RenderString("pseudonym", "bob") |
| ->EndObject(); |
| // Protostream created with Author, but parsed with BadAuthor. |
| DoTest(author, BadAuthor::descriptor()); |
| } |
| |
| TEST_P(ProtostreamObjectSourceTest, NestedBookToBadNestedBook) { |
| Book* book = new Book(); |
| book->set_length(250); |
| book->set_published(2014L); |
| NestedBook nested; |
| nested.set_allocated_book(book); |
| |
| ow_.StartObject("") |
| ->StartList("book") |
| ->RenderUint32("", 24) // tag for field length (3 << 3) |
| ->RenderUint32("", 250) |
| ->RenderUint32("", 32) // tag for field published (4 << 3) |
| ->RenderUint32("", 2014) |
| ->EndList() |
| ->EndObject(); |
| // Protostream created with NestedBook, but parsed with BadNestedBook. |
| DoTest(nested, BadNestedBook::descriptor()); |
| } |
| |
| TEST_P(ProtostreamObjectSourceTest, BadNestedBookToNestedBook) { |
| BadNestedBook nested; |
| nested.add_book(1); |
| nested.add_book(2); |
| nested.add_book(3); |
| nested.add_book(4); |
| nested.add_book(5); |
| nested.add_book(6); |
| nested.add_book(7); |
| |
| ow_.StartObject("")->StartObject("book")->EndObject()->EndObject(); |
| // Protostream created with BadNestedBook, but parsed with NestedBook. |
| DoTest(nested, NestedBook::descriptor()); |
| } |
| |
| TEST_P(ProtostreamObjectSourceTest, |
| LongRepeatedListDoesNotBreakIntoMultipleJsonLists) { |
| Book book; |
| |
| int repeat = 10000; |
| for (int i = 0; i < repeat; ++i) { |
| Book_Label* label = book.add_labels(); |
| label->set_key(StrCat("i", i)); |
| label->set_value(StrCat("v", i)); |
| } |
| |
| // Make sure StartList and EndList are called exactly once (see b/18227499 for |
| // problems when this doesn't happen) |
| EXPECT_CALL(mock_, StartList(_)).Times(1); |
| EXPECT_CALL(mock_, EndList()).Times(1); |
| |
| DoTest(book, Book::descriptor()); |
| } |
| |
| TEST_P(ProtostreamObjectSourceTest, LowerCamelEnumOutputTest) { |
| Book book; |
| book.set_type(Book::ACTION_AND_ADVENTURE); |
| |
| UseLowerCamelForEnums(); |
| |
| ow_.StartObject("")->RenderString("type", "actionAndAdventure")->EndObject(); |
| DoTest(book, Book::descriptor()); |
| } |
| |
| TEST_P(ProtostreamObjectSourceTest, EnumCaseIsUnchangedByDefault) { |
| Book book; |
| book.set_type(Book::ACTION_AND_ADVENTURE); |
| ow_.StartObject("") |
| ->RenderString("type", "ACTION_AND_ADVENTURE") |
| ->EndObject(); |
| DoTest(book, Book::descriptor()); |
| } |
| |
| TEST_P(ProtostreamObjectSourceTest, CyclicMessageDepthTest) { |
| Cyclic cyclic; |
| cyclic.set_m_int(123); |
| |
| Book* book = cyclic.mutable_m_book(); |
| book->set_title("book title"); |
| Cyclic* current = cyclic.mutable_m_cyclic(); |
| Author* current_author = cyclic.add_m_author(); |
| for (int i = 0; i < 63; ++i) { |
| Author* next = current_author->add_friend_(); |
| next->set_id(i); |
| next->set_name(StrCat("author_name_", i)); |
| next->set_alive(true); |
| current_author = next; |
| } |
| |
| // Recursive message with depth (65) > max (max is 64). |
| for (int i = 0; i < 64; ++i) { |
| Cyclic* next = current->mutable_m_cyclic(); |
| next->set_m_str(StrCat("count_", i)); |
| current = next; |
| } |
| |
| Status status = ExecuteTest(cyclic, Cyclic::descriptor()); |
| EXPECT_EQ(util::error::INVALID_ARGUMENT, status.error_code()); |
| } |
| |
| class ProtostreamObjectSourceMapsTest : public ProtostreamObjectSourceTest { |
| protected: |
| ProtostreamObjectSourceMapsTest() { |
| helper_.ResetTypeInfo(MapOut::descriptor()); |
| } |
| }; |
| |
| INSTANTIATE_TEST_CASE_P(DifferentTypeInfoSourceTest, |
| ProtostreamObjectSourceMapsTest, |
| ::testing::Values( |
| testing::USE_TYPE_RESOLVER)); |
| |
| // Tests JSON map. |
| // |
| // This is the example expected output. |
| // { |
| // "map1": { |
| // "key1": { |
| // "foo": "foovalue" |
| // }, |
| // "key2": { |
| // "foo": "barvalue" |
| // } |
| // }, |
| // "map2": { |
| // "nestedself": { |
| // "map1": { |
| // "nested_key1": { |
| // "foo": "nested_foo" |
| // } |
| // }, |
| // "bar": "nested_bar_string" |
| // } |
| // }, |
| // "map3": { |
| // "111": "one one one" |
| // }, |
| // "bar": "top bar" |
| // } |
| TEST_P(ProtostreamObjectSourceMapsTest, MapsTest) { |
| MapOut out; |
| (*out.mutable_map1())["key1"].set_foo("foovalue"); |
| (*out.mutable_map1())["key2"].set_foo("barvalue"); |
| |
| MapOut* nested_value = &(*out.mutable_map2())["nestedself"]; |
| (*nested_value->mutable_map1())["nested_key1"].set_foo("nested_foo"); |
| nested_value->set_bar("nested_bar_string"); |
| |
| (*out.mutable_map3())[111] = "one one one"; |
| |
| out.set_bar("top bar"); |
| |
| ow_.StartObject("") |
| ->StartObject("map1") |
| ->StartObject("key1") |
| ->RenderString("foo", "foovalue") |
| ->EndObject() |
| ->StartObject("key2") |
| ->RenderString("foo", "barvalue") |
| ->EndObject() |
| ->StartObject("map2") |
| ->StartObject("nestedself") |
| ->StartObject("map1") |
| ->StartObject("nested_key1") |
| ->RenderString("foo", "nested_foo") |
| ->EndObject() |
| ->EndObject() |
| ->RenderString("bar", "nested_bar_string") |
| ->EndObject() |
| ->EndObject() |
| ->StartObject("map3") |
| ->RenderString("111", "one one one") |
| ->EndObject() |
| ->EndObject() |
| ->RenderString("bar", "top bar") |
| ->EndObject(); |
| |
| DoTest(out, MapOut::descriptor()); |
| } |
| |
| TEST_P(ProtostreamObjectSourceMapsTest, MissingKeysTest) { |
| // MapOutWireFormat has the same wire representation with MapOut but uses |
| // repeated message fields to represent map fields so we can intentionally |
| // leave out the key field or the value field of a map entry. |
| MapOutWireFormat out; |
| // Create some map entries without keys. They will be rendered with the |
| // default values ("" for strings, "0" for integers, etc.). |
| // { |
| // "map1": { |
| // "": { |
| // "foo": "foovalue" |
| // } |
| // }, |
| // "map2": { |
| // "": { |
| // "map1": { |
| // "nested_key1": { |
| // "foo": "nested_foo" |
| // } |
| // } |
| // } |
| // }, |
| // "map3": { |
| // "0": "one one one" |
| // }, |
| // "map4": { |
| // "false": "bool" |
| // } |
| // } |
| out.add_map1()->mutable_value()->set_foo("foovalue"); |
| MapOut* nested = out.add_map2()->mutable_value(); |
| (*nested->mutable_map1())["nested_key1"].set_foo("nested_foo"); |
| out.add_map3()->set_value("one one one"); |
| out.add_map4()->set_value("bool"); |
| |
| ow_.StartObject("") |
| ->StartObject("map1") |
| ->StartObject("") |
| ->RenderString("foo", "foovalue") |
| ->EndObject() |
| ->EndObject() |
| ->StartObject("map2") |
| ->StartObject("") |
| ->StartObject("map1") |
| ->StartObject("nested_key1") |
| ->RenderString("foo", "nested_foo") |
| ->EndObject() |
| ->EndObject() |
| ->EndObject() |
| ->EndObject() |
| ->StartObject("map3") |
| ->RenderString("0", "one one one") |
| ->EndObject() |
| ->StartObject("map4") |
| ->RenderString("false", "bool") |
| ->EndObject() |
| ->EndObject(); |
| |
| DoTest(out, MapOut::descriptor()); |
| } |
| |
| class ProtostreamObjectSourceAnysTest : public ProtostreamObjectSourceTest { |
| protected: |
| ProtostreamObjectSourceAnysTest() { |
| helper_.ResetTypeInfo(AnyOut::descriptor(), |
| google::protobuf::Any::descriptor()); |
| } |
| }; |
| |
| INSTANTIATE_TEST_CASE_P(DifferentTypeInfoSourceTest, |
| ProtostreamObjectSourceAnysTest, |
| ::testing::Values( |
| testing::USE_TYPE_RESOLVER)); |
| |
| // Tests JSON any support. |
| // |
| // This is the example expected output. |
| // { |
| // "any": { |
| // "@type": "type.googleapis.com/google.protobuf.testing.anys.AnyM" |
| // "foo": "foovalue" |
| // } |
| // } |
| TEST_P(ProtostreamObjectSourceAnysTest, BasicAny) { |
| AnyOut out; |
| ::google::protobuf::Any* any = out.mutable_any(); |
| |
| AnyM m; |
| m.set_foo("foovalue"); |
| any->PackFrom(m); |
| |
| ow_.StartObject("") |
| ->StartObject("any") |
| ->RenderString("@type", |
| "type.googleapis.com/google.protobuf.testing.anys.AnyM") |
| ->RenderString("foo", "foovalue") |
| ->EndObject() |
| ->EndObject(); |
| |
| DoTest(out, AnyOut::descriptor()); |
| } |
| |
| TEST_P(ProtostreamObjectSourceAnysTest, RecursiveAny) { |
| AnyOut out; |
| ::google::protobuf::Any* any = out.mutable_any(); |
| any->set_type_url("type.googleapis.com/google.protobuf.Any"); |
| |
| ::google::protobuf::Any nested_any; |
| nested_any.set_type_url( |
| "type.googleapis.com/google.protobuf.testing.anys.AnyM"); |
| |
| AnyM m; |
| m.set_foo("foovalue"); |
| nested_any.set_value(m.SerializeAsString()); |
| |
| any->set_value(nested_any.SerializeAsString()); |
| |
| ow_.StartObject("") |
| ->StartObject("any") |
| ->RenderString("@type", "type.googleapis.com/google.protobuf.Any") |
| ->StartObject("value") |
| ->RenderString("@type", |
| "type.googleapis.com/google.protobuf.testing.anys.AnyM") |
| ->RenderString("foo", "foovalue") |
| ->EndObject() |
| ->EndObject() |
| ->EndObject(); |
| |
| DoTest(out, AnyOut::descriptor()); |
| } |
| |
| TEST_P(ProtostreamObjectSourceAnysTest, DoubleRecursiveAny) { |
| AnyOut out; |
| ::google::protobuf::Any* any = out.mutable_any(); |
| any->set_type_url("type.googleapis.com/google.protobuf.Any"); |
| |
| ::google::protobuf::Any nested_any; |
| nested_any.set_type_url("type.googleapis.com/google.protobuf.Any"); |
| |
| ::google::protobuf::Any second_nested_any; |
| second_nested_any.set_type_url( |
| "type.googleapis.com/google.protobuf.testing.anys.AnyM"); |
| |
| AnyM m; |
| m.set_foo("foovalue"); |
| second_nested_any.set_value(m.SerializeAsString()); |
| nested_any.set_value(second_nested_any.SerializeAsString()); |
| any->set_value(nested_any.SerializeAsString()); |
| |
| ow_.StartObject("") |
| ->StartObject("any") |
| ->RenderString("@type", "type.googleapis.com/google.protobuf.Any") |
| ->StartObject("value") |
| ->RenderString("@type", "type.googleapis.com/google.protobuf.Any") |
| ->StartObject("value") |
| ->RenderString("@type", |
| "type.googleapis.com/google.protobuf.testing.anys.AnyM") |
| ->RenderString("foo", "foovalue") |
| ->EndObject() |
| ->EndObject() |
| ->EndObject() |
| ->EndObject(); |
| |
| DoTest(out, AnyOut::descriptor()); |
| } |
| |
| TEST_P(ProtostreamObjectSourceAnysTest, EmptyAnyOutputsEmptyObject) { |
| AnyOut out; |
| out.mutable_any(); |
| |
| ow_.StartObject("")->StartObject("any")->EndObject()->EndObject(); |
| |
| DoTest(out, AnyOut::descriptor()); |
| } |
| |
| TEST_P(ProtostreamObjectSourceAnysTest, EmptyWithTypeAndNoValueOutputsType) { |
| AnyOut out; |
| out.mutable_any()->set_type_url("foo.googleapis.com/my.Type"); |
| |
| ow_.StartObject("") |
| ->StartObject("any") |
| ->RenderString("@type", "foo.googleapis.com/my.Type") |
| ->EndObject() |
| ->EndObject(); |
| |
| DoTest(out, AnyOut::descriptor()); |
| } |
| |
| TEST_P(ProtostreamObjectSourceAnysTest, MissingTypeUrlError) { |
| AnyOut out; |
| ::google::protobuf::Any* any = out.mutable_any(); |
| |
| AnyM m; |
| m.set_foo("foovalue"); |
| any->set_value(m.SerializeAsString()); |
| |
| // We start the "AnyOut" part and then fail when we hit the Any object. |
| ow_.StartObject(""); |
| |
| Status status = ExecuteTest(out, AnyOut::descriptor()); |
| EXPECT_EQ(util::error::INTERNAL, status.error_code()); |
| } |
| |
| TEST_P(ProtostreamObjectSourceAnysTest, UnknownTypeServiceError) { |
| AnyOut out; |
| ::google::protobuf::Any* any = out.mutable_any(); |
| any->set_type_url("foo.googleapis.com/my.own.Type"); |
| |
| AnyM m; |
| m.set_foo("foovalue"); |
| any->set_value(m.SerializeAsString()); |
| |
| // We start the "AnyOut" part and then fail when we hit the Any object. |
| ow_.StartObject(""); |
| |
| Status status = ExecuteTest(out, AnyOut::descriptor()); |
| EXPECT_EQ(util::error::INTERNAL, status.error_code()); |
| } |
| |
| TEST_P(ProtostreamObjectSourceAnysTest, UnknownTypeError) { |
| AnyOut out; |
| ::google::protobuf::Any* any = out.mutable_any(); |
| any->set_type_url("type.googleapis.com/unknown.Type"); |
| |
| AnyM m; |
| m.set_foo("foovalue"); |
| any->set_value(m.SerializeAsString()); |
| |
| // We start the "AnyOut" part and then fail when we hit the Any object. |
| ow_.StartObject(""); |
| |
| Status status = ExecuteTest(out, AnyOut::descriptor()); |
| EXPECT_EQ(util::error::INTERNAL, status.error_code()); |
| } |
| |
| class ProtostreamObjectSourceStructTest : public ProtostreamObjectSourceTest { |
| protected: |
| ProtostreamObjectSourceStructTest() { |
| helper_.ResetTypeInfo(StructType::descriptor(), |
| google::protobuf::Struct::descriptor()); |
| } |
| }; |
| |
| INSTANTIATE_TEST_CASE_P(DifferentTypeInfoSourceTest, |
| ProtostreamObjectSourceStructTest, |
| ::testing::Values( |
| testing::USE_TYPE_RESOLVER)); |
| |
| // Tests struct |
| // |
| // "object": { |
| // "k1": 123, |
| // "k2": true |
| // } |
| TEST_P(ProtostreamObjectSourceStructTest, StructRenderSuccess) { |
| StructType out; |
| google::protobuf::Struct* s = out.mutable_object(); |
| s->mutable_fields()->operator[]("k1").set_number_value(123); |
| s->mutable_fields()->operator[]("k2").set_bool_value(true); |
| |
| ow_.StartObject("") |
| ->StartObject("object") |
| ->RenderDouble("k1", 123) |
| ->RenderBool("k2", true) |
| ->EndObject() |
| ->EndObject(); |
| |
| DoTest(out, StructType::descriptor()); |
| } |
| |
| TEST_P(ProtostreamObjectSourceStructTest, MissingValueSkipsField) { |
| StructType out; |
| google::protobuf::Struct* s = out.mutable_object(); |
| s->mutable_fields()->operator[]("k1"); |
| |
| ow_.StartObject("")->StartObject("object")->EndObject()->EndObject(); |
| |
| DoTest(out, StructType::descriptor()); |
| } |
| |
| class ProtostreamObjectSourceFieldMaskTest |
| : public ProtostreamObjectSourceTest { |
| protected: |
| ProtostreamObjectSourceFieldMaskTest() { |
| helper_.ResetTypeInfo(FieldMaskTest::descriptor(), |
| google::protobuf::FieldMask::descriptor()); |
| } |
| }; |
| |
| INSTANTIATE_TEST_CASE_P(DifferentTypeInfoSourceTest, |
| ProtostreamObjectSourceFieldMaskTest, |
| ::testing::Values( |
| testing::USE_TYPE_RESOLVER)); |
| |
| TEST_P(ProtostreamObjectSourceFieldMaskTest, FieldMaskRenderSuccess) { |
| FieldMaskTest out; |
| out.set_id("1"); |
| out.mutable_single_mask()->add_paths("path1"); |
| out.mutable_single_mask()->add_paths("snake_case_path2"); |
| ::google::protobuf::FieldMask* mask = out.add_repeated_mask(); |
| mask->add_paths("path3"); |
| mask = out.add_repeated_mask(); |
| mask->add_paths("snake_case_path4"); |
| mask->add_paths("path5"); |
| NestedFieldMask* nested = out.add_nested_mask(); |
| nested->set_data("data"); |
| nested->mutable_single_mask()->add_paths("nested.path1"); |
| nested->mutable_single_mask()->add_paths("nested_field.snake_case_path2"); |
| mask = nested->add_repeated_mask(); |
| mask->add_paths("nested_field.path3"); |
| mask->add_paths("nested.snake_case_path4"); |
| mask = nested->add_repeated_mask(); |
| mask->add_paths("nested.path5"); |
| mask = nested->add_repeated_mask(); |
| mask->add_paths( |
| "snake_case.map_field[\"map_key_should_be_ignored\"].nested_snake_case." |
| "map_field[\"map_key_sho\\\"uld_be_ignored\"]"); |
| |
| ow_.StartObject("") |
| ->RenderString("id", "1") |
| ->RenderString("singleMask", "path1,snakeCasePath2") |
| ->StartList("repeatedMask") |
| ->RenderString("", "path3") |
| ->RenderString("", "snakeCasePath4,path5") |
| ->EndList() |
| ->StartList("nestedMask") |
| ->StartObject("") |
| ->RenderString("data", "data") |
| ->RenderString("singleMask", "nested.path1,nestedField.snakeCasePath2") |
| ->StartList("repeatedMask") |
| ->RenderString("", "nestedField.path3,nested.snakeCasePath4") |
| ->RenderString("", "nested.path5") |
| ->RenderString("", |
| "snakeCase.mapField[\"map_key_should_be_ignored\"]." |
| "nestedSnakeCase.mapField[\"map_key_sho\\\"uld_be_" |
| "ignored\"]") |
| ->EndList() |
| ->EndObject() |
| ->EndList() |
| ->EndObject(); |
| |
| DoTest(out, FieldMaskTest::descriptor()); |
| } |
| |
| class ProtostreamObjectSourceTimestampTest |
| : public ProtostreamObjectSourceTest { |
| protected: |
| ProtostreamObjectSourceTimestampTest() { |
| helper_.ResetTypeInfo(TimestampDuration::descriptor()); |
| } |
| }; |
| |
| INSTANTIATE_TEST_CASE_P(DifferentTypeInfoSourceTest, |
| ProtostreamObjectSourceTimestampTest, |
| ::testing::Values( |
| testing::USE_TYPE_RESOLVER)); |
| |
| TEST_P(ProtostreamObjectSourceTimestampTest, InvalidTimestampBelowMinTest) { |
| TimestampDuration out; |
| google::protobuf::Timestamp* ts = out.mutable_ts(); |
| // Min allowed seconds - 1 |
| ts->set_seconds(kTimestampMinSeconds - 1); |
| ow_.StartObject(""); |
| |
| Status status = ExecuteTest(out, TimestampDuration::descriptor()); |
| EXPECT_EQ(util::error::INTERNAL, status.error_code()); |
| } |
| |
| TEST_P(ProtostreamObjectSourceTimestampTest, InvalidTimestampAboveMaxTest) { |
| TimestampDuration out; |
| google::protobuf::Timestamp* ts = out.mutable_ts(); |
| // Max allowed seconds + 1 |
| ts->set_seconds(kTimestampMaxSeconds + 1); |
| ow_.StartObject(""); |
| |
| Status status = ExecuteTest(out, TimestampDuration::descriptor()); |
| EXPECT_EQ(util::error::INTERNAL, status.error_code()); |
| } |
| |
| TEST_P(ProtostreamObjectSourceTimestampTest, InvalidDurationBelowMinTest) { |
| TimestampDuration out; |
| google::protobuf::Duration* dur = out.mutable_dur(); |
| // Min allowed seconds - 1 |
| dur->set_seconds(kDurationMinSeconds - 1); |
| ow_.StartObject(""); |
| |
| Status status = ExecuteTest(out, TimestampDuration::descriptor()); |
| EXPECT_EQ(util::error::INTERNAL, status.error_code()); |
| } |
| |
| TEST_P(ProtostreamObjectSourceTimestampTest, InvalidDurationAboveMaxTest) { |
| TimestampDuration out; |
| google::protobuf::Duration* dur = out.mutable_dur(); |
| // Min allowed seconds + 1 |
| dur->set_seconds(kDurationMaxSeconds + 1); |
| ow_.StartObject(""); |
| |
| Status status = ExecuteTest(out, TimestampDuration::descriptor()); |
| EXPECT_EQ(util::error::INTERNAL, status.error_code()); |
| } |
| |
| } // namespace converter |
| } // namespace util |
| } // namespace protobuf |
| } // namespace google |