| // 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_objectwriter.h> |
| |
| #include <stddef.h> // For size_t |
| |
| #include <google/protobuf/field_mask.pb.h> |
| #include <google/protobuf/timestamp.pb.h> |
| #include <google/protobuf/wrappers.pb.h> |
| #include <google/protobuf/io/zero_copy_stream_impl_lite.h> |
| #include <google/protobuf/descriptor.pb.h> |
| #include <google/protobuf/descriptor.h> |
| #include <google/protobuf/dynamic_message.h> |
| #include <google/protobuf/message.h> |
| #include <google/protobuf/util/internal/mock_error_listener.h> |
| #include <google/protobuf/util/internal/testdata/anys.pb.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/testdata/maps.pb.h> |
| #include <google/protobuf/util/internal/testdata/oneofs.pb.h> |
| #include <google/protobuf/util/internal/testdata/proto3.pb.h> |
| #include <google/protobuf/util/internal/testdata/struct.pb.h> |
| #include <google/protobuf/util/internal/testdata/timestamp_duration.pb.h> |
| #include <google/protobuf/util/internal/testdata/wrappers.pb.h> |
| #include <google/protobuf/util/internal/type_info_test_helper.h> |
| #include <google/protobuf/util/internal/constants.h> |
| #include <google/protobuf/util/message_differencer.h> |
| #include <google/protobuf/stubs/bytestream.h> |
| #include <google/protobuf/stubs/strutil.h> |
| #include <gtest/gtest.h> |
| |
| |
| namespace google { |
| namespace protobuf { |
| namespace util { |
| namespace converter { |
| |
| using google::protobuf::testing::AnyM; |
| using google::protobuf::testing::AnyOut; |
| using google::protobuf::testing::Author; |
| using google::protobuf::testing::Book; |
| using google::protobuf::testing::FieldMaskTest; |
| using google::protobuf::testing::Int32Wrapper; |
| using google::protobuf::testing::MapIn; |
| using google::protobuf::testing::Primitive; |
| using google::protobuf::testing::Proto3Message; |
| using google::protobuf::testing::Publisher; |
| using google::protobuf::testing::StructType; |
| using google::protobuf::testing::TestJsonName1; |
| using google::protobuf::testing::TestJsonName2; |
| using google::protobuf::testing::TimestampDuration; |
| using google::protobuf::testing::ValueWrapper; |
| using google::protobuf::testing::oneofs::OneOfsRequest; |
| using google::protobuf::Descriptor; |
| using google::protobuf::DescriptorPool; |
| using google::protobuf::DynamicMessageFactory; |
| using google::protobuf::FileDescriptorProto; |
| using google::protobuf::Message; |
| using strings::GrowingArrayByteSink; |
| using ::testing::_; |
| using ::testing::Args; |
| |
| |
| namespace { |
| string GetTypeUrl(const Descriptor* descriptor) { |
| return string(kTypeServiceBaseUrl) + "/" + descriptor->full_name(); |
| } |
| } // namespace |
| |
| #if __cplusplus >= 201103L |
| using std::get; |
| #else |
| using std::tr1::get; |
| #endif |
| |
| class BaseProtoStreamObjectWriterTest |
| : public ::testing::TestWithParam<testing::TypeInfoSource> { |
| protected: |
| BaseProtoStreamObjectWriterTest() |
| : helper_(GetParam()), |
| listener_(), |
| output_(new GrowingArrayByteSink(1000)), |
| ow_() {} |
| |
| explicit BaseProtoStreamObjectWriterTest(const Descriptor* descriptor) |
| : helper_(GetParam()), |
| listener_(), |
| output_(new GrowingArrayByteSink(1000)), |
| ow_() { |
| std::vector<const Descriptor*> descriptors; |
| descriptors.push_back(descriptor); |
| ResetTypeInfo(descriptors); |
| } |
| |
| explicit BaseProtoStreamObjectWriterTest( |
| std::vector<const Descriptor*> descriptors) |
| : helper_(GetParam()), |
| listener_(), |
| output_(new GrowingArrayByteSink(1000)), |
| ow_() { |
| ResetTypeInfo(descriptors); |
| } |
| |
| void ResetTypeInfo(std::vector<const Descriptor*> descriptors) { |
| GOOGLE_CHECK(!descriptors.empty()) << "Must have at least one descriptor!"; |
| helper_.ResetTypeInfo(descriptors); |
| ow_.reset(helper_.NewProtoWriter(GetTypeUrl(descriptors[0]), output_.get(), |
| &listener_, options_)); |
| } |
| |
| void ResetTypeInfo(const Descriptor* descriptor) { |
| std::vector<const Descriptor*> descriptors; |
| descriptors.push_back(descriptor); |
| ResetTypeInfo(descriptors); |
| } |
| |
| virtual ~BaseProtoStreamObjectWriterTest() {} |
| |
| void CheckOutput(const Message& expected, int expected_length) { |
| size_t nbytes; |
| google::protobuf::scoped_array<char> buffer(output_->GetBuffer(&nbytes)); |
| if (expected_length >= 0) { |
| EXPECT_EQ(expected_length, nbytes); |
| } |
| string str(buffer.get(), nbytes); |
| |
| std::stringbuf str_buf(str, std::ios_base::in); |
| std::istream istream(&str_buf); |
| google::protobuf::scoped_ptr<Message> message(expected.New()); |
| message->ParsePartialFromIstream(&istream); |
| |
| if (!MessageDifferencer::Equivalent(expected, *message)) { |
| EXPECT_EQ(expected.DebugString(), message->DebugString()); |
| } |
| } |
| |
| void CheckOutput(const Message& expected) { CheckOutput(expected, -1); } |
| |
| const google::protobuf::Type* GetType(const Descriptor* descriptor) { |
| return helper_.GetTypeInfo()->GetTypeByTypeUrl(GetTypeUrl(descriptor)); |
| } |
| |
| testing::TypeInfoTestHelper helper_; |
| MockErrorListener listener_; |
| google::protobuf::scoped_ptr<GrowingArrayByteSink> output_; |
| google::protobuf::scoped_ptr<ProtoStreamObjectWriter> ow_; |
| ProtoStreamObjectWriter::Options options_; |
| }; |
| |
| MATCHER_P(HasObjectLocation, expected, |
| "Verifies the expected object location") { |
| string actual = get<0>(arg).ToString(); |
| if (actual.compare(expected) == 0) return true; |
| *result_listener << "actual location is: " << actual; |
| return false; |
| } |
| |
| class ProtoStreamObjectWriterTest : public BaseProtoStreamObjectWriterTest { |
| protected: |
| ProtoStreamObjectWriterTest() |
| : BaseProtoStreamObjectWriterTest(Book::descriptor()) {} |
| |
| void ResetProtoWriter() { |
| ResetTypeInfo(Book::descriptor()); |
| } |
| |
| virtual ~ProtoStreamObjectWriterTest() {} |
| }; |
| |
| INSTANTIATE_TEST_CASE_P(DifferentTypeInfoSourceTest, |
| ProtoStreamObjectWriterTest, |
| ::testing::Values( |
| testing::USE_TYPE_RESOLVER)); |
| |
| TEST_P(ProtoStreamObjectWriterTest, EmptyObject) { |
| Book empty; |
| ow_->StartObject("")->EndObject(); |
| CheckOutput(empty, 0); |
| } |
| |
| TEST_P(ProtoStreamObjectWriterTest, SimpleObject) { |
| string content("My content"); |
| |
| Book book; |
| book.set_title("My Title"); |
| book.set_length(222); |
| book.set_content(content); |
| |
| ow_->StartObject("") |
| ->RenderString("title", "My Title") |
| ->RenderInt32("length", 222) |
| ->RenderBytes("content", content) |
| ->EndObject(); |
| CheckOutput(book); |
| } |
| |
| TEST_P(ProtoStreamObjectWriterTest, SimpleMessage) { |
| Book book; |
| book.set_title("Some Book"); |
| book.set_length(102); |
| Publisher* publisher = book.mutable_publisher(); |
| publisher->set_name("My Publisher"); |
| Author* robert = book.mutable_author(); |
| robert->set_alive(true); |
| robert->set_name("robert"); |
| robert->add_pseudonym("bob"); |
| robert->add_pseudonym("bobby"); |
| robert->add_friend_()->set_name("john"); |
| |
| ow_->StartObject("") |
| ->RenderString("title", "Some Book") |
| ->RenderInt32("length", 102) |
| ->StartObject("publisher") |
| ->RenderString("name", "My Publisher") |
| ->EndObject() |
| ->StartObject("author") |
| ->RenderBool("alive", true) |
| ->RenderString("name", "robert") |
| ->StartList("pseudonym") |
| ->RenderString("", "bob") |
| ->RenderString("", "bobby") |
| ->EndList() |
| ->StartList("friend") |
| ->StartObject("") |
| ->RenderString("name", "john") |
| ->EndObject() |
| ->EndList() |
| ->EndObject() |
| ->EndObject(); |
| CheckOutput(book); |
| } |
| |
| TEST_P(ProtoStreamObjectWriterTest, CustomJsonName) { |
| Book book; |
| Author* robert = book.mutable_author(); |
| robert->set_id(12345); |
| robert->set_name("robert"); |
| |
| ow_->StartObject("") |
| ->StartObject("author") |
| ->RenderUint64("@id", 12345) |
| ->RenderString("name", "robert") |
| ->EndObject() |
| ->EndObject(); |
| CheckOutput(book); |
| } |
| |
| // Test that two messages can have different fields mapped to the same JSON |
| // name. See: https://github.com/google/protobuf/issues/1415 |
| TEST_P(ProtoStreamObjectWriterTest, ConflictingJsonName) { |
| ResetTypeInfo(TestJsonName1::descriptor()); |
| TestJsonName1 message1; |
| message1.set_one_value(12345); |
| ow_->StartObject("")->RenderInt32("value", 12345)->EndObject(); |
| CheckOutput(message1); |
| |
| ResetTypeInfo(TestJsonName2::descriptor()); |
| TestJsonName2 message2; |
| message2.set_another_value(12345); |
| ow_->StartObject("")->RenderInt32("value", 12345)->EndObject(); |
| CheckOutput(message2); |
| } |
| |
| TEST_P(ProtoStreamObjectWriterTest, IntEnumValuesAreAccepted) { |
| Book book; |
| book.set_title("Some Book"); |
| book.set_type(google::protobuf::testing::Book_Type_KIDS); |
| Author* robert = book.mutable_author(); |
| robert->set_name("robert"); |
| |
| ow_->StartObject("") |
| ->RenderString("title", "Some Book") |
| ->RenderString("type", "2") |
| ->StartObject("author") |
| ->RenderString("name", "robert") |
| ->EndObject() |
| ->EndObject(); |
| CheckOutput(book); |
| } |
| |
| TEST_P(ProtoStreamObjectWriterTest, EnumValuesWithoutUnderscoreAreAccepted) { |
| Book book; |
| book.set_title("Some Book"); |
| book.set_type(google::protobuf::testing::Book_Type_ACTION_AND_ADVENTURE); |
| Author* robert = book.mutable_author(); |
| robert->set_name("robert"); |
| |
| options_.use_lower_camel_for_enums = true; |
| ResetProtoWriter(); |
| |
| ow_->StartObject("") |
| ->RenderString("title", "Some Book") |
| ->RenderString("type", "ACTIONANDADVENTURE") |
| ->StartObject("author") |
| ->RenderString("name", "robert") |
| ->EndObject() |
| ->EndObject(); |
| CheckOutput(book); |
| } |
| |
| TEST_P(ProtoStreamObjectWriterTest, EnumValuesInCamelCaseAreAccepted) { |
| Book book; |
| book.set_title("Some Book"); |
| book.set_type(google::protobuf::testing::Book_Type_ACTION_AND_ADVENTURE); |
| Author* robert = book.mutable_author(); |
| robert->set_name("robert"); |
| |
| options_.use_lower_camel_for_enums = true; |
| ResetProtoWriter(); |
| |
| ow_->StartObject("") |
| ->RenderString("title", "Some Book") |
| ->RenderString("type", "actionAndAdventure") |
| ->StartObject("author") |
| ->RenderString("name", "robert") |
| ->EndObject() |
| ->EndObject(); |
| CheckOutput(book); |
| } |
| |
| TEST_P(ProtoStreamObjectWriterTest, |
| EnumValuesInCamelCaseWithNameNotUppercaseAreAccepted) { |
| Book book; |
| book.set_title("Some Book"); |
| book.set_type(google::protobuf::testing::Book_Type_arts_and_photography); |
| Author* robert = book.mutable_author(); |
| robert->set_name("robert"); |
| |
| options_.use_lower_camel_for_enums = true; |
| ResetProtoWriter(); |
| |
| ow_->StartObject("") |
| ->RenderString("title", "Some Book") |
| ->RenderString("type", "artsAndPhotography") |
| ->StartObject("author") |
| ->RenderString("name", "robert") |
| ->EndObject() |
| ->EndObject(); |
| CheckOutput(book); |
| } |
| |
| TEST_P(ProtoStreamObjectWriterTest, PrimitiveFromStringConversion) { |
| Primitive full; |
| full.set_fix32(101); |
| full.set_u32(102); |
| full.set_i32(-103); |
| full.set_sf32(-104); |
| full.set_s32(-105); |
| full.set_fix64(40000000001L); |
| full.set_u64(40000000002L); |
| full.set_i64(-40000000003L); |
| full.set_sf64(-40000000004L); |
| full.set_s64(-40000000005L); |
| full.set_str("string1"); |
| full.set_bytes("Some Bytes"); |
| full.set_float_(3.14f); |
| full.set_double_(-4.05L); |
| full.set_bool_(true); |
| full.add_rep_fix32(201); |
| full.add_rep_u32(202); |
| full.add_rep_i32(-203); |
| full.add_rep_sf32(-204); |
| full.add_rep_s32(-205); |
| full.add_rep_fix64(80000000001L); |
| full.add_rep_u64(80000000002L); |
| full.add_rep_i64(-80000000003L); |
| full.add_rep_sf64(-80000000004L); |
| full.add_rep_s64(-80000000005L); |
| full.add_rep_str("string2"); |
| full.add_rep_bytes("More Bytes"); |
| full.add_rep_float(6.14f); |
| full.add_rep_double(-8.05L); |
| full.add_rep_bool(false); |
| |
| ResetTypeInfo(Primitive::descriptor()); |
| |
| ow_->StartObject("") |
| ->RenderString("fix32", "101") |
| ->RenderString("u32", "102") |
| ->RenderString("i32", "-103") |
| ->RenderString("sf32", "-104") |
| ->RenderString("s32", "-105") |
| ->RenderString("fix64", "40000000001") |
| ->RenderString("u64", "40000000002") |
| ->RenderString("i64", "-40000000003") |
| ->RenderString("sf64", "-40000000004") |
| ->RenderString("s64", "-40000000005") |
| ->RenderString("str", "string1") |
| ->RenderString("bytes", "U29tZSBCeXRlcw==") // "Some Bytes" |
| ->RenderString("float", "3.14") |
| ->RenderString("double", "-4.05") |
| ->RenderString("bool", "true") |
| ->StartList("rep_fix32") |
| ->RenderString("", "201") |
| ->EndList() |
| ->StartList("rep_u32") |
| ->RenderString("", "202") |
| ->EndList() |
| ->StartList("rep_i32") |
| ->RenderString("", "-203") |
| ->EndList() |
| ->StartList("rep_sf32") |
| ->RenderString("", "-204") |
| ->EndList() |
| ->StartList("rep_s32") |
| ->RenderString("", "-205") |
| ->EndList() |
| ->StartList("rep_fix64") |
| ->RenderString("", "80000000001") |
| ->EndList() |
| ->StartList("rep_u64") |
| ->RenderString("", "80000000002") |
| ->EndList() |
| ->StartList("rep_i64") |
| ->RenderString("", "-80000000003") |
| ->EndList() |
| ->StartList("rep_sf64") |
| ->RenderString("", "-80000000004") |
| ->EndList() |
| ->StartList("rep_s64") |
| ->RenderString("", "-80000000005") |
| ->EndList() |
| ->StartList("rep_str") |
| ->RenderString("", "string2") |
| ->EndList() |
| ->StartList("rep_bytes") |
| ->RenderString("", "TW9yZSBCeXRlcw==") // "More Bytes" |
| ->EndList() |
| ->StartList("rep_float") |
| ->RenderString("", "6.14") |
| ->EndList() |
| ->StartList("rep_double") |
| ->RenderString("", "-8.05") |
| ->EndList() |
| ->StartList("rep_bool") |
| ->RenderString("", "false") |
| ->EndList() |
| ->EndObject(); |
| CheckOutput(full); |
| } |
| |
| TEST_P(ProtoStreamObjectWriterTest, InfinityInputTest) { |
| Primitive full; |
| full.set_double_(std::numeric_limits<double>::infinity()); |
| full.set_float_(std::numeric_limits<float>::infinity()); |
| full.set_str("-Infinity"); |
| |
| ResetTypeInfo(Primitive::descriptor()); |
| |
| EXPECT_CALL(listener_, InvalidValue(_, StringPiece("TYPE_INT32"), |
| StringPiece("\"Infinity\""))) |
| .With(Args<0>(HasObjectLocation("i32"))); |
| EXPECT_CALL(listener_, InvalidValue(_, StringPiece("TYPE_UINT32"), |
| StringPiece("\"Infinity\""))) |
| .With(Args<0>(HasObjectLocation("u32"))); |
| EXPECT_CALL(listener_, InvalidValue(_, StringPiece("TYPE_SFIXED64"), |
| StringPiece("\"-Infinity\""))) |
| .With(Args<0>(HasObjectLocation("sf64"))); |
| EXPECT_CALL(listener_, InvalidValue(_, StringPiece("TYPE_BOOL"), |
| StringPiece("\"Infinity\""))) |
| .With(Args<0>(HasObjectLocation("bool"))); |
| |
| ow_->StartObject("") |
| ->RenderString("double", "Infinity") |
| ->RenderString("float", "Infinity") |
| ->RenderString("i32", "Infinity") |
| ->RenderString("u32", "Infinity") |
| ->RenderString("sf64", "-Infinity") |
| ->RenderString("str", "-Infinity") |
| ->RenderString("bool", "Infinity") |
| ->EndObject(); |
| CheckOutput(full); |
| } |
| |
| TEST_P(ProtoStreamObjectWriterTest, NaNInputTest) { |
| Primitive full; |
| full.set_double_(std::numeric_limits<double>::quiet_NaN()); |
| full.set_float_(std::numeric_limits<float>::quiet_NaN()); |
| full.set_str("NaN"); |
| |
| ResetTypeInfo(Primitive::descriptor()); |
| |
| EXPECT_CALL(listener_, InvalidValue(_, StringPiece("TYPE_INT32"), |
| StringPiece("\"NaN\""))) |
| .With(Args<0>(HasObjectLocation("i32"))); |
| EXPECT_CALL(listener_, InvalidValue(_, StringPiece("TYPE_UINT32"), |
| StringPiece("\"NaN\""))) |
| .With(Args<0>(HasObjectLocation("u32"))); |
| EXPECT_CALL(listener_, InvalidValue(_, StringPiece("TYPE_SFIXED64"), |
| StringPiece("\"NaN\""))) |
| .With(Args<0>(HasObjectLocation("sf64"))); |
| EXPECT_CALL(listener_, |
| InvalidValue(_, StringPiece("TYPE_BOOL"), StringPiece("\"NaN\""))) |
| .With(Args<0>(HasObjectLocation("bool"))); |
| |
| ow_->StartObject("") |
| ->RenderString("double", "NaN") |
| ->RenderString("float", "NaN") |
| ->RenderString("i32", "NaN") |
| ->RenderString("u32", "NaN") |
| ->RenderString("sf64", "NaN") |
| ->RenderString("str", "NaN") |
| ->RenderString("bool", "NaN") |
| ->EndObject(); |
| |
| CheckOutput(full); |
| } |
| |
| TEST_P(ProtoStreamObjectWriterTest, ImplicitPrimitiveList) { |
| Book expected; |
| Author* author = expected.mutable_author(); |
| author->set_name("The Author"); |
| author->add_pseudonym("first"); |
| author->add_pseudonym("second"); |
| |
| ow_->StartObject("") |
| ->StartObject("author") |
| ->RenderString("name", "The Author") |
| ->RenderString("pseudonym", "first") |
| ->RenderString("pseudonym", "second") |
| ->EndObject() |
| ->EndObject(); |
| CheckOutput(expected); |
| } |
| |
| TEST_P(ProtoStreamObjectWriterTest, |
| LastWriteWinsOnNonRepeatedPrimitiveFieldWithDuplicates) { |
| Book expected; |
| Author* author = expected.mutable_author(); |
| author->set_name("second"); |
| |
| ow_->StartObject("") |
| ->StartObject("author") |
| ->RenderString("name", "first") |
| ->RenderString("name", "second") |
| ->EndObject() |
| ->EndObject(); |
| CheckOutput(expected); |
| } |
| |
| TEST_P(ProtoStreamObjectWriterTest, ExplicitPrimitiveList) { |
| Book expected; |
| Author* author = expected.mutable_author(); |
| author->set_name("The Author"); |
| author->add_pseudonym("first"); |
| author->add_pseudonym("second"); |
| |
| ow_->StartObject("") |
| ->StartObject("author") |
| ->RenderString("name", "The Author") |
| ->StartList("pseudonym") |
| ->RenderString("", "first") |
| ->RenderString("", "second") |
| ->EndList() |
| ->EndObject() |
| ->EndObject(); |
| CheckOutput(expected); |
| } |
| |
| TEST_P(ProtoStreamObjectWriterTest, NonRepeatedExplicitPrimitiveList) { |
| Book expected; |
| expected.set_allocated_author(new Author()); |
| |
| EXPECT_CALL( |
| listener_, |
| InvalidName( |
| _, StringPiece("name"), |
| StringPiece("Proto field is not repeating, cannot start list."))) |
| .With(Args<0>(HasObjectLocation("author"))); |
| ow_->StartObject("") |
| ->StartObject("author") |
| ->StartList("name") |
| ->RenderString("", "first") |
| ->RenderString("", "second") |
| ->EndList() |
| ->EndObject() |
| ->EndObject(); |
| CheckOutput(expected); |
| } |
| |
| TEST_P(ProtoStreamObjectWriterTest, ImplicitMessageList) { |
| Book expected; |
| Author* outer = expected.mutable_author(); |
| outer->set_name("outer"); |
| outer->set_alive(true); |
| Author* first = outer->add_friend_(); |
| first->set_name("first"); |
| Author* second = outer->add_friend_(); |
| second->set_name("second"); |
| |
| ow_->StartObject("") |
| ->StartObject("author") |
| ->RenderString("name", "outer") |
| ->RenderBool("alive", true) |
| ->StartObject("friend") |
| ->RenderString("name", "first") |
| ->EndObject() |
| ->StartObject("friend") |
| ->RenderString("name", "second") |
| ->EndObject() |
| ->EndObject() |
| ->EndObject(); |
| CheckOutput(expected); |
| } |
| |
| TEST_P(ProtoStreamObjectWriterTest, |
| LastWriteWinsOnNonRepeatedMessageFieldWithDuplicates) { |
| Book expected; |
| Author* author = expected.mutable_author(); |
| author->set_name("The Author"); |
| Publisher* publisher = expected.mutable_publisher(); |
| publisher->set_name("second"); |
| |
| ow_->StartObject("") |
| ->StartObject("author") |
| ->RenderString("name", "The Author") |
| ->EndObject() |
| ->StartObject("publisher") |
| ->RenderString("name", "first") |
| ->EndObject() |
| ->StartObject("publisher") |
| ->RenderString("name", "second") |
| ->EndObject() |
| ->EndObject(); |
| CheckOutput(expected); |
| } |
| |
| TEST_P(ProtoStreamObjectWriterTest, ExplicitMessageList) { |
| Book expected; |
| Author* outer = expected.mutable_author(); |
| outer->set_name("outer"); |
| outer->set_alive(true); |
| Author* first = outer->add_friend_(); |
| first->set_name("first"); |
| Author* second = outer->add_friend_(); |
| second->set_name("second"); |
| |
| ow_->StartObject("") |
| ->StartObject("author") |
| ->RenderString("name", "outer") |
| ->RenderBool("alive", true) |
| ->StartList("friend") |
| ->StartObject("") |
| ->RenderString("name", "first") |
| ->EndObject() |
| ->StartObject("") |
| ->RenderString("name", "second") |
| ->EndObject() |
| ->EndList() |
| ->EndObject() |
| ->EndObject(); |
| CheckOutput(expected); |
| } |
| |
| TEST_P(ProtoStreamObjectWriterTest, NonRepeatedExplicitMessageList) { |
| Book expected; |
| Author* author = expected.mutable_author(); |
| author->set_name("The Author"); |
| |
| EXPECT_CALL( |
| listener_, |
| InvalidName( |
| _, StringPiece("publisher"), |
| StringPiece("Proto field is not repeating, cannot start list."))) |
| .With(Args<0>(HasObjectLocation(""))); |
| ow_->StartObject("") |
| ->StartObject("author") |
| ->RenderString("name", "The Author") |
| ->EndObject() |
| ->StartList("publisher") |
| ->StartObject("") |
| ->RenderString("name", "first") |
| ->EndObject() |
| ->StartObject("") |
| ->RenderString("name", "second") |
| ->EndObject() |
| ->EndList() |
| ->EndObject(); |
| CheckOutput(expected); |
| } |
| |
| TEST_P(ProtoStreamObjectWriterTest, UnknownFieldAtRoot) { |
| Book empty; |
| |
| EXPECT_CALL(listener_, InvalidName(_, StringPiece("unknown"), |
| StringPiece("Cannot find field."))) |
| .With(Args<0>(HasObjectLocation(""))); |
| ow_->StartObject("")->RenderString("unknown", "Nope!")->EndObject(); |
| CheckOutput(empty, 0); |
| } |
| |
| TEST_P(ProtoStreamObjectWriterTest, UnknownFieldAtAuthorFriend) { |
| Book expected; |
| Author* paul = expected.mutable_author(); |
| paul->set_name("Paul"); |
| Author* mark = paul->add_friend_(); |
| mark->set_name("Mark"); |
| Author* john = paul->add_friend_(); |
| john->set_name("John"); |
| Author* luke = paul->add_friend_(); |
| luke->set_name("Luke"); |
| |
| EXPECT_CALL(listener_, InvalidName(_, StringPiece("address"), |
| StringPiece("Cannot find field."))) |
| .With(Args<0>(HasObjectLocation("author.friend[1]"))); |
| ow_->StartObject("") |
| ->StartObject("author") |
| ->RenderString("name", "Paul") |
| ->StartList("friend") |
| ->StartObject("") |
| ->RenderString("name", "Mark") |
| ->EndObject() |
| ->StartObject("") |
| ->RenderString("name", "John") |
| ->RenderString("address", "Patmos") |
| ->EndObject() |
| ->StartObject("") |
| ->RenderString("name", "Luke") |
| ->EndObject() |
| ->EndList() |
| ->EndObject() |
| ->EndObject(); |
| CheckOutput(expected); |
| } |
| |
| TEST_P(ProtoStreamObjectWriterTest, UnknownObjectAtRoot) { |
| Book empty; |
| |
| EXPECT_CALL(listener_, InvalidName(_, StringPiece("unknown"), |
| StringPiece("Cannot find field."))) |
| .With(Args<0>(HasObjectLocation(""))); |
| ow_->StartObject("")->StartObject("unknown")->EndObject()->EndObject(); |
| CheckOutput(empty, 0); |
| } |
| |
| TEST_P(ProtoStreamObjectWriterTest, UnknownObjectAtAuthor) { |
| Book expected; |
| Author* author = expected.mutable_author(); |
| author->set_name("William"); |
| author->add_pseudonym("Bill"); |
| |
| EXPECT_CALL(listener_, InvalidName(_, StringPiece("wife"), |
| StringPiece("Cannot find field."))) |
| .With(Args<0>(HasObjectLocation("author"))); |
| ow_->StartObject("") |
| ->StartObject("author") |
| ->RenderString("name", "William") |
| ->StartObject("wife") |
| ->RenderString("name", "Hilary") |
| ->EndObject() |
| ->RenderString("pseudonym", "Bill") |
| ->EndObject() |
| ->EndObject(); |
| CheckOutput(expected); |
| } |
| |
| TEST_P(ProtoStreamObjectWriterTest, UnknownListAtRoot) { |
| Book empty; |
| |
| EXPECT_CALL(listener_, InvalidName(_, StringPiece("unknown"), |
| StringPiece("Cannot find field."))) |
| .With(Args<0>(HasObjectLocation(""))); |
| ow_->StartObject("")->StartList("unknown")->EndList()->EndObject(); |
| CheckOutput(empty, 0); |
| } |
| |
| TEST_P(ProtoStreamObjectWriterTest, UnknownListAtPublisher) { |
| Book expected; |
| expected.set_title("Brainwashing"); |
| Publisher* publisher = expected.mutable_publisher(); |
| publisher->set_name("propaganda"); |
| |
| EXPECT_CALL(listener_, InvalidName(_, StringPiece("alliance"), |
| StringPiece("Cannot find field."))) |
| .With(Args<0>(HasObjectLocation("publisher"))); |
| ow_->StartObject("") |
| ->StartObject("publisher") |
| ->RenderString("name", "propaganda") |
| ->StartList("alliance") |
| ->EndList() |
| ->EndObject() |
| ->RenderString("title", "Brainwashing") |
| ->EndObject(); |
| CheckOutput(expected); |
| } |
| |
| TEST_P(ProtoStreamObjectWriterTest, IgnoreUnknownFieldAtRoot) { |
| Book empty; |
| |
| options_.ignore_unknown_fields = true; |
| ResetProtoWriter(); |
| |
| EXPECT_CALL(listener_, InvalidName(_, _, _)).Times(0); |
| ow_->StartObject("")->RenderString("unknown", "Nope!")->EndObject(); |
| CheckOutput(empty, 0); |
| } |
| |
| TEST_P(ProtoStreamObjectWriterTest, IgnoreUnknownFieldAtAuthorFriend) { |
| Book expected; |
| Author* paul = expected.mutable_author(); |
| paul->set_name("Paul"); |
| Author* mark = paul->add_friend_(); |
| mark->set_name("Mark"); |
| Author* john = paul->add_friend_(); |
| john->set_name("John"); |
| Author* luke = paul->add_friend_(); |
| luke->set_name("Luke"); |
| |
| options_.ignore_unknown_fields = true; |
| ResetProtoWriter(); |
| |
| EXPECT_CALL(listener_, InvalidName(_, _, _)).Times(0); |
| ow_->StartObject("") |
| ->StartObject("author") |
| ->RenderString("name", "Paul") |
| ->StartList("friend") |
| ->StartObject("") |
| ->RenderString("name", "Mark") |
| ->EndObject() |
| ->StartObject("") |
| ->RenderString("name", "John") |
| ->RenderString("address", "Patmos") |
| ->EndObject() |
| ->StartObject("") |
| ->RenderString("name", "Luke") |
| ->EndObject() |
| ->EndList() |
| ->EndObject() |
| ->EndObject(); |
| CheckOutput(expected); |
| } |
| |
| TEST_P(ProtoStreamObjectWriterTest, IgnoreUnknownObjectAtRoot) { |
| Book empty; |
| |
| options_.ignore_unknown_fields = true; |
| ResetProtoWriter(); |
| |
| EXPECT_CALL(listener_, InvalidName(_, StringPiece("unknown"), |
| StringPiece("Cannot find field."))) |
| .Times(0); |
| ow_->StartObject("")->StartObject("unknown")->EndObject()->EndObject(); |
| CheckOutput(empty, 0); |
| } |
| |
| TEST_P(ProtoStreamObjectWriterTest, IgnoreUnknownObjectAtAuthor) { |
| Book expected; |
| Author* author = expected.mutable_author(); |
| author->set_name("William"); |
| author->add_pseudonym("Bill"); |
| |
| options_.ignore_unknown_fields = true; |
| ResetProtoWriter(); |
| |
| EXPECT_CALL(listener_, InvalidName(_, _, _)).Times(0); |
| ow_->StartObject("") |
| ->StartObject("author") |
| ->RenderString("name", "William") |
| ->StartObject("wife") |
| ->RenderString("name", "Hilary") |
| ->EndObject() |
| ->RenderString("pseudonym", "Bill") |
| ->EndObject() |
| ->EndObject(); |
| CheckOutput(expected); |
| } |
| |
| TEST_P(ProtoStreamObjectWriterTest, IgnoreUnknownListAtRoot) { |
| Book empty; |
| |
| options_.ignore_unknown_fields = true; |
| ResetProtoWriter(); |
| |
| EXPECT_CALL(listener_, InvalidName(_, _, _)).Times(0); |
| ow_->StartObject("")->StartList("unknown")->EndList()->EndObject(); |
| CheckOutput(empty, 0); |
| } |
| |
| TEST_P(ProtoStreamObjectWriterTest, IgnoreUnknownListAtPublisher) { |
| Book expected; |
| expected.set_title("Brainwashing"); |
| Publisher* publisher = expected.mutable_publisher(); |
| publisher->set_name("propaganda"); |
| |
| options_.ignore_unknown_fields = true; |
| ResetProtoWriter(); |
| |
| EXPECT_CALL(listener_, InvalidName(_, _, _)).Times(0); |
| ow_->StartObject("") |
| ->StartObject("publisher") |
| ->RenderString("name", "propaganda") |
| ->StartList("alliance") |
| ->EndList() |
| ->EndObject() |
| ->RenderString("title", "Brainwashing") |
| ->EndObject(); |
| CheckOutput(expected); |
| } |
| |
| TEST_P(ProtoStreamObjectWriterTest, AcceptUnknownEnumValue) { |
| ResetTypeInfo(Proto3Message::descriptor()); |
| |
| Proto3Message expected; |
| expected.set_enum_value(static_cast<Proto3Message::NestedEnum>(12345)); |
| |
| EXPECT_CALL(listener_, InvalidValue(_, _, _)).Times(0); |
| ow_->StartObject("") |
| ->RenderInt32("enumValue", 12345) |
| ->EndObject(); |
| CheckOutput(expected); |
| } |
| |
| TEST_P(ProtoStreamObjectWriterTest, MissingRequiredField) { |
| Book expected; |
| expected.set_title("My Title"); |
| expected.set_allocated_publisher(new Publisher()); |
| |
| EXPECT_CALL(listener_, MissingField(_, StringPiece("name"))) |
| .With(Args<0>(HasObjectLocation("publisher"))); |
| ow_->StartObject("") |
| ->StartObject("publisher") |
| ->EndObject() |
| ->RenderString("title", "My Title") |
| ->EndObject(); |
| CheckOutput(expected); |
| } |
| |
| TEST_P(ProtoStreamObjectWriterTest, InvalidFieldValueAtRoot) { |
| Book empty; |
| |
| EXPECT_CALL(listener_, InvalidValue(_, StringPiece("TYPE_UINT32"), |
| StringPiece("\"garbage\""))) |
| .With(Args<0>(HasObjectLocation("length"))); |
| ow_->StartObject("")->RenderString("length", "garbage")->EndObject(); |
| CheckOutput(empty, 0); |
| } |
| |
| TEST_P(ProtoStreamObjectWriterTest, MultipleInvalidFieldValues) { |
| Book expected; |
| expected.set_title("My Title"); |
| |
| EXPECT_CALL(listener_, InvalidValue(_, StringPiece("TYPE_UINT32"), |
| StringPiece("\"-400\""))) |
| .With(Args<0>(HasObjectLocation("length"))); |
| EXPECT_CALL(listener_, InvalidValue(_, StringPiece("TYPE_INT64"), |
| StringPiece("\"3.14\""))) |
| .With(Args<0>(HasObjectLocation("published"))); |
| ow_->StartObject("") |
| ->RenderString("length", "-400") |
| ->RenderString("published", "3.14") |
| ->RenderString("title", "My Title") |
| ->EndObject(); |
| CheckOutput(expected); |
| } |
| |
| TEST_P(ProtoStreamObjectWriterTest, UnnamedFieldAtRoot) { |
| Book empty; |
| |
| EXPECT_CALL(listener_, |
| InvalidName(_, StringPiece(""), |
| StringPiece("Proto fields must have a name."))) |
| .With(Args<0>(HasObjectLocation(""))); |
| ow_->StartObject("")->RenderFloat("", 3.14)->EndObject(); |
| CheckOutput(empty, 0); |
| } |
| |
| TEST_P(ProtoStreamObjectWriterTest, UnnamedFieldAtAuthor) { |
| Book expected; |
| expected.set_title("noname"); |
| expected.set_allocated_author(new Author()); |
| |
| EXPECT_CALL(listener_, |
| InvalidName(_, StringPiece(""), |
| StringPiece("Proto fields must have a name."))) |
| .With(Args<0>(HasObjectLocation("author"))); |
| ow_->StartObject("") |
| ->StartObject("author") |
| ->RenderInt32("", 123) |
| ->EndObject() |
| ->RenderString("title", "noname") |
| ->EndObject(); |
| CheckOutput(expected); |
| } |
| |
| TEST_P(ProtoStreamObjectWriterTest, UnnamedListAtRoot) { |
| Book expected; |
| expected.set_title("noname"); |
| |
| EXPECT_CALL(listener_, |
| InvalidName(_, StringPiece(""), |
| StringPiece("Proto fields must have a name."))) |
| .With(Args<0>(HasObjectLocation(""))); |
| ow_->StartObject("") |
| ->StartList("") |
| ->EndList() |
| ->RenderString("title", "noname") |
| ->EndObject(); |
| CheckOutput(expected); |
| } |
| |
| TEST_P(ProtoStreamObjectWriterTest, RootNamedObject) { |
| Book expected; |
| expected.set_title("Annie"); |
| |
| EXPECT_CALL(listener_, |
| InvalidName(_, StringPiece("oops"), |
| StringPiece("Root element should not be named."))) |
| .With(Args<0>(HasObjectLocation(""))); |
| ow_->StartObject("oops")->RenderString("title", "Annie")->EndObject(); |
| CheckOutput(expected, 7); |
| } |
| |
| TEST_P(ProtoStreamObjectWriterTest, RootNamedList) { |
| Book empty; |
| |
| EXPECT_CALL(listener_, |
| InvalidName(_, StringPiece("oops"), |
| StringPiece("Root element should not be named."))) |
| .With(Args<0>(HasObjectLocation(""))); |
| ow_->StartList("oops")->RenderString("", "item")->EndList(); |
| CheckOutput(empty, 0); |
| } |
| |
| TEST_P(ProtoStreamObjectWriterTest, RootUnnamedField) { |
| Book empty; |
| |
| EXPECT_CALL(listener_, |
| InvalidName(_, StringPiece(""), |
| StringPiece("Root element must be a message."))) |
| .With(Args<0>(HasObjectLocation(""))); |
| ow_->RenderBool("", true); |
| CheckOutput(empty, 0); |
| } |
| |
| TEST_P(ProtoStreamObjectWriterTest, RootNamedField) { |
| Book empty; |
| |
| EXPECT_CALL(listener_, |
| InvalidName(_, StringPiece("oops"), |
| StringPiece("Root element must be a message."))) |
| .With(Args<0>(HasObjectLocation(""))); |
| ow_->RenderBool("oops", true); |
| CheckOutput(empty, 0); |
| } |
| |
| TEST_P(ProtoStreamObjectWriterTest, NullValue) { |
| Book empty; |
| |
| ow_->RenderNull(""); |
| CheckOutput(empty, 0); |
| } |
| |
| TEST_P(ProtoStreamObjectWriterTest, NullValueForMessageField) { |
| Book empty; |
| |
| ow_->RenderNull("author"); |
| CheckOutput(empty, 0); |
| } |
| |
| TEST_P(ProtoStreamObjectWriterTest, NullValueForPrimitiveField) { |
| Book empty; |
| |
| ow_->RenderNull("length"); |
| CheckOutput(empty, 0); |
| } |
| |
| class ProtoStreamObjectWriterTimestampDurationTest |
| : public BaseProtoStreamObjectWriterTest { |
| protected: |
| ProtoStreamObjectWriterTimestampDurationTest() { |
| std::vector<const Descriptor*> descriptors; |
| descriptors.push_back(TimestampDuration::descriptor()); |
| descriptors.push_back(google::protobuf::Timestamp::descriptor()); |
| descriptors.push_back(google::protobuf::Duration::descriptor()); |
| ResetTypeInfo(descriptors); |
| } |
| }; |
| |
| INSTANTIATE_TEST_CASE_P(DifferentTypeInfoSourceTest, |
| ProtoStreamObjectWriterTimestampDurationTest, |
| ::testing::Values( |
| testing::USE_TYPE_RESOLVER)); |
| |
| TEST_P(ProtoStreamObjectWriterTimestampDurationTest, ParseTimestamp) { |
| TimestampDuration timestamp; |
| google::protobuf::Timestamp* ts = timestamp.mutable_ts(); |
| ts->set_seconds(1448249855); |
| ts->set_nanos(33155000); |
| |
| ow_->StartObject("") |
| ->RenderString("ts", "2015-11-23T03:37:35.033155Z") |
| ->EndObject(); |
| CheckOutput(timestamp); |
| } |
| |
| TEST_P(ProtoStreamObjectWriterTimestampDurationTest, |
| ParseTimestampYearNotZeroPadded) { |
| TimestampDuration timestamp; |
| google::protobuf::Timestamp* ts = timestamp.mutable_ts(); |
| ts->set_seconds(-61665654145); |
| ts->set_nanos(33155000); |
| |
| ow_->StartObject("") |
| ->RenderString("ts", "15-11-23T03:37:35.033155Z") |
| ->EndObject(); |
| CheckOutput(timestamp); |
| } |
| |
| TEST_P(ProtoStreamObjectWriterTimestampDurationTest, |
| ParseTimestampYearZeroPadded) { |
| TimestampDuration timestamp; |
| google::protobuf::Timestamp* ts = timestamp.mutable_ts(); |
| ts->set_seconds(-61665654145); |
| ts->set_nanos(33155000); |
| |
| ow_->StartObject("") |
| ->RenderString("ts", "0015-11-23T03:37:35.033155Z") |
| ->EndObject(); |
| CheckOutput(timestamp); |
| } |
| |
| TEST_P(ProtoStreamObjectWriterTimestampDurationTest, |
| ParseTimestampWithPositiveOffset) { |
| TimestampDuration timestamp; |
| google::protobuf::Timestamp* ts = timestamp.mutable_ts(); |
| ts->set_seconds(1448249855); |
| ts->set_nanos(33155000); |
| |
| ow_->StartObject("") |
| ->RenderString("ts", "2015-11-23T11:47:35.033155+08:10") |
| ->EndObject(); |
| CheckOutput(timestamp); |
| } |
| |
| TEST_P(ProtoStreamObjectWriterTimestampDurationTest, |
| ParseTimestampWithNegativeOffset) { |
| TimestampDuration timestamp; |
| google::protobuf::Timestamp* ts = timestamp.mutable_ts(); |
| ts->set_seconds(1448249855); |
| ts->set_nanos(33155000); |
| |
| ow_->StartObject("") |
| ->RenderString("ts", "2015-11-22T19:47:35.033155-07:50") |
| ->EndObject(); |
| CheckOutput(timestamp); |
| } |
| |
| TEST_P(ProtoStreamObjectWriterTimestampDurationTest, |
| TimestampWithInvalidOffset1) { |
| TimestampDuration timestamp; |
| |
| EXPECT_CALL( |
| listener_, |
| InvalidValue(_, |
| StringPiece("type.googleapis.com/google.protobuf.Timestamp"), |
| StringPiece("Field 'ts', Invalid time format: " |
| "2016-03-07T15:14:23+"))); |
| |
| ow_->StartObject("")->RenderString("ts", "2016-03-07T15:14:23+")->EndObject(); |
| CheckOutput(timestamp); |
| } |
| |
| TEST_P(ProtoStreamObjectWriterTimestampDurationTest, |
| TimestampWithInvalidOffset2) { |
| TimestampDuration timestamp; |
| |
| EXPECT_CALL( |
| listener_, |
| InvalidValue(_, |
| StringPiece("type.googleapis.com/google.protobuf.Timestamp"), |
| StringPiece("Field 'ts', Invalid time format: " |
| "2016-03-07T15:14:23+08-10"))); |
| |
| ow_->StartObject("") |
| ->RenderString("ts", "2016-03-07T15:14:23+08-10") |
| ->EndObject(); |
| CheckOutput(timestamp); |
| } |
| |
| TEST_P(ProtoStreamObjectWriterTimestampDurationTest, |
| TimestampWithInvalidOffset3) { |
| TimestampDuration timestamp; |
| |
| EXPECT_CALL( |
| listener_, |
| InvalidValue(_, |
| StringPiece("type.googleapis.com/google.protobuf.Timestamp"), |
| StringPiece("Field 'ts', Invalid time format: " |
| "2016-03-07T15:14:23+24:10"))); |
| |
| ow_->StartObject("") |
| ->RenderString("ts", "2016-03-07T15:14:23+24:10") |
| ->EndObject(); |
| CheckOutput(timestamp); |
| } |
| |
| TEST_P(ProtoStreamObjectWriterTimestampDurationTest, |
| TimestampWithInvalidOffset4) { |
| TimestampDuration timestamp; |
| |
| EXPECT_CALL( |
| listener_, |
| InvalidValue(_, |
| StringPiece("type.googleapis.com/google.protobuf.Timestamp"), |
| StringPiece("Field 'ts', Invalid time format: " |
| "2016-03-07T15:14:23+04:60"))); |
| |
| ow_->StartObject("") |
| ->RenderString("ts", "2016-03-07T15:14:23+04:60") |
| ->EndObject(); |
| CheckOutput(timestamp); |
| } |
| |
| TEST_P(ProtoStreamObjectWriterTimestampDurationTest, InvalidTimestampError1) { |
| TimestampDuration timestamp; |
| |
| EXPECT_CALL( |
| listener_, |
| InvalidValue(_, |
| StringPiece("type.googleapis.com/google.protobuf.Timestamp"), |
| StringPiece("Field 'ts', Invalid time format: "))); |
| |
| ow_->StartObject("")->RenderString("ts", "")->EndObject(); |
| CheckOutput(timestamp); |
| } |
| |
| TEST_P(ProtoStreamObjectWriterTimestampDurationTest, InvalidTimestampError2) { |
| TimestampDuration timestamp; |
| |
| EXPECT_CALL( |
| listener_, |
| InvalidValue(_, |
| StringPiece("type.googleapis.com/google.protobuf.Timestamp"), |
| StringPiece("Field 'ts', Invalid time format: Z"))); |
| |
| ow_->StartObject("")->RenderString("ts", "Z")->EndObject(); |
| CheckOutput(timestamp); |
| } |
| |
| TEST_P(ProtoStreamObjectWriterTimestampDurationTest, InvalidTimestampError3) { |
| TimestampDuration timestamp; |
| |
| EXPECT_CALL( |
| listener_, |
| InvalidValue(_, |
| StringPiece("type.googleapis.com/google.protobuf.Timestamp"), |
| StringPiece("Field 'ts', Invalid time format: " |
| "1970-01-01T00:00:00.ABZ"))); |
| |
| ow_->StartObject("") |
| ->RenderString("ts", "1970-01-01T00:00:00.ABZ") |
| ->EndObject(); |
| CheckOutput(timestamp); |
| } |
| |
| TEST_P(ProtoStreamObjectWriterTimestampDurationTest, InvalidTimestampError4) { |
| TimestampDuration timestamp; |
| |
| EXPECT_CALL( |
| listener_, |
| InvalidValue(_, |
| StringPiece("type.googleapis.com/google.protobuf.Timestamp"), |
| StringPiece("Field 'ts', Invalid time format: " |
| "-8031-10-18T00:00:00.000Z"))); |
| |
| ow_->StartObject("") |
| ->RenderString("ts", "-8031-10-18T00:00:00.000Z") |
| ->EndObject(); |
| CheckOutput(timestamp); |
| } |
| |
| TEST_P(ProtoStreamObjectWriterTimestampDurationTest, InvalidTimestampError5) { |
| TimestampDuration timestamp; |
| |
| EXPECT_CALL( |
| listener_, |
| InvalidValue(_, |
| StringPiece("type.googleapis.com/google.protobuf.Timestamp"), |
| StringPiece("Field 'ts', Invalid time format: " |
| "2015-11-23T03:37:35.033155 Z"))); |
| |
| ow_->StartObject("") |
| // Whitespace in the Timestamp nanos is not allowed. |
| ->RenderString("ts", "2015-11-23T03:37:35.033155 Z") |
| ->EndObject(); |
| CheckOutput(timestamp); |
| } |
| |
| TEST_P(ProtoStreamObjectWriterTimestampDurationTest, InvalidTimestampError6) { |
| TimestampDuration timestamp; |
| |
| EXPECT_CALL( |
| listener_, |
| InvalidValue(_, |
| StringPiece("type.googleapis.com/google.protobuf.Timestamp"), |
| StringPiece("Field 'ts', Invalid time format: " |
| "2015-11-23T03:37:35.033155 1234Z"))); |
| |
| ow_->StartObject("") |
| // Whitespace in the Timestamp nanos is not allowed. |
| ->RenderString("ts", "2015-11-23T03:37:35.033155 1234Z") |
| ->EndObject(); |
| CheckOutput(timestamp); |
| } |
| |
| TEST_P(ProtoStreamObjectWriterTimestampDurationTest, InvalidTimestampError7) { |
| TimestampDuration timestamp; |
| |
| EXPECT_CALL( |
| listener_, |
| InvalidValue(_, |
| StringPiece("type.googleapis.com/google.protobuf.Timestamp"), |
| StringPiece("Field 'ts', Invalid time format: " |
| "2015-11-23T03:37:35.033abc155Z"))); |
| |
| ow_->StartObject("") |
| // Non-numeric characters in the Timestamp nanos is not allowed. |
| ->RenderString("ts", "2015-11-23T03:37:35.033abc155Z") |
| ->EndObject(); |
| CheckOutput(timestamp); |
| } |
| |
| TEST_P(ProtoStreamObjectWriterTimestampDurationTest, InvalidTimestampError8) { |
| TimestampDuration timestamp; |
| |
| EXPECT_CALL( |
| listener_, |
| InvalidValue(_, |
| StringPiece("type.googleapis.com/google.protobuf.Timestamp"), |
| StringPiece("Field 'ts', Invalid time format: " |
| "0-12-31T23:59:59.000Z"))); |
| |
| ow_->StartObject("") |
| ->RenderString("ts", "0-12-31T23:59:59.000Z") |
| ->EndObject(); |
| CheckOutput(timestamp); |
| } |
| |
| TEST_P(ProtoStreamObjectWriterTimestampDurationTest, ParseDuration) { |
| TimestampDuration duration; |
| google::protobuf::Duration* dur = duration.mutable_dur(); |
| dur->set_seconds(1448216930); |
| dur->set_nanos(132262000); |
| |
| ow_->StartObject("")->RenderString("dur", "1448216930.132262s")->EndObject(); |
| CheckOutput(duration); |
| } |
| |
| TEST_P(ProtoStreamObjectWriterTimestampDurationTest, InvalidDurationError1) { |
| TimestampDuration duration; |
| |
| EXPECT_CALL( |
| listener_, |
| InvalidValue( |
| _, StringPiece("type.googleapis.com/google.protobuf.Duration"), |
| StringPiece("Field 'dur', Illegal duration format; duration must " |
| "end with 's'"))); |
| |
| ow_->StartObject("")->RenderString("dur", "")->EndObject(); |
| CheckOutput(duration); |
| } |
| |
| TEST_P(ProtoStreamObjectWriterTimestampDurationTest, InvalidDurationError2) { |
| TimestampDuration duration; |
| |
| EXPECT_CALL( |
| listener_, |
| InvalidValue( |
| _, StringPiece("type.googleapis.com/google.protobuf.Duration"), |
| StringPiece("Field 'dur', Invalid duration format, failed to parse " |
| "seconds"))); |
| |
| ow_->StartObject("")->RenderString("dur", "s")->EndObject(); |
| CheckOutput(duration); |
| } |
| |
| TEST_P(ProtoStreamObjectWriterTimestampDurationTest, InvalidDurationError3) { |
| TimestampDuration duration; |
| |
| EXPECT_CALL( |
| listener_, |
| InvalidValue( |
| _, StringPiece("type.googleapis.com/google.protobuf.Duration"), |
| StringPiece("Field 'dur', Invalid duration format, failed to " |
| "parse nano seconds"))); |
| |
| ow_->StartObject("")->RenderString("dur", "123.DEFs")->EndObject(); |
| CheckOutput(duration); |
| } |
| |
| TEST_P(ProtoStreamObjectWriterTimestampDurationTest, InvalidDurationError4) { |
| TimestampDuration duration; |
| |
| EXPECT_CALL( |
| listener_, |
| InvalidValue(_, |
| StringPiece("type.googleapis.com/google.protobuf.Duration"), |
| StringPiece("Field 'dur', Duration value exceeds limits"))); |
| |
| ow_->StartObject("")->RenderString("dur", "315576000002s")->EndObject(); |
| CheckOutput(duration); |
| } |
| |
| TEST_P(ProtoStreamObjectWriterTimestampDurationTest, InvalidDurationError5) { |
| TimestampDuration duration; |
| |
| EXPECT_CALL( |
| listener_, |
| InvalidValue(_, |
| StringPiece("type.googleapis.com/google.protobuf.Duration"), |
| StringPiece("Field 'dur', Duration value exceeds limits"))); |
| |
| ow_->StartObject("")->RenderString("dur", "0.1000000001s")->EndObject(); |
| CheckOutput(duration); |
| } |
| |
| TEST_P(ProtoStreamObjectWriterTimestampDurationTest, |
| MismatchedTimestampTypeInput) { |
| TimestampDuration timestamp; |
| EXPECT_CALL( |
| listener_, |
| InvalidValue( |
| _, StringPiece("type.googleapis.com/google.protobuf.Timestamp"), |
| StringPiece( |
| "Field 'ts', Invalid data type for timestamp, value is 1"))) |
| .With(Args<0>(HasObjectLocation("ts"))); |
| ow_->StartObject("")->RenderInt32("ts", 1)->EndObject(); |
| CheckOutput(timestamp); |
| } |
| |
| TEST_P(ProtoStreamObjectWriterTimestampDurationTest, |
| MismatchedDurationTypeInput) { |
| TimestampDuration duration; |
| EXPECT_CALL( |
| listener_, |
| InvalidValue( |
| _, StringPiece("type.googleapis.com/google.protobuf.Duration"), |
| StringPiece( |
| "Field 'dur', Invalid data type for duration, value is 1"))) |
| .With(Args<0>(HasObjectLocation("dur"))); |
| ow_->StartObject("")->RenderInt32("dur", 1)->EndObject(); |
| CheckOutput(duration); |
| } |
| |
| TEST_P(ProtoStreamObjectWriterTimestampDurationTest, TimestampAcceptsNull) { |
| TimestampDuration timestamp; |
| EXPECT_CALL(listener_, InvalidValue(_, _, _)).Times(0); |
| ow_->StartObject("")->RenderNull("ts")->EndObject(); |
| CheckOutput(timestamp); |
| } |
| |
| TEST_P(ProtoStreamObjectWriterTimestampDurationTest, DurationAcceptsNull) { |
| TimestampDuration duration; |
| EXPECT_CALL(listener_, InvalidValue(_, _, _)).Times(0); |
| ow_->StartObject("")->RenderNull("dur")->EndObject(); |
| CheckOutput(duration); |
| } |
| |
| class ProtoStreamObjectWriterStructTest |
| : public BaseProtoStreamObjectWriterTest { |
| protected: |
| ProtoStreamObjectWriterStructTest() { ResetProtoWriter(); } |
| |
| // Resets ProtoWriter with current set of options and other state. |
| void ResetProtoWriter() { |
| std::vector<const Descriptor*> descriptors; |
| descriptors.push_back(StructType::descriptor()); |
| descriptors.push_back(google::protobuf::Struct::descriptor()); |
| ResetTypeInfo(descriptors); |
| } |
| }; |
| |
| INSTANTIATE_TEST_CASE_P(DifferentTypeInfoSourceTest, |
| ProtoStreamObjectWriterStructTest, |
| ::testing::Values( |
| testing::USE_TYPE_RESOLVER)); |
| |
| // TODO(skarvaje): Write tests for failure cases. |
| TEST_P(ProtoStreamObjectWriterStructTest, StructRenderSuccess) { |
| StructType struct_type; |
| google::protobuf::Struct* s = struct_type.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(); |
| CheckOutput(struct_type); |
| } |
| |
| TEST_P(ProtoStreamObjectWriterStructTest, StructNullInputSuccess) { |
| StructType struct_type; |
| EXPECT_CALL(listener_, |
| InvalidName(_, StringPiece(""), |
| StringPiece("Proto fields must have a name."))) |
| .With(Args<0>(HasObjectLocation(""))); |
| ow_->StartObject("")->RenderNull("")->EndObject(); |
| CheckOutput(struct_type); |
| } |
| |
| TEST_P(ProtoStreamObjectWriterStructTest, StructInvalidInputFailure) { |
| StructType struct_type; |
| EXPECT_CALL( |
| listener_, |
| InvalidValue(_, StringPiece("type.googleapis.com/google.protobuf.Struct"), |
| StringPiece("true"))) |
| .With(Args<0>(HasObjectLocation("object"))); |
| |
| ow_->StartObject("")->RenderBool("object", true)->EndObject(); |
| CheckOutput(struct_type); |
| } |
| |
| TEST_P(ProtoStreamObjectWriterStructTest, StructAcceptsNull) { |
| StructType struct_type; |
| EXPECT_CALL(listener_, InvalidValue(_, _, _)).Times(0); |
| |
| ow_->StartObject("")->RenderNull("object")->EndObject(); |
| CheckOutput(struct_type); |
| } |
| |
| TEST_P(ProtoStreamObjectWriterStructTest, StructValuePreservesNull) { |
| StructType struct_type; |
| (*struct_type.mutable_object()->mutable_fields())["key"].set_null_value( |
| google::protobuf::NULL_VALUE); |
| EXPECT_CALL(listener_, InvalidValue(_, _, _)).Times(0); |
| |
| ow_->StartObject("") |
| ->StartObject("object") |
| ->RenderNull("key") |
| ->EndObject() |
| ->EndObject(); |
| CheckOutput(struct_type); |
| } |
| |
| TEST_P(ProtoStreamObjectWriterStructTest, SimpleRepeatedStructMapKeyTest) { |
| EXPECT_CALL( |
| listener_, |
| InvalidName(_, StringPiece("gBike"), |
| StringPiece("Repeated map key: 'gBike' is already set."))); |
| ow_->StartObject("") |
| ->StartObject("object") |
| ->RenderString("gBike", "v1") |
| ->RenderString("gBike", "v2") |
| ->EndObject() |
| ->EndObject(); |
| } |
| |
| TEST_P(ProtoStreamObjectWriterStructTest, RepeatedStructMapListKeyTest) { |
| EXPECT_CALL( |
| listener_, |
| InvalidName(_, StringPiece("k1"), |
| StringPiece("Repeated map key: 'k1' is already set."))); |
| ow_->StartObject("") |
| ->StartObject("object") |
| ->RenderString("k1", "v1") |
| ->StartList("k1") |
| ->RenderString("", "v2") |
| ->EndList() |
| ->EndObject() |
| ->EndObject(); |
| } |
| |
| TEST_P(ProtoStreamObjectWriterStructTest, RepeatedStructMapObjectKeyTest) { |
| EXPECT_CALL( |
| listener_, |
| InvalidName(_, StringPiece("k1"), |
| StringPiece("Repeated map key: 'k1' is already set."))); |
| ow_->StartObject("") |
| ->StartObject("object") |
| ->StartObject("k1") |
| ->RenderString("sub_k1", "v1") |
| ->EndObject() |
| ->StartObject("k1") |
| ->RenderString("sub_k2", "v2") |
| ->EndObject() |
| ->EndObject() |
| ->EndObject(); |
| } |
| |
| TEST_P(ProtoStreamObjectWriterStructTest, OptionStructIntAsStringsTest) { |
| StructType struct_type; |
| google::protobuf::Struct* s = struct_type.mutable_object(); |
| s->mutable_fields()->operator[]("k1").set_string_value("123"); |
| s->mutable_fields()->operator[]("k2").set_bool_value(true); |
| s->mutable_fields()->operator[]("k3").set_string_value("-222222222"); |
| s->mutable_fields()->operator[]("k4").set_string_value("33333333"); |
| |
| options_.struct_integers_as_strings = true; |
| ResetProtoWriter(); |
| |
| ow_->StartObject("") |
| ->StartObject("object") |
| ->RenderDouble("k1", 123) |
| ->RenderBool("k2", true) |
| ->RenderInt64("k3", -222222222) |
| ->RenderUint64("k4", 33333333) |
| ->EndObject() |
| ->EndObject(); |
| CheckOutput(struct_type); |
| } |
| |
| TEST_P(ProtoStreamObjectWriterStructTest, ValuePreservesNull) { |
| ValueWrapper value; |
| value.mutable_value()->set_null_value(google::protobuf::NULL_VALUE); |
| |
| EXPECT_CALL(listener_, InvalidValue(_, _, _)).Times(0); |
| ow_->StartObject("")->RenderNull("value")->EndObject(); |
| CheckOutput(value); |
| } |
| |
| class ProtoStreamObjectWriterMapTest : public BaseProtoStreamObjectWriterTest { |
| protected: |
| ProtoStreamObjectWriterMapTest() |
| : BaseProtoStreamObjectWriterTest(MapIn::descriptor()) {} |
| }; |
| |
| INSTANTIATE_TEST_CASE_P(DifferentTypeInfoSourceTest, |
| ProtoStreamObjectWriterMapTest, |
| ::testing::Values( |
| testing::USE_TYPE_RESOLVER)); |
| |
| TEST_P(ProtoStreamObjectWriterMapTest, MapShouldNotAcceptList) { |
| MapIn mm; |
| EXPECT_CALL( |
| listener_, |
| InvalidValue( |
| _, StringPiece("Map"), |
| StringPiece("Cannot bind a list to map for field 'map_input'."))); |
| ow_->StartObject("") |
| ->StartList("map_input") |
| ->RenderString("a", "b") |
| ->EndList() |
| ->EndObject(); |
| CheckOutput(mm); |
| } |
| |
| TEST_P(ProtoStreamObjectWriterMapTest, RepeatedMapKeyTest) { |
| EXPECT_CALL( |
| listener_, |
| InvalidName(_, StringPiece("k1"), |
| StringPiece("Repeated map key: 'k1' is already set."))); |
| ow_->StartObject("") |
| ->RenderString("other", "test") |
| ->StartObject("map_input") |
| ->RenderString("k1", "v1") |
| ->RenderString("k1", "v2") |
| ->EndObject() |
| ->EndObject(); |
| } |
| |
| class ProtoStreamObjectWriterAnyTest : public BaseProtoStreamObjectWriterTest { |
| protected: |
| ProtoStreamObjectWriterAnyTest() { |
| std::vector<const Descriptor*> descriptors; |
| descriptors.push_back(AnyOut::descriptor()); |
| descriptors.push_back(Book::descriptor()); |
| descriptors.push_back(google::protobuf::Any::descriptor()); |
| descriptors.push_back(google::protobuf::DoubleValue::descriptor()); |
| descriptors.push_back(google::protobuf::FieldMask::descriptor()); |
| descriptors.push_back(google::protobuf::Int32Value::descriptor()); |
| descriptors.push_back(google::protobuf::Struct::descriptor()); |
| descriptors.push_back(google::protobuf::Timestamp::descriptor()); |
| descriptors.push_back(google::protobuf::Value::descriptor()); |
| ResetTypeInfo(descriptors); |
| } |
| }; |
| |
| INSTANTIATE_TEST_CASE_P(DifferentTypeInfoSourceTest, |
| ProtoStreamObjectWriterAnyTest, |
| ::testing::Values( |
| testing::USE_TYPE_RESOLVER)); |
| |
| TEST_P(ProtoStreamObjectWriterAnyTest, AnyRenderSuccess) { |
| AnyOut any; |
| google::protobuf::Any* any_type = any.mutable_any(); |
| any_type->set_type_url("type.googleapis.com/google.protobuf.DoubleValue"); |
| google::protobuf::DoubleValue d; |
| d.set_value(40.2); |
| any_type->set_value(d.SerializeAsString()); |
| |
| ow_->StartObject("") |
| ->StartObject("any") |
| ->RenderString("@type", "type.googleapis.com/google.protobuf.DoubleValue") |
| ->RenderDouble("value", 40.2) |
| ->EndObject() |
| ->EndObject(); |
| CheckOutput(any); |
| } |
| |
| TEST_P(ProtoStreamObjectWriterAnyTest, 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.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.AnyM") |
| ->RenderString("foo", "foovalue") |
| ->EndObject() |
| ->EndObject() |
| ->EndObject(); |
| CheckOutput(out, 107); |
| } |
| |
| TEST_P(ProtoStreamObjectWriterAnyTest, 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.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.AnyM") |
| ->RenderString("foo", "foovalue") |
| ->EndObject() |
| ->EndObject() |
| ->EndObject() |
| ->EndObject(); |
| CheckOutput(out, 151); |
| } |
| |
| TEST_P(ProtoStreamObjectWriterAnyTest, TypeUrlAtEnd) { |
| Book book; |
| book.set_title("C++"); |
| book.set_length(1234); |
| book.set_content("Hello World!"); |
| |
| ::google::protobuf::Any any; |
| any.PackFrom(book); |
| |
| ::google::protobuf::Any outer_any; |
| outer_any.PackFrom(any); |
| |
| AnyOut out; |
| out.mutable_any()->PackFrom(outer_any); |
| |
| // Put the @type field at the end of each Any message. Parsers should |
| // be able to accept that. |
| ow_->StartObject("") |
| ->StartObject("any") |
| ->StartObject("value") |
| ->StartObject("value") |
| ->RenderString("title", "C++") |
| ->RenderInt32("length", 1234) |
| ->RenderBytes("content", "Hello World!") |
| ->RenderString("@type", |
| "type.googleapis.com/google.protobuf.testing.Book") |
| ->EndObject() |
| ->RenderString("@type", "type.googleapis.com/google.protobuf.Any") |
| ->EndObject() |
| ->RenderString("@type", "type.googleapis.com/google.protobuf.Any") |
| ->EndObject() |
| ->EndObject(); |
| CheckOutput(out); |
| } |
| |
| // Same as TypeUrlAtEnd, but use temporary string values to make sure we don't |
| // mistakenly store StringPiece objects pointing to invalid memory. |
| TEST_P(ProtoStreamObjectWriterAnyTest, TypeUrlAtEndWithTemporaryStrings) { |
| Book book; |
| book.set_title("C++"); |
| book.set_length(1234); |
| book.set_content("Hello World!"); |
| |
| ::google::protobuf::Any any; |
| any.PackFrom(book); |
| |
| ::google::protobuf::Any outer_any; |
| outer_any.PackFrom(any); |
| |
| AnyOut out; |
| out.mutable_any()->PackFrom(outer_any); |
| |
| string name, value; |
| // Put the @type field at the end of each Any message. Parsers should |
| // be able to accept that. |
| ow_->StartObject("")->StartObject("any"); |
| { |
| ow_->StartObject("value"); |
| { |
| ow_->StartObject("value"); |
| { |
| name = "title"; |
| value = "C++"; |
| ow_->RenderString(name, value); |
| name = "length"; |
| ow_->RenderInt32(name, 1234); |
| name = "content"; |
| value = "Hello World!"; |
| ow_->RenderBytes(name, value); |
| name = "@type"; |
| value = "type.googleapis.com/google.protobuf.testing.Book"; |
| ow_->RenderString(name, value); |
| } |
| ow_->EndObject(); |
| |
| name = "@type"; |
| value = "type.googleapis.com/google.protobuf.Any"; |
| ow_->RenderString(name, value); |
| } |
| ow_->EndObject(); |
| |
| name = "@type"; |
| value = "type.googleapis.com/google.protobuf.Any"; |
| ow_->RenderString(name, value); |
| } |
| ow_->EndObject()->EndObject(); |
| CheckOutput(out); |
| } |
| |
| TEST_P(ProtoStreamObjectWriterAnyTest, EmptyAnyFromEmptyObject) { |
| AnyOut out; |
| out.mutable_any(); |
| |
| EXPECT_CALL(listener_, InvalidValue(_, _, _)).Times(0); |
| |
| ow_->StartObject("")->StartObject("any")->EndObject()->EndObject(); |
| |
| CheckOutput(out, 2); |
| } |
| |
| TEST_P(ProtoStreamObjectWriterAnyTest, AnyWithoutTypeUrlFails1) { |
| AnyOut any; |
| |
| EXPECT_CALL(listener_, |
| InvalidValue(_, StringPiece("Any"), |
| StringPiece("Missing @type for any field in " |
| "google.protobuf.testing.AnyOut"))); |
| |
| ow_->StartObject("") |
| ->StartObject("any") |
| ->StartObject("another") |
| ->EndObject() |
| ->EndObject() |
| ->EndObject(); |
| CheckOutput(any); |
| } |
| |
| TEST_P(ProtoStreamObjectWriterAnyTest, AnyWithoutTypeUrlFails2) { |
| AnyOut any; |
| |
| EXPECT_CALL(listener_, |
| InvalidValue(_, StringPiece("Any"), |
| StringPiece("Missing @type for any field in " |
| "google.protobuf.testing.AnyOut"))); |
| |
| ow_->StartObject("") |
| ->StartObject("any") |
| ->StartList("another") |
| ->EndObject() |
| ->EndObject() |
| ->EndObject(); |
| CheckOutput(any); |
| } |
| |
| TEST_P(ProtoStreamObjectWriterAnyTest, AnyWithoutTypeUrlFails3) { |
| AnyOut any; |
| |
| EXPECT_CALL(listener_, |
| InvalidValue(_, StringPiece("Any"), |
| StringPiece("Missing @type for any field in " |
| "google.protobuf.testing.AnyOut"))); |
| |
| ow_->StartObject("") |
| ->StartObject("any") |
| ->RenderString("value", "somevalue") |
| ->EndObject() |
| ->EndObject(); |
| CheckOutput(any); |
| } |
| |
| TEST_P(ProtoStreamObjectWriterAnyTest, AnyWithInvalidTypeUrlFails) { |
| AnyOut any; |
| |
| EXPECT_CALL(listener_, |
| InvalidValue( |
| _, StringPiece("Any"), |
| StringPiece("Invalid type URL, type URLs must be of the form " |
| "'type.googleapis.com/<typename>', got: " |
| "type.other.com/some.Type"))); |
| |
| ow_->StartObject("") |
| ->StartObject("any") |
| ->RenderString("@type", "type.other.com/some.Type") |
| ->RenderDouble("value", 40.2) |
| ->EndObject() |
| ->EndObject(); |
| CheckOutput(any); |
| } |
| |
| TEST_P(ProtoStreamObjectWriterAnyTest, AnyWithUnknownTypeFails) { |
| AnyOut any; |
| |
| EXPECT_CALL( |
| listener_, |
| InvalidValue(_, StringPiece("Any"), |
| StringPiece("Invalid type URL, unknown type: some.Type"))); |
| ow_->StartObject("") |
| ->StartObject("any") |
| ->RenderString("@type", "type.googleapis.com/some.Type") |
| ->RenderDouble("value", 40.2) |
| ->EndObject() |
| ->EndObject(); |
| CheckOutput(any); |
| } |
| |
| TEST_P(ProtoStreamObjectWriterAnyTest, AnyIncorrectInputTypeFails) { |
| AnyOut any; |
| |
| EXPECT_CALL( |
| listener_, |
| InvalidValue(_, StringPiece("type.googleapis.com/google.protobuf.Any"), |
| StringPiece("1"))); |
| ow_->StartObject("")->RenderInt32("any", 1)->EndObject(); |
| CheckOutput(any); |
| } |
| |
| TEST_P(ProtoStreamObjectWriterAnyTest, AnyAcceptsNull) { |
| AnyOut any; |
| |
| EXPECT_CALL(listener_, InvalidValue(_, _, _)).Times(0); |
| ow_->StartObject("")->RenderNull("any")->EndObject(); |
| CheckOutput(any); |
| } |
| |
| TEST_P(ProtoStreamObjectWriterAnyTest, AnyWellKnownTypeErrorTest) { |
| EXPECT_CALL(listener_, InvalidValue(_, StringPiece("Any"), |
| StringPiece("Invalid time format: "))); |
| |
| AnyOut any; |
| google::protobuf::Any* any_type = any.mutable_any(); |
| any_type->set_type_url("type.googleapis.com/google.protobuf.Timestamp"); |
| |
| ow_->StartObject("") |
| ->StartObject("any") |
| ->RenderString("@type", "type.googleapis.com/google.protobuf.Timestamp") |
| ->RenderString("value", "") |
| ->EndObject() |
| ->EndObject(); |
| CheckOutput(any); |
| } |
| |
| // Test the following case: |
| // |
| // { |
| // "any": { |
| // "@type": "type.googleapis.com/google.protobuf.Value", |
| // "value": "abc" |
| // } |
| // } |
| TEST_P(ProtoStreamObjectWriterAnyTest, AnyWithNestedPrimitiveValue) { |
| AnyOut out; |
| ::google::protobuf::Any* any = out.mutable_any(); |
| |
| ::google::protobuf::Value value; |
| value.set_string_value("abc"); |
| any->PackFrom(value); |
| |
| ow_->StartObject("") |
| ->StartObject("any") |
| ->RenderString("@type", "type.googleapis.com/google.protobuf.Value") |
| ->RenderString("value", "abc") |
| ->EndObject() |
| ->EndObject(); |
| CheckOutput(out); |
| } |
| |
| // Test the following case: |
| // |
| // { |
| // "any": { |
| // "@type": "type.googleapis.com/google.protobuf.Value", |
| // "value": { |
| // "foo": "abc" |
| // } |
| // } |
| // } |
| TEST_P(ProtoStreamObjectWriterAnyTest, AnyWithNestedObjectValue) { |
| AnyOut out; |
| ::google::protobuf::Any* any = out.mutable_any(); |
| |
| ::google::protobuf::Value value; |
| (*value.mutable_struct_value()->mutable_fields())["foo"].set_string_value( |
| "abc"); |
| any->PackFrom(value); |
| |
| ow_->StartObject("") |
| ->StartObject("any") |
| ->RenderString("@type", "type.googleapis.com/google.protobuf.Value") |
| ->StartObject("value") |
| ->RenderString("foo", "abc") |
| ->EndObject() |
| ->EndObject() |
| ->EndObject(); |
| CheckOutput(out); |
| } |
| |
| // Test the following case: |
| // |
| // { |
| // "any": { |
| // "@type": "type.googleapis.com/google.protobuf.Value", |
| // "value": ["hello"], |
| // } |
| // } |
| TEST_P(ProtoStreamObjectWriterAnyTest, AnyWithNestedArrayValue) { |
| AnyOut out; |
| ::google::protobuf::Any* any = out.mutable_any(); |
| |
| ::google::protobuf::Value value; |
| value.mutable_list_value()->add_values()->set_string_value("hello"); |
| any->PackFrom(value); |
| |
| ow_->StartObject("") |
| ->StartObject("any") |
| ->RenderString("@type", "type.googleapis.com/google.protobuf.Value") |
| ->StartList("value") |
| ->RenderString("", "hello") |
| ->EndList() |
| ->EndObject() |
| ->EndObject() |
| ->EndObject(); |
| CheckOutput(out); |
| } |
| |
| // Test the following case: |
| // |
| // { |
| // "any": { |
| // "@type": "type.googleapis.com/google.protobuf.Value", |
| // "not_value": "" |
| // } |
| // } |
| TEST_P(ProtoStreamObjectWriterAnyTest, |
| AnyWellKnownTypesNoValueFieldForPrimitive) { |
| EXPECT_CALL( |
| listener_, |
| InvalidValue( |
| _, StringPiece("Any"), |
| StringPiece("Expect a \"value\" field for well-known types."))); |
| AnyOut any; |
| google::protobuf::Any* any_type = any.mutable_any(); |
| any_type->set_type_url("type.googleapis.com/google.protobuf.Value"); |
| |
| ow_->StartObject("") |
| ->StartObject("any") |
| ->RenderString("@type", "type.googleapis.com/google.protobuf.Value") |
| ->RenderString("not_value", "") |
| ->EndObject() |
| ->EndObject(); |
| CheckOutput(any); |
| } |
| |
| // Test the following case: |
| // |
| // { |
| // "any": { |
| // "@type": "type.googleapis.com/google.protobuf.Value", |
| // "not_value": {} |
| // } |
| // } |
| TEST_P(ProtoStreamObjectWriterAnyTest, AnyWellKnownTypesNoValueFieldForObject) { |
| EXPECT_CALL( |
| listener_, |
| InvalidValue( |
| _, StringPiece("Any"), |
| StringPiece("Expect a \"value\" field for well-known types."))); |
| AnyOut any; |
| google::protobuf::Any* any_type = any.mutable_any(); |
| any_type->set_type_url("type.googleapis.com/google.protobuf.Value"); |
| |
| ow_->StartObject("") |
| ->StartObject("any") |
| ->RenderString("@type", "type.googleapis.com/google.protobuf.Value") |
| ->StartObject("not_value") |
| ->EndObject() |
| ->EndObject() |
| ->EndObject(); |
| CheckOutput(any); |
| } |
| |
| // Test the following case: |
| // |
| // { |
| // "any": { |
| // "@type": "type.googleapis.com/google.protobuf.Value", |
| // "not_value": [], |
| // } |
| // } |
| TEST_P(ProtoStreamObjectWriterAnyTest, AnyWellKnownTypesNoValueFieldForArray) { |
| EXPECT_CALL( |
| listener_, |
| InvalidValue( |
| _, StringPiece("Any"), |
| StringPiece("Expect a \"value\" field for well-known types."))); |
| AnyOut any; |
| google::protobuf::Any* any_type = any.mutable_any(); |
| any_type->set_type_url("type.googleapis.com/google.protobuf.Value"); |
| |
| ow_->StartObject("") |
| ->StartObject("any") |
| ->RenderString("@type", "type.googleapis.com/google.protobuf.Value") |
| ->StartList("not_value") |
| ->EndList() |
| ->EndObject() |
| ->EndObject() |
| ->EndObject(); |
| CheckOutput(any); |
| } |
| |
| // Test the following case: |
| // |
| // { |
| // "any": { |
| // "@type": "type.googleapis.com/google.protobuf.Struct", |
| // "value": "", |
| // } |
| // } |
| TEST_P(ProtoStreamObjectWriterAnyTest, AnyWellKnownTypesExpectObjectForStruct) { |
| EXPECT_CALL(listener_, InvalidValue(_, StringPiece("Any"), |
| StringPiece("Expect a JSON object."))); |
| AnyOut any; |
| google::protobuf::Any* any_type = any.mutable_any(); |
| any_type->set_type_url("type.googleapis.com/google.protobuf.Struct"); |
| |
| ow_->StartObject("") |
| ->StartObject("any") |
| ->RenderString("@type", "type.googleapis.com/google.protobuf.Struct") |
| ->RenderString("value", "") |
| ->EndObject() |
| ->EndObject(); |
| CheckOutput(any); |
| } |
| |
| // Test the following case: |
| // |
| // { |
| // "any": { |
| // "@type": "type.googleapis.com/google.protobuf.Any", |
| // "value": "", |
| // } |
| // } |
| TEST_P(ProtoStreamObjectWriterAnyTest, AnyWellKnownTypesExpectObjectForAny) { |
| EXPECT_CALL(listener_, InvalidValue(_, StringPiece("Any"), |
| StringPiece("Expect a JSON object."))); |
| AnyOut any; |
| google::protobuf::Any* any_type = any.mutable_any(); |
| any_type->set_type_url("type.googleapis.com/google.protobuf.Any"); |
| |
| ow_->StartObject("") |
| ->StartObject("any") |
| ->RenderString("@type", "type.googleapis.com/google.protobuf.Any") |
| ->RenderString("value", "") |
| ->EndObject() |
| ->EndObject(); |
| CheckOutput(any); |
| } |
| |
| // { |
| // "any": { |
| // "@type": "type.googleapis.com/google.protobuf.Any", |
| // "value": null |
| // } |
| // } |
| TEST_P(ProtoStreamObjectWriterAnyTest, AnyInAnyAcceptsNull) { |
| AnyOut out; |
| google::protobuf::Any empty; |
| out.mutable_any()->PackFrom(empty); |
| |
| EXPECT_CALL(listener_, InvalidValue(_, _, _)).Times(0); |
| ow_->StartObject("") |
| ->StartObject("any") |
| ->RenderString("@type", "type.googleapis.com/google.protobuf.Any") |
| ->RenderNull("value") |
| ->EndObject() |
| ->EndObject(); |
| CheckOutput(out); |
| } |
| |
| // { |
| // "any": { |
| // "@type": "type.googleapis.com/google.protobuf.Timestamp", |
| // "value": null |
| // } |
| // } |
| TEST_P(ProtoStreamObjectWriterAnyTest, TimestampInAnyAcceptsNull) { |
| AnyOut out; |
| google::protobuf::Timestamp empty; |
| out.mutable_any()->PackFrom(empty); |
| |
| EXPECT_CALL(listener_, InvalidValue(_, _, _)).Times(0); |
| ow_->StartObject("") |
| ->StartObject("any") |
| ->RenderString("@type", "type.googleapis.com/google.protobuf.Timestamp") |
| ->RenderNull("value") |
| ->EndObject() |
| ->EndObject(); |
| CheckOutput(out); |
| } |
| |
| // { |
| // "any": { |
| // "@type": "type.googleapis.com/google.protobuf.Duration", |
| // "value": null |
| // } |
| // } |
| TEST_P(ProtoStreamObjectWriterAnyTest, DurationInAnyAcceptsNull) { |
| AnyOut out; |
| google::protobuf::Duration empty; |
| out.mutable_any()->PackFrom(empty); |
| |
| EXPECT_CALL(listener_, InvalidValue(_, _, _)).Times(0); |
| ow_->StartObject("") |
| ->StartObject("any") |
| ->RenderString("@type", "type.googleapis.com/google.protobuf.Duration") |
| ->RenderNull("value") |
| ->EndObject() |
| ->EndObject(); |
| CheckOutput(out); |
| } |
| |
| // { |
| // "any": { |
| // "@type": "type.googleapis.com/google.protobuf.FieldMask", |
| // "value": null |
| // } |
| // } |
| TEST_P(ProtoStreamObjectWriterAnyTest, FieldMaskInAnyAcceptsNull) { |
| AnyOut out; |
| google::protobuf::FieldMask empty; |
| out.mutable_any()->PackFrom(empty); |
| |
| EXPECT_CALL(listener_, InvalidValue(_, _, _)).Times(0); |
| ow_->StartObject("") |
| ->StartObject("any") |
| ->RenderString("@type", "type.googleapis.com/google.protobuf.FieldMask") |
| ->RenderNull("value") |
| ->EndObject() |
| ->EndObject(); |
| CheckOutput(out); |
| } |
| |
| // { |
| // "any": { |
| // "@type": "type.googleapis.com/google.protobuf.Int32Value", |
| // "value": null |
| // } |
| // } |
| TEST_P(ProtoStreamObjectWriterAnyTest, WrapperInAnyAcceptsNull) { |
| AnyOut out; |
| google::protobuf::Int32Value empty; |
| out.mutable_any()->PackFrom(empty); |
| |
| EXPECT_CALL(listener_, InvalidValue(_, _, _)).Times(0); |
| ow_->StartObject("") |
| ->StartObject("any") |
| ->RenderString("@type", "type.googleapis.com/google.protobuf.Int32Value") |
| ->RenderNull("value") |
| ->EndObject() |
| ->EndObject(); |
| CheckOutput(out); |
| } |
| |
| class ProtoStreamObjectWriterFieldMaskTest |
| : public BaseProtoStreamObjectWriterTest { |
| protected: |
| ProtoStreamObjectWriterFieldMaskTest() { |
| std::vector<const Descriptor*> descriptors; |
| descriptors.push_back(FieldMaskTest::descriptor()); |
| descriptors.push_back(google::protobuf::FieldMask::descriptor()); |
| ResetTypeInfo(descriptors); |
| } |
| }; |
| |
| INSTANTIATE_TEST_CASE_P(DifferentTypeInfoSourceTest, |
| ProtoStreamObjectWriterFieldMaskTest, |
| ::testing::Values( |
| testing::USE_TYPE_RESOLVER)); |
| |
| TEST_P(ProtoStreamObjectWriterFieldMaskTest, SimpleFieldMaskTest) { |
| FieldMaskTest expected; |
| expected.set_id("1"); |
| expected.mutable_single_mask()->add_paths("path1"); |
| |
| ow_->StartObject(""); |
| ow_->RenderString("id", "1"); |
| ow_->RenderString("single_mask", "path1"); |
| ow_->EndObject(); |
| |
| CheckOutput(expected); |
| } |
| |
| TEST_P(ProtoStreamObjectWriterFieldMaskTest, MutipleMasksInCompactForm) { |
| FieldMaskTest expected; |
| expected.set_id("1"); |
| expected.mutable_single_mask()->add_paths("camel_case1"); |
| expected.mutable_single_mask()->add_paths("camel_case2"); |
| expected.mutable_single_mask()->add_paths("camel_case3"); |
| |
| ow_->StartObject(""); |
| ow_->RenderString("id", "1"); |
| ow_->RenderString("single_mask", "camelCase1,camelCase2,camelCase3"); |
| ow_->EndObject(); |
| |
| CheckOutput(expected); |
| } |
| |
| TEST_P(ProtoStreamObjectWriterFieldMaskTest, RepeatedFieldMaskTest) { |
| FieldMaskTest expected; |
| expected.set_id("1"); |
| google::protobuf::FieldMask* mask = expected.add_repeated_mask(); |
| mask->add_paths("field1"); |
| mask->add_paths("field2"); |
| expected.add_repeated_mask()->add_paths("field3"); |
| |
| ow_->StartObject(""); |
| ow_->RenderString("id", "1"); |
| ow_->StartList("repeated_mask"); |
| ow_->RenderString("", "field1,field2"); |
| ow_->RenderString("", "field3"); |
| ow_->EndList(); |
| ow_->EndObject(); |
| |
| CheckOutput(expected); |
| } |
| |
| TEST_P(ProtoStreamObjectWriterFieldMaskTest, EmptyFieldMaskTest) { |
| FieldMaskTest expected; |
| expected.set_id("1"); |
| |
| ow_->StartObject(""); |
| ow_->RenderString("id", "1"); |
| ow_->RenderString("single_mask", ""); |
| ow_->EndObject(); |
| |
| CheckOutput(expected); |
| } |
| |
| TEST_P(ProtoStreamObjectWriterFieldMaskTest, MaskUsingApiaryStyleShouldWork) { |
| FieldMaskTest expected; |
| expected.set_id("1"); |
| |
| ow_->StartObject(""); |
| ow_->RenderString("id", "1"); |
| // Case1 |
| ow_->RenderString("single_mask", |
| "outerField(camelCase1,camelCase2,camelCase3)"); |
| expected.mutable_single_mask()->add_paths("outer_field.camel_case1"); |
| expected.mutable_single_mask()->add_paths("outer_field.camel_case2"); |
| expected.mutable_single_mask()->add_paths("outer_field.camel_case3"); |
| |
| ow_->StartList("repeated_mask"); |
| |
| ow_->RenderString("", "a(field1,field2)"); |
| google::protobuf::FieldMask* mask = expected.add_repeated_mask(); |
| mask->add_paths("a.field1"); |
| mask->add_paths("a.field2"); |
| |
| ow_->RenderString("", "a(field3)"); |
| mask = expected.add_repeated_mask(); |
| mask->add_paths("a.field3"); |
| |
| ow_->RenderString("", "a()"); |
| expected.add_repeated_mask(); |
| |
| ow_->RenderString("", "a(,)"); |
| expected.add_repeated_mask(); |
| |
| ow_->RenderString("", "a(field1(field2(field3)))"); |
| mask = expected.add_repeated_mask(); |
| mask->add_paths("a.field1.field2.field3"); |
| |
| ow_->RenderString("", "a(field1(field2(field3,field4),field5),field6)"); |
| mask = expected.add_repeated_mask(); |
| mask->add_paths("a.field1.field2.field3"); |
| mask->add_paths("a.field1.field2.field4"); |
| mask->add_paths("a.field1.field5"); |
| mask->add_paths("a.field6"); |
| |
| ow_->RenderString("", "a(id,field1(id,field2(field3,field4),field5),field6)"); |
| mask = expected.add_repeated_mask(); |
| mask->add_paths("a.id"); |
| mask->add_paths("a.field1.id"); |
| mask->add_paths("a.field1.field2.field3"); |
| mask->add_paths("a.field1.field2.field4"); |
| mask->add_paths("a.field1.field5"); |
| mask->add_paths("a.field6"); |
| |
| ow_->RenderString("", "a(((field3,field4)))"); |
| mask = expected.add_repeated_mask(); |
| mask->add_paths("a.field3"); |
| mask->add_paths("a.field4"); |
| |
| ow_->EndList(); |
| ow_->EndObject(); |
| |
| CheckOutput(expected); |
| } |
| |
| TEST_P(ProtoStreamObjectWriterFieldMaskTest, MoreCloseThanOpenParentheses) { |
| EXPECT_CALL( |
| listener_, |
| InvalidValue( |
| _, StringPiece("type.googleapis.com/google.protobuf.FieldMask"), |
| StringPiece("Field 'single_mask', Invalid FieldMask 'a(b,c))'. " |
| "Cannot find matching '(' for all ')'."))); |
| |
| ow_->StartObject(""); |
| ow_->RenderString("id", "1"); |
| ow_->RenderString("single_mask", "a(b,c))"); |
| ow_->EndObject(); |
| } |
| |
| TEST_P(ProtoStreamObjectWriterFieldMaskTest, MoreOpenThanCloseParentheses) { |
| EXPECT_CALL( |
| listener_, |
| InvalidValue( |
| _, StringPiece("type.googleapis.com/google.protobuf.FieldMask"), |
| StringPiece( |
| "Field 'single_mask', Invalid FieldMask 'a(((b,c)'. Cannot " |
| "find matching ')' for all '('."))); |
| |
| ow_->StartObject(""); |
| ow_->RenderString("id", "1"); |
| ow_->RenderString("single_mask", "a(((b,c)"); |
| ow_->EndObject(); |
| } |
| |
| TEST_P(ProtoStreamObjectWriterFieldMaskTest, PathWithMapKeyShouldWork) { |
| FieldMaskTest expected; |
| expected.mutable_single_mask()->add_paths("path.to.map[\"key1\"]"); |
| expected.mutable_single_mask()->add_paths( |
| "path.to.map[\"e\\\"[]][scape\\\"\"]"); |
| expected.mutable_single_mask()->add_paths("path.to.map[\"key2\"]"); |
| |
| ow_->StartObject(""); |
| ow_->RenderString("single_mask", |
| "path.to.map[\"key1\"],path.to.map[\"e\\\"[]][scape\\\"\"]," |
| "path.to.map[\"key2\"]"); |
| ow_->EndObject(); |
| |
| CheckOutput(expected); |
| } |
| |
| TEST_P(ProtoStreamObjectWriterFieldMaskTest, |
| MapKeyMustBeAtTheEndOfAPathSegment) { |
| EXPECT_CALL( |
| listener_, |
| InvalidValue( |
| _, StringPiece("type.googleapis.com/google.protobuf.FieldMask"), |
| StringPiece("Field 'single_mask', Invalid FieldMask " |
| "'path.to.map[\"key1\"]a,path.to.map[\"key2\"]'. " |
| "Map keys should be at the end of a path segment."))); |
| |
| ow_->StartObject(""); |
| ow_->RenderString("single_mask", |
| "path.to.map[\"key1\"]a,path.to.map[\"key2\"]"); |
| ow_->EndObject(); |
| } |
| |
| TEST_P(ProtoStreamObjectWriterFieldMaskTest, MapKeyMustEnd) { |
| EXPECT_CALL( |
| listener_, |
| InvalidValue(_, |
| StringPiece("type.googleapis.com/google.protobuf.FieldMask"), |
| StringPiece("Field 'single_mask', Invalid FieldMask " |
| "'path.to.map[\"key1\"'. Map keys should be " |
| "represented as [\"some_key\"]."))); |
| |
| ow_->StartObject(""); |
| ow_->RenderString("single_mask", "path.to.map[\"key1\""); |
| ow_->EndObject(); |
| } |
| |
| TEST_P(ProtoStreamObjectWriterFieldMaskTest, MapKeyMustBeEscapedCorrectly) { |
| EXPECT_CALL( |
| listener_, |
| InvalidValue(_, |
| StringPiece("type.googleapis.com/google.protobuf.FieldMask"), |
| StringPiece("Field 'single_mask', Invalid FieldMask " |
| "'path.to.map[\"ke\"y1\"]'. Map keys should be " |
| "represented as [\"some_key\"]."))); |
| |
| ow_->StartObject(""); |
| ow_->RenderString("single_mask", "path.to.map[\"ke\"y1\"]"); |
| ow_->EndObject(); |
| } |
| |
| TEST_P(ProtoStreamObjectWriterFieldMaskTest, MapKeyCanContainAnyChars) { |
| FieldMaskTest expected; |
| expected.mutable_single_mask()->add_paths( |
| // \xE5\xAD\x99 is the UTF-8 byte sequence for chinese character å™. |
| // We cannot embed non-ASCII characters in the code directly because |
| // some windows compilers will try to interpret them using the system's |
| // current encoding and end up with invalid UTF-8 byte sequence. |
| "path.to.map[\"(),[],\\\"'!@#$%^&*123_|War\xE5\xAD\x99,./?><\\\\\"]"); |
| expected.mutable_single_mask()->add_paths("path.to.map[\"key2\"]"); |
| |
| ow_->StartObject(""); |
| ow_->RenderString( |
| "single_mask", |
| "path.to.map[\"(),[],\\\"'!@#$%^&*123_|War\xE5\xAD\x99,./?><\\\\\"]," |
| "path.to.map[\"key2\"]"); |
| ow_->EndObject(); |
| |
| CheckOutput(expected); |
| } |
| |
| TEST_P(ProtoStreamObjectWriterFieldMaskTest, FieldMaskAcceptsNull) { |
| FieldMaskTest expected; |
| EXPECT_CALL(listener_, InvalidValue(_, _, _)).Times(0); |
| ow_->StartObject("")->RenderNull("single_mask")->EndObject(); |
| CheckOutput(expected); |
| } |
| |
| class ProtoStreamObjectWriterWrappersTest |
| : public BaseProtoStreamObjectWriterTest { |
| protected: |
| ProtoStreamObjectWriterWrappersTest() { |
| std::vector<const Descriptor*> descriptors; |
| descriptors.push_back(Int32Wrapper::descriptor()); |
| descriptors.push_back(google::protobuf::Int32Value::descriptor()); |
| ResetTypeInfo(descriptors); |
| } |
| }; |
| |
| INSTANTIATE_TEST_CASE_P(DifferentTypeInfoSourceTest, |
| ProtoStreamObjectWriterWrappersTest, |
| ::testing::Values( |
| testing::USE_TYPE_RESOLVER)); |
| |
| TEST_P(ProtoStreamObjectWriterWrappersTest, WrapperAcceptsNull) { |
| Int32Wrapper wrapper; |
| EXPECT_CALL(listener_, InvalidName(_, _, _)).Times(0); |
| ow_->StartObject("")->RenderNull("int32")->EndObject(); |
| CheckOutput(wrapper); |
| } |
| |
| class ProtoStreamObjectWriterOneOfsTest |
| : public BaseProtoStreamObjectWriterTest { |
| protected: |
| ProtoStreamObjectWriterOneOfsTest() { |
| std::vector<const Descriptor*> descriptors; |
| descriptors.push_back(OneOfsRequest::descriptor()); |
| descriptors.push_back(google::protobuf::Struct::descriptor()); |
| ResetTypeInfo(descriptors); |
| } |
| }; |
| |
| INSTANTIATE_TEST_CASE_P(DifferentTypeInfoSourceTest, |
| ProtoStreamObjectWriterOneOfsTest, |
| ::testing::Values( |
| testing::USE_TYPE_RESOLVER)); |
| |
| TEST_P(ProtoStreamObjectWriterOneOfsTest, |
| MultipleOneofsFailForPrimitiveTypesTest) { |
| EXPECT_CALL( |
| listener_, |
| InvalidValue( |
| _, StringPiece("oneof"), |
| StringPiece( |
| "oneof field 'data' is already set. Cannot set 'intData'"))); |
| |
| ow_->StartObject(""); |
| ow_->RenderString("strData", "blah"); |
| ow_->RenderString("intData", "123"); |
| ow_->EndObject(); |
| } |
| |
| TEST_P(ProtoStreamObjectWriterOneOfsTest, |
| MultipleOneofsFailForMessageTypesPrimitiveFirstTest) { |
| // Test for setting primitive oneof field first and then message field. |
| EXPECT_CALL(listener_, |
| InvalidValue(_, StringPiece("oneof"), |
| StringPiece("oneof field 'data' is already set. " |
| "Cannot set 'messageData'"))); |
| |
| // JSON: { "strData": "blah", "messageData": { "dataValue": 123 } } |
| ow_->StartObject(""); |
| ow_->RenderString("strData", "blah"); |
| ow_->StartObject("messageData"); |
| ow_->RenderInt32("dataValue", 123); |
| ow_->EndObject(); |
| ow_->EndObject(); |
| } |
| |
| TEST_P(ProtoStreamObjectWriterOneOfsTest, |
| MultipleOneofsFailForMessageTypesMessageFirstTest) { |
| // Test for setting message oneof field first and then primitive field. |
| EXPECT_CALL(listener_, |
| InvalidValue(_, StringPiece("oneof"), |
| StringPiece("oneof field 'data' is already set. " |
| "Cannot set 'strData'"))); |
| |
| // JSON: { "messageData": { "dataValue": 123 }, "strData": "blah" } |
| ow_->StartObject(""); |
| ow_->StartObject("messageData"); |
| ow_->RenderInt32("dataValue", 123); |
| ow_->EndObject(); |
| ow_->RenderString("strData", "blah"); |
| ow_->EndObject(); |
| } |
| |
| TEST_P(ProtoStreamObjectWriterOneOfsTest, |
| MultipleOneofsFailForStructTypesPrimitiveFirstTest) { |
| EXPECT_CALL(listener_, |
| InvalidValue(_, StringPiece("oneof"), |
| StringPiece("oneof field 'data' is already set. " |
| "Cannot set 'structData'"))); |
| |
| // JSON: { "strData": "blah", "structData": { "a": "b" } } |
| ow_->StartObject(""); |
| ow_->RenderString("strData", "blah"); |
| ow_->StartObject("structData"); |
| ow_->RenderString("a", "b"); |
| ow_->EndObject(); |
| ow_->EndObject(); |
| } |
| |
| TEST_P(ProtoStreamObjectWriterOneOfsTest, |
| MultipleOneofsFailForStructTypesStructFirstTest) { |
| EXPECT_CALL(listener_, |
| InvalidValue(_, StringPiece("oneof"), |
| StringPiece("oneof field 'data' is already set. " |
| "Cannot set 'strData'"))); |
| |
| // JSON: { "structData": { "a": "b" }, "strData": "blah" } |
| ow_->StartObject(""); |
| ow_->StartObject("structData"); |
| ow_->RenderString("a", "b"); |
| ow_->EndObject(); |
| ow_->RenderString("strData", "blah"); |
| ow_->EndObject(); |
| } |
| |
| TEST_P(ProtoStreamObjectWriterOneOfsTest, |
| MultipleOneofsFailForStructValueTypesTest) { |
| EXPECT_CALL(listener_, |
| InvalidValue(_, StringPiece("oneof"), |
| StringPiece("oneof field 'data' is already set. " |
| "Cannot set 'valueData'"))); |
| |
| // JSON: { "messageData": { "dataValue": 123 }, "valueData": { "a": "b" } } |
| ow_->StartObject(""); |
| ow_->StartObject("messageData"); |
| ow_->RenderInt32("dataValue", 123); |
| ow_->EndObject(); |
| ow_->StartObject("valueData"); |
| ow_->RenderString("a", "b"); |
| ow_->EndObject(); |
| ow_->EndObject(); |
| } |
| |
| TEST_P(ProtoStreamObjectWriterOneOfsTest, |
| MultipleOneofsFailForWellKnownTypesPrimitiveFirstTest) { |
| EXPECT_CALL(listener_, |
| InvalidValue(_, StringPiece("oneof"), |
| StringPiece("oneof field 'data' is already set. " |
| "Cannot set 'tsData'"))); |
| |
| // JSON: { "intData": 123, "tsData": "1970-01-02T01:00:00.000Z" } |
| ow_->StartObject(""); |
| ow_->RenderInt32("intData", 123); |
| ow_->RenderString("tsData", "1970-01-02T01:00:00.000Z"); |
| ow_->EndObject(); |
| } |
| |
| TEST_P(ProtoStreamObjectWriterOneOfsTest, |
| MultipleOneofsFailForWellKnownTypesWktFirstTest) { |
| EXPECT_CALL(listener_, |
| InvalidValue(_, StringPiece("oneof"), |
| StringPiece("oneof field 'data' is already set. " |
| "Cannot set 'intData'"))); |
| |
| // JSON: { "tsData": "1970-01-02T01:00:00.000Z", "intData": 123 } |
| ow_->StartObject(""); |
| ow_->RenderString("tsData", "1970-01-02T01:00:00.000Z"); |
| ow_->RenderInt32("intData", 123); |
| ow_->EndObject(); |
| } |
| |
| TEST_P(ProtoStreamObjectWriterOneOfsTest, |
| MultipleOneofsFailForWellKnownTypesAndMessageTest) { |
| EXPECT_CALL(listener_, |
| InvalidValue(_, StringPiece("oneof"), |
| StringPiece("oneof field 'data' is already set. " |
| "Cannot set 'messageData'"))); |
| |
| // JSON: { "tsData": "1970-01-02T01:00:00.000Z", |
| // "messageData": { "dataValue": 123 } } |
| ow_->StartObject(""); |
| ow_->RenderString("tsData", "1970-01-02T01:00:00.000Z"); |
| ow_->StartObject("messageData"); |
| ow_->RenderInt32("dataValue", 123); |
| ow_->EndObject(); |
| ow_->EndObject(); |
| } |
| |
| TEST_P(ProtoStreamObjectWriterOneOfsTest, |
| MultipleOneofsFailForOneofWithinAnyTest) { |
| EXPECT_CALL(listener_, |
| InvalidValue(_, StringPiece("oneof"), |
| StringPiece("oneof field 'data' is already set. " |
| "Cannot set 'intData'"))); |
| |
| // JSON: |
| // { "anyData": |
| // { "@type": |
| // "type.googleapis.com/google.protobuf.testing.oneofs.OneOfsRequest", |
| // "strData": "blah", |
| // "intData": 123 |
| // } |
| // } |
| ow_->StartObject(""); |
| ow_->StartObject("anyData"); |
| ow_->RenderString( |
| "@type", |
| "type.googleapis.com/google.protobuf.testing.oneofs.OneOfsRequest"); |
| ow_->RenderString("strData", "blah"); |
| ow_->RenderInt32("intData", 123); |
| ow_->EndObject(); |
| ow_->EndObject(); |
| } |
| |
| } // namespace converter |
| } // namespace util |
| } // namespace protobuf |
| } // namespace google |