• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 import com.google.protobuf.AbstractMessage;
2 import com.google.protobuf.ByteString;
3 import com.google.protobuf.CodedInputStream;
4 import com.google.protobuf.ExtensionRegistry;
5 import com.google.protobuf.InvalidProtocolBufferException;
6 import com.google.protobuf.Parser;
7 import com.google.protobuf.TextFormat;
8 import com.google.protobuf.conformance.Conformance;
9 import com.google.protobuf.util.JsonFormat;
10 import com.google.protobuf.util.JsonFormat.TypeRegistry;
11 import com.google.protobuf_test_messages.proto2.TestMessagesProto2;
12 import com.google.protobuf_test_messages.proto2.TestMessagesProto2.TestAllTypesProto2;
13 import com.google.protobuf_test_messages.proto3.TestMessagesProto3;
14 import com.google.protobuf_test_messages.proto3.TestMessagesProto3.TestAllTypesProto3;
15 import java.nio.ByteBuffer;
16 import java.util.ArrayList;
17 
18 class ConformanceJava {
19   private int testCount = 0;
20   private TypeRegistry typeRegistry;
21 
readFromStdin(byte[] buf, int len)22   private boolean readFromStdin(byte[] buf, int len) throws Exception {
23     int ofs = 0;
24     while (len > 0) {
25       int read = System.in.read(buf, ofs, len);
26       if (read == -1) {
27         return false;  // EOF
28       }
29       ofs += read;
30       len -= read;
31     }
32 
33     return true;
34   }
35 
writeToStdout(byte[] buf)36   private void writeToStdout(byte[] buf) throws Exception {
37     System.out.write(buf);
38   }
39 
40   // Returns -1 on EOF (the actual values will always be positive).
readLittleEndianIntFromStdin()41   private int readLittleEndianIntFromStdin() throws Exception {
42     byte[] buf = new byte[4];
43     if (!readFromStdin(buf, 4)) {
44       return -1;
45     }
46     return (buf[0] & 0xff)
47         | ((buf[1] & 0xff) << 8)
48         | ((buf[2] & 0xff) << 16)
49         | ((buf[3] & 0xff) << 24);
50   }
51 
writeLittleEndianIntToStdout(int val)52   private void writeLittleEndianIntToStdout(int val) throws Exception {
53     byte[] buf = new byte[4];
54     buf[0] = (byte)val;
55     buf[1] = (byte)(val >> 8);
56     buf[2] = (byte)(val >> 16);
57     buf[3] = (byte)(val >> 24);
58     writeToStdout(buf);
59   }
60 
61   private enum BinaryDecoderType {
62     BTYE_STRING_DECODER,
63     BYTE_ARRAY_DECODER,
64     ARRAY_BYTE_BUFFER_DECODER,
65     READONLY_ARRAY_BYTE_BUFFER_DECODER,
66     DIRECT_BYTE_BUFFER_DECODER,
67     READONLY_DIRECT_BYTE_BUFFER_DECODER,
68     INPUT_STREAM_DECODER;
69   }
70 
71   private static class BinaryDecoder <MessageType extends AbstractMessage> {
decode(ByteString bytes, BinaryDecoderType type, Parser <MessageType> parser, ExtensionRegistry extensions)72     public MessageType decode (ByteString bytes, BinaryDecoderType type,
73         Parser <MessageType> parser, ExtensionRegistry extensions)
74       throws InvalidProtocolBufferException {
75       switch (type) {
76         case BTYE_STRING_DECODER:
77           return parser.parseFrom(bytes, extensions);
78         case BYTE_ARRAY_DECODER:
79           return parser.parseFrom(bytes.toByteArray(), extensions);
80         case ARRAY_BYTE_BUFFER_DECODER: {
81           ByteBuffer buffer = ByteBuffer.allocate(bytes.size());
82           bytes.copyTo(buffer);
83           buffer.flip();
84           try {
85             return parser.parseFrom(CodedInputStream.newInstance(buffer), extensions);
86           } catch (InvalidProtocolBufferException e) {
87             throw e;
88           }
89         }
90         case READONLY_ARRAY_BYTE_BUFFER_DECODER: {
91           try {
92             return parser.parseFrom(
93                 CodedInputStream.newInstance(bytes.asReadOnlyByteBuffer()), extensions);
94           } catch (InvalidProtocolBufferException e) {
95             throw e;
96           }
97         }
98         case DIRECT_BYTE_BUFFER_DECODER: {
99           ByteBuffer buffer = ByteBuffer.allocateDirect(bytes.size());
100           bytes.copyTo(buffer);
101           buffer.flip();
102           try {
103             return parser.parseFrom(CodedInputStream.newInstance(buffer), extensions);
104           } catch (InvalidProtocolBufferException e) {
105             throw e;
106           }
107         }
108         case READONLY_DIRECT_BYTE_BUFFER_DECODER: {
109           ByteBuffer buffer = ByteBuffer.allocateDirect(bytes.size());
110           bytes.copyTo(buffer);
111           buffer.flip();
112           try {
113             return parser.parseFrom(
114                 CodedInputStream.newInstance(buffer.asReadOnlyBuffer()), extensions);
115           } catch (InvalidProtocolBufferException e) {
116             throw e;
117           }
118         }
119         case INPUT_STREAM_DECODER: {
120           try {
121             return parser.parseFrom(bytes.newInput(), extensions);
122           } catch (InvalidProtocolBufferException e) {
123             throw e;
124           }
125         }
126         default :
127           return null;
128       }
129     }
130   }
131 
parseBinary( ByteString bytes, Parser <MessageType> parser, ExtensionRegistry extensions)132   private <MessageType extends AbstractMessage> MessageType parseBinary(
133       ByteString bytes, Parser <MessageType> parser, ExtensionRegistry extensions)
134       throws InvalidProtocolBufferException {
135     ArrayList <MessageType> messages = new ArrayList <MessageType> ();
136     ArrayList <InvalidProtocolBufferException> exceptions =
137         new ArrayList <InvalidProtocolBufferException>();
138 
139     for (int i = 0; i < BinaryDecoderType.values().length; i++) {
140       messages.add(null);
141       exceptions.add(null);
142     }
143     BinaryDecoder <MessageType> decoder = new BinaryDecoder <MessageType> ();
144 
145     boolean hasMessage = false;
146     boolean hasException = false;
147     for (int i = 0; i < BinaryDecoderType.values().length; ++i) {
148       try {
149         //= BinaryDecoderType.values()[i].parseProto3(bytes);
150         messages.set(i, decoder.decode(bytes, BinaryDecoderType.values()[i], parser, extensions));
151         hasMessage = true;
152       } catch (InvalidProtocolBufferException e) {
153         exceptions.set(i, e);
154         hasException = true;
155       }
156     }
157 
158     if (hasMessage && hasException) {
159       StringBuilder sb =
160           new StringBuilder("Binary decoders disagreed on whether the payload was valid.\n");
161       for (int i = 0; i < BinaryDecoderType.values().length; ++i) {
162         sb.append(BinaryDecoderType.values()[i].name());
163         if (messages.get(i) != null) {
164           sb.append(" accepted the payload.\n");
165         } else {
166           sb.append(" rejected the payload.\n");
167         }
168       }
169       throw new RuntimeException(sb.toString());
170     }
171 
172     if (hasException) {
173       // We do not check if exceptions are equal. Different implementations may return different
174       // exception messages. Throw an arbitrary one out instead.
175       throw exceptions.get(0);
176     }
177 
178     // Fast path comparing all the messages with the first message, assuming equality being
179     // symmetric and transitive.
180     boolean allEqual = true;
181     for (int i = 1; i < messages.size(); ++i) {
182       if (!messages.get(0).equals(messages.get(i))) {
183         allEqual = false;
184         break;
185       }
186     }
187 
188     // Slow path: compare and find out all unequal pairs.
189     if (!allEqual) {
190       StringBuilder sb = new StringBuilder();
191       for (int i = 0; i < messages.size() - 1; ++i) {
192         for (int j = i + 1; j < messages.size(); ++j) {
193           if (!messages.get(i).equals(messages.get(j))) {
194             sb.append(BinaryDecoderType.values()[i].name())
195                 .append(" and ")
196                 .append(BinaryDecoderType.values()[j].name())
197                 .append(" parsed the payload differently.\n");
198           }
199         }
200       }
201       throw new RuntimeException(sb.toString());
202     }
203 
204     return messages.get(0);
205   }
206 
doTest(Conformance.ConformanceRequest request)207   private Conformance.ConformanceResponse doTest(Conformance.ConformanceRequest request) {
208     com.google.protobuf.AbstractMessage testMessage;
209     boolean isProto3 = request.getMessageType().equals("protobuf_test_messages.proto3.TestAllTypesProto3");
210     boolean isProto2 = request.getMessageType().equals("protobuf_test_messages.proto2.TestAllTypesProto2");
211 
212     switch (request.getPayloadCase()) {
213       case PROTOBUF_PAYLOAD: {
214         if (isProto3) {
215           try {
216             ExtensionRegistry extensions = ExtensionRegistry.newInstance();
217             TestMessagesProto3.registerAllExtensions(extensions);
218             testMessage = parseBinary(request.getProtobufPayload(), TestAllTypesProto3.parser(), extensions);
219           } catch (InvalidProtocolBufferException e) {
220             return Conformance.ConformanceResponse.newBuilder().setParseError(e.getMessage()).build();
221           }
222         } else if (isProto2) {
223           try {
224             ExtensionRegistry extensions = ExtensionRegistry.newInstance();
225             TestMessagesProto2.registerAllExtensions(extensions);
226             testMessage = parseBinary(request.getProtobufPayload(), TestAllTypesProto2.parser(), extensions);
227           } catch (InvalidProtocolBufferException e) {
228             return Conformance.ConformanceResponse.newBuilder().setParseError(e.getMessage()).build();
229           }
230         } else {
231           throw new RuntimeException("Protobuf request doesn't have specific payload type.");
232         }
233         break;
234       }
235       case JSON_PAYLOAD: {
236         try {
237           TestMessagesProto3.TestAllTypesProto3.Builder builder =
238               TestMessagesProto3.TestAllTypesProto3.newBuilder();
239           JsonFormat.Parser parser = JsonFormat.parser().usingTypeRegistry(typeRegistry);
240           if (request.getTestCategory()
241               == Conformance.TestCategory.JSON_IGNORE_UNKNOWN_PARSING_TEST) {
242             parser = parser.ignoringUnknownFields();
243           }
244           parser.merge(request.getJsonPayload(), builder);
245           testMessage = builder.build();
246         } catch (InvalidProtocolBufferException e) {
247           return Conformance.ConformanceResponse.newBuilder().setParseError(e.getMessage()).build();
248         }
249         break;
250       }
251       case TEXT_PAYLOAD: {
252         if (isProto3) {
253           try {
254             TestMessagesProto3.TestAllTypesProto3.Builder builder =
255                 TestMessagesProto3.TestAllTypesProto3.newBuilder();
256             TextFormat.merge(request.getTextPayload(), builder);
257             testMessage = builder.build();
258           } catch (TextFormat.ParseException e) {
259               return Conformance.ConformanceResponse.newBuilder()
260                   .setParseError(e.getMessage())
261                   .build();
262           }
263         } else if (isProto2) {
264           try {
265             TestMessagesProto2.TestAllTypesProto2.Builder builder =
266                 TestMessagesProto2.TestAllTypesProto2.newBuilder();
267             TextFormat.merge(request.getTextPayload(), builder);
268             testMessage = builder.build();
269           } catch (TextFormat.ParseException e) {
270               return Conformance.ConformanceResponse.newBuilder()
271                   .setParseError(e.getMessage())
272                   .build();
273           }
274         } else {
275           throw new RuntimeException("Protobuf request doesn't have specific payload type.");
276         }
277         break;
278       }
279       case PAYLOAD_NOT_SET: {
280         throw new RuntimeException("Request didn't have payload.");
281       }
282 
283       default: {
284         throw new RuntimeException("Unexpected payload case.");
285       }
286     }
287 
288     switch (request.getRequestedOutputFormat()) {
289       case UNSPECIFIED:
290         throw new RuntimeException("Unspecified output format.");
291 
292       case PROTOBUF: {
293         ByteString MessageString = testMessage.toByteString();
294         return Conformance.ConformanceResponse.newBuilder().setProtobufPayload(MessageString).build();
295       }
296 
297       case JSON:
298         try {
299           return Conformance.ConformanceResponse.newBuilder().setJsonPayload(
300               JsonFormat.printer().usingTypeRegistry(typeRegistry).print(testMessage)).build();
301         } catch (InvalidProtocolBufferException | IllegalArgumentException e) {
302           return Conformance.ConformanceResponse.newBuilder().setSerializeError(
303               e.getMessage()).build();
304         }
305 
306       case TEXT_FORMAT:
307         return Conformance.ConformanceResponse.newBuilder().setTextPayload(
308             TextFormat.printToString(testMessage)).build();
309 
310       default: {
311         throw new RuntimeException("Unexpected request output.");
312       }
313     }
314   }
315 
doTestIo()316   private boolean doTestIo() throws Exception {
317     int bytes = readLittleEndianIntFromStdin();
318 
319     if (bytes == -1) {
320       return false;  // EOF
321     }
322 
323     byte[] serializedInput = new byte[bytes];
324 
325     if (!readFromStdin(serializedInput, bytes)) {
326       throw new RuntimeException("Unexpected EOF from test program.");
327     }
328 
329     Conformance.ConformanceRequest request =
330         Conformance.ConformanceRequest.parseFrom(serializedInput);
331     Conformance.ConformanceResponse response = doTest(request);
332     byte[] serializedOutput = response.toByteArray();
333 
334     writeLittleEndianIntToStdout(serializedOutput.length);
335     writeToStdout(serializedOutput);
336 
337     return true;
338   }
339 
run()340   public void run() throws Exception {
341     typeRegistry = TypeRegistry.newBuilder().add(
342         TestMessagesProto3.TestAllTypesProto3.getDescriptor()).build();
343     while (doTestIo()) {
344       this.testCount++;
345     }
346 
347     System.err.println("ConformanceJava: received EOF from test runner after " +
348         this.testCount + " tests");
349   }
350 
main(String[] args)351   public static void main(String[] args) throws Exception {
352     new ConformanceJava().run();
353   }
354 }
355