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