blob: e376b1cd721aeb441296cdc8d8d7e7ba4545fb44 [file] [log] [blame]
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
// https://developers.google.com/protocol-buffers/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
package com.google.protobuf;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
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 org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
/**
* 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)
*/
@RunWith(JUnit4.class)
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() {
@Override
public DescriptorProto parse(InputStream in) throws IOException {
return DescriptorProto.parseFrom(in);
}
});
}
@Test public void message_parseFrom_InputStreamAndExtensionRegistry() {
setup();
verifyExceptions(
new ParseTester() {
@Override
public DescriptorProto parse(InputStream in) throws IOException {
return DescriptorProto.parseFrom(in, ExtensionRegistry.newInstance());
}
});
}
@Test public void message_parseFrom_CodedInputStream() {
setup();
verifyExceptions(
new ParseTester() {
@Override
public DescriptorProto parse(InputStream in) throws IOException {
return DescriptorProto.parseFrom(CodedInputStream.newInstance(in));
}
});
}
@Test public void message_parseFrom_CodedInputStreamAndExtensionRegistry() {
setup();
verifyExceptions(
new ParseTester() {
@Override
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() {
@Override
public DescriptorProto parse(InputStream in) throws IOException {
return DescriptorProto.parseDelimitedFrom(in);
}
});
}
@Test public void message_parseDelimitedFrom_InputStreamAndExtensionRegistry() {
setupDelimited();
verifyExceptions(
new ParseTester() {
@Override
public DescriptorProto parse(InputStream in) throws IOException {
return DescriptorProto.parseDelimitedFrom(in, ExtensionRegistry.newInstance());
}
});
}
@Test public void messageBuilder_mergeFrom_InputStream() {
setup();
verifyExceptions(
new ParseTester() {
@Override
public DescriptorProto parse(InputStream in) throws IOException {
return DescriptorProto.newBuilder().mergeFrom(in).build();
}
});
}
@Test public void messageBuilder_mergeFrom_InputStreamAndExtensionRegistry() {
setup();
verifyExceptions(
new ParseTester() {
@Override
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() {
@Override
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() {
@Override
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() {
@Override
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() {
@Override
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);
}
};
}
}