1#!/usr/bin/env python3 2# Protocol Buffers - Google's data interchange format 3# Copyright 2008 Google Inc. All rights reserved. 4# 5# Use of this source code is governed by a BSD-style 6# license that can be found in the LICENSE file or at 7# https://developers.google.com/open-source/licenses/bsd 8 9"""A conformance test implementation for the Python protobuf library. 10 11See conformance.proto for more information. 12""" 13 14import struct 15import sys 16from google.protobuf import json_format 17from google.protobuf import message 18from google.protobuf import text_format 19from google.protobuf import test_messages_proto2_pb2 20from google.protobuf import test_messages_proto3_pb2 21from conformance import conformance_pb2 22from conformance.test_protos import test_messages_edition2023_pb2 23from editions.golden import test_messages_proto2_editions_pb2 24from editions.golden import test_messages_proto3_editions_pb2 25 26test_count = 0 27verbose = False 28 29 30class ProtocolError(Exception): 31 pass 32 33 34def _create_test_message(type): 35 if type == "protobuf_test_messages.proto2.TestAllTypesProto2": 36 return test_messages_proto2_pb2.TestAllTypesProto2() 37 if type == "protobuf_test_messages.proto3.TestAllTypesProto3": 38 return test_messages_proto3_pb2.TestAllTypesProto3() 39 if type == "protobuf_test_messages.editions.TestAllTypesEdition2023": 40 return test_messages_edition2023_pb2.TestAllTypesEdition2023() 41 if type == "protobuf_test_messages.editions.proto2.TestAllTypesProto2": 42 return test_messages_proto2_editions_pb2.TestAllTypesProto2() 43 if type == "protobuf_test_messages.editions.proto3.TestAllTypesProto3": 44 return test_messages_proto3_editions_pb2.TestAllTypesProto3() 45 return None 46 47 48def do_test(request): 49 response = conformance_pb2.ConformanceResponse() 50 51 if request.message_type == "conformance.FailureSet": 52 failure_set = conformance_pb2.FailureSet() 53 failures = [] 54 # TODO: Remove, this is a hack to detect if the old vs new 55 # parser is used by the cpp code. Relying on a bug in the old parser. 56 hack_proto = test_messages_proto2_pb2.TestAllTypesProto2() 57 old_parser = True 58 try: 59 hack_proto.ParseFromString(b"\322\002\001") 60 except message.DecodeError as e: 61 old_parser = False 62 if old_parser: 63 # the string above is one of the failing conformance test strings of the 64 # old parser. If we succeed the c++ implementation is using the old 65 # parser so we add the list of failing conformance tests. 66 failures = [ 67 "Required.Proto3.ProtobufInput.PrematureEofInDelimitedDataForKnownNonRepeatedValue.MESSAGE", 68 "Required.Proto3.ProtobufInput.PrematureEofInDelimitedDataForKnownRepeatedValue.MESSAGE", 69 "Required.Proto3.ProtobufInput.PrematureEofInPackedField.BOOL", 70 "Required.Proto3.ProtobufInput.PrematureEofInPackedField.DOUBLE", 71 "Required.Proto3.ProtobufInput.PrematureEofInPackedField.ENUM", 72 "Required.Proto3.ProtobufInput.PrematureEofInPackedField.FIXED32", 73 "Required.Proto3.ProtobufInput.PrematureEofInPackedField.FIXED64", 74 "Required.Proto3.ProtobufInput.PrematureEofInPackedField.FLOAT", 75 "Required.Proto3.ProtobufInput.PrematureEofInPackedField.INT32", 76 "Required.Proto3.ProtobufInput.PrematureEofInPackedField.INT64", 77 "Required.Proto3.ProtobufInput.PrematureEofInPackedField.SFIXED32", 78 "Required.Proto3.ProtobufInput.PrematureEofInPackedField.SFIXED64", 79 "Required.Proto3.ProtobufInput.PrematureEofInPackedField.SINT32", 80 "Required.Proto3.ProtobufInput.PrematureEofInPackedField.SINT64", 81 "Required.Proto3.ProtobufInput.PrematureEofInPackedField.UINT32", 82 "Required.Proto3.ProtobufInput.PrematureEofInPackedField.UINT64", 83 "Required.Proto2.ProtobufInput.PrematureEofInDelimitedDataForKnownNonRepeatedValue.MESSAGE", 84 "Required.Proto2.ProtobufInput.PrematureEofInDelimitedDataForKnownRepeatedValue.MESSAGE", 85 "Required.Proto2.ProtobufInput.PrematureEofInPackedField.BOOL", 86 "Required.Proto2.ProtobufInput.PrematureEofInPackedField.DOUBLE", 87 "Required.Proto2.ProtobufInput.PrematureEofInPackedField.ENUM", 88 "Required.Proto2.ProtobufInput.PrematureEofInPackedField.FIXED32", 89 "Required.Proto2.ProtobufInput.PrematureEofInPackedField.FIXED64", 90 "Required.Proto2.ProtobufInput.PrematureEofInPackedField.FLOAT", 91 "Required.Proto2.ProtobufInput.PrematureEofInPackedField.INT32", 92 "Required.Proto2.ProtobufInput.PrematureEofInPackedField.INT64", 93 "Required.Proto2.ProtobufInput.PrematureEofInPackedField.SFIXED32", 94 "Required.Proto2.ProtobufInput.PrematureEofInPackedField.SFIXED64", 95 "Required.Proto2.ProtobufInput.PrematureEofInPackedField.SINT32", 96 "Required.Proto2.ProtobufInput.PrematureEofInPackedField.SINT64", 97 "Required.Proto2.ProtobufInput.PrematureEofInPackedField.UINT32", 98 "Required.Proto2.ProtobufInput.PrematureEofInPackedField.UINT64", 99 ] 100 for x in failures: 101 failure_set.test.append(conformance_pb2.TestStatus(name=x)) 102 response.protobuf_payload = failure_set.SerializeToString() 103 return response 104 105 isJson = request.WhichOneof("payload") == "json_payload" 106 test_message = _create_test_message(request.message_type) 107 108 if (not isJson) and (test_message is None): 109 raise ProtocolError("Protobuf request doesn't have specific payload type") 110 111 try: 112 if request.WhichOneof("payload") == "protobuf_payload": 113 try: 114 test_message.ParseFromString(request.protobuf_payload) 115 except message.DecodeError as e: 116 response.parse_error = str(e) 117 return response 118 119 elif request.WhichOneof("payload") == "json_payload": 120 try: 121 ignore_unknown_fields = ( 122 request.test_category 123 == conformance_pb2.JSON_IGNORE_UNKNOWN_PARSING_TEST 124 ) 125 json_format.Parse( 126 request.json_payload, test_message, ignore_unknown_fields 127 ) 128 except Exception as e: 129 response.parse_error = str(e) 130 return response 131 132 elif request.WhichOneof("payload") == "text_payload": 133 try: 134 text_format.Parse(request.text_payload, test_message) 135 except Exception as e: 136 response.parse_error = str(e) 137 return response 138 139 else: 140 raise ProtocolError("Request didn't have payload.") 141 142 if request.requested_output_format == conformance_pb2.UNSPECIFIED: 143 raise ProtocolError("Unspecified output format") 144 145 elif request.requested_output_format == conformance_pb2.PROTOBUF: 146 response.protobuf_payload = test_message.SerializeToString() 147 148 elif request.requested_output_format == conformance_pb2.JSON: 149 try: 150 response.json_payload = json_format.MessageToJson(test_message) 151 except Exception as e: 152 response.serialize_error = str(e) 153 return response 154 155 elif request.requested_output_format == conformance_pb2.TEXT_FORMAT: 156 response.text_payload = text_format.MessageToString( 157 test_message, print_unknown_fields=request.print_unknown_fields 158 ) 159 160 except Exception as e: 161 response.runtime_error = str(e) 162 163 return response 164 165 166def do_test_io(): 167 length_bytes = sys.stdin.buffer.read(4) 168 if len(length_bytes) == 0: 169 return False # EOF 170 elif len(length_bytes) != 4: 171 raise IOError("I/O error") 172 173 length = struct.unpack("<I", length_bytes)[0] 174 serialized_request = sys.stdin.buffer.read(length) 175 if len(serialized_request) != length: 176 raise IOError("I/O error") 177 178 request = conformance_pb2.ConformanceRequest() 179 request.ParseFromString(serialized_request) 180 181 response = do_test(request) 182 183 serialized_response = response.SerializeToString() 184 sys.stdout.buffer.write(struct.pack("<I", len(serialized_response))) 185 sys.stdout.buffer.write(serialized_response) 186 sys.stdout.buffer.flush() 187 188 if verbose: 189 sys.stderr.write( 190 "conformance_python: request=%s, response=%s\n" 191 % ( 192 request.ShortDebugString().c_str(), 193 response.ShortDebugString().c_str(), 194 ) 195 ) 196 197 global test_count 198 test_count += 1 199 200 return True 201 202 203while True: 204 if not do_test_io(): 205 sys.stderr.write( 206 "conformance_python: received EOF from test runner " 207 + "after %s tests, exiting\n" % (test_count,) 208 ) 209 sys.exit(0) 210