Commit b0500b37 authored by Josh Haberman's avatar Josh Haberman

Added support for Json and valid input to conformance tests.

This was enabled by the recent open-sourcing of JSON
support and MessageDifferencer.

MessageDifferencer allows the conformance suite to expand
because it allows us to write tests for payloads that parse
successfully.  To verify the testee's output payload, we
need to parse it back into a message and compare the message
instances.  Comparing output bytes vs. a golden message is
*not* valid, because protobufs do not have a canonical
encoding (especially in the presence of maps, which have
no prescribed serialization order).

We only add one small JSON test for now, but with the
framework in place we now have the foundation to dramatically
expand the coverage of the conformance test suite.

Also added the ability for the testee to skip tests that
exercise features that are unimplemented.  This allows
Java (which currently has no JSON support) to skip tests
involving JSON.

Change-Id: I697b4363da432b61ae3b638b4287c4cda1af4deb
parent fe500440
...@@ -54,7 +54,7 @@ class ConformanceJava { ...@@ -54,7 +54,7 @@ class ConformanceJava {
break; break;
} }
case JSON_PAYLOAD: { case JSON_PAYLOAD: {
return Conformance.ConformanceResponse.newBuilder().setRuntimeError("JSON not yet supported.").build(); return Conformance.ConformanceResponse.newBuilder().setSkipped("JSON not yet supported.").build();
} }
case PAYLOAD_NOT_SET: { case PAYLOAD_NOT_SET: {
throw new RuntimeException("Request didn't have payload."); throw new RuntimeException("Request didn't have payload.");
...@@ -65,7 +65,7 @@ class ConformanceJava { ...@@ -65,7 +65,7 @@ class ConformanceJava {
} }
} }
switch (request.getRequestedOutput()) { switch (request.getRequestedOutputFormat()) {
case UNSPECIFIED: case UNSPECIFIED:
throw new RuntimeException("Unspecified output format."); throw new RuntimeException("Unspecified output format.");
...@@ -73,7 +73,7 @@ class ConformanceJava { ...@@ -73,7 +73,7 @@ class ConformanceJava {
return Conformance.ConformanceResponse.newBuilder().setProtobufPayload(testMessage.toByteString()).build(); return Conformance.ConformanceResponse.newBuilder().setProtobufPayload(testMessage.toByteString()).build();
case JSON: case JSON:
return Conformance.ConformanceResponse.newBuilder().setRuntimeError("JSON not yet supported.").build(); return Conformance.ConformanceResponse.newBuilder().setSkipped("JSON not yet supported.").build();
default: { default: {
throw new RuntimeException("Unexpected request output."); throw new RuntimeException("Unexpected request output.");
......
...@@ -51,6 +51,12 @@ option java_package = "com.google.protobuf.conformance"; ...@@ -51,6 +51,12 @@ option java_package = "com.google.protobuf.conformance";
// - running as a sub-process may be more tricky in unusual environments like // - running as a sub-process may be more tricky in unusual environments like
// iOS apps, where fork/stdin/stdout are not available. // iOS apps, where fork/stdin/stdout are not available.
enum WireFormat {
UNSPECIFIED = 0;
PROTOBUF = 1;
JSON = 2;
}
// Represents a single test case's input. The testee should: // Represents a single test case's input. The testee should:
// //
// 1. parse this proto (which should always succeed) // 1. parse this proto (which should always succeed)
...@@ -64,14 +70,8 @@ message ConformanceRequest { ...@@ -64,14 +70,8 @@ message ConformanceRequest {
string json_payload = 2; string json_payload = 2;
} }
enum RequestedOutput {
UNSPECIFIED = 0;
PROTOBUF = 1;
JSON = 2;
}
// Which format should the testee serialize its message to? // Which format should the testee serialize its message to?
RequestedOutput requested_output = 3; WireFormat requested_output_format = 3;
} }
// Represents a single test case's output. // Represents a single test case's output.
...@@ -96,6 +96,10 @@ message ConformanceResponse { ...@@ -96,6 +96,10 @@ message ConformanceResponse {
// If the input was successfully parsed and the requested output was JSON, // If the input was successfully parsed and the requested output was JSON,
// serialize to JSON and set it in this field. // serialize to JSON and set it in this field.
string json_payload = 4; string json_payload = 4;
// For when the testee skipped the test, likely because a certain feature
// wasn't supported, like JSON input/output.
string skipped = 5;
} }
} }
......
...@@ -33,14 +33,33 @@ ...@@ -33,14 +33,33 @@
#include <unistd.h> #include <unistd.h>
#include "conformance.pb.h" #include "conformance.pb.h"
#include <google/protobuf/util/json_util.h>
#include <google/protobuf/util/type_resolver_util.h>
using std::string;
using conformance::ConformanceRequest; using conformance::ConformanceRequest;
using conformance::ConformanceResponse; using conformance::ConformanceResponse;
using conformance::TestAllTypes; using conformance::TestAllTypes;
using google::protobuf::Descriptor;
using google::protobuf::DescriptorPool;
using google::protobuf::internal::scoped_ptr;
using google::protobuf::util::BinaryToJsonString;
using google::protobuf::util::JsonToBinaryString;
using google::protobuf::util::NewTypeResolverForDescriptorPool;
using google::protobuf::util::Status;
using google::protobuf::util::TypeResolver;
using std::string;
static const char kTypeUrlPrefix[] = "type.googleapis.com";
static string GetTypeUrl(const Descriptor* message) {
return string(kTypeUrlPrefix) + "/" + message->full_name();
}
int test_count = 0; int test_count = 0;
bool verbose = false; bool verbose = false;
TypeResolver* type_resolver;
string* type_url;
bool CheckedRead(int fd, void *buf, size_t len) { bool CheckedRead(int fd, void *buf, size_t len) {
size_t ofs = 0; size_t ofs = 0;
...@@ -79,27 +98,43 @@ void DoTest(const ConformanceRequest& request, ConformanceResponse* response) { ...@@ -79,27 +98,43 @@ void DoTest(const ConformanceRequest& request, ConformanceResponse* response) {
} }
break; break;
case ConformanceRequest::kJsonPayload: case ConformanceRequest::kJsonPayload: {
response->set_runtime_error("JSON input is not yet supported."); string proto_binary;
Status status = JsonToBinaryString(type_resolver, *type_url,
request.json_payload(), &proto_binary);
if (!status.ok()) {
response->set_parse_error(string("Parse error: ") +
status.error_message().as_string());
return;
}
GOOGLE_CHECK(test_message.ParseFromString(proto_binary));
break; break;
}
case ConformanceRequest::PAYLOAD_NOT_SET: case ConformanceRequest::PAYLOAD_NOT_SET:
GOOGLE_LOG(FATAL) << "Request didn't have payload."; GOOGLE_LOG(FATAL) << "Request didn't have payload.";
break; break;
} }
switch (request.requested_output()) { switch (request.requested_output_format()) {
case ConformanceRequest::UNSPECIFIED: case conformance::UNSPECIFIED:
GOOGLE_LOG(FATAL) << "Unspecified output format"; GOOGLE_LOG(FATAL) << "Unspecified output format";
break; break;
case ConformanceRequest::PROTOBUF: case conformance::PROTOBUF:
test_message.SerializeToString(response->mutable_protobuf_payload()); GOOGLE_CHECK(
test_message.SerializeToString(response->mutable_protobuf_payload()));
break; break;
case ConformanceRequest::JSON: case conformance::JSON: {
response->set_runtime_error("JSON output is not yet supported."); string proto_binary;
GOOGLE_CHECK(test_message.SerializeToString(&proto_binary));
Status status = BinaryToJsonString(type_resolver, *type_url, proto_binary,
response->mutable_json_payload());
GOOGLE_CHECK(status.ok());
break; break;
}
} }
} }
...@@ -146,6 +181,9 @@ bool DoTestIo() { ...@@ -146,6 +181,9 @@ bool DoTestIo() {
} }
int main() { int main() {
type_resolver = NewTypeResolverForDescriptorPool(
kTypeUrlPrefix, DescriptorPool::generated_pool());
type_url = new string(GetTypeUrl(TestAllTypes::descriptor()));
while (1) { while (1) {
if (!DoTestIo()) { if (!DoTestIo()) {
fprintf(stderr, "conformance-cpp: received EOF from test runner " fprintf(stderr, "conformance-cpp: received EOF from test runner "
......
This diff is collapsed.
...@@ -39,6 +39,8 @@ ...@@ -39,6 +39,8 @@
#define CONFORMANCE_CONFORMANCE_TEST_H #define CONFORMANCE_CONFORMANCE_TEST_H
#include <string> #include <string>
#include <google/protobuf/stubs/common.h>
#include <google/protobuf/util/type_resolver.h>
#include <google/protobuf/wire_format_lite.h> #include <google/protobuf/wire_format_lite.h>
namespace conformance { namespace conformance {
...@@ -98,10 +100,22 @@ class ConformanceTestSuite { ...@@ -98,10 +100,22 @@ class ConformanceTestSuite {
private: private:
void ReportSuccess(const std::string& test_name); void ReportSuccess(const std::string& test_name);
void ReportFailure(const std::string& test_name, const char* fmt, ...); void ReportFailure(const string& test_name,
const conformance::ConformanceRequest& request,
const conformance::ConformanceResponse& response,
const char* fmt, ...);
void ReportSkip(const string& test_name,
const conformance::ConformanceRequest& request,
const conformance::ConformanceResponse& response);
void RunTest(const std::string& test_name, void RunTest(const std::string& test_name,
const conformance::ConformanceRequest& request, const conformance::ConformanceRequest& request,
conformance::ConformanceResponse* response); conformance::ConformanceResponse* response);
void RunValidInputTest(const string& test_name, const string& input,
conformance::WireFormat input_format,
const string& equivalent_text_format,
conformance::WireFormat requested_output);
void RunValidJsonTest(const string& test_name, const string& input_json,
const string& equivalent_text_format);
void ExpectParseFailureForProto(const std::string& proto, void ExpectParseFailureForProto(const std::string& proto,
const std::string& test_name); const std::string& test_name);
void ExpectHardParseFailureForProto(const std::string& proto, void ExpectHardParseFailureForProto(const std::string& proto,
...@@ -110,7 +124,7 @@ class ConformanceTestSuite { ...@@ -110,7 +124,7 @@ class ConformanceTestSuite {
bool CheckSetEmpty(const set<string>& set_to_check, const char* msg); bool CheckSetEmpty(const set<string>& set_to_check, const char* msg);
ConformanceTestRunner* runner_; ConformanceTestRunner* runner_;
int successes_; int successes_;
int failures_; int expected_failures_;
bool verbose_; bool verbose_;
std::string output_; std::string output_;
...@@ -127,6 +141,13 @@ class ConformanceTestSuite { ...@@ -127,6 +141,13 @@ class ConformanceTestSuite {
// The set of tests that succeeded, but weren't expected to. // The set of tests that succeeded, but weren't expected to.
std::set<std::string> unexpected_succeeding_tests_; std::set<std::string> unexpected_succeeding_tests_;
// The set of tests that the testee opted out of;
std::set<std::string> skipped_;
google::protobuf::internal::scoped_ptr<google::protobuf::util::TypeResolver>
type_resolver_;
std::string type_url_;
}; };
} // namespace protobuf } // namespace protobuf
......
...@@ -7,15 +7,15 @@ ...@@ -7,15 +7,15 @@
# TODO(haberman): insert links to corresponding bugs tracking the issue. # TODO(haberman): insert links to corresponding bugs tracking the issue.
# Should we use GitHub issues or the Google-internal bug tracker? # Should we use GitHub issues or the Google-internal bug tracker?
PrematureEofBeforeKnownRepeatedValue.MESSAGE ProtobufInput.PrematureEofBeforeKnownRepeatedValue.MESSAGE
PrematureEofInDelimitedDataForKnownNonRepeatedValue.MESSAGE ProtobufInput.PrematureEofInDelimitedDataForKnownNonRepeatedValue.MESSAGE
PrematureEofInDelimitedDataForKnownRepeatedValue.MESSAGE ProtobufInput.PrematureEofInDelimitedDataForKnownRepeatedValue.MESSAGE
PrematureEofInPackedField.BOOL ProtobufInput.PrematureEofInPackedField.BOOL
PrematureEofInPackedField.ENUM ProtobufInput.PrematureEofInPackedField.ENUM
PrematureEofInPackedField.INT32 ProtobufInput.PrematureEofInPackedField.INT32
PrematureEofInPackedField.INT64 ProtobufInput.PrematureEofInPackedField.INT64
PrematureEofInPackedField.SINT32 ProtobufInput.PrematureEofInPackedField.SINT32
PrematureEofInPackedField.SINT64 ProtobufInput.PrematureEofInPackedField.SINT64
PrematureEofInPackedField.UINT32 ProtobufInput.PrematureEofInPackedField.UINT32
PrematureEofInPackedField.UINT64 ProtobufInput.PrematureEofInPackedField.UINT64
PrematureEofInsideKnownRepeatedValue.MESSAGE ProtobufInput.PrematureEofInsideKnownRepeatedValue.MESSAGE
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment