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