PageRenderTime 82ms CodeModel.GetById 48ms app.highlight 29ms RepoModel.GetById 1ms app.codeStats 0ms

/thirdparty/breakpad/third_party/protobuf/protobuf/java/src/test/java/com/google/protobuf/CodedInputStreamTest.java

http://github.com/tomahawk-player/tomahawk
Java | 528 lines | 347 code | 66 blank | 115 comment | 17 complexity | b097f41aa7ada591a86833c3786334c7 MD5 | raw file
  1// Protocol Buffers - Google's data interchange format
  2// Copyright 2008 Google Inc.  All rights reserved.
  3// http://code.google.com/p/protobuf/
  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
 31package com.google.protobuf;
 32
 33import protobuf_unittest.UnittestProto.TestAllTypes;
 34import protobuf_unittest.UnittestProto.TestRecursiveMessage;
 35
 36import junit.framework.TestCase;
 37
 38import java.io.ByteArrayInputStream;
 39import java.io.FilterInputStream;
 40import java.io.InputStream;
 41import java.io.IOException;
 42
 43/**
 44 * Unit test for {@link CodedInputStream}.
 45 *
 46 * @author kenton@google.com Kenton Varda
 47 */
 48public class CodedInputStreamTest extends TestCase {
 49  /**
 50   * Helper to construct a byte array from a bunch of bytes.  The inputs are
 51   * actually ints so that I can use hex notation and not get stupid errors
 52   * about precision.
 53   */
 54  private byte[] bytes(int... bytesAsInts) {
 55    byte[] bytes = new byte[bytesAsInts.length];
 56    for (int i = 0; i < bytesAsInts.length; i++) {
 57      bytes[i] = (byte) bytesAsInts[i];
 58    }
 59    return bytes;
 60  }
 61
 62  /**
 63   * An InputStream which limits the number of bytes it reads at a time.
 64   * We use this to make sure that CodedInputStream doesn't screw up when
 65   * reading in small blocks.
 66   */
 67  private static final class SmallBlockInputStream extends FilterInputStream {
 68    private final int blockSize;
 69
 70    public SmallBlockInputStream(byte[] data, int blockSize) {
 71      this(new ByteArrayInputStream(data), blockSize);
 72    }
 73
 74    public SmallBlockInputStream(InputStream in, int blockSize) {
 75      super(in);
 76      this.blockSize = blockSize;
 77    }
 78
 79    public int read(byte[] b) throws IOException {
 80      return super.read(b, 0, Math.min(b.length, blockSize));
 81    }
 82
 83    public int read(byte[] b, int off, int len) throws IOException {
 84      return super.read(b, off, Math.min(len, blockSize));
 85    }
 86  }
 87
 88  /**
 89   * Parses the given bytes using readRawVarint32() and readRawVarint64() and
 90   * checks that the result matches the given value.
 91   */
 92  private void assertReadVarint(byte[] data, long value) throws Exception {
 93    CodedInputStream input = CodedInputStream.newInstance(data);
 94    assertEquals((int)value, input.readRawVarint32());
 95
 96    input = CodedInputStream.newInstance(data);
 97    assertEquals(value, input.readRawVarint64());
 98    assertTrue(input.isAtEnd());
 99
100    // Try different block sizes.
101    for (int blockSize = 1; blockSize <= 16; blockSize *= 2) {
102      input = CodedInputStream.newInstance(
103        new SmallBlockInputStream(data, blockSize));
104      assertEquals((int)value, input.readRawVarint32());
105
106      input = CodedInputStream.newInstance(
107        new SmallBlockInputStream(data, blockSize));
108      assertEquals(value, input.readRawVarint64());
109      assertTrue(input.isAtEnd());
110    }
111
112    // Try reading direct from an InputStream.  We want to verify that it
113    // doesn't read past the end of the input, so we copy to a new, bigger
114    // array first.
115    byte[] longerData = new byte[data.length + 1];
116    System.arraycopy(data, 0, longerData, 0, data.length);
117    InputStream rawInput = new ByteArrayInputStream(longerData);
118    assertEquals((int)value, CodedInputStream.readRawVarint32(rawInput));
119    assertEquals(1, rawInput.available());
120  }
121
122  /**
123   * Parses the given bytes using readRawVarint32() and readRawVarint64() and
124   * expects them to fail with an InvalidProtocolBufferException whose
125   * description matches the given one.
126   */
127  private void assertReadVarintFailure(
128      InvalidProtocolBufferException expected, byte[] data)
129      throws Exception {
130    CodedInputStream input = CodedInputStream.newInstance(data);
131    try {
132      input.readRawVarint32();
133      fail("Should have thrown an exception.");
134    } catch (InvalidProtocolBufferException e) {
135      assertEquals(expected.getMessage(), e.getMessage());
136    }
137
138    input = CodedInputStream.newInstance(data);
139    try {
140      input.readRawVarint64();
141      fail("Should have thrown an exception.");
142    } catch (InvalidProtocolBufferException e) {
143      assertEquals(expected.getMessage(), e.getMessage());
144    }
145
146    // Make sure we get the same error when reading direct from an InputStream.
147    try {
148      CodedInputStream.readRawVarint32(new ByteArrayInputStream(data));
149      fail("Should have thrown an exception.");
150    } catch (InvalidProtocolBufferException e) {
151      assertEquals(expected.getMessage(), e.getMessage());
152    }
153  }
154
155  /** Tests readRawVarint32() and readRawVarint64(). */
156  public void testReadVarint() throws Exception {
157    assertReadVarint(bytes(0x00), 0);
158    assertReadVarint(bytes(0x01), 1);
159    assertReadVarint(bytes(0x7f), 127);
160    // 14882
161    assertReadVarint(bytes(0xa2, 0x74), (0x22 << 0) | (0x74 << 7));
162    // 2961488830
163    assertReadVarint(bytes(0xbe, 0xf7, 0x92, 0x84, 0x0b),
164      (0x3e << 0) | (0x77 << 7) | (0x12 << 14) | (0x04 << 21) |
165      (0x0bL << 28));
166
167    // 64-bit
168    // 7256456126
169    assertReadVarint(bytes(0xbe, 0xf7, 0x92, 0x84, 0x1b),
170      (0x3e << 0) | (0x77 << 7) | (0x12 << 14) | (0x04 << 21) |
171      (0x1bL << 28));
172    // 41256202580718336
173    assertReadVarint(
174      bytes(0x80, 0xe6, 0xeb, 0x9c, 0xc3, 0xc9, 0xa4, 0x49),
175      (0x00 << 0) | (0x66 << 7) | (0x6b << 14) | (0x1c << 21) |
176      (0x43L << 28) | (0x49L << 35) | (0x24L << 42) | (0x49L << 49));
177    // 11964378330978735131
178    assertReadVarint(
179      bytes(0x9b, 0xa8, 0xf9, 0xc2, 0xbb, 0xd6, 0x80, 0x85, 0xa6, 0x01),
180      (0x1b << 0) | (0x28 << 7) | (0x79 << 14) | (0x42 << 21) |
181      (0x3bL << 28) | (0x56L << 35) | (0x00L << 42) |
182      (0x05L << 49) | (0x26L << 56) | (0x01L << 63));
183
184    // Failures
185    assertReadVarintFailure(
186      InvalidProtocolBufferException.malformedVarint(),
187      bytes(0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
188            0x00));
189    assertReadVarintFailure(
190      InvalidProtocolBufferException.truncatedMessage(),
191      bytes(0x80));
192  }
193
194  /**
195   * Parses the given bytes using readRawLittleEndian32() and checks
196   * that the result matches the given value.
197   */
198  private void assertReadLittleEndian32(byte[] data, int value)
199                                        throws Exception {
200    CodedInputStream input = CodedInputStream.newInstance(data);
201    assertEquals(value, input.readRawLittleEndian32());
202    assertTrue(input.isAtEnd());
203
204    // Try different block sizes.
205    for (int blockSize = 1; blockSize <= 16; blockSize *= 2) {
206      input = CodedInputStream.newInstance(
207        new SmallBlockInputStream(data, blockSize));
208      assertEquals(value, input.readRawLittleEndian32());
209      assertTrue(input.isAtEnd());
210    }
211  }
212
213  /**
214   * Parses the given bytes using readRawLittleEndian64() and checks
215   * that the result matches the given value.
216   */
217  private void assertReadLittleEndian64(byte[] data, long value)
218                                        throws Exception {
219    CodedInputStream input = CodedInputStream.newInstance(data);
220    assertEquals(value, input.readRawLittleEndian64());
221    assertTrue(input.isAtEnd());
222
223    // Try different block sizes.
224    for (int blockSize = 1; blockSize <= 16; blockSize *= 2) {
225      input = CodedInputStream.newInstance(
226        new SmallBlockInputStream(data, blockSize));
227      assertEquals(value, input.readRawLittleEndian64());
228      assertTrue(input.isAtEnd());
229    }
230  }
231
232  /** Tests readRawLittleEndian32() and readRawLittleEndian64(). */
233  public void testReadLittleEndian() throws Exception {
234    assertReadLittleEndian32(bytes(0x78, 0x56, 0x34, 0x12), 0x12345678);
235    assertReadLittleEndian32(bytes(0xf0, 0xde, 0xbc, 0x9a), 0x9abcdef0);
236
237    assertReadLittleEndian64(
238      bytes(0xf0, 0xde, 0xbc, 0x9a, 0x78, 0x56, 0x34, 0x12),
239      0x123456789abcdef0L);
240    assertReadLittleEndian64(
241      bytes(0x78, 0x56, 0x34, 0x12, 0xf0, 0xde, 0xbc, 0x9a),
242      0x9abcdef012345678L);
243  }
244
245  /** Test decodeZigZag32() and decodeZigZag64(). */
246  public void testDecodeZigZag() throws Exception {
247    assertEquals( 0, CodedInputStream.decodeZigZag32(0));
248    assertEquals(-1, CodedInputStream.decodeZigZag32(1));
249    assertEquals( 1, CodedInputStream.decodeZigZag32(2));
250    assertEquals(-2, CodedInputStream.decodeZigZag32(3));
251    assertEquals(0x3FFFFFFF, CodedInputStream.decodeZigZag32(0x7FFFFFFE));
252    assertEquals(0xC0000000, CodedInputStream.decodeZigZag32(0x7FFFFFFF));
253    assertEquals(0x7FFFFFFF, CodedInputStream.decodeZigZag32(0xFFFFFFFE));
254    assertEquals(0x80000000, CodedInputStream.decodeZigZag32(0xFFFFFFFF));
255
256    assertEquals( 0, CodedInputStream.decodeZigZag64(0));
257    assertEquals(-1, CodedInputStream.decodeZigZag64(1));
258    assertEquals( 1, CodedInputStream.decodeZigZag64(2));
259    assertEquals(-2, CodedInputStream.decodeZigZag64(3));
260    assertEquals(0x000000003FFFFFFFL,
261                 CodedInputStream.decodeZigZag64(0x000000007FFFFFFEL));
262    assertEquals(0xFFFFFFFFC0000000L,
263                 CodedInputStream.decodeZigZag64(0x000000007FFFFFFFL));
264    assertEquals(0x000000007FFFFFFFL,
265                 CodedInputStream.decodeZigZag64(0x00000000FFFFFFFEL));
266    assertEquals(0xFFFFFFFF80000000L,
267                 CodedInputStream.decodeZigZag64(0x00000000FFFFFFFFL));
268    assertEquals(0x7FFFFFFFFFFFFFFFL,
269                 CodedInputStream.decodeZigZag64(0xFFFFFFFFFFFFFFFEL));
270    assertEquals(0x8000000000000000L,
271                 CodedInputStream.decodeZigZag64(0xFFFFFFFFFFFFFFFFL));
272  }
273
274  /** Tests reading and parsing a whole message with every field type. */
275  public void testReadWholeMessage() throws Exception {
276    TestAllTypes message = TestUtil.getAllSet();
277
278    byte[] rawBytes = message.toByteArray();
279    assertEquals(rawBytes.length, message.getSerializedSize());
280
281    TestAllTypes message2 = TestAllTypes.parseFrom(rawBytes);
282    TestUtil.assertAllFieldsSet(message2);
283
284    // Try different block sizes.
285    for (int blockSize = 1; blockSize < 256; blockSize *= 2) {
286      message2 = TestAllTypes.parseFrom(
287        new SmallBlockInputStream(rawBytes, blockSize));
288      TestUtil.assertAllFieldsSet(message2);
289    }
290  }
291
292  /** Tests skipField(). */
293  public void testSkipWholeMessage() throws Exception {
294    TestAllTypes message = TestUtil.getAllSet();
295    byte[] rawBytes = message.toByteArray();
296
297    // Create two parallel inputs.  Parse one as unknown fields while using
298    // skipField() to skip each field on the other.  Expect the same tags.
299    CodedInputStream input1 = CodedInputStream.newInstance(rawBytes);
300    CodedInputStream input2 = CodedInputStream.newInstance(rawBytes);
301    UnknownFieldSet.Builder unknownFields = UnknownFieldSet.newBuilder();
302
303    while (true) {
304      int tag = input1.readTag();
305      assertEquals(tag, input2.readTag());
306      if (tag == 0) {
307        break;
308      }
309      unknownFields.mergeFieldFrom(tag, input1);
310      input2.skipField(tag);
311    }
312  }
313
314  /**
315   * Test that a bug in skipRawBytes() has been fixed:  if the skip skips
316   * exactly up to a limit, this should not break things.
317   */
318  public void testSkipRawBytesBug() throws Exception {
319    byte[] rawBytes = new byte[] { 1, 2 };
320    CodedInputStream input = CodedInputStream.newInstance(rawBytes);
321
322    int limit = input.pushLimit(1);
323    input.skipRawBytes(1);
324    input.popLimit(limit);
325    assertEquals(2, input.readRawByte());
326  }
327
328  /**
329   * Test that a bug in skipRawBytes() has been fixed:  if the skip skips
330   * past the end of a buffer with a limit that has been set past the end of
331   * that buffer, this should not break things.
332   */
333  public void testSkipRawBytesPastEndOfBufferWithLimit() throws Exception {
334    byte[] rawBytes = new byte[] { 1, 2, 3, 4, 5 };
335    CodedInputStream input = CodedInputStream.newInstance(
336        new SmallBlockInputStream(rawBytes, 3));
337
338    int limit = input.pushLimit(4);
339    // In order to expose the bug we need to read at least one byte to prime the
340    // buffer inside the CodedInputStream.
341    assertEquals(1, input.readRawByte());
342    // Skip to the end of the limit.
343    input.skipRawBytes(3);
344    assertTrue(input.isAtEnd());
345    input.popLimit(limit);
346    assertEquals(5, input.readRawByte());
347  }
348
349  public void testReadHugeBlob() throws Exception {
350    // Allocate and initialize a 1MB blob.
351    byte[] blob = new byte[1 << 20];
352    for (int i = 0; i < blob.length; i++) {
353      blob[i] = (byte)i;
354    }
355
356    // Make a message containing it.
357    TestAllTypes.Builder builder = TestAllTypes.newBuilder();
358    TestUtil.setAllFields(builder);
359    builder.setOptionalBytes(ByteString.copyFrom(blob));
360    TestAllTypes message = builder.build();
361
362    // Serialize and parse it.  Make sure to parse from an InputStream, not
363    // directly from a ByteString, so that CodedInputStream uses buffered
364    // reading.
365    TestAllTypes message2 =
366      TestAllTypes.parseFrom(message.toByteString().newInput());
367
368    assertEquals(message.getOptionalBytes(), message2.getOptionalBytes());
369
370    // Make sure all the other fields were parsed correctly.
371    TestAllTypes message3 = TestAllTypes.newBuilder(message2)
372      .setOptionalBytes(TestUtil.getAllSet().getOptionalBytes())
373      .build();
374    TestUtil.assertAllFieldsSet(message3);
375  }
376
377  public void testReadMaliciouslyLargeBlob() throws Exception {
378    ByteString.Output rawOutput = ByteString.newOutput();
379    CodedOutputStream output = CodedOutputStream.newInstance(rawOutput);
380
381    int tag = WireFormat.makeTag(1, WireFormat.WIRETYPE_LENGTH_DELIMITED);
382    output.writeRawVarint32(tag);
383    output.writeRawVarint32(0x7FFFFFFF);
384    output.writeRawBytes(new byte[32]);  // Pad with a few random bytes.
385    output.flush();
386
387    CodedInputStream input = rawOutput.toByteString().newCodedInput();
388    assertEquals(tag, input.readTag());
389
390    try {
391      input.readBytes();
392      fail("Should have thrown an exception!");
393    } catch (InvalidProtocolBufferException e) {
394      // success.
395    }
396  }
397
398  private TestRecursiveMessage makeRecursiveMessage(int depth) {
399    if (depth == 0) {
400      return TestRecursiveMessage.newBuilder().setI(5).build();
401    } else {
402      return TestRecursiveMessage.newBuilder()
403        .setA(makeRecursiveMessage(depth - 1)).build();
404    }
405  }
406
407  private void assertMessageDepth(TestRecursiveMessage message, int depth) {
408    if (depth == 0) {
409      assertFalse(message.hasA());
410      assertEquals(5, message.getI());
411    } else {
412      assertTrue(message.hasA());
413      assertMessageDepth(message.getA(), depth - 1);
414    }
415  }
416
417  public void testMaliciousRecursion() throws Exception {
418    ByteString data64 = makeRecursiveMessage(64).toByteString();
419    ByteString data65 = makeRecursiveMessage(65).toByteString();
420
421    assertMessageDepth(TestRecursiveMessage.parseFrom(data64), 64);
422
423    try {
424      TestRecursiveMessage.parseFrom(data65);
425      fail("Should have thrown an exception!");
426    } catch (InvalidProtocolBufferException e) {
427      // success.
428    }
429
430    CodedInputStream input = data64.newCodedInput();
431    input.setRecursionLimit(8);
432    try {
433      TestRecursiveMessage.parseFrom(input);
434      fail("Should have thrown an exception!");
435    } catch (InvalidProtocolBufferException e) {
436      // success.
437    }
438  }
439
440  public void testSizeLimit() throws Exception {
441    CodedInputStream input = CodedInputStream.newInstance(
442      TestUtil.getAllSet().toByteString().newInput());
443    input.setSizeLimit(16);
444
445    try {
446      TestAllTypes.parseFrom(input);
447      fail("Should have thrown an exception!");
448    } catch (InvalidProtocolBufferException e) {
449      // success.
450    }
451  }
452
453  public void testResetSizeCounter() throws Exception {
454    CodedInputStream input = CodedInputStream.newInstance(
455        new SmallBlockInputStream(new byte[256], 8));
456    input.setSizeLimit(16);
457    input.readRawBytes(16);
458    assertEquals(16, input.getTotalBytesRead());
459
460    try {
461      input.readRawByte();
462      fail("Should have thrown an exception!");
463    } catch (InvalidProtocolBufferException e) {
464      // success.
465    }
466
467    input.resetSizeCounter();
468    assertEquals(0, input.getTotalBytesRead());
469    input.readRawByte();  // No exception thrown.
470    input.resetSizeCounter();
471    assertEquals(0, input.getTotalBytesRead());
472
473    try {
474      input.readRawBytes(16);  // Hits limit again.
475      fail("Should have thrown an exception!");
476    } catch (InvalidProtocolBufferException e) {
477      // success.
478    }
479  }
480
481  /**
482   * Tests that if we read an string that contains invalid UTF-8, no exception
483   * is thrown.  Instead, the invalid bytes are replaced with the Unicode
484   * "replacement character" U+FFFD.
485   */
486  public void testReadInvalidUtf8() throws Exception {
487    ByteString.Output rawOutput = ByteString.newOutput();
488    CodedOutputStream output = CodedOutputStream.newInstance(rawOutput);
489
490    int tag = WireFormat.makeTag(1, WireFormat.WIRETYPE_LENGTH_DELIMITED);
491    output.writeRawVarint32(tag);
492    output.writeRawVarint32(1);
493    output.writeRawBytes(new byte[] { (byte)0x80 });
494    output.flush();
495
496    CodedInputStream input = rawOutput.toByteString().newCodedInput();
497    assertEquals(tag, input.readTag());
498    String text = input.readString();
499    assertEquals(0xfffd, text.charAt(0));
500  }
501
502  public void testReadFromSlice() throws Exception {
503    byte[] bytes = bytes(0, 1, 2, 3, 4, 5, 6, 7, 8, 9);
504    CodedInputStream in = CodedInputStream.newInstance(bytes, 3, 5);
505    assertEquals(0, in.getTotalBytesRead());
506    for (int i = 3; i < 8; i++) {
507      assertEquals(i, in.readRawByte());
508      assertEquals(i-2, in.getTotalBytesRead());
509    }
510    // eof
511    assertEquals(0, in.readTag());
512    assertEquals(5, in.getTotalBytesRead());
513  }
514
515  public void testInvalidTag() throws Exception {
516    // Any tag number which corresponds to field number zero is invalid and
517    // should throw InvalidProtocolBufferException.
518    for (int i = 0; i < 8; i++) {
519      try {
520        CodedInputStream.newInstance(bytes(i)).readTag();
521        fail("Should have thrown an exception.");
522      } catch (InvalidProtocolBufferException e) {
523        assertEquals(InvalidProtocolBufferException.invalidTag().getMessage(),
524                     e.getMessage());
525      }
526    }
527  }
528}