// 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.
writer.writeInt32(5, sentinel);
var dummyMessage = /** @type {!jspb.BinaryMessage} */({});
writer.writeGroup(5, dummyMessage, function() {
writer.writeInt64(42, 42);
writer.writeGroup(6, dummyMessage, function() {
writer.writeInt64(84, 42);
});
});
// 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());
});
});