@ -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_ = " \n CONFORMANCE 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 ;