From e841bac4fcf47f809e089a70d5f84ac37b3883df Mon Sep 17 00:00:00 2001 From: Feng Xiao Date: Fri, 11 Dec 2015 17:09:20 -0800 Subject: Down-integrate from internal code base. --- js/binary/reader_test.js | 889 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 889 insertions(+) create mode 100644 js/binary/reader_test.js (limited to 'js/binary/reader_test.js') diff --git a/js/binary/reader_test.js b/js/binary/reader_test.js new file mode 100644 index 00000000..a6482610 --- /dev/null +++ b/js/binary/reader_test.js @@ -0,0 +1,889 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. All rights reserved. +// https://developers.google.com/protocol-buffers/ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +/** + * @fileoverview Test cases for jspb's binary protocol buffer reader. + * + * There are two particular magic numbers that need to be pointed out - + * 2^64-1025 is the largest number representable as both a double and an + * unsigned 64-bit integer, and 2^63-513 is the largest number representable as + * both a double and a signed 64-bit integer. + * + * Test suite is written using Jasmine -- see http://jasmine.github.io/ + * + * @author aappleby@google.com (Austin Appleby) + */ + +goog.require('goog.testing.asserts'); +goog.require('jspb.BinaryConstants'); +goog.require('jspb.BinaryDecoder'); +goog.require('jspb.BinaryReader'); +goog.require('jspb.BinaryWriter'); + + + +describe('binaryReaderTest', function() { + /** + * Tests the reader instance cache. + * @suppress {visibility} + */ + it('testInstanceCaches', function() { + var writer = new jspb.BinaryWriter(); + var dummyMessage = /** @type {!jspb.BinaryMessage} */({}); + writer.writeMessage(1, dummyMessage, goog.nullFunction); + writer.writeMessage(2, dummyMessage, goog.nullFunction); + + var buffer = writer.getResultBuffer(); + + // Empty the instance caches. + jspb.BinaryReader.instanceCache_ = []; + + // Allocating and then freeing three decoders should leave us with three in + // the cache. + + var decoder1 = jspb.BinaryDecoder.alloc(); + var decoder2 = jspb.BinaryDecoder.alloc(); + var decoder3 = jspb.BinaryDecoder.alloc(); + decoder1.free(); + decoder2.free(); + decoder3.free(); + + assertEquals(3, jspb.BinaryDecoder.instanceCache_.length); + assertEquals(0, jspb.BinaryReader.instanceCache_.length); + + // Allocating and then freeing a reader should remove one decoder from its + // cache, but it should stay stuck to the reader afterwards since we can't + // have a reader without a decoder. + jspb.BinaryReader.alloc().free(); + + assertEquals(2, jspb.BinaryDecoder.instanceCache_.length); + assertEquals(1, jspb.BinaryReader.instanceCache_.length); + + // Allocating a reader should remove a reader from the cache. + var reader = jspb.BinaryReader.alloc(buffer); + + assertEquals(2, jspb.BinaryDecoder.instanceCache_.length); + assertEquals(0, jspb.BinaryReader.instanceCache_.length); + + // Processing the message reuses the current reader. + reader.nextField(); + assertEquals(1, reader.getFieldNumber()); + reader.readMessage(dummyMessage, function() { + assertEquals(0, jspb.BinaryReader.instanceCache_.length); + }); + + reader.nextField(); + assertEquals(2, reader.getFieldNumber()); + reader.readMessage(dummyMessage, function() { + assertEquals(0, jspb.BinaryReader.instanceCache_.length); + }); + + assertEquals(false, reader.nextField()); + + assertEquals(2, jspb.BinaryDecoder.instanceCache_.length); + assertEquals(0, jspb.BinaryReader.instanceCache_.length); + + // Freeing the reader should put it back into the cache. + reader.free(); + + assertEquals(2, jspb.BinaryDecoder.instanceCache_.length); + assertEquals(1, jspb.BinaryReader.instanceCache_.length); + }); + + + /** + * @param {number} x + * @return {number} + */ + function truncate(x) { + var temp = new Float32Array(1); + temp[0] = x; + return temp[0]; + } + + + /** + * Verifies that misuse of the reader class triggers assertions. + * @suppress {checkTypes|visibility} + */ + it('testReadErrors', function() { + // Calling readMessage on a non-delimited field should trigger an + // assertion. + var reader = jspb.BinaryReader.alloc([8, 1]); + var dummyMessage = /** @type {!jspb.BinaryMessage} */({}); + reader.nextField(); + assertThrows(function() { + reader.readMessage(dummyMessage, goog.nullFunction); + }); + + // Reading past the end of the stream should trigger an assertion. + reader = jspb.BinaryReader.alloc([9, 1]); + reader.nextField(); + assertThrows(function() {reader.readFixed64()}); + + // Reading past the end of a submessage should trigger an assertion. + reader = jspb.BinaryReader.alloc([10, 4, 13, 1, 1, 1]); + reader.nextField(); + reader.readMessage(dummyMessage, function() { + reader.nextField(); + assertThrows(function() {reader.readFixed32()}); + }); + + // Skipping an invalid field should trigger an assertion. + reader = jspb.BinaryReader.alloc([12, 1]); + reader.nextWireType_ = 1000; + assertThrows(function() {reader.skipField()}); + + // Reading fields with the wrong wire type should assert. + reader = jspb.BinaryReader.alloc([9, 0, 0, 0, 0, 0, 0, 0, 0]); + reader.nextField(); + assertThrows(function() {reader.readInt32()}); + assertThrows(function() {reader.readInt32String()}); + assertThrows(function() {reader.readInt64()}); + assertThrows(function() {reader.readInt64String()}); + assertThrows(function() {reader.readUint32()}); + assertThrows(function() {reader.readUint32String()}); + assertThrows(function() {reader.readUint64()}); + assertThrows(function() {reader.readUint64String()}); + assertThrows(function() {reader.readSint32()}); + assertThrows(function() {reader.readBool()}); + assertThrows(function() {reader.readEnum()}); + + reader = jspb.BinaryReader.alloc([8, 1]); + reader.nextField(); + assertThrows(function() {reader.readFixed32()}); + assertThrows(function() {reader.readFixed64()}); + assertThrows(function() {reader.readSfixed32()}); + assertThrows(function() {reader.readSfixed64()}); + assertThrows(function() {reader.readFloat()}); + assertThrows(function() {reader.readDouble()}); + + assertThrows(function() {reader.readString()}); + assertThrows(function() {reader.readBytes()}); + }); + + + /** + * Tests encoding and decoding of unsigned field types. + * @param {Function} readField + * @param {Function} writeField + * @param {number} epsilon + * @param {number} upperLimit + * @param {Function} filter + * @private + * @suppress {missingProperties} + */ + function doTestUnsignedField_(readField, + writeField, epsilon, upperLimit, filter) { + assertNotNull(readField); + assertNotNull(writeField); + + var writer = new jspb.BinaryWriter(); + + // Encode zero and limits. + writeField.call(writer, 1, filter(0)); + writeField.call(writer, 2, filter(epsilon)); + writeField.call(writer, 3, filter(upperLimit)); + + // Encode positive values. + for (var cursor = epsilon; cursor < upperLimit; cursor *= 1.1) { + writeField.call(writer, 4, filter(cursor)); + } + + var reader = jspb.BinaryReader.alloc(writer.getResultBuffer()); + + // Check zero and limits. + reader.nextField(); + assertEquals(1, reader.getFieldNumber()); + assertEquals(filter(0), readField.call(reader)); + + reader.nextField(); + assertEquals(2, reader.getFieldNumber()); + assertEquals(filter(epsilon), readField.call(reader)); + + reader.nextField(); + assertEquals(3, reader.getFieldNumber()); + assertEquals(filter(upperLimit), readField.call(reader)); + + // Check positive values. + for (var cursor = epsilon; cursor < upperLimit; cursor *= 1.1) { + reader.nextField(); + if (4 != reader.getFieldNumber()) throw 'fail!'; + if (filter(cursor) != readField.call(reader)) throw 'fail!'; + } + }; + + + /** + * Tests encoding and decoding of signed field types. + * @param {Function} readField + * @param {Function} writeField + * @param {number} epsilon + * @param {number} lowerLimit + * @param {number} upperLimit + * @param {Function} filter + * @private + * @suppress {missingProperties} + */ + function doTestSignedField_(readField, + writeField, epsilon, lowerLimit, upperLimit, filter) { + var writer = new jspb.BinaryWriter(); + + // Encode zero and limits. + writeField.call(writer, 1, filter(lowerLimit)); + writeField.call(writer, 2, filter(-epsilon)); + writeField.call(writer, 3, filter(0)); + writeField.call(writer, 4, filter(epsilon)); + writeField.call(writer, 5, filter(upperLimit)); + + var inputValues = []; + + // Encode negative values. + for (var cursor = lowerLimit; cursor < -epsilon; cursor /= 1.1) { + var val = filter(cursor); + writeField.call(writer, 6, val); + inputValues.push({ + fieldNumber: 6, + value: val + }); + } + + // Encode positive values. + for (var cursor = epsilon; cursor < upperLimit; cursor *= 1.1) { + var val = filter(cursor); + writeField.call(writer, 7, val); + inputValues.push({ + fieldNumber: 7, + value: val + }); + } + + var reader = jspb.BinaryReader.alloc(writer.getResultBuffer()); + + // Check zero and limits. + reader.nextField(); + assertEquals(1, reader.getFieldNumber()); + assertEquals(filter(lowerLimit), readField.call(reader)); + + reader.nextField(); + assertEquals(2, reader.getFieldNumber()); + assertEquals(filter(-epsilon), readField.call(reader)); + + reader.nextField(); + assertEquals(3, reader.getFieldNumber()); + assertEquals(filter(0), readField.call(reader)); + + reader.nextField(); + assertEquals(4, reader.getFieldNumber()); + assertEquals(filter(epsilon), readField.call(reader)); + + reader.nextField(); + assertEquals(5, reader.getFieldNumber()); + assertEquals(filter(upperLimit), readField.call(reader)); + + for (var i = 0; i < inputValues.length; i++) { + var expected = inputValues[i]; + reader.nextField(); + assertEquals(expected.fieldNumber, reader.getFieldNumber()); + assertEquals(expected.value, readField.call(reader)); + } + }; + + + /** + * Tests fields that use varint encoding. + */ + it('testVarintFields', function() { + assertNotNull(jspb.BinaryReader.prototype.readUint32); + assertNotNull(jspb.BinaryReader.prototype.writeUint32); + assertNotNull(jspb.BinaryReader.prototype.readUint64); + assertNotNull(jspb.BinaryReader.prototype.writeUint64); + assertNotNull(jspb.BinaryReader.prototype.readBool); + assertNotNull(jspb.BinaryReader.prototype.writeBool); + doTestUnsignedField_( + jspb.BinaryReader.prototype.readUint32, + jspb.BinaryWriter.prototype.writeUint32, + 1, Math.pow(2, 32) - 1, Math.round); + + doTestUnsignedField_( + jspb.BinaryReader.prototype.readUint64, + jspb.BinaryWriter.prototype.writeUint64, + 1, Math.pow(2, 64) - 1025, Math.round); + + doTestSignedField_( + jspb.BinaryReader.prototype.readInt32, + jspb.BinaryWriter.prototype.writeInt32, + 1, -Math.pow(2, 31), Math.pow(2, 31) - 1, Math.round); + + doTestSignedField_( + jspb.BinaryReader.prototype.readInt64, + jspb.BinaryWriter.prototype.writeInt64, + 1, -Math.pow(2, 63), Math.pow(2, 63) - 513, Math.round); + + doTestSignedField_( + jspb.BinaryReader.prototype.readEnum, + jspb.BinaryWriter.prototype.writeEnum, + 1, -Math.pow(2, 31), Math.pow(2, 31) - 1, Math.round); + + doTestUnsignedField_( + jspb.BinaryReader.prototype.readBool, + jspb.BinaryWriter.prototype.writeBool, + 1, 1, function(x) { return !!x; }); + }); + + + /** + * Tests 64-bit fields that are handled as strings. + */ + it('testStringInt64Fields', function() { + var writer = new jspb.BinaryWriter(); + + var testSignedData = [ + '2730538252207801776', + '-2688470994844604560', + '3398529779486536359', + '3568577411627971000', + '272477188847484900', + '-6649058714086158188', + '-7695254765712060806', + '-4525541438037104029', + '-4993706538836508568', + '4990160321893729138' + ]; + var testUnsignedData = [ + '7822732630241694882', + '6753602971916687352', + '2399935075244442116', + '8724292567325338867', + '16948784802625696584', + '4136275908516066934', + '3575388346793700364', + '5167142028379259461', + '1557573948689737699', + '17100725280812548567' + ]; + + for (var i = 0; i < testSignedData.length; i++) { + writer.writeInt64String(2 * i + 1, testSignedData[i]); + writer.writeUint64String(2 * i + 2, testUnsignedData[i]); + } + + var reader = jspb.BinaryReader.alloc(writer.getResultBuffer()); + + for (var i = 0; i < testSignedData.length; i++) { + reader.nextField(); + assertEquals(2 * i + 1, reader.getFieldNumber()); + assertEquals(testSignedData[i], reader.readInt64String()); + reader.nextField(); + assertEquals(2 * i + 2, reader.getFieldNumber()); + assertEquals(testUnsignedData[i], reader.readUint64String()); + } + }); + + + /** + * Tests fields that use zigzag encoding. + */ + it('testZigzagFields', function() { + doTestSignedField_( + jspb.BinaryReader.prototype.readSint32, + jspb.BinaryWriter.prototype.writeSint32, + 1, -Math.pow(2, 31), Math.pow(2, 31) - 1, Math.round); + + doTestSignedField_( + jspb.BinaryReader.prototype.readSint64, + jspb.BinaryWriter.prototype.writeSint64, + 1, -Math.pow(2, 63), Math.pow(2, 63) - 513, Math.round); + }); + + + /** + * Tests fields that use fixed-length encoding. + */ + it('testFixedFields', function() { + doTestUnsignedField_( + jspb.BinaryReader.prototype.readFixed32, + jspb.BinaryWriter.prototype.writeFixed32, + 1, Math.pow(2, 32) - 1, Math.round); + + doTestUnsignedField_( + jspb.BinaryReader.prototype.readFixed64, + jspb.BinaryWriter.prototype.writeFixed64, + 1, Math.pow(2, 64) - 1025, Math.round); + + doTestSignedField_( + jspb.BinaryReader.prototype.readSfixed32, + jspb.BinaryWriter.prototype.writeSfixed32, + 1, -Math.pow(2, 31), Math.pow(2, 31) - 1, Math.round); + + doTestSignedField_( + jspb.BinaryReader.prototype.readSfixed64, + jspb.BinaryWriter.prototype.writeSfixed64, + 1, -Math.pow(2, 63), Math.pow(2, 63) - 513, Math.round); + }); + + + /** + * Tests floating point fields. + */ + it('testFloatFields', function() { + doTestSignedField_( + jspb.BinaryReader.prototype.readFloat, + jspb.BinaryWriter.prototype.writeFloat, + jspb.BinaryConstants.FLOAT32_MIN, + -jspb.BinaryConstants.FLOAT32_MAX, + jspb.BinaryConstants.FLOAT32_MAX, + truncate); + + doTestSignedField_( + jspb.BinaryReader.prototype.readDouble, + jspb.BinaryWriter.prototype.writeDouble, + jspb.BinaryConstants.FLOAT64_EPS * 10, + -jspb.BinaryConstants.FLOAT64_MIN, + jspb.BinaryConstants.FLOAT64_MIN, + function(x) { return x; }); + }); + + + /** + * Tests length-delimited string fields. + */ + it('testStringFields', function() { + var s1 = 'The quick brown fox jumps over the lazy dog.'; + var s2 = '人人生而自由,在尊嚴和權利上一律平等。'; + + var writer = new jspb.BinaryWriter(); + + writer.writeString(1, s1); + writer.writeString(2, s2); + + var reader = jspb.BinaryReader.alloc(writer.getResultBuffer()); + + reader.nextField(); + assertEquals(1, reader.getFieldNumber()); + assertEquals(s1, reader.readString()); + + reader.nextField(); + assertEquals(2, reader.getFieldNumber()); + assertEquals(s2, reader.readString()); + }); + + + /** + * Tests length-delimited byte fields. + */ + it('testByteFields', function() { + var message = []; + var lowerLimit = 1; + var upperLimit = 256; + var scale = 1.1; + + var writer = new jspb.BinaryWriter(); + + for (var cursor = lowerLimit; cursor < upperLimit; cursor *= 1.1) { + var len = Math.round(cursor); + var bytes = []; + for (var i = 0; i < len; i++) bytes.push(i % 256); + + writer.writeBytes(len, bytes); + } + + var reader = jspb.BinaryReader.alloc(writer.getResultBuffer()); + + for (var cursor = lowerLimit; reader.nextField(); cursor *= 1.1) { + var len = Math.round(cursor); + if (len != reader.getFieldNumber()) throw 'fail!'; + + var bytes = reader.readBytes(); + if (len != bytes.length) throw 'fail!'; + for (var i = 0; i < bytes.length; i++) { + if (i % 256 != bytes[i]) throw 'fail!'; + } + } + }); + + + /** + * Tests nested messages. + */ + it('testNesting', function() { + var writer = new jspb.BinaryWriter(); + var dummyMessage = /** @type {!jspb.BinaryMessage} */({}); + + writer.writeInt32(1, 100); + + // Add one message with 3 int fields. + writer.writeMessage(2, dummyMessage, function() { + writer.writeInt32(3, 300); + writer.writeInt32(4, 400); + writer.writeInt32(5, 500); + }); + + // Add one empty message. + writer.writeMessage(6, dummyMessage, goog.nullFunction); + + writer.writeInt32(7, 700); + + var reader = jspb.BinaryReader.alloc(writer.getResultBuffer()); + + // Validate outermost message. + + reader.nextField(); + assertEquals(1, reader.getFieldNumber()); + assertEquals(100, reader.readInt32()); + + reader.nextField(); + assertEquals(2, reader.getFieldNumber()); + reader.readMessage(dummyMessage, function() { + // Validate embedded message 1. + reader.nextField(); + assertEquals(3, reader.getFieldNumber()); + assertEquals(300, reader.readInt32()); + + reader.nextField(); + assertEquals(4, reader.getFieldNumber()); + assertEquals(400, reader.readInt32()); + + reader.nextField(); + assertEquals(5, reader.getFieldNumber()); + assertEquals(500, reader.readInt32()); + + assertEquals(false, reader.nextField()); + }); + + reader.nextField(); + assertEquals(6, reader.getFieldNumber()); + reader.readMessage(dummyMessage, function() { + // Validate embedded message 2. + + assertEquals(false, reader.nextField()); + }); + + reader.nextField(); + assertEquals(7, reader.getFieldNumber()); + assertEquals(700, reader.readInt32()); + + assertEquals(false, reader.nextField()); + }); + + /** + * Tests skipping fields of each type by interleaving them with sentinel + * values and skipping everything that's not a sentinel. + */ + it('testSkipField', function() { + var writer = new jspb.BinaryWriter(); + + var sentinel = 123456789; + + // Write varint fields of different sizes. + writer.writeInt32(1, sentinel); + writer.writeInt32(1, 1); + writer.writeInt32(1, 1000); + writer.writeInt32(1, 1000000); + writer.writeInt32(1, 1000000000); + + // Write fixed 64-bit encoded fields. + writer.writeInt32(2, sentinel); + writer.writeDouble(2, 1); + writer.writeFixed64(2, 1); + writer.writeSfixed64(2, 1); + + // Write fixed 32-bit encoded fields. + writer.writeInt32(3, sentinel); + writer.writeFloat(3, 1); + writer.writeFixed32(3, 1); + writer.writeSfixed32(3, 1); + + // Write delimited fields. + writer.writeInt32(4, sentinel); + writer.writeBytes(4, [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]); + writer.writeString(4, 'The quick brown fox jumps over the lazy dog'); + + // Write a group with a nested group inside. We use the internal + // .rawWriteVarint() to ensure the tested wire data is what we want, + // independently of any serialization logic. + writer.writeInt32(5, sentinel); + // Start group, field 5. + writer.rawWriteVarint( + (5 << 3) + jspb.BinaryConstants.WireType.START_GROUP); + // Varint, field 42. + writer.rawWriteVarint( + (42 << 3) + jspb.BinaryConstants.WireType.VARINT); + // Varint data. + writer.rawWriteVarint(42); + // Start group, field 6. + writer.rawWriteVarint( + (6 << 3) + jspb.BinaryConstants.WireType.START_GROUP); + // Varint, field 84. + writer.rawWriteVarint( + (84 << 3) + jspb.BinaryConstants.WireType.VARINT); + writer.rawWriteVarint(42); + // End group, field 6. + writer.rawWriteVarint( + (6 << 3) + jspb.BinaryConstants.WireType.END_GROUP); + // End group, field 5. + writer.rawWriteVarint( + (5 << 3) + jspb.BinaryConstants.WireType.END_GROUP); + + // Write final sentinel. + writer.writeInt32(6, sentinel); + + var reader = jspb.BinaryReader.alloc(writer.getResultBuffer()); + + function skip(field, count) { + for (var i = 0; i < count; i++) { + reader.nextField(); + if (field != reader.getFieldNumber()) throw 'fail!'; + reader.skipField(); + } + } + + reader.nextField(); + assertEquals(1, reader.getFieldNumber()); + assertEquals(sentinel, reader.readInt32()); + skip(1, 4); + + reader.nextField(); + assertEquals(2, reader.getFieldNumber()); + assertEquals(sentinel, reader.readInt32()); + skip(2, 3); + + reader.nextField(); + assertEquals(3, reader.getFieldNumber()); + assertEquals(sentinel, reader.readInt32()); + skip(3, 3); + + reader.nextField(); + assertEquals(4, reader.getFieldNumber()); + assertEquals(sentinel, reader.readInt32()); + skip(4, 2); + + reader.nextField(); + assertEquals(5, reader.getFieldNumber()); + assertEquals(sentinel, reader.readInt32()); + skip(5, 1); + + reader.nextField(); + assertEquals(6, reader.getFieldNumber()); + assertEquals(sentinel, reader.readInt32()); + }); + + + /** + * Tests packed fields. + */ + it('testPackedFields', function() { + var writer = new jspb.BinaryWriter(); + + var sentinel = 123456789; + + var unsignedData = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; + var signedData = [-1, 2, -3, 4, -5, 6, -7, 8, -9, 10]; + var floatData = [1.1, 2.2, 3.3, 4.4, 5.5, 6.6, 7.7, 8.8, 9.9, 10.10]; + var doubleData = [1.1, 2.2, 3.3, 4.4, 5.5, 6.6, 7.7, 8.8, 9.9, 10.10]; + var boolData = [true, false, true, true, false, false, true, false]; + + for (var i = 0; i < floatData.length; i++) { + floatData[i] = truncate(floatData[i]); + } + + writer.writeInt32(1, sentinel); + + writer.writePackedInt32(2, signedData); + writer.writePackedInt64(2, signedData); + writer.writePackedUint32(2, unsignedData); + writer.writePackedUint64(2, unsignedData); + writer.writePackedSint32(2, signedData); + writer.writePackedSint64(2, signedData); + writer.writePackedFixed32(2, unsignedData); + writer.writePackedFixed64(2, unsignedData); + writer.writePackedSfixed32(2, signedData); + writer.writePackedSfixed64(2, signedData); + writer.writePackedFloat(2, floatData); + writer.writePackedDouble(2, doubleData); + writer.writePackedBool(2, boolData); + writer.writePackedEnum(2, unsignedData); + + writer.writeInt32(3, sentinel); + + var reader = jspb.BinaryReader.alloc(writer.getResultBuffer()); + + reader.nextField(); + assertEquals(sentinel, reader.readInt32()); + + reader.nextField(); + assertElementsEquals(reader.readPackedInt32(), signedData); + + reader.nextField(); + assertElementsEquals(reader.readPackedInt64(), signedData); + + reader.nextField(); + assertElementsEquals(reader.readPackedUint32(), unsignedData); + + reader.nextField(); + assertElementsEquals(reader.readPackedUint64(), unsignedData); + + reader.nextField(); + assertElementsEquals(reader.readPackedSint32(), signedData); + + reader.nextField(); + assertElementsEquals(reader.readPackedSint64(), signedData); + + reader.nextField(); + assertElementsEquals(reader.readPackedFixed32(), unsignedData); + + reader.nextField(); + assertElementsEquals(reader.readPackedFixed64(), unsignedData); + + reader.nextField(); + assertElementsEquals(reader.readPackedSfixed32(), signedData); + + reader.nextField(); + assertElementsEquals(reader.readPackedSfixed64(), signedData); + + reader.nextField(); + assertElementsEquals(reader.readPackedFloat(), floatData); + + reader.nextField(); + assertElementsEquals(reader.readPackedDouble(), doubleData); + + reader.nextField(); + assertElementsEquals(reader.readPackedBool(), boolData); + + reader.nextField(); + assertElementsEquals(reader.readPackedEnum(), unsignedData); + + reader.nextField(); + assertEquals(sentinel, reader.readInt32()); + }); + + + /** + * Byte blobs inside nested messages should always have their byte offset set + * relative to the start of the outermost blob, not the start of their parent + * blob. + */ + it('testNestedBlobs', function() { + // Create a proto consisting of two nested messages, with the inner one + // containing a blob of bytes. + + var fieldTag = (1 << 3) | jspb.BinaryConstants.WireType.DELIMITED; + var blob = [1, 2, 3, 4, 5]; + var writer = new jspb.BinaryWriter(); + var dummyMessage = /** @type {!jspb.BinaryMessage} */({}); + + writer.writeMessage(1, dummyMessage, function() { + writer.writeMessage(1, dummyMessage, function() { + writer.writeBytes(1, blob); + }); + }); + + // Peel off the outer two message layers. Each layer should have two bytes + // of overhead, one for the field tag and one for the length of the inner + // blob. + + var decoder1 = new jspb.BinaryDecoder(writer.getResultBuffer()); + assertEquals(fieldTag, decoder1.readUnsignedVarint32()); + assertEquals(blob.length + 4, decoder1.readUnsignedVarint32()); + + var decoder2 = new jspb.BinaryDecoder(decoder1.readBytes(blob.length + 4)); + assertEquals(fieldTag, decoder2.readUnsignedVarint32()); + assertEquals(blob.length + 2, decoder2.readUnsignedVarint32()); + + assertEquals(fieldTag, decoder2.readUnsignedVarint32()); + assertEquals(blob.length, decoder2.readUnsignedVarint32()); + var bytes = decoder2.readBytes(blob.length); + + assertElementsEquals(bytes, blob); + }); + + + /** + * Tests read callbacks. + */ + it('testReadCallbacks', function() { + var writer = new jspb.BinaryWriter(); + var dummyMessage = /** @type {!jspb.BinaryMessage} */({}); + + // Add an int, a submessage, and another int. + writer.writeInt32(1, 100); + + writer.writeMessage(2, dummyMessage, function() { + writer.writeInt32(3, 300); + writer.writeInt32(4, 400); + writer.writeInt32(5, 500); + }); + + writer.writeInt32(7, 700); + + // Create the reader and register a custom read callback. + var reader = jspb.BinaryReader.alloc(writer.getResultBuffer()); + + /** + * @param {!jspb.BinaryReader} reader + * @return {*} + */ + function readCallback(reader) { + reader.nextField(); + assertEquals(3, reader.getFieldNumber()); + assertEquals(300, reader.readInt32()); + + reader.nextField(); + assertEquals(4, reader.getFieldNumber()); + assertEquals(400, reader.readInt32()); + + reader.nextField(); + assertEquals(5, reader.getFieldNumber()); + assertEquals(500, reader.readInt32()); + + assertEquals(false, reader.nextField()); + }; + + reader.registerReadCallback('readCallback', readCallback); + + // Read the container message. + reader.nextField(); + assertEquals(1, reader.getFieldNumber()); + assertEquals(100, reader.readInt32()); + + reader.nextField(); + assertEquals(2, reader.getFieldNumber()); + reader.readMessage(dummyMessage, function() { + // Decode the embedded message using the registered callback. + reader.runReadCallback('readCallback'); + }); + + reader.nextField(); + assertEquals(7, reader.getFieldNumber()); + assertEquals(700, reader.readInt32()); + + assertEquals(false, reader.nextField()); + }); +}); -- cgit v1.2.3