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, except for doubles 143 // whose range runs all the way to infinity. 144 var pastLowerLimit = lowerLimit * 1.1; 145 var pastUpperLimit = upperLimit * 1.1; 146 if (pastLowerLimit !== -Infinity) { 147 expect(function() { 148 writeValue.call(encoder, lowerLimit * 1.1); 149 }).toThrow(); 150 } 151 if (pastUpperLimit !== Infinity) { 152 expect(function() { 153 writeValue.call(encoder, upperLimit * 1.1); 154 }).toThrow(); 155 } 156} 157 158describe('binaryDecoderTest', function() { 159 /** 160 * Tests the decoder instance cache. 161 */ 162 it('testInstanceCache', /** @suppress {visibility} */ function() { 163 // Empty the instance caches. 164 jspb.BinaryDecoder.instanceCache_ = []; 165 166 // Allocating and then freeing a decoder should put it in the instance 167 // cache. 168 jspb.BinaryDecoder.alloc().free(); 169 170 assertEquals(1, jspb.BinaryDecoder.instanceCache_.length); 171 172 // Allocating and then freeing three decoders should leave us with three in 173 // the cache. 174 175 var decoder1 = jspb.BinaryDecoder.alloc(); 176 var decoder2 = jspb.BinaryDecoder.alloc(); 177 var decoder3 = jspb.BinaryDecoder.alloc(); 178 decoder1.free(); 179 decoder2.free(); 180 decoder3.free(); 181 182 assertEquals(3, jspb.BinaryDecoder.instanceCache_.length); 183 }); 184 185 186 /** 187 * Tests reading 64-bit integers as hash strings. 188 */ 189 it('testHashStrings', function() { 190 var encoder = new jspb.BinaryEncoder(); 191 192 var hashA = String.fromCharCode(0x00, 0x00, 0x00, 0x00, 193 0x00, 0x00, 0x00, 0x00); 194 var hashB = String.fromCharCode(0x12, 0x34, 0x00, 0x00, 195 0x00, 0x00, 0x00, 0x00); 196 var hashC = String.fromCharCode(0x12, 0x34, 0x56, 0x78, 197 0x87, 0x65, 0x43, 0x21); 198 var hashD = String.fromCharCode(0xFF, 0xFF, 0xFF, 0xFF, 199 0xFF, 0xFF, 0xFF, 0xFF); 200 201 encoder.writeVarintHash64(hashA); 202 encoder.writeVarintHash64(hashB); 203 encoder.writeVarintHash64(hashC); 204 encoder.writeVarintHash64(hashD); 205 206 encoder.writeFixedHash64(hashA); 207 encoder.writeFixedHash64(hashB); 208 encoder.writeFixedHash64(hashC); 209 encoder.writeFixedHash64(hashD); 210 211 var decoder = jspb.BinaryDecoder.alloc(encoder.end()); 212 213 assertEquals(hashA, decoder.readVarintHash64()); 214 assertEquals(hashB, decoder.readVarintHash64()); 215 assertEquals(hashC, decoder.readVarintHash64()); 216 assertEquals(hashD, decoder.readVarintHash64()); 217 218 assertEquals(hashA, decoder.readFixedHash64()); 219 assertEquals(hashB, decoder.readFixedHash64()); 220 assertEquals(hashC, decoder.readFixedHash64()); 221 assertEquals(hashD, decoder.readFixedHash64()); 222 }); 223 224 225 /** 226 * Verifies that misuse of the decoder class triggers assertions. 227 * @suppress {checkTypes|visibility} 228 */ 229 it('testDecodeErrors', function() { 230 // Reading a value past the end of the stream should trigger an assertion. 231 var decoder = jspb.BinaryDecoder.alloc([0, 1, 2]); 232 assertThrows(function() {decoder.readUint64()}); 233 234 // Overlong varints should trigger assertions. 235 decoder.setBlock([255, 255, 255, 255, 255, 255, 236 255, 255, 255, 255, 255, 0]); 237 assertThrows(function() {decoder.readUnsignedVarint64()}); 238 decoder.reset(); 239 assertThrows(function() {decoder.readSignedVarint64()}); 240 decoder.reset(); 241 assertThrows(function() {decoder.readZigzagVarint64()}); 242 decoder.reset(); 243 assertThrows(function() {decoder.readUnsignedVarint32()}); 244 }); 245 246 247 /** 248 * Tests encoding and decoding of unsigned integers. 249 */ 250 it('testUnsignedIntegers', function() { 251 doTestUnsignedValue( 252 jspb.BinaryDecoder.prototype.readUint8, 253 jspb.BinaryEncoder.prototype.writeUint8, 254 1, 0xFF, Math.round); 255 256 doTestUnsignedValue( 257 jspb.BinaryDecoder.prototype.readUint16, 258 jspb.BinaryEncoder.prototype.writeUint16, 259 1, 0xFFFF, Math.round); 260 261 doTestUnsignedValue( 262 jspb.BinaryDecoder.prototype.readUint32, 263 jspb.BinaryEncoder.prototype.writeUint32, 264 1, 0xFFFFFFFF, Math.round); 265 266 doTestUnsignedValue( 267 jspb.BinaryDecoder.prototype.readUint64, 268 jspb.BinaryEncoder.prototype.writeUint64, 269 1, Math.pow(2, 64) - 1025, Math.round); 270 }); 271 272 273 /** 274 * Tests encoding and decoding of signed integers. 275 */ 276 it('testSignedIntegers', function() { 277 doTestSignedValue( 278 jspb.BinaryDecoder.prototype.readInt8, 279 jspb.BinaryEncoder.prototype.writeInt8, 280 1, -0x80, 0x7F, Math.round); 281 282 doTestSignedValue( 283 jspb.BinaryDecoder.prototype.readInt16, 284 jspb.BinaryEncoder.prototype.writeInt16, 285 1, -0x8000, 0x7FFF, Math.round); 286 287 doTestSignedValue( 288 jspb.BinaryDecoder.prototype.readInt32, 289 jspb.BinaryEncoder.prototype.writeInt32, 290 1, -0x80000000, 0x7FFFFFFF, Math.round); 291 292 doTestSignedValue( 293 jspb.BinaryDecoder.prototype.readInt64, 294 jspb.BinaryEncoder.prototype.writeInt64, 295 1, -Math.pow(2, 63), Math.pow(2, 63) - 513, Math.round); 296 }); 297 298 299 /** 300 * Tests encoding and decoding of floats. 301 */ 302 it('testFloats', function() { 303 /** 304 * @param {number} x 305 * @return {number} 306 */ 307 function truncate(x) { 308 var temp = new Float32Array(1); 309 temp[0] = x; 310 return temp[0]; 311 } 312 doTestSignedValue( 313 jspb.BinaryDecoder.prototype.readFloat, 314 jspb.BinaryEncoder.prototype.writeFloat, 315 jspb.BinaryConstants.FLOAT32_EPS, 316 -jspb.BinaryConstants.FLOAT32_MAX, 317 jspb.BinaryConstants.FLOAT32_MAX, 318 truncate); 319 320 doTestSignedValue( 321 jspb.BinaryDecoder.prototype.readDouble, 322 jspb.BinaryEncoder.prototype.writeDouble, 323 jspb.BinaryConstants.FLOAT64_EPS * 10, 324 -jspb.BinaryConstants.FLOAT64_MAX, 325 jspb.BinaryConstants.FLOAT64_MAX, 326 function(x) { return x; }); 327 }); 328}); 329