• 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