blob: 531998262d72116307e27174ea68229bb6686ee5 [file] [log] [blame]
Rafi Kamal58d44202019-11-11 17:06:56 -08001// 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
Yilun Chongfcb92682017-06-29 14:40:47 -070031import com.google.protobuf.AbstractMessage;
Yilun Chongcb95a7f2019-01-11 11:40:52 -080032import com.google.protobuf.ByteString;
Adam Cozzette5a76e632016-11-17 16:48:38 -080033import com.google.protobuf.CodedInputStream;
Yilun Chongfcb92682017-06-29 14:40:47 -070034import com.google.protobuf.ExtensionRegistry;
Yilun Chongcb95a7f2019-01-11 11:40:52 -080035import com.google.protobuf.InvalidProtocolBufferException;
36import com.google.protobuf.Parser;
37import com.google.protobuf.TextFormat;
38import com.google.protobuf.conformance.Conformance;
Adam Cozzette5a76e632016-11-17 16:48:38 -080039import com.google.protobuf.util.JsonFormat;
Joshua Habermanf1ce60e2016-12-03 11:51:25 -050040import com.google.protobuf.util.JsonFormat.TypeRegistry;
Yilun Chongcb95a7f2019-01-11 11:40:52 -080041import com.google.protobuf_test_messages.proto2.TestMessagesProto2;
42import com.google.protobuf_test_messages.proto2.TestMessagesProto2.TestAllTypesProto2;
43import com.google.protobuf_test_messages.proto3.TestMessagesProto3;
44import com.google.protobuf_test_messages.proto3.TestMessagesProto3.TestAllTypesProto3;
Adam Cozzette5a76e632016-11-17 16:48:38 -080045import java.nio.ByteBuffer;
Yilun Chongfcb92682017-06-29 14:40:47 -070046import java.util.ArrayList;
Josh Haberman420f9382015-04-16 12:50:39 -070047
48class ConformanceJava {
49 private int testCount = 0;
Feng Xiaoe841bac2015-12-11 17:09:20 -080050 private TypeRegistry typeRegistry;
Josh Haberman420f9382015-04-16 12:50:39 -070051
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
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).
71 private int readLittleEndianIntFromStdin() throws Exception {
72 byte[] buf = new byte[4];
73 if (!readFromStdin(buf, 4)) {
74 return -1;
75 }
Feng Xiaoe841bac2015-12-11 17:09:20 -080076 return (buf[0] & 0xff)
77 | ((buf[1] & 0xff) << 8)
78 | ((buf[2] & 0xff) << 16)
79 | ((buf[3] & 0xff) << 24);
Josh Haberman420f9382015-04-16 12:50:39 -070080 }
81
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 }
Xiang Daie4794102019-02-21 11:28:50 +080090
Yilun Chongfcb92682017-06-29 14:40:47 -070091 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> {
Xiang Daie4794102019-02-21 11:28:50 +0800102 public MessageType decode (ByteString bytes, BinaryDecoderType type,
Yilun Chongfcb92682017-06-29 14:40:47 -0700103 Parser <MessageType> parser, ExtensionRegistry extensions)
Yilun Chong696cf772017-06-28 11:32:01 -0700104 throws InvalidProtocolBufferException {
Yilun Chongfcb92682017-06-29 14:40:47 -0700105 switch (type) {
Xiang Daie4794102019-02-21 11:28:50 +0800106 case BTYE_STRING_DECODER:
Yilun Chongfcb92682017-06-29 14:40:47 -0700107 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 }
Xiang Daie4794102019-02-21 11:28:50 +0800127 }
Yilun Chongfcb92682017-06-29 14:40:47 -0700128 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
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>();
Xiang Daie4794102019-02-21 11:28:50 +0800168
Yilun Chongfcb92682017-06-29 14:40:47 -0700169 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> ();
Yilun Chong696cf772017-06-28 11:32:01 -0700174
175 boolean hasMessage = false;
176 boolean hasException = false;
Yilun Chongfcb92682017-06-29 14:40:47 -0700177 for (int i = 0; i < BinaryDecoderType.values().length; ++i) {
Yilun Chong696cf772017-06-28 11:32:01 -0700178 try {
Yilun Chongfcb92682017-06-29 14:40:47 -0700179 //= BinaryDecoderType.values()[i].parseProto3(bytes);
180 messages.set(i, decoder.decode(bytes, BinaryDecoderType.values()[i], parser, extensions));
Adam Cozzette5a76e632016-11-17 16:48:38 -0800181 hasMessage = true;
182 } catch (InvalidProtocolBufferException e) {
Yilun Chongfcb92682017-06-29 14:40:47 -0700183 exceptions.set(i, e);
Adam Cozzette5a76e632016-11-17 16:48:38 -0800184 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");
Yilun Chongfcb92682017-06-29 14:40:47 -0700191 for (int i = 0; i < BinaryDecoderType.values().length; ++i) {
192 sb.append(BinaryDecoderType.values()[i].name());
193 if (messages.get(i) != null) {
Adam Cozzette5a76e632016-11-17 16:48:38 -0800194 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.
Yilun Chongfcb92682017-06-29 14:40:47 -0700205 throw exceptions.get(0);
Adam Cozzette5a76e632016-11-17 16:48:38 -0800206 }
207
208 // Fast path comparing all the messages with the first message, assuming equality being
209 // symmetric and transitive.
210 boolean allEqual = true;
Yilun Chongfcb92682017-06-29 14:40:47 -0700211 for (int i = 1; i < messages.size(); ++i) {
212 if (!messages.get(0).equals(messages.get(i))) {
Adam Cozzette5a76e632016-11-17 16:48:38 -0800213 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();
Yilun Chongfcb92682017-06-29 14:40:47 -0700221 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())
Adam Cozzette5a76e632016-11-17 16:48:38 -0800225 .append(" and ")
Yilun Chongfcb92682017-06-29 14:40:47 -0700226 .append(BinaryDecoderType.values()[j].name())
Adam Cozzette5a76e632016-11-17 16:48:38 -0800227 .append(" parsed the payload differently.\n");
228 }
229 }
230 }
231 throw new RuntimeException(sb.toString());
232 }
233
Yilun Chongfcb92682017-06-29 14:40:47 -0700234 return messages.get(0);
Adam Cozzette5a76e632016-11-17 16:48:38 -0800235 }
236
Josh Haberman420f9382015-04-16 12:50:39 -0700237 private Conformance.ConformanceResponse doTest(Conformance.ConformanceRequest request) {
Yilun Chong696cf772017-06-28 11:32:01 -0700238 com.google.protobuf.AbstractMessage testMessage;
Joshua Haberman6ed73832020-05-13 13:46:15 -0700239 boolean isProto3 =
240 request.getMessageType().equals("protobuf_test_messages.proto3.TestAllTypesProto3");
241 boolean isProto2 =
242 request.getMessageType().equals("protobuf_test_messages.proto2.TestAllTypesProto2");
Josh Haberman420f9382015-04-16 12:50:39 -0700243
244 switch (request.getPayloadCase()) {
245 case PROTOBUF_PAYLOAD: {
Yilun Chong696cf772017-06-28 11:32:01 -0700246 if (isProto3) {
247 try {
Yilun Chongfcb92682017-06-29 14:40:47 -0700248 ExtensionRegistry extensions = ExtensionRegistry.newInstance();
249 TestMessagesProto3.registerAllExtensions(extensions);
Yilun Chong3adb0542017-06-30 17:22:32 -0700250 testMessage = parseBinary(request.getProtobufPayload(), TestAllTypesProto3.parser(), extensions);
Yilun Chong696cf772017-06-28 11:32:01 -0700251 } catch (InvalidProtocolBufferException e) {
252 return Conformance.ConformanceResponse.newBuilder().setParseError(e.getMessage()).build();
253 }
Yilun Chongfcb92682017-06-29 14:40:47 -0700254 } else if (isProto2) {
Yilun Chong696cf772017-06-28 11:32:01 -0700255 try {
Yilun Chongfcb92682017-06-29 14:40:47 -0700256 ExtensionRegistry extensions = ExtensionRegistry.newInstance();
257 TestMessagesProto2.registerAllExtensions(extensions);
258 testMessage = parseBinary(request.getProtobufPayload(), TestAllTypesProto2.parser(), extensions);
Yilun Chong696cf772017-06-28 11:32:01 -0700259 } 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.");
Josh Haberman420f9382015-04-16 12:50:39 -0700264 }
265 break;
266 }
267 case JSON_PAYLOAD: {
Feng Xiaoe841bac2015-12-11 17:09:20 -0800268 try {
Feng Xiao6bbe1972018-08-08 17:00:41 -0700269 JsonFormat.Parser parser = JsonFormat.parser().usingTypeRegistry(typeRegistry);
270 if (request.getTestCategory()
271 == Conformance.TestCategory.JSON_IGNORE_UNKNOWN_PARSING_TEST) {
272 parser = parser.ignoringUnknownFields();
273 }
Joshua Haberman6ed73832020-05-13 13:46:15 -0700274 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 }
Feng Xiaoe841bac2015-12-11 17:09:20 -0800287 } catch (InvalidProtocolBufferException e) {
288 return Conformance.ConformanceResponse.newBuilder().setParseError(e.getMessage()).build();
289 }
290 break;
Josh Haberman420f9382015-04-16 12:50:39 -0700291 }
Yilun Chongcb95a7f2019-01-11 11:40:52 -0800292 case TEXT_PAYLOAD: {
Yilun Chongd8c25012019-02-22 18:13:33 +0800293 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.");
Yilun Chongcb95a7f2019-01-11 11:40:52 -0800317 }
318 break;
319 }
Josh Haberman420f9382015-04-16 12:50:39 -0700320 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
Josh Habermanb0500b32015-07-10 16:36:59 -0700329 switch (request.getRequestedOutputFormat()) {
Josh Haberman420f9382015-04-16 12:50:39 -0700330 case UNSPECIFIED:
331 throw new RuntimeException("Unspecified output format.");
332
Yilun Chong696cf772017-06-28 11:32:01 -0700333 case PROTOBUF: {
Xiang Daie4794102019-02-21 11:28:50 +0800334 ByteString MessageString = testMessage.toByteString();
Yilun Chong696cf772017-06-28 11:32:01 -0700335 return Conformance.ConformanceResponse.newBuilder().setProtobufPayload(MessageString).build();
336 }
Josh Haberman420f9382015-04-16 12:50:39 -0700337
338 case JSON:
Feng Xiaoe841bac2015-12-11 17:09:20 -0800339 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 }
Josh Haberman420f9382015-04-16 12:50:39 -0700346
Yilun Chongcb95a7f2019-01-11 11:40:52 -0800347 case TEXT_FORMAT:
348 return Conformance.ConformanceResponse.newBuilder().setTextPayload(
349 TextFormat.printToString(testMessage)).build();
350
Josh Haberman420f9382015-04-16 12:50:39 -0700351 default: {
352 throw new RuntimeException("Unexpected request output.");
353 }
354 }
355 }
356
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
381 public void run() throws Exception {
Feng Xiaoe841bac2015-12-11 17:09:20 -0800382 typeRegistry = TypeRegistry.newBuilder().add(
Yilun Chong3adb0542017-06-30 17:22:32 -0700383 TestMessagesProto3.TestAllTypesProto3.getDescriptor()).build();
Josh Haberman420f9382015-04-16 12:50:39 -0700384 while (doTestIo()) {
Jisi Liu3b3c8ab2016-03-30 11:39:59 -0700385 this.testCount++;
Josh Haberman420f9382015-04-16 12:50:39 -0700386 }
387
388 System.err.println("ConformanceJava: received EOF from test runner after " +
389 this.testCount + " tests");
390 }
391
392 public static void main(String[] args) throws Exception {
393 new ConformanceJava().run();
394 }
395}