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