|
@@ -0,0 +1,211 @@
|
|
|
+package com.google.protobuf;
|
|
|
+
|
|
|
+import com.google.protobuf.DescriptorProtos.DescriptorProto;
|
|
|
+import java.io.ByteArrayInputStream;
|
|
|
+import java.io.ByteArrayOutputStream;
|
|
|
+import java.io.FilterInputStream;
|
|
|
+import java.io.IOException;
|
|
|
+import java.io.InputStream;
|
|
|
+import org.junit.Test;
|
|
|
+
|
|
|
+import static org.junit.Assert.assertEquals;
|
|
|
+import static org.junit.Assert.assertFalse;
|
|
|
+import static org.junit.Assert.assertTrue;
|
|
|
+import static org.junit.Assert.fail;
|
|
|
+
|
|
|
+/**
|
|
|
+ * Tests the exceptions thrown when parsing from a stream. The methods on the {@link Parser}
|
|
|
+ * interface are specified to only throw {@link InvalidProtocolBufferException}. But we really want
|
|
|
+ * to distinguish between invalid protos vs. actual I/O errors (like failures reading from a
|
|
|
+ * socket, etc.). So, when we're not using the parser directly, an {@link IOException} should be
|
|
|
+ * thrown where appropriate, instead of always an {@link InvalidProtocolBufferException}.
|
|
|
+ *
|
|
|
+ * @author jh@squareup.com (Joshua Humphries)
|
|
|
+ */
|
|
|
+public class ParseExceptionsTest {
|
|
|
+
|
|
|
+ private interface ParseTester {
|
|
|
+ DescriptorProto parse(InputStream in) throws IOException;
|
|
|
+ }
|
|
|
+
|
|
|
+ private byte serializedProto[];
|
|
|
+
|
|
|
+ private void setup() {
|
|
|
+ serializedProto = DescriptorProto.getDescriptor().toProto().toByteArray();
|
|
|
+ }
|
|
|
+
|
|
|
+ private void setupDelimited() {
|
|
|
+ ByteArrayOutputStream bos = new ByteArrayOutputStream();
|
|
|
+ try {
|
|
|
+ DescriptorProto.getDescriptor().toProto().writeDelimitedTo(bos);
|
|
|
+ } catch (IOException e) {
|
|
|
+ fail("Exception not expected: " + e);
|
|
|
+ }
|
|
|
+ serializedProto = bos.toByteArray();
|
|
|
+ }
|
|
|
+
|
|
|
+ @Test public void message_parseFrom_InputStream() {
|
|
|
+ setup();
|
|
|
+ verifyExceptions(new ParseTester() {
|
|
|
+ public DescriptorProto parse(InputStream in) throws IOException {
|
|
|
+ return DescriptorProto.parseFrom(in);
|
|
|
+ }
|
|
|
+ });
|
|
|
+ }
|
|
|
+
|
|
|
+ @Test public void message_parseFrom_InputStreamAndExtensionRegistry() {
|
|
|
+ setup();
|
|
|
+ verifyExceptions(new ParseTester() {
|
|
|
+ public DescriptorProto parse(InputStream in) throws IOException {
|
|
|
+ return DescriptorProto.parseFrom(in, ExtensionRegistry.newInstance());
|
|
|
+ }
|
|
|
+ });
|
|
|
+ }
|
|
|
+
|
|
|
+ @Test public void message_parseFrom_CodedInputStream() {
|
|
|
+ setup();
|
|
|
+ verifyExceptions(new ParseTester() {
|
|
|
+ public DescriptorProto parse(InputStream in) throws IOException {
|
|
|
+ return DescriptorProto.parseFrom(CodedInputStream.newInstance(in));
|
|
|
+ }
|
|
|
+ });
|
|
|
+ }
|
|
|
+
|
|
|
+ @Test public void message_parseFrom_CodedInputStreamAndExtensionRegistry() {
|
|
|
+ setup();
|
|
|
+ verifyExceptions(new ParseTester() {
|
|
|
+ public DescriptorProto parse(InputStream in) throws IOException {
|
|
|
+ return DescriptorProto.parseFrom(CodedInputStream.newInstance(in),
|
|
|
+ ExtensionRegistry.newInstance());
|
|
|
+ }
|
|
|
+ });
|
|
|
+ }
|
|
|
+
|
|
|
+ @Test public void message_parseDelimitedFrom_InputStream() {
|
|
|
+ setupDelimited();
|
|
|
+ verifyExceptions(new ParseTester() {
|
|
|
+ public DescriptorProto parse(InputStream in) throws IOException {
|
|
|
+ return DescriptorProto.parseDelimitedFrom(in);
|
|
|
+ }
|
|
|
+ });
|
|
|
+ }
|
|
|
+
|
|
|
+ @Test public void message_parseDelimitedFrom_InputStreamAndExtensionRegistry() {
|
|
|
+ setupDelimited();
|
|
|
+ verifyExceptions(new ParseTester() {
|
|
|
+ public DescriptorProto parse(InputStream in) throws IOException {
|
|
|
+ return DescriptorProto.parseDelimitedFrom(in, ExtensionRegistry.newInstance());
|
|
|
+ }
|
|
|
+ });
|
|
|
+ }
|
|
|
+
|
|
|
+ @Test public void messageBuilder_mergeFrom_InputStream() {
|
|
|
+ setup();
|
|
|
+ verifyExceptions(new ParseTester() {
|
|
|
+ public DescriptorProto parse(InputStream in) throws IOException {
|
|
|
+ return DescriptorProto.newBuilder().mergeFrom(in).build();
|
|
|
+ }
|
|
|
+ });
|
|
|
+ }
|
|
|
+
|
|
|
+ @Test public void messageBuilder_mergeFrom_InputStreamAndExtensionRegistry() {
|
|
|
+ setup();
|
|
|
+ verifyExceptions(new ParseTester() {
|
|
|
+ public DescriptorProto parse(InputStream in) throws IOException {
|
|
|
+ return DescriptorProto.newBuilder().mergeFrom(in, ExtensionRegistry.newInstance()).build();
|
|
|
+ }
|
|
|
+ });
|
|
|
+ }
|
|
|
+
|
|
|
+ @Test public void messageBuilder_mergeFrom_CodedInputStream() {
|
|
|
+ setup();
|
|
|
+ verifyExceptions(new ParseTester() {
|
|
|
+ public DescriptorProto parse(InputStream in) throws IOException {
|
|
|
+ return DescriptorProto.newBuilder().mergeFrom(CodedInputStream.newInstance(in)).build();
|
|
|
+ }
|
|
|
+ });
|
|
|
+ }
|
|
|
+
|
|
|
+ @Test public void messageBuilder_mergeFrom_CodedInputStreamAndExtensionRegistry() {
|
|
|
+ setup();
|
|
|
+ verifyExceptions(new ParseTester() {
|
|
|
+ public DescriptorProto parse(InputStream in) throws IOException {
|
|
|
+ return DescriptorProto.newBuilder()
|
|
|
+ .mergeFrom(CodedInputStream.newInstance(in), ExtensionRegistry.newInstance()).build();
|
|
|
+ }
|
|
|
+ });
|
|
|
+ }
|
|
|
+
|
|
|
+ @Test public void messageBuilder_mergeDelimitedFrom_InputStream() {
|
|
|
+ setupDelimited();
|
|
|
+ verifyExceptions(new ParseTester() {
|
|
|
+ public DescriptorProto parse(InputStream in) throws IOException {
|
|
|
+ DescriptorProto.Builder builder = DescriptorProto.newBuilder();
|
|
|
+ builder.mergeDelimitedFrom(in);
|
|
|
+ return builder.build();
|
|
|
+ }
|
|
|
+ });
|
|
|
+ }
|
|
|
+
|
|
|
+ @Test public void messageBuilder_mergeDelimitedFrom_InputStreamAndExtensionRegistry() {
|
|
|
+ setupDelimited();
|
|
|
+ verifyExceptions(new ParseTester() {
|
|
|
+ public DescriptorProto parse(InputStream in) throws IOException {
|
|
|
+ DescriptorProto.Builder builder = DescriptorProto.newBuilder();
|
|
|
+ builder.mergeDelimitedFrom(in, ExtensionRegistry.newInstance());
|
|
|
+ return builder.build();
|
|
|
+ }
|
|
|
+ });
|
|
|
+ }
|
|
|
+
|
|
|
+ private void verifyExceptions(ParseTester parseTester) {
|
|
|
+ // No exception
|
|
|
+ try {
|
|
|
+ assertEquals(DescriptorProto.getDescriptor().toProto(),
|
|
|
+ parseTester.parse(new ByteArrayInputStream(serializedProto)));
|
|
|
+ } catch (IOException e) {
|
|
|
+ fail("No exception expected: " + e);
|
|
|
+ }
|
|
|
+
|
|
|
+ // IOException
|
|
|
+ try {
|
|
|
+ // using a "broken" stream that will throw part-way through reading the message
|
|
|
+ parseTester.parse(broken(new ByteArrayInputStream(serializedProto)));
|
|
|
+ fail("IOException expected but not thrown");
|
|
|
+ } catch (IOException e) {
|
|
|
+ assertFalse(e instanceof InvalidProtocolBufferException);
|
|
|
+ }
|
|
|
+
|
|
|
+ // InvalidProtocolBufferException
|
|
|
+ try {
|
|
|
+ // make the serialized proto invalid
|
|
|
+ for (int i = 0; i < 50; i++) {
|
|
|
+ serializedProto[i] = -1;
|
|
|
+ }
|
|
|
+ parseTester.parse(new ByteArrayInputStream(serializedProto));
|
|
|
+ fail("InvalidProtocolBufferException expected but not thrown");
|
|
|
+ } catch (IOException e) {
|
|
|
+ assertTrue(e instanceof InvalidProtocolBufferException);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ private InputStream broken(InputStream i) {
|
|
|
+ return new FilterInputStream(i) {
|
|
|
+ int count = 0;
|
|
|
+
|
|
|
+ @Override public int read() throws IOException {
|
|
|
+ if (count++ >= 50) {
|
|
|
+ throw new IOException("I'm broken!");
|
|
|
+ }
|
|
|
+ return super.read();
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override public int read(byte b[], int off, int len) throws IOException {
|
|
|
+ if ((count += len) >= 50) {
|
|
|
+ throw new IOException("I'm broken!");
|
|
|
+ }
|
|
|
+ return super.read(b, off, len);
|
|
|
+ }
|
|
|
+ };
|
|
|
+ }
|
|
|
+}
|