• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Protocol Buffers - Google's data interchange format
2 // Copyright 2008 Google Inc.  All rights reserved.
3 //
4 // Use of this source code is governed by a BSD-style
5 // license that can be found in the LICENSE file or at
6 // https://developers.google.com/open-source/licenses/bsd
7 
8 package com.google.protobuf;
9 
10 import static com.google.common.truth.Truth.assertThat;
11 import static org.junit.Assert.fail;
12 
13 import com.google.protobuf.DescriptorProtos.DescriptorProto;
14 import java.io.ByteArrayInputStream;
15 import java.io.ByteArrayOutputStream;
16 import java.io.FilterInputStream;
17 import java.io.IOException;
18 import java.io.InputStream;
19 import org.junit.Test;
20 import org.junit.runner.RunWith;
21 import org.junit.runners.JUnit4;
22 
23 /**
24  * Tests the exceptions thrown when parsing from a stream. The methods on the {@link Parser}
25  * interface are specified to only throw {@link InvalidProtocolBufferException}. But we really want
26  * to distinguish between invalid protos vs. actual I/O errors (like failures reading from a socket,
27  * etc.). So, when we're not using the parser directly, an {@link IOException} should be thrown
28  * where appropriate, instead of always an {@link InvalidProtocolBufferException}.
29  *
30  * @author jh@squareup.com (Joshua Humphries)
31  */
32 @RunWith(JUnit4.class)
33 public class ParseExceptionsTest {
34 
35   private interface ParseTester {
parse(InputStream in)36     DescriptorProto parse(InputStream in) throws IOException;
37   }
38 
39   private byte[] serializedProto;
40 
setup()41   private void setup() {
42     serializedProto = DescriptorProto.getDescriptor().toProto().toByteArray();
43   }
44 
setupDelimited()45   private void setupDelimited() {
46     ByteArrayOutputStream bos = new ByteArrayOutputStream();
47     try {
48       DescriptorProto.getDescriptor().toProto().writeDelimitedTo(bos);
49     } catch (IOException e) {
50       fail("Exception not expected: " + e);
51     }
52     serializedProto = bos.toByteArray();
53   }
54 
55   @Test
message_parseFrom_InputStream()56   public void message_parseFrom_InputStream() {
57     setup();
58     verifyExceptions(
59         new ParseTester() {
60           @Override
61           public DescriptorProto parse(InputStream in) throws IOException {
62             return DescriptorProto.parseFrom(in);
63           }
64         });
65   }
66 
67   @Test
message_parseFrom_InputStreamAndExtensionRegistry()68   public void message_parseFrom_InputStreamAndExtensionRegistry() {
69     setup();
70     verifyExceptions(
71         new ParseTester() {
72           @Override
73           public DescriptorProto parse(InputStream in) throws IOException {
74             return DescriptorProto.parseFrom(in, ExtensionRegistry.newInstance());
75           }
76         });
77   }
78 
79   @Test
message_parseFrom_CodedInputStream()80   public void message_parseFrom_CodedInputStream() {
81     setup();
82     verifyExceptions(
83         new ParseTester() {
84           @Override
85           public DescriptorProto parse(InputStream in) throws IOException {
86             return DescriptorProto.parseFrom(CodedInputStream.newInstance(in));
87           }
88         });
89   }
90 
91   @Test
message_parseFrom_CodedInputStreamAndExtensionRegistry()92   public void message_parseFrom_CodedInputStreamAndExtensionRegistry() {
93     setup();
94     verifyExceptions(
95         new ParseTester() {
96           @Override
97           public DescriptorProto parse(InputStream in) throws IOException {
98             return DescriptorProto.parseFrom(
99                 CodedInputStream.newInstance(in), ExtensionRegistry.newInstance());
100           }
101         });
102   }
103 
104   @Test
message_parseDelimitedFrom_InputStream()105   public void message_parseDelimitedFrom_InputStream() {
106     setupDelimited();
107     verifyExceptions(
108         new ParseTester() {
109           @Override
110           public DescriptorProto parse(InputStream in) throws IOException {
111             return DescriptorProto.parseDelimitedFrom(in);
112           }
113         });
114   }
115 
116   @Test
message_parseDelimitedFrom_InputStreamAndExtensionRegistry()117   public void message_parseDelimitedFrom_InputStreamAndExtensionRegistry() {
118     setupDelimited();
119     verifyExceptions(
120         new ParseTester() {
121           @Override
122           public DescriptorProto parse(InputStream in) throws IOException {
123             return DescriptorProto.parseDelimitedFrom(in, ExtensionRegistry.newInstance());
124           }
125         });
126   }
127 
128   @Test
messageBuilder_mergeFrom_InputStream()129   public void messageBuilder_mergeFrom_InputStream() {
130     setup();
131     verifyExceptions(
132         new ParseTester() {
133           @Override
134           public DescriptorProto parse(InputStream in) throws IOException {
135             return DescriptorProto.newBuilder().mergeFrom(in).build();
136           }
137         });
138   }
139 
140   @Test
messageBuilder_mergeFrom_InputStreamAndExtensionRegistry()141   public void messageBuilder_mergeFrom_InputStreamAndExtensionRegistry() {
142     setup();
143     verifyExceptions(
144         new ParseTester() {
145           @Override
146           public DescriptorProto parse(InputStream in) throws IOException {
147             return DescriptorProto.newBuilder()
148                 .mergeFrom(in, ExtensionRegistry.newInstance())
149                 .build();
150           }
151         });
152   }
153 
154   @Test
messageBuilder_mergeFrom_CodedInputStream()155   public void messageBuilder_mergeFrom_CodedInputStream() {
156     setup();
157     verifyExceptions(
158         new ParseTester() {
159           @Override
160           public DescriptorProto parse(InputStream in) throws IOException {
161             return DescriptorProto.newBuilder().mergeFrom(CodedInputStream.newInstance(in)).build();
162           }
163         });
164   }
165 
166   @Test
messageBuilder_mergeFrom_CodedInputStreamAndExtensionRegistry()167   public void messageBuilder_mergeFrom_CodedInputStreamAndExtensionRegistry() {
168     setup();
169     verifyExceptions(
170         new ParseTester() {
171           @Override
172           public DescriptorProto parse(InputStream in) throws IOException {
173             return DescriptorProto.newBuilder()
174                 .mergeFrom(CodedInputStream.newInstance(in), ExtensionRegistry.newInstance())
175                 .build();
176           }
177         });
178   }
179 
180   @Test
messageBuilder_mergeDelimitedFrom_InputStream()181   public void messageBuilder_mergeDelimitedFrom_InputStream() {
182     setupDelimited();
183     verifyExceptions(
184         new ParseTester() {
185           @Override
186           public DescriptorProto parse(InputStream in) throws IOException {
187             DescriptorProto.Builder builder = DescriptorProto.newBuilder();
188             builder.mergeDelimitedFrom(in);
189             return builder.build();
190           }
191         });
192   }
193 
194   @Test
messageBuilder_mergeDelimitedFrom_InputStream_malformed()195   public void messageBuilder_mergeDelimitedFrom_InputStream_malformed() throws Exception {
196     byte[] body = new byte[80];
197     CodedOutputStream cos = CodedOutputStream.newInstance(body);
198     cos.writeUInt32NoTag(90); // Greater than bytes in stream
199     cos.writeTag(DescriptorProto.ENUM_TYPE_FIELD_NUMBER, WireFormat.WIRETYPE_LENGTH_DELIMITED);
200     cos.writeUInt32NoTag(98); // Nested message with size larger than parent
201     cos.writeTag(1000, WireFormat.WIRETYPE_LENGTH_DELIMITED);
202     cos.writeUInt32NoTag(100); // Unknown field with size larger than parent
203     ByteArrayInputStream bais = new ByteArrayInputStream(body);
204     try {
205       DescriptorProto.parseDelimitedFrom(bais);
206       fail();
207     } catch (InvalidProtocolBufferException expected) {
208     }
209   }
210 
211   @Test
messageBuilder_mergeDelimitedFrom_InputStreamAndExtensionRegistry()212   public void messageBuilder_mergeDelimitedFrom_InputStreamAndExtensionRegistry() {
213     setupDelimited();
214     verifyExceptions(
215         new ParseTester() {
216           @Override
217           public DescriptorProto parse(InputStream in) throws IOException {
218             DescriptorProto.Builder builder = DescriptorProto.newBuilder();
219             builder.mergeDelimitedFrom(in, ExtensionRegistry.newInstance());
220             return builder.build();
221           }
222         });
223   }
224 
verifyExceptions(ParseTester parseTester)225   private void verifyExceptions(ParseTester parseTester) {
226     // No exception
227     try {
228       assertThat(parseTester.parse(new ByteArrayInputStream(serializedProto)))
229           .isEqualTo(DescriptorProto.getDescriptor().toProto());
230     } catch (IOException e) {
231       fail("No exception expected: " + e);
232     }
233 
234     // IOException
235     try {
236       // using a "broken" stream that will throw part-way through reading the message
237       parseTester.parse(broken(new ByteArrayInputStream(serializedProto)));
238       fail("IOException expected but not thrown");
239     } catch (IOException e) {
240       assertThat(e).isNotInstanceOf(InvalidProtocolBufferException.class);
241     }
242 
243     // InvalidProtocolBufferException
244     try {
245       // make the serialized proto invalid
246       for (int i = 0; i < 50; i++) {
247         serializedProto[i] = -1;
248       }
249       parseTester.parse(new ByteArrayInputStream(serializedProto));
250       fail("InvalidProtocolBufferException expected but not thrown");
251     } catch (IOException e) {
252       assertThat(e).isInstanceOf(InvalidProtocolBufferException.class);
253     }
254   }
255 
broken(InputStream i)256   private InputStream broken(InputStream i) {
257     return new FilterInputStream(i) {
258       int count = 0;
259 
260       @Override
261       public int read() throws IOException {
262         if (count++ >= 50) {
263           throw new IOException("I'm broken!");
264         }
265         return super.read();
266       }
267 
268       @Override
269       public int read(byte[] b, int off, int len) throws IOException {
270         if ((count += len) >= 50) {
271           throw new IOException("I'm broken!");
272         }
273         return super.read(b, off, len);
274       }
275     };
276   }
277 }
278