content: // 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);
        }
      };
    }
  }
  