diff options
Diffstat (limited to 'conformance/conformance_test.cc')
-rw-r--r-- | conformance/conformance_test.cc | 186 |
1 files changed, 167 insertions, 19 deletions
diff --git a/conformance/conformance_test.cc b/conformance/conformance_test.cc index 7a7fc6f5..0ee201f3 100644 --- a/conformance/conformance_test.cc +++ b/conformance/conformance_test.cc @@ -35,18 +35,34 @@ #include "conformance_test.h" #include <google/protobuf/stubs/common.h> #include <google/protobuf/stubs/stringprintf.h> +#include <google/protobuf/text_format.h> +#include <google/protobuf/util/json_util.h> +#include <google/protobuf/util/message_differencer.h> +#include <google/protobuf/util/type_resolver_util.h> #include <google/protobuf/wire_format_lite.h> using conformance::ConformanceRequest; using conformance::ConformanceResponse; using conformance::TestAllTypes; +using conformance::WireFormat; using google::protobuf::Descriptor; using google::protobuf::FieldDescriptor; using google::protobuf::internal::WireFormatLite; +using google::protobuf::TextFormat; +using google::protobuf::util::JsonToBinaryString; +using google::protobuf::util::MessageDifferencer; +using google::protobuf::util::NewTypeResolverForDescriptorPool; +using google::protobuf::util::Status; using std::string; namespace { +static const char kTypeUrlPrefix[] = "type.googleapis.com"; + +static string GetTypeUrl(const Descriptor* message) { + return string(kTypeUrlPrefix) + "/" + message->full_name(); +} + /* Routines for building arbitrary protos *************************************/ // We would use CodedOutputStream except that we want more freedom to build @@ -162,9 +178,13 @@ void ConformanceTestSuite::ReportSuccess(const string& test_name) { } void ConformanceTestSuite::ReportFailure(const string& test_name, + const ConformanceRequest& request, + const ConformanceResponse& response, const char* fmt, ...) { if (expected_to_fail_.erase(test_name) == 1) { - StringAppendF(&output_, "FAILED AS EXPECTED, test=%s: ", test_name.c_str()); + expected_failures_++; + if (!verbose_) + return; } else { StringAppendF(&output_, "ERROR, test=%s: ", test_name.c_str()); unexpected_failing_tests_.insert(test_name); @@ -173,7 +193,20 @@ void ConformanceTestSuite::ReportFailure(const string& test_name, va_start(args, fmt); StringAppendV(&output_, fmt, args); va_end(args); - failures_++; + StringAppendF(&output_, " request=%s, response=%s\n", + request.ShortDebugString().c_str(), + response.ShortDebugString().c_str()); +} + +void ConformanceTestSuite::ReportSkip(const string& test_name, + const ConformanceRequest& request, + const ConformanceResponse& response) { + if (verbose_) { + StringAppendF(&output_, "SKIPPED, test=%s request=%s, response=%s\n", + test_name.c_str(), request.ShortDebugString().c_str(), + response.ShortDebugString().c_str()); + } + skipped_.insert(test_name); } void ConformanceTestSuite::RunTest(const string& test_name, @@ -202,26 +235,117 @@ void ConformanceTestSuite::RunTest(const string& test_name, } } +void ConformanceTestSuite::RunValidInputTest( + const string& test_name, const string& input, WireFormat input_format, + const string& equivalent_text_format, WireFormat requested_output) { + TestAllTypes reference_message; + GOOGLE_CHECK( + TextFormat::ParseFromString(equivalent_text_format, &reference_message)); + + ConformanceRequest request; + ConformanceResponse response; + + switch (input_format) { + case conformance::PROTOBUF: + request.set_protobuf_payload(input); + break; + + case conformance::JSON: + request.set_json_payload(input); + break; + + case conformance::UNSPECIFIED: + GOOGLE_LOG(FATAL) << "Unspecified input format"; + + } + + request.set_requested_output_format(requested_output); + + RunTest(test_name, request, &response); + + TestAllTypes test_message; + + switch (response.result_case()) { + case ConformanceResponse::kParseError: + case ConformanceResponse::kRuntimeError: + ReportFailure(test_name, request, response, + "Failed to parse valid JSON input."); + return; + + case ConformanceResponse::kSkipped: + ReportSkip(test_name, request, response); + return; + + case ConformanceResponse::kJsonPayload: { + if (requested_output != conformance::JSON) { + ReportFailure( + test_name, request, response, + "Test was asked for protobuf output but provided JSON instead."); + return; + } + string binary_protobuf; + Status status = + JsonToBinaryString(type_resolver_.get(), type_url_, + response.json_payload(), &binary_protobuf); + if (!status.ok()) { + ReportFailure(test_name, request, response, + "JSON output we received from test was unparseable."); + return; + } + + GOOGLE_CHECK(test_message.ParseFromString(binary_protobuf)); + break; + } + + case ConformanceResponse::kProtobufPayload: { + if (requested_output != conformance::PROTOBUF) { + ReportFailure( + test_name, request, response, + "Test was asked for JSON output but provided protobuf instead."); + return; + } + + if (!test_message.ParseFromString(response.protobuf_payload())) { + ReportFailure(test_name, request, response, + "Protobuf output we received from test was unparseable."); + return; + } + + break; + } + } + + MessageDifferencer differencer; + string differences; + differencer.ReportDifferencesToString(&differences); + + if (differencer.Equals(reference_message, test_message)) { + ReportSuccess(test_name); + } else { + ReportFailure(test_name, request, response, + "Output was not equivalent to reference message: %s.", + differences.c_str()); + } +} + // Expect that this precise protobuf will cause a parse error. void ConformanceTestSuite::ExpectParseFailureForProto( const string& proto, const string& test_name) { ConformanceRequest request; ConformanceResponse response; request.set_protobuf_payload(proto); + string effective_test_name = "ProtobufInput." + test_name; // 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. - request.set_requested_output(ConformanceRequest::PROTOBUF); + request.set_requested_output_format(conformance::PROTOBUF); - RunTest(test_name, request, &response); + RunTest(effective_test_name, request, &response); if (response.result_case() == ConformanceResponse::kParseError) { - ReportSuccess(test_name); + ReportSuccess(effective_test_name); } else { - ReportFailure(test_name, - "Should have failed to parse, but didn't. Request: %s, " - "response: %s\n", - request.ShortDebugString().c_str(), - response.ShortDebugString().c_str()); + ReportFailure(effective_test_name, request, response, + "Should have failed to parse, but didn't."); } } @@ -235,6 +359,16 @@ void ConformanceTestSuite::ExpectHardParseFailureForProto( return ExpectParseFailureForProto(proto, test_name); } +void ConformanceTestSuite::RunValidJsonTest( + const string& test_name, const string& input_json, + const string& equivalent_text_format) { + RunValidInputTest("JsonInput." + test_name + ".JsonOutput", input_json, + conformance::JSON, equivalent_text_format, + conformance::PROTOBUF); + RunValidInputTest("JsonInput." + test_name + ".ProtobufOutput", input_json, conformance::JSON, + equivalent_text_format, conformance::JSON); +} + void ConformanceTestSuite::TestPrematureEOFForType(FieldDescriptor::Type type) { // Incomplete values for each wire type. static const string incompletes[6] = { @@ -333,11 +467,12 @@ bool ConformanceTestSuite::CheckSetEmpty(const set<string>& set_to_check, return true; } else { StringAppendF(&output_, "\n"); - StringAppendF(&output_, "ERROR: %s:\n", msg); + StringAppendF(&output_, "%s:\n", msg); for (set<string>::const_iterator iter = set_to_check.begin(); iter != set_to_check.end(); ++iter) { - StringAppendF(&output_, "%s\n", iter->c_str()); + StringAppendF(&output_, " %s\n", iter->c_str()); } + StringAppendF(&output_, "\n"); return false; } } @@ -345,23 +480,25 @@ bool ConformanceTestSuite::CheckSetEmpty(const set<string>& set_to_check, bool ConformanceTestSuite::RunSuite(ConformanceTestRunner* runner, std::string* output) { runner_ = runner; - output_.clear(); successes_ = 0; - failures_ = 0; + expected_failures_ = 0; + skipped_.clear(); test_names_.clear(); unexpected_failing_tests_.clear(); unexpected_succeeding_tests_.clear(); + type_resolver_.reset(NewTypeResolverForDescriptorPool( + kTypeUrlPrefix, DescriptorPool::generated_pool())); + type_url_ = GetTypeUrl(TestAllTypes::descriptor()); + + output_ = "\nCONFORMANCE TEST BEGIN ====================================\n\n"; for (int i = 1; i <= FieldDescriptor::MAX_TYPE; i++) { if (i == FieldDescriptor::TYPE_GROUP) continue; TestPrematureEOFForType(static_cast<FieldDescriptor::Type>(i)); } - StringAppendF(&output_, "\n"); - StringAppendF(&output_, - "CONFORMANCE SUITE FINISHED: completed %d tests, %d successes, " - "%d failures.\n", - successes_ + failures_, successes_, failures_); + RunValidJsonTest("HelloWorld", "{\"optionalString\":\"Hello, World!\"}", + "optional_string: 'Hello, World!'"); bool ok = CheckSetEmpty(expected_to_fail_, @@ -377,6 +514,17 @@ bool ConformanceTestSuite::RunSuite(ConformanceTestRunner* runner, "These tests succeeded, even though they were listed in " "the failure list. Remove them from the failure list"); + CheckSetEmpty(skipped_, + "These tests were skipped (probably because support for some " + "features is not implemented)"); + + StringAppendF(&output_, + "CONFORMANCE SUITE %s: %d successes, %d skipped, " + "%d expected failures, %d unexpected failures.\n", + ok ? "PASSED" : "FAILED", successes_, skipped_.size(), + expected_failures_, unexpected_failing_tests_.size()); + StringAppendF(&output_, "\n"); + output->assign(output_); return ok; |