ConformanceJava.java 12 KB

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