diff --git a/conformance/BUILD.bazel b/conformance/BUILD.bazel index 42c1b3da34..eafd606b09 100644 --- a/conformance/BUILD.bazel +++ b/conformance/BUILD.bazel @@ -171,6 +171,9 @@ cc_library( ":conformance_test", ":test_messages_proto2_proto_cc", ":test_messages_proto3_proto_cc", + "@com_google_absl//absl/log:absl_log", + "@com_google_absl//absl/log:die_if_null", + "@com_google_absl//absl/strings", ], ) diff --git a/conformance/binary_json_conformance_suite.cc b/conformance/binary_json_conformance_suite.cc index d063c1270b..dbacc05450 100644 --- a/conformance/binary_json_conformance_suite.cc +++ b/conformance/binary_json_conformance_suite.cc @@ -349,7 +349,7 @@ void BinaryAndJsonConformanceSuiteImpl:: ConformanceResponse response; std::string effective_test_name = absl::StrCat(setting.ConformanceLevelToString(level), ".", - SyntaxIdentifier(), ".ProtobufInput.", test_name); + setting.GetSyntaxIdentifier(), ".ProtobufInput.", test_name); suite_.RunTest(effective_test_name, request, &response); if (response.result_case() == ConformanceResponse::kParseError) { @@ -522,9 +522,9 @@ void BinaryAndJsonConformanceSuiteImpl< test_name, input_json); const ConformanceRequest& request = setting.GetRequest(); ConformanceResponse response; - std::string effective_test_name = - absl::StrCat(setting.ConformanceLevelToString(level), ".", - SyntaxIdentifier(), ".JsonInput.", test_name, ".Validator"); + std::string effective_test_name = absl::StrCat( + setting.ConformanceLevelToString(level), ".", + setting.GetSyntaxIdentifier(), ".JsonInput.", test_name, ".Validator"); suite_.RunTest(effective_test_name, request, &response); @@ -3202,7 +3202,7 @@ BinaryAndJsonConformanceSuiteImpl::GetFieldForType( } ABSL_LOG(FATAL) << "Couldn't find field with type: " << repeated_string << packed_string << FieldDescriptor::TypeName(type) << " for " - << SyntaxIdentifier(); + << d->full_name(); return nullptr; } @@ -3226,7 +3226,7 @@ BinaryAndJsonConformanceSuiteImpl::GetFieldForMapType( ABSL_LOG(FATAL) << "Couldn't find map field with type: " << FieldDescriptor::TypeName(key_type) << " and " << FieldDescriptor::TypeName(key_type) << " for " - << SyntaxIdentifier(); + << d->full_name(); return nullptr; } @@ -3244,7 +3244,7 @@ BinaryAndJsonConformanceSuiteImpl::GetFieldForOneofType( ABSL_LOG(FATAL) << "Couldn't find oneof field with type: " << FieldDescriptor::TypeName(type) << " for " - << SyntaxIdentifier(); + << d->full_name(); return nullptr; } diff --git a/conformance/conformance_test.cc b/conformance/conformance_test.cc index fc80ec6d0f..94efbcc466 100644 --- a/conformance/conformance_test.cc +++ b/conformance/conformance_test.cc @@ -141,22 +141,22 @@ ConformanceTestSuite::ConformanceRequestSetting::NewTestMessage() const { return std::unique_ptr(prototype_message_for_compare_->New()); } -std::string ConformanceTestSuite::ConformanceRequestSetting::GetTestName() - const { - std::string rname; +std::string +ConformanceTestSuite::ConformanceRequestSetting::GetSyntaxIdentifier() const { switch (FileDescriptorLegacy(prototype_message_.GetDescriptor()->file()) .syntax()) { case FileDescriptorLegacy::Syntax::SYNTAX_PROTO3: - rname = ".Proto3."; - break; + return "Proto3"; case FileDescriptorLegacy::Syntax::SYNTAX_PROTO2: - rname = ".Proto2."; - break; + return "Proto2"; default: - break; + return "Unknown"; } +} - return absl::StrCat(ConformanceLevelToString(level_), rname, +string ConformanceTestSuite::ConformanceRequestSetting::GetTestName() const { + return absl::StrCat(ConformanceLevelToString(level_), ".", + GetSyntaxIdentifier(), ".", InputFormatString(input_format_), ".", test_name_, ".", OutputFormatString(output_format_)); } diff --git a/conformance/conformance_test.h b/conformance/conformance_test.h index bdc7a697fe..00559d9e1a 100644 --- a/conformance/conformance_test.h +++ b/conformance/conformance_test.h @@ -19,6 +19,7 @@ #include #include +#include "google/protobuf/descriptor.pb.h" #include "google/protobuf/util/type_resolver.h" #include "absl/container/btree_set.h" #include "absl/container/flat_hash_set.h" @@ -200,6 +201,8 @@ class ConformanceTestSuite { std::unique_ptr NewTestMessage() const; + std::string GetSyntaxIdentifier() const; + std::string GetTestName() const; const conformance::ConformanceRequest& GetRequest() const { diff --git a/conformance/text_format_conformance_suite.cc b/conformance/text_format_conformance_suite.cc index fb0ca1f6d5..ae39296917 100644 --- a/conformance/text_format_conformance_suite.cc +++ b/conformance/text_format_conformance_suite.cc @@ -12,19 +12,18 @@ #include #include "absl/log/absl_log.h" +#include "absl/log/die_if_null.h" #include "absl/strings/str_cat.h" #include "conformance_test.h" #include "google/protobuf/test_messages_proto2.pb.h" #include "google/protobuf/test_messages_proto3.pb.h" #include "google/protobuf/text_format.h" -namespace proto2_messages = protobuf_test_messages::proto2; - using conformance::ConformanceRequest; using conformance::ConformanceResponse; using conformance::WireFormat; -using proto2_messages::TestAllTypesProto2; -using proto2_messages::UnknownToTestAllTypes; +using protobuf_test_messages::proto2::TestAllTypesProto2; +using protobuf_test_messages::proto2::UnknownToTestAllTypes; using protobuf_test_messages::proto3::TestAllTypesProto3; namespace google { @@ -111,10 +110,35 @@ bool TextFormatConformanceTestSuite::ParseResponse( return true; } -void TextFormatConformanceTestSuite::ExpectParseFailure( +void TextFormatConformanceTestSuite::RunSuiteImpl() { + 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_) { + RunTextFormatPerformanceTests(); + } else { + if (MessageType::GetDescriptor()->name() == "TestAllTypesProto2") { + RunGroupTests(); + } + 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) { - TestAllTypesProto3 prototype; + 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( @@ -122,89 +146,75 @@ void TextFormatConformanceTestSuite::ExpectParseFailure( 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), - ".Proto3.TextFormatInput.", test_name); + std::string effective_test_name = absl::StrCat( + setting.ConformanceLevelToString(level), ".", + setting.GetSyntaxIdentifier(), ".TextFormatInput.", test_name); - RunTest(effective_test_name, request, &response); + suite_.RunTest(effective_test_name, request, &response); if (response.result_case() == ConformanceResponse::kParseError) { - ReportSuccess(effective_test_name); + suite_.ReportSuccess(effective_test_name); } else if (response.result_case() == ConformanceResponse::kSkipped) { - ReportSkip(effective_test_name, request, response); + suite_.ReportSkip(effective_test_name, request, response); } else { - ReportFailure(effective_test_name, level, request, response, - "Should have failed to parse, but didn't."); + suite_.ReportFailure(effective_test_name, level, request, response, + "Should have failed to parse, but didn't."); } } -void TextFormatConformanceTestSuite::RunValidTextFormatTest( +template +void TextFormatConformanceTestSuiteImpl::RunValidTextFormatTest( const std::string& test_name, ConformanceLevel level, const std::string& input_text) { - TestAllTypesProto3 prototype; + MessageType prototype; RunValidTextFormatTestWithMessage(test_name, level, input_text, prototype); } -void TextFormatConformanceTestSuite::RunValidTextFormatTestProto2( - const std::string& test_name, ConformanceLevel level, - const std::string& input_text) { - TestAllTypesProto2 prototype; - RunValidTextFormatTestWithMessage(test_name, level, input_text, prototype); -} - -void TextFormatConformanceTestSuite::RunValidTextFormatTestWithExpected( - const std::string& test_name, ConformanceLevel level, - const std::string& input_text, const std::string& expected_text) { - TestAllTypesProto3 prototype; - RunValidTextFormatTestWithMessage(test_name, level, input_text, expected_text, - prototype); -} - -void TextFormatConformanceTestSuite::RunValidTextFormatTestProto2WithExpected( - const std::string& test_name, ConformanceLevel level, - const std::string& input_text, const std::string& expected_text) { - TestAllTypesProto2 prototype; - RunValidTextFormatTestWithMessage(test_name, level, input_text, expected_text, - prototype); -} - -void TextFormatConformanceTestSuite::RunValidTextFormatTestWithMessage( - const std::string& test_name, ConformanceLevel level, - const std::string& input_text, const Message& 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, prototype, test_name, input_text); - RunValidInputTest(setting1, input_text); + 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, prototype, test_name, input_text); - RunValidInputTest(setting2, input_text); + conformance::TEXT_FORMAT_TEST, message, test_name, input_text); + suite_.RunValidInputTest(setting2, input_text); } -void TextFormatConformanceTestSuite::RunValidTextFormatTestWithMessage( - const std::string& test_name, ConformanceLevel level, - const std::string& input_text, const std::string& expected_text, - const Message& prototype) { +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); - RunValidInputTest(setting1, expected_text); + suite_.RunValidInputTest(setting1, expected_text); ConformanceRequestSetting setting2( level, conformance::TEXT_FORMAT, conformance::TEXT_FORMAT, conformance::TEXT_FORMAT_TEST, prototype, test_name, input_text); - RunValidInputTest(setting2, expected_text); + suite_.RunValidInputTest(setting2, expected_text); } -void TextFormatConformanceTestSuite::RunValidUnknownTextFormatTest( - const std::string& test_name, const Message& message) { +template +void TextFormatConformanceTestSuiteImpl< + MessageType>::RunValidUnknownTextFormatTest(const std::string& test_name, + const Message& message) { std::string serialized_input; message.SerializeToString(&serialized_input); - TestAllTypesProto3 prototype; + 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); - RunValidBinaryInputTest(setting1, ""); + suite_.RunValidBinaryInputTest(setting1, ""); ConformanceRequestSetting setting2( RECOMMENDED, conformance::PROTOBUF, conformance::TEXT_FORMAT, @@ -212,207 +222,182 @@ void TextFormatConformanceTestSuite::RunValidUnknownTextFormatTest( absl::StrCat(test_name, "_Print"), serialized_input); setting2.SetPrototypeMessageForCompare(message); setting2.SetPrintUnknownFields(true); - RunValidBinaryInputTest(setting2, serialized_input); + suite_.RunValidBinaryInputTest(setting2, serialized_input); } -void TextFormatConformanceTestSuite::RunSuiteImpl() { - if (!performance_) { - 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"); - - // 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"); - - // Floating point fields - RunValidTextFormatTest("FloatField", REQUIRED, "optional_float: 3.192837"); - RunValidTextFormatTest("FloatFieldWithVeryPreciseNumber", REQUIRED, - "optional_float: 3.123456789123456789"); - RunValidTextFormatTest("FloatFieldMaxValue", REQUIRED, - "optional_float: 3.4028235e+38"); - RunValidTextFormatTest("FloatFieldMinValue", REQUIRED, - "optional_float: 1.17549e-38"); - RunValidTextFormatTest("FloatFieldNaNValue", REQUIRED, - "optional_float: NaN"); - RunValidTextFormatTest("FloatFieldPosInfValue", REQUIRED, - "optional_float: inf"); - RunValidTextFormatTest("FloatFieldNegInfValue", REQUIRED, - "optional_float: -inf"); - RunValidTextFormatTest("FloatFieldWithInt32Max", REQUIRED, - "optional_float: 4294967296"); - RunValidTextFormatTest("FloatFieldLargerThanInt64", REQUIRED, - "optional_float: 9223372036854775808"); - RunValidTextFormatTest("FloatFieldTooLarge", REQUIRED, - "optional_float: 3.4028235e+39"); - RunValidTextFormatTest("FloatFieldTooSmall", REQUIRED, - "optional_float: 1.17549e-39"); - RunValidTextFormatTest("FloatFieldLargerThanUint64", REQUIRED, - "optional_float: 18446744073709551616"); - - // 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" - ? &TextFormatConformanceTestSuite::ExpectParseFailure - : &TextFormatConformanceTestSuite::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'")); - } +template +void TextFormatConformanceTestSuiteImpl::RunGroupTests() { + RunValidTextFormatTest("GroupFieldNoColon", REQUIRED, + "Data { group_int32: 1 }"); + RunValidTextFormatTest("GroupFieldWithColon", REQUIRED, + "Data: { group_int32: 1 }"); + RunValidTextFormatTest("GroupFieldEmpty", REQUIRED, "Data {}"); +} - // Group fields - RunValidTextFormatTestProto2("GroupFieldNoColon", REQUIRED, - "Data { group_int32: 1 }"); - RunValidTextFormatTestProto2("GroupFieldWithColon", REQUIRED, - "Data: { group_int32: 1 }"); - RunValidTextFormatTestProto2("GroupFieldEmpty", REQUIRED, "Data {}"); - - // 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); - - // 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::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"); + + // 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"); + + // Floating point fields + RunValidTextFormatTest("FloatField", REQUIRED, "optional_float: 3.192837"); + RunValidTextFormatTest("FloatFieldWithVeryPreciseNumber", REQUIRED, + "optional_float: 3.123456789123456789"); + RunValidTextFormatTest("FloatFieldMaxValue", REQUIRED, + "optional_float: 3.4028235e+38"); + RunValidTextFormatTest("FloatFieldMinValue", REQUIRED, + "optional_float: 1.17549e-38"); + RunValidTextFormatTest("FloatFieldNaNValue", REQUIRED, "optional_float: NaN"); + RunValidTextFormatTest("FloatFieldPosInfValue", REQUIRED, + "optional_float: inf"); + RunValidTextFormatTest("FloatFieldNegInfValue", REQUIRED, + "optional_float: -inf"); + RunValidTextFormatTest("FloatFieldWithInt32Max", REQUIRED, + "optional_float: 4294967296"); + RunValidTextFormatTest("FloatFieldLargerThanInt64", REQUIRED, + "optional_float: 9223372036854775808"); + RunValidTextFormatTest("FloatFieldTooLarge", REQUIRED, + "optional_float: 3.4028235e+39"); + RunValidTextFormatTest("FloatFieldTooSmall", REQUIRED, + "optional_float: 1.17549e-39"); + RunValidTextFormatTest("FloatFieldLargerThanUint64", REQUIRED, + "optional_float: 18446744073709551616"); + + // 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'")); + } - // Map fields - TestAllTypesProto3 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"( + // 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" @@ -426,15 +411,14 @@ void TextFormatConformanceTestSuite::RunSuiteImpl() { 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"( + 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 @@ -448,14 +432,13 @@ void TextFormatConformanceTestSuite::RunSuiteImpl() { value: 0 } )", - prototype); - - prototype.Clear(); - (*prototype.mutable_map_bool_bool())[true] = false; - (*prototype.mutable_map_bool_bool())[false] = false; - RunValidTextFormatTestWithMessage("AlphabeticallySortedMapBoolKeys", - REQUIRED, - R"( + 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 @@ -465,12 +448,12 @@ void TextFormatConformanceTestSuite::RunSuiteImpl() { value: false } )", - prototype); + prototype); - prototype.Clear(); - ConformanceRequestSetting setting_map( - REQUIRED, conformance::TEXT_FORMAT, conformance::PROTOBUF, - conformance::TEXT_FORMAT_TEST, prototype, "DuplicateMapKey", R"( + 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 } @@ -480,21 +463,47 @@ void TextFormatConformanceTestSuite::RunSuiteImpl() { value: { corecursive: {} } } )"); - // The last-specified value will be retained in a parsed map - RunValidInputTest(setting_map, R"( + // The last-specified value will be retained in a parsed map + suite_.RunValidInputTest(setting_map, R"( map_string_nested_message { key: "duplicate" value: { corecursive: {} } } )"); - } - // Flag control performance tests to keep them internal and opt-in only - if (performance_) { - RunTextFormatPerformanceTests(); - } } -void TextFormatConformanceTestSuite::RunTextFormatPerformanceTests() { +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( @@ -510,7 +519,8 @@ void TextFormatConformanceTestSuite::RunTextFormatPerformanceTests() { } // This is currently considered valid input by some languages but not others -void TextFormatConformanceTestSuite:: +template +void TextFormatConformanceTestSuiteImpl:: TestTextFormatPerformanceMergeMessageWithRepeatedField( const std::string& test_type_name, const std::string& message_field) { std::string recursive_message = @@ -527,13 +537,9 @@ void TextFormatConformanceTestSuite:: } absl::StrAppend(&expected, "}"); - RunValidTextFormatTestProto2WithExpected( - absl::StrCat("TestTextFormatPerformanceMergeMessageWithRepeatedField", - test_type_name, "Proto2"), - RECOMMENDED, input, expected); RunValidTextFormatTestWithExpected( absl::StrCat("TestTextFormatPerformanceMergeMessageWithRepeatedField", - test_type_name, "Proto3"), + test_type_name), RECOMMENDED, input, expected); } diff --git a/conformance/text_format_conformance_suite.h b/conformance/text_format_conformance_suite.h index 1917ee565b..27de29e13a 100644 --- a/conformance/text_format_conformance_suite.h +++ b/conformance/text_format_conformance_suite.h @@ -22,41 +22,55 @@ class TextFormatConformanceTestSuite : public ConformanceTestSuite { private: void RunSuiteImpl() override; + + bool ParseTextFormatResponse(const conformance::ConformanceResponse& response, + const ConformanceRequestSetting& setting, + Message* test_message); + bool ParseResponse(const conformance::ConformanceResponse& response, + const ConformanceRequestSetting& setting, + Message* test_message) override; + + template + friend class TextFormatConformanceTestSuiteImpl; +}; + +template +class TextFormatConformanceTestSuiteImpl { + public: + explicit TextFormatConformanceTestSuiteImpl( + TextFormatConformanceTestSuite* suite); + + private: + using ConformanceRequestSetting = + TextFormatConformanceTestSuite::ConformanceRequestSetting; + using ConformanceLevel = TextFormatConformanceTestSuite::ConformanceLevel; + constexpr static ConformanceLevel RECOMMENDED = ConformanceLevel::RECOMMENDED; + constexpr static ConformanceLevel REQUIRED = ConformanceLevel::REQUIRED; + + void RunAllTests(); + + void RunGroupTests(); + void RunAnyTests(); + void RunTextFormatPerformanceTests(); void RunValidTextFormatTest(const std::string& test_name, ConformanceLevel level, const std::string& input); - void RunValidTextFormatTestProto2(const std::string& test_name, - ConformanceLevel level, - const std::string& input); void RunValidTextFormatTestWithExpected(const std::string& test_name, ConformanceLevel level, - const std::string& input, - const std::string& expected); - void RunValidTextFormatTestProto2WithExpected(const std::string& test_name, - ConformanceLevel level, - const std::string& input, - const std::string& expected); - void RunValidTextFormatTestWithMessage(const std::string& test_name, - ConformanceLevel level, - const std::string& input_text, - const Message& prototype); + const std::string& input_text, + const std::string& expected_text); + void RunValidUnknownTextFormatTest(const std::string& test_name, + const Message& message); void RunValidTextFormatTestWithMessage(const std::string& test_name, ConformanceLevel level, const std::string& input_text, - const std::string& expected_text, - const Message& prototype); - void RunValidUnknownTextFormatTest(const std::string& test_name, - const Message& message); + const Message& message); void ExpectParseFailure(const std::string& test_name, ConformanceLevel level, const std::string& input); - bool ParseTextFormatResponse(const conformance::ConformanceResponse& response, - const ConformanceRequestSetting& setting, - Message* test_message); - bool ParseResponse(const conformance::ConformanceResponse& response, - const ConformanceRequestSetting& setting, - Message* test_message) override; void TestTextFormatPerformanceMergeMessageWithRepeatedField( const std::string& test_type_name, const std::string& message_field); + + TextFormatConformanceTestSuite& suite_; }; } // namespace protobuf