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