1#!/usr/bin/env python
2#
3# Protocol Buffers - Google's data interchange format
4# Copyright 2008 Google Inc.  All rights reserved.
5# https://developers.google.com/protocol-buffers/
6#
7# Redistribution and use in source and binary forms, with or without
8# modification, are permitted provided that the following conditions are
9# met:
10#
11#     * Redistributions of source code must retain the above copyright
12# notice, this list of conditions and the following disclaimer.
13#     * Redistributions in binary form must reproduce the above
14# copyright notice, this list of conditions and the following disclaimer
15# in the documentation and/or other materials provided with the
16# distribution.
17#     * Neither the name of Google Inc. nor the names of its
18# contributors may be used to endorse or promote products derived from
19# this software without specific prior written permission.
20#
21# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
24# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
25# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
26# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
27# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
28# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
29# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
30# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
31# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32
33"""A conformance test implementation for the Python protobuf library.
34
35See conformance.proto for more information.
36"""
37
38import struct
39import sys
40import os
41from google.protobuf import descriptor
42from google.protobuf import descriptor_pool
43from google.protobuf import json_format
44from google.protobuf import message
45from google.protobuf import test_messages_proto3_pb2
46from google.protobuf import test_messages_proto2_pb2
47from google.protobuf import text_format
48import conformance_pb2
49
50sys.stdout = os.fdopen(sys.stdout.fileno(), 'wb', 0)
51sys.stdin = os.fdopen(sys.stdin.fileno(), 'rb', 0)
52
53test_count = 0
54verbose = False
55
56class ProtocolError(Exception):
57  pass
58
59def do_test(request):
60  response = conformance_pb2.ConformanceResponse()
61
62  if request.message_type == "conformance.FailureSet":
63    failure_set = conformance_pb2.FailureSet()
64    failures = []
65    # TODO(gerbens): Remove, this is a hack to detect if the old vs new
66    # parser is used by the cpp code. Relying on a bug in the old parser.
67    hack_proto = test_messages_proto2_pb2.TestAllTypesProto2()
68    old_parser = True
69    try:
70      hack_proto.ParseFromString(b"\322\002\001")
71    except message.DecodeError as e:
72      old_parser = False
73    if old_parser:
74      # the string above is one of the failing conformance test strings of the
75      # old parser. If we succeed the c++ implementation is using the old
76      # parser so we add the list of failing conformance tests.
77      failures = [
78          "Required.Proto3.ProtobufInput.PrematureEofInDelimitedDataForKnownNonRepeatedValue.MESSAGE",
79          "Required.Proto3.ProtobufInput.PrematureEofInDelimitedDataForKnownRepeatedValue.MESSAGE",
80          "Required.Proto3.ProtobufInput.PrematureEofInPackedField.BOOL",
81          "Required.Proto3.ProtobufInput.PrematureEofInPackedField.DOUBLE",
82          "Required.Proto3.ProtobufInput.PrematureEofInPackedField.ENUM",
83          "Required.Proto3.ProtobufInput.PrematureEofInPackedField.FIXED32",
84          "Required.Proto3.ProtobufInput.PrematureEofInPackedField.FIXED64",
85          "Required.Proto3.ProtobufInput.PrematureEofInPackedField.FLOAT",
86          "Required.Proto3.ProtobufInput.PrematureEofInPackedField.INT32",
87          "Required.Proto3.ProtobufInput.PrematureEofInPackedField.INT64",
88          "Required.Proto3.ProtobufInput.PrematureEofInPackedField.SFIXED32",
89          "Required.Proto3.ProtobufInput.PrematureEofInPackedField.SFIXED64",
90          "Required.Proto3.ProtobufInput.PrematureEofInPackedField.SINT32",
91          "Required.Proto3.ProtobufInput.PrematureEofInPackedField.SINT64",
92          "Required.Proto3.ProtobufInput.PrematureEofInPackedField.UINT32",
93          "Required.Proto3.ProtobufInput.PrematureEofInPackedField.UINT64",
94          "Required.Proto2.ProtobufInput.PrematureEofInDelimitedDataForKnownNonRepeatedValue.MESSAGE",
95          "Required.Proto2.ProtobufInput.PrematureEofInDelimitedDataForKnownRepeatedValue.MESSAGE",
96          "Required.Proto2.ProtobufInput.PrematureEofInPackedField.BOOL",
97          "Required.Proto2.ProtobufInput.PrematureEofInPackedField.DOUBLE",
98          "Required.Proto2.ProtobufInput.PrematureEofInPackedField.ENUM",
99          "Required.Proto2.ProtobufInput.PrematureEofInPackedField.FIXED32",
100          "Required.Proto2.ProtobufInput.PrematureEofInPackedField.FIXED64",
101          "Required.Proto2.ProtobufInput.PrematureEofInPackedField.FLOAT",
102          "Required.Proto2.ProtobufInput.PrematureEofInPackedField.INT32",
103          "Required.Proto2.ProtobufInput.PrematureEofInPackedField.INT64",
104          "Required.Proto2.ProtobufInput.PrematureEofInPackedField.SFIXED32",
105          "Required.Proto2.ProtobufInput.PrematureEofInPackedField.SFIXED64",
106          "Required.Proto2.ProtobufInput.PrematureEofInPackedField.SINT32",
107          "Required.Proto2.ProtobufInput.PrematureEofInPackedField.SINT64",
108          "Required.Proto2.ProtobufInput.PrematureEofInPackedField.UINT32",
109          "Required.Proto2.ProtobufInput.PrematureEofInPackedField.UINT64",
110      ]
111    for x in failures:
112      failure_set.failure.append(x)
113    response.protobuf_payload = failure_set.SerializeToString()
114    return response
115
116  isProto3 = (request.message_type == "protobuf_test_messages.proto3.TestAllTypesProto3")
117  isJson = (request.WhichOneof('payload') == 'json_payload')
118  isProto2 = (request.message_type == "protobuf_test_messages.proto2.TestAllTypesProto2")
119
120  if (not isProto3) and (not isJson) and (not isProto2):
121    raise ProtocolError("Protobuf request doesn't have specific payload type")
122
123  test_message = test_messages_proto2_pb2.TestAllTypesProto2() if isProto2 else \
124    test_messages_proto3_pb2.TestAllTypesProto3()
125
126  try:
127    if request.WhichOneof('payload') == 'protobuf_payload':
128      try:
129        test_message.ParseFromString(request.protobuf_payload)
130      except message.DecodeError as e:
131        response.parse_error = str(e)
132        return response
133
134    elif request.WhichOneof('payload') == 'json_payload':
135      try:
136        ignore_unknown_fields = \
137            request.test_category == \
138                conformance_pb2.JSON_IGNORE_UNKNOWN_PARSING_TEST
139        json_format.Parse(request.json_payload, test_message,
140                          ignore_unknown_fields)
141      except Exception as e:
142        response.parse_error = str(e)
143        return response
144
145    elif request.WhichOneof('payload') == 'text_payload':
146      try:
147        text_format.Parse(request.text_payload, test_message)
148      except Exception as e:
149        response.parse_error = str(e)
150        return response
151
152    else:
153      raise ProtocolError("Request didn't have payload.")
154
155    if request.requested_output_format == conformance_pb2.UNSPECIFIED:
156      raise ProtocolError("Unspecified output format")
157
158    elif request.requested_output_format == conformance_pb2.PROTOBUF:
159      response.protobuf_payload = test_message.SerializeToString()
160
161    elif request.requested_output_format == conformance_pb2.JSON:
162      try:
163        response.json_payload = json_format.MessageToJson(test_message)
164      except Exception as e:
165        response.serialize_error = str(e)
166        return response
167
168    elif request.requested_output_format == conformance_pb2.TEXT_FORMAT:
169      response.text_payload = text_format.MessageToString(
170          test_message, print_unknown_fields=request.print_unknown_fields)
171
172  except Exception as e:
173    response.runtime_error = str(e)
174
175  return response
176
177def do_test_io():
178  length_bytes = sys.stdin.read(4)
179  if len(length_bytes) == 0:
180    return False   # EOF
181  elif len(length_bytes) != 4:
182    raise IOError("I/O error")
183
184  # "I" is "unsigned int", so this depends on running on a platform with
185  # 32-bit "unsigned int" type.  The Python struct module unfortunately
186  # has no format specifier for uint32_t.
187  length = struct.unpack("<I", length_bytes)[0]
188  serialized_request = sys.stdin.read(length)
189  if len(serialized_request) != length:
190    raise IOError("I/O error")
191
192  request = conformance_pb2.ConformanceRequest()
193  request.ParseFromString(serialized_request)
194
195  response = do_test(request)
196
197  serialized_response = response.SerializeToString()
198  sys.stdout.write(struct.pack("<I", len(serialized_response)))
199  sys.stdout.write(serialized_response)
200  sys.stdout.flush()
201
202  if verbose:
203    sys.stderr.write("conformance_python: request=%s, response=%s\n" % (
204                       request.ShortDebugString().c_str(),
205                       response.ShortDebugString().c_str()))
206
207  global test_count
208  test_count += 1
209
210  return True
211
212while True:
213  if not do_test_io():
214    sys.stderr.write("conformance_python: received EOF from test runner " +
215                     "after %s tests, exiting\n" % (test_count))
216    sys.exit(0)
217