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