diff options
author | Feng Xiao <xfxyjwf@gmail.com> | 2018-08-08 17:00:41 -0700 |
---|---|---|
committer | Feng Xiao <xfxyjwf@gmail.com> | 2018-08-08 17:00:41 -0700 |
commit | 6bbe197e9c1b6fc38cbdc45e3bf83fa7ced792a3 (patch) | |
tree | e575738adf52d24b883cca5e8928a5ded31caba1 /conformance | |
parent | e7746f487cb9cca685ffb1b3d7dccc5554b618a4 (diff) | |
download | protobuf-6bbe197e9c1b6fc38cbdc45e3bf83fa7ced792a3.tar.gz protobuf-6bbe197e9c1b6fc38cbdc45e3bf83fa7ced792a3.tar.bz2 protobuf-6bbe197e9c1b6fc38cbdc45e3bf83fa7ced792a3.zip |
Down-integrate from google3.
Diffstat (limited to 'conformance')
-rw-r--r-- | conformance/ConformanceJava.java | 10 | ||||
-rw-r--r-- | conformance/Makefile.am | 2 | ||||
-rw-r--r-- | conformance/conformance.proto | 17 | ||||
-rw-r--r-- | conformance/conformance_cpp.cc | 8 | ||||
-rwxr-xr-x | conformance/conformance_php.php | 6 | ||||
-rwxr-xr-x | conformance/conformance_python.py | 6 | ||||
-rw-r--r-- | conformance/conformance_test.cc | 2424 | ||||
-rw-r--r-- | conformance/conformance_test.h | 155 | ||||
-rw-r--r-- | conformance/conformance_test_impl.cc | 2368 | ||||
-rw-r--r-- | conformance/conformance_test_runner.cc | 22 | ||||
-rw-r--r-- | conformance/failure_list_cpp.txt | 6 | ||||
-rw-r--r-- | conformance/failure_list_csharp.txt | 6 | ||||
-rw-r--r-- | conformance/failure_list_java.txt | 6 | ||||
-rw-r--r-- | conformance/failure_list_php_c.txt | 20 | ||||
-rw-r--r-- | conformance/failure_list_python-post26.txt | 6 | ||||
-rw-r--r-- | conformance/failure_list_python.txt | 6 | ||||
-rw-r--r-- | conformance/failure_list_python_cpp.txt | 6 | ||||
-rw-r--r-- | conformance/failure_list_ruby.txt | 8 |
18 files changed, 2626 insertions, 2456 deletions
diff --git a/conformance/ConformanceJava.java b/conformance/ConformanceJava.java index 596d113a..d1ce4059 100644 --- a/conformance/ConformanceJava.java +++ b/conformance/ConformanceJava.java @@ -233,10 +233,14 @@ class ConformanceJava { } case JSON_PAYLOAD: { try { - TestMessagesProto3.TestAllTypesProto3.Builder builder = + TestMessagesProto3.TestAllTypesProto3.Builder builder = TestMessagesProto3.TestAllTypesProto3.newBuilder(); - JsonFormat.parser().usingTypeRegistry(typeRegistry) - .merge(request.getJsonPayload(), builder); + JsonFormat.Parser parser = JsonFormat.parser().usingTypeRegistry(typeRegistry); + if (request.getTestCategory() + == Conformance.TestCategory.JSON_IGNORE_UNKNOWN_PARSING_TEST) { + parser = parser.ignoringUnknownFields(); + } + parser.merge(request.getJsonPayload(), builder); testMessage = builder.build(); } catch (InvalidProtocolBufferException e) { return Conformance.ConformanceResponse.newBuilder().setParseError(e.getMessage()).build(); diff --git a/conformance/Makefile.am b/conformance/Makefile.am index e51ab80a..765f3588 100644 --- a/conformance/Makefile.am +++ b/conformance/Makefile.am @@ -279,6 +279,8 @@ $(protoc_outputs): protoc_middleman $(other_language_protoc_outputs): protoc_middleman +BUILT_SOURCES = $(protoc_outputs) $(other_language_protoc_outputs) + CLEANFILES = $(protoc_outputs) protoc_middleman javac_middleman conformance-java javac_middleman_lite conformance-java-lite conformance-csharp conformance-php conformance-php-c $(other_language_protoc_outputs) MAINTAINERCLEANFILES = \ diff --git a/conformance/conformance.proto b/conformance/conformance.proto index 897e7b38..af89152d 100644 --- a/conformance/conformance.proto +++ b/conformance/conformance.proto @@ -57,6 +57,18 @@ enum WireFormat { JSON = 2; } +enum TestCategory { + UNSPECIFIED_TEST = 0; + BINARY_TEST = 1; // Test binary wire format. + JSON_TEST = 2; // Test json wire format. + // Similar to JSON_TEST. However, during parsing json, testee should ignore + // unknown fields. This feature is optional. Each implementation can descide + // whether to support it. See + // https://developers.google.com/protocol-buffers/docs/proto3#json_options + // for more detail. + JSON_IGNORE_UNKNOWN_PARSING_TEST = 3; +} + // Represents a single test case's input. The testee should: // // 1. parse this proto (which should always succeed) @@ -83,7 +95,10 @@ message ConformanceRequest { // protobuf_test_messages.proto2.TestAllTypesProto2. string message_type = 4; - bool ignore_unknown_json = 5; + // Each test is given a specific test category. Some category may need + // spedific support in testee programs. Refer to the defintion of TestCategory + // for more information. + TestCategory test_category = 5; } // Represents a single test case's output. diff --git a/conformance/conformance_cpp.cc b/conformance/conformance_cpp.cc index 97ae1a7a..ac2f6dea 100644 --- a/conformance/conformance_cpp.cc +++ b/conformance/conformance_cpp.cc @@ -46,6 +46,7 @@ using google::protobuf::DescriptorPool; using google::protobuf::Message; using google::protobuf::MessageFactory; using google::protobuf::util::BinaryToJsonString; +using google::protobuf::util::JsonParseOptions; using google::protobuf::util::JsonToBinaryString; using google::protobuf::util::NewTypeResolverForDescriptorPool; using google::protobuf::util::Status; @@ -112,8 +113,13 @@ void DoTest(const ConformanceRequest& request, ConformanceResponse* response) { case ConformanceRequest::kJsonPayload: { string proto_binary; + JsonParseOptions options; + options.ignore_unknown_fields = + (request.test_category() == + conformance::JSON_IGNORE_UNKNOWN_PARSING_TEST); Status status = JsonToBinaryString(type_resolver, *type_url, - request.json_payload(), &proto_binary); + request.json_payload(), &proto_binary, + options); if (!status.ok()) { response->set_parse_error(string("Parse error: ") + status.error_message().as_string()); diff --git a/conformance/conformance_php.php b/conformance/conformance_php.php index 65483e27..799cc3e3 100755 --- a/conformance/conformance_php.php +++ b/conformance/conformance_php.php @@ -3,6 +3,7 @@ require_once("Conformance/WireFormat.php"); require_once("Conformance/ConformanceResponse.php"); require_once("Conformance/ConformanceRequest.php"); +require_once("Conformance/TestCategory.php"); require_once("Protobuf_test_messages/Proto3/ForeignMessage.php"); require_once("Protobuf_test_messages/Proto3/ForeignEnum.php"); require_once("Protobuf_test_messages/Proto3/TestAllTypesProto3.php"); @@ -12,6 +13,7 @@ require_once("Protobuf_test_messages/Proto3/TestAllTypesProto3/NestedEnum.php"); require_once("GPBMetadata/Conformance.php"); require_once("GPBMetadata/Google/Protobuf/TestMessagesProto3.php"); +use \Conformance\TestCategory; use \Conformance\WireFormat; if (!ini_get("date.timezone")) { @@ -39,7 +41,9 @@ function doTest($request) trigger_error("Protobuf request doesn't have specific payload type", E_USER_ERROR); } } elseif ($request->getPayload() == "json_payload") { - $ignore_json_unknown = $request->getIgnoreUnknownJson(); + $ignore_json_unknown = + ($request->getTestCategory() == + TestCategory::JSON_IGNORE_UNKNOWN_PARSING_TEST); try { $test_message->mergeFromJsonString($request->getJsonPayload(), $ignore_json_unknown); diff --git a/conformance/conformance_python.py b/conformance/conformance_python.py index c5ba2467..876642bc 100755 --- a/conformance/conformance_python.py +++ b/conformance/conformance_python.py @@ -78,7 +78,11 @@ def do_test(request): elif request.WhichOneof('payload') == 'json_payload': try: - json_format.Parse(request.json_payload, test_message) + ignore_unknown_fields = \ + request.test_category == \ + conformance_pb2.JSON_IGNORE_UNKNOWN_PARSING_TEST + json_format.Parse(request.json_payload, test_message, + ignore_unknown_fields) except Exception as e: response.parse_error = str(e) return response diff --git a/conformance/conformance_test.cc b/conformance/conformance_test.cc index d9e88ce5..0a45f9b9 100644 --- a/conformance/conformance_test.cc +++ b/conformance/conformance_test.cc @@ -28,199 +28,83 @@ // (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 <set> #include <stdarg.h> #include <string> #include <fstream> #include "conformance.pb.h" #include "conformance_test.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/stringprintf.h> +#include <google/protobuf/stubs/strutil.h> +#include <google/protobuf/message.h> #include <google/protobuf/text_format.h> #include <google/protobuf/util/field_comparator.h> #include <google/protobuf/util/json_util.h> #include <google/protobuf/util/message_differencer.h> -#include <google/protobuf/util/type_resolver_util.h> -#include <google/protobuf/wire_format_lite.h> - -#include "third_party/jsoncpp/json.h" using conformance::ConformanceRequest; using conformance::ConformanceResponse; using conformance::WireFormat; -using google::protobuf::Descriptor; -using google::protobuf::FieldDescriptor; -using google::protobuf::internal::WireFormatLite; using google::protobuf::TextFormat; using google::protobuf::util::DefaultFieldComparator; using google::protobuf::util::JsonToBinaryString; using google::protobuf::util::MessageDifferencer; -using google::protobuf::util::NewTypeResolverForDescriptorPool; using google::protobuf::util::Status; -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; -} +namespace google { +namespace protobuf { -// The maximum number of bytes that it takes to encode a 64-bit varint. -#define VARINT_MAX_LEN 10 +std::set<ConformanceTestSuite*> *conformance_test_suite_set; +GOOGLE_PROTOBUF_DECLARE_ONCE(conformance_test_suite_set_init_); -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; +void DeleteConformanceTestSuiteSet() { + delete conformance_test_suite_set; } -string varint(uint64_t x) { - char buf[VARINT_MAX_LEN]; - size_t len = vencode64(x, 0, buf); - return string(buf, len); +static void InitConformanceTestSuiteSet() { + conformance_test_suite_set = new std::set<ConformanceTestSuite*>(); + internal::OnShutdown(&DeleteConformanceTestSuiteSet); } -// 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); +static void InitConformanceTestSuiteSetOnce() { + ::google::protobuf::GoogleOnceInit( + &conformance_test_suite_set_init_, + &InitConformanceTestSuiteSet); } -// 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); +void AddTestSuite(ConformanceTestSuite* suite) { + InitConformanceTestSuiteSetOnce(); + conformance_test_suite_set->insert(suite); } -string submsg(uint32_t fn, const string& buf) { - return cat( tag(fn, WireFormatLite::WIRETYPE_LENGTH_DELIMITED), delim(buf) ); +const std::set<ConformanceTestSuite*>& GetTestSuiteSet() { + InitConformanceTestSuiteSetOnce(); + return *conformance_test_suite_set; } -#define UNKNOWN_FIELD 666 - -const FieldDescriptor* GetFieldForType(FieldDescriptor::Type type, - bool repeated, bool isProto3) { - - const Descriptor* d = isProto3 ? - 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; -} - -} // anonymous namespace - -namespace google { -namespace protobuf { - ConformanceTestSuite::ConformanceRequestSetting::ConformanceRequestSetting( - ConformanceLevel level, conformance::WireFormat input_format, - conformance::WireFormat output_format, bool is_proto3, + ConformanceLevel level, + conformance::WireFormat input_format, + conformance::WireFormat output_format, + conformance::TestCategory test_category, + const Message& prototype_message, const string& test_name, const string& input) - : level_(level), input_format_(input_format), - output_format_(output_format), is_proto3_(is_proto3) { - auto newTestMessage = [&is_proto3]() { - Message* newMessage; - if (is_proto3) { - newMessage = new TestAllTypesProto3; - } else { - newMessage = new TestAllTypesProto2; - } - return newMessage; - }; - - string input_format_string; - string output_format_string; - string rname = is_proto3 ? ".Proto3" : ".Proto2"; - + : level_(level), + input_format_(input_format), + output_format_(output_format), + prototype_message_(prototype_message), + test_name_(test_name) { switch (input_format) { case conformance::PROTOBUF: { request_.set_protobuf_payload(input); - input_format_string = ".ProtobufInput."; break; } case conformance::JSON: { request_.set_json_payload(input); - input_format_string = ".JsonInput."; break; } @@ -228,39 +112,74 @@ ConformanceTestSuite::ConformanceRequestSetting::ConformanceRequestSetting( GOOGLE_LOG(FATAL) << "Unspecified input format"; } - switch (output_format) { - case conformance::PROTOBUF: { - output_format_string = ".ProtobufOutput"; - break; - } + request_.set_test_category(test_category); - case conformance::JSON: { - output_format_string = ".JsonOutput"; - break; - } + request_.set_message_type(prototype_message.GetDescriptor()->full_name()); + request_.set_requested_output_format(output_format); +} + +Message* ConformanceTestSuite::ConformanceRequestSetting:: + GetTestMessage() const { + return prototype_message_.New(); +} + +string ConformanceTestSuite::ConformanceRequestSetting:: + GetTestName() const { + string rname = + prototype_message_.GetDescriptor()->file()->syntax() == + FileDescriptor::SYNTAX_PROTO3 ? "Proto3" : "Proto2"; + + return StrCat(ConformanceLevelToString(level_), ".", + rname, ".", + InputFormatString(input_format_), + ".", test_name_, ".", + OutputFormatString(output_format_)); +} + +string ConformanceTestSuite::ConformanceRequestSetting:: + ConformanceLevelToString( + ConformanceLevel level) const { + switch (level) { + case REQUIRED: return "Required"; + case RECOMMENDED: return "Recommended"; + } + GOOGLE_LOG(FATAL) << "Unknown value: " << level; + return ""; +} +string ConformanceTestSuite::ConformanceRequestSetting:: + InputFormatString(conformance::WireFormat format) const { + switch (format) { + case conformance::PROTOBUF: + return "ProtobufInput"; + case conformance::JSON: + return "JsonInput"; default: GOOGLE_LOG(FATAL) << "Unspecified output format"; } + return ""; +} - test_name_ = ConformanceLevelToString(level) + rname + - input_format_string + test_name + - output_format_string; - - if (is_proto3) { - request_.set_message_type("protobuf_test_messages.proto3.TestAllTypesProto3"); - } else { - request_.set_message_type("protobuf_test_messages.proto2.TestAllTypesProto2"); +string ConformanceTestSuite::ConformanceRequestSetting:: + OutputFormatString(conformance::WireFormat format) const { + switch (format) { + case conformance::PROTOBUF: + return "ProtobufOutput"; + case conformance::JSON: + return "JsonOutput"; + default: + GOOGLE_LOG(FATAL) << "Unspecified output format"; } - request_.set_requested_output_format(output_format); + return ""; } -Message* ConformanceTestSuite::ConformanceRequestSetting::GetTestMessage() const { - if (is_proto3_) { - return new TestAllTypesProto3(); - } else { - return new TestAllTypesProto2(); - } +void ConformanceTestSuite::SetFailureList( + const string& filename, + const std::vector<string>& failure_list) { + failure_list_filename_ = filename; + expected_to_fail_.clear(); + std::copy(failure_list.begin(), failure_list.end(), + std::inserter(expected_to_fail_, expected_to_fail_.end())); } void ConformanceTestSuite::ReportSuccess(const string& test_name) { @@ -309,41 +228,6 @@ void ConformanceTestSuite::ReportSkip(const string& test_name, skipped_.insert(test_name); } -string ConformanceTestSuite::ConformanceLevelToString(ConformanceLevel level) { - switch (level) { - case REQUIRED: return "Required"; - case RECOMMENDED: return "Recommended"; - } - GOOGLE_LOG(FATAL) << "Unknown value: " << level; - return ""; -} - -void ConformanceTestSuite::RunTest(const string& test_name, - const ConformanceRequest& request, - ConformanceResponse* response) { - if (test_names_.insert(test_name).second == false) { - GOOGLE_LOG(FATAL) << "Duplicated test name: " << test_name; - } - - string serialized_request; - string serialized_response; - request.SerializeToString(&serialized_request); - - runner_->RunTest(test_name, serialized_request, &serialized_response); - - if (!response->ParseFromString(serialized_response)) { - response->Clear(); - response->set_runtime_error("response proto could not be parsed."); - } - - if (verbose_) { - StringAppendF(&output_, "conformance test: name=%s, request=%s, response=%s\n", - test_name.c_str(), - request.ShortDebugString().c_str(), - response->ShortDebugString().c_str()); - } -} - void ConformanceTestSuite::RunValidInputTest( const ConformanceRequestSetting& setting, const string& equivalent_text_format) { @@ -459,348 +343,38 @@ void ConformanceTestSuite::RunValidBinaryInputTest( differences.c_str()); } } -void ConformanceTestSuite::ExpectParseFailureForProtoWithProtoVersion ( - const string& proto, const string& test_name, ConformanceLevel level, - bool isProto3) { - ConformanceRequest request; - ConformanceResponse response; - request.set_protobuf_payload(proto); - if (isProto3) { - request.set_message_type("protobuf_test_messages.proto3.TestAllTypesProto3"); - } else { - request.set_message_type("protobuf_test_messages.proto2.TestAllTypesProto2"); - } - string effective_test_name = ConformanceLevelToString(level) + - (isProto3 ? ".Proto3" : ".Proto2") + - ".ProtobufInput." + test_name; - // 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. - request.set_requested_output_format(conformance::PROTOBUF); - - 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 ConformanceTestSuite::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 ConformanceTestSuite::ExpectHardParseFailureForProto( - const string& proto, const string& test_name, ConformanceLevel level) { - return ExpectParseFailureForProto(proto, test_name, level); -} - -void ConformanceTestSuite::RunValidJsonTest( - const string& test_name, ConformanceLevel level, const string& input_json, - const string& equivalent_text_format) { - ConformanceRequestSetting setting1( - level, conformance::JSON, conformance::PROTOBUF, - true, test_name, input_json); - RunValidInputTest(setting1, equivalent_text_format); - - ConformanceRequestSetting setting2( - level, conformance::JSON, conformance::JSON, - true, test_name, input_json); - RunValidInputTest(setting2, equivalent_text_format); -} - -void ConformanceTestSuite::RunValidJsonTestWithProtobufInput( - const string& test_name, ConformanceLevel level, const TestAllTypesProto3& input, - const string& equivalent_text_format) { - ConformanceRequestSetting setting( - level, conformance::PROTOBUF, conformance::JSON, - true, test_name, input.SerializeAsString()); - RunValidInputTest(setting, equivalent_text_format); -} - -void ConformanceTestSuite::RunValidJsonIgnoreUnknownTest( - const string& test_name, ConformanceLevel level, const string& input_json, - const string& equivalent_text_format) { - ConformanceRequestSetting setting( - level, conformance::JSON, conformance::PROTOBUF, - true, test_name, input_json); - setting.SetIgnoreUnknownJson(true); - RunValidInputTest(setting, equivalent_text_format); -} - -void ConformanceTestSuite::RunValidProtobufTest( - const string& test_name, ConformanceLevel level, - const string& input_protobuf, const string& equivalent_text_format, - bool isProto3) { - ConformanceRequestSetting setting1( - level, conformance::PROTOBUF, conformance::PROTOBUF, - isProto3, test_name, input_protobuf); - RunValidInputTest(setting1, equivalent_text_format); - - if (isProto3) { - ConformanceRequestSetting setting2( - level, conformance::PROTOBUF, conformance::JSON, - true, test_name, input_protobuf); - RunValidInputTest(setting2, equivalent_text_format); - } -} - -void ConformanceTestSuite::RunValidBinaryProtobufTest( - const string& test_name, ConformanceLevel level, - const string& input_protobuf, bool isProto3) { - ConformanceRequestSetting setting( - level, conformance::PROTOBUF, conformance::PROTOBUF, - isProto3, test_name, input_protobuf); - RunValidBinaryInputTest(setting, input_protobuf); -} - -void ConformanceTestSuite::RunValidProtobufTestWithMessage( - const string& test_name, ConformanceLevel level, const Message *input, - const string& equivalent_text_format, bool isProto3) { - RunValidProtobufTest(test_name, level, input->SerializeAsString(), equivalent_text_format, isProto3); -} - -// 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 ConformanceTestSuite::RunValidJsonTestWithValidator( - const string& test_name, ConformanceLevel level, const string& input_json, - const Validator& validator) { - ConformanceRequest request; - ConformanceResponse response; - request.set_json_payload(input_json); - request.set_requested_output_format(conformance::JSON); - request.set_message_type("protobuf_test_messages.proto3.TestAllTypesProto3"); - - string effective_test_name = 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 ConformanceTestSuite::ExpectParseFailureForJson( - const string& test_name, ConformanceLevel level, const string& input_json) { - ConformanceRequest request; - ConformanceResponse response; - request.set_json_payload(input_json); - request.set_message_type("protobuf_test_messages.proto3.TestAllTypesProto3"); - string effective_test_name = - ConformanceLevelToString(level) + ".Proto3.JsonInput." + test_name; - - // 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. - request.set_requested_output_format(conformance::JSON); - - 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 ConformanceTestSuite::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; - - ConformanceRequest request; - ConformanceResponse response; - request.set_protobuf_payload(payload_message.SerializeAsString()); - request.set_message_type("protobuf_test_messages.proto3.TestAllTypesProto3"); - string effective_test_name = - ConformanceLevelToString(level) + "." + test_name + ".JsonOutput"; - request.set_requested_output_format(conformance::JSON); - - 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."); +void ConformanceTestSuite::RunTest(const string& test_name, + const ConformanceRequest& request, + ConformanceResponse* response) { + if (test_names_.insert(test_name).second == false) { + GOOGLE_LOG(FATAL) << "Duplicated test name: " << test_name; } -} - -//TODO: proto2? -void ConformanceTestSuite::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. + string serialized_request; + string serialized_response; + request.SerializeToString(&serialized_request); - // 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); + runner_->RunTest(test_name, serialized_request, &serialized_response); - // EOF in the middle of packed region. - ExpectParseFailureForProto( - cat(tag(rep_field->number(), WireFormatLite::WIRETYPE_LENGTH_DELIMITED), - varint(1)), - "PrematureEofInPackedField" + type_name, REQUIRED); + if (!response->ParseFromString(serialized_response)) { + response->Clear(); + response->set_runtime_error("response proto could not be parsed."); } -} - -void ConformanceTestSuite::TestValidDataForType( - FieldDescriptor::Type type, - std::vector<std::pair<std::string, std::string>> values) { - for (int isProto3 = 0; isProto3 < 2; isProto3++) { - 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, isProto3); - const FieldDescriptor* rep_field = GetFieldForType(type, true, isProto3); - RunValidProtobufTest("ValidDataScalar" + type_name, REQUIRED, - cat(tag(field->number(), wire_type), values[0].first), - field->name() + ": " + values[0].second, isProto3); - - 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, isProto3); - - 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, isProto3); + if (verbose_) { + StringAppendF(&output_, + "conformance test: name=%s, request=%s, response=%s\n", + test_name.c_str(), + request.ShortDebugString().c_str(), + response->ShortDebugString().c_str()); } } -void ConformanceTestSuite::SetFailureList(const string& filename, - const std::vector<string>& failure_list) { - failure_list_filename_ = filename; - expected_to_fail_.clear(); - std::copy(failure_list.begin(), failure_list.end(), - std::inserter(expected_to_fail_, expected_to_fail_.end())); -} - -bool ConformanceTestSuite::CheckSetEmpty(const std::set<string>& set_to_check, - const std::string& write_to_file, - const std::string& msg) { +bool ConformanceTestSuite::CheckSetEmpty( + const std::set<string>& set_to_check, + const std::string& write_to_file, + const std::string& msg) { if (set_to_check.empty()) { return true; } else { @@ -829,70 +403,8 @@ bool ConformanceTestSuite::CheckSetEmpty(const std::set<string>& set_to_check, } } -// TODO: proto2? -void ConformanceTestSuite::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 ConformanceTestSuite::TestOneofMessage (MessageType &message, - bool isProto3) { - message.set_oneof_uint32(0); - RunValidProtobufTestWithMessage( - "OneofZeroUint32", RECOMMENDED, &message, "oneof_uint32: 0", isProto3); - message.mutable_oneof_nested_message()->set_a(0); - RunValidProtobufTestWithMessage( - "OneofZeroMessage", RECOMMENDED, &message, - isProto3 ? "oneof_nested_message: {}" : "oneof_nested_message: {a: 0}", - isProto3); - message.mutable_oneof_nested_message()->set_a(1); - RunValidProtobufTestWithMessage( - "OneofZeroMessageSetTwice", RECOMMENDED, &message, - "oneof_nested_message: {a: 1}", - isProto3); - message.set_oneof_string(""); - RunValidProtobufTestWithMessage( - "OneofZeroString", RECOMMENDED, &message, "oneof_string: \"\"", isProto3); - message.set_oneof_bytes(""); - RunValidProtobufTestWithMessage( - "OneofZeroBytes", RECOMMENDED, &message, "oneof_bytes: \"\"", isProto3); - message.set_oneof_bool(false); - RunValidProtobufTestWithMessage( - "OneofZeroBool", RECOMMENDED, &message, "oneof_bool: false", isProto3); - message.set_oneof_uint64(0); - RunValidProtobufTestWithMessage( - "OneofZeroUint64", RECOMMENDED, &message, "oneof_uint64: 0", isProto3); - message.set_oneof_float(0.0f); - RunValidProtobufTestWithMessage( - "OneofZeroFloat", RECOMMENDED, &message, "oneof_float: 0", isProto3); - message.set_oneof_double(0.0); - RunValidProtobufTestWithMessage( - "OneofZeroDouble", RECOMMENDED, &message, "oneof_double: 0", isProto3); - message.set_oneof_enum(MessageType::FOO); - RunValidProtobufTestWithMessage( - "OneofZeroEnum", RECOMMENDED, &message, "oneof_enum: FOO", isProto3); -} - -template <class MessageType> -void ConformanceTestSuite::TestUnknownMessage(MessageType& message, - bool isProto3) { - message.ParseFromString("\xA8\x1F\x01"); - RunValidBinaryProtobufTest("UnknownVarint", REQUIRED, - message.SerializeAsString(), isProto3); -} - -bool ConformanceTestSuite::RunSuite(ConformanceTestRunner* runner, - std::string* output) { +bool ConformanceTestSuite::RunSuite( + ConformanceTestRunner* runner, std::string* output) { runner_ = runner; successes_ = 0; expected_failures_ = 0; @@ -900,1714 +412,10 @@ bool ConformanceTestSuite::RunSuite(ConformanceTestRunner* runner, test_names_.clear(); unexpected_failing_tests_.clear(); unexpected_succeeding_tests_.clear(); - type_resolver_.reset(NewTypeResolverForDescriptorPool( - kTypeUrlPrefix, DescriptorPool::generated_pool())); - type_url_ = GetTypeUrl(TestAllTypesProto3::descriptor()); output_ = "\nCONFORMANCE TEST BEGIN ====================================\n\n"; - 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( - "ValueAcceptListWithNull", REQUIRED, - R"({"optionalValue": ["x", null, "y"]})", - R"( - optional_value: { - list_value: { - values: { - string_value: "x" - } - values: { - null_value: NULL_VALUE - } - values: { - string_value: "y" - } - } - } - )"); - 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} - })", - ""); + RunSuiteImpl(); bool ok = true; if (!CheckSetEmpty(expected_to_fail_, "nonexistent_tests.txt", diff --git a/conformance/conformance_test.h b/conformance/conformance_test.h index 685f67fb..87387350 100644 --- a/conformance/conformance_test.h +++ b/conformance/conformance_test.h @@ -40,12 +40,13 @@ #include <functional> #include <string> + +#include <google/protobuf/descriptor.h> #include <google/protobuf/stubs/common.h> #include <google/protobuf/util/type_resolver.h> #include <google/protobuf/wire_format_lite.h> #include "conformance.pb.h" -#include "third_party/jsoncpp/json.h" namespace conformance { class ConformanceRequest; @@ -78,7 +79,23 @@ class ConformanceTestRunner { }; // Class representing the test suite itself. To run it, implement your own -// class derived from ConformanceTestRunner and then write code like: +// class derived from ConformanceTestRunner, class derived from +// ConformanceTestSuite and then write code like: +// +// class MyConformanceTestSuite : public ConformanceTestSuite { +// public: +// void RunSuiteImpl() { +// // INSERT ACTURAL TESTS. +// } +// }; +// +// // Force MyConformanceTestSuite to be added at dynamic initialization +// // time. +// struct StaticTestSuiteInitializer { +// StaticTestSuiteInitializer() { +// AddTestSuite(new MyConformanceTestSuite()); +// } +// } static_test_suite_initializer; // // class MyConformanceTestRunner : public ConformanceTestRunner { // public: @@ -89,15 +106,17 @@ class ConformanceTestRunner { // // int main() { // MyConformanceTestRunner runner; -// google::protobuf::ConformanceTestSuite suite; -// -// std::string output; -// suite.RunSuite(&runner, &output); +// const std::set<ConformanceTestSuite*>& test_suite_set = +// ::google::protobuf::GetTestSuiteSet(); +// for (auto suite : test_suite_set) { +// suite->RunSuite(&runner, &output); +// } // } // class ConformanceTestSuite { public: ConformanceTestSuite() : verbose_(false), enforce_recommended_(false) {} + virtual ~ConformanceTestSuite() {} void SetVerbose(bool verbose) { verbose_ = verbose; } @@ -130,7 +149,7 @@ class ConformanceTestSuite { // tests passed. bool RunSuite(ConformanceTestRunner* runner, std::string* output); - private: + protected: // Test cases are classified into a few categories: // REQUIRED: the test case must be passed for an implementation to be // interoperable with other implementations. For example, a @@ -151,38 +170,43 @@ class ConformanceTestSuite { class ConformanceRequestSetting { public: ConformanceRequestSetting( - ConformanceLevel level, conformance::WireFormat input_format, - conformance::WireFormat output_format, bool is_proto3, + ConformanceLevel level, + conformance::WireFormat input_format, + conformance::WireFormat output_format, + conformance::TestCategory test_category, + const Message& prototype_message, const string& test_name, const string& input); + virtual ~ConformanceRequestSetting() {} + + Message* GetTestMessage() const; - Message* GetTestMessage() const; + string GetTestName() const; - const string& GetTestName() const { - return test_name_; - } + const conformance::ConformanceRequest& GetRequest() const { + return request_; + } - const conformance::ConformanceRequest& GetRequest() const { - return request_; - } + const ConformanceLevel GetLevel() const { + return level_; + } - const ConformanceLevel GetLevel() const { - return level_; - } + string ConformanceLevelToString(ConformanceLevel level) const; + + protected: + virtual string InputFormatString(conformance::WireFormat format) const; + virtual string OutputFormatString(conformance::WireFormat format) const; - void SetIgnoreUnknownJson(bool ignore_unknown_json) { - request_.set_ignore_unknown_json(ignore_unknown_json); - } - private: ConformanceLevel level_; - conformance::WireFormat input_format_; - conformance::WireFormat output_format_; - bool is_proto3_; + ::conformance::WireFormat input_format_; + ::conformance::WireFormat output_format_; + const Message& prototype_message_; string test_name_; conformance::ConformanceRequest request_; }; - static string ConformanceLevelToString(ConformanceLevel level); + bool CheckSetEmpty(const std::set<string>& set_to_check, + const std::string& write_to_file, const std::string& msg); void ReportSuccess(const std::string& test_name); void ReportFailure(const string& test_name, @@ -193,73 +217,18 @@ class ConformanceTestSuite { void ReportSkip(const string& test_name, const conformance::ConformanceRequest& request, const conformance::ConformanceResponse& response); - void RunTest(const std::string& test_name, - const conformance::ConformanceRequest& request, - conformance::ConformanceResponse* response); + void RunValidInputTest(const ConformanceRequestSetting& setting, const string& equivalent_text_format); void RunValidBinaryInputTest(const ConformanceRequestSetting& setting, const string& equivalent_wire_format); - void RunValidJsonTest(const string& test_name, - ConformanceLevel level, - const string& input_json, - const string& equivalent_text_format); - void RunValidJsonIgnoreUnknownTest( - 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 RunValidProtobufTest(const string& test_name, ConformanceLevel level, - const string& input_protobuf, - const string& equivalent_text_format, - bool isProto3); - void RunValidBinaryProtobufTest(const string& test_name, - ConformanceLevel level, - const string& input_protobuf, - bool isProto3); - void RunValidProtobufTestWithMessage( - const string& test_name, ConformanceLevel level, - const Message *input, - const string& equivalent_text_format, - bool isProto3); - - 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 isProto3); - 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 isProto3); - template <class MessageType> - void TestUnknownMessage (MessageType &message, - bool isProto3); - void TestValidDataForType( - google::protobuf::FieldDescriptor::Type, - std::vector<std::pair<std::string, std::string>> values); - bool CheckSetEmpty(const std::set<string>& set_to_check, - const std::string& write_to_file, const std::string& msg); + + void RunTest(const std::string& test_name, + const conformance::ConformanceRequest& request, + conformance::ConformanceResponse* response); + + virtual void RunSuiteImpl() = 0; + ConformanceTestRunner* runner_; int successes_; int expected_failures_; @@ -285,10 +254,14 @@ class ConformanceTestSuite { // The set of tests that the testee opted out of; std::set<std::string> skipped_; - std::unique_ptr<google::protobuf::util::TypeResolver> type_resolver_; + std::unique_ptr<google::protobuf::util::TypeResolver> + type_resolver_; std::string type_url_; }; +void AddTestSuite(ConformanceTestSuite* suite); +const std::set<ConformanceTestSuite*>& GetTestSuiteSet(); + } // namespace protobuf } // namespace google diff --git a/conformance/conformance_test_impl.cc b/conformance/conformance_test_impl.cc new file mode 100644 index 00000000..acd0f259 --- /dev/null +++ b/conformance/conformance_test_impl.cc @@ -0,0 +1,2368 @@ +// 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} + })", + ""); +} + +struct StaticTestSuiteInitializer { + StaticTestSuiteInitializer() { + AddTestSuite(new ConformanceTestSuiteImpl()); + } +} static_test_suite_initializer; + +} // namespace protobuf +} // namespace google diff --git a/conformance/conformance_test_runner.cc b/conformance/conformance_test_runner.cc index b0357b87..3340f1cd 100644 --- a/conformance/conformance_test_runner.cc +++ b/conformance/conformance_test_runner.cc @@ -66,9 +66,9 @@ #include "conformance.pb.h" #include "conformance_test.h" -using conformance::ConformanceRequest; using conformance::ConformanceResponse; using google::protobuf::StringAppendF; +using google::protobuf::ConformanceTestSuite; using std::string; using std::vector; @@ -287,7 +287,8 @@ void ParseFailureList(const char *filename, std::vector<string>* failure_list) { int main(int argc, char *argv[]) { char *program; - google::protobuf::ConformanceTestSuite suite; + const std::set<ConformanceTestSuite*>& test_suite_set = + ::google::protobuf::GetTestSuiteSet(); string failure_list_filename; std::vector<string> failure_list; @@ -298,9 +299,13 @@ int main(int argc, char *argv[]) { failure_list_filename = argv[arg]; ParseFailureList(argv[arg], &failure_list); } else if (strcmp(argv[arg], "--verbose") == 0) { - suite.SetVerbose(true); + for (auto *suite : test_suite_set) { + suite->SetVerbose(true); + } } else if (strcmp(argv[arg], "--enforce_recommended") == 0) { - suite.SetEnforceRecommended(true); + for (auto suite : test_suite_set) { + suite->SetEnforceRecommended(true); + } } else if (argv[arg][0] == '-') { fprintf(stderr, "Unknown option: %s\n", argv[arg]); UsageError(); @@ -313,11 +318,16 @@ int main(int argc, char *argv[]) { } } - suite.SetFailureList(failure_list_filename, failure_list); + for (auto suite : test_suite_set) { + suite->SetFailureList(failure_list_filename, failure_list); + } ForkPipeRunner runner(program); std::string output; - bool ok = suite.RunSuite(&runner, &output); + bool ok = true; + for (auto suite : test_suite_set) { + ok &= suite->RunSuite(&runner, &output); + } fwrite(output.c_str(), 1, output.size(), stderr); diff --git a/conformance/failure_list_cpp.txt b/conformance/failure_list_cpp.txt index ea8e8473..752fbb5d 100644 --- a/conformance/failure_list_cpp.txt +++ b/conformance/failure_list_cpp.txt @@ -54,9 +54,3 @@ Required.Proto2.ProtobufInput.PrematureEofInPackedField.SINT32 Required.Proto2.ProtobufInput.PrematureEofInPackedField.SINT64 Required.Proto2.ProtobufInput.PrematureEofInPackedField.UINT32 Required.Proto2.ProtobufInput.PrematureEofInPackedField.UINT64 -Required.Proto3.JsonInput.IgnoreUnknownJsonFalse.ProtobufOutput -Required.Proto3.JsonInput.IgnoreUnknownJsonNull.ProtobufOutput -Required.Proto3.JsonInput.IgnoreUnknownJsonNumber.ProtobufOutput -Required.Proto3.JsonInput.IgnoreUnknownJsonObject.ProtobufOutput -Required.Proto3.JsonInput.IgnoreUnknownJsonString.ProtobufOutput -Required.Proto3.JsonInput.IgnoreUnknownJsonTrue.ProtobufOutput diff --git a/conformance/failure_list_csharp.txt b/conformance/failure_list_csharp.txt index 69c723b5..2a20aa78 100644 --- a/conformance/failure_list_csharp.txt +++ b/conformance/failure_list_csharp.txt @@ -1,8 +1,2 @@ Recommended.Proto3.JsonInput.BytesFieldBase64Url.JsonOutput Recommended.Proto3.JsonInput.BytesFieldBase64Url.ProtobufOutput -Required.Proto3.JsonInput.IgnoreUnknownJsonFalse.ProtobufOutput -Required.Proto3.JsonInput.IgnoreUnknownJsonNull.ProtobufOutput -Required.Proto3.JsonInput.IgnoreUnknownJsonNumber.ProtobufOutput -Required.Proto3.JsonInput.IgnoreUnknownJsonObject.ProtobufOutput -Required.Proto3.JsonInput.IgnoreUnknownJsonString.ProtobufOutput -Required.Proto3.JsonInput.IgnoreUnknownJsonTrue.ProtobufOutput diff --git a/conformance/failure_list_java.txt b/conformance/failure_list_java.txt index 6f085c66..dc1f9ba5 100644 --- a/conformance/failure_list_java.txt +++ b/conformance/failure_list_java.txt @@ -45,9 +45,3 @@ Required.Proto3.ProtobufInput.PrematureEofInDelimitedDataForKnownNonRepeatedValu Required.Proto3.ProtobufInput.PrematureEofInDelimitedDataForKnownRepeatedValue.MESSAGE Required.Proto2.ProtobufInput.PrematureEofInDelimitedDataForKnownNonRepeatedValue.MESSAGE Required.Proto2.ProtobufInput.PrematureEofInDelimitedDataForKnownRepeatedValue.MESSAGE -Required.Proto3.JsonInput.IgnoreUnknownJsonFalse.ProtobufOutput -Required.Proto3.JsonInput.IgnoreUnknownJsonNull.ProtobufOutput -Required.Proto3.JsonInput.IgnoreUnknownJsonNumber.ProtobufOutput -Required.Proto3.JsonInput.IgnoreUnknownJsonObject.ProtobufOutput -Required.Proto3.JsonInput.IgnoreUnknownJsonString.ProtobufOutput -Required.Proto3.JsonInput.IgnoreUnknownJsonTrue.ProtobufOutput diff --git a/conformance/failure_list_php_c.txt b/conformance/failure_list_php_c.txt index 6ecd94fd..5f7c0c8d 100644 --- a/conformance/failure_list_php_c.txt +++ b/conformance/failure_list_php_c.txt @@ -1,6 +1,8 @@ Recommended.FieldMaskNumbersDontRoundTrip.JsonOutput Recommended.FieldMaskPathsDontRoundTrip.JsonOutput Recommended.FieldMaskTooManyUnderscore.JsonOutput +Recommended.Proto3.JsonInput.BytesFieldBase64Url.JsonOutput +Recommended.Proto3.JsonInput.BytesFieldBase64Url.ProtobufOutput Recommended.Proto3.JsonInput.DurationHas3FractionalDigits.Validator Recommended.Proto3.JsonInput.DurationHas6FractionalDigits.Validator Recommended.Proto3.JsonInput.DurationHas9FractionalDigits.Validator @@ -14,6 +16,11 @@ Recommended.Proto3.JsonInput.StringEndsWithEscapeChar Recommended.Proto3.JsonInput.StringFieldSurrogateInWrongOrder Recommended.Proto3.JsonInput.StringFieldUnpairedHighSurrogate Recommended.Proto3.JsonInput.StringFieldUnpairedLowSurrogate +Recommended.Proto3.JsonInput.TimestampHas3FractionalDigits.Validator +Recommended.Proto3.JsonInput.TimestampHas6FractionalDigits.Validator +Recommended.Proto3.JsonInput.TimestampHas9FractionalDigits.Validator +Recommended.Proto3.JsonInput.TimestampHasZeroFractionalDigit.Validator +Recommended.Proto3.JsonInput.TimestampZeroNormalized.Validator Recommended.Proto3.JsonInput.Uint64FieldBeString.Validator Recommended.Proto3.ProtobufInput.OneofZeroBytes.JsonOutput Required.DurationProtoInputTooLarge.JsonOutput @@ -55,6 +62,7 @@ Required.Proto3.JsonInput.FieldMask.ProtobufOutput Required.Proto3.JsonInput.FloatFieldInfinity.JsonOutput Required.Proto3.JsonInput.FloatFieldNan.JsonOutput Required.Proto3.JsonInput.FloatFieldNegativeInfinity.JsonOutput +Required.Proto3.JsonInput.OneofFieldDuplicate Required.Proto3.JsonInput.OptionalBoolWrapper.JsonOutput Required.Proto3.JsonInput.OptionalBoolWrapper.ProtobufOutput Required.Proto3.JsonInput.OptionalBytesWrapper.JsonOutput @@ -103,6 +111,16 @@ Required.Proto3.JsonInput.StringFieldUnicodeEscapeWithLowercaseHexLetters.JsonOu Required.Proto3.JsonInput.StringFieldUnicodeEscapeWithLowercaseHexLetters.ProtobufOutput Required.Proto3.JsonInput.Struct.JsonOutput Required.Proto3.JsonInput.Struct.ProtobufOutput +Required.Proto3.JsonInput.TimestampMaxValue.JsonOutput +Required.Proto3.JsonInput.TimestampMaxValue.ProtobufOutput +Required.Proto3.JsonInput.TimestampMinValue.JsonOutput +Required.Proto3.JsonInput.TimestampMinValue.ProtobufOutput +Required.Proto3.JsonInput.TimestampRepeatedValue.JsonOutput +Required.Proto3.JsonInput.TimestampRepeatedValue.ProtobufOutput +Required.Proto3.JsonInput.TimestampWithNegativeOffset.JsonOutput +Required.Proto3.JsonInput.TimestampWithNegativeOffset.ProtobufOutput +Required.Proto3.JsonInput.TimestampWithPositiveOffset.JsonOutput +Required.Proto3.JsonInput.TimestampWithPositiveOffset.ProtobufOutput Required.Proto3.JsonInput.ValueAcceptBool.JsonOutput Required.Proto3.JsonInput.ValueAcceptBool.ProtobufOutput Required.Proto3.JsonInput.ValueAcceptFloat.JsonOutput @@ -111,6 +129,8 @@ Required.Proto3.JsonInput.ValueAcceptInteger.JsonOutput Required.Proto3.JsonInput.ValueAcceptInteger.ProtobufOutput Required.Proto3.JsonInput.ValueAcceptList.JsonOutput Required.Proto3.JsonInput.ValueAcceptList.ProtobufOutput +Required.Proto3.JsonInput.ValueAcceptListWithNull.JsonOutput +Required.Proto3.JsonInput.ValueAcceptListWithNull.ProtobufOutput Required.Proto3.JsonInput.ValueAcceptNull.JsonOutput Required.Proto3.JsonInput.ValueAcceptNull.ProtobufOutput Required.Proto3.JsonInput.ValueAcceptObject.JsonOutput diff --git a/conformance/failure_list_python-post26.txt b/conformance/failure_list_python-post26.txt index 60b1146e..19d99b04 100644 --- a/conformance/failure_list_python-post26.txt +++ b/conformance/failure_list_python-post26.txt @@ -1,8 +1,2 @@ JsonInput.StringFieldSurrogateInWrongOrder JsonInput.StringFieldUnpairedHighSurrogate -Required.Proto3.JsonInput.IgnoreUnknownJsonFalse.ProtobufOutput -Required.Proto3.JsonInput.IgnoreUnknownJsonNull.ProtobufOutput -Required.Proto3.JsonInput.IgnoreUnknownJsonNumber.ProtobufOutput -Required.Proto3.JsonInput.IgnoreUnknownJsonObject.ProtobufOutput -Required.Proto3.JsonInput.IgnoreUnknownJsonString.ProtobufOutput -Required.Proto3.JsonInput.IgnoreUnknownJsonTrue.ProtobufOutput diff --git a/conformance/failure_list_python.txt b/conformance/failure_list_python.txt index 5f01b53b..e3ce7af7 100644 --- a/conformance/failure_list_python.txt +++ b/conformance/failure_list_python.txt @@ -19,9 +19,3 @@ Required.Proto3.ProtobufInput.IllegalZeroFieldNum_Case_0 Required.Proto3.ProtobufInput.IllegalZeroFieldNum_Case_1 Required.Proto3.ProtobufInput.IllegalZeroFieldNum_Case_2 Required.Proto3.ProtobufInput.IllegalZeroFieldNum_Case_3 -Required.Proto3.JsonInput.IgnoreUnknownJsonFalse.ProtobufOutput -Required.Proto3.JsonInput.IgnoreUnknownJsonNull.ProtobufOutput -Required.Proto3.JsonInput.IgnoreUnknownJsonNumber.ProtobufOutput -Required.Proto3.JsonInput.IgnoreUnknownJsonObject.ProtobufOutput -Required.Proto3.JsonInput.IgnoreUnknownJsonString.ProtobufOutput -Required.Proto3.JsonInput.IgnoreUnknownJsonTrue.ProtobufOutput diff --git a/conformance/failure_list_python_cpp.txt b/conformance/failure_list_python_cpp.txt index f80517d9..a498ad1a 100644 --- a/conformance/failure_list_python_cpp.txt +++ b/conformance/failure_list_python_cpp.txt @@ -52,9 +52,3 @@ Required.Proto2.ProtobufInput.PrematureEofInPackedField.SINT32 Required.Proto2.ProtobufInput.PrematureEofInPackedField.SINT64 Required.Proto2.ProtobufInput.PrematureEofInPackedField.UINT32 Required.Proto2.ProtobufInput.PrematureEofInPackedField.UINT64 -Required.Proto3.JsonInput.IgnoreUnknownJsonFalse.ProtobufOutput -Required.Proto3.JsonInput.IgnoreUnknownJsonNull.ProtobufOutput -Required.Proto3.JsonInput.IgnoreUnknownJsonNumber.ProtobufOutput -Required.Proto3.JsonInput.IgnoreUnknownJsonObject.ProtobufOutput -Required.Proto3.JsonInput.IgnoreUnknownJsonString.ProtobufOutput -Required.Proto3.JsonInput.IgnoreUnknownJsonTrue.ProtobufOutput diff --git a/conformance/failure_list_ruby.txt b/conformance/failure_list_ruby.txt index 2d09acd4..1bab1359 100644 --- a/conformance/failure_list_ruby.txt +++ b/conformance/failure_list_ruby.txt @@ -120,8 +120,6 @@ Required.Proto3.JsonInput.ValueAcceptInteger.JsonOutput Required.Proto3.JsonInput.ValueAcceptInteger.ProtobufOutput Required.Proto3.JsonInput.ValueAcceptList.JsonOutput Required.Proto3.JsonInput.ValueAcceptList.ProtobufOutput -Required.Proto3.JsonInput.ValueAcceptListWithNull.JsonOutput -Required.Proto3.JsonInput.ValueAcceptListWithNull.ProtobufOutput Required.Proto3.JsonInput.ValueAcceptNull.JsonOutput Required.Proto3.JsonInput.ValueAcceptNull.ProtobufOutput Required.Proto3.JsonInput.ValueAcceptObject.JsonOutput @@ -135,9 +133,3 @@ Required.Proto3.ProtobufInput.FloatFieldNormalizeSignalingNan.JsonOutput Required.Proto3.ProtobufInput.ValidDataRepeated.FLOAT.JsonOutput Required.TimestampProtoInputTooLarge.JsonOutput Required.TimestampProtoInputTooSmall.JsonOutput -Required.Proto3.JsonInput.IgnoreUnknownJsonFalse.ProtobufOutput -Required.Proto3.JsonInput.IgnoreUnknownJsonNull.ProtobufOutput -Required.Proto3.JsonInput.IgnoreUnknownJsonNumber.ProtobufOutput -Required.Proto3.JsonInput.IgnoreUnknownJsonObject.ProtobufOutput -Required.Proto3.JsonInput.IgnoreUnknownJsonString.ProtobufOutput -Required.Proto3.JsonInput.IgnoreUnknownJsonTrue.ProtobufOutput |