From 28e573e77fc3b453dd242e3848b19e7adbf04984 Mon Sep 17 00:00:00 2001 From: Mike Kruskal Date: Tue, 17 Oct 2023 17:26:08 -0700 Subject: [PATCH] Restructure syntax branches in text format conformance tests. This doesn't change the tests at all, but refactors them to be more reusable in different contexts. Specifically, this will make it easier to add corresponding editions-based tests. This also splits the concept of "uses a proto3 message" and "runs proto3 tests", in preparation for that change. PiperOrigin-RevId: 574310612 --- conformance/BUILD.bazel | 3 + conformance/binary_json_conformance_suite.cc | 14 +- conformance/conformance_test.cc | 18 +- conformance/conformance_test.h | 3 + conformance/text_format_conformance_suite.cc | 584 ++++++++++--------- conformance/text_format_conformance_suite.h | 60 +- 6 files changed, 354 insertions(+), 328 deletions(-) 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