| // Protocol Buffers - Google's data interchange format |
| // Copyright 2008 Google Inc. All rights reserved. |
| // |
| // Use of this source code is governed by a BSD-style |
| // license that can be found in the LICENSE file or at |
| // https://developers.google.com/open-source/licenses/bsd |
| |
| #include "text_format_conformance_suite.h" |
| |
| #include <cstddef> |
| #include <string> |
| #include <utility> |
| #include <vector> |
| |
| #include "absl/log/absl_log.h" |
| #include "absl/log/die_if_null.h" |
| #include "absl/strings/str_cat.h" |
| #include "absl/strings/str_format.h" |
| #include "conformance_test.h" |
| #include "conformance/test_protos/test_messages_edition2023.pb.h" |
| #include "editions/golden/test_messages_proto2_editions.pb.h" |
| #include "editions/golden/test_messages_proto3_editions.pb.h" |
| #include "google/protobuf/test_messages_proto2.pb.h" |
| #include "google/protobuf/test_messages_proto3.pb.h" |
| #include "google/protobuf/text_format.h" |
| |
| using conformance::ConformanceRequest; |
| using conformance::ConformanceResponse; |
| using conformance::TestStatus; |
| using conformance::WireFormat; |
| using protobuf_test_messages::editions::TestAllTypesEdition2023; |
| using protobuf_test_messages::proto2::TestAllTypesProto2; |
| using protobuf_test_messages::proto2::UnknownToTestAllTypes; |
| using protobuf_test_messages::proto3::TestAllTypesProto3; |
| using TestAllTypesProto2Editions = |
| protobuf_test_messages::editions::proto2::TestAllTypesProto2; |
| using TestAllTypesProto3Editions = |
| protobuf_test_messages::editions::proto3::TestAllTypesProto3; |
| |
| namespace google { |
| namespace protobuf { |
| |
| // The number of repetitions to use for performance tests. |
| // Corresponds approx to 500KB wireformat bytes. |
| static const size_t kPerformanceRepeatCount = 50000; |
| |
| TextFormatConformanceTestSuite::TextFormatConformanceTestSuite() { |
| SetFailureListFlagName("--text_format_failure_list"); |
| } |
| |
| bool TextFormatConformanceTestSuite::ParseTextFormatResponse( |
| const ConformanceResponse& response, |
| const ConformanceRequestSetting& setting, Message* test_message) { |
| TextFormat::Parser parser; |
| const ConformanceRequest& request = setting.GetRequest(); |
| if (request.print_unknown_fields()) { |
| parser.AllowFieldNumber(true); |
| } |
| if (!parser.ParseFromString(response.text_payload(), test_message)) { |
| ABSL_LOG(ERROR) << "INTERNAL ERROR: internal text->protobuf transcode " |
| << "yielded unparseable proto. Text payload: " |
| << response.text_payload(); |
| return false; |
| } |
| |
| return true; |
| } |
| |
| bool TextFormatConformanceTestSuite::ParseResponse( |
| const ConformanceResponse& response, |
| const ConformanceRequestSetting& setting, Message* test_message) { |
| const ConformanceRequest& request = setting.GetRequest(); |
| WireFormat requested_output = request.requested_output_format(); |
| const std::string& test_name = setting.GetTestName(); |
| ConformanceLevel level = setting.GetLevel(); |
| |
| TestStatus test; |
| test.set_name(test_name); |
| switch (response.result_case()) { |
| case ConformanceResponse::kProtobufPayload: { |
| if (requested_output != conformance::PROTOBUF) { |
| test.set_failure_message(absl::StrCat( |
| "Test was asked for ", WireFormatToString(requested_output), |
| " output but provided PROTOBUF instead.")); |
| ReportFailure(test, level, request, response); |
| return false; |
| } |
| |
| if (!test_message->ParseFromString(response.protobuf_payload())) { |
| test.set_failure_message( |
| "Protobuf output we received from test was unparseable."); |
| ReportFailure(test, level, request, response); |
| return false; |
| } |
| |
| break; |
| } |
| |
| case ConformanceResponse::kTextPayload: { |
| if (requested_output != conformance::TEXT_FORMAT) { |
| test.set_failure_message(absl::StrCat( |
| "Test was asked for ", WireFormatToString(requested_output), |
| " output but provided TEXT_FORMAT instead.")); |
| ReportFailure(test, level, request, response); |
| return false; |
| } |
| |
| if (!ParseTextFormatResponse(response, setting, test_message)) { |
| test.set_failure_message( |
| "TEXT_FORMAT output we received from test was unparseable."); |
| ReportFailure(test, level, request, response); |
| return false; |
| } |
| |
| break; |
| } |
| |
| default: |
| ABSL_LOG(FATAL) << test_name |
| << ": unknown payload type: " << response.result_case(); |
| } |
| |
| return true; |
| } |
| |
| void TextFormatConformanceTestSuite::RunSuiteImpl() { |
| TextFormatConformanceTestSuiteImpl<TestAllTypesProto2>(this); |
| TextFormatConformanceTestSuiteImpl<TestAllTypesProto3>(this); |
| if (maximum_edition_ >= Edition::EDITION_2023) { |
| TextFormatConformanceTestSuiteImpl<TestAllTypesProto2Editions>(this); |
| TextFormatConformanceTestSuiteImpl<TestAllTypesProto3Editions>(this); |
| TextFormatConformanceTestSuiteImpl<TestAllTypesEdition2023>(this); |
| } |
| } |
| |
| template <typename MessageType> |
| TextFormatConformanceTestSuiteImpl<MessageType>:: |
| TextFormatConformanceTestSuiteImpl(TextFormatConformanceTestSuite* suite) |
| : suite_(*ABSL_DIE_IF_NULL(suite)) { |
| // Flag control performance tests to keep them internal and opt-in only |
| if (suite_.performance_) { |
| if (MessageType::GetDescriptor()->name() == "TestAllTypesEdition2023") { |
| // There are no editions-sensitive performance tests. |
| return; |
| } |
| RunTextFormatPerformanceTests(); |
| } else { |
| if (MessageType::GetDescriptor()->name() == "TestAllTypesProto2") { |
| RunGroupTests(); |
| RunClosedEnumTests(); |
| } |
| if (MessageType::GetDescriptor()->name() == "TestAllTypesEdition2023") { |
| RunDelimitedTests(); |
| } |
| if (MessageType::GetDescriptor()->name() == "TestAllTypesProto3") { |
| RunAnyTests(); |
| RunOpenEnumTests(); |
| // TODO Run these over proto2 also. |
| RunAllTests(); |
| } |
| } |
| } |
| |
| template <typename MessageType> |
| void TextFormatConformanceTestSuiteImpl<MessageType>::ExpectParseFailure( |
| const std::string& test_name, ConformanceLevel level, |
| const std::string& input) { |
| MessageType 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::TEXT_FORMAT, conformance::TEXT_FORMAT, |
| conformance::TEXT_FORMAT_TEST, prototype, test_name, input); |
| const ConformanceRequest& request = setting.GetRequest(); |
| ConformanceResponse response; |
| std::string effective_test_name = absl::StrCat( |
| setting.ConformanceLevelToString(level), ".", |
| setting.GetSyntaxIdentifier(), ".TextFormatInput.", test_name); |
| |
| if (!suite_.RunTest(effective_test_name, request, &response)) { |
| return; |
| } |
| |
| TestStatus test; |
| test.set_name(effective_test_name); |
| if (response.result_case() == ConformanceResponse::kParseError) { |
| suite_.ReportSuccess(test); |
| } else if (response.result_case() == ConformanceResponse::kSkipped) { |
| suite_.ReportSkip(test, request, response); |
| } else { |
| test.set_failure_message("Should have failed to parse, but didn't."); |
| suite_.ReportFailure(test, level, request, response); |
| } |
| } |
| |
| template <typename MessageType> |
| void TextFormatConformanceTestSuiteImpl<MessageType>::RunValidTextFormatTest( |
| const std::string& test_name, ConformanceLevel level, |
| const std::string& input_text) { |
| MessageType prototype; |
| RunValidTextFormatTestWithMessage(test_name, level, input_text, prototype); |
| } |
| |
| template <typename MessageType> |
| void TextFormatConformanceTestSuiteImpl<MessageType>:: |
| RunValidTextFormatTestWithMessage(const std::string& test_name, |
| ConformanceLevel level, |
| const std::string& input_text, |
| const Message& message) { |
| ConformanceRequestSetting setting1( |
| level, conformance::TEXT_FORMAT, conformance::PROTOBUF, |
| conformance::TEXT_FORMAT_TEST, message, test_name, input_text); |
| suite_.RunValidInputTest(setting1, input_text); |
| ConformanceRequestSetting setting2( |
| level, conformance::TEXT_FORMAT, conformance::TEXT_FORMAT, |
| conformance::TEXT_FORMAT_TEST, message, test_name, input_text); |
| suite_.RunValidInputTest(setting2, input_text); |
| } |
| |
| template <typename MessageType> |
| void TextFormatConformanceTestSuiteImpl<MessageType>:: |
| RunValidTextFormatTestWithExpected(const std::string& test_name, |
| ConformanceLevel level, |
| const std::string& input_text, |
| const std::string& expected_text) { |
| MessageType prototype; |
| ConformanceRequestSetting setting1( |
| level, conformance::TEXT_FORMAT, conformance::PROTOBUF, |
| conformance::TEXT_FORMAT_TEST, prototype, test_name, input_text); |
| suite_.RunValidInputTest(setting1, expected_text); |
| ConformanceRequestSetting setting2( |
| level, conformance::TEXT_FORMAT, conformance::TEXT_FORMAT, |
| conformance::TEXT_FORMAT_TEST, prototype, test_name, input_text); |
| suite_.RunValidInputTest(setting2, expected_text); |
| } |
| |
| template <typename MessageType> |
| void TextFormatConformanceTestSuiteImpl< |
| MessageType>::RunValidUnknownTextFormatTest(const std::string& test_name, |
| const Message& message) { |
| std::string serialized_input; |
| message.SerializeToString(&serialized_input); |
| MessageType prototype; |
| ConformanceRequestSetting setting1( |
| RECOMMENDED, conformance::PROTOBUF, conformance::TEXT_FORMAT, |
| conformance::TEXT_FORMAT_TEST, prototype, |
| absl::StrCat(test_name, "_Drop"), serialized_input); |
| setting1.SetPrototypeMessageForCompare(message); |
| suite_.RunValidBinaryInputTest(setting1, ""); |
| |
| ConformanceRequestSetting setting2( |
| RECOMMENDED, conformance::PROTOBUF, conformance::TEXT_FORMAT, |
| conformance::TEXT_FORMAT_TEST, prototype, |
| absl::StrCat(test_name, "_Print"), serialized_input); |
| setting2.SetPrototypeMessageForCompare(message); |
| setting2.SetPrintUnknownFields(true); |
| suite_.RunValidBinaryInputTest(setting2, serialized_input); |
| } |
| |
| template <typename MessageType> |
| void TextFormatConformanceTestSuiteImpl<MessageType>::RunDelimitedTests() { |
| RunValidTextFormatTest("GroupFieldNoColon", REQUIRED, |
| "GroupLikeType { group_int32: 1 }"); |
| RunValidTextFormatTest("GroupFieldWithColon", REQUIRED, |
| "GroupLikeType: { group_int32: 1 }"); |
| RunValidTextFormatTest("GroupFieldEmpty", REQUIRED, "GroupLikeType {}"); |
| RunValidTextFormatTest( |
| "GroupFieldExtension", REQUIRED, |
| "[protobuf_test_messages.editions.groupliketype] { c: 1 }"); |
| RunValidTextFormatTest( |
| "DelimitedFieldExtension", REQUIRED, |
| "[protobuf_test_messages.editions.delimited_ext] { c: 1 }"); |
| |
| |
| // Test that lower-cased group name (i.e. implicit field name) are accepted. |
| RunValidTextFormatTest("DelimitedFieldLowercased", REQUIRED, |
| "groupliketype { group_int32: 1 }"); |
| RunValidTextFormatTest("DelimitedFieldLowercasedDifferent", REQUIRED, |
| "delimited_field { group_int32: 1 }"); |
| |
| // Extensions always used the field name, and should never accept the message |
| // name. |
| ExpectParseFailure( |
| "DelimitedFieldExtensionMessageName", REQUIRED, |
| "[protobuf_test_messages.editions.GroupLikeType] { group_int32: 1 }"); |
| } |
| |
| template <typename MessageType> |
| void TextFormatConformanceTestSuiteImpl<MessageType>::RunGroupTests() { |
| RunValidTextFormatTest("GroupFieldNoColon", REQUIRED, |
| "Data { group_int32: 1 }"); |
| RunValidTextFormatTest("GroupFieldWithColon", REQUIRED, |
| "Data: { group_int32: 1 }"); |
| RunValidTextFormatTest("GroupFieldEmpty", REQUIRED, "Data {}"); |
| RunValidTextFormatTest("GroupFieldMultiWord", REQUIRED, |
| "MultiWordGroupField { group_int32: 1 }"); |
| |
| // Test that lower-cased group name (i.e. implicit field name) is accepted |
| RunValidTextFormatTest("GroupFieldLowercased", REQUIRED, |
| "data { group_int32: 1 }"); |
| RunValidTextFormatTest("GroupFieldLowercasedMultiWord", REQUIRED, |
| "multiwordgroupfield { group_int32: 1 }"); |
| |
| // Test extensions of group type |
| RunValidTextFormatTest("GroupFieldExtension", REQUIRED, |
| absl::StrFormat("[%s] { group_int32: 1 }", |
| MessageType::GetDescriptor() |
| ->file() |
| ->FindExtensionByName("groupfield") |
| ->PrintableNameForExtension())); |
| ExpectParseFailure("GroupFieldExtensionGroupName", REQUIRED, |
| absl::StrFormat("[%s] { group_int32: 1 }", |
| MessageType::GetDescriptor() |
| ->file() |
| ->FindMessageTypeByName("GroupField") |
| ->full_name())); |
| } |
| |
| template <typename MessageType> |
| void TextFormatConformanceTestSuiteImpl<MessageType>::RunAllTests() { |
| RunValidTextFormatTest("HelloWorld", REQUIRED, |
| "optional_string: 'Hello, World!'"); |
| // Integer fields. |
| RunValidTextFormatTest("Int32FieldMaxValue", REQUIRED, |
| "optional_int32: 2147483647"); |
| RunValidTextFormatTest("Int32FieldMinValue", REQUIRED, |
| "optional_int32: -2147483648"); |
| RunValidTextFormatTest("Uint32FieldMaxValue", REQUIRED, |
| "optional_uint32: 4294967295"); |
| RunValidTextFormatTest("Int64FieldMaxValue", REQUIRED, |
| "optional_int64: 9223372036854775807"); |
| RunValidTextFormatTest("Int64FieldMinValue", REQUIRED, |
| "optional_int64: -9223372036854775808"); |
| RunValidTextFormatTest("Uint64FieldMaxValue", REQUIRED, |
| "optional_uint64: 18446744073709551615"); |
| // Integer fields - Hex |
| RunValidTextFormatTestWithExpected("Int32FieldMaxValueHex", REQUIRED, |
| "optional_int32: 0x7FFFFFFF", |
| "optional_int32: 2147483647"); |
| RunValidTextFormatTestWithExpected("Int32FieldMinValueHex", REQUIRED, |
| "optional_int32: -0x80000000", |
| "optional_int32: -2147483648"); |
| RunValidTextFormatTestWithExpected("Uint32FieldMaxValueHex", REQUIRED, |
| "optional_uint32: 0xFFFFFFFF", |
| "optional_uint32: 4294967295"); |
| RunValidTextFormatTestWithExpected("Int64FieldMaxValueHex", REQUIRED, |
| "optional_int64: 0x7FFFFFFFFFFFFFFF", |
| "optional_int64: 9223372036854775807"); |
| RunValidTextFormatTestWithExpected("Int64FieldMinValueHex", REQUIRED, |
| "optional_int64: -0x8000000000000000", |
| "optional_int64: -9223372036854775808"); |
| RunValidTextFormatTestWithExpected("Uint64FieldMaxValueHex", REQUIRED, |
| "optional_uint64: 0xFFFFFFFFFFFFFFFF", |
| "optional_uint64: 18446744073709551615"); |
| // Integer fields - Octal |
| RunValidTextFormatTestWithExpected("Int32FieldMaxValueOctal", REQUIRED, |
| "optional_int32: 017777777777", |
| "optional_int32: 2147483647"); |
| RunValidTextFormatTestWithExpected("Int32FieldMinValueOctal", REQUIRED, |
| "optional_int32: -020000000000", |
| "optional_int32: -2147483648"); |
| RunValidTextFormatTestWithExpected("Uint32FieldMaxValueOctal", REQUIRED, |
| "optional_uint32: 037777777777", |
| "optional_uint32: 4294967295"); |
| RunValidTextFormatTestWithExpected("Int64FieldMaxValueOctal", REQUIRED, |
| "optional_int64: 0777777777777777777777", |
| "optional_int64: 9223372036854775807"); |
| RunValidTextFormatTestWithExpected("Int64FieldMinValueOctal", REQUIRED, |
| "optional_int64: -01000000000000000000000", |
| "optional_int64: -9223372036854775808"); |
| RunValidTextFormatTestWithExpected("Uint64FieldMaxValueOctal", REQUIRED, |
| "optional_uint64: 01777777777777777777777", |
| "optional_uint64: 18446744073709551615"); |
| |
| // Parsers reject out-of-bound integer values. |
| ExpectParseFailure("Int32FieldTooLarge", REQUIRED, |
| "optional_int32: 2147483648"); |
| ExpectParseFailure("Int32FieldTooSmall", REQUIRED, |
| "optional_int32: -2147483649"); |
| ExpectParseFailure("Uint32FieldTooLarge", REQUIRED, |
| "optional_uint32: 4294967296"); |
| ExpectParseFailure("Int64FieldTooLarge", REQUIRED, |
| "optional_int64: 9223372036854775808"); |
| ExpectParseFailure("Int64FieldTooSmall", REQUIRED, |
| "optional_int64: -9223372036854775809"); |
| ExpectParseFailure("Uint64FieldTooLarge", REQUIRED, |
| "optional_uint64: 18446744073709551616"); |
| // Parsers reject out-of-bound integer values - Hex |
| ExpectParseFailure("Int32FieldTooLargeHex", REQUIRED, |
| "optional_int32: 0x80000000"); |
| ExpectParseFailure("Int32FieldTooSmallHex", REQUIRED, |
| "optional_int32: -0x80000001"); |
| ExpectParseFailure("Uint32FieldTooLargeHex", REQUIRED, |
| "optional_uint32: 0x100000000"); |
| ExpectParseFailure("Int64FieldTooLargeHex", REQUIRED, |
| "optional_int64: 0x8000000000000000"); |
| ExpectParseFailure("Int64FieldTooSmallHex", REQUIRED, |
| "optional_int64: -0x8000000000000001"); |
| ExpectParseFailure("Uint64FieldTooLargeHex", REQUIRED, |
| "optional_uint64: 0x10000000000000000"); |
| // Parsers reject out-of-bound integer values - Octal |
| ExpectParseFailure("Int32FieldTooLargeOctal", REQUIRED, |
| "optional_int32: 020000000000"); |
| ExpectParseFailure("Int32FieldTooSmallOctal", REQUIRED, |
| "optional_int32: -020000000001"); |
| ExpectParseFailure("Uint32FieldTooLargeOctal", REQUIRED, |
| "optional_uint32: 040000000000"); |
| ExpectParseFailure("Int64FieldTooLargeOctal", REQUIRED, |
| "optional_int64: 01000000000000000000000"); |
| ExpectParseFailure("Int64FieldTooSmallOctal", REQUIRED, |
| "optional_int64: -01000000000000000000001"); |
| ExpectParseFailure("Uint64FieldTooLargeOctal", REQUIRED, |
| "optional_uint64: 02000000000000000000000"); |
| |
| // Floating point fields |
| for (const auto& suffix : std::vector<std::string>{"", "f", "F"}) { |
| const std::string name_suffix = |
| suffix.empty() ? "" : absl::StrCat("_", suffix); |
| |
| RunValidTextFormatTest(absl::StrCat("FloatField", name_suffix), REQUIRED, |
| absl::StrCat("optional_float: 3.192837", suffix)); |
| RunValidTextFormatTestWithExpected( |
| absl::StrCat("FloatFieldZero", name_suffix), REQUIRED, |
| absl::StrCat("optional_float: 0", suffix), |
| "" /* implicit presence, so zero means unset*/); |
| RunValidTextFormatTest(absl::StrCat("FloatFieldNegative", name_suffix), |
| REQUIRED, |
| absl::StrCat("optional_float: -3.192837", suffix)); |
| RunValidTextFormatTest( |
| absl::StrCat("FloatFieldWithVeryPreciseNumber", name_suffix), REQUIRED, |
| absl::StrCat("optional_float: 3.123456789123456789", suffix)); |
| RunValidTextFormatTest( |
| absl::StrCat("FloatFieldMaxValue", name_suffix), REQUIRED, |
| absl::StrCat("optional_float: 3.4028235e+38", suffix)); |
| RunValidTextFormatTest(absl::StrCat("FloatFieldMinValue", name_suffix), |
| REQUIRED, |
| absl::StrCat("optional_float: 1.17549e-38", suffix)); |
| RunValidTextFormatTest(absl::StrCat("FloatFieldWithInt32Max", name_suffix), |
| REQUIRED, |
| absl::StrCat("optional_float: 4294967296", suffix)); |
| RunValidTextFormatTest( |
| absl::StrCat("FloatFieldLargerThanInt64", name_suffix), REQUIRED, |
| absl::StrCat("optional_float: 9223372036854775808", suffix)); |
| RunValidTextFormatTest( |
| absl::StrCat("FloatFieldTooLarge", name_suffix), REQUIRED, |
| absl::StrCat("optional_float: 3.4028235e+39", suffix)); |
| RunValidTextFormatTest(absl::StrCat("FloatFieldTooSmall", name_suffix), |
| REQUIRED, |
| absl::StrCat("optional_float: 1.17549e-39", suffix)); |
| RunValidTextFormatTest( |
| absl::StrCat("FloatFieldLargerThanUint64", name_suffix), REQUIRED, |
| absl::StrCat("optional_float: 18446744073709551616", suffix)); |
| // https://protobuf.dev/reference/protobuf/textformat-spec/#literals says |
| // "-0" is a valid float literal. -0 should be considered not the same as 0 |
| // when considering implicit presence, and so should round trip. |
| RunValidTextFormatTest(absl::StrCat("FloatFieldNegativeZero", name_suffix), |
| REQUIRED, |
| absl::StrCat("optional_float: -0", suffix)); |
| // https://protobuf.dev/reference/protobuf/textformat-spec/#literals says |
| // ".123", "-.123", ".123e2" are a valid float literal. |
| RunValidTextFormatTest(absl::StrCat("FloatFieldNoLeadingZero", name_suffix), |
| REQUIRED, |
| absl::StrCat("optional_float: .123", suffix)); |
| RunValidTextFormatTest( |
| absl::StrCat("FloatFieldNegativeNoLeadingZero", name_suffix), REQUIRED, |
| absl::StrCat("optional_float: -.123", suffix)); |
| RunValidTextFormatTest( |
| absl::StrCat("FloatFieldNoLeadingZeroWithExponent", name_suffix), |
| REQUIRED, absl::StrCat("optional_float: .123e2", suffix)); |
| } |
| // https://protobuf.dev/reference/protobuf/textformat-spec/#value say case |
| // doesn't matter for special values, test a few |
| for (const auto& value : std::vector<std::string>{"nan", "NaN", "nAn"}) { |
| RunValidTextFormatTest(absl::StrCat("FloatFieldValue_", value), REQUIRED, |
| absl::StrCat("optional_float: ", value)); |
| } |
| for (const auto& value : std::vector<std::string>{ |
| "inf", "infinity", "INF", "INFINITY", "iNF", "inFINITY"}) { |
| RunValidTextFormatTest(absl::StrCat("FloatFieldValue_Pos", value), REQUIRED, |
| absl::StrCat("optional_float: ", value)); |
| RunValidTextFormatTest(absl::StrCat("FloatFieldValue_Neg", value), REQUIRED, |
| absl::StrCat("optional_float: -", value)); |
| } |
| // https://protobuf.dev/reference/protobuf/textformat-spec/#numeric and |
| // https://protobuf.dev/reference/protobuf/textformat-spec/#value says |
| // hex or octal float literals are invalid. |
| ExpectParseFailure("FloatFieldNoHex", REQUIRED, "optional_float: 0x1"); |
| ExpectParseFailure("FloatFieldNoNegativeHex", REQUIRED, |
| "optional_float: -0x1"); |
| ExpectParseFailure("FloatFieldNoOctal", REQUIRED, "optional_float: 012"); |
| ExpectParseFailure("FloatFieldNoNegativeOctal", REQUIRED, |
| "optional_float: -012"); |
| // https://protobuf.dev/reference/protobuf/textformat-spec/#value says |
| // overflows are mapped to infinity/-infinity. |
| RunValidTextFormatTestWithExpected("FloatFieldOverflowInfinity", REQUIRED, |
| "optional_float: 1e50", |
| "optional_float: inf"); |
| RunValidTextFormatTestWithExpected("FloatFieldOverflowNegativeInfinity", |
| REQUIRED, "optional_float: -1e50", |
| "optional_float: -inf"); |
| RunValidTextFormatTestWithExpected("DoubleFieldOverflowInfinity", REQUIRED, |
| "optional_double: 1e9999", |
| "optional_double: inf"); |
| RunValidTextFormatTestWithExpected("DoubleFieldOverflowNegativeInfinity", |
| REQUIRED, "optional_double: -1e9999", |
| "optional_double: -inf"); |
| // Exponent is one more than uint64 max. |
| RunValidTextFormatTestWithExpected( |
| "FloatFieldOverflowInfinityHugeExponent", REQUIRED, |
| "optional_float: 1e18446744073709551616", "optional_float: inf"); |
| RunValidTextFormatTestWithExpected( |
| "DoubleFieldOverflowInfinityHugeExponent", REQUIRED, |
| "optional_double: 1e18446744073709551616", "optional_double: inf"); |
| RunValidTextFormatTestWithExpected( |
| "DoubleFieldLargeNegativeExponentParsesAsZero", REQUIRED, |
| "optional_double: 1e-18446744073709551616", ""); |
| RunValidTextFormatTestWithExpected( |
| "NegDoubleFieldLargeNegativeExponentParsesAsNegZero", REQUIRED, |
| "optional_double: -1e-18446744073709551616", "optional_double: -0"); |
| |
| RunValidTextFormatTestWithExpected( |
| "FloatFieldLargeNegativeExponentParsesAsZero", REQUIRED, |
| "optional_float: 1e-50", ""); |
| RunValidTextFormatTestWithExpected( |
| "NegFloatFieldLargeNegativeExponentParsesAsNegZero", REQUIRED, |
| "optional_float: -1e-50", "optional_float: -0"); |
| |
| // String literals x {Strings, Bytes} |
| for (const auto& field_type : std::vector<std::string>{"String", "Bytes"}) { |
| const std::string field_name = |
| field_type == "String" ? "optional_string" : "optional_bytes"; |
| RunValidTextFormatTest( |
| absl::StrCat("StringLiteralConcat", field_type), REQUIRED, |
| absl::StrCat(field_name, ": 'first' \"second\"\n'third'")); |
| RunValidTextFormatTest( |
| absl::StrCat("StringLiteralBasicEscapes", field_type), REQUIRED, |
| absl::StrCat(field_name, ": '\\a\\b\\f\\n\\r\\t\\v\\?\\\\\\'\\\"'")); |
| RunValidTextFormatTest( |
| absl::StrCat("StringLiteralOctalEscapes", field_type), REQUIRED, |
| absl::StrCat(field_name, ": '\\341\\210\\264'")); |
| RunValidTextFormatTest(absl::StrCat("StringLiteralHexEscapes", field_type), |
| REQUIRED, |
| absl::StrCat(field_name, ": '\\xe1\\x88\\xb4'")); |
| RunValidTextFormatTest( |
| absl::StrCat("StringLiteralShortUnicodeEscape", field_type), |
| RECOMMENDED, absl::StrCat(field_name, ": '\\u1234'")); |
| RunValidTextFormatTest( |
| absl::StrCat("StringLiteralLongUnicodeEscapes", field_type), |
| RECOMMENDED, absl::StrCat(field_name, ": '\\U00001234\\U00010437'")); |
| // String literals don't include line feeds. |
| ExpectParseFailure(absl::StrCat("StringLiteralIncludesLF", field_type), |
| REQUIRED, |
| absl::StrCat(field_name, ": 'first line\nsecond line'")); |
| // Unicode escapes don't include code points that lie beyond the planes |
| // (> 0x10ffff). |
| ExpectParseFailure( |
| absl::StrCat("StringLiteralLongUnicodeEscapeTooLarge", field_type), |
| REQUIRED, absl::StrCat(field_name, ": '\\U00110000'")); |
| // Unicode escapes don't include surrogates. |
| ExpectParseFailure( |
| absl::StrCat("StringLiteralShortUnicodeEscapeSurrogatePair", |
| field_type), |
| RECOMMENDED, absl::StrCat(field_name, ": '\\ud801\\udc37'")); |
| ExpectParseFailure( |
| absl::StrCat("StringLiteralShortUnicodeEscapeSurrogateFirstOnly", |
| field_type), |
| RECOMMENDED, absl::StrCat(field_name, ": '\\ud800'")); |
| ExpectParseFailure( |
| absl::StrCat("StringLiteralShortUnicodeEscapeSurrogateSecondOnly", |
| field_type), |
| RECOMMENDED, absl::StrCat(field_name, ": '\\udc00'")); |
| ExpectParseFailure( |
| absl::StrCat("StringLiteralLongUnicodeEscapeSurrogateFirstOnly", |
| field_type), |
| RECOMMENDED, absl::StrCat(field_name, ": '\\U0000d800'")); |
| ExpectParseFailure( |
| absl::StrCat("StringLiteralLongUnicodeEscapeSurrogateSecondOnly", |
| field_type), |
| RECOMMENDED, absl::StrCat(field_name, ": '\\U0000dc00'")); |
| ExpectParseFailure( |
| absl::StrCat("StringLiteralLongUnicodeEscapeSurrogatePair", field_type), |
| RECOMMENDED, absl::StrCat(field_name, ": '\\U0000d801\\U00000dc37'")); |
| ExpectParseFailure( |
| absl::StrCat("StringLiteralUnicodeEscapeSurrogatePairLongShort", |
| field_type), |
| RECOMMENDED, absl::StrCat(field_name, ": '\\U0000d801\\udc37'")); |
| ExpectParseFailure( |
| absl::StrCat("StringLiteralUnicodeEscapeSurrogatePairShortLong", |
| field_type), |
| RECOMMENDED, absl::StrCat(field_name, ": '\\ud801\\U0000dc37'")); |
| |
| // The following method depend on the type of field, as strings have extra |
| // validation. |
| const auto test_method = |
| field_type == "String" |
| ? &TextFormatConformanceTestSuiteImpl::ExpectParseFailure |
| : &TextFormatConformanceTestSuiteImpl::RunValidTextFormatTest; |
| |
| // String fields reject invalid UTF-8 byte sequences; bytes fields don't. |
| (this->*test_method)(absl::StrCat(field_type, "FieldBadUTF8Octal"), |
| REQUIRED, absl::StrCat(field_name, ": '\\300'")); |
| (this->*test_method)(absl::StrCat(field_type, "FieldBadUTF8Hex"), REQUIRED, |
| absl::StrCat(field_name, ": '\\xc0'")); |
| } |
| |
| // Separators |
| for (const auto& test_case : std::vector<std::pair<std::string, std::string>>{ |
| {"string", "\"abc\""}, |
| {"bytes", "\"abc\""}, |
| {"int32", "123"}, |
| {"bool", "true"}, |
| {"double", "1.23"}, |
| {"fixed32", "0x123"}, |
| }) { |
| // Optional Field Separators |
| for (const auto& field_type : |
| std::vector<std::string>{"Single", "Repeated"}) { |
| std::string field_name, field_value; |
| if (field_type == "Single") { |
| field_name = absl::StrCat("optional_", test_case.first); |
| field_value = test_case.second; |
| } else { |
| field_name = absl::StrCat("repeated_", test_case.first); |
| field_value = absl::StrCat("[", test_case.second, "]"); |
| } |
| |
| RunValidTextFormatTest(absl::StrCat("FieldSeparatorCommaTopLevel", |
| field_type, "_", test_case.first), |
| REQUIRED, |
| absl::StrCat(field_name, ": ", field_value, ",")); |
| RunValidTextFormatTest(absl::StrCat("FieldSeparatorSemiTopLevelSingle", |
| field_type, "_", test_case.first), |
| REQUIRED, |
| absl::StrCat(field_name, ": ", field_value, ";")); |
| |
| ExpectParseFailure( |
| absl::StrCat("FieldSeparatorCommaTopLevelDuplicatesFails", field_type, |
| "_", test_case.first), |
| REQUIRED, absl::StrCat(field_name, ": ", field_value, ",,")); |
| ExpectParseFailure( |
| absl::StrCat("FieldSeparatorSemiTopLevelDuplicateFails", field_type, |
| "_", test_case.first), |
| REQUIRED, absl::StrCat(field_name, ": ", field_value, ";;")); |
| } |
| |
| // Required List Separators |
| RunValidTextFormatTest( |
| absl::StrCat("ListSeparator_", test_case.first), REQUIRED, |
| absl::StrCat("repeated_", test_case.first, ": [", test_case.second, ",", |
| test_case.second, "]")); |
| ExpectParseFailure( |
| absl::StrCat("ListSeparatorSemiFails_", test_case.first), REQUIRED, |
| absl::StrCat("repeated_", test_case.first, ": [", test_case.second, ";", |
| test_case.second, "]")); |
| // For string and bytes, if we skip the separator, the parser will treat |
| // the two values as a single value. |
| if (test_case.first == "string" || test_case.first == "bytes") { |
| RunValidTextFormatTest( |
| absl::StrCat("ListSeparatorMissingIsOneValue_", test_case.first), |
| REQUIRED, |
| absl::StrCat("repeated_", test_case.first, ": [", test_case.second, |
| " ", test_case.second, "]")); |
| } else { |
| ExpectParseFailure( |
| absl::StrCat("ListSeparatorMissingFails_", test_case.first), REQUIRED, |
| absl::StrCat("repeated_", test_case.first, ": [", test_case.second, |
| " ", test_case.second, "]")); |
| } |
| ExpectParseFailure( |
| absl::StrCat("ListSeparatorDuplicateFails_", test_case.first), REQUIRED, |
| absl::StrCat("repeated_", test_case.first, ": [", test_case.second, |
| ",,", test_case.second, "]")); |
| ExpectParseFailure( |
| absl::StrCat("ListSeparatorSingleTrailingFails_", test_case.first), |
| REQUIRED, |
| absl::StrCat("repeated_", test_case.first, ": [", test_case.second, |
| ",]")); |
| ExpectParseFailure( |
| absl::StrCat("ListSeparatorTwoValuesTrailingFails_", test_case.first), |
| REQUIRED, |
| absl::StrCat("repeated_", test_case.first, ": [", test_case.second, ",", |
| test_case.second, ",]")); |
| } |
| // The test message don't really have all types nested, so just check one |
| // data type for the nested field separator support |
| RunValidTextFormatTest("FieldSeparatorCommaNested", REQUIRED, |
| "optional_nested_message: { a: 123, }"); |
| RunValidTextFormatTest("FieldSeparatorSemiNested", REQUIRED, |
| "optional_nested_message: { a: 123; }"); |
| ExpectParseFailure("FieldSeparatorCommaNestedDuplicates", REQUIRED, |
| "optional_nested_message: { a: 123,, }"); |
| ExpectParseFailure("FieldSeparatorSemiNestedDuplicates", REQUIRED, |
| "optional_nested_message: { a: 123;; }"); |
| |
| // Unknown Fields |
| UnknownToTestAllTypes message; |
| // Unable to print unknown Fixed32/Fixed64 fields as if they are known. |
| // Fixed32/Fixed64 fields are not added in the tests. |
| message.set_optional_int32(123); |
| message.set_optional_string("hello"); |
| message.set_optional_bool(true); |
| RunValidUnknownTextFormatTest("ScalarUnknownFields", message); |
| |
| message.Clear(); |
| message.mutable_nested_message()->set_c(111); |
| RunValidUnknownTextFormatTest("MessageUnknownFields", message); |
| |
| message.Clear(); |
| message.mutable_optionalgroup()->set_a(321); |
| RunValidUnknownTextFormatTest("GroupUnknownFields", message); |
| |
| message.add_repeated_int32(1); |
| message.add_repeated_int32(2); |
| message.add_repeated_int32(3); |
| RunValidUnknownTextFormatTest("RepeatedUnknownFields", message); |
| |
| // Map fields |
| MessageType prototype; |
| (*prototype.mutable_map_string_string())["c"] = "value"; |
| (*prototype.mutable_map_string_string())["b"] = "value"; |
| (*prototype.mutable_map_string_string())["a"] = "value"; |
| RunValidTextFormatTestWithMessage("AlphabeticallySortedMapStringKeys", |
| REQUIRED, |
| R"( |
| map_string_string { |
| key: "a" |
| value: "value" |
| } |
| map_string_string { |
| key: "b" |
| value: "value" |
| } |
| map_string_string { |
| key: "c" |
| value: "value" |
| } |
| )", |
| prototype); |
| |
| prototype.Clear(); |
| (*prototype.mutable_map_int32_int32())[3] = 0; |
| (*prototype.mutable_map_int32_int32())[2] = 0; |
| (*prototype.mutable_map_int32_int32())[1] = 0; |
| RunValidTextFormatTestWithMessage("AlphabeticallySortedMapIntKeys", REQUIRED, |
| R"( |
| map_int32_int32 { |
| key: 1 |
| value: 0 |
| } |
| map_int32_int32 { |
| key: 2 |
| value: 0 |
| } |
| map_int32_int32 { |
| key: 3 |
| value: 0 |
| } |
| )", |
| prototype); |
| |
| prototype.Clear(); |
| (*prototype.mutable_map_bool_bool())[true] = false; |
| (*prototype.mutable_map_bool_bool())[false] = false; |
| RunValidTextFormatTestWithMessage("AlphabeticallySortedMapBoolKeys", REQUIRED, |
| R"( |
| map_bool_bool { |
| key: false |
| value: false |
| } |
| map_bool_bool { |
| key: true |
| value: false |
| } |
| )", |
| prototype); |
| |
| prototype.Clear(); |
| ConformanceRequestSetting setting_map( |
| REQUIRED, conformance::TEXT_FORMAT, conformance::PROTOBUF, |
| conformance::TEXT_FORMAT_TEST, prototype, "DuplicateMapKey", R"( |
| map_string_nested_message { |
| key: "duplicate" |
| value: { a: 123 } |
| } |
| map_string_nested_message { |
| key: "duplicate" |
| value: { corecursive: {} } |
| } |
| )"); |
| // The last-specified value will be retained in a parsed map |
| suite_.RunValidInputTest(setting_map, R"( |
| map_string_nested_message { |
| key: "duplicate" |
| value: { corecursive: {} } |
| } |
| )"); |
| } |
| |
| template <typename MessageType> |
| void TextFormatConformanceTestSuiteImpl<MessageType>::RunAnyTests() { |
| // Any fields |
| RunValidTextFormatTest("AnyField", REQUIRED, |
| R"( |
| optional_any: { |
| [type.googleapis.com/protobuf_test_messages.proto3.TestAllTypesProto3] |
| { optional_int32: 12345 |
| } |
| } |
| )"); |
| RunValidTextFormatTest("AnyFieldWithRawBytes", REQUIRED, |
| R"( |
| optional_any: { |
| type_url: |
| "type.googleapis.com/protobuf_test_messages.proto3.TestAllTypesProto3" value: |
| "\b\271`" |
| } |
| )"); |
| ExpectParseFailure("AnyFieldWithInvalidType", REQUIRED, |
| R"( |
| optional_any: { |
| [type.googleapis.com/unknown] { |
| optional_int32: 12345 |
| } |
| } |
| )"); |
| } |
| |
| template <typename MessageType> |
| void TextFormatConformanceTestSuiteImpl< |
| MessageType>::RunTextFormatPerformanceTests() { |
| TestTextFormatPerformanceMergeMessageWithRepeatedField("Bool", |
| "repeated_bool: true"); |
| TestTextFormatPerformanceMergeMessageWithRepeatedField( |
| "Double", "repeated_double: 123"); |
| TestTextFormatPerformanceMergeMessageWithRepeatedField( |
| "Int32", "repeated_uint32: 123"); |
| TestTextFormatPerformanceMergeMessageWithRepeatedField( |
| "Int64", "repeated_uint64: 123"); |
| TestTextFormatPerformanceMergeMessageWithRepeatedField( |
| "String", R"(repeated_string: "foo")"); |
| TestTextFormatPerformanceMergeMessageWithRepeatedField( |
| "Bytes", R"(repeated_bytes: "foo")"); |
| } |
| |
| // This is currently considered valid input by some languages but not others |
| template <typename MessageType> |
| void TextFormatConformanceTestSuiteImpl<MessageType>:: |
| TestTextFormatPerformanceMergeMessageWithRepeatedField( |
| const std::string& test_type_name, const std::string& message_field) { |
| std::string recursive_message = |
| absl::StrCat("recursive_message { ", message_field, " }"); |
| |
| std::string input; |
| for (size_t i = 0; i < kPerformanceRepeatCount; i++) { |
| absl::StrAppend(&input, recursive_message); |
| } |
| |
| std::string expected = "recursive_message { "; |
| for (size_t i = 0; i < kPerformanceRepeatCount; i++) { |
| absl::StrAppend(&expected, message_field, " "); |
| } |
| absl::StrAppend(&expected, "}"); |
| |
| RunValidTextFormatTestWithExpected( |
| absl::StrCat("TestTextFormatPerformanceMergeMessageWithRepeatedField", |
| test_type_name), |
| RECOMMENDED, input, expected); |
| } |
| |
| template <typename MessageType> |
| void TextFormatConformanceTestSuiteImpl<MessageType>::RunOpenEnumTests() { |
| RunValidTextFormatTest("ClosedEnumFieldByNumber", REQUIRED, |
| R"( |
| optional_nested_enum: 1 |
| )"); |
| RunValidTextFormatTest("ClosedEnumFieldWithUnknownNumber", REQUIRED, |
| R"( |
| optional_nested_enum: 42 |
| )"); |
| } |
| |
| template <typename MessageType> |
| void TextFormatConformanceTestSuiteImpl<MessageType>::RunClosedEnumTests() { |
| RunValidTextFormatTest("ClosedEnumFieldByNumber", REQUIRED, |
| R"( |
| optional_nested_enum: 1 |
| )"); |
| ExpectParseFailure("ClosedEnumFieldWithUnknownNumber", REQUIRED, |
| R"( |
| optional_nested_enum: 42 |
| )"); |
| } |
| |
| } // namespace protobuf |
| } // namespace google |