Yilun Chong | d8c2501 | 2019-02-22 18:13:33 +0800 | [diff] [blame] | 1 | // Protocol Buffers - Google's data interchange format |
| 2 | // Copyright 2008 Google Inc. All rights reserved. |
| 3 | // https://developers.google.com/protocol-buffers/ |
| 4 | // |
| 5 | // Redistribution and use in source and binary forms, with or without |
| 6 | // modification, are permitted provided that the following conditions are |
| 7 | // met: |
| 8 | // |
| 9 | // * Redistributions of source code must retain the above copyright |
| 10 | // notice, this list of conditions and the following disclaimer. |
| 11 | // * Redistributions in binary form must reproduce the above |
| 12 | // copyright notice, this list of conditions and the following disclaimer |
| 13 | // in the documentation and/or other materials provided with the |
| 14 | // distribution. |
| 15 | // * Neither the name of Google Inc. nor the names of its |
| 16 | // contributors may be used to endorse or promote products derived from |
| 17 | // this software without specific prior written permission. |
| 18 | // |
| 19 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
| 20 | // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
| 21 | // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
| 22 | // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
| 23 | // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
| 24 | // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
| 25 | // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
| 26 | // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
| 27 | // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| 28 | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
| 29 | // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| 30 | |
| 31 | #include "text_format_conformance_suite.h" |
| 32 | |
| 33 | #include "conformance_test.h" |
| 34 | |
| 35 | #include <google/protobuf/any.pb.h> |
| 36 | #include <google/protobuf/test_messages_proto2.pb.h> |
| 37 | #include <google/protobuf/test_messages_proto3.pb.h> |
| 38 | #include <google/protobuf/text_format.h> |
| 39 | |
| 40 | using conformance::ConformanceRequest; |
| 41 | using conformance::ConformanceResponse; |
| 42 | using conformance::WireFormat; |
| 43 | using google::protobuf::Message; |
| 44 | using google::protobuf::TextFormat; |
| 45 | using protobuf_test_messages::proto2::TestAllTypesProto2; |
Hao Nguyen | 2f864fd | 2019-03-20 11:45:01 -0700 | [diff] [blame] | 46 | using protobuf_test_messages::proto2::UnknownToTestAllTypes; |
Yilun Chong | d8c2501 | 2019-02-22 18:13:33 +0800 | [diff] [blame] | 47 | using protobuf_test_messages::proto3::TestAllTypesProto3; |
| 48 | using std::string; |
| 49 | |
| 50 | namespace google { |
| 51 | namespace protobuf { |
| 52 | |
| 53 | TextFormatConformanceTestSuite::TextFormatConformanceTestSuite() { |
| 54 | SetFailureListFlagName("--text_format_failure_list"); |
| 55 | } |
| 56 | |
| 57 | bool TextFormatConformanceTestSuite::ParseTextFormatResponse( |
Hao Nguyen | 2f864fd | 2019-03-20 11:45:01 -0700 | [diff] [blame] | 58 | const ConformanceResponse& response, |
| 59 | const ConformanceRequestSetting& setting, Message* test_message) { |
| 60 | TextFormat::Parser parser; |
| 61 | const ConformanceRequest& request = setting.GetRequest(); |
| 62 | if (request.print_unknown_fields()) { |
| 63 | parser.AllowFieldNumber(true); |
| 64 | } |
| 65 | if (!parser.ParseFromString(response.text_payload(), test_message)) { |
Yilun Chong | d8c2501 | 2019-02-22 18:13:33 +0800 | [diff] [blame] | 66 | GOOGLE_LOG(ERROR) << "INTERNAL ERROR: internal text->protobuf transcode " |
| 67 | << "yielded unparseable proto. Text payload: " |
| 68 | << response.text_payload(); |
| 69 | return false; |
| 70 | } |
| 71 | |
| 72 | return true; |
| 73 | } |
| 74 | |
| 75 | bool TextFormatConformanceTestSuite::ParseResponse( |
| 76 | const ConformanceResponse& response, |
| 77 | const ConformanceRequestSetting& setting, Message* test_message) { |
| 78 | const ConformanceRequest& request = setting.GetRequest(); |
| 79 | WireFormat requested_output = request.requested_output_format(); |
| 80 | const string& test_name = setting.GetTestName(); |
| 81 | ConformanceLevel level = setting.GetLevel(); |
| 82 | |
| 83 | switch (response.result_case()) { |
| 84 | case ConformanceResponse::kProtobufPayload: { |
| 85 | if (requested_output != conformance::PROTOBUF) { |
| 86 | ReportFailure( |
| 87 | test_name, level, request, response, |
| 88 | StrCat("Test was asked for ", WireFormatToString(requested_output), |
| 89 | " output but provided PROTOBUF instead.") |
| 90 | .c_str()); |
| 91 | return false; |
| 92 | } |
| 93 | |
| 94 | if (!test_message->ParseFromString(response.protobuf_payload())) { |
| 95 | ReportFailure(test_name, level, request, response, |
| 96 | "Protobuf output we received from test was unparseable."); |
| 97 | return false; |
| 98 | } |
| 99 | |
| 100 | break; |
| 101 | } |
| 102 | |
| 103 | case ConformanceResponse::kTextPayload: { |
| 104 | if (requested_output != conformance::TEXT_FORMAT) { |
| 105 | ReportFailure( |
| 106 | test_name, level, request, response, |
| 107 | StrCat("Test was asked for ", WireFormatToString(requested_output), |
| 108 | " output but provided TEXT_FORMAT instead.") |
| 109 | .c_str()); |
| 110 | return false; |
| 111 | } |
| 112 | |
Hao Nguyen | 2f864fd | 2019-03-20 11:45:01 -0700 | [diff] [blame] | 113 | if (!ParseTextFormatResponse(response, setting, test_message)) { |
Yilun Chong | d8c2501 | 2019-02-22 18:13:33 +0800 | [diff] [blame] | 114 | ReportFailure( |
| 115 | test_name, level, request, response, |
| 116 | "TEXT_FORMAT output we received from test was unparseable."); |
| 117 | return false; |
| 118 | } |
| 119 | |
| 120 | break; |
| 121 | } |
| 122 | |
| 123 | default: |
| 124 | GOOGLE_LOG(FATAL) << test_name |
| 125 | << ": unknown payload type: " << response.result_case(); |
| 126 | } |
| 127 | |
| 128 | return true; |
| 129 | } |
| 130 | |
| 131 | void TextFormatConformanceTestSuite::ExpectParseFailure(const string& test_name, |
| 132 | ConformanceLevel level, |
| 133 | const string& input) { |
| 134 | TestAllTypesProto3 prototype; |
| 135 | // We don't expect output, but if the program erroneously accepts the protobuf |
| 136 | // we let it send its response as this. We must not leave it unspecified. |
| 137 | ConformanceRequestSetting setting( |
| 138 | level, conformance::TEXT_FORMAT, conformance::TEXT_FORMAT, |
| 139 | conformance::TEXT_FORMAT_TEST, prototype, test_name, input); |
| 140 | const ConformanceRequest& request = setting.GetRequest(); |
| 141 | ConformanceResponse response; |
| 142 | string effective_test_name = StrCat(setting.ConformanceLevelToString(level), |
| 143 | ".Proto3.TextFormatInput.", test_name); |
| 144 | |
| 145 | RunTest(effective_test_name, request, &response); |
| 146 | if (response.result_case() == ConformanceResponse::kParseError) { |
| 147 | ReportSuccess(effective_test_name); |
| 148 | } else if (response.result_case() == ConformanceResponse::kSkipped) { |
| 149 | ReportSkip(effective_test_name, request, response); |
| 150 | } else { |
| 151 | ReportFailure(effective_test_name, level, request, response, |
| 152 | "Should have failed to parse, but didn't."); |
| 153 | } |
| 154 | } |
| 155 | |
| 156 | void TextFormatConformanceTestSuite::RunValidTextFormatTest( |
| 157 | const string& test_name, ConformanceLevel level, const string& input_text) { |
| 158 | TestAllTypesProto3 prototype; |
| 159 | RunValidTextFormatTestWithMessage(test_name, level, input_text, prototype); |
| 160 | } |
| 161 | |
| 162 | void TextFormatConformanceTestSuite::RunValidTextFormatTestProto2( |
| 163 | const string& test_name, ConformanceLevel level, const string& input_text) { |
| 164 | TestAllTypesProto2 prototype; |
| 165 | RunValidTextFormatTestWithMessage(test_name, level, input_text, prototype); |
| 166 | } |
| 167 | |
| 168 | void TextFormatConformanceTestSuite::RunValidTextFormatTestWithMessage( |
| 169 | const string& test_name, ConformanceLevel level, const string& input_text, |
| 170 | const Message& prototype) { |
| 171 | ConformanceRequestSetting setting1( |
| 172 | level, conformance::TEXT_FORMAT, conformance::PROTOBUF, |
| 173 | conformance::TEXT_FORMAT_TEST, prototype, test_name, input_text); |
| 174 | RunValidInputTest(setting1, input_text); |
| 175 | ConformanceRequestSetting setting2( |
| 176 | level, conformance::TEXT_FORMAT, conformance::TEXT_FORMAT, |
| 177 | conformance::TEXT_FORMAT_TEST, prototype, test_name, input_text); |
| 178 | RunValidInputTest(setting2, input_text); |
| 179 | } |
| 180 | |
Hao Nguyen | 2f864fd | 2019-03-20 11:45:01 -0700 | [diff] [blame] | 181 | void TextFormatConformanceTestSuite::RunValidUnknownTextFormatTest( |
| 182 | const string& test_name, const Message& message) { |
| 183 | string serialized_input; |
| 184 | message.SerializeToString(&serialized_input); |
| 185 | TestAllTypesProto3 prototype; |
| 186 | ConformanceRequestSetting setting1( |
| 187 | RECOMMENDED, conformance::PROTOBUF, conformance::TEXT_FORMAT, |
| 188 | conformance::TEXT_FORMAT_TEST, prototype, test_name + "_Drop", |
| 189 | serialized_input); |
| 190 | setting1.SetPrototypeMessageForCompare(message); |
| 191 | RunValidBinaryInputTest(setting1, ""); |
| 192 | |
| 193 | ConformanceRequestSetting setting2( |
| 194 | RECOMMENDED, conformance::PROTOBUF, conformance::TEXT_FORMAT, |
| 195 | conformance::TEXT_FORMAT_TEST, prototype, test_name + "_Print", |
| 196 | serialized_input); |
| 197 | setting2.SetPrototypeMessageForCompare(message); |
| 198 | setting2.SetPrintUnknownFields(true); |
| 199 | RunValidBinaryInputTest(setting2, serialized_input); |
| 200 | } |
| 201 | |
Yilun Chong | d8c2501 | 2019-02-22 18:13:33 +0800 | [diff] [blame] | 202 | void TextFormatConformanceTestSuite::RunSuiteImpl() { |
| 203 | RunValidTextFormatTest("HelloWorld", REQUIRED, |
| 204 | "optional_string: 'Hello, World!'"); |
| 205 | // Integer fields. |
| 206 | RunValidTextFormatTest("Int32FieldMaxValue", REQUIRED, |
| 207 | "optional_int32: 2147483647"); |
| 208 | RunValidTextFormatTest("Int32FieldMinValue", REQUIRED, |
| 209 | "optional_int32: -2147483648"); |
| 210 | RunValidTextFormatTest("Uint32FieldMaxValue", REQUIRED, |
| 211 | "optional_uint32: 4294967295"); |
| 212 | RunValidTextFormatTest("Int64FieldMaxValue", REQUIRED, |
| 213 | "optional_int64: 9223372036854775807"); |
| 214 | RunValidTextFormatTest("Int64FieldMinValue", REQUIRED, |
| 215 | "optional_int64: -9223372036854775808"); |
| 216 | RunValidTextFormatTest("Uint64FieldMaxValue", REQUIRED, |
| 217 | "optional_uint64: 18446744073709551615"); |
| 218 | |
| 219 | // Parsers reject out-of-bound integer values. |
| 220 | ExpectParseFailure("Int32FieldTooLarge", REQUIRED, |
| 221 | "optional_int32: 2147483648"); |
| 222 | ExpectParseFailure("Int32FieldTooSmall", REQUIRED, |
| 223 | "optional_int32: -2147483649"); |
| 224 | ExpectParseFailure("Uint32FieldTooLarge", REQUIRED, |
| 225 | "optional_uint32: 4294967296"); |
| 226 | ExpectParseFailure("Int64FieldTooLarge", REQUIRED, |
| 227 | "optional_int64: 9223372036854775808"); |
| 228 | ExpectParseFailure("Int64FieldTooSmall", REQUIRED, |
| 229 | "optional_int64: -9223372036854775809"); |
| 230 | ExpectParseFailure("Uint64FieldTooLarge", REQUIRED, |
| 231 | "optional_uint64: 18446744073709551616"); |
| 232 | |
| 233 | // Floating point fields |
| 234 | RunValidTextFormatTest("FloatField", REQUIRED, |
| 235 | "optional_float: 3.192837"); |
| 236 | RunValidTextFormatTest("FloatFieldWithVeryPreciseNumber", REQUIRED, |
| 237 | "optional_float: 3.123456789123456789"); |
| 238 | RunValidTextFormatTest("FloatFieldMaxValue", REQUIRED, |
Hao Nguyen | 176f7db | 2019-04-09 06:23:32 -0700 | [diff] [blame] | 239 | "optional_float: 3.4028235e+38"); |
Yilun Chong | d8c2501 | 2019-02-22 18:13:33 +0800 | [diff] [blame] | 240 | RunValidTextFormatTest("FloatFieldMinValue", REQUIRED, |
| 241 | "optional_float: 1.17549e-38"); |
| 242 | RunValidTextFormatTest("FloatFieldNaNValue", REQUIRED, |
| 243 | "optional_float: NaN"); |
| 244 | RunValidTextFormatTest("FloatFieldPosInfValue", REQUIRED, |
| 245 | "optional_float: inf"); |
| 246 | RunValidTextFormatTest("FloatFieldNegInfValue", REQUIRED, |
| 247 | "optional_float: -inf"); |
| 248 | RunValidTextFormatTest("FloatFieldWithInt32Max", REQUIRED, |
| 249 | "optional_float: 4294967296"); |
| 250 | RunValidTextFormatTest("FloatFieldLargerThanInt64", REQUIRED, |
| 251 | "optional_float: 9223372036854775808"); |
Hao Nguyen | d0f91c8 | 2019-03-06 12:39:12 -0800 | [diff] [blame] | 252 | RunValidTextFormatTest("FloatFieldTooLarge", REQUIRED, |
| 253 | "optional_float: 3.4028235e+39"); |
| 254 | RunValidTextFormatTest("FloatFieldTooSmall", REQUIRED, |
| 255 | "optional_float: 1.17549e-39"); |
| 256 | RunValidTextFormatTest("FloatFieldLargerThanUint64", REQUIRED, |
| 257 | "optional_float: 18446744073709551616"); |
Yilun Chong | d8c2501 | 2019-02-22 18:13:33 +0800 | [diff] [blame] | 258 | |
| 259 | // Group fields |
| 260 | RunValidTextFormatTestProto2("GroupFieldNoColon", REQUIRED, |
| 261 | "Data { group_int32: 1 }"); |
| 262 | RunValidTextFormatTestProto2("GroupFieldWithColon", REQUIRED, |
| 263 | "Data: { group_int32: 1 }"); |
| 264 | RunValidTextFormatTestProto2("GroupFieldEmpty", REQUIRED, |
| 265 | "Data {}"); |
Hao Nguyen | 2f864fd | 2019-03-20 11:45:01 -0700 | [diff] [blame] | 266 | |
| 267 | |
| 268 | // Unknown Fields |
| 269 | UnknownToTestAllTypes message; |
| 270 | // Unable to print unknown Fixed32/Fixed64 fields as if they are known. |
| 271 | // Fixed32/Fixed64 fields are not added in the tests. |
| 272 | message.set_optional_int32(123); |
| 273 | message.set_optional_string("hello"); |
| 274 | message.set_optional_bool(true); |
| 275 | RunValidUnknownTextFormatTest("ScalarUnknownFields", message); |
| 276 | |
| 277 | message.Clear(); |
| 278 | message.mutable_nested_message()->set_c(111); |
| 279 | RunValidUnknownTextFormatTest("MessageUnknownFields", message); |
| 280 | |
| 281 | message.Clear(); |
| 282 | message.mutable_optionalgroup()->set_a(321); |
| 283 | RunValidUnknownTextFormatTest("GroupUnknownFields", message); |
| 284 | |
| 285 | message.add_repeated_int32(1); |
| 286 | message.add_repeated_int32(2); |
| 287 | message.add_repeated_int32(3); |
| 288 | RunValidUnknownTextFormatTest("RepeatedUnknownFields", message); |
Hao Nguyen | 176f7db | 2019-04-09 06:23:32 -0700 | [diff] [blame] | 289 | |
| 290 | // Any fields |
| 291 | RunValidTextFormatTest("AnyField", REQUIRED, |
| 292 | R"( |
| 293 | optional_any: { |
| 294 | [type.googleapis.com/protobuf_test_messages.proto3.TestAllTypesProto3] { |
| 295 | optional_int32: 12345 |
| 296 | } |
| 297 | } |
| 298 | )"); |
| 299 | RunValidTextFormatTest("AnyFieldWithRawBytes", REQUIRED, |
| 300 | R"( |
| 301 | optional_any: { |
| 302 | type_url: "type.googleapis.com/protobuf_test_messages.proto3.TestAllTypesProto3" |
| 303 | value: "\b\271`" |
| 304 | } |
| 305 | )"); |
| 306 | ExpectParseFailure("AnyFieldWithInvalidType", REQUIRED, |
| 307 | R"( |
| 308 | optional_any: { |
| 309 | [type.googleapis.com/unknown] { |
| 310 | optional_int32: 12345 |
| 311 | } |
| 312 | } |
| 313 | )"); |
Yilun Chong | d8c2501 | 2019-02-22 18:13:33 +0800 | [diff] [blame] | 314 | } |
| 315 | |
| 316 | } // namespace protobuf |
| 317 | } // namespace google |