1 // Protocol Buffers - Google's data interchange format 2 // Copyright 2008 Google Inc. All rights reserved. 3 // https://developers.google.com/protocol-buffers/ 4 // 5 // Redistribution and use in source and binary forms, with or without 6 // modification, are permitted provided that the following conditions are 7 // met: 8 // 9 // * Redistributions of source code must retain the above copyright 10 // notice, this list of conditions and the following disclaimer. 11 // * Redistributions in binary form must reproduce the above 12 // copyright notice, this list of conditions and the following disclaimer 13 // in the documentation and/or other materials provided with the 14 // distribution. 15 // * Neither the name of Google Inc. nor the names of its 16 // contributors may be used to endorse or promote products derived from 17 // this software without specific prior written permission. 18 // 19 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 20 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 21 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 22 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 23 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 24 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 25 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 26 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 27 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 28 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 31 import com.google.protobuf.AbstractMessage; 32 import com.google.protobuf.ByteString; 33 import com.google.protobuf.CodedInputStream; 34 import com.google.protobuf.ExtensionRegistry; 35 import com.google.protobuf.InvalidProtocolBufferException; 36 import com.google.protobuf.Parser; 37 import com.google.protobuf.TextFormat; 38 import com.google.protobuf.conformance.Conformance; 39 import com.google.protobuf.util.JsonFormat; 40 import com.google.protobuf.util.JsonFormat.TypeRegistry; 41 import com.google.protobuf_test_messages.proto2.TestMessagesProto2; 42 import com.google.protobuf_test_messages.proto2.TestMessagesProto2.TestAllTypesProto2; 43 import com.google.protobuf_test_messages.proto3.TestMessagesProto3; 44 import com.google.protobuf_test_messages.proto3.TestMessagesProto3.TestAllTypesProto3; 45 import java.nio.ByteBuffer; 46 import java.util.ArrayList; 47 48 class ConformanceJava { 49 private int testCount = 0; 50 private TypeRegistry typeRegistry; 51 readFromStdin(byte[] buf, int len)52 private boolean readFromStdin(byte[] buf, int len) throws Exception { 53 int ofs = 0; 54 while (len > 0) { 55 int read = System.in.read(buf, ofs, len); 56 if (read == -1) { 57 return false; // EOF 58 } 59 ofs += read; 60 len -= read; 61 } 62 63 return true; 64 } 65 writeToStdout(byte[] buf)66 private void writeToStdout(byte[] buf) throws Exception { 67 System.out.write(buf); 68 } 69 70 // Returns -1 on EOF (the actual values will always be positive). readLittleEndianIntFromStdin()71 private int readLittleEndianIntFromStdin() throws Exception { 72 byte[] buf = new byte[4]; 73 if (!readFromStdin(buf, 4)) { 74 return -1; 75 } 76 return (buf[0] & 0xff) 77 | ((buf[1] & 0xff) << 8) 78 | ((buf[2] & 0xff) << 16) 79 | ((buf[3] & 0xff) << 24); 80 } 81 writeLittleEndianIntToStdout(int val)82 private void writeLittleEndianIntToStdout(int val) throws Exception { 83 byte[] buf = new byte[4]; 84 buf[0] = (byte)val; 85 buf[1] = (byte)(val >> 8); 86 buf[2] = (byte)(val >> 16); 87 buf[3] = (byte)(val >> 24); 88 writeToStdout(buf); 89 } 90 91 private enum BinaryDecoderType { 92 BTYE_STRING_DECODER, 93 BYTE_ARRAY_DECODER, 94 ARRAY_BYTE_BUFFER_DECODER, 95 READONLY_ARRAY_BYTE_BUFFER_DECODER, 96 DIRECT_BYTE_BUFFER_DECODER, 97 READONLY_DIRECT_BYTE_BUFFER_DECODER, 98 INPUT_STREAM_DECODER; 99 } 100 101 private static class BinaryDecoder <MessageType extends AbstractMessage> { decode(ByteString bytes, BinaryDecoderType type, Parser <MessageType> parser, ExtensionRegistry extensions)102 public MessageType decode (ByteString bytes, BinaryDecoderType type, 103 Parser <MessageType> parser, ExtensionRegistry extensions) 104 throws InvalidProtocolBufferException { 105 switch (type) { 106 case BTYE_STRING_DECODER: 107 return parser.parseFrom(bytes, extensions); 108 case BYTE_ARRAY_DECODER: 109 return parser.parseFrom(bytes.toByteArray(), extensions); 110 case ARRAY_BYTE_BUFFER_DECODER: { 111 ByteBuffer buffer = ByteBuffer.allocate(bytes.size()); 112 bytes.copyTo(buffer); 113 buffer.flip(); 114 try { 115 return parser.parseFrom(CodedInputStream.newInstance(buffer), extensions); 116 } catch (InvalidProtocolBufferException e) { 117 throw e; 118 } 119 } 120 case READONLY_ARRAY_BYTE_BUFFER_DECODER: { 121 try { 122 return parser.parseFrom( 123 CodedInputStream.newInstance(bytes.asReadOnlyByteBuffer()), extensions); 124 } catch (InvalidProtocolBufferException e) { 125 throw e; 126 } 127 } 128 case DIRECT_BYTE_BUFFER_DECODER: { 129 ByteBuffer buffer = ByteBuffer.allocateDirect(bytes.size()); 130 bytes.copyTo(buffer); 131 buffer.flip(); 132 try { 133 return parser.parseFrom(CodedInputStream.newInstance(buffer), extensions); 134 } catch (InvalidProtocolBufferException e) { 135 throw e; 136 } 137 } 138 case READONLY_DIRECT_BYTE_BUFFER_DECODER: { 139 ByteBuffer buffer = ByteBuffer.allocateDirect(bytes.size()); 140 bytes.copyTo(buffer); 141 buffer.flip(); 142 try { 143 return parser.parseFrom( 144 CodedInputStream.newInstance(buffer.asReadOnlyBuffer()), extensions); 145 } catch (InvalidProtocolBufferException e) { 146 throw e; 147 } 148 } 149 case INPUT_STREAM_DECODER: { 150 try { 151 return parser.parseFrom(bytes.newInput(), extensions); 152 } catch (InvalidProtocolBufferException e) { 153 throw e; 154 } 155 } 156 default : 157 return null; 158 } 159 } 160 } 161 parseBinary( ByteString bytes, Parser <MessageType> parser, ExtensionRegistry extensions)162 private <MessageType extends AbstractMessage> MessageType parseBinary( 163 ByteString bytes, Parser <MessageType> parser, ExtensionRegistry extensions) 164 throws InvalidProtocolBufferException { 165 ArrayList <MessageType> messages = new ArrayList <MessageType> (); 166 ArrayList <InvalidProtocolBufferException> exceptions = 167 new ArrayList <InvalidProtocolBufferException>(); 168 169 for (int i = 0; i < BinaryDecoderType.values().length; i++) { 170 messages.add(null); 171 exceptions.add(null); 172 } 173 BinaryDecoder <MessageType> decoder = new BinaryDecoder <MessageType> (); 174 175 boolean hasMessage = false; 176 boolean hasException = false; 177 for (int i = 0; i < BinaryDecoderType.values().length; ++i) { 178 try { 179 //= BinaryDecoderType.values()[i].parseProto3(bytes); 180 messages.set(i, decoder.decode(bytes, BinaryDecoderType.values()[i], parser, extensions)); 181 hasMessage = true; 182 } catch (InvalidProtocolBufferException e) { 183 exceptions.set(i, e); 184 hasException = true; 185 } 186 } 187 188 if (hasMessage && hasException) { 189 StringBuilder sb = 190 new StringBuilder("Binary decoders disagreed on whether the payload was valid.\n"); 191 for (int i = 0; i < BinaryDecoderType.values().length; ++i) { 192 sb.append(BinaryDecoderType.values()[i].name()); 193 if (messages.get(i) != null) { 194 sb.append(" accepted the payload.\n"); 195 } else { 196 sb.append(" rejected the payload.\n"); 197 } 198 } 199 throw new RuntimeException(sb.toString()); 200 } 201 202 if (hasException) { 203 // We do not check if exceptions are equal. Different implementations may return different 204 // exception messages. Throw an arbitrary one out instead. 205 throw exceptions.get(0); 206 } 207 208 // Fast path comparing all the messages with the first message, assuming equality being 209 // symmetric and transitive. 210 boolean allEqual = true; 211 for (int i = 1; i < messages.size(); ++i) { 212 if (!messages.get(0).equals(messages.get(i))) { 213 allEqual = false; 214 break; 215 } 216 } 217 218 // Slow path: compare and find out all unequal pairs. 219 if (!allEqual) { 220 StringBuilder sb = new StringBuilder(); 221 for (int i = 0; i < messages.size() - 1; ++i) { 222 for (int j = i + 1; j < messages.size(); ++j) { 223 if (!messages.get(i).equals(messages.get(j))) { 224 sb.append(BinaryDecoderType.values()[i].name()) 225 .append(" and ") 226 .append(BinaryDecoderType.values()[j].name()) 227 .append(" parsed the payload differently.\n"); 228 } 229 } 230 } 231 throw new RuntimeException(sb.toString()); 232 } 233 234 return messages.get(0); 235 } 236 doTest(Conformance.ConformanceRequest request)237 private Conformance.ConformanceResponse doTest(Conformance.ConformanceRequest request) { 238 com.google.protobuf.AbstractMessage testMessage; 239 boolean isProto3 = 240 request.getMessageType().equals("protobuf_test_messages.proto3.TestAllTypesProto3"); 241 boolean isProto2 = 242 request.getMessageType().equals("protobuf_test_messages.proto2.TestAllTypesProto2"); 243 244 switch (request.getPayloadCase()) { 245 case PROTOBUF_PAYLOAD: { 246 if (isProto3) { 247 try { 248 ExtensionRegistry extensions = ExtensionRegistry.newInstance(); 249 TestMessagesProto3.registerAllExtensions(extensions); 250 testMessage = parseBinary(request.getProtobufPayload(), TestAllTypesProto3.parser(), extensions); 251 } catch (InvalidProtocolBufferException e) { 252 return Conformance.ConformanceResponse.newBuilder().setParseError(e.getMessage()).build(); 253 } 254 } else if (isProto2) { 255 try { 256 ExtensionRegistry extensions = ExtensionRegistry.newInstance(); 257 TestMessagesProto2.registerAllExtensions(extensions); 258 testMessage = parseBinary(request.getProtobufPayload(), TestAllTypesProto2.parser(), extensions); 259 } catch (InvalidProtocolBufferException e) { 260 return Conformance.ConformanceResponse.newBuilder().setParseError(e.getMessage()).build(); 261 } 262 } else { 263 throw new RuntimeException("Protobuf request doesn't have specific payload type."); 264 } 265 break; 266 } 267 case JSON_PAYLOAD: { 268 try { 269 JsonFormat.Parser parser = JsonFormat.parser().usingTypeRegistry(typeRegistry); 270 if (request.getTestCategory() 271 == Conformance.TestCategory.JSON_IGNORE_UNKNOWN_PARSING_TEST) { 272 parser = parser.ignoringUnknownFields(); 273 } 274 if (isProto3) { 275 TestMessagesProto3.TestAllTypesProto3.Builder builder = 276 TestMessagesProto3.TestAllTypesProto3.newBuilder(); 277 parser.merge(request.getJsonPayload(), builder); 278 testMessage = builder.build(); 279 } else if (isProto2) { 280 TestMessagesProto2.TestAllTypesProto2.Builder builder = 281 TestMessagesProto2.TestAllTypesProto2.newBuilder(); 282 parser.merge(request.getJsonPayload(), builder); 283 testMessage = builder.build(); 284 } else { 285 throw new RuntimeException("Protobuf request doesn't have specific payload type."); 286 } 287 } catch (InvalidProtocolBufferException e) { 288 return Conformance.ConformanceResponse.newBuilder().setParseError(e.getMessage()).build(); 289 } 290 break; 291 } 292 case TEXT_PAYLOAD: { 293 if (isProto3) { 294 try { 295 TestMessagesProto3.TestAllTypesProto3.Builder builder = 296 TestMessagesProto3.TestAllTypesProto3.newBuilder(); 297 TextFormat.merge(request.getTextPayload(), builder); 298 testMessage = builder.build(); 299 } catch (TextFormat.ParseException e) { 300 return Conformance.ConformanceResponse.newBuilder() 301 .setParseError(e.getMessage()) 302 .build(); 303 } 304 } else if (isProto2) { 305 try { 306 TestMessagesProto2.TestAllTypesProto2.Builder builder = 307 TestMessagesProto2.TestAllTypesProto2.newBuilder(); 308 TextFormat.merge(request.getTextPayload(), builder); 309 testMessage = builder.build(); 310 } catch (TextFormat.ParseException e) { 311 return Conformance.ConformanceResponse.newBuilder() 312 .setParseError(e.getMessage()) 313 .build(); 314 } 315 } else { 316 throw new RuntimeException("Protobuf request doesn't have specific payload type."); 317 } 318 break; 319 } 320 case PAYLOAD_NOT_SET: { 321 throw new RuntimeException("Request didn't have payload."); 322 } 323 324 default: { 325 throw new RuntimeException("Unexpected payload case."); 326 } 327 } 328 329 switch (request.getRequestedOutputFormat()) { 330 case UNSPECIFIED: 331 throw new RuntimeException("Unspecified output format."); 332 333 case PROTOBUF: { 334 ByteString MessageString = testMessage.toByteString(); 335 return Conformance.ConformanceResponse.newBuilder().setProtobufPayload(MessageString).build(); 336 } 337 338 case JSON: 339 try { 340 return Conformance.ConformanceResponse.newBuilder().setJsonPayload( 341 JsonFormat.printer().usingTypeRegistry(typeRegistry).print(testMessage)).build(); 342 } catch (InvalidProtocolBufferException | IllegalArgumentException e) { 343 return Conformance.ConformanceResponse.newBuilder().setSerializeError( 344 e.getMessage()).build(); 345 } 346 347 case TEXT_FORMAT: 348 return Conformance.ConformanceResponse.newBuilder().setTextPayload( 349 TextFormat.printToString(testMessage)).build(); 350 351 default: { 352 throw new RuntimeException("Unexpected request output."); 353 } 354 } 355 } 356 doTestIo()357 private boolean doTestIo() throws Exception { 358 int bytes = readLittleEndianIntFromStdin(); 359 360 if (bytes == -1) { 361 return false; // EOF 362 } 363 364 byte[] serializedInput = new byte[bytes]; 365 366 if (!readFromStdin(serializedInput, bytes)) { 367 throw new RuntimeException("Unexpected EOF from test program."); 368 } 369 370 Conformance.ConformanceRequest request = 371 Conformance.ConformanceRequest.parseFrom(serializedInput); 372 Conformance.ConformanceResponse response = doTest(request); 373 byte[] serializedOutput = response.toByteArray(); 374 375 writeLittleEndianIntToStdout(serializedOutput.length); 376 writeToStdout(serializedOutput); 377 378 return true; 379 } 380 run()381 public void run() throws Exception { 382 typeRegistry = TypeRegistry.newBuilder().add( 383 TestMessagesProto3.TestAllTypesProto3.getDescriptor()).build(); 384 while (doTestIo()) { 385 this.testCount++; 386 } 387 388 System.err.println("ConformanceJava: received EOF from test runner after " + 389 this.testCount + " tests"); 390 } 391 main(String[] args)392 public static void main(String[] args) throws Exception { 393 new ConformanceJava().run(); 394 } 395 } 396