• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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