• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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/**
32 * @fileoverview Test cases for jspb's binary protocol buffer reader.
33 *
34 * There are two particular magic numbers that need to be pointed out -
35 * 2^64-1025 is the largest number representable as both a double and an
36 * unsigned 64-bit integer, and 2^63-513 is the largest number representable as
37 * both a double and a signed 64-bit integer.
38 *
39 * Test suite is written using Jasmine -- see http://jasmine.github.io/
40 *
41 * @author aappleby@google.com (Austin Appleby)
42 */
43
44goog.require('goog.testing.asserts');
45goog.require('jspb.BinaryConstants');
46goog.require('jspb.BinaryDecoder');
47goog.require('jspb.BinaryReader');
48goog.require('jspb.BinaryWriter');
49
50
51
52describe('binaryReaderTest', function() {
53  /**
54   * Tests the reader instance cache.
55   */
56  it('testInstanceCaches', /** @suppress {visibility} */ function() {
57    var writer = new jspb.BinaryWriter();
58    var dummyMessage = /** @type {!jspb.BinaryMessage} */({});
59    writer.writeMessage(1, dummyMessage, goog.nullFunction);
60    writer.writeMessage(2, dummyMessage, goog.nullFunction);
61
62    var buffer = writer.getResultBuffer();
63
64    // Empty the instance caches.
65    jspb.BinaryReader.instanceCache_ = [];
66
67    // Allocating and then freeing three decoders should leave us with three in
68    // the cache.
69
70    var decoder1 = jspb.BinaryDecoder.alloc();
71    var decoder2 = jspb.BinaryDecoder.alloc();
72    var decoder3 = jspb.BinaryDecoder.alloc();
73    decoder1.free();
74    decoder2.free();
75    decoder3.free();
76
77    assertEquals(3, jspb.BinaryDecoder.instanceCache_.length);
78    assertEquals(0, jspb.BinaryReader.instanceCache_.length);
79
80    // Allocating and then freeing a reader should remove one decoder from its
81    // cache, but it should stay stuck to the reader afterwards since we can't
82    // have a reader without a decoder.
83    jspb.BinaryReader.alloc().free();
84
85    assertEquals(2, jspb.BinaryDecoder.instanceCache_.length);
86    assertEquals(1, jspb.BinaryReader.instanceCache_.length);
87
88    // Allocating a reader should remove a reader from the cache.
89    var reader = jspb.BinaryReader.alloc(buffer);
90
91    assertEquals(2, jspb.BinaryDecoder.instanceCache_.length);
92    assertEquals(0, jspb.BinaryReader.instanceCache_.length);
93
94    // Processing the message reuses the current reader.
95    reader.nextField();
96    assertEquals(1, reader.getFieldNumber());
97    reader.readMessage(dummyMessage, function() {
98      assertEquals(0, jspb.BinaryReader.instanceCache_.length);
99    });
100
101    reader.nextField();
102    assertEquals(2, reader.getFieldNumber());
103    reader.readMessage(dummyMessage, function() {
104      assertEquals(0, jspb.BinaryReader.instanceCache_.length);
105    });
106
107    assertEquals(false, reader.nextField());
108
109    assertEquals(2, jspb.BinaryDecoder.instanceCache_.length);
110    assertEquals(0, jspb.BinaryReader.instanceCache_.length);
111
112    // Freeing the reader should put it back into the cache.
113    reader.free();
114
115    assertEquals(2, jspb.BinaryDecoder.instanceCache_.length);
116    assertEquals(1, jspb.BinaryReader.instanceCache_.length);
117  });
118
119
120  /**
121   * @param {number} x
122   * @return {number}
123   */
124  function truncate(x) {
125    var temp = new Float32Array(1);
126    temp[0] = x;
127    return temp[0];
128  }
129
130
131  /**
132   * Verifies that misuse of the reader class triggers assertions.
133   */
134  it('testReadErrors', /** @suppress {checkTypes|visibility} */ function() {
135    // Calling readMessage on a non-delimited field should trigger an
136    // assertion.
137    var reader = jspb.BinaryReader.alloc([8, 1]);
138    var dummyMessage = /** @type {!jspb.BinaryMessage} */({});
139    reader.nextField();
140    assertThrows(function() {
141      reader.readMessage(dummyMessage, goog.nullFunction);
142    });
143
144    // Reading past the end of the stream should trigger an assertion.
145    reader = jspb.BinaryReader.alloc([9, 1]);
146    reader.nextField();
147    assertThrows(function() {reader.readFixed64()});
148
149    // Reading past the end of a submessage should trigger an assertion.
150    reader = jspb.BinaryReader.alloc([10, 4, 13, 1, 1, 1]);
151    reader.nextField();
152    reader.readMessage(dummyMessage, function() {
153      reader.nextField();
154      assertThrows(function() {reader.readFixed32()});
155    });
156
157    // Skipping an invalid field should trigger an assertion.
158    reader = jspb.BinaryReader.alloc([12, 1]);
159    reader.nextWireType_ = 1000;
160    assertThrows(function() {reader.skipField()});
161
162    // Reading fields with the wrong wire type should assert.
163    reader = jspb.BinaryReader.alloc([9, 0, 0, 0, 0, 0, 0, 0, 0]);
164    reader.nextField();
165    assertThrows(function() {reader.readInt32()});
166    assertThrows(function() {reader.readInt32String()});
167    assertThrows(function() {reader.readInt64()});
168    assertThrows(function() {reader.readInt64String()});
169    assertThrows(function() {reader.readUint32()});
170    assertThrows(function() {reader.readUint32String()});
171    assertThrows(function() {reader.readUint64()});
172    assertThrows(function() {reader.readUint64String()});
173    assertThrows(function() {reader.readSint32()});
174    assertThrows(function() {reader.readBool()});
175    assertThrows(function() {reader.readEnum()});
176
177    reader = jspb.BinaryReader.alloc([8, 1]);
178    reader.nextField();
179    assertThrows(function() {reader.readFixed32()});
180    assertThrows(function() {reader.readFixed64()});
181    assertThrows(function() {reader.readSfixed32()});
182    assertThrows(function() {reader.readSfixed64()});
183    assertThrows(function() {reader.readFloat()});
184    assertThrows(function() {reader.readDouble()});
185
186    assertThrows(function() {reader.readString()});
187    assertThrows(function() {reader.readBytes()});
188  });
189
190
191  /**
192   * Tests encoding and decoding of unsigned field types.
193   * @param {Function} readField
194   * @param {Function} writeField
195   * @param {number} epsilon
196   * @param {number} upperLimit
197   * @param {Function} filter
198   * @private
199   * @suppress {missingProperties}
200   */
201  var doTestUnsignedField_ = function(readField,
202      writeField, epsilon, upperLimit, filter) {
203    assertNotNull(readField);
204    assertNotNull(writeField);
205
206    var writer = new jspb.BinaryWriter();
207
208    // Encode zero and limits.
209    writeField.call(writer, 1, filter(0));
210    writeField.call(writer, 2, filter(epsilon));
211    writeField.call(writer, 3, filter(upperLimit));
212
213    // Encode positive values.
214    for (var cursor = epsilon; cursor < upperLimit; cursor *= 1.1) {
215      writeField.call(writer, 4, filter(cursor));
216    }
217
218    var reader = jspb.BinaryReader.alloc(writer.getResultBuffer());
219
220    // Check zero and limits.
221    reader.nextField();
222    assertEquals(1, reader.getFieldNumber());
223    assertEquals(filter(0), readField.call(reader));
224
225    reader.nextField();
226    assertEquals(2, reader.getFieldNumber());
227    assertEquals(filter(epsilon), readField.call(reader));
228
229    reader.nextField();
230    assertEquals(3, reader.getFieldNumber());
231    assertEquals(filter(upperLimit), readField.call(reader));
232
233    // Check positive values.
234    for (var cursor = epsilon; cursor < upperLimit; cursor *= 1.1) {
235      reader.nextField();
236      if (4 != reader.getFieldNumber()) throw 'fail!';
237      if (filter(cursor) != readField.call(reader)) throw 'fail!';
238    }
239  };
240
241
242  /**
243   * Tests encoding and decoding of signed field types.
244   * @param {Function} readField
245   * @param {Function} writeField
246   * @param {number} epsilon
247   * @param {number} lowerLimit
248   * @param {number} upperLimit
249   * @param {Function} filter
250   * @private
251   * @suppress {missingProperties}
252   */
253  var doTestSignedField_ = function(readField,
254      writeField, epsilon, lowerLimit, upperLimit, filter) {
255    var writer = new jspb.BinaryWriter();
256
257    // Encode zero and limits.
258    writeField.call(writer, 1, filter(lowerLimit));
259    writeField.call(writer, 2, filter(-epsilon));
260    writeField.call(writer, 3, filter(0));
261    writeField.call(writer, 4, filter(epsilon));
262    writeField.call(writer, 5, filter(upperLimit));
263
264    var inputValues = [];
265
266    // Encode negative values.
267    for (var cursor = lowerLimit; cursor < -epsilon; cursor /= 1.1) {
268      var val = filter(cursor);
269      writeField.call(writer, 6, val);
270      inputValues.push({
271        fieldNumber: 6,
272        value: val
273      });
274    }
275
276    // Encode positive values.
277    for (var cursor = epsilon; cursor < upperLimit; cursor *= 1.1) {
278      var val = filter(cursor);
279      writeField.call(writer, 7, val);
280      inputValues.push({
281        fieldNumber: 7,
282        value: val
283      });
284    }
285
286    var reader = jspb.BinaryReader.alloc(writer.getResultBuffer());
287
288    // Check zero and limits.
289    reader.nextField();
290    assertEquals(1, reader.getFieldNumber());
291    assertEquals(filter(lowerLimit), readField.call(reader));
292
293    reader.nextField();
294    assertEquals(2, reader.getFieldNumber());
295    assertEquals(filter(-epsilon), readField.call(reader));
296
297    reader.nextField();
298    assertEquals(3, reader.getFieldNumber());
299    assertEquals(filter(0), readField.call(reader));
300
301    reader.nextField();
302    assertEquals(4, reader.getFieldNumber());
303    assertEquals(filter(epsilon), readField.call(reader));
304
305    reader.nextField();
306    assertEquals(5, reader.getFieldNumber());
307    assertEquals(filter(upperLimit), readField.call(reader));
308
309    for (var i = 0; i < inputValues.length; i++) {
310      var expected = inputValues[i];
311      reader.nextField();
312      assertEquals(expected.fieldNumber, reader.getFieldNumber());
313      assertEquals(expected.value, readField.call(reader));
314    }
315  };
316
317
318  /**
319   * Tests fields that use varint encoding.
320   */
321  it('testVarintFields', function() {
322    assertNotUndefined(jspb.BinaryReader.prototype.readUint32);
323    assertNotUndefined(jspb.BinaryWriter.prototype.writeUint32);
324    assertNotUndefined(jspb.BinaryReader.prototype.readUint64);
325    assertNotUndefined(jspb.BinaryWriter.prototype.writeUint64);
326    assertNotUndefined(jspb.BinaryReader.prototype.readBool);
327    assertNotUndefined(jspb.BinaryWriter.prototype.writeBool);
328    doTestUnsignedField_(
329        jspb.BinaryReader.prototype.readUint32,
330        jspb.BinaryWriter.prototype.writeUint32,
331        1, Math.pow(2, 32) - 1, Math.round);
332
333    doTestUnsignedField_(
334        jspb.BinaryReader.prototype.readUint64,
335        jspb.BinaryWriter.prototype.writeUint64,
336        1, Math.pow(2, 64) - 1025, Math.round);
337
338    doTestSignedField_(
339        jspb.BinaryReader.prototype.readInt32,
340        jspb.BinaryWriter.prototype.writeInt32,
341        1, -Math.pow(2, 31), Math.pow(2, 31) - 1, Math.round);
342
343    doTestSignedField_(
344        jspb.BinaryReader.prototype.readInt64,
345        jspb.BinaryWriter.prototype.writeInt64,
346        1, -Math.pow(2, 63), Math.pow(2, 63) - 513, Math.round);
347
348    doTestSignedField_(
349        jspb.BinaryReader.prototype.readEnum,
350        jspb.BinaryWriter.prototype.writeEnum,
351        1, -Math.pow(2, 31), Math.pow(2, 31) - 1, Math.round);
352
353    doTestUnsignedField_(
354        jspb.BinaryReader.prototype.readBool,
355        jspb.BinaryWriter.prototype.writeBool,
356        1, 1, function(x) { return !!x; });
357  });
358
359
360  /**
361   * Tests reading a field from hexadecimal string (format: '08 BE EF').
362   * @param {Function} readField
363   * @param {number} expected
364   * @param {string} hexString
365   */
366  function doTestHexStringVarint_(readField, expected, hexString) {
367    var bytesCount = (hexString.length + 1) / 3;
368    var bytes = new Uint8Array(bytesCount);
369    for (var i = 0; i < bytesCount; i++) {
370      bytes[i] = parseInt(hexString.substring(i * 3, i * 3 + 2), 16);
371    }
372    var reader = jspb.BinaryReader.alloc(bytes);
373    reader.nextField();
374    assertEquals(expected, readField.call(reader));
375  }
376
377
378  /**
379   * Tests non-canonical redundant varint decoding.
380   */
381  it('testRedundantVarintFields', function() {
382    assertNotNull(jspb.BinaryReader.prototype.readUint32);
383    assertNotNull(jspb.BinaryReader.prototype.readUint64);
384    assertNotNull(jspb.BinaryReader.prototype.readSint32);
385    assertNotNull(jspb.BinaryReader.prototype.readSint64);
386
387    // uint32 and sint32 take no more than 5 bytes
388    // 08 - field prefix (type = 0 means varint)
389    doTestHexStringVarint_(
390      jspb.BinaryReader.prototype.readUint32,
391      12, '08 8C 80 80 80 00');
392
393    // 11 stands for -6 in zigzag encoding
394    doTestHexStringVarint_(
395      jspb.BinaryReader.prototype.readSint32,
396      -6, '08 8B 80 80 80 00');
397
398    // uint64 and sint64 take no more than 10 bytes
399    // 08 - field prefix (type = 0 means varint)
400    doTestHexStringVarint_(
401      jspb.BinaryReader.prototype.readUint64,
402      12, '08 8C 80 80 80 80 80 80 80 80 00');
403
404    // 11 stands for -6 in zigzag encoding
405    doTestHexStringVarint_(
406      jspb.BinaryReader.prototype.readSint64,
407      -6, '08 8B 80 80 80 80 80 80 80 80 00');
408  });
409
410  /**
411   * Tests reading 64-bit integers as split values.
412   */
413  it('handles split 64 fields', function() {
414    var writer = new jspb.BinaryWriter();
415    writer.writeInt64String(1, '4294967296');
416    writer.writeSfixed64String(2, '4294967298');
417    writer.writeInt64String(3, '3');  // 3 is the zig-zag encoding of -2.
418    var reader = jspb.BinaryReader.alloc(writer.getResultBuffer());
419
420    function rejoin(lowBits, highBits) {
421      return highBits * 2 ** 32 + (lowBits >>> 0);
422    }
423    reader.nextField();
424    expect(reader.getFieldNumber()).toEqual(1);
425    expect(reader.readSplitVarint64(rejoin)).toEqual(0x100000000);
426
427    reader.nextField();
428    expect(reader.getFieldNumber()).toEqual(2);
429    expect(reader.readSplitFixed64(rejoin)).toEqual(0x100000002);
430
431    reader.nextField();
432    expect(reader.getFieldNumber()).toEqual(3);
433    expect(reader.readSplitZigzagVarint64(rejoin)).toEqual(-2);
434  });
435
436  /**
437   * Tests 64-bit fields that are handled as strings.
438   */
439  it('testStringInt64Fields', function() {
440    var writer = new jspb.BinaryWriter();
441
442    var testSignedData = [
443      '2730538252207801776',
444      '-2688470994844604560',
445      '3398529779486536359',
446      '3568577411627971000',
447      '272477188847484900',
448      '-6649058714086158188',
449      '-7695254765712060806',
450      '-4525541438037104029',
451      '-4993706538836508568',
452      '4990160321893729138'
453    ];
454    var testUnsignedData = [
455      '7822732630241694882',
456      '6753602971916687352',
457      '2399935075244442116',
458      '8724292567325338867',
459      '16948784802625696584',
460      '4136275908516066934',
461      '3575388346793700364',
462      '5167142028379259461',
463      '1557573948689737699',
464      '17100725280812548567'
465    ];
466
467    for (var i = 0; i < testSignedData.length; i++) {
468      writer.writeInt64String(2 * i + 1, testSignedData[i]);
469      writer.writeUint64String(2 * i + 2, testUnsignedData[i]);
470    }
471
472    var reader = jspb.BinaryReader.alloc(writer.getResultBuffer());
473
474    for (var i = 0; i < testSignedData.length; i++) {
475      reader.nextField();
476      assertEquals(2 * i + 1, reader.getFieldNumber());
477      assertEquals(testSignedData[i], reader.readInt64String());
478      reader.nextField();
479      assertEquals(2 * i + 2, reader.getFieldNumber());
480      assertEquals(testUnsignedData[i], reader.readUint64String());
481    }
482  });
483
484
485  /**
486   * Tests fields that use zigzag encoding.
487   */
488  it('testZigzagFields', function() {
489    doTestSignedField_(
490        jspb.BinaryReader.prototype.readSint32,
491        jspb.BinaryWriter.prototype.writeSint32,
492        1, -Math.pow(2, 31), Math.pow(2, 31) - 1, Math.round);
493
494    doTestSignedField_(
495        jspb.BinaryReader.prototype.readSint64,
496        jspb.BinaryWriter.prototype.writeSint64,
497        1, -Math.pow(2, 63), Math.pow(2, 63) - 513, Math.round);
498
499    doTestSignedField_(
500        jspb.BinaryReader.prototype.readSintHash64,
501        jspb.BinaryWriter.prototype.writeSintHash64, 1, -Math.pow(2, 63),
502        Math.pow(2, 63) - 513, jspb.utils.numberToHash64);
503  });
504
505
506  /**
507   * Tests fields that use fixed-length encoding.
508   */
509  it('testFixedFields', function() {
510    doTestUnsignedField_(
511        jspb.BinaryReader.prototype.readFixed32,
512        jspb.BinaryWriter.prototype.writeFixed32,
513        1, Math.pow(2, 32) - 1, Math.round);
514
515    doTestUnsignedField_(
516        jspb.BinaryReader.prototype.readFixed64,
517        jspb.BinaryWriter.prototype.writeFixed64,
518        1, Math.pow(2, 64) - 1025, Math.round);
519
520    doTestSignedField_(
521        jspb.BinaryReader.prototype.readSfixed32,
522        jspb.BinaryWriter.prototype.writeSfixed32,
523        1, -Math.pow(2, 31), Math.pow(2, 31) - 1, Math.round);
524
525    doTestSignedField_(
526        jspb.BinaryReader.prototype.readSfixed64,
527        jspb.BinaryWriter.prototype.writeSfixed64,
528        1, -Math.pow(2, 63), Math.pow(2, 63) - 513, Math.round);
529  });
530
531
532  /**
533   * Tests floating point fields.
534   */
535  it('testFloatFields', function() {
536    doTestSignedField_(
537        jspb.BinaryReader.prototype.readFloat,
538        jspb.BinaryWriter.prototype.writeFloat,
539        jspb.BinaryConstants.FLOAT32_MIN,
540        -jspb.BinaryConstants.FLOAT32_MAX,
541        jspb.BinaryConstants.FLOAT32_MAX,
542        truncate);
543
544    doTestSignedField_(
545        jspb.BinaryReader.prototype.readDouble,
546        jspb.BinaryWriter.prototype.writeDouble,
547        jspb.BinaryConstants.FLOAT64_EPS * 10,
548        -jspb.BinaryConstants.FLOAT64_MIN,
549        jspb.BinaryConstants.FLOAT64_MIN,
550        function(x) { return x; });
551  });
552
553
554  /**
555   * Tests length-delimited string fields.
556   */
557  it('testStringFields', function() {
558    var s1 = 'The quick brown fox jumps over the lazy dog.';
559    var s2 = '人人生而自由,在尊嚴和權利上一律平等。';
560
561    var writer = new jspb.BinaryWriter();
562
563    writer.writeString(1, s1);
564    writer.writeString(2, s2);
565
566    var reader = jspb.BinaryReader.alloc(writer.getResultBuffer());
567
568    reader.nextField();
569    assertEquals(1, reader.getFieldNumber());
570    assertEquals(s1, reader.readString());
571
572    reader.nextField();
573    assertEquals(2, reader.getFieldNumber());
574    assertEquals(s2, reader.readString());
575  });
576
577
578  /**
579   * Tests length-delimited byte fields.
580   */
581  it('testByteFields', function() {
582    var message = [];
583    var lowerLimit = 1;
584    var upperLimit = 256;
585    var scale = 1.1;
586
587    var writer = new jspb.BinaryWriter();
588
589    for (var cursor = lowerLimit; cursor < upperLimit; cursor *= 1.1) {
590      var len = Math.round(cursor);
591      var bytes = [];
592      for (var i = 0; i < len; i++) bytes.push(i % 256);
593
594      writer.writeBytes(len, bytes);
595    }
596
597    var reader = jspb.BinaryReader.alloc(writer.getResultBuffer());
598
599    for (var cursor = lowerLimit; reader.nextField(); cursor *= 1.1) {
600      var len = Math.round(cursor);
601      if (len != reader.getFieldNumber()) throw 'fail!';
602
603      var bytes = reader.readBytes();
604      if (len != bytes.length) throw 'fail!';
605      for (var i = 0; i < bytes.length; i++) {
606        if (i % 256 != bytes[i]) throw 'fail!';
607      }
608    }
609  });
610
611
612  /**
613   * Tests nested messages.
614   */
615  it('testNesting', function() {
616    var writer = new jspb.BinaryWriter();
617    var dummyMessage = /** @type {!jspb.BinaryMessage} */({});
618
619    writer.writeInt32(1, 100);
620
621    // Add one message with 3 int fields.
622    writer.writeMessage(2, dummyMessage, function() {
623      writer.writeInt32(3, 300);
624      writer.writeInt32(4, 400);
625      writer.writeInt32(5, 500);
626    });
627
628    // Add one empty message.
629    writer.writeMessage(6, dummyMessage, goog.nullFunction);
630
631    writer.writeInt32(7, 700);
632
633    var reader = jspb.BinaryReader.alloc(writer.getResultBuffer());
634
635    // Validate outermost message.
636
637    reader.nextField();
638    assertEquals(1, reader.getFieldNumber());
639    assertEquals(100, reader.readInt32());
640
641    reader.nextField();
642    assertEquals(2, reader.getFieldNumber());
643    reader.readMessage(dummyMessage, function() {
644      // Validate embedded message 1.
645      reader.nextField();
646      assertEquals(3, reader.getFieldNumber());
647      assertEquals(300, reader.readInt32());
648
649      reader.nextField();
650      assertEquals(4, reader.getFieldNumber());
651      assertEquals(400, reader.readInt32());
652
653      reader.nextField();
654      assertEquals(5, reader.getFieldNumber());
655      assertEquals(500, reader.readInt32());
656
657      assertEquals(false, reader.nextField());
658    });
659
660    reader.nextField();
661    assertEquals(6, reader.getFieldNumber());
662    reader.readMessage(dummyMessage, function() {
663      // Validate embedded message 2.
664
665      assertEquals(false, reader.nextField());
666    });
667
668    reader.nextField();
669    assertEquals(7, reader.getFieldNumber());
670    assertEquals(700, reader.readInt32());
671
672    assertEquals(false, reader.nextField());
673  });
674
675  /**
676   * Tests skipping fields of each type by interleaving them with sentinel
677   * values and skipping everything that's not a sentinel.
678   */
679  it('testSkipField', function() {
680    var writer = new jspb.BinaryWriter();
681
682    var sentinel = 123456789;
683
684    // Write varint fields of different sizes.
685    writer.writeInt32(1, sentinel);
686    writer.writeInt32(1, 1);
687    writer.writeInt32(1, 1000);
688    writer.writeInt32(1, 1000000);
689    writer.writeInt32(1, 1000000000);
690
691    // Write fixed 64-bit encoded fields.
692    writer.writeInt32(2, sentinel);
693    writer.writeDouble(2, 1);
694    writer.writeFixed64(2, 1);
695    writer.writeSfixed64(2, 1);
696
697    // Write fixed 32-bit encoded fields.
698    writer.writeInt32(3, sentinel);
699    writer.writeFloat(3, 1);
700    writer.writeFixed32(3, 1);
701    writer.writeSfixed32(3, 1);
702
703    // Write delimited fields.
704    writer.writeInt32(4, sentinel);
705    writer.writeBytes(4, [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]);
706    writer.writeString(4, 'The quick brown fox jumps over the lazy dog');
707
708    // Write a group with a nested group inside.
709    writer.writeInt32(5, sentinel);
710    var dummyMessage = /** @type {!jspb.BinaryMessage} */({});
711    writer.writeGroup(5, dummyMessage, function() {
712      // Previously the skipGroup implementation was wrong, which only consume
713      // the decoder by nextField. This case is for making the previous
714      // implementation failed in skipGroup by an early end group tag.
715      // The reason is 44 = 5 * 8 + 4, this will be translated in to a field
716      // with number 5 and with type 4 (end group)
717      writer.writeInt64(44, 44);
718      // This will make previous implementation failed by invalid tag (7).
719      writer.writeInt64(42, 47);
720      writer.writeInt64(42, 42);
721      // This is for making the previous implementation failed by an invalid
722      // varint. The bytes have at least 9 consecutive minus byte, which will
723      // fail in this.nextField for previous implementation.
724      writer.writeBytes(43, [255, 255, 255, 255, 255, 255, 255, 255, 255, 255]);
725      writer.writeGroup(6, dummyMessage, function() {
726        writer.writeInt64(84, 42);
727        writer.writeInt64(84, 44);
728        writer.writeBytes(
729          43, [255, 255, 255, 255, 255, 255, 255, 255, 255, 255]);
730      });
731    });
732
733    // Write final sentinel.
734    writer.writeInt32(6, sentinel);
735
736    var reader = jspb.BinaryReader.alloc(writer.getResultBuffer());
737
738    function skip(field, count) {
739      for (var i = 0; i < count; i++) {
740        reader.nextField();
741        if (field != reader.getFieldNumber()) throw 'fail!';
742        reader.skipField();
743      }
744    }
745
746    reader.nextField();
747    assertEquals(1, reader.getFieldNumber());
748    assertEquals(sentinel, reader.readInt32());
749    skip(1, 4);
750
751    reader.nextField();
752    assertEquals(2, reader.getFieldNumber());
753    assertEquals(sentinel, reader.readInt32());
754    skip(2, 3);
755
756    reader.nextField();
757    assertEquals(3, reader.getFieldNumber());
758    assertEquals(sentinel, reader.readInt32());
759    skip(3, 3);
760
761    reader.nextField();
762    assertEquals(4, reader.getFieldNumber());
763    assertEquals(sentinel, reader.readInt32());
764    skip(4, 2);
765
766    reader.nextField();
767    assertEquals(5, reader.getFieldNumber());
768    assertEquals(sentinel, reader.readInt32());
769    skip(5, 1);
770
771    reader.nextField();
772    assertEquals(6, reader.getFieldNumber());
773    assertEquals(sentinel, reader.readInt32());
774  });
775
776
777  /**
778   * Tests packed fields.
779   */
780  it('testPackedFields', function() {
781    var writer = new jspb.BinaryWriter();
782
783    var sentinel = 123456789;
784
785    var unsignedData = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
786    var signedData = [-1, 2, -3, 4, -5, 6, -7, 8, -9, 10];
787    var floatData = [1.1, 2.2, 3.3, 4.4, 5.5, 6.6, 7.7, 8.8, 9.9, 10.10];
788    var doubleData = [1.1, 2.2, 3.3, 4.4, 5.5, 6.6, 7.7, 8.8, 9.9, 10.10];
789    var boolData = [true, false, true, true, false, false, true, false];
790
791    for (var i = 0; i < floatData.length; i++) {
792      floatData[i] = truncate(floatData[i]);
793    }
794
795    writer.writeInt32(1, sentinel);
796
797    writer.writePackedInt32(2, signedData);
798    writer.writePackedInt64(2, signedData);
799    writer.writePackedUint32(2, unsignedData);
800    writer.writePackedUint64(2, unsignedData);
801    writer.writePackedSint32(2, signedData);
802    writer.writePackedSint64(2, signedData);
803    writer.writePackedFixed32(2, unsignedData);
804    writer.writePackedFixed64(2, unsignedData);
805    writer.writePackedSfixed32(2, signedData);
806    writer.writePackedSfixed64(2, signedData);
807    writer.writePackedFloat(2, floatData);
808    writer.writePackedDouble(2, doubleData);
809    writer.writePackedBool(2, boolData);
810    writer.writePackedEnum(2, unsignedData);
811
812    writer.writeInt32(3, sentinel);
813
814    var reader = jspb.BinaryReader.alloc(writer.getResultBuffer());
815
816    reader.nextField();
817    assertEquals(sentinel, reader.readInt32());
818
819    reader.nextField();
820    assertElementsEquals(reader.readPackedInt32(), signedData);
821
822    reader.nextField();
823    assertElementsEquals(reader.readPackedInt64(), signedData);
824
825    reader.nextField();
826    assertElementsEquals(reader.readPackedUint32(), unsignedData);
827
828    reader.nextField();
829    assertElementsEquals(reader.readPackedUint64(), unsignedData);
830
831    reader.nextField();
832    assertElementsEquals(reader.readPackedSint32(), signedData);
833
834    reader.nextField();
835    assertElementsEquals(reader.readPackedSint64(), signedData);
836
837    reader.nextField();
838    assertElementsEquals(reader.readPackedFixed32(), unsignedData);
839
840    reader.nextField();
841    assertElementsEquals(reader.readPackedFixed64(), unsignedData);
842
843    reader.nextField();
844    assertElementsEquals(reader.readPackedSfixed32(), signedData);
845
846    reader.nextField();
847    assertElementsEquals(reader.readPackedSfixed64(), signedData);
848
849    reader.nextField();
850    assertElementsEquals(reader.readPackedFloat(), floatData);
851
852    reader.nextField();
853    assertElementsEquals(reader.readPackedDouble(), doubleData);
854
855    reader.nextField();
856    assertElementsEquals(reader.readPackedBool(), boolData);
857
858    reader.nextField();
859    assertElementsEquals(reader.readPackedEnum(), unsignedData);
860
861    reader.nextField();
862    assertEquals(sentinel, reader.readInt32());
863  });
864
865
866  /**
867   * Byte blobs inside nested messages should always have their byte offset set
868   * relative to the start of the outermost blob, not the start of their parent
869   * blob.
870   */
871  it('testNestedBlobs', function() {
872    // Create a proto consisting of two nested messages, with the inner one
873    // containing a blob of bytes.
874
875    var fieldTag = (1 << 3) | jspb.BinaryConstants.WireType.DELIMITED;
876    var blob = [1, 2, 3, 4, 5];
877    var writer = new jspb.BinaryWriter();
878    var dummyMessage = /** @type {!jspb.BinaryMessage} */({});
879
880    writer.writeMessage(1, dummyMessage, function() {
881      writer.writeMessage(1, dummyMessage, function() {
882        writer.writeBytes(1, blob);
883      });
884    });
885
886    // Peel off the outer two message layers. Each layer should have two bytes
887    // of overhead, one for the field tag and one for the length of the inner
888    // blob.
889
890    var decoder1 = new jspb.BinaryDecoder(writer.getResultBuffer());
891    assertEquals(fieldTag, decoder1.readUnsignedVarint32());
892    assertEquals(blob.length + 4, decoder1.readUnsignedVarint32());
893
894    var decoder2 = new jspb.BinaryDecoder(decoder1.readBytes(blob.length + 4));
895    assertEquals(fieldTag, decoder2.readUnsignedVarint32());
896    assertEquals(blob.length + 2, decoder2.readUnsignedVarint32());
897
898    assertEquals(fieldTag, decoder2.readUnsignedVarint32());
899    assertEquals(blob.length, decoder2.readUnsignedVarint32());
900    var bytes = decoder2.readBytes(blob.length);
901
902    assertElementsEquals(bytes, blob);
903  });
904
905
906  /**
907   * Tests read callbacks.
908   */
909  it('testReadCallbacks', function() {
910    var writer = new jspb.BinaryWriter();
911    var dummyMessage = /** @type {!jspb.BinaryMessage} */({});
912
913    // Add an int, a submessage, and another int.
914    writer.writeInt32(1, 100);
915
916    writer.writeMessage(2, dummyMessage, function() {
917      writer.writeInt32(3, 300);
918      writer.writeInt32(4, 400);
919      writer.writeInt32(5, 500);
920    });
921
922    writer.writeInt32(7, 700);
923
924    // Create the reader and register a custom read callback.
925    var reader = jspb.BinaryReader.alloc(writer.getResultBuffer());
926
927    /**
928     * @param {!jspb.BinaryReader} reader
929     * @return {*}
930     */
931    function readCallback(reader) {
932      reader.nextField();
933      assertEquals(3, reader.getFieldNumber());
934      assertEquals(300, reader.readInt32());
935
936      reader.nextField();
937      assertEquals(4, reader.getFieldNumber());
938      assertEquals(400, reader.readInt32());
939
940      reader.nextField();
941      assertEquals(5, reader.getFieldNumber());
942      assertEquals(500, reader.readInt32());
943
944      assertEquals(false, reader.nextField());
945    };
946
947    reader.registerReadCallback('readCallback', readCallback);
948
949    // Read the container message.
950    reader.nextField();
951    assertEquals(1, reader.getFieldNumber());
952    assertEquals(100, reader.readInt32());
953
954    reader.nextField();
955    assertEquals(2, reader.getFieldNumber());
956    reader.readMessage(dummyMessage, function() {
957      // Decode the embedded message using the registered callback.
958      reader.runReadCallback('readCallback');
959    });
960
961    reader.nextField();
962    assertEquals(7, reader.getFieldNumber());
963    assertEquals(700, reader.readInt32());
964
965    assertEquals(false, reader.nextField());
966  });
967});
968