| OLD | NEW |
| (Empty) |
| 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 <stdarg.h> | |
| 32 #include <string> | |
| 33 | |
| 34 #include "conformance.pb.h" | |
| 35 #include "conformance_test.h" | |
| 36 #include <google/protobuf/stubs/common.h> | |
| 37 #include <google/protobuf/stubs/stringprintf.h> | |
| 38 #include <google/protobuf/text_format.h> | |
| 39 #include <google/protobuf/util/json_util.h> | |
| 40 #include <google/protobuf/util/message_differencer.h> | |
| 41 #include <google/protobuf/util/type_resolver_util.h> | |
| 42 #include <google/protobuf/wire_format_lite.h> | |
| 43 | |
| 44 using conformance::ConformanceRequest; | |
| 45 using conformance::ConformanceResponse; | |
| 46 using conformance::TestAllTypes; | |
| 47 using conformance::WireFormat; | |
| 48 using google::protobuf::Descriptor; | |
| 49 using google::protobuf::FieldDescriptor; | |
| 50 using google::protobuf::internal::WireFormatLite; | |
| 51 using google::protobuf::TextFormat; | |
| 52 using google::protobuf::util::JsonToBinaryString; | |
| 53 using google::protobuf::util::MessageDifferencer; | |
| 54 using google::protobuf::util::NewTypeResolverForDescriptorPool; | |
| 55 using google::protobuf::util::Status; | |
| 56 using std::string; | |
| 57 | |
| 58 namespace { | |
| 59 | |
| 60 static const char kTypeUrlPrefix[] = "type.googleapis.com"; | |
| 61 | |
| 62 static string GetTypeUrl(const Descriptor* message) { | |
| 63 return string(kTypeUrlPrefix) + "/" + message->full_name(); | |
| 64 } | |
| 65 | |
| 66 /* Routines for building arbitrary protos *************************************/ | |
| 67 | |
| 68 // We would use CodedOutputStream except that we want more freedom to build | |
| 69 // arbitrary protos (even invalid ones). | |
| 70 | |
| 71 const string empty; | |
| 72 | |
| 73 string cat(const string& a, const string& b, | |
| 74 const string& c = empty, | |
| 75 const string& d = empty, | |
| 76 const string& e = empty, | |
| 77 const string& f = empty, | |
| 78 const string& g = empty, | |
| 79 const string& h = empty, | |
| 80 const string& i = empty, | |
| 81 const string& j = empty, | |
| 82 const string& k = empty, | |
| 83 const string& l = empty) { | |
| 84 string ret; | |
| 85 ret.reserve(a.size() + b.size() + c.size() + d.size() + e.size() + f.size() + | |
| 86 g.size() + h.size() + i.size() + j.size() + k.size() + l.size()); | |
| 87 ret.append(a); | |
| 88 ret.append(b); | |
| 89 ret.append(c); | |
| 90 ret.append(d); | |
| 91 ret.append(e); | |
| 92 ret.append(f); | |
| 93 ret.append(g); | |
| 94 ret.append(h); | |
| 95 ret.append(i); | |
| 96 ret.append(j); | |
| 97 ret.append(k); | |
| 98 ret.append(l); | |
| 99 return ret; | |
| 100 } | |
| 101 | |
| 102 // The maximum number of bytes that it takes to encode a 64-bit varint. | |
| 103 #define VARINT_MAX_LEN 10 | |
| 104 | |
| 105 size_t vencode64(uint64_t val, char *buf) { | |
| 106 if (val == 0) { buf[0] = 0; return 1; } | |
| 107 size_t i = 0; | |
| 108 while (val) { | |
| 109 uint8_t byte = val & 0x7fU; | |
| 110 val >>= 7; | |
| 111 if (val) byte |= 0x80U; | |
| 112 buf[i++] = byte; | |
| 113 } | |
| 114 return i; | |
| 115 } | |
| 116 | |
| 117 string varint(uint64_t x) { | |
| 118 char buf[VARINT_MAX_LEN]; | |
| 119 size_t len = vencode64(x, buf); | |
| 120 return string(buf, len); | |
| 121 } | |
| 122 | |
| 123 // TODO: proper byte-swapping for big-endian machines. | |
| 124 string fixed32(void *data) { return string(static_cast<char*>(data), 4); } | |
| 125 string fixed64(void *data) { return string(static_cast<char*>(data), 8); } | |
| 126 | |
| 127 string delim(const string& buf) { return cat(varint(buf.size()), buf); } | |
| 128 string uint32(uint32_t u32) { return fixed32(&u32); } | |
| 129 string uint64(uint64_t u64) { return fixed64(&u64); } | |
| 130 string flt(float f) { return fixed32(&f); } | |
| 131 string dbl(double d) { return fixed64(&d); } | |
| 132 string zz32(int32_t x) { return varint(WireFormatLite::ZigZagEncode32(x)); } | |
| 133 string zz64(int64_t x) { return varint(WireFormatLite::ZigZagEncode64(x)); } | |
| 134 | |
| 135 string tag(uint32_t fieldnum, char wire_type) { | |
| 136 return varint((fieldnum << 3) | wire_type); | |
| 137 } | |
| 138 | |
| 139 string submsg(uint32_t fn, const string& buf) { | |
| 140 return cat( tag(fn, WireFormatLite::WIRETYPE_LENGTH_DELIMITED), delim(buf) ); | |
| 141 } | |
| 142 | |
| 143 #define UNKNOWN_FIELD 666 | |
| 144 | |
| 145 uint32_t GetFieldNumberForType(FieldDescriptor::Type type, bool repeated) { | |
| 146 const Descriptor* d = TestAllTypes().GetDescriptor(); | |
| 147 for (int i = 0; i < d->field_count(); i++) { | |
| 148 const FieldDescriptor* f = d->field(i); | |
| 149 if (f->type() == type && f->is_repeated() == repeated) { | |
| 150 return f->number(); | |
| 151 } | |
| 152 } | |
| 153 GOOGLE_LOG(FATAL) << "Couldn't find field with type " << (int)type; | |
| 154 return 0; | |
| 155 } | |
| 156 | |
| 157 string UpperCase(string str) { | |
| 158 for (int i = 0; i < str.size(); i++) { | |
| 159 str[i] = toupper(str[i]); | |
| 160 } | |
| 161 return str; | |
| 162 } | |
| 163 | |
| 164 } // anonymous namespace | |
| 165 | |
| 166 namespace google { | |
| 167 namespace protobuf { | |
| 168 | |
| 169 void ConformanceTestSuite::ReportSuccess(const string& test_name) { | |
| 170 if (expected_to_fail_.erase(test_name) != 0) { | |
| 171 StringAppendF(&output_, | |
| 172 "ERROR: test %s is in the failure list, but test succeeded. " | |
| 173 "Remove it from the failure list.\n", | |
| 174 test_name.c_str()); | |
| 175 unexpected_succeeding_tests_.insert(test_name); | |
| 176 } | |
| 177 successes_++; | |
| 178 } | |
| 179 | |
| 180 void ConformanceTestSuite::ReportFailure(const string& test_name, | |
| 181 const ConformanceRequest& request, | |
| 182 const ConformanceResponse& response, | |
| 183 const char* fmt, ...) { | |
| 184 if (expected_to_fail_.erase(test_name) == 1) { | |
| 185 expected_failures_++; | |
| 186 if (!verbose_) | |
| 187 return; | |
| 188 } else { | |
| 189 StringAppendF(&output_, "ERROR, test=%s: ", test_name.c_str()); | |
| 190 unexpected_failing_tests_.insert(test_name); | |
| 191 } | |
| 192 va_list args; | |
| 193 va_start(args, fmt); | |
| 194 StringAppendV(&output_, fmt, args); | |
| 195 va_end(args); | |
| 196 StringAppendF(&output_, " request=%s, response=%s\n", | |
| 197 request.ShortDebugString().c_str(), | |
| 198 response.ShortDebugString().c_str()); | |
| 199 } | |
| 200 | |
| 201 void ConformanceTestSuite::ReportSkip(const string& test_name, | |
| 202 const ConformanceRequest& request, | |
| 203 const ConformanceResponse& response) { | |
| 204 if (verbose_) { | |
| 205 StringAppendF(&output_, "SKIPPED, test=%s request=%s, response=%s\n", | |
| 206 test_name.c_str(), request.ShortDebugString().c_str(), | |
| 207 response.ShortDebugString().c_str()); | |
| 208 } | |
| 209 skipped_.insert(test_name); | |
| 210 } | |
| 211 | |
| 212 void ConformanceTestSuite::RunTest(const string& test_name, | |
| 213 const ConformanceRequest& request, | |
| 214 ConformanceResponse* response) { | |
| 215 if (test_names_.insert(test_name).second == false) { | |
| 216 GOOGLE_LOG(FATAL) << "Duplicated test name: " << test_name; | |
| 217 } | |
| 218 | |
| 219 string serialized_request; | |
| 220 string serialized_response; | |
| 221 request.SerializeToString(&serialized_request); | |
| 222 | |
| 223 runner_->RunTest(serialized_request, &serialized_response); | |
| 224 | |
| 225 if (!response->ParseFromString(serialized_response)) { | |
| 226 response->Clear(); | |
| 227 response->set_runtime_error("response proto could not be parsed."); | |
| 228 } | |
| 229 | |
| 230 if (verbose_) { | |
| 231 StringAppendF(&output_, "conformance test: name=%s, request=%s, response=%s\
n", | |
| 232 test_name.c_str(), | |
| 233 request.ShortDebugString().c_str(), | |
| 234 response->ShortDebugString().c_str()); | |
| 235 } | |
| 236 } | |
| 237 | |
| 238 void ConformanceTestSuite::RunValidInputTest( | |
| 239 const string& test_name, const string& input, WireFormat input_format, | |
| 240 const string& equivalent_text_format, WireFormat requested_output) { | |
| 241 TestAllTypes reference_message; | |
| 242 GOOGLE_CHECK( | |
| 243 TextFormat::ParseFromString(equivalent_text_format, &reference_message)); | |
| 244 | |
| 245 ConformanceRequest request; | |
| 246 ConformanceResponse response; | |
| 247 | |
| 248 switch (input_format) { | |
| 249 case conformance::PROTOBUF: | |
| 250 request.set_protobuf_payload(input); | |
| 251 break; | |
| 252 | |
| 253 case conformance::JSON: | |
| 254 request.set_json_payload(input); | |
| 255 break; | |
| 256 | |
| 257 case conformance::UNSPECIFIED: | |
| 258 GOOGLE_LOG(FATAL) << "Unspecified input format"; | |
| 259 | |
| 260 } | |
| 261 | |
| 262 request.set_requested_output_format(requested_output); | |
| 263 | |
| 264 RunTest(test_name, request, &response); | |
| 265 | |
| 266 TestAllTypes test_message; | |
| 267 | |
| 268 switch (response.result_case()) { | |
| 269 case ConformanceResponse::kParseError: | |
| 270 case ConformanceResponse::kRuntimeError: | |
| 271 ReportFailure(test_name, request, response, | |
| 272 "Failed to parse valid JSON input."); | |
| 273 return; | |
| 274 | |
| 275 case ConformanceResponse::kSkipped: | |
| 276 ReportSkip(test_name, request, response); | |
| 277 return; | |
| 278 | |
| 279 case ConformanceResponse::kJsonPayload: { | |
| 280 if (requested_output != conformance::JSON) { | |
| 281 ReportFailure( | |
| 282 test_name, request, response, | |
| 283 "Test was asked for protobuf output but provided JSON instead."); | |
| 284 return; | |
| 285 } | |
| 286 string binary_protobuf; | |
| 287 Status status = | |
| 288 JsonToBinaryString(type_resolver_.get(), type_url_, | |
| 289 response.json_payload(), &binary_protobuf); | |
| 290 if (!status.ok()) { | |
| 291 ReportFailure(test_name, request, response, | |
| 292 "JSON output we received from test was unparseable."); | |
| 293 return; | |
| 294 } | |
| 295 | |
| 296 GOOGLE_CHECK(test_message.ParseFromString(binary_protobuf)); | |
| 297 break; | |
| 298 } | |
| 299 | |
| 300 case ConformanceResponse::kProtobufPayload: { | |
| 301 if (requested_output != conformance::PROTOBUF) { | |
| 302 ReportFailure( | |
| 303 test_name, request, response, | |
| 304 "Test was asked for JSON output but provided protobuf instead."); | |
| 305 return; | |
| 306 } | |
| 307 | |
| 308 if (!test_message.ParseFromString(response.protobuf_payload())) { | |
| 309 ReportFailure(test_name, request, response, | |
| 310 "Protobuf output we received from test was unparseable."); | |
| 311 return; | |
| 312 } | |
| 313 | |
| 314 break; | |
| 315 } | |
| 316 } | |
| 317 | |
| 318 MessageDifferencer differencer; | |
| 319 string differences; | |
| 320 differencer.ReportDifferencesToString(&differences); | |
| 321 | |
| 322 if (differencer.Equals(reference_message, test_message)) { | |
| 323 ReportSuccess(test_name); | |
| 324 } else { | |
| 325 ReportFailure(test_name, request, response, | |
| 326 "Output was not equivalent to reference message: %s.", | |
| 327 differences.c_str()); | |
| 328 } | |
| 329 } | |
| 330 | |
| 331 // Expect that this precise protobuf will cause a parse error. | |
| 332 void ConformanceTestSuite::ExpectParseFailureForProto( | |
| 333 const string& proto, const string& test_name) { | |
| 334 ConformanceRequest request; | |
| 335 ConformanceResponse response; | |
| 336 request.set_protobuf_payload(proto); | |
| 337 string effective_test_name = "ProtobufInput." + test_name; | |
| 338 | |
| 339 // We don't expect output, but if the program erroneously accepts the protobuf | |
| 340 // we let it send its response as this. We must not leave it unspecified. | |
| 341 request.set_requested_output_format(conformance::PROTOBUF); | |
| 342 | |
| 343 RunTest(effective_test_name, request, &response); | |
| 344 if (response.result_case() == ConformanceResponse::kParseError) { | |
| 345 ReportSuccess(effective_test_name); | |
| 346 } else { | |
| 347 ReportFailure(effective_test_name, request, response, | |
| 348 "Should have failed to parse, but didn't."); | |
| 349 } | |
| 350 } | |
| 351 | |
| 352 // Expect that this protobuf will cause a parse error, even if it is followed | |
| 353 // by valid protobuf data. We can try running this twice: once with this | |
| 354 // data verbatim and once with this data followed by some valid data. | |
| 355 // | |
| 356 // TODO(haberman): implement the second of these. | |
| 357 void ConformanceTestSuite::ExpectHardParseFailureForProto( | |
| 358 const string& proto, const string& test_name) { | |
| 359 return ExpectParseFailureForProto(proto, test_name); | |
| 360 } | |
| 361 | |
| 362 void ConformanceTestSuite::RunValidJsonTest( | |
| 363 const string& test_name, const string& input_json, | |
| 364 const string& equivalent_text_format) { | |
| 365 RunValidInputTest("JsonInput." + test_name + ".JsonOutput", input_json, | |
| 366 conformance::JSON, equivalent_text_format, | |
| 367 conformance::PROTOBUF); | |
| 368 RunValidInputTest("JsonInput." + test_name + ".ProtobufOutput", input_json, co
nformance::JSON, | |
| 369 equivalent_text_format, conformance::JSON); | |
| 370 } | |
| 371 | |
| 372 void ConformanceTestSuite::TestPrematureEOFForType(FieldDescriptor::Type type) { | |
| 373 // Incomplete values for each wire type. | |
| 374 static const string incompletes[6] = { | |
| 375 string("\x80"), // VARINT | |
| 376 string("abcdefg"), // 64BIT | |
| 377 string("\x80"), // DELIMITED (partial length) | |
| 378 string(), // START_GROUP (no value required) | |
| 379 string(), // END_GROUP (no value required) | |
| 380 string("abc") // 32BIT | |
| 381 }; | |
| 382 | |
| 383 uint32_t fieldnum = GetFieldNumberForType(type, false); | |
| 384 uint32_t rep_fieldnum = GetFieldNumberForType(type, true); | |
| 385 WireFormatLite::WireType wire_type = WireFormatLite::WireTypeForFieldType( | |
| 386 static_cast<WireFormatLite::FieldType>(type)); | |
| 387 const string& incomplete = incompletes[wire_type]; | |
| 388 const string type_name = | |
| 389 UpperCase(string(".") + FieldDescriptor::TypeName(type)); | |
| 390 | |
| 391 ExpectParseFailureForProto( | |
| 392 tag(fieldnum, wire_type), | |
| 393 "PrematureEofBeforeKnownNonRepeatedValue" + type_name); | |
| 394 | |
| 395 ExpectParseFailureForProto( | |
| 396 tag(rep_fieldnum, wire_type), | |
| 397 "PrematureEofBeforeKnownRepeatedValue" + type_name); | |
| 398 | |
| 399 ExpectParseFailureForProto( | |
| 400 tag(UNKNOWN_FIELD, wire_type), | |
| 401 "PrematureEofBeforeUnknownValue" + type_name); | |
| 402 | |
| 403 ExpectParseFailureForProto( | |
| 404 cat( tag(fieldnum, wire_type), incomplete ), | |
| 405 "PrematureEofInsideKnownNonRepeatedValue" + type_name); | |
| 406 | |
| 407 ExpectParseFailureForProto( | |
| 408 cat( tag(rep_fieldnum, wire_type), incomplete ), | |
| 409 "PrematureEofInsideKnownRepeatedValue" + type_name); | |
| 410 | |
| 411 ExpectParseFailureForProto( | |
| 412 cat( tag(UNKNOWN_FIELD, wire_type), incomplete ), | |
| 413 "PrematureEofInsideUnknownValue" + type_name); | |
| 414 | |
| 415 if (wire_type == WireFormatLite::WIRETYPE_LENGTH_DELIMITED) { | |
| 416 ExpectParseFailureForProto( | |
| 417 cat( tag(fieldnum, wire_type), varint(1) ), | |
| 418 "PrematureEofInDelimitedDataForKnownNonRepeatedValue" + type_name); | |
| 419 | |
| 420 ExpectParseFailureForProto( | |
| 421 cat( tag(rep_fieldnum, wire_type), varint(1) ), | |
| 422 "PrematureEofInDelimitedDataForKnownRepeatedValue" + type_name); | |
| 423 | |
| 424 // EOF in the middle of delimited data for unknown value. | |
| 425 ExpectParseFailureForProto( | |
| 426 cat( tag(UNKNOWN_FIELD, wire_type), varint(1) ), | |
| 427 "PrematureEofInDelimitedDataForUnknownValue" + type_name); | |
| 428 | |
| 429 if (type == FieldDescriptor::TYPE_MESSAGE) { | |
| 430 // Submessage ends in the middle of a value. | |
| 431 string incomplete_submsg = | |
| 432 cat( tag(WireFormatLite::TYPE_INT32, WireFormatLite::WIRETYPE_VARINT), | |
| 433 incompletes[WireFormatLite::WIRETYPE_VARINT] ); | |
| 434 ExpectHardParseFailureForProto( | |
| 435 cat( tag(fieldnum, WireFormatLite::WIRETYPE_LENGTH_DELIMITED), | |
| 436 varint(incomplete_submsg.size()), | |
| 437 incomplete_submsg ), | |
| 438 "PrematureEofInSubmessageValue" + type_name); | |
| 439 } | |
| 440 } else if (type != FieldDescriptor::TYPE_GROUP) { | |
| 441 // Non-delimited, non-group: eligible for packing. | |
| 442 | |
| 443 // Packed region ends in the middle of a value. | |
| 444 ExpectHardParseFailureForProto( | |
| 445 cat( tag(rep_fieldnum, WireFormatLite::WIRETYPE_LENGTH_DELIMITED), | |
| 446 varint(incomplete.size()), | |
| 447 incomplete ), | |
| 448 "PrematureEofInPackedFieldValue" + type_name); | |
| 449 | |
| 450 // EOF in the middle of packed region. | |
| 451 ExpectParseFailureForProto( | |
| 452 cat( tag(rep_fieldnum, WireFormatLite::WIRETYPE_LENGTH_DELIMITED), | |
| 453 varint(1) ), | |
| 454 "PrematureEofInPackedField" + type_name); | |
| 455 } | |
| 456 } | |
| 457 | |
| 458 void ConformanceTestSuite::SetFailureList(const vector<string>& failure_list) { | |
| 459 expected_to_fail_.clear(); | |
| 460 std::copy(failure_list.begin(), failure_list.end(), | |
| 461 std::inserter(expected_to_fail_, expected_to_fail_.end())); | |
| 462 } | |
| 463 | |
| 464 bool ConformanceTestSuite::CheckSetEmpty(const set<string>& set_to_check, | |
| 465 const char* msg) { | |
| 466 if (set_to_check.empty()) { | |
| 467 return true; | |
| 468 } else { | |
| 469 StringAppendF(&output_, "\n"); | |
| 470 StringAppendF(&output_, "%s:\n", msg); | |
| 471 for (set<string>::const_iterator iter = set_to_check.begin(); | |
| 472 iter != set_to_check.end(); ++iter) { | |
| 473 StringAppendF(&output_, " %s\n", iter->c_str()); | |
| 474 } | |
| 475 StringAppendF(&output_, "\n"); | |
| 476 return false; | |
| 477 } | |
| 478 } | |
| 479 | |
| 480 bool ConformanceTestSuite::RunSuite(ConformanceTestRunner* runner, | |
| 481 std::string* output) { | |
| 482 runner_ = runner; | |
| 483 successes_ = 0; | |
| 484 expected_failures_ = 0; | |
| 485 skipped_.clear(); | |
| 486 test_names_.clear(); | |
| 487 unexpected_failing_tests_.clear(); | |
| 488 unexpected_succeeding_tests_.clear(); | |
| 489 type_resolver_.reset(NewTypeResolverForDescriptorPool( | |
| 490 kTypeUrlPrefix, DescriptorPool::generated_pool())); | |
| 491 type_url_ = GetTypeUrl(TestAllTypes::descriptor()); | |
| 492 | |
| 493 output_ = "\nCONFORMANCE TEST BEGIN ====================================\n\n"; | |
| 494 | |
| 495 for (int i = 1; i <= FieldDescriptor::MAX_TYPE; i++) { | |
| 496 if (i == FieldDescriptor::TYPE_GROUP) continue; | |
| 497 TestPrematureEOFForType(static_cast<FieldDescriptor::Type>(i)); | |
| 498 } | |
| 499 | |
| 500 RunValidJsonTest("HelloWorld", "{\"optionalString\":\"Hello, World!\"}", | |
| 501 "optional_string: 'Hello, World!'"); | |
| 502 | |
| 503 bool ok = | |
| 504 CheckSetEmpty(expected_to_fail_, | |
| 505 "These tests were listed in the failure list, but they " | |
| 506 "don't exist. Remove them from the failure list") && | |
| 507 | |
| 508 CheckSetEmpty(unexpected_failing_tests_, | |
| 509 "These tests failed. If they can't be fixed right now, " | |
| 510 "you can add them to the failure list so the overall " | |
| 511 "suite can succeed") && | |
| 512 | |
| 513 CheckSetEmpty(unexpected_succeeding_tests_, | |
| 514 "These tests succeeded, even though they were listed in " | |
| 515 "the failure list. Remove them from the failure list"); | |
| 516 | |
| 517 CheckSetEmpty(skipped_, | |
| 518 "These tests were skipped (probably because support for some " | |
| 519 "features is not implemented)"); | |
| 520 | |
| 521 StringAppendF(&output_, | |
| 522 "CONFORMANCE SUITE %s: %d successes, %d skipped, " | |
| 523 "%d expected failures, %d unexpected failures.\n", | |
| 524 ok ? "PASSED" : "FAILED", successes_, skipped_.size(), | |
| 525 expected_failures_, unexpected_failing_tests_.size()); | |
| 526 StringAppendF(&output_, "\n"); | |
| 527 | |
| 528 output->assign(output_); | |
| 529 | |
| 530 return ok; | |
| 531 } | |
| 532 | |
| 533 } // namespace protobuf | |
| 534 } // namespace google | |
| OLD | NEW |