Free cookie consent management tool by TermsFeed Policy Generator

source: trunk/sources/HeuristicLab.ExtLibs/HeuristicLab.ProtobufCS/0.9.1/ProtobufCS/src/ProtocolBuffers.Test/CodedInputStreamTest.cs @ 4068

Last change on this file since 4068 was 4068, checked in by swagner, 14 years ago

Sorted usings and removed unused usings in entire solution (#1094)

File size: 17.8 KB
Line 
1#region Copyright notice and license
2// Protocol Buffers - Google's data interchange format
3// Copyright 2008 Google Inc.  All rights reserved.
4// http://github.com/jskeet/dotnet-protobufs/
5// Original C++/Java/Python code:
6// http://code.google.com/p/protobuf/
7//
8// Redistribution and use in source and binary forms, with or without
9// modification, are permitted provided that the following conditions are
10// met:
11//
12//     * Redistributions of source code must retain the above copyright
13// notice, this list of conditions and the following disclaimer.
14//     * Redistributions in binary form must reproduce the above
15// copyright notice, this list of conditions and the following disclaimer
16// in the documentation and/or other materials provided with the
17// distribution.
18//     * Neither the name of Google Inc. nor the names of its
19// contributors may be used to endorse or promote products derived from
20// this software without specific prior written permission.
21//
22// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
23// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
24// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
25// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
26// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
27// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
28// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
29// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
30// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
31// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
32// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
33#endregion
34
35using System;
36using System.IO;
37using Google.ProtocolBuffers.TestProtos;
38using NUnit.Framework;
39
40namespace Google.ProtocolBuffers {
41  [TestFixture]
42  public class CodedInputStreamTest {
43
44    /// <summary>
45    /// Helper to construct a byte array from a bunch of bytes.  The inputs are
46    /// actually ints so that I can use hex notation and not get stupid errors
47    /// about precision.
48    /// </summary>
49    private static byte[] Bytes(params int[] bytesAsInts) {
50      byte[] bytes = new byte[bytesAsInts.Length];
51      for (int i = 0; i < bytesAsInts.Length; i++) {
52        bytes[i] = (byte)bytesAsInts[i];
53      }
54      return bytes;
55    }
56
57    /// <summary>
58    /// Parses the given bytes using ReadRawVarint32() and ReadRawVarint64() and
59    /// </summary>
60    private static void AssertReadVarint(byte[] data, ulong value) {
61      CodedInputStream input = CodedInputStream.CreateInstance(data);
62      Assert.AreEqual((uint)value, input.ReadRawVarint32());
63
64      input = CodedInputStream.CreateInstance(data);
65      Assert.AreEqual(value, input.ReadRawVarint64());
66      Assert.IsTrue(input.IsAtEnd);
67
68      // Try different block sizes.
69      for (int bufferSize = 1; bufferSize <= 16; bufferSize *= 2) {
70        input = CodedInputStream.CreateInstance(new SmallBlockInputStream(data, bufferSize));
71        Assert.AreEqual((uint)value, input.ReadRawVarint32());
72
73        input = CodedInputStream.CreateInstance(new SmallBlockInputStream(data, bufferSize));
74        Assert.AreEqual(value, input.ReadRawVarint64());
75        Assert.IsTrue(input.IsAtEnd);
76      }
77
78      // Try reading directly from a MemoryStream. We want to verify that it
79      // doesn't read past the end of the input, so write an extra byte - this
80      // lets us test the position at the end.
81      MemoryStream memoryStream = new MemoryStream();
82      memoryStream.Write(data, 0, data.Length);
83      memoryStream.WriteByte(0);
84      memoryStream.Position = 0;
85      Assert.AreEqual((uint)value, CodedInputStream.ReadRawVarint32(memoryStream));
86      Assert.AreEqual(data.Length, memoryStream.Position);
87    }
88
89    /// <summary>
90    /// Parses the given bytes using ReadRawVarint32() and ReadRawVarint64() and
91    /// expects them to fail with an InvalidProtocolBufferException whose
92    /// description matches the given one.
93    /// </summary>
94    private static void AssertReadVarintFailure(InvalidProtocolBufferException expected, byte[] data) {
95      CodedInputStream input = CodedInputStream.CreateInstance(data);
96      try {
97        input.ReadRawVarint32();
98        Assert.Fail("Should have thrown an exception.");
99      }
100      catch (InvalidProtocolBufferException e) {
101        Assert.AreEqual(expected.Message, e.Message);
102      }
103
104      input = CodedInputStream.CreateInstance(data);
105      try {
106        input.ReadRawVarint64();
107        Assert.Fail("Should have thrown an exception.");
108      }
109      catch (InvalidProtocolBufferException e) {
110        Assert.AreEqual(expected.Message, e.Message);
111      }
112
113      // Make sure we get the same error when reading directly from a Stream.
114      try {
115        CodedInputStream.ReadRawVarint32(new MemoryStream(data));
116        Assert.Fail("Should have thrown an exception.");
117      }
118      catch (InvalidProtocolBufferException e) {
119        Assert.AreEqual(expected.Message, e.Message);
120      }
121    }
122
123    [Test]
124    public void ReadVarint() {
125      AssertReadVarint(Bytes(0x00), 0);
126      AssertReadVarint(Bytes(0x01), 1);
127      AssertReadVarint(Bytes(0x7f), 127);
128      // 14882
129      AssertReadVarint(Bytes(0xa2, 0x74), (0x22 << 0) | (0x74 << 7));
130      // 2961488830
131      AssertReadVarint(Bytes(0xbe, 0xf7, 0x92, 0x84, 0x0b),
132        (0x3e << 0) | (0x77 << 7) | (0x12 << 14) | (0x04 << 21) |
133        (0x0bL << 28));
134
135      // 64-bit
136      // 7256456126
137      AssertReadVarint(Bytes(0xbe, 0xf7, 0x92, 0x84, 0x1b),
138        (0x3e << 0) | (0x77 << 7) | (0x12 << 14) | (0x04 << 21) |
139        (0x1bL << 28));
140      // 41256202580718336
141      AssertReadVarint(Bytes(0x80, 0xe6, 0xeb, 0x9c, 0xc3, 0xc9, 0xa4, 0x49),
142        (0x00 << 0) | (0x66 << 7) | (0x6b << 14) | (0x1c << 21) |
143        (0x43L << 28) | (0x49L << 35) | (0x24L << 42) | (0x49L << 49));
144      // 11964378330978735131
145      AssertReadVarint(Bytes(0x9b, 0xa8, 0xf9, 0xc2, 0xbb, 0xd6, 0x80, 0x85, 0xa6, 0x01),
146        (0x1b << 0) | (0x28 << 7) | (0x79 << 14) | (0x42 << 21) |
147        (0x3bUL << 28) | (0x56UL << 35) | (0x00UL << 42) |
148        (0x05UL << 49) | (0x26UL << 56) | (0x01UL << 63));
149
150      // Failures
151      AssertReadVarintFailure(
152        InvalidProtocolBufferException.MalformedVarint(),
153        Bytes(0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
154              0x00));
155      AssertReadVarintFailure(
156        InvalidProtocolBufferException.TruncatedMessage(),
157        Bytes(0x80));
158    }
159
160    /// <summary>
161    /// Parses the given bytes using ReadRawLittleEndian32() and checks
162    /// that the result matches the given value.
163    /// </summary>
164    private static void AssertReadLittleEndian32(byte[] data, uint value) {
165      CodedInputStream input = CodedInputStream.CreateInstance(data);
166      Assert.AreEqual(value, input.ReadRawLittleEndian32());
167      Assert.IsTrue(input.IsAtEnd);
168
169      // Try different block sizes.
170      for (int blockSize = 1; blockSize <= 16; blockSize *= 2) {
171        input = CodedInputStream.CreateInstance(
172          new SmallBlockInputStream(data, blockSize));
173        Assert.AreEqual(value, input.ReadRawLittleEndian32());
174        Assert.IsTrue(input.IsAtEnd);
175      }
176    }
177
178    /// <summary>
179    /// Parses the given bytes using ReadRawLittleEndian64() and checks
180    /// that the result matches the given value.
181    /// </summary>
182    private static void AssertReadLittleEndian64(byte[] data, ulong value) {
183      CodedInputStream input = CodedInputStream.CreateInstance(data);
184      Assert.AreEqual(value, input.ReadRawLittleEndian64());
185      Assert.IsTrue(input.IsAtEnd);
186
187      // Try different block sizes.
188      for (int blockSize = 1; blockSize <= 16; blockSize *= 2) {
189        input = CodedInputStream.CreateInstance(
190          new SmallBlockInputStream(data, blockSize));
191        Assert.AreEqual(value, input.ReadRawLittleEndian64());
192        Assert.IsTrue(input.IsAtEnd);
193      }
194    }
195
196    [Test]
197    public void ReadLittleEndian() {
198      AssertReadLittleEndian32(Bytes(0x78, 0x56, 0x34, 0x12), 0x12345678);
199      AssertReadLittleEndian32(Bytes(0xf0, 0xde, 0xbc, 0x9a), 0x9abcdef0);
200
201      AssertReadLittleEndian64(Bytes(0xf0, 0xde, 0xbc, 0x9a, 0x78, 0x56, 0x34, 0x12),
202        0x123456789abcdef0L);
203      AssertReadLittleEndian64(
204        Bytes(0x78, 0x56, 0x34, 0x12, 0xf0, 0xde, 0xbc, 0x9a), 0x9abcdef012345678UL);
205    }
206
207    [Test]
208    public void DecodeZigZag32() {
209      Assert.AreEqual(0, CodedInputStream.DecodeZigZag32(0));
210      Assert.AreEqual(-1, CodedInputStream.DecodeZigZag32(1));
211      Assert.AreEqual(1, CodedInputStream.DecodeZigZag32(2));
212      Assert.AreEqual(-2, CodedInputStream.DecodeZigZag32(3));
213      Assert.AreEqual(0x3FFFFFFF, CodedInputStream.DecodeZigZag32(0x7FFFFFFE));
214      Assert.AreEqual(unchecked((int)0xC0000000), CodedInputStream.DecodeZigZag32(0x7FFFFFFF));
215      Assert.AreEqual(0x7FFFFFFF, CodedInputStream.DecodeZigZag32(0xFFFFFFFE));
216      Assert.AreEqual(unchecked((int)0x80000000), CodedInputStream.DecodeZigZag32(0xFFFFFFFF));
217    }
218
219    [Test]
220    public void DecodeZigZag64() {
221      Assert.AreEqual(0, CodedInputStream.DecodeZigZag64(0));
222      Assert.AreEqual(-1, CodedInputStream.DecodeZigZag64(1));
223      Assert.AreEqual(1, CodedInputStream.DecodeZigZag64(2));
224      Assert.AreEqual(-2, CodedInputStream.DecodeZigZag64(3));
225      Assert.AreEqual(0x000000003FFFFFFFL, CodedInputStream.DecodeZigZag64(0x000000007FFFFFFEL));
226      Assert.AreEqual(unchecked((long)0xFFFFFFFFC0000000L), CodedInputStream.DecodeZigZag64(0x000000007FFFFFFFL));
227      Assert.AreEqual(0x000000007FFFFFFFL, CodedInputStream.DecodeZigZag64(0x00000000FFFFFFFEL));
228      Assert.AreEqual(unchecked((long)0xFFFFFFFF80000000L), CodedInputStream.DecodeZigZag64(0x00000000FFFFFFFFL));
229      Assert.AreEqual(0x7FFFFFFFFFFFFFFFL, CodedInputStream.DecodeZigZag64(0xFFFFFFFFFFFFFFFEL));
230      Assert.AreEqual(unchecked((long)0x8000000000000000L), CodedInputStream.DecodeZigZag64(0xFFFFFFFFFFFFFFFFL));
231    }
232
233    [Test]
234    public void ReadWholeMessage() {
235      TestAllTypes message = TestUtil.GetAllSet();
236
237      byte[] rawBytes = message.ToByteArray();
238      Assert.AreEqual(rawBytes.Length, message.SerializedSize);
239      TestAllTypes message2 = TestAllTypes.ParseFrom(rawBytes);
240      TestUtil.AssertAllFieldsSet(message2);
241
242      // Try different block sizes.
243      for (int blockSize = 1; blockSize < 256; blockSize *= 2) {
244        message2 = TestAllTypes.ParseFrom(new SmallBlockInputStream(rawBytes, blockSize));
245        TestUtil.AssertAllFieldsSet(message2);
246      }
247    }
248
249    [Test]
250    public void SkipWholeMessage() {
251      TestAllTypes message = TestUtil.GetAllSet();
252      byte[] rawBytes = message.ToByteArray();
253
254      // Create two parallel inputs.  Parse one as unknown fields while using
255      // skipField() to skip each field on the other.  Expect the same tags.
256      CodedInputStream input1 = CodedInputStream.CreateInstance(rawBytes);
257      CodedInputStream input2 = CodedInputStream.CreateInstance(rawBytes);
258      UnknownFieldSet.Builder unknownFields = UnknownFieldSet.CreateBuilder();
259
260      while (true) {
261        uint tag = input1.ReadTag();
262        Assert.AreEqual(tag, input2.ReadTag());
263        if (tag == 0) {
264          break;
265        }
266        unknownFields.MergeFieldFrom(tag, input1);
267        input2.SkipField(tag);
268      }
269    }
270
271    /// <summary>
272    /// Test that a bug in SkipRawBytes has been fixed: if the skip
273    /// skips exactly up to a limit, this should bnot break things
274    /// </summary>
275    [Test]
276    public void SkipRawBytesBug() {
277      byte[] rawBytes = new byte[] { 1, 2 };
278      CodedInputStream input = CodedInputStream.CreateInstance(rawBytes);
279
280      int limit = input.PushLimit(1);
281      input.SkipRawBytes(1);
282      input.PopLimit(limit);
283      Assert.AreEqual(2, input.ReadRawByte());
284    }
285
286    public void ReadHugeBlob() {
287      // Allocate and initialize a 1MB blob.
288      byte[] blob = new byte[1 << 20];
289      for (int i = 0; i < blob.Length; i++) {
290        blob[i] = (byte)i;
291      }
292
293      // Make a message containing it.
294      TestAllTypes.Builder builder = TestAllTypes.CreateBuilder();
295      TestUtil.SetAllFields(builder);
296      builder.SetOptionalBytes(ByteString.CopyFrom(blob));
297      TestAllTypes message = builder.Build();
298
299      // Serialize and parse it.  Make sure to parse from an InputStream, not
300      // directly from a ByteString, so that CodedInputStream uses buffered
301      // reading.
302      TestAllTypes message2 = TestAllTypes.ParseFrom(message.ToByteString().CreateCodedInput());
303
304      Assert.AreEqual(message.OptionalBytes, message2.OptionalBytes);
305
306      // Make sure all the other fields were parsed correctly.
307      TestAllTypes message3 = TestAllTypes.CreateBuilder(message2)
308        .SetOptionalBytes(TestUtil.GetAllSet().OptionalBytes)
309        .Build();
310      TestUtil.AssertAllFieldsSet(message3);
311    }
312
313    [Test]
314    public void ReadMaliciouslyLargeBlob() {
315      MemoryStream ms = new MemoryStream();
316      CodedOutputStream output = CodedOutputStream.CreateInstance(ms);
317
318      uint tag = WireFormat.MakeTag(1, WireFormat.WireType.LengthDelimited);
319      output.WriteRawVarint32(tag);
320      output.WriteRawVarint32(0x7FFFFFFF);
321      output.WriteRawBytes(new byte[32]);  // Pad with a few random bytes.
322      output.Flush();
323      ms.Position = 0;
324
325      CodedInputStream input = CodedInputStream.CreateInstance(ms);
326      Assert.AreEqual(tag, input.ReadTag());
327
328      try {
329        input.ReadBytes();
330        Assert.Fail("Should have thrown an exception!");
331      }
332      catch (InvalidProtocolBufferException) {
333        // success.
334      }
335    }
336
337    private static TestRecursiveMessage MakeRecursiveMessage(int depth) {
338      if (depth == 0) {
339        return TestRecursiveMessage.CreateBuilder().SetI(5).Build();
340      } else {
341        return TestRecursiveMessage.CreateBuilder()
342          .SetA(MakeRecursiveMessage(depth - 1)).Build();
343      }
344    }
345
346    private static void AssertMessageDepth(TestRecursiveMessage message, int depth) {
347      if (depth == 0) {
348        Assert.IsFalse(message.HasA);
349        Assert.AreEqual(5, message.I);
350      } else {
351        Assert.IsTrue(message.HasA);
352        AssertMessageDepth(message.A, depth - 1);
353      }
354    }
355
356    [Test]
357    public void MaliciousRecursion() {
358      ByteString data64 = MakeRecursiveMessage(64).ToByteString();
359      ByteString data65 = MakeRecursiveMessage(65).ToByteString();
360
361      AssertMessageDepth(TestRecursiveMessage.ParseFrom(data64), 64);
362
363      try {
364        TestRecursiveMessage.ParseFrom(data65);
365        Assert.Fail("Should have thrown an exception!");
366      }
367      catch (InvalidProtocolBufferException) {
368        // success.
369      }
370
371      CodedInputStream input = data64.CreateCodedInput();
372      input.SetRecursionLimit(8);
373      try {
374        TestRecursiveMessage.ParseFrom(input);
375        Assert.Fail("Should have thrown an exception!");
376      }
377      catch (InvalidProtocolBufferException) {
378        // success.
379      }
380    }
381
382    [Test]
383    public void SizeLimit() {
384      // Have to use a Stream rather than ByteString.CreateCodedInput as SizeLimit doesn't
385      // apply to the latter case.
386      MemoryStream ms = new MemoryStream(TestUtil.GetAllSet().ToByteString().ToByteArray());
387      CodedInputStream input = CodedInputStream.CreateInstance(ms);
388      input.SetSizeLimit(16);
389
390      try {
391        TestAllTypes.ParseFrom(input);
392        Assert.Fail("Should have thrown an exception!");
393      }
394      catch (InvalidProtocolBufferException) {
395        // success.
396      }
397    }
398
399    [Test]
400    public void ResetSizeCounter() {
401      CodedInputStream input = CodedInputStream.CreateInstance(
402          new SmallBlockInputStream(new byte[256], 8));
403      input.SetSizeLimit(16);
404      input.ReadRawBytes(16);
405
406      try {
407        input.ReadRawByte();
408        Assert.Fail("Should have thrown an exception!");
409      }
410      catch (InvalidProtocolBufferException) {
411        // Success.
412      }
413
414      input.ResetSizeCounter();
415      input.ReadRawByte();  // No exception thrown.
416
417      try {
418        input.ReadRawBytes(16);  // Hits limit again.
419        Assert.Fail("Should have thrown an exception!");
420      }
421      catch (InvalidProtocolBufferException) {
422        // Success.
423      }
424    }
425
426    /// <summary>
427    /// Tests that if we read an string that contains invalid UTF-8, no exception
428    /// is thrown.  Instead, the invalid bytes are replaced with the Unicode
429    /// "replacement character" U+FFFD.
430    /// </summary>
431    [Test]
432    public void ReadInvalidUtf8() {
433      MemoryStream ms = new MemoryStream();
434      CodedOutputStream output = CodedOutputStream.CreateInstance(ms);
435
436      uint tag = WireFormat.MakeTag(1, WireFormat.WireType.LengthDelimited);
437      output.WriteRawVarint32(tag);
438      output.WriteRawVarint32(1);
439      output.WriteRawBytes(new byte[] { 0x80 });
440      output.Flush();
441      ms.Position = 0;
442
443      CodedInputStream input = CodedInputStream.CreateInstance(ms);
444      Assert.AreEqual(tag, input.ReadTag());
445      string text = input.ReadString();
446      Assert.AreEqual('\ufffd', text[0]);
447    }
448
449    /// <summary>
450    /// A stream which limits the number of bytes it reads at a time.
451    /// We use this to make sure that CodedInputStream doesn't screw up when
452    /// reading in small blocks.
453    /// </summary>
454    private sealed class SmallBlockInputStream : MemoryStream {
455      private readonly int blockSize;
456
457      public SmallBlockInputStream(byte[] data, int blockSize)
458        : base(data) {
459        this.blockSize = blockSize;
460      }
461
462      public override int Read(byte[] buffer, int offset, int count) {
463        return base.Read(buffer, offset, Math.Min(count, blockSize));
464      }
465    }
466  }
467}
Note: See TracBrowser for help on using the repository browser.