1 // 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 31 package com.google.protobuf; 32 33 import static org.junit.Assert.assertEquals; 34 import static org.junit.Assert.assertFalse; 35 import static org.junit.Assert.assertTrue; 36 import static org.junit.Assert.fail; 37 38 import com.google.protobuf.DescriptorProtos.DescriptorProto; 39 40 import org.junit.Test; 41 import org.junit.runner.RunWith; 42 import org.junit.runners.JUnit4; 43 44 import java.io.ByteArrayInputStream; 45 import java.io.ByteArrayOutputStream; 46 import java.io.FilterInputStream; 47 import java.io.IOException; 48 import java.io.InputStream; 49 50 /** 51 * Tests the exceptions thrown when parsing from a stream. The methods on the {@link Parser} 52 * interface are specified to only throw {@link InvalidProtocolBufferException}. But we really want 53 * to distinguish between invalid protos vs. actual I/O errors (like failures reading from a 54 * socket, etc.). So, when we're not using the parser directly, an {@link IOException} should be 55 * thrown where appropriate, instead of always an {@link InvalidProtocolBufferException}. 56 * 57 * @author jh (at) squareup.com (Joshua Humphries) 58 */ 59 @RunWith(JUnit4.class) 60 public class ParseExceptionsTest { 61 62 private interface ParseTester { 63 DescriptorProto parse(InputStream in) throws IOException; 64 } 65 66 private byte serializedProto[]; 67 68 private void setup() { 69 serializedProto = DescriptorProto.getDescriptor().toProto().toByteArray(); 70 } 71 72 private void setupDelimited() { 73 ByteArrayOutputStream bos = new ByteArrayOutputStream(); 74 try { 75 DescriptorProto.getDescriptor().toProto().writeDelimitedTo(bos); 76 } catch (IOException e) { 77 fail("Exception not expected: " + e); 78 } 79 serializedProto = bos.toByteArray(); 80 } 81 82 @Test public void message_parseFrom_InputStream() { 83 setup(); 84 verifyExceptions( 85 new ParseTester() { 86 @Override 87 public DescriptorProto parse(InputStream in) throws IOException { 88 return DescriptorProto.parseFrom(in); 89 } 90 }); 91 } 92 93 @Test public void message_parseFrom_InputStreamAndExtensionRegistry() { 94 setup(); 95 verifyExceptions( 96 new ParseTester() { 97 @Override 98 public DescriptorProto parse(InputStream in) throws IOException { 99 return DescriptorProto.parseFrom(in, ExtensionRegistry.newInstance()); 100 } 101 }); 102 } 103 104 @Test public void message_parseFrom_CodedInputStream() { 105 setup(); 106 verifyExceptions( 107 new ParseTester() { 108 @Override 109 public DescriptorProto parse(InputStream in) throws IOException { 110 return DescriptorProto.parseFrom(CodedInputStream.newInstance(in)); 111 } 112 }); 113 } 114 115 @Test public void message_parseFrom_CodedInputStreamAndExtensionRegistry() { 116 setup(); 117 verifyExceptions( 118 new ParseTester() { 119 @Override 120 public DescriptorProto parse(InputStream in) throws IOException { 121 return DescriptorProto.parseFrom( 122 CodedInputStream.newInstance(in), ExtensionRegistry.newInstance()); 123 } 124 }); 125 } 126 127 @Test public void message_parseDelimitedFrom_InputStream() { 128 setupDelimited(); 129 verifyExceptions( 130 new ParseTester() { 131 @Override 132 public DescriptorProto parse(InputStream in) throws IOException { 133 return DescriptorProto.parseDelimitedFrom(in); 134 } 135 }); 136 } 137 138 @Test public void message_parseDelimitedFrom_InputStreamAndExtensionRegistry() { 139 setupDelimited(); 140 verifyExceptions( 141 new ParseTester() { 142 @Override 143 public DescriptorProto parse(InputStream in) throws IOException { 144 return DescriptorProto.parseDelimitedFrom(in, ExtensionRegistry.newInstance()); 145 } 146 }); 147 } 148 149 @Test public void messageBuilder_mergeFrom_InputStream() { 150 setup(); 151 verifyExceptions( 152 new ParseTester() { 153 @Override 154 public DescriptorProto parse(InputStream in) throws IOException { 155 return DescriptorProto.newBuilder().mergeFrom(in).build(); 156 } 157 }); 158 } 159 160 @Test public void messageBuilder_mergeFrom_InputStreamAndExtensionRegistry() { 161 setup(); 162 verifyExceptions( 163 new ParseTester() { 164 @Override 165 public DescriptorProto parse(InputStream in) throws IOException { 166 return DescriptorProto.newBuilder() 167 .mergeFrom(in, ExtensionRegistry.newInstance()) 168 .build(); 169 } 170 }); 171 } 172 173 @Test public void messageBuilder_mergeFrom_CodedInputStream() { 174 setup(); 175 verifyExceptions( 176 new ParseTester() { 177 @Override 178 public DescriptorProto parse(InputStream in) throws IOException { 179 return DescriptorProto.newBuilder().mergeFrom(CodedInputStream.newInstance(in)).build(); 180 } 181 }); 182 } 183 184 @Test public void messageBuilder_mergeFrom_CodedInputStreamAndExtensionRegistry() { 185 setup(); 186 verifyExceptions( 187 new ParseTester() { 188 @Override 189 public DescriptorProto parse(InputStream in) throws IOException { 190 return DescriptorProto.newBuilder() 191 .mergeFrom(CodedInputStream.newInstance(in), ExtensionRegistry.newInstance()) 192 .build(); 193 } 194 }); 195 } 196 197 @Test public void messageBuilder_mergeDelimitedFrom_InputStream() { 198 setupDelimited(); 199 verifyExceptions( 200 new ParseTester() { 201 @Override 202 public DescriptorProto parse(InputStream in) throws IOException { 203 DescriptorProto.Builder builder = DescriptorProto.newBuilder(); 204 builder.mergeDelimitedFrom(in); 205 return builder.build(); 206 } 207 }); 208 } 209 210 @Test public void messageBuilder_mergeDelimitedFrom_InputStreamAndExtensionRegistry() { 211 setupDelimited(); 212 verifyExceptions( 213 new ParseTester() { 214 @Override 215 public DescriptorProto parse(InputStream in) throws IOException { 216 DescriptorProto.Builder builder = DescriptorProto.newBuilder(); 217 builder.mergeDelimitedFrom(in, ExtensionRegistry.newInstance()); 218 return builder.build(); 219 } 220 }); 221 } 222 223 private void verifyExceptions(ParseTester parseTester) { 224 // No exception 225 try { 226 assertEquals(DescriptorProto.getDescriptor().toProto(), 227 parseTester.parse(new ByteArrayInputStream(serializedProto))); 228 } catch (IOException e) { 229 fail("No exception expected: " + e); 230 } 231 232 // IOException 233 try { 234 // using a "broken" stream that will throw part-way through reading the message 235 parseTester.parse(broken(new ByteArrayInputStream(serializedProto))); 236 fail("IOException expected but not thrown"); 237 } catch (IOException e) { 238 assertFalse(e instanceof InvalidProtocolBufferException); 239 } 240 241 // InvalidProtocolBufferException 242 try { 243 // make the serialized proto invalid 244 for (int i = 0; i < 50; i++) { 245 serializedProto[i] = -1; 246 } 247 parseTester.parse(new ByteArrayInputStream(serializedProto)); 248 fail("InvalidProtocolBufferException expected but not thrown"); 249 } catch (IOException e) { 250 assertTrue(e instanceof InvalidProtocolBufferException); 251 } 252 } 253 254 private InputStream broken(InputStream i) { 255 return new FilterInputStream(i) { 256 int count = 0; 257 258 @Override public int read() throws IOException { 259 if (count++ >= 50) { 260 throw new IOException("I'm broken!"); 261 } 262 return super.read(); 263 } 264 265 @Override public int read(byte b[], int off, int len) throws IOException { 266 if ((count += len) >= 50) { 267 throw new IOException("I'm broken!"); 268 } 269 return super.read(b, off, len); 270 } 271 }; 272 } 273 } 274