Home | History | Annotate | Download | only in protobuf
      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