ConformanceJavaLite.java 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351
  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. import com.google.protobuf.ByteString;
  31. import com.google.protobuf.CodedInputStream;
  32. import com.google.protobuf.ExtensionRegistryLite;
  33. import com.google.protobuf.InvalidProtocolBufferException;
  34. import com.google.protobuf.MessageLite;
  35. import com.google.protobuf.Parser;
  36. import com.google.protobuf.conformance.Conformance;
  37. import com.google.protobuf_test_messages.proto2.TestMessagesProto2;
  38. import com.google.protobuf_test_messages.proto2.TestMessagesProto2.TestAllTypesProto2;
  39. import com.google.protobuf_test_messages.proto3.TestMessagesProto3;
  40. import com.google.protobuf_test_messages.proto3.TestMessagesProto3.TestAllTypesProto3;
  41. import java.nio.ByteBuffer;
  42. import java.util.ArrayList;
  43. class ConformanceJavaLite {
  44. private int testCount = 0;
  45. private boolean readFromStdin(byte[] buf, int len) throws Exception {
  46. int ofs = 0;
  47. while (len > 0) {
  48. int read = System.in.read(buf, ofs, len);
  49. if (read == -1) {
  50. return false; // EOF
  51. }
  52. ofs += read;
  53. len -= read;
  54. }
  55. return true;
  56. }
  57. private void writeToStdout(byte[] buf) throws Exception {
  58. System.out.write(buf);
  59. }
  60. // Returns -1 on EOF (the actual values will always be positive).
  61. private int readLittleEndianIntFromStdin() throws Exception {
  62. byte[] buf = new byte[4];
  63. if (!readFromStdin(buf, 4)) {
  64. return -1;
  65. }
  66. return (buf[0] & 0xff)
  67. | ((buf[1] & 0xff) << 8)
  68. | ((buf[2] & 0xff) << 16)
  69. | ((buf[3] & 0xff) << 24);
  70. }
  71. private void writeLittleEndianIntToStdout(int val) throws Exception {
  72. byte[] buf = new byte[4];
  73. buf[0] = (byte) val;
  74. buf[1] = (byte) (val >> 8);
  75. buf[2] = (byte) (val >> 16);
  76. buf[3] = (byte) (val >> 24);
  77. writeToStdout(buf);
  78. }
  79. private enum BinaryDecoderType {
  80. BTYE_STRING_DECODER,
  81. BYTE_ARRAY_DECODER,
  82. ARRAY_BYTE_BUFFER_DECODER,
  83. READONLY_ARRAY_BYTE_BUFFER_DECODER,
  84. DIRECT_BYTE_BUFFER_DECODER,
  85. READONLY_DIRECT_BYTE_BUFFER_DECODER,
  86. INPUT_STREAM_DECODER;
  87. }
  88. private static class BinaryDecoder<T extends MessageLite> {
  89. public T decode(
  90. ByteString bytes,
  91. BinaryDecoderType type,
  92. Parser<T> parser,
  93. ExtensionRegistryLite extensions)
  94. throws InvalidProtocolBufferException {
  95. switch (type) {
  96. case BTYE_STRING_DECODER:
  97. case BYTE_ARRAY_DECODER:
  98. return parser.parseFrom(bytes, extensions);
  99. case ARRAY_BYTE_BUFFER_DECODER:
  100. {
  101. ByteBuffer buffer = ByteBuffer.allocate(bytes.size());
  102. bytes.copyTo(buffer);
  103. buffer.flip();
  104. return parser.parseFrom(CodedInputStream.newInstance(buffer), extensions);
  105. }
  106. case READONLY_ARRAY_BYTE_BUFFER_DECODER:
  107. {
  108. return parser.parseFrom(
  109. CodedInputStream.newInstance(bytes.asReadOnlyByteBuffer()), extensions);
  110. }
  111. case DIRECT_BYTE_BUFFER_DECODER:
  112. {
  113. ByteBuffer buffer = ByteBuffer.allocateDirect(bytes.size());
  114. bytes.copyTo(buffer);
  115. buffer.flip();
  116. return parser.parseFrom(CodedInputStream.newInstance(buffer), extensions);
  117. }
  118. case READONLY_DIRECT_BYTE_BUFFER_DECODER:
  119. {
  120. ByteBuffer buffer = ByteBuffer.allocateDirect(bytes.size());
  121. bytes.copyTo(buffer);
  122. buffer.flip();
  123. return parser.parseFrom(
  124. CodedInputStream.newInstance(buffer.asReadOnlyBuffer()), extensions);
  125. }
  126. case INPUT_STREAM_DECODER:
  127. {
  128. return parser.parseFrom(bytes.newInput(), extensions);
  129. }
  130. }
  131. return null;
  132. }
  133. }
  134. private <T extends MessageLite> T parseBinary(
  135. ByteString bytes, Parser<T> parser, ExtensionRegistryLite extensions)
  136. throws InvalidProtocolBufferException {
  137. ArrayList<T> messages = new ArrayList<>();
  138. ArrayList<InvalidProtocolBufferException> exceptions = new ArrayList<>();
  139. for (int i = 0; i < BinaryDecoderType.values().length; i++) {
  140. messages.add(null);
  141. exceptions.add(null);
  142. }
  143. if (messages.isEmpty()) {
  144. throw new RuntimeException("binary decoder types missing");
  145. }
  146. BinaryDecoder<T> decoder = new BinaryDecoder<>();
  147. boolean hasMessage = false;
  148. boolean hasException = false;
  149. for (int i = 0; i < BinaryDecoderType.values().length; ++i) {
  150. try {
  151. messages.set(i, decoder.decode(bytes, BinaryDecoderType.values()[i], parser, extensions));
  152. hasMessage = true;
  153. } catch (InvalidProtocolBufferException e) {
  154. exceptions.set(i, e);
  155. hasException = true;
  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. if (hasException) {
  172. // We do not check if exceptions are equal. Different implementations may return different
  173. // exception messages. Throw an arbitrary one out instead.
  174. InvalidProtocolBufferException exception = null;
  175. for (InvalidProtocolBufferException e : exceptions) {
  176. if (exception != null) {
  177. exception.addSuppressed(e);
  178. } else {
  179. exception = e;
  180. }
  181. }
  182. throw exception;
  183. }
  184. // Fast path comparing all the messages with the first message, assuming equality being
  185. // symmetric and transitive.
  186. boolean allEqual = true;
  187. for (int i = 1; i < messages.size(); ++i) {
  188. if (!messages.get(0).equals(messages.get(i))) {
  189. allEqual = false;
  190. break;
  191. }
  192. }
  193. // Slow path: compare and find out all unequal pairs.
  194. if (!allEqual) {
  195. StringBuilder sb = new StringBuilder();
  196. for (int i = 0; i < messages.size() - 1; ++i) {
  197. for (int j = i + 1; j < messages.size(); ++j) {
  198. if (!messages.get(i).equals(messages.get(j))) {
  199. sb.append(BinaryDecoderType.values()[i].name())
  200. .append(" and ")
  201. .append(BinaryDecoderType.values()[j].name())
  202. .append(" parsed the payload differently.\n");
  203. }
  204. }
  205. }
  206. throw new RuntimeException(sb.toString());
  207. }
  208. return messages.get(0);
  209. }
  210. private Conformance.ConformanceResponse doTest(Conformance.ConformanceRequest request) {
  211. com.google.protobuf.MessageLite testMessage;
  212. boolean isProto3 =
  213. request.getMessageType().equals("protobuf_test_messages.proto3.TestAllTypesProto3");
  214. boolean isProto2 =
  215. request.getMessageType().equals("protobuf_test_messages.proto2.TestAllTypesProto2");
  216. switch (request.getPayloadCase()) {
  217. case PROTOBUF_PAYLOAD:
  218. {
  219. if (isProto3) {
  220. try {
  221. ExtensionRegistryLite extensions = ExtensionRegistryLite.newInstance();
  222. TestMessagesProto3.registerAllExtensions(extensions);
  223. testMessage =
  224. parseBinary(
  225. request.getProtobufPayload(), TestAllTypesProto3.parser(), extensions);
  226. } catch (InvalidProtocolBufferException e) {
  227. return Conformance.ConformanceResponse.newBuilder()
  228. .setParseError(e.getMessage())
  229. .build();
  230. }
  231. } else if (isProto2) {
  232. try {
  233. ExtensionRegistryLite extensions = ExtensionRegistryLite.newInstance();
  234. TestMessagesProto2.registerAllExtensions(extensions);
  235. testMessage =
  236. parseBinary(
  237. request.getProtobufPayload(), TestAllTypesProto2.parser(), extensions);
  238. } catch (InvalidProtocolBufferException e) {
  239. return Conformance.ConformanceResponse.newBuilder()
  240. .setParseError(e.getMessage())
  241. .build();
  242. }
  243. } else {
  244. throw new RuntimeException("Protobuf request doesn't have specific payload type.");
  245. }
  246. break;
  247. }
  248. case JSON_PAYLOAD:
  249. {
  250. return Conformance.ConformanceResponse.newBuilder()
  251. .setSkipped("Lite runtime does not support JSON format.")
  252. .build();
  253. }
  254. case TEXT_PAYLOAD:
  255. {
  256. return Conformance.ConformanceResponse.newBuilder()
  257. .setSkipped("Lite runtime does not support Text format.")
  258. .build();
  259. }
  260. case PAYLOAD_NOT_SET:
  261. {
  262. throw new RuntimeException("Request didn't have payload.");
  263. }
  264. default:
  265. {
  266. throw new RuntimeException("Unexpected payload case.");
  267. }
  268. }
  269. switch (request.getRequestedOutputFormat()) {
  270. case UNSPECIFIED:
  271. throw new RuntimeException("Unspecified output format.");
  272. case PROTOBUF:
  273. return Conformance.ConformanceResponse.newBuilder()
  274. .setProtobufPayload(testMessage.toByteString())
  275. .build();
  276. case JSON:
  277. return Conformance.ConformanceResponse.newBuilder()
  278. .setSkipped("Lite runtime does not support JSON format.")
  279. .build();
  280. case TEXT_FORMAT:
  281. return Conformance.ConformanceResponse.newBuilder()
  282. .setSkipped("Lite runtime does not support Text format.")
  283. .build();
  284. default:
  285. {
  286. throw new RuntimeException("Unexpected request output.");
  287. }
  288. }
  289. }
  290. private boolean doTestIo() throws Exception {
  291. int bytes = readLittleEndianIntFromStdin();
  292. if (bytes == -1) {
  293. return false; // EOF
  294. }
  295. byte[] serializedInput = new byte[bytes];
  296. if (!readFromStdin(serializedInput, bytes)) {
  297. throw new RuntimeException("Unexpected EOF from test program.");
  298. }
  299. Conformance.ConformanceRequest request =
  300. Conformance.ConformanceRequest.parseFrom(serializedInput);
  301. Conformance.ConformanceResponse response = doTest(request);
  302. byte[] serializedOutput = response.toByteArray();
  303. writeLittleEndianIntToStdout(serializedOutput.length);
  304. writeToStdout(serializedOutput);
  305. return true;
  306. }
  307. public void run() throws Exception {
  308. while (doTestIo()) {
  309. this.testCount++;
  310. }
  311. System.err.println(
  312. "ConformanceJavaLite: received EOF from test runner after " + this.testCount + " tests");
  313. }
  314. public static void main(String[] args) throws Exception {
  315. new ConformanceJavaLite().run();
  316. }
  317. }