// 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 #include #include #include #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(this); TextFormatConformanceTestSuiteImpl(this); if (maximum_edition_ >= Edition::EDITION_2023) { TextFormatConformanceTestSuiteImpl(this); TextFormatConformanceTestSuiteImpl(this); TextFormatConformanceTestSuiteImpl(this); } } template TextFormatConformanceTestSuiteImpl:: 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(); } if (MessageType::GetDescriptor()->name() == "TestAllTypesEdition2023") { RunDelimitedTests(); } if (MessageType::GetDescriptor()->name() == "TestAllTypesProto3") { RunAnyTests(); // TODO Run these over proto2 also. RunAllTests(); } } } template void TextFormatConformanceTestSuiteImpl::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 void TextFormatConformanceTestSuiteImpl::RunValidTextFormatTest( const std::string& test_name, ConformanceLevel level, const std::string& input_text) { MessageType prototype; RunValidTextFormatTestWithMessage(test_name, level, input_text, prototype); } template void TextFormatConformanceTestSuiteImpl:: 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 void TextFormatConformanceTestSuiteImpl:: 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 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 void TextFormatConformanceTestSuiteImpl::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 void TextFormatConformanceTestSuiteImpl::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 void TextFormatConformanceTestSuiteImpl::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{"", "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{"nan", "NaN", "nAn"}) { RunValidTextFormatTest(absl::StrCat("FloatFieldValue_", value), REQUIRED, absl::StrCat("optional_float: ", value)); } for (const auto& value : std::vector{ "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{"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>{ {"string", "\"abc\""}, {"bytes", "\"abc\""}, {"int32", "123"}, {"bool", "true"}, {"double", "1.23"}, {"fixed32", "0x123"}, }) { // Optional Field Separators for (const auto& field_type : std::vector{"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 void TextFormatConformanceTestSuiteImpl::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 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 void TextFormatConformanceTestSuiteImpl:: 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); } } // namespace protobuf } // namespace google