blob: 9b6c36e82a8b79874983e53e7807be06085b1747 [file] [log] [blame]
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
//
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file or at
// https://developers.google.com/open-source/licenses/bsd
import com.google.protobuf.AbstractMessageLite;
import com.google.protobuf.ByteString;
import com.google.protobuf.CodedInputStream;
import com.google.protobuf.ExtensionRegistryLite;
import com.google.protobuf.InvalidProtocolBufferException;
import com.google.protobuf.MessageLite;
import com.google.protobuf.Parser;
import com.google.protobuf.conformance.Conformance;
import com.google.protobuf_test_messages.edition2023.TestAllTypesEdition2023;
import com.google.protobuf_test_messages.edition2023.TestMessagesEdition2023;
import com.google.protobuf_test_messages.editions.proto2.TestMessagesProto2Editions;
import com.google.protobuf_test_messages.editions.proto3.TestMessagesProto3Editions;
import com.google.protobuf_test_messages.proto2.TestMessagesProto2;
import com.google.protobuf_test_messages.proto2.TestMessagesProto2.TestAllTypesProto2;
import com.google.protobuf_test_messages.proto3.TestMessagesProto3;
import com.google.protobuf_test_messages.proto3.TestMessagesProto3.TestAllTypesProto3;
import java.nio.ByteBuffer;
import java.util.ArrayList;
class ConformanceJavaLite {
private int testCount = 0;
private boolean readFromStdin(byte[] buf, int len) throws Exception {
int ofs = 0;
while (len > 0) {
int read = System.in.read(buf, ofs, len);
if (read == -1) {
return false; // EOF
}
ofs += read;
len -= read;
}
return true;
}
private void writeToStdout(byte[] buf) throws Exception {
System.out.write(buf);
}
// Returns -1 on EOF (the actual values will always be positive).
private int readLittleEndianIntFromStdin() throws Exception {
byte[] buf = new byte[4];
if (!readFromStdin(buf, 4)) {
return -1;
}
return (buf[0] & 0xff)
| ((buf[1] & 0xff) << 8)
| ((buf[2] & 0xff) << 16)
| ((buf[3] & 0xff) << 24);
}
private void writeLittleEndianIntToStdout(int val) throws Exception {
byte[] buf = new byte[4];
buf[0] = (byte) val;
buf[1] = (byte) (val >> 8);
buf[2] = (byte) (val >> 16);
buf[3] = (byte) (val >> 24);
writeToStdout(buf);
}
private enum BinaryDecoderType {
BYTE_STRING_DECODER,
BYTE_ARRAY_DECODER,
ARRAY_BYTE_BUFFER_DECODER,
READONLY_ARRAY_BYTE_BUFFER_DECODER,
DIRECT_BYTE_BUFFER_DECODER,
READONLY_DIRECT_BYTE_BUFFER_DECODER,
INPUT_STREAM_DECODER;
}
private static class BinaryDecoder<T extends MessageLite> {
public T decode(
ByteString bytes,
BinaryDecoderType type,
Parser<T> parser,
ExtensionRegistryLite extensions)
throws InvalidProtocolBufferException {
switch (type) {
case BYTE_STRING_DECODER:
case BYTE_ARRAY_DECODER:
return parser.parseFrom(bytes, extensions);
case ARRAY_BYTE_BUFFER_DECODER:
{
ByteBuffer buffer = ByteBuffer.allocate(bytes.size());
bytes.copyTo(buffer);
buffer.flip();
return parser.parseFrom(CodedInputStream.newInstance(buffer), extensions);
}
case READONLY_ARRAY_BYTE_BUFFER_DECODER:
{
return parser.parseFrom(
CodedInputStream.newInstance(bytes.asReadOnlyByteBuffer()), extensions);
}
case DIRECT_BYTE_BUFFER_DECODER:
{
ByteBuffer buffer = ByteBuffer.allocateDirect(bytes.size());
bytes.copyTo(buffer);
buffer.flip();
return parser.parseFrom(CodedInputStream.newInstance(buffer), extensions);
}
case READONLY_DIRECT_BYTE_BUFFER_DECODER:
{
ByteBuffer buffer = ByteBuffer.allocateDirect(bytes.size());
bytes.copyTo(buffer);
buffer.flip();
return parser.parseFrom(
CodedInputStream.newInstance(buffer.asReadOnlyBuffer()), extensions);
}
case INPUT_STREAM_DECODER:
{
return parser.parseFrom(bytes.newInput(), extensions);
}
}
return null;
}
}
private <T extends MessageLite> T parseBinary(
ByteString bytes, Parser<T> parser, ExtensionRegistryLite extensions)
throws InvalidProtocolBufferException {
ArrayList<T> messages = new ArrayList<>();
ArrayList<InvalidProtocolBufferException> exceptions = new ArrayList<>();
for (int i = 0; i < BinaryDecoderType.values().length; i++) {
messages.add(null);
exceptions.add(null);
}
if (messages.isEmpty()) {
throw new RuntimeException("binary decoder types missing");
}
BinaryDecoder<T> decoder = new BinaryDecoder<>();
boolean hasMessage = false;
boolean hasException = false;
for (int i = 0; i < BinaryDecoderType.values().length; ++i) {
try {
messages.set(i, decoder.decode(bytes, BinaryDecoderType.values()[i], parser, extensions));
hasMessage = true;
} catch (InvalidProtocolBufferException e) {
exceptions.set(i, e);
hasException = true;
}
}
if (hasMessage && hasException) {
StringBuilder sb =
new StringBuilder("Binary decoders disagreed on whether the payload was valid.\n");
for (int i = 0; i < BinaryDecoderType.values().length; ++i) {
sb.append(BinaryDecoderType.values()[i].name());
if (messages.get(i) != null) {
sb.append(" accepted the payload.\n");
} else {
sb.append(" rejected the payload.\n");
}
}
throw new RuntimeException(sb.toString());
}
if (hasException) {
// We do not check if exceptions are equal. Different implementations may return different
// exception messages. Throw an arbitrary one out instead.
InvalidProtocolBufferException exception = null;
for (InvalidProtocolBufferException e : exceptions) {
if (exception != null) {
exception.addSuppressed(e);
} else {
exception = e;
}
}
throw exception;
}
// Fast path comparing all the messages with the first message, assuming equality being
// symmetric and transitive.
boolean allEqual = true;
for (int i = 1; i < messages.size(); ++i) {
if (!messages.get(0).equals(messages.get(i))) {
allEqual = false;
break;
}
}
// Slow path: compare and find out all unequal pairs.
if (!allEqual) {
StringBuilder sb = new StringBuilder();
for (int i = 0; i < messages.size() - 1; ++i) {
for (int j = i + 1; j < messages.size(); ++j) {
if (!messages.get(i).equals(messages.get(j))) {
sb.append(BinaryDecoderType.values()[i].name())
.append(" and ")
.append(BinaryDecoderType.values()[j].name())
.append(" parsed the payload differently.\n");
}
}
}
throw new RuntimeException(sb.toString());
}
return messages.get(0);
}
private Class<? extends AbstractMessageLite> createTestMessage(String messageType) {
switch (messageType) {
case "protobuf_test_messages.proto3.TestAllTypesProto3":
return TestAllTypesProto3.class;
case "protobuf_test_messages.proto2.TestAllTypesProto2":
return TestAllTypesProto2.class;
case "protobuf_test_messages.editions.TestAllTypesEdition2023":
return TestAllTypesEdition2023.class;
case "protobuf_test_messages.editions.proto3.TestAllTypesProto3":
return TestMessagesProto3Editions.TestAllTypesProto3.class;
case "protobuf_test_messages.editions.proto2.TestAllTypesProto2":
return TestMessagesProto2Editions.TestAllTypesProto2.class;
default:
throw new IllegalArgumentException(
"Protobuf request has unexpected payload type: " + messageType);
}
}
private Class<?> createTestFile(String messageType) {
switch (messageType) {
case "protobuf_test_messages.proto3.TestAllTypesProto3":
return TestMessagesProto3.class;
case "protobuf_test_messages.proto2.TestAllTypesProto2":
return TestMessagesProto2.class;
case "protobuf_test_messages.editions.TestAllTypesEdition2023":
return TestMessagesEdition2023.class;
case "protobuf_test_messages.editions.proto3.TestAllTypesProto3":
return TestMessagesProto3Editions.class;
case "protobuf_test_messages.editions.proto2.TestAllTypesProto2":
return TestMessagesProto2Editions.class;
default:
throw new IllegalArgumentException(
"Protobuf request has unexpected payload type: " + messageType);
}
}
@SuppressWarnings("unchecked")
private Conformance.ConformanceResponse doTest(Conformance.ConformanceRequest request) {
com.google.protobuf.MessageLite testMessage;
String messageType = request.getMessageType();
switch (request.getPayloadCase()) {
case PROTOBUF_PAYLOAD:
{
try {
ExtensionRegistryLite extensions = ExtensionRegistryLite.newInstance();
createTestFile(messageType)
.getMethod("registerAllExtensions", ExtensionRegistryLite.class)
.invoke(null, extensions);
testMessage =
parseBinary(
request.getProtobufPayload(),
(Parser<AbstractMessageLite>)
createTestMessage(messageType).getMethod("parser").invoke(null),
extensions);
} catch (InvalidProtocolBufferException e) {
return Conformance.ConformanceResponse.newBuilder()
.setParseError(e.getMessage())
.build();
} catch (Exception e) {
throw new RuntimeException(e);
}
break;
}
case JSON_PAYLOAD:
{
return Conformance.ConformanceResponse.newBuilder()
.setSkipped("Lite runtime does not support JSON format.")
.build();
}
case TEXT_PAYLOAD:
{
return Conformance.ConformanceResponse.newBuilder()
.setSkipped("Lite runtime does not support Text format.")
.build();
}
case PAYLOAD_NOT_SET:
{
throw new RuntimeException("Request didn't have payload.");
}
default:
{
throw new RuntimeException("Unexpected payload case.");
}
}
switch (request.getRequestedOutputFormat()) {
case UNSPECIFIED:
throw new RuntimeException("Unspecified output format.");
case PROTOBUF:
return Conformance.ConformanceResponse.newBuilder()
.setProtobufPayload(testMessage.toByteString())
.build();
case JSON:
return Conformance.ConformanceResponse.newBuilder()
.setSkipped("Lite runtime does not support JSON format.")
.build();
case TEXT_FORMAT:
return Conformance.ConformanceResponse.newBuilder()
.setSkipped("Lite runtime does not support Text format.")
.build();
default:
{
throw new RuntimeException("Unexpected request output.");
}
}
}
private boolean doTestIo() throws Exception {
int bytes = readLittleEndianIntFromStdin();
if (bytes == -1) {
return false; // EOF
}
byte[] serializedInput = new byte[bytes];
if (!readFromStdin(serializedInput, bytes)) {
throw new RuntimeException("Unexpected EOF from test program.");
}
Conformance.ConformanceRequest request =
Conformance.ConformanceRequest.parseFrom(serializedInput);
Conformance.ConformanceResponse response = doTest(request);
byte[] serializedOutput = response.toByteArray();
writeLittleEndianIntToStdout(serializedOutput.length);
writeToStdout(serializedOutput);
return true;
}
public void run() throws Exception {
while (doTestIo()) {
this.testCount++;
}
System.err.println(
"ConformanceJavaLite: received EOF from test runner after " + this.testCount + " tests");
}
public static void main(String[] args) throws Exception {
new ConformanceJavaLite().run();
}
}