diff options
Diffstat (limited to 'conformance/conformance_test_impl.cc')
-rw-r--r-- | conformance/conformance_test_impl.cc | 2367 |
1 files changed, 2367 insertions, 0 deletions
diff --git a/conformance/conformance_test_impl.cc b/conformance/conformance_test_impl.cc new file mode 100644 index 00000000..a884deaa --- /dev/null +++ b/conformance/conformance_test_impl.cc @@ -0,0 +1,2367 @@ +// 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 "conformance_test.h" +#include "third_party/jsoncpp/json.h" + +#include <google/protobuf/test_messages_proto3.pb.h> +#include <google/protobuf/test_messages_proto2.pb.h> + +#include <google/protobuf/stubs/common.h> +#include <google/protobuf/stubs/strutil.h> +#include <google/protobuf/text_format.h> +#include <google/protobuf/util/type_resolver_util.h> +#include <google/protobuf/wire_format_lite.h> + +using conformance::ConformanceRequest; +using conformance::ConformanceResponse; +using google::protobuf::Descriptor; +using google::protobuf::FieldDescriptor; +using google::protobuf::Message; +using google::protobuf::internal::WireFormatLite; +using google::protobuf::TextFormat; +using google::protobuf::util::NewTypeResolverForDescriptorPool; +using protobuf_test_messages::proto3::TestAllTypesProto3; +using protobuf_test_messages::proto2::TestAllTypesProto2; +using std::string; + +namespace { + +static const char kTypeUrlPrefix[] = "type.googleapis.com"; + +static string GetTypeUrl(const Descriptor* message) { + return string(kTypeUrlPrefix) + "/" + message->full_name(); +} + +/* Routines for building arbitrary protos *************************************/ + +// We would use CodedOutputStream except that we want more freedom to build +// arbitrary protos (even invalid ones). + +const string empty; + +string cat(const string& a, const string& b, + const string& c = empty, + const string& d = empty, + const string& e = empty, + const string& f = empty, + const string& g = empty, + const string& h = empty, + const string& i = empty, + const string& j = empty, + const string& k = empty, + const string& l = empty) { + string ret; + ret.reserve(a.size() + b.size() + c.size() + d.size() + e.size() + f.size() + + g.size() + h.size() + i.size() + j.size() + k.size() + l.size()); + ret.append(a); + ret.append(b); + ret.append(c); + ret.append(d); + ret.append(e); + ret.append(f); + ret.append(g); + ret.append(h); + ret.append(i); + ret.append(j); + ret.append(k); + ret.append(l); + return ret; +} + +// The maximum number of bytes that it takes to encode a 64-bit varint. +#define VARINT_MAX_LEN 10 + +size_t vencode64(uint64_t val, int over_encoded_bytes, char *buf) { + if (val == 0) { buf[0] = 0; return 1; } + size_t i = 0; + while (val) { + uint8_t byte = val & 0x7fU; + val >>= 7; + if (val || over_encoded_bytes) byte |= 0x80U; + buf[i++] = byte; + } + while (over_encoded_bytes--) { + assert(i < 10); + uint8_t byte = over_encoded_bytes ? 0x80 : 0; + buf[i++] = byte; + } + return i; +} + +string varint(uint64_t x) { + char buf[VARINT_MAX_LEN]; + size_t len = vencode64(x, 0, buf); + return string(buf, len); +} + +// Encodes a varint that is |extra| bytes longer than it needs to be, but still +// valid. +string longvarint(uint64_t x, int extra) { + char buf[VARINT_MAX_LEN]; + size_t len = vencode64(x, extra, buf); + return string(buf, len); +} + +// TODO: proper byte-swapping for big-endian machines. +string fixed32(void *data) { return string(static_cast<char*>(data), 4); } +string fixed64(void *data) { return string(static_cast<char*>(data), 8); } + +string delim(const string& buf) { return cat(varint(buf.size()), buf); } +string u32(uint32_t u32) { return fixed32(&u32); } +string u64(uint64_t u64) { return fixed64(&u64); } +string flt(float f) { return fixed32(&f); } +string dbl(double d) { return fixed64(&d); } +string zz32(int32_t x) { return varint(WireFormatLite::ZigZagEncode32(x)); } +string zz64(int64_t x) { return varint(WireFormatLite::ZigZagEncode64(x)); } + +string tag(uint32_t fieldnum, char wire_type) { + return varint((fieldnum << 3) | wire_type); +} + +string submsg(uint32_t fn, const string& buf) { + return cat( tag(fn, WireFormatLite::WIRETYPE_LENGTH_DELIMITED), delim(buf) ); +} + +#define UNKNOWN_FIELD 666 + +const FieldDescriptor* GetFieldForType(FieldDescriptor::Type type, + bool repeated, bool is_proto3) { + + const Descriptor* d = is_proto3 ? + TestAllTypesProto3().GetDescriptor() : TestAllTypesProto2().GetDescriptor(); + for (int i = 0; i < d->field_count(); i++) { + const FieldDescriptor* f = d->field(i); + if (f->type() == type && f->is_repeated() == repeated) { + return f; + } + } + GOOGLE_LOG(FATAL) << "Couldn't find field with type " << (int)type; + return nullptr; +} + +string UpperCase(string str) { + for (int i = 0; i < str.size(); i++) { + str[i] = toupper(str[i]); + } + return str; +} + +std::unique_ptr<Message> NewTestMessage(bool is_proto3) { + std::unique_ptr<Message> prototype; + if (is_proto3) { + prototype.reset(new TestAllTypesProto3()); + } else { + prototype.reset(new TestAllTypesProto2()); + } + return prototype; +} + +} // anonymous namespace + +namespace google { +namespace protobuf { + +class ConformanceTestSuiteImpl : public ConformanceTestSuite { + public: + ConformanceTestSuiteImpl() {} + + private: + void RunSuiteImpl(); + void RunValidJsonTest(const string& test_name, + ConformanceLevel level, + const string& input_json, + const string& equivalent_text_format); + void RunValidJsonTestWithProtobufInput( + const string& test_name, + ConformanceLevel level, + const protobuf_test_messages::proto3::TestAllTypesProto3& input, + const string& equivalent_text_format); + void RunValidJsonIgnoreUnknownTest( + const string& test_name, ConformanceLevel level, const string& input_json, + const string& equivalent_text_format); + void RunValidProtobufTest(const string& test_name, ConformanceLevel level, + const string& input_protobuf, + const string& equivalent_text_format, + bool is_proto3); + void RunValidBinaryProtobufTest(const string& test_name, + ConformanceLevel level, + const string& input_protobuf, + bool is_proto3); + void RunValidProtobufTestWithMessage( + const string& test_name, ConformanceLevel level, + const Message *input, + const string& equivalent_text_format, + bool is_proto3); + + typedef std::function<bool(const Json::Value&)> Validator; + void RunValidJsonTestWithValidator(const string& test_name, + ConformanceLevel level, + const string& input_json, + const Validator& validator); + void ExpectParseFailureForJson(const string& test_name, + ConformanceLevel level, + const string& input_json); + void ExpectSerializeFailureForJson(const string& test_name, + ConformanceLevel level, + const string& text_format); + void ExpectParseFailureForProtoWithProtoVersion (const string& proto, + const string& test_name, + ConformanceLevel level, + bool is_proto3); + void ExpectParseFailureForProto(const std::string& proto, + const std::string& test_name, + ConformanceLevel level); + void ExpectHardParseFailureForProto(const std::string& proto, + const std::string& test_name, + ConformanceLevel level); + void TestPrematureEOFForType(google::protobuf::FieldDescriptor::Type type); + void TestIllegalTags(); + template <class MessageType> + void TestOneofMessage (MessageType &message, + bool is_proto3); + template <class MessageType> + void TestUnknownMessage (MessageType &message, + bool is_proto3); + void TestValidDataForType( + google::protobuf::FieldDescriptor::Type, + std::vector<std::pair<std::string, std::string>> values); +}; + +void ConformanceTestSuiteImpl::ExpectParseFailureForProtoWithProtoVersion ( + const string& proto, const string& test_name, ConformanceLevel level, + bool is_proto3) { + std::unique_ptr<Message> prototype = NewTestMessage(is_proto3); + // We don't expect output, but if the program erroneously accepts the protobuf + // we let it send its response as this. We must not leave it unspecified. + ConformanceRequestSetting setting( + level, conformance::PROTOBUF, conformance::PROTOBUF, + conformance::BINARY_TEST, + *prototype, test_name, proto); + + const ConformanceRequest& request = setting.GetRequest(); + ConformanceResponse response; + string effective_test_name = + StrCat(setting.ConformanceLevelToString(level), + (is_proto3 ? ".Proto3" : ".Proto2"), + ".ProtobufInput.", test_name); + + RunTest(effective_test_name, request, &response); + if (response.result_case() == ConformanceResponse::kParseError) { + ReportSuccess(effective_test_name); + } else if (response.result_case() == ConformanceResponse::kSkipped) { + ReportSkip(effective_test_name, request, response); + } else { + ReportFailure(effective_test_name, level, request, response, + "Should have failed to parse, but didn't."); + } +} + +// Expect that this precise protobuf will cause a parse error. +void ConformanceTestSuiteImpl::ExpectParseFailureForProto( + const string& proto, const string& test_name, ConformanceLevel level) { + ExpectParseFailureForProtoWithProtoVersion(proto, test_name, level, true); + ExpectParseFailureForProtoWithProtoVersion(proto, test_name, level, false); +} + +// Expect that this protobuf will cause a parse error, even if it is followed +// by valid protobuf data. We can try running this twice: once with this +// data verbatim and once with this data followed by some valid data. +// +// TODO(haberman): implement the second of these. +void ConformanceTestSuiteImpl::ExpectHardParseFailureForProto( + const string& proto, const string& test_name, ConformanceLevel level) { + return ExpectParseFailureForProto(proto, test_name, level); +} + +void ConformanceTestSuiteImpl::RunValidJsonTest( + const string& test_name, ConformanceLevel level, const string& input_json, + const string& equivalent_text_format) { + TestAllTypesProto3 prototype; + ConformanceRequestSetting setting1( + level, conformance::JSON, conformance::PROTOBUF, + conformance::JSON_TEST, + prototype, test_name, input_json); + RunValidInputTest(setting1, equivalent_text_format); + ConformanceRequestSetting setting2( + level, conformance::JSON, conformance::JSON, + conformance::JSON_TEST, + prototype, test_name, input_json); + RunValidInputTest(setting2, equivalent_text_format); +} + +void ConformanceTestSuiteImpl::RunValidJsonTestWithProtobufInput( + const string& test_name, ConformanceLevel level, const TestAllTypesProto3& input, + const string& equivalent_text_format) { + ConformanceRequestSetting setting( + level, conformance::PROTOBUF, conformance::JSON, + conformance::JSON_TEST, + input, test_name, input.SerializeAsString()); + RunValidInputTest(setting, equivalent_text_format); +} + +void ConformanceTestSuiteImpl::RunValidJsonIgnoreUnknownTest( + const string& test_name, ConformanceLevel level, const string& input_json, + const string& equivalent_text_format) { + TestAllTypesProto3 prototype; + ConformanceRequestSetting setting( + level, conformance::JSON, conformance::PROTOBUF, + conformance::JSON_IGNORE_UNKNOWN_PARSING_TEST, + prototype, test_name, input_json); + RunValidInputTest(setting, equivalent_text_format); +} + +void ConformanceTestSuiteImpl::RunValidProtobufTest( + const string& test_name, ConformanceLevel level, + const string& input_protobuf, const string& equivalent_text_format, + bool is_proto3) { + std::unique_ptr<Message> prototype = NewTestMessage(is_proto3); + + ConformanceRequestSetting setting1( + level, conformance::PROTOBUF, conformance::PROTOBUF, + conformance::BINARY_TEST, + *prototype, test_name, input_protobuf); + RunValidInputTest(setting1, equivalent_text_format); + + if (is_proto3) { + ConformanceRequestSetting setting2( + level, conformance::PROTOBUF, conformance::JSON, + conformance::BINARY_TEST, + *prototype, test_name, input_protobuf); + RunValidInputTest(setting2, equivalent_text_format); + } +} + +void ConformanceTestSuiteImpl::RunValidBinaryProtobufTest( + const string& test_name, ConformanceLevel level, + const string& input_protobuf, bool is_proto3) { + std::unique_ptr<Message> prototype = NewTestMessage(is_proto3); + ConformanceRequestSetting setting( + level, conformance::PROTOBUF, conformance::PROTOBUF, + conformance::BINARY_TEST, + *prototype, test_name, input_protobuf); + RunValidBinaryInputTest(setting, input_protobuf); +} + +void ConformanceTestSuiteImpl::RunValidProtobufTestWithMessage( + const string& test_name, ConformanceLevel level, const Message *input, + const string& equivalent_text_format, bool is_proto3) { + RunValidProtobufTest(test_name, level, input->SerializeAsString(), + equivalent_text_format, is_proto3); +} + +// According to proto3 JSON specification, JSON serializers follow more strict +// rules than parsers (e.g., a serializer must serialize int32 values as JSON +// numbers while the parser is allowed to accept them as JSON strings). This +// method allows strict checking on a proto3 JSON serializer by inspecting +// the JSON output directly. +void ConformanceTestSuiteImpl::RunValidJsonTestWithValidator( + const string& test_name, ConformanceLevel level, const string& input_json, + const Validator& validator) { + TestAllTypesProto3 prototype; + ConformanceRequestSetting setting( + level, conformance::JSON, conformance::JSON, + conformance::JSON_TEST, + prototype, test_name, input_json); + const ConformanceRequest& request = setting.GetRequest(); + ConformanceResponse response; + string effective_test_name = + StrCat(setting.ConformanceLevelToString(level), + ".Proto3.JsonInput.", + test_name, ".Validator"); + + RunTest(effective_test_name, request, &response); + + if (response.result_case() == ConformanceResponse::kSkipped) { + ReportSkip(effective_test_name, request, response); + return; + } + + if (response.result_case() != ConformanceResponse::kJsonPayload) { + ReportFailure(effective_test_name, level, request, response, + "Expected JSON payload but got type %d.", + response.result_case()); + return; + } + Json::Reader reader; + Json::Value value; + if (!reader.parse(response.json_payload(), value)) { + ReportFailure(effective_test_name, level, request, response, + "JSON payload cannot be parsed as valid JSON: %s", + reader.getFormattedErrorMessages().c_str()); + return; + } + if (!validator(value)) { + ReportFailure(effective_test_name, level, request, response, + "JSON payload validation failed."); + return; + } + ReportSuccess(effective_test_name); +} + +void ConformanceTestSuiteImpl::ExpectParseFailureForJson( + const string& test_name, ConformanceLevel level, const string& input_json) { + TestAllTypesProto3 prototype; + // We don't expect output, but if the program erroneously accepts the protobuf + // we let it send its response as this. We must not leave it unspecified. + ConformanceRequestSetting setting( + level, conformance::JSON, conformance::JSON, + conformance::JSON_TEST, + prototype, test_name, input_json); + const ConformanceRequest& request = setting.GetRequest(); + ConformanceResponse response; + string effective_test_name = + StrCat(setting.ConformanceLevelToString(level), + ".Proto3.JsonInput.", test_name); + + RunTest(effective_test_name, request, &response); + if (response.result_case() == ConformanceResponse::kParseError) { + ReportSuccess(effective_test_name); + } else if (response.result_case() == ConformanceResponse::kSkipped) { + ReportSkip(effective_test_name, request, response); + } else { + ReportFailure(effective_test_name, level, request, response, + "Should have failed to parse, but didn't."); + } +} + +void ConformanceTestSuiteImpl::ExpectSerializeFailureForJson( + const string& test_name, ConformanceLevel level, const string& text_format) { + TestAllTypesProto3 payload_message; + GOOGLE_CHECK( + TextFormat::ParseFromString(text_format, &payload_message)) + << "Failed to parse: " << text_format; + + TestAllTypesProto3 prototype; + ConformanceRequestSetting setting( + level, conformance::PROTOBUF, conformance::JSON, + conformance::JSON_TEST, + prototype, test_name, payload_message.SerializeAsString()); + const ConformanceRequest& request = setting.GetRequest(); + ConformanceResponse response; + string effective_test_name = + StrCat(setting.ConformanceLevelToString(level), + ".", test_name, ".JsonOutput"); + + RunTest(effective_test_name, request, &response); + if (response.result_case() == ConformanceResponse::kSerializeError) { + ReportSuccess(effective_test_name); + } else if (response.result_case() == ConformanceResponse::kSkipped) { + ReportSkip(effective_test_name, request, response); + } else { + ReportFailure(effective_test_name, level, request, response, + "Should have failed to serialize, but didn't."); + } +} + +//TODO: proto2? +void ConformanceTestSuiteImpl::TestPrematureEOFForType( + FieldDescriptor::Type type) { + // Incomplete values for each wire type. + static const string incompletes[6] = { + string("\x80"), // VARINT + string("abcdefg"), // 64BIT + string("\x80"), // DELIMITED (partial length) + string(), // START_GROUP (no value required) + string(), // END_GROUP (no value required) + string("abc") // 32BIT + }; + + const FieldDescriptor* field = GetFieldForType(type, false, true); + const FieldDescriptor* rep_field = GetFieldForType(type, true, true); + WireFormatLite::WireType wire_type = WireFormatLite::WireTypeForFieldType( + static_cast<WireFormatLite::FieldType>(type)); + const string& incomplete = incompletes[wire_type]; + const string type_name = + UpperCase(string(".") + FieldDescriptor::TypeName(type)); + + ExpectParseFailureForProto( + tag(field->number(), wire_type), + "PrematureEofBeforeKnownNonRepeatedValue" + type_name, REQUIRED); + + ExpectParseFailureForProto( + tag(rep_field->number(), wire_type), + "PrematureEofBeforeKnownRepeatedValue" + type_name, REQUIRED); + + ExpectParseFailureForProto( + tag(UNKNOWN_FIELD, wire_type), + "PrematureEofBeforeUnknownValue" + type_name, REQUIRED); + + ExpectParseFailureForProto( + cat( tag(field->number(), wire_type), incomplete ), + "PrematureEofInsideKnownNonRepeatedValue" + type_name, REQUIRED); + + ExpectParseFailureForProto( + cat( tag(rep_field->number(), wire_type), incomplete ), + "PrematureEofInsideKnownRepeatedValue" + type_name, REQUIRED); + + ExpectParseFailureForProto( + cat( tag(UNKNOWN_FIELD, wire_type), incomplete ), + "PrematureEofInsideUnknownValue" + type_name, REQUIRED); + + if (wire_type == WireFormatLite::WIRETYPE_LENGTH_DELIMITED) { + ExpectParseFailureForProto( + cat( tag(field->number(), wire_type), varint(1) ), + "PrematureEofInDelimitedDataForKnownNonRepeatedValue" + type_name, + REQUIRED); + + ExpectParseFailureForProto( + cat( tag(rep_field->number(), wire_type), varint(1) ), + "PrematureEofInDelimitedDataForKnownRepeatedValue" + type_name, + REQUIRED); + + // EOF in the middle of delimited data for unknown value. + ExpectParseFailureForProto( + cat( tag(UNKNOWN_FIELD, wire_type), varint(1) ), + "PrematureEofInDelimitedDataForUnknownValue" + type_name, REQUIRED); + + if (type == FieldDescriptor::TYPE_MESSAGE) { + // Submessage ends in the middle of a value. + string incomplete_submsg = + cat( tag(WireFormatLite::TYPE_INT32, WireFormatLite::WIRETYPE_VARINT), + incompletes[WireFormatLite::WIRETYPE_VARINT] ); + ExpectHardParseFailureForProto( + cat( tag(field->number(), WireFormatLite::WIRETYPE_LENGTH_DELIMITED), + varint(incomplete_submsg.size()), + incomplete_submsg ), + "PrematureEofInSubmessageValue" + type_name, REQUIRED); + } + } else if (type != FieldDescriptor::TYPE_GROUP) { + // Non-delimited, non-group: eligible for packing. + + // Packed region ends in the middle of a value. + ExpectHardParseFailureForProto( + cat(tag(rep_field->number(), WireFormatLite::WIRETYPE_LENGTH_DELIMITED), + varint(incomplete.size()), incomplete), + "PrematureEofInPackedFieldValue" + type_name, REQUIRED); + + // EOF in the middle of packed region. + ExpectParseFailureForProto( + cat(tag(rep_field->number(), WireFormatLite::WIRETYPE_LENGTH_DELIMITED), + varint(1)), + "PrematureEofInPackedField" + type_name, REQUIRED); + } +} + +void ConformanceTestSuiteImpl::TestValidDataForType( + FieldDescriptor::Type type, + std::vector<std::pair<std::string, std::string>> values) { + for (int is_proto3 = 0; is_proto3 < 2; is_proto3++) { + const string type_name = + UpperCase(string(".") + FieldDescriptor::TypeName(type)); + WireFormatLite::WireType wire_type = WireFormatLite::WireTypeForFieldType( + static_cast<WireFormatLite::FieldType>(type)); + const FieldDescriptor* field = GetFieldForType(type, false, is_proto3); + const FieldDescriptor* rep_field = GetFieldForType(type, true, is_proto3); + + RunValidProtobufTest("ValidDataScalar" + type_name, REQUIRED, + cat(tag(field->number(), wire_type), values[0].first), + field->name() + ": " + values[0].second, is_proto3); + + string proto; + string text = field->name() + ": " + values.back().second; + for (size_t i = 0; i < values.size(); i++) { + proto += cat(tag(field->number(), wire_type), values[i].first); + } + RunValidProtobufTest("RepeatedScalarSelectsLast" + type_name, REQUIRED, + proto, text, is_proto3); + + proto.clear(); + text.clear(); + + for (size_t i = 0; i < values.size(); i++) { + proto += cat(tag(rep_field->number(), wire_type), values[i].first); + text += rep_field->name() + ": " + values[i].second + " "; + } + RunValidProtobufTest("ValidDataRepeated" + type_name, REQUIRED, + proto, text, is_proto3); + } +} + +// TODO: proto2? +void ConformanceTestSuiteImpl::TestIllegalTags() { + // field num 0 is illegal + string nullfield[] = { + "\1DEADBEEF", + "\2\1\1", + "\3\4", + "\5DEAD" + }; + for (int i = 0; i < 4; i++) { + string name = "IllegalZeroFieldNum_Case_0"; + name.back() += i; + ExpectParseFailureForProto(nullfield[i], name, REQUIRED); + } +} +template <class MessageType> +void ConformanceTestSuiteImpl::TestOneofMessage ( + MessageType &message, bool is_proto3) { + message.set_oneof_uint32(0); + RunValidProtobufTestWithMessage( + "OneofZeroUint32", RECOMMENDED, &message, "oneof_uint32: 0", is_proto3); + message.mutable_oneof_nested_message()->set_a(0); + RunValidProtobufTestWithMessage( + "OneofZeroMessage", RECOMMENDED, &message, + is_proto3 ? "oneof_nested_message: {}" : "oneof_nested_message: {a: 0}", + is_proto3); + message.mutable_oneof_nested_message()->set_a(1); + RunValidProtobufTestWithMessage( + "OneofZeroMessageSetTwice", RECOMMENDED, &message, + "oneof_nested_message: {a: 1}", + is_proto3); + message.set_oneof_string(""); + RunValidProtobufTestWithMessage( + "OneofZeroString", RECOMMENDED, &message, "oneof_string: \"\"", is_proto3); + message.set_oneof_bytes(""); + RunValidProtobufTestWithMessage( + "OneofZeroBytes", RECOMMENDED, &message, "oneof_bytes: \"\"", is_proto3); + message.set_oneof_bool(false); + RunValidProtobufTestWithMessage( + "OneofZeroBool", RECOMMENDED, &message, "oneof_bool: false", is_proto3); + message.set_oneof_uint64(0); + RunValidProtobufTestWithMessage( + "OneofZeroUint64", RECOMMENDED, &message, "oneof_uint64: 0", is_proto3); + message.set_oneof_float(0.0f); + RunValidProtobufTestWithMessage( + "OneofZeroFloat", RECOMMENDED, &message, "oneof_float: 0", is_proto3); + message.set_oneof_double(0.0); + RunValidProtobufTestWithMessage( + "OneofZeroDouble", RECOMMENDED, &message, "oneof_double: 0", is_proto3); + message.set_oneof_enum(MessageType::FOO); + RunValidProtobufTestWithMessage( + "OneofZeroEnum", RECOMMENDED, &message, "oneof_enum: FOO", is_proto3); +} + +template <class MessageType> +void ConformanceTestSuiteImpl::TestUnknownMessage( + MessageType& message, bool is_proto3) { + message.ParseFromString("\xA8\x1F\x01"); + RunValidBinaryProtobufTest("UnknownVarint", REQUIRED, + message.SerializeAsString(), is_proto3); +} + +void ConformanceTestSuiteImpl::RunSuiteImpl() { + type_resolver_.reset(NewTypeResolverForDescriptorPool( + kTypeUrlPrefix, DescriptorPool::generated_pool())); + type_url_ = GetTypeUrl(TestAllTypesProto3::descriptor()); + + for (int i = 1; i <= FieldDescriptor::MAX_TYPE; i++) { + if (i == FieldDescriptor::TYPE_GROUP) continue; + TestPrematureEOFForType(static_cast<FieldDescriptor::Type>(i)); + } + + TestIllegalTags(); + + int64 kInt64Min = -9223372036854775808ULL; + int64 kInt64Max = 9223372036854775807ULL; + uint64 kUint64Max = 18446744073709551615ULL; + int32 kInt32Max = 2147483647; + int32 kInt32Min = -2147483648; + uint32 kUint32Max = 4294967295UL; + + TestValidDataForType(FieldDescriptor::TYPE_DOUBLE, { + {dbl(0.1), "0.1"}, + {dbl(1.7976931348623157e+308), "1.7976931348623157e+308"}, + {dbl(2.22507385850720138309e-308), "2.22507385850720138309e-308"} + }); + TestValidDataForType(FieldDescriptor::TYPE_FLOAT, { + {flt(0.1), "0.1"}, + {flt(1.00000075e-36), "1.00000075e-36"}, + {flt(3.402823e+38), "3.402823e+38"}, // 3.40282347e+38 + {flt(1.17549435e-38f), "1.17549435e-38"} + }); + TestValidDataForType(FieldDescriptor::TYPE_INT64, { + {varint(12345), "12345"}, + {varint(kInt64Max), std::to_string(kInt64Max)}, + {varint(kInt64Min), std::to_string(kInt64Min)} + }); + TestValidDataForType(FieldDescriptor::TYPE_UINT64, { + {varint(12345), "12345"}, + {varint(kUint64Max), std::to_string(kUint64Max)}, + {varint(0), "0"} + }); + TestValidDataForType(FieldDescriptor::TYPE_INT32, { + {varint(12345), "12345"}, + {longvarint(12345, 2), "12345"}, + {longvarint(12345, 7), "12345"}, + {varint(kInt32Max), std::to_string(kInt32Max)}, + {varint(kInt32Min), std::to_string(kInt32Min)}, + {varint(1LL << 33), std::to_string(static_cast<int32>(1LL << 33))}, + {varint((1LL << 33) - 1), + std::to_string(static_cast<int32>((1LL << 33) - 1))}, + }); + TestValidDataForType(FieldDescriptor::TYPE_UINT32, { + {varint(12345), "12345"}, + {longvarint(12345, 2), "12345"}, + {longvarint(12345, 7), "12345"}, + {varint(kUint32Max), std::to_string(kUint32Max)}, // UINT32_MAX + {varint(0), "0"}, + {varint(1LL << 33), std::to_string(static_cast<uint32>(1LL << 33))}, + {varint((1LL << 33) - 1), + std::to_string(static_cast<uint32>((1LL << 33) - 1))}, + }); + TestValidDataForType(FieldDescriptor::TYPE_FIXED64, { + {u64(12345), "12345"}, + {u64(kUint64Max), std::to_string(kUint64Max)}, + {u64(0), "0"} + }); + TestValidDataForType(FieldDescriptor::TYPE_FIXED32, { + {u32(12345), "12345"}, + {u32(kUint32Max), std::to_string(kUint32Max)}, // UINT32_MAX + {u32(0), "0"} + }); + TestValidDataForType(FieldDescriptor::TYPE_SFIXED64, { + {u64(12345), "12345"}, + {u64(kInt64Max), std::to_string(kInt64Max)}, + {u64(kInt64Min), std::to_string(kInt64Min)} + }); + TestValidDataForType(FieldDescriptor::TYPE_SFIXED32, { + {u32(12345), "12345"}, + {u32(kInt32Max), std::to_string(kInt32Max)}, + {u32(kInt32Min), std::to_string(kInt32Min)} + }); + TestValidDataForType(FieldDescriptor::TYPE_BOOL, { + {varint(1), "true"}, + {varint(0), "false"}, + {varint(12345678), "true"} + }); + TestValidDataForType(FieldDescriptor::TYPE_SINT32, { + {zz32(12345), "12345"}, + {zz32(kInt32Max), std::to_string(kInt32Max)}, + {zz32(kInt32Min), std::to_string(kInt32Min)} + }); + TestValidDataForType(FieldDescriptor::TYPE_SINT64, { + {zz64(12345), "12345"}, + {zz64(kInt64Max), std::to_string(kInt64Max)}, + {zz64(kInt64Min), std::to_string(kInt64Min)} + }); + + // TODO(haberman): + // TestValidDataForType(FieldDescriptor::TYPE_STRING + // TestValidDataForType(FieldDescriptor::TYPE_GROUP + // TestValidDataForType(FieldDescriptor::TYPE_MESSAGE + // TestValidDataForType(FieldDescriptor::TYPE_BYTES + // TestValidDataForType(FieldDescriptor::TYPE_ENUM + + RunValidJsonTest("HelloWorld", REQUIRED, + "{\"optionalString\":\"Hello, World!\"}", + "optional_string: 'Hello, World!'"); + + // NOTE: The spec for JSON support is still being sorted out, these may not + // all be correct. + // Test field name conventions. + RunValidJsonTest( + "FieldNameInSnakeCase", REQUIRED, + R"({ + "fieldname1": 1, + "fieldName2": 2, + "FieldName3": 3, + "fieldName4": 4 + })", + R"( + fieldname1: 1 + field_name2: 2 + _field_name3: 3 + field__name4_: 4 + )"); + RunValidJsonTest( + "FieldNameWithNumbers", REQUIRED, + R"({ + "field0name5": 5, + "field0Name6": 6 + })", + R"( + field0name5: 5 + field_0_name6: 6 + )"); + RunValidJsonTest( + "FieldNameWithMixedCases", REQUIRED, + R"({ + "fieldName7": 7, + "FieldName8": 8, + "fieldName9": 9, + "FieldName10": 10, + "FIELDNAME11": 11, + "FIELDName12": 12 + })", + R"( + fieldName7: 7 + FieldName8: 8 + field_Name9: 9 + Field_Name10: 10 + FIELD_NAME11: 11 + FIELD_name12: 12 + )"); + RunValidJsonTest( + "FieldNameWithDoubleUnderscores", RECOMMENDED, + R"({ + "FieldName13": 13, + "FieldName14": 14, + "fieldName15": 15, + "fieldName16": 16, + "fieldName17": 17, + "FieldName18": 18 + })", + R"( + __field_name13: 13 + __Field_name14: 14 + field__name15: 15 + field__Name16: 16 + field_name17__: 17 + Field_name18__: 18 + )"); + // Using the original proto field name in JSON is also allowed. + RunValidJsonTest( + "OriginalProtoFieldName", REQUIRED, + R"({ + "fieldname1": 1, + "field_name2": 2, + "_field_name3": 3, + "field__name4_": 4, + "field0name5": 5, + "field_0_name6": 6, + "fieldName7": 7, + "FieldName8": 8, + "field_Name9": 9, + "Field_Name10": 10, + "FIELD_NAME11": 11, + "FIELD_name12": 12, + "__field_name13": 13, + "__Field_name14": 14, + "field__name15": 15, + "field__Name16": 16, + "field_name17__": 17, + "Field_name18__": 18 + })", + R"( + fieldname1: 1 + field_name2: 2 + _field_name3: 3 + field__name4_: 4 + field0name5: 5 + field_0_name6: 6 + fieldName7: 7 + FieldName8: 8 + field_Name9: 9 + Field_Name10: 10 + FIELD_NAME11: 11 + FIELD_name12: 12 + __field_name13: 13 + __Field_name14: 14 + field__name15: 15 + field__Name16: 16 + field_name17__: 17 + Field_name18__: 18 + )"); + // Field names can be escaped. + RunValidJsonTest( + "FieldNameEscaped", REQUIRED, + R"({"fieldn\u0061me1": 1})", + "fieldname1: 1"); + // String ends with escape character. + ExpectParseFailureForJson( + "StringEndsWithEscapeChar", RECOMMENDED, + "{\"optionalString\": \"abc\\"); + // Field names must be quoted (or it's not valid JSON). + ExpectParseFailureForJson( + "FieldNameNotQuoted", RECOMMENDED, + "{fieldname1: 1}"); + // Trailing comma is not allowed (not valid JSON). + ExpectParseFailureForJson( + "TrailingCommaInAnObject", RECOMMENDED, + R"({"fieldname1":1,})"); + ExpectParseFailureForJson( + "TrailingCommaInAnObjectWithSpace", RECOMMENDED, + R"({"fieldname1":1 ,})"); + ExpectParseFailureForJson( + "TrailingCommaInAnObjectWithSpaceCommaSpace", RECOMMENDED, + R"({"fieldname1":1 , })"); + ExpectParseFailureForJson( + "TrailingCommaInAnObjectWithNewlines", RECOMMENDED, + R"({ + "fieldname1":1, + })"); + // JSON doesn't support comments. + ExpectParseFailureForJson( + "JsonWithComments", RECOMMENDED, + R"({ + // This is a comment. + "fieldname1": 1 + })"); + // JSON spec says whitespace doesn't matter, so try a few spacings to be sure. + RunValidJsonTest( + "OneLineNoSpaces", RECOMMENDED, + "{\"optionalInt32\":1,\"optionalInt64\":2}", + R"( + optional_int32: 1 + optional_int64: 2 + )"); + RunValidJsonTest( + "OneLineWithSpaces", RECOMMENDED, + "{ \"optionalInt32\" : 1 , \"optionalInt64\" : 2 }", + R"( + optional_int32: 1 + optional_int64: 2 + )"); + RunValidJsonTest( + "MultilineNoSpaces", RECOMMENDED, + "{\n\"optionalInt32\"\n:\n1\n,\n\"optionalInt64\"\n:\n2\n}", + R"( + optional_int32: 1 + optional_int64: 2 + )"); + RunValidJsonTest( + "MultilineWithSpaces", RECOMMENDED, + "{\n \"optionalInt32\" : 1\n ,\n \"optionalInt64\" : 2\n}\n", + R"( + optional_int32: 1 + optional_int64: 2 + )"); + // Missing comma between key/value pairs. + ExpectParseFailureForJson( + "MissingCommaOneLine", RECOMMENDED, + "{ \"optionalInt32\": 1 \"optionalInt64\": 2 }"); + ExpectParseFailureForJson( + "MissingCommaMultiline", RECOMMENDED, + "{\n \"optionalInt32\": 1\n \"optionalInt64\": 2\n}"); + // Duplicated field names are not allowed. + ExpectParseFailureForJson( + "FieldNameDuplicate", RECOMMENDED, + R"({ + "optionalNestedMessage": {a: 1}, + "optionalNestedMessage": {} + })"); + ExpectParseFailureForJson( + "FieldNameDuplicateDifferentCasing1", RECOMMENDED, + R"({ + "optional_nested_message": {a: 1}, + "optionalNestedMessage": {} + })"); + ExpectParseFailureForJson( + "FieldNameDuplicateDifferentCasing2", RECOMMENDED, + R"({ + "optionalNestedMessage": {a: 1}, + "optional_nested_message": {} + })"); + // Serializers should use lowerCamelCase by default. + RunValidJsonTestWithValidator( + "FieldNameInLowerCamelCase", REQUIRED, + R"({ + "fieldname1": 1, + "fieldName2": 2, + "FieldName3": 3, + "fieldName4": 4 + })", + [](const Json::Value& value) { + return value.isMember("fieldname1") && + value.isMember("fieldName2") && + value.isMember("FieldName3") && + value.isMember("fieldName4"); + }); + RunValidJsonTestWithValidator( + "FieldNameWithNumbers", REQUIRED, + R"({ + "field0name5": 5, + "field0Name6": 6 + })", + [](const Json::Value& value) { + return value.isMember("field0name5") && + value.isMember("field0Name6"); + }); + RunValidJsonTestWithValidator( + "FieldNameWithMixedCases", REQUIRED, + R"({ + "fieldName7": 7, + "FieldName8": 8, + "fieldName9": 9, + "FieldName10": 10, + "FIELDNAME11": 11, + "FIELDName12": 12 + })", + [](const Json::Value& value) { + return value.isMember("fieldName7") && + value.isMember("FieldName8") && + value.isMember("fieldName9") && + value.isMember("FieldName10") && + value.isMember("FIELDNAME11") && + value.isMember("FIELDName12"); + }); + RunValidJsonTestWithValidator( + "FieldNameWithDoubleUnderscores", RECOMMENDED, + R"({ + "FieldName13": 13, + "FieldName14": 14, + "fieldName15": 15, + "fieldName16": 16, + "fieldName17": 17, + "FieldName18": 18 + })", + [](const Json::Value& value) { + return value.isMember("FieldName13") && + value.isMember("FieldName14") && + value.isMember("fieldName15") && + value.isMember("fieldName16") && + value.isMember("fieldName17") && + value.isMember("FieldName18"); + }); + + // Integer fields. + RunValidJsonTest( + "Int32FieldMaxValue", REQUIRED, + R"({"optionalInt32": 2147483647})", + "optional_int32: 2147483647"); + RunValidJsonTest( + "Int32FieldMinValue", REQUIRED, + R"({"optionalInt32": -2147483648})", + "optional_int32: -2147483648"); + RunValidJsonTest( + "Uint32FieldMaxValue", REQUIRED, + R"({"optionalUint32": 4294967295})", + "optional_uint32: 4294967295"); + RunValidJsonTest( + "Int64FieldMaxValue", REQUIRED, + R"({"optionalInt64": "9223372036854775807"})", + "optional_int64: 9223372036854775807"); + RunValidJsonTest( + "Int64FieldMinValue", REQUIRED, + R"({"optionalInt64": "-9223372036854775808"})", + "optional_int64: -9223372036854775808"); + RunValidJsonTest( + "Uint64FieldMaxValue", REQUIRED, + R"({"optionalUint64": "18446744073709551615"})", + "optional_uint64: 18446744073709551615"); + // While not the largest Int64, this is the largest + // Int64 which can be exactly represented within an + // IEEE-754 64-bit float, which is the expected level + // of interoperability guarantee. Larger values may + // work in some implementations, but should not be + // relied upon. + RunValidJsonTest( + "Int64FieldMaxValueNotQuoted", REQUIRED, + R"({"optionalInt64": 9223372036854774784})", + "optional_int64: 9223372036854774784"); + RunValidJsonTest( + "Int64FieldMinValueNotQuoted", REQUIRED, + R"({"optionalInt64": -9223372036854775808})", + "optional_int64: -9223372036854775808"); + // Largest interoperable Uint64; see comment above + // for Int64FieldMaxValueNotQuoted. + RunValidJsonTest( + "Uint64FieldMaxValueNotQuoted", REQUIRED, + R"({"optionalUint64": 18446744073709549568})", + "optional_uint64: 18446744073709549568"); + // Values can be represented as JSON strings. + RunValidJsonTest( + "Int32FieldStringValue", REQUIRED, + R"({"optionalInt32": "2147483647"})", + "optional_int32: 2147483647"); + RunValidJsonTest( + "Int32FieldStringValueEscaped", REQUIRED, + R"({"optionalInt32": "2\u003147483647"})", + "optional_int32: 2147483647"); + + // Parsers reject out-of-bound integer values. + ExpectParseFailureForJson( + "Int32FieldTooLarge", REQUIRED, + R"({"optionalInt32": 2147483648})"); + ExpectParseFailureForJson( + "Int32FieldTooSmall", REQUIRED, + R"({"optionalInt32": -2147483649})"); + ExpectParseFailureForJson( + "Uint32FieldTooLarge", REQUIRED, + R"({"optionalUint32": 4294967296})"); + ExpectParseFailureForJson( + "Int64FieldTooLarge", REQUIRED, + R"({"optionalInt64": "9223372036854775808"})"); + ExpectParseFailureForJson( + "Int64FieldTooSmall", REQUIRED, + R"({"optionalInt64": "-9223372036854775809"})"); + ExpectParseFailureForJson( + "Uint64FieldTooLarge", REQUIRED, + R"({"optionalUint64": "18446744073709551616"})"); + // Parser reject non-integer numeric values as well. + ExpectParseFailureForJson( + "Int32FieldNotInteger", REQUIRED, + R"({"optionalInt32": 0.5})"); + ExpectParseFailureForJson( + "Uint32FieldNotInteger", REQUIRED, + R"({"optionalUint32": 0.5})"); + ExpectParseFailureForJson( + "Int64FieldNotInteger", REQUIRED, + R"({"optionalInt64": "0.5"})"); + ExpectParseFailureForJson( + "Uint64FieldNotInteger", REQUIRED, + R"({"optionalUint64": "0.5"})"); + + // Integers but represented as float values are accepted. + RunValidJsonTest( + "Int32FieldFloatTrailingZero", REQUIRED, + R"({"optionalInt32": 100000.000})", + "optional_int32: 100000"); + RunValidJsonTest( + "Int32FieldExponentialFormat", REQUIRED, + R"({"optionalInt32": 1e5})", + "optional_int32: 100000"); + RunValidJsonTest( + "Int32FieldMaxFloatValue", REQUIRED, + R"({"optionalInt32": 2.147483647e9})", + "optional_int32: 2147483647"); + RunValidJsonTest( + "Int32FieldMinFloatValue", REQUIRED, + R"({"optionalInt32": -2.147483648e9})", + "optional_int32: -2147483648"); + RunValidJsonTest( + "Uint32FieldMaxFloatValue", REQUIRED, + R"({"optionalUint32": 4.294967295e9})", + "optional_uint32: 4294967295"); + + // Parser reject non-numeric values. + ExpectParseFailureForJson( + "Int32FieldNotNumber", REQUIRED, + R"({"optionalInt32": "3x3"})"); + ExpectParseFailureForJson( + "Uint32FieldNotNumber", REQUIRED, + R"({"optionalUint32": "3x3"})"); + ExpectParseFailureForJson( + "Int64FieldNotNumber", REQUIRED, + R"({"optionalInt64": "3x3"})"); + ExpectParseFailureForJson( + "Uint64FieldNotNumber", REQUIRED, + R"({"optionalUint64": "3x3"})"); + // JSON does not allow "+" on numric values. + ExpectParseFailureForJson( + "Int32FieldPlusSign", REQUIRED, + R"({"optionalInt32": +1})"); + // JSON doesn't allow leading 0s. + ExpectParseFailureForJson( + "Int32FieldLeadingZero", REQUIRED, + R"({"optionalInt32": 01})"); + ExpectParseFailureForJson( + "Int32FieldNegativeWithLeadingZero", REQUIRED, + R"({"optionalInt32": -01})"); + // String values must follow the same syntax rule. Specifically leading + // or trailing spaces are not allowed. + ExpectParseFailureForJson( + "Int32FieldLeadingSpace", REQUIRED, + R"({"optionalInt32": " 1"})"); + ExpectParseFailureForJson( + "Int32FieldTrailingSpace", REQUIRED, + R"({"optionalInt32": "1 "})"); + + // 64-bit values are serialized as strings. + RunValidJsonTestWithValidator( + "Int64FieldBeString", RECOMMENDED, + R"({"optionalInt64": 1})", + [](const Json::Value& value) { + return value["optionalInt64"].type() == Json::stringValue && + value["optionalInt64"].asString() == "1"; + }); + RunValidJsonTestWithValidator( + "Uint64FieldBeString", RECOMMENDED, + R"({"optionalUint64": 1})", + [](const Json::Value& value) { + return value["optionalUint64"].type() == Json::stringValue && + value["optionalUint64"].asString() == "1"; + }); + + // Bool fields. + RunValidJsonTest( + "BoolFieldTrue", REQUIRED, + R"({"optionalBool":true})", + "optional_bool: true"); + RunValidJsonTest( + "BoolFieldFalse", REQUIRED, + R"({"optionalBool":false})", + "optional_bool: false"); + + // Other forms are not allowed. + ExpectParseFailureForJson( + "BoolFieldIntegerZero", RECOMMENDED, + R"({"optionalBool":0})"); + ExpectParseFailureForJson( + "BoolFieldIntegerOne", RECOMMENDED, + R"({"optionalBool":1})"); + ExpectParseFailureForJson( + "BoolFieldCamelCaseTrue", RECOMMENDED, + R"({"optionalBool":True})"); + ExpectParseFailureForJson( + "BoolFieldCamelCaseFalse", RECOMMENDED, + R"({"optionalBool":False})"); + ExpectParseFailureForJson( + "BoolFieldAllCapitalTrue", RECOMMENDED, + R"({"optionalBool":TRUE})"); + ExpectParseFailureForJson( + "BoolFieldAllCapitalFalse", RECOMMENDED, + R"({"optionalBool":FALSE})"); + ExpectParseFailureForJson( + "BoolFieldDoubleQuotedTrue", RECOMMENDED, + R"({"optionalBool":"true"})"); + ExpectParseFailureForJson( + "BoolFieldDoubleQuotedFalse", RECOMMENDED, + R"({"optionalBool":"false"})"); + + // Float fields. + RunValidJsonTest( + "FloatFieldMinPositiveValue", REQUIRED, + R"({"optionalFloat": 1.175494e-38})", + "optional_float: 1.175494e-38"); + RunValidJsonTest( + "FloatFieldMaxNegativeValue", REQUIRED, + R"({"optionalFloat": -1.175494e-38})", + "optional_float: -1.175494e-38"); + RunValidJsonTest( + "FloatFieldMaxPositiveValue", REQUIRED, + R"({"optionalFloat": 3.402823e+38})", + "optional_float: 3.402823e+38"); + RunValidJsonTest( + "FloatFieldMinNegativeValue", REQUIRED, + R"({"optionalFloat": 3.402823e+38})", + "optional_float: 3.402823e+38"); + // Values can be quoted. + RunValidJsonTest( + "FloatFieldQuotedValue", REQUIRED, + R"({"optionalFloat": "1"})", + "optional_float: 1"); + // Special values. + RunValidJsonTest( + "FloatFieldNan", REQUIRED, + R"({"optionalFloat": "NaN"})", + "optional_float: nan"); + RunValidJsonTest( + "FloatFieldInfinity", REQUIRED, + R"({"optionalFloat": "Infinity"})", + "optional_float: inf"); + RunValidJsonTest( + "FloatFieldNegativeInfinity", REQUIRED, + R"({"optionalFloat": "-Infinity"})", + "optional_float: -inf"); + // Non-cannonical Nan will be correctly normalized. + { + TestAllTypesProto3 message; + // IEEE floating-point standard 32-bit quiet NaN: + // 0111 1111 1xxx xxxx xxxx xxxx xxxx xxxx + message.set_optional_float( + WireFormatLite::DecodeFloat(0x7FA12345)); + RunValidJsonTestWithProtobufInput( + "FloatFieldNormalizeQuietNan", REQUIRED, message, + "optional_float: nan"); + // IEEE floating-point standard 64-bit signaling NaN: + // 1111 1111 1xxx xxxx xxxx xxxx xxxx xxxx + message.set_optional_float( + WireFormatLite::DecodeFloat(0xFFB54321)); + RunValidJsonTestWithProtobufInput( + "FloatFieldNormalizeSignalingNan", REQUIRED, message, + "optional_float: nan"); + } + + // Special values must be quoted. + ExpectParseFailureForJson( + "FloatFieldNanNotQuoted", RECOMMENDED, + R"({"optionalFloat": NaN})"); + ExpectParseFailureForJson( + "FloatFieldInfinityNotQuoted", RECOMMENDED, + R"({"optionalFloat": Infinity})"); + ExpectParseFailureForJson( + "FloatFieldNegativeInfinityNotQuoted", RECOMMENDED, + R"({"optionalFloat": -Infinity})"); + // Parsers should reject out-of-bound values. + ExpectParseFailureForJson( + "FloatFieldTooSmall", REQUIRED, + R"({"optionalFloat": -3.502823e+38})"); + ExpectParseFailureForJson( + "FloatFieldTooLarge", REQUIRED, + R"({"optionalFloat": 3.502823e+38})"); + + // Double fields. + RunValidJsonTest( + "DoubleFieldMinPositiveValue", REQUIRED, + R"({"optionalDouble": 2.22507e-308})", + "optional_double: 2.22507e-308"); + RunValidJsonTest( + "DoubleFieldMaxNegativeValue", REQUIRED, + R"({"optionalDouble": -2.22507e-308})", + "optional_double: -2.22507e-308"); + RunValidJsonTest( + "DoubleFieldMaxPositiveValue", REQUIRED, + R"({"optionalDouble": 1.79769e+308})", + "optional_double: 1.79769e+308"); + RunValidJsonTest( + "DoubleFieldMinNegativeValue", REQUIRED, + R"({"optionalDouble": -1.79769e+308})", + "optional_double: -1.79769e+308"); + // Values can be quoted. + RunValidJsonTest( + "DoubleFieldQuotedValue", REQUIRED, + R"({"optionalDouble": "1"})", + "optional_double: 1"); + // Speical values. + RunValidJsonTest( + "DoubleFieldNan", REQUIRED, + R"({"optionalDouble": "NaN"})", + "optional_double: nan"); + RunValidJsonTest( + "DoubleFieldInfinity", REQUIRED, + R"({"optionalDouble": "Infinity"})", + "optional_double: inf"); + RunValidJsonTest( + "DoubleFieldNegativeInfinity", REQUIRED, + R"({"optionalDouble": "-Infinity"})", + "optional_double: -inf"); + // Non-cannonical Nan will be correctly normalized. + { + TestAllTypesProto3 message; + message.set_optional_double( + WireFormatLite::DecodeDouble(0x7FFA123456789ABCLL)); + RunValidJsonTestWithProtobufInput( + "DoubleFieldNormalizeQuietNan", REQUIRED, message, + "optional_double: nan"); + message.set_optional_double( + WireFormatLite::DecodeDouble(0xFFFBCBA987654321LL)); + RunValidJsonTestWithProtobufInput( + "DoubleFieldNormalizeSignalingNan", REQUIRED, message, + "optional_double: nan"); + } + + // Special values must be quoted. + ExpectParseFailureForJson( + "DoubleFieldNanNotQuoted", RECOMMENDED, + R"({"optionalDouble": NaN})"); + ExpectParseFailureForJson( + "DoubleFieldInfinityNotQuoted", RECOMMENDED, + R"({"optionalDouble": Infinity})"); + ExpectParseFailureForJson( + "DoubleFieldNegativeInfinityNotQuoted", RECOMMENDED, + R"({"optionalDouble": -Infinity})"); + + // Parsers should reject out-of-bound values. + ExpectParseFailureForJson( + "DoubleFieldTooSmall", REQUIRED, + R"({"optionalDouble": -1.89769e+308})"); + ExpectParseFailureForJson( + "DoubleFieldTooLarge", REQUIRED, + R"({"optionalDouble": +1.89769e+308})"); + + // Enum fields. + RunValidJsonTest( + "EnumField", REQUIRED, + R"({"optionalNestedEnum": "FOO"})", + "optional_nested_enum: FOO"); + // Enum values must be represented as strings. + ExpectParseFailureForJson( + "EnumFieldNotQuoted", REQUIRED, + R"({"optionalNestedEnum": FOO})"); + // Numeric values are allowed. + RunValidJsonTest( + "EnumFieldNumericValueZero", REQUIRED, + R"({"optionalNestedEnum": 0})", + "optional_nested_enum: FOO"); + RunValidJsonTest( + "EnumFieldNumericValueNonZero", REQUIRED, + R"({"optionalNestedEnum": 1})", + "optional_nested_enum: BAR"); + // Unknown enum values are represented as numeric values. + RunValidJsonTestWithValidator( + "EnumFieldUnknownValue", REQUIRED, + R"({"optionalNestedEnum": 123})", + [](const Json::Value& value) { + return value["optionalNestedEnum"].type() == Json::intValue && + value["optionalNestedEnum"].asInt() == 123; + }); + + // String fields. + RunValidJsonTest( + "StringField", REQUIRED, + R"({"optionalString": "Hello world!"})", + "optional_string: \"Hello world!\""); + RunValidJsonTest( + "StringFieldUnicode", REQUIRED, + // Google in Chinese. + R"({"optionalString": "谷歌"})", + R"(optional_string: "谷歌")"); + RunValidJsonTest( + "StringFieldEscape", REQUIRED, + R"({"optionalString": "\"\\\/\b\f\n\r\t"})", + R"(optional_string: "\"\\/\b\f\n\r\t")"); + RunValidJsonTest( + "StringFieldUnicodeEscape", REQUIRED, + R"({"optionalString": "\u8C37\u6B4C"})", + R"(optional_string: "谷歌")"); + RunValidJsonTest( + "StringFieldUnicodeEscapeWithLowercaseHexLetters", REQUIRED, + R"({"optionalString": "\u8c37\u6b4c"})", + R"(optional_string: "谷歌")"); + RunValidJsonTest( + "StringFieldSurrogatePair", REQUIRED, + // The character is an emoji: grinning face with smiling eyes. 😁 + R"({"optionalString": "\uD83D\uDE01"})", + R"(optional_string: "\xF0\x9F\x98\x81")"); + + // Unicode escapes must start with "\u" (lowercase u). + ExpectParseFailureForJson( + "StringFieldUppercaseEscapeLetter", RECOMMENDED, + R"({"optionalString": "\U8C37\U6b4C"})"); + ExpectParseFailureForJson( + "StringFieldInvalidEscape", RECOMMENDED, + R"({"optionalString": "\uXXXX\u6B4C"})"); + ExpectParseFailureForJson( + "StringFieldUnterminatedEscape", RECOMMENDED, + R"({"optionalString": "\u8C3"})"); + ExpectParseFailureForJson( + "StringFieldUnpairedHighSurrogate", RECOMMENDED, + R"({"optionalString": "\uD800"})"); + ExpectParseFailureForJson( + "StringFieldUnpairedLowSurrogate", RECOMMENDED, + R"({"optionalString": "\uDC00"})"); + ExpectParseFailureForJson( + "StringFieldSurrogateInWrongOrder", RECOMMENDED, + R"({"optionalString": "\uDE01\uD83D"})"); + ExpectParseFailureForJson( + "StringFieldNotAString", REQUIRED, + R"({"optionalString": 12345})"); + + // Bytes fields. + RunValidJsonTest( + "BytesField", REQUIRED, + R"({"optionalBytes": "AQI="})", + R"(optional_bytes: "\x01\x02")"); + RunValidJsonTest( + "BytesFieldBase64Url", RECOMMENDED, + R"({"optionalBytes": "-_"})", + R"(optional_bytes: "\xfb")"); + + // Message fields. + RunValidJsonTest( + "MessageField", REQUIRED, + R"({"optionalNestedMessage": {"a": 1234}})", + "optional_nested_message: {a: 1234}"); + + // Oneof fields. + ExpectParseFailureForJson( + "OneofFieldDuplicate", REQUIRED, + R"({"oneofUint32": 1, "oneofString": "test"})"); + // Ensure zero values for oneof make it out/backs. + TestAllTypesProto3 messageProto3; + TestAllTypesProto2 messageProto2; + TestOneofMessage(messageProto3, true); + TestOneofMessage(messageProto2, false); + RunValidJsonTest( + "OneofZeroUint32", RECOMMENDED, + R"({"oneofUint32": 0})", "oneof_uint32: 0"); + RunValidJsonTest( + "OneofZeroMessage", RECOMMENDED, + R"({"oneofNestedMessage": {}})", "oneof_nested_message: {}"); + RunValidJsonTest( + "OneofZeroString", RECOMMENDED, + R"({"oneofString": ""})", "oneof_string: \"\""); + RunValidJsonTest( + "OneofZeroBytes", RECOMMENDED, + R"({"oneofBytes": ""})", "oneof_bytes: \"\""); + RunValidJsonTest( + "OneofZeroBool", RECOMMENDED, + R"({"oneofBool": false})", "oneof_bool: false"); + RunValidJsonTest( + "OneofZeroUint64", RECOMMENDED, + R"({"oneofUint64": 0})", "oneof_uint64: 0"); + RunValidJsonTest( + "OneofZeroFloat", RECOMMENDED, + R"({"oneofFloat": 0.0})", "oneof_float: 0"); + RunValidJsonTest( + "OneofZeroDouble", RECOMMENDED, + R"({"oneofDouble": 0.0})", "oneof_double: 0"); + RunValidJsonTest( + "OneofZeroEnum", RECOMMENDED, + R"({"oneofEnum":"FOO"})", "oneof_enum: FOO"); + + // Repeated fields. + RunValidJsonTest( + "PrimitiveRepeatedField", REQUIRED, + R"({"repeatedInt32": [1, 2, 3, 4]})", + "repeated_int32: [1, 2, 3, 4]"); + RunValidJsonTest( + "EnumRepeatedField", REQUIRED, + R"({"repeatedNestedEnum": ["FOO", "BAR", "BAZ"]})", + "repeated_nested_enum: [FOO, BAR, BAZ]"); + RunValidJsonTest( + "StringRepeatedField", REQUIRED, + R"({"repeatedString": ["Hello", "world"]})", + R"(repeated_string: ["Hello", "world"])"); + RunValidJsonTest( + "BytesRepeatedField", REQUIRED, + R"({"repeatedBytes": ["AAEC", "AQI="]})", + R"(repeated_bytes: ["\x00\x01\x02", "\x01\x02"])"); + RunValidJsonTest( + "MessageRepeatedField", REQUIRED, + R"({"repeatedNestedMessage": [{"a": 1234}, {"a": 5678}]})", + "repeated_nested_message: {a: 1234}" + "repeated_nested_message: {a: 5678}"); + + // Repeated field elements are of incorrect type. + ExpectParseFailureForJson( + "RepeatedFieldWrongElementTypeExpectingIntegersGotBool", REQUIRED, + R"({"repeatedInt32": [1, false, 3, 4]})"); + ExpectParseFailureForJson( + "RepeatedFieldWrongElementTypeExpectingIntegersGotString", REQUIRED, + R"({"repeatedInt32": [1, 2, "name", 4]})"); + ExpectParseFailureForJson( + "RepeatedFieldWrongElementTypeExpectingIntegersGotMessage", REQUIRED, + R"({"repeatedInt32": [1, 2, 3, {"a": 4}]})"); + ExpectParseFailureForJson( + "RepeatedFieldWrongElementTypeExpectingStringsGotInt", REQUIRED, + R"({"repeatedString": ["1", 2, "3", "4"]})"); + ExpectParseFailureForJson( + "RepeatedFieldWrongElementTypeExpectingStringsGotBool", REQUIRED, + R"({"repeatedString": ["1", "2", false, "4"]})"); + ExpectParseFailureForJson( + "RepeatedFieldWrongElementTypeExpectingStringsGotMessage", REQUIRED, + R"({"repeatedString": ["1", 2, "3", {"a": 4}]})"); + ExpectParseFailureForJson( + "RepeatedFieldWrongElementTypeExpectingMessagesGotInt", REQUIRED, + R"({"repeatedNestedMessage": [{"a": 1}, 2]})"); + ExpectParseFailureForJson( + "RepeatedFieldWrongElementTypeExpectingMessagesGotBool", REQUIRED, + R"({"repeatedNestedMessage": [{"a": 1}, false]})"); + ExpectParseFailureForJson( + "RepeatedFieldWrongElementTypeExpectingMessagesGotString", REQUIRED, + R"({"repeatedNestedMessage": [{"a": 1}, "2"]})"); + // Trailing comma in the repeated field is not allowed. + ExpectParseFailureForJson( + "RepeatedFieldTrailingComma", RECOMMENDED, + R"({"repeatedInt32": [1, 2, 3, 4,]})"); + ExpectParseFailureForJson( + "RepeatedFieldTrailingCommaWithSpace", RECOMMENDED, + "{\"repeatedInt32\": [1, 2, 3, 4 ,]}"); + ExpectParseFailureForJson( + "RepeatedFieldTrailingCommaWithSpaceCommaSpace", RECOMMENDED, + "{\"repeatedInt32\": [1, 2, 3, 4 , ]}"); + ExpectParseFailureForJson( + "RepeatedFieldTrailingCommaWithNewlines", RECOMMENDED, + "{\"repeatedInt32\": [\n 1,\n 2,\n 3,\n 4,\n]}"); + + // Map fields. + RunValidJsonTest( + "Int32MapField", REQUIRED, + R"({"mapInt32Int32": {"1": 2, "3": 4}})", + "map_int32_int32: {key: 1 value: 2}" + "map_int32_int32: {key: 3 value: 4}"); + ExpectParseFailureForJson( + "Int32MapFieldKeyNotQuoted", RECOMMENDED, + R"({"mapInt32Int32": {1: 2, 3: 4}})"); + RunValidJsonTest( + "Uint32MapField", REQUIRED, + R"({"mapUint32Uint32": {"1": 2, "3": 4}})", + "map_uint32_uint32: {key: 1 value: 2}" + "map_uint32_uint32: {key: 3 value: 4}"); + ExpectParseFailureForJson( + "Uint32MapFieldKeyNotQuoted", RECOMMENDED, + R"({"mapUint32Uint32": {1: 2, 3: 4}})"); + RunValidJsonTest( + "Int64MapField", REQUIRED, + R"({"mapInt64Int64": {"1": 2, "3": 4}})", + "map_int64_int64: {key: 1 value: 2}" + "map_int64_int64: {key: 3 value: 4}"); + ExpectParseFailureForJson( + "Int64MapFieldKeyNotQuoted", RECOMMENDED, + R"({"mapInt64Int64": {1: 2, 3: 4}})"); + RunValidJsonTest( + "Uint64MapField", REQUIRED, + R"({"mapUint64Uint64": {"1": 2, "3": 4}})", + "map_uint64_uint64: {key: 1 value: 2}" + "map_uint64_uint64: {key: 3 value: 4}"); + ExpectParseFailureForJson( + "Uint64MapFieldKeyNotQuoted", RECOMMENDED, + R"({"mapUint64Uint64": {1: 2, 3: 4}})"); + RunValidJsonTest( + "BoolMapField", REQUIRED, + R"({"mapBoolBool": {"true": true, "false": false}})", + "map_bool_bool: {key: true value: true}" + "map_bool_bool: {key: false value: false}"); + ExpectParseFailureForJson( + "BoolMapFieldKeyNotQuoted", RECOMMENDED, + R"({"mapBoolBool": {true: true, false: false}})"); + RunValidJsonTest( + "MessageMapField", REQUIRED, + R"({ + "mapStringNestedMessage": { + "hello": {"a": 1234}, + "world": {"a": 5678} + } + })", + R"( + map_string_nested_message: { + key: "hello" + value: {a: 1234} + } + map_string_nested_message: { + key: "world" + value: {a: 5678} + } + )"); + // Since Map keys are represented as JSON strings, escaping should be allowed. + RunValidJsonTest( + "Int32MapEscapedKey", REQUIRED, + R"({"mapInt32Int32": {"\u0031": 2}})", + "map_int32_int32: {key: 1 value: 2}"); + RunValidJsonTest( + "Int64MapEscapedKey", REQUIRED, + R"({"mapInt64Int64": {"\u0031": 2}})", + "map_int64_int64: {key: 1 value: 2}"); + RunValidJsonTest( + "BoolMapEscapedKey", REQUIRED, + R"({"mapBoolBool": {"tr\u0075e": true}})", + "map_bool_bool: {key: true value: true}"); + + // "null" is accepted for all fields types. + RunValidJsonTest( + "AllFieldAcceptNull", REQUIRED, + R"({ + "optionalInt32": null, + "optionalInt64": null, + "optionalUint32": null, + "optionalUint64": null, + "optionalSint32": null, + "optionalSint64": null, + "optionalFixed32": null, + "optionalFixed64": null, + "optionalSfixed32": null, + "optionalSfixed64": null, + "optionalFloat": null, + "optionalDouble": null, + "optionalBool": null, + "optionalString": null, + "optionalBytes": null, + "optionalNestedEnum": null, + "optionalNestedMessage": null, + "repeatedInt32": null, + "repeatedInt64": null, + "repeatedUint32": null, + "repeatedUint64": null, + "repeatedSint32": null, + "repeatedSint64": null, + "repeatedFixed32": null, + "repeatedFixed64": null, + "repeatedSfixed32": null, + "repeatedSfixed64": null, + "repeatedFloat": null, + "repeatedDouble": null, + "repeatedBool": null, + "repeatedString": null, + "repeatedBytes": null, + "repeatedNestedEnum": null, + "repeatedNestedMessage": null, + "mapInt32Int32": null, + "mapBoolBool": null, + "mapStringNestedMessage": null + })", + ""); + + // Repeated field elements cannot be null. + ExpectParseFailureForJson( + "RepeatedFieldPrimitiveElementIsNull", RECOMMENDED, + R"({"repeatedInt32": [1, null, 2]})"); + ExpectParseFailureForJson( + "RepeatedFieldMessageElementIsNull", RECOMMENDED, + R"({"repeatedNestedMessage": [{"a":1}, null, {"a":2}]})"); + // Map field keys cannot be null. + ExpectParseFailureForJson( + "MapFieldKeyIsNull", RECOMMENDED, + R"({"mapInt32Int32": {null: 1}})"); + // Map field values cannot be null. + ExpectParseFailureForJson( + "MapFieldValueIsNull", RECOMMENDED, + R"({"mapInt32Int32": {"0": null}})"); + + // http://www.rfc-editor.org/rfc/rfc7159.txt says strings have to use double + // quotes. + ExpectParseFailureForJson( + "StringFieldSingleQuoteKey", RECOMMENDED, + R"({'optionalString': "Hello world!"})"); + ExpectParseFailureForJson( + "StringFieldSingleQuoteValue", RECOMMENDED, + R"({"optionalString": 'Hello world!'})"); + ExpectParseFailureForJson( + "StringFieldSingleQuoteBoth", RECOMMENDED, + R"({'optionalString': 'Hello world!'})"); + + // Unknown fields. + { + TestAllTypesProto3 messageProto3; + TestAllTypesProto2 messageProto2; + //TODO(yilunchong): update this behavior when unknown field's behavior + // changed in open source. Also delete + // Required.Proto3.ProtobufInput.UnknownVarint.ProtobufOutput + // from failure list of python_cpp python java + TestUnknownMessage(messageProto3, true); + TestUnknownMessage(messageProto2, false); + } + + // Wrapper types. + RunValidJsonTest( + "OptionalBoolWrapper", REQUIRED, + R"({"optionalBoolWrapper": false})", + "optional_bool_wrapper: {value: false}"); + RunValidJsonTest( + "OptionalInt32Wrapper", REQUIRED, + R"({"optionalInt32Wrapper": 0})", + "optional_int32_wrapper: {value: 0}"); + RunValidJsonTest( + "OptionalUint32Wrapper", REQUIRED, + R"({"optionalUint32Wrapper": 0})", + "optional_uint32_wrapper: {value: 0}"); + RunValidJsonTest( + "OptionalInt64Wrapper", REQUIRED, + R"({"optionalInt64Wrapper": 0})", + "optional_int64_wrapper: {value: 0}"); + RunValidJsonTest( + "OptionalUint64Wrapper", REQUIRED, + R"({"optionalUint64Wrapper": 0})", + "optional_uint64_wrapper: {value: 0}"); + RunValidJsonTest( + "OptionalFloatWrapper", REQUIRED, + R"({"optionalFloatWrapper": 0})", + "optional_float_wrapper: {value: 0}"); + RunValidJsonTest( + "OptionalDoubleWrapper", REQUIRED, + R"({"optionalDoubleWrapper": 0})", + "optional_double_wrapper: {value: 0}"); + RunValidJsonTest( + "OptionalStringWrapper", REQUIRED, + R"({"optionalStringWrapper": ""})", + R"(optional_string_wrapper: {value: ""})"); + RunValidJsonTest( + "OptionalBytesWrapper", REQUIRED, + R"({"optionalBytesWrapper": ""})", + R"(optional_bytes_wrapper: {value: ""})"); + RunValidJsonTest( + "OptionalWrapperTypesWithNonDefaultValue", REQUIRED, + R"({ + "optionalBoolWrapper": true, + "optionalInt32Wrapper": 1, + "optionalUint32Wrapper": 1, + "optionalInt64Wrapper": "1", + "optionalUint64Wrapper": "1", + "optionalFloatWrapper": 1, + "optionalDoubleWrapper": 1, + "optionalStringWrapper": "1", + "optionalBytesWrapper": "AQI=" + })", + R"( + optional_bool_wrapper: {value: true} + optional_int32_wrapper: {value: 1} + optional_uint32_wrapper: {value: 1} + optional_int64_wrapper: {value: 1} + optional_uint64_wrapper: {value: 1} + optional_float_wrapper: {value: 1} + optional_double_wrapper: {value: 1} + optional_string_wrapper: {value: "1"} + optional_bytes_wrapper: {value: "\x01\x02"} + )"); + RunValidJsonTest( + "RepeatedBoolWrapper", REQUIRED, + R"({"repeatedBoolWrapper": [true, false]})", + "repeated_bool_wrapper: {value: true}" + "repeated_bool_wrapper: {value: false}"); + RunValidJsonTest( + "RepeatedInt32Wrapper", REQUIRED, + R"({"repeatedInt32Wrapper": [0, 1]})", + "repeated_int32_wrapper: {value: 0}" + "repeated_int32_wrapper: {value: 1}"); + RunValidJsonTest( + "RepeatedUint32Wrapper", REQUIRED, + R"({"repeatedUint32Wrapper": [0, 1]})", + "repeated_uint32_wrapper: {value: 0}" + "repeated_uint32_wrapper: {value: 1}"); + RunValidJsonTest( + "RepeatedInt64Wrapper", REQUIRED, + R"({"repeatedInt64Wrapper": [0, 1]})", + "repeated_int64_wrapper: {value: 0}" + "repeated_int64_wrapper: {value: 1}"); + RunValidJsonTest( + "RepeatedUint64Wrapper", REQUIRED, + R"({"repeatedUint64Wrapper": [0, 1]})", + "repeated_uint64_wrapper: {value: 0}" + "repeated_uint64_wrapper: {value: 1}"); + RunValidJsonTest( + "RepeatedFloatWrapper", REQUIRED, + R"({"repeatedFloatWrapper": [0, 1]})", + "repeated_float_wrapper: {value: 0}" + "repeated_float_wrapper: {value: 1}"); + RunValidJsonTest( + "RepeatedDoubleWrapper", REQUIRED, + R"({"repeatedDoubleWrapper": [0, 1]})", + "repeated_double_wrapper: {value: 0}" + "repeated_double_wrapper: {value: 1}"); + RunValidJsonTest( + "RepeatedStringWrapper", REQUIRED, + R"({"repeatedStringWrapper": ["", "AQI="]})", + R"( + repeated_string_wrapper: {value: ""} + repeated_string_wrapper: {value: "AQI="} + )"); + RunValidJsonTest( + "RepeatedBytesWrapper", REQUIRED, + R"({"repeatedBytesWrapper": ["", "AQI="]})", + R"( + repeated_bytes_wrapper: {value: ""} + repeated_bytes_wrapper: {value: "\x01\x02"} + )"); + RunValidJsonTest( + "WrapperTypesWithNullValue", REQUIRED, + R"({ + "optionalBoolWrapper": null, + "optionalInt32Wrapper": null, + "optionalUint32Wrapper": null, + "optionalInt64Wrapper": null, + "optionalUint64Wrapper": null, + "optionalFloatWrapper": null, + "optionalDoubleWrapper": null, + "optionalStringWrapper": null, + "optionalBytesWrapper": null, + "repeatedBoolWrapper": null, + "repeatedInt32Wrapper": null, + "repeatedUint32Wrapper": null, + "repeatedInt64Wrapper": null, + "repeatedUint64Wrapper": null, + "repeatedFloatWrapper": null, + "repeatedDoubleWrapper": null, + "repeatedStringWrapper": null, + "repeatedBytesWrapper": null + })", + ""); + + // Duration + RunValidJsonTest( + "DurationMinValue", REQUIRED, + R"({"optionalDuration": "-315576000000.999999999s"})", + "optional_duration: {seconds: -315576000000 nanos: -999999999}"); + RunValidJsonTest( + "DurationMaxValue", REQUIRED, + R"({"optionalDuration": "315576000000.999999999s"})", + "optional_duration: {seconds: 315576000000 nanos: 999999999}"); + RunValidJsonTest( + "DurationRepeatedValue", REQUIRED, + R"({"repeatedDuration": ["1.5s", "-1.5s"]})", + "repeated_duration: {seconds: 1 nanos: 500000000}" + "repeated_duration: {seconds: -1 nanos: -500000000}"); + RunValidJsonTest( + "DurationNull", REQUIRED, + R"({"optionalDuration": null})", + ""); + + ExpectParseFailureForJson( + "DurationMissingS", REQUIRED, + R"({"optionalDuration": "1"})"); + ExpectParseFailureForJson( + "DurationJsonInputTooSmall", REQUIRED, + R"({"optionalDuration": "-315576000001.000000000s"})"); + ExpectParseFailureForJson( + "DurationJsonInputTooLarge", REQUIRED, + R"({"optionalDuration": "315576000001.000000000s"})"); + ExpectSerializeFailureForJson( + "DurationProtoInputTooSmall", REQUIRED, + "optional_duration: {seconds: -315576000001 nanos: 0}"); + ExpectSerializeFailureForJson( + "DurationProtoInputTooLarge", REQUIRED, + "optional_duration: {seconds: 315576000001 nanos: 0}"); + + RunValidJsonTestWithValidator( + "DurationHasZeroFractionalDigit", RECOMMENDED, + R"({"optionalDuration": "1.000000000s"})", + [](const Json::Value& value) { + return value["optionalDuration"].asString() == "1s"; + }); + RunValidJsonTestWithValidator( + "DurationHas3FractionalDigits", RECOMMENDED, + R"({"optionalDuration": "1.010000000s"})", + [](const Json::Value& value) { + return value["optionalDuration"].asString() == "1.010s"; + }); + RunValidJsonTestWithValidator( + "DurationHas6FractionalDigits", RECOMMENDED, + R"({"optionalDuration": "1.000010000s"})", + [](const Json::Value& value) { + return value["optionalDuration"].asString() == "1.000010s"; + }); + RunValidJsonTestWithValidator( + "DurationHas9FractionalDigits", RECOMMENDED, + R"({"optionalDuration": "1.000000010s"})", + [](const Json::Value& value) { + return value["optionalDuration"].asString() == "1.000000010s"; + }); + + // Timestamp + RunValidJsonTest( + "TimestampMinValue", REQUIRED, + R"({"optionalTimestamp": "0001-01-01T00:00:00Z"})", + "optional_timestamp: {seconds: -62135596800}"); + RunValidJsonTest( + "TimestampMaxValue", REQUIRED, + R"({"optionalTimestamp": "9999-12-31T23:59:59.999999999Z"})", + "optional_timestamp: {seconds: 253402300799 nanos: 999999999}"); + RunValidJsonTest( + "TimestampRepeatedValue", REQUIRED, + R"({ + "repeatedTimestamp": [ + "0001-01-01T00:00:00Z", + "9999-12-31T23:59:59.999999999Z" + ] + })", + "repeated_timestamp: {seconds: -62135596800}" + "repeated_timestamp: {seconds: 253402300799 nanos: 999999999}"); + RunValidJsonTest( + "TimestampWithPositiveOffset", REQUIRED, + R"({"optionalTimestamp": "1970-01-01T08:00:00+08:00"})", + "optional_timestamp: {seconds: 0}"); + RunValidJsonTest( + "TimestampWithNegativeOffset", REQUIRED, + R"({"optionalTimestamp": "1969-12-31T16:00:00-08:00"})", + "optional_timestamp: {seconds: 0}"); + RunValidJsonTest( + "TimestampNull", REQUIRED, + R"({"optionalTimestamp": null})", + ""); + + ExpectParseFailureForJson( + "TimestampJsonInputTooSmall", REQUIRED, + R"({"optionalTimestamp": "0000-01-01T00:00:00Z"})"); + ExpectParseFailureForJson( + "TimestampJsonInputTooLarge", REQUIRED, + R"({"optionalTimestamp": "10000-01-01T00:00:00Z"})"); + ExpectParseFailureForJson( + "TimestampJsonInputMissingZ", REQUIRED, + R"({"optionalTimestamp": "0001-01-01T00:00:00"})"); + ExpectParseFailureForJson( + "TimestampJsonInputMissingT", REQUIRED, + R"({"optionalTimestamp": "0001-01-01 00:00:00Z"})"); + ExpectParseFailureForJson( + "TimestampJsonInputLowercaseZ", REQUIRED, + R"({"optionalTimestamp": "0001-01-01T00:00:00z"})"); + ExpectParseFailureForJson( + "TimestampJsonInputLowercaseT", REQUIRED, + R"({"optionalTimestamp": "0001-01-01t00:00:00Z"})"); + ExpectSerializeFailureForJson( + "TimestampProtoInputTooSmall", REQUIRED, + "optional_timestamp: {seconds: -62135596801}"); + ExpectSerializeFailureForJson( + "TimestampProtoInputTooLarge", REQUIRED, + "optional_timestamp: {seconds: 253402300800}"); + RunValidJsonTestWithValidator( + "TimestampZeroNormalized", RECOMMENDED, + R"({"optionalTimestamp": "1969-12-31T16:00:00-08:00"})", + [](const Json::Value& value) { + return value["optionalTimestamp"].asString() == + "1970-01-01T00:00:00Z"; + }); + RunValidJsonTestWithValidator( + "TimestampHasZeroFractionalDigit", RECOMMENDED, + R"({"optionalTimestamp": "1970-01-01T00:00:00.000000000Z"})", + [](const Json::Value& value) { + return value["optionalTimestamp"].asString() == + "1970-01-01T00:00:00Z"; + }); + RunValidJsonTestWithValidator( + "TimestampHas3FractionalDigits", RECOMMENDED, + R"({"optionalTimestamp": "1970-01-01T00:00:00.010000000Z"})", + [](const Json::Value& value) { + return value["optionalTimestamp"].asString() == + "1970-01-01T00:00:00.010Z"; + }); + RunValidJsonTestWithValidator( + "TimestampHas6FractionalDigits", RECOMMENDED, + R"({"optionalTimestamp": "1970-01-01T00:00:00.000010000Z"})", + [](const Json::Value& value) { + return value["optionalTimestamp"].asString() == + "1970-01-01T00:00:00.000010Z"; + }); + RunValidJsonTestWithValidator( + "TimestampHas9FractionalDigits", RECOMMENDED, + R"({"optionalTimestamp": "1970-01-01T00:00:00.000000010Z"})", + [](const Json::Value& value) { + return value["optionalTimestamp"].asString() == + "1970-01-01T00:00:00.000000010Z"; + }); + + // FieldMask + RunValidJsonTest( + "FieldMask", REQUIRED, + R"({"optionalFieldMask": "foo,barBaz"})", + R"(optional_field_mask: {paths: "foo" paths: "bar_baz"})"); + ExpectParseFailureForJson( + "FieldMaskInvalidCharacter", RECOMMENDED, + R"({"optionalFieldMask": "foo,bar_bar"})"); + ExpectSerializeFailureForJson( + "FieldMaskPathsDontRoundTrip", RECOMMENDED, + R"(optional_field_mask: {paths: "fooBar"})"); + ExpectSerializeFailureForJson( + "FieldMaskNumbersDontRoundTrip", RECOMMENDED, + R"(optional_field_mask: {paths: "foo_3_bar"})"); + ExpectSerializeFailureForJson( + "FieldMaskTooManyUnderscore", RECOMMENDED, + R"(optional_field_mask: {paths: "foo__bar"})"); + + // Struct + RunValidJsonTest( + "Struct", REQUIRED, + R"({ + "optionalStruct": { + "nullValue": null, + "intValue": 1234, + "boolValue": true, + "doubleValue": 1234.5678, + "stringValue": "Hello world!", + "listValue": [1234, "5678"], + "objectValue": { + "value": 0 + } + } + })", + R"( + optional_struct: { + fields: { + key: "nullValue" + value: {null_value: NULL_VALUE} + } + fields: { + key: "intValue" + value: {number_value: 1234} + } + fields: { + key: "boolValue" + value: {bool_value: true} + } + fields: { + key: "doubleValue" + value: {number_value: 1234.5678} + } + fields: { + key: "stringValue" + value: {string_value: "Hello world!"} + } + fields: { + key: "listValue" + value: { + list_value: { + values: { + number_value: 1234 + } + values: { + string_value: "5678" + } + } + } + } + fields: { + key: "objectValue" + value: { + struct_value: { + fields: { + key: "value" + value: { + number_value: 0 + } + } + } + } + } + } + )"); + // Value + RunValidJsonTest( + "ValueAcceptInteger", REQUIRED, + R"({"optionalValue": 1})", + "optional_value: { number_value: 1}"); + RunValidJsonTest( + "ValueAcceptFloat", REQUIRED, + R"({"optionalValue": 1.5})", + "optional_value: { number_value: 1.5}"); + RunValidJsonTest( + "ValueAcceptBool", REQUIRED, + R"({"optionalValue": false})", + "optional_value: { bool_value: false}"); + RunValidJsonTest( + "ValueAcceptNull", REQUIRED, + R"({"optionalValue": null})", + "optional_value: { null_value: NULL_VALUE}"); + RunValidJsonTest( + "ValueAcceptString", REQUIRED, + R"({"optionalValue": "hello"})", + R"(optional_value: { string_value: "hello"})"); + RunValidJsonTest( + "ValueAcceptList", REQUIRED, + R"({"optionalValue": [0, "hello"]})", + R"( + optional_value: { + list_value: { + values: { + number_value: 0 + } + values: { + string_value: "hello" + } + } + } + )"); + RunValidJsonTest( + "ValueAcceptObject", REQUIRED, + R"({"optionalValue": {"value": 1}})", + R"( + optional_value: { + struct_value: { + fields: { + key: "value" + value: { + number_value: 1 + } + } + } + } + )"); + + // Any + RunValidJsonTest( + "Any", REQUIRED, + R"({ + "optionalAny": { + "@type": "type.googleapis.com/protobuf_test_messages.proto3.TestAllTypesProto3", + "optionalInt32": 12345 + } + })", + R"( + optional_any: { + [type.googleapis.com/protobuf_test_messages.proto3.TestAllTypesProto3] { + optional_int32: 12345 + } + } + )"); + RunValidJsonTest( + "AnyNested", REQUIRED, + R"({ + "optionalAny": { + "@type": "type.googleapis.com/google.protobuf.Any", + "value": { + "@type": "type.googleapis.com/protobuf_test_messages.proto3.TestAllTypesProto3", + "optionalInt32": 12345 + } + } + })", + R"( + optional_any: { + [type.googleapis.com/google.protobuf.Any] { + [type.googleapis.com/protobuf_test_messages.proto3.TestAllTypesProto3] { + optional_int32: 12345 + } + } + } + )"); + // The special "@type" tag is not required to appear first. + RunValidJsonTest( + "AnyUnorderedTypeTag", REQUIRED, + R"({ + "optionalAny": { + "optionalInt32": 12345, + "@type": "type.googleapis.com/protobuf_test_messages.proto3.TestAllTypesProto3" + } + })", + R"( + optional_any: { + [type.googleapis.com/protobuf_test_messages.proto3.TestAllTypesProto3] { + optional_int32: 12345 + } + } + )"); + // Well-known types in Any. + RunValidJsonTest( + "AnyWithInt32ValueWrapper", REQUIRED, + R"({ + "optionalAny": { + "@type": "type.googleapis.com/google.protobuf.Int32Value", + "value": 12345 + } + })", + R"( + optional_any: { + [type.googleapis.com/google.protobuf.Int32Value] { + value: 12345 + } + } + )"); + RunValidJsonTest( + "AnyWithDuration", REQUIRED, + R"({ + "optionalAny": { + "@type": "type.googleapis.com/google.protobuf.Duration", + "value": "1.5s" + } + })", + R"( + optional_any: { + [type.googleapis.com/google.protobuf.Duration] { + seconds: 1 + nanos: 500000000 + } + } + )"); + RunValidJsonTest( + "AnyWithTimestamp", REQUIRED, + R"({ + "optionalAny": { + "@type": "type.googleapis.com/google.protobuf.Timestamp", + "value": "1970-01-01T00:00:00Z" + } + })", + R"( + optional_any: { + [type.googleapis.com/google.protobuf.Timestamp] { + seconds: 0 + nanos: 0 + } + } + )"); + RunValidJsonTest( + "AnyWithFieldMask", REQUIRED, + R"({ + "optionalAny": { + "@type": "type.googleapis.com/google.protobuf.FieldMask", + "value": "foo,barBaz" + } + })", + R"( + optional_any: { + [type.googleapis.com/google.protobuf.FieldMask] { + paths: ["foo", "bar_baz"] + } + } + )"); + RunValidJsonTest( + "AnyWithStruct", REQUIRED, + R"({ + "optionalAny": { + "@type": "type.googleapis.com/google.protobuf.Struct", + "value": { + "foo": 1 + } + } + })", + R"( + optional_any: { + [type.googleapis.com/google.protobuf.Struct] { + fields: { + key: "foo" + value: { + number_value: 1 + } + } + } + } + )"); + RunValidJsonTest( + "AnyWithValueForJsonObject", REQUIRED, + R"({ + "optionalAny": { + "@type": "type.googleapis.com/google.protobuf.Value", + "value": { + "foo": 1 + } + } + })", + R"( + optional_any: { + [type.googleapis.com/google.protobuf.Value] { + struct_value: { + fields: { + key: "foo" + value: { + number_value: 1 + } + } + } + } + } + )"); + RunValidJsonTest( + "AnyWithValueForInteger", REQUIRED, + R"({ + "optionalAny": { + "@type": "type.googleapis.com/google.protobuf.Value", + "value": 1 + } + })", + R"( + optional_any: { + [type.googleapis.com/google.protobuf.Value] { + number_value: 1 + } + } + )"); + + RunValidJsonIgnoreUnknownTest( + "IgnoreUnknownJsonNumber", REQUIRED, + R"({ + "unknown": 1 + })", + ""); + RunValidJsonIgnoreUnknownTest( + "IgnoreUnknownJsonString", REQUIRED, + R"({ + "unknown": "a" + })", + ""); + RunValidJsonIgnoreUnknownTest( + "IgnoreUnknownJsonTrue", REQUIRED, + R"({ + "unknown": true + })", + ""); + RunValidJsonIgnoreUnknownTest( + "IgnoreUnknownJsonFalse", REQUIRED, + R"({ + "unknown": false + })", + ""); + RunValidJsonIgnoreUnknownTest( + "IgnoreUnknownJsonNull", REQUIRED, + R"({ + "unknown": null + })", + ""); + RunValidJsonIgnoreUnknownTest( + "IgnoreUnknownJsonObject", REQUIRED, + R"({ + "unknown": {"a": 1} + })", + ""); +} + +} // namespace protobuf +} // namespace google + +int main(int argc, char *argv[]) { + google::protobuf::ConformanceTestSuiteImpl suite; + return google::protobuf::ForkPipeRunner::Run(argc, argv, &suite); +} |