Merge pull request #896 from jhump/jh/fix-ioexception-vs-invalidprotobuf-exception
throw IOException instead of InvalidProtocolBufferException when appropriatepull/1149/head
commit
bbe6e430f6
6 changed files with 300 additions and 14 deletions
@ -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); |
||||
} |
||||
}; |
||||
} |
||||
} |
Loading…
Reference in new issue