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 decoder. 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.BinaryEncoder'); 48 49 50/** 51 * Tests encoding and decoding of unsigned types. 52 * @param {Function} readValue 53 * @param {Function} writeValue 54 * @param {number} epsilon 55 * @param {number} upperLimit 56 * @param {Function} filter 57 * @suppress {missingProperties|visibility} 58 */ 59function doTestUnsignedValue(readValue, 60 writeValue, epsilon, upperLimit, filter) { 61 var encoder = new jspb.BinaryEncoder(); 62 63 // Encode zero and limits. 64 writeValue.call(encoder, filter(0)); 65 writeValue.call(encoder, filter(epsilon)); 66 writeValue.call(encoder, filter(upperLimit)); 67 68 // Encode positive values. 69 for (var cursor = epsilon; cursor < upperLimit; cursor *= 1.1) { 70 writeValue.call(encoder, filter(cursor)); 71 } 72 73 var decoder = jspb.BinaryDecoder.alloc(encoder.end()); 74 75 // Check zero and limits. 76 assertEquals(filter(0), readValue.call(decoder)); 77 assertEquals(filter(epsilon), readValue.call(decoder)); 78 assertEquals(filter(upperLimit), readValue.call(decoder)); 79 80 // Check positive values. 81 for (var cursor = epsilon; cursor < upperLimit; cursor *= 1.1) { 82 if (filter(cursor) != readValue.call(decoder)) throw 'fail!'; 83 } 84 85 // Encoding values outside the valid range should assert. 86 assertThrows(function() {writeValue.call(encoder, -1);}); 87 assertThrows(function() {writeValue.call(encoder, upperLimit * 1.1);}); 88} 89 90 91/** 92 * Tests encoding and decoding of signed types. 93 * @param {Function} readValue 94 * @param {Function} writeValue 95 * @param {number} epsilon 96 * @param {number} lowerLimit 97 * @param {number} upperLimit 98 * @param {Function} filter 99 * @suppress {missingProperties} 100 */ 101function doTestSignedValue(readValue, 102 writeValue, epsilon, lowerLimit, upperLimit, filter) { 103 var encoder = new jspb.BinaryEncoder(); 104 105 // Encode zero and limits. 106 writeValue.call(encoder, filter(lowerLimit)); 107 writeValue.call(encoder, filter(-epsilon)); 108 writeValue.call(encoder, filter(0)); 109 writeValue.call(encoder, filter(epsilon)); 110 writeValue.call(encoder, filter(upperLimit)); 111 112 var inputValues = []; 113 114 // Encode negative values. 115 for (var cursor = lowerLimit; cursor < -epsilon; cursor /= 1.1) { 116 var val = filter(cursor); 117 writeValue.call(encoder, val); 118 inputValues.push(val); 119 } 120 121 // Encode positive values. 122 for (var cursor = epsilon; cursor < upperLimit; cursor *= 1.1) { 123 var val = filter(cursor); 124 writeValue.call(encoder, val); 125 inputValues.push(val); 126 } 127 128 var decoder = jspb.BinaryDecoder.alloc(encoder.end()); 129 130 // Check zero and limits. 131 assertEquals(filter(lowerLimit), readValue.call(decoder)); 132 assertEquals(filter(-epsilon), readValue.call(decoder)); 133 assertEquals(filter(0), readValue.call(decoder)); 134 assertEquals(filter(epsilon), readValue.call(decoder)); 135 assertEquals(filter(upperLimit), readValue.call(decoder)); 136 137 // Verify decoded values. 138 for (var i = 0; i < inputValues.length; i++) { 139 assertEquals(inputValues[i], readValue.call(decoder)); 140 } 141 142 // Encoding values outside the valid range should assert. 143 assertThrows(function() {writeValue.call(encoder, lowerLimit * 1.1);}); 144 assertThrows(function() {writeValue.call(encoder, upperLimit * 1.1);}); 145} 146 147describe('binaryDecoderTest', function() { 148 /** 149 * Tests the decoder instance cache. 150 * @suppress {visibility} 151 */ 152 it('testInstanceCache', function() { 153 // Empty the instance caches. 154 jspb.BinaryDecoder.instanceCache_ = []; 155 156 // Allocating and then freeing a decoder should put it in the instance 157 // cache. 158 jspb.BinaryDecoder.alloc().free(); 159 160 assertEquals(1, jspb.BinaryDecoder.instanceCache_.length); 161 162 // Allocating and then freeing three decoders should leave us with three in 163 // the cache. 164 165 var decoder1 = jspb.BinaryDecoder.alloc(); 166 var decoder2 = jspb.BinaryDecoder.alloc(); 167 var decoder3 = jspb.BinaryDecoder.alloc(); 168 decoder1.free(); 169 decoder2.free(); 170 decoder3.free(); 171 172 assertEquals(3, jspb.BinaryDecoder.instanceCache_.length); 173 }); 174 175 176 /** 177 * Tests reading 64-bit integers as hash strings. 178 */ 179 it('testHashStrings', function() { 180 var encoder = new jspb.BinaryEncoder(); 181 182 var hashA = String.fromCharCode(0x00, 0x00, 0x00, 0x00, 183 0x00, 0x00, 0x00, 0x00); 184 var hashB = String.fromCharCode(0x12, 0x34, 0x00, 0x00, 185 0x00, 0x00, 0x00, 0x00); 186 var hashC = String.fromCharCode(0x12, 0x34, 0x56, 0x78, 187 0x87, 0x65, 0x43, 0x21); 188 var hashD = String.fromCharCode(0xFF, 0xFF, 0xFF, 0xFF, 189 0xFF, 0xFF, 0xFF, 0xFF); 190 191 encoder.writeVarintHash64(hashA); 192 encoder.writeVarintHash64(hashB); 193 encoder.writeVarintHash64(hashC); 194 encoder.writeVarintHash64(hashD); 195 196 encoder.writeFixedHash64(hashA); 197 encoder.writeFixedHash64(hashB); 198 encoder.writeFixedHash64(hashC); 199 encoder.writeFixedHash64(hashD); 200 201 var decoder = jspb.BinaryDecoder.alloc(encoder.end()); 202 203 assertEquals(hashA, decoder.readVarintHash64()); 204 assertEquals(hashB, decoder.readVarintHash64()); 205 assertEquals(hashC, decoder.readVarintHash64()); 206 assertEquals(hashD, decoder.readVarintHash64()); 207 208 assertEquals(hashA, decoder.readFixedHash64()); 209 assertEquals(hashB, decoder.readFixedHash64()); 210 assertEquals(hashC, decoder.readFixedHash64()); 211 assertEquals(hashD, decoder.readFixedHash64()); 212 }); 213 214 215 /** 216 * Verifies that misuse of the decoder class triggers assertions. 217 * @suppress {checkTypes|visibility} 218 */ 219 it('testDecodeErrors', function() { 220 // Reading a value past the end of the stream should trigger an assertion. 221 var decoder = jspb.BinaryDecoder.alloc([0, 1, 2]); 222 assertThrows(function() {decoder.readUint64()}); 223 224 // Overlong varints should trigger assertions. 225 decoder.setBlock([255, 255, 255, 255, 255, 255, 226 255, 255, 255, 255, 255, 0]); 227 assertThrows(function() {decoder.readUnsignedVarint64()}); 228 decoder.reset(); 229 assertThrows(function() {decoder.readSignedVarint64()}); 230 decoder.reset(); 231 assertThrows(function() {decoder.readZigzagVarint64()}); 232 233 // Positive 32-bit varints encoded with 1 bits in positions 33 through 35 234 // should trigger assertions. 235 decoder.setBlock([255, 255, 255, 255, 0x1F]); 236 assertThrows(function() {decoder.readUnsignedVarint32()}); 237 238 decoder.setBlock([255, 255, 255, 255, 0x2F]); 239 assertThrows(function() {decoder.readUnsignedVarint32()}); 240 241 decoder.setBlock([255, 255, 255, 255, 0x4F]); 242 assertThrows(function() {decoder.readUnsignedVarint32()}); 243 244 // Negative 32-bit varints encoded with non-1 bits in the high dword should 245 // trigger assertions. 246 decoder.setBlock([255, 255, 255, 255, 255, 255, 0, 255, 255, 1]); 247 assertThrows(function() {decoder.readUnsignedVarint32()}); 248 249 decoder.setBlock([255, 255, 255, 255, 255, 255, 255, 255, 255, 0]); 250 assertThrows(function() {decoder.readUnsignedVarint32()}); 251 }); 252 253 254 /** 255 * Tests encoding and decoding of unsigned integers. 256 */ 257 it('testUnsignedIntegers', function() { 258 doTestUnsignedValue( 259 jspb.BinaryDecoder.prototype.readUint8, 260 jspb.BinaryEncoder.prototype.writeUint8, 261 1, 0xFF, Math.round); 262 263 doTestUnsignedValue( 264 jspb.BinaryDecoder.prototype.readUint16, 265 jspb.BinaryEncoder.prototype.writeUint16, 266 1, 0xFFFF, Math.round); 267 268 doTestUnsignedValue( 269 jspb.BinaryDecoder.prototype.readUint32, 270 jspb.BinaryEncoder.prototype.writeUint32, 271 1, 0xFFFFFFFF, Math.round); 272 273 doTestUnsignedValue( 274 jspb.BinaryDecoder.prototype.readUint64, 275 jspb.BinaryEncoder.prototype.writeUint64, 276 1, Math.pow(2, 64) - 1025, Math.round); 277 }); 278 279 280 /** 281 * Tests encoding and decoding of signed integers. 282 */ 283 it('testSignedIntegers', function() { 284 doTestSignedValue( 285 jspb.BinaryDecoder.prototype.readInt8, 286 jspb.BinaryEncoder.prototype.writeInt8, 287 1, -0x80, 0x7F, Math.round); 288 289 doTestSignedValue( 290 jspb.BinaryDecoder.prototype.readInt16, 291 jspb.BinaryEncoder.prototype.writeInt16, 292 1, -0x8000, 0x7FFF, Math.round); 293 294 doTestSignedValue( 295 jspb.BinaryDecoder.prototype.readInt32, 296 jspb.BinaryEncoder.prototype.writeInt32, 297 1, -0x80000000, 0x7FFFFFFF, Math.round); 298 299 doTestSignedValue( 300 jspb.BinaryDecoder.prototype.readInt64, 301 jspb.BinaryEncoder.prototype.writeInt64, 302 1, -Math.pow(2, 63), Math.pow(2, 63) - 513, Math.round); 303 }); 304 305 306 /** 307 * Tests encoding and decoding of floats. 308 */ 309 it('testFloats', function() { 310 /** 311 * @param {number} x 312 * @return {number} 313 */ 314 function truncate(x) { 315 var temp = new Float32Array(1); 316 temp[0] = x; 317 return temp[0]; 318 } 319 doTestSignedValue( 320 jspb.BinaryDecoder.prototype.readFloat, 321 jspb.BinaryEncoder.prototype.writeFloat, 322 jspb.BinaryConstants.FLOAT32_EPS, 323 -jspb.BinaryConstants.FLOAT32_MAX, 324 jspb.BinaryConstants.FLOAT32_MAX, 325 truncate); 326 327 doTestSignedValue( 328 jspb.BinaryDecoder.prototype.readDouble, 329 jspb.BinaryEncoder.prototype.writeDouble, 330 jspb.BinaryConstants.FLOAT64_EPS * 10, 331 -jspb.BinaryConstants.FLOAT64_MAX, 332 jspb.BinaryConstants.FLOAT64_MAX, 333 function(x) { return x; }); 334 }); 335}); 336