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 * Tests reading and writing large strings 215 */ 216 it('testLargeStrings', function() { 217 var encoder = new jspb.BinaryEncoder(); 218 219 var len = 150000; 220 var long_string = ''; 221 for (var i = 0; i < len; i++) { 222 long_string += 'a'; 223 } 224 225 encoder.writeString(long_string); 226 227 var decoder = jspb.BinaryDecoder.alloc(encoder.end()); 228 229 assertEquals(long_string, decoder.readString(len)); 230 }); 231 232 /** 233 * Test encoding and decoding utf-8. 234 */ 235 it('testUtf8', function() { 236 var encoder = new jspb.BinaryEncoder(); 237 238 var ascii = "ASCII should work in 3, 2, 1..."; 239 var utf8_two_bytes = "©"; 240 var utf8_three_bytes = "❄"; 241 var utf8_four_bytes = ""; 242 243 encoder.writeString(ascii); 244 encoder.writeString(utf8_two_bytes); 245 encoder.writeString(utf8_three_bytes); 246 encoder.writeString(utf8_four_bytes); 247 248 var decoder = jspb.BinaryDecoder.alloc(encoder.end()); 249 250 assertEquals(ascii, decoder.readString(ascii.length)); 251 assertEquals(utf8_two_bytes, decoder.readString(utf8_two_bytes.length)); 252 assertEquals(utf8_three_bytes, decoder.readString(utf8_three_bytes.length)); 253 assertEquals(utf8_four_bytes, decoder.readString(utf8_four_bytes.length)); 254 }); 255 256 /** 257 * Verifies that misuse of the decoder class triggers assertions. 258 */ 259 it('testDecodeErrors', function() { 260 // Reading a value past the end of the stream should trigger an assertion. 261 var decoder = jspb.BinaryDecoder.alloc([0, 1, 2]); 262 assertThrows(function() {decoder.readUint64()}); 263 264 // Overlong varints should trigger assertions. 265 decoder.setBlock([255, 255, 255, 255, 255, 255, 266 255, 255, 255, 255, 255, 0]); 267 assertThrows(function() {decoder.readUnsignedVarint64()}); 268 decoder.reset(); 269 assertThrows(function() {decoder.readSignedVarint64()}); 270 decoder.reset(); 271 assertThrows(function() {decoder.readZigzagVarint64()}); 272 decoder.reset(); 273 assertThrows(function() {decoder.readUnsignedVarint32()}); 274 }); 275 276 277 /** 278 * Tests encoding and decoding of unsigned integers. 279 */ 280 it('testUnsignedIntegers', function() { 281 doTestUnsignedValue( 282 jspb.BinaryDecoder.prototype.readUint8, 283 jspb.BinaryEncoder.prototype.writeUint8, 284 1, 0xFF, Math.round); 285 286 doTestUnsignedValue( 287 jspb.BinaryDecoder.prototype.readUint16, 288 jspb.BinaryEncoder.prototype.writeUint16, 289 1, 0xFFFF, Math.round); 290 291 doTestUnsignedValue( 292 jspb.BinaryDecoder.prototype.readUint32, 293 jspb.BinaryEncoder.prototype.writeUint32, 294 1, 0xFFFFFFFF, Math.round); 295 296 doTestUnsignedValue( 297 jspb.BinaryDecoder.prototype.readUint64, 298 jspb.BinaryEncoder.prototype.writeUint64, 299 1, Math.pow(2, 64) - 1025, Math.round); 300 }); 301 302 303 /** 304 * Tests encoding and decoding of signed integers. 305 */ 306 it('testSignedIntegers', function() { 307 doTestSignedValue( 308 jspb.BinaryDecoder.prototype.readInt8, 309 jspb.BinaryEncoder.prototype.writeInt8, 310 1, -0x80, 0x7F, Math.round); 311 312 doTestSignedValue( 313 jspb.BinaryDecoder.prototype.readInt16, 314 jspb.BinaryEncoder.prototype.writeInt16, 315 1, -0x8000, 0x7FFF, Math.round); 316 317 doTestSignedValue( 318 jspb.BinaryDecoder.prototype.readInt32, 319 jspb.BinaryEncoder.prototype.writeInt32, 320 1, -0x80000000, 0x7FFFFFFF, Math.round); 321 322 doTestSignedValue( 323 jspb.BinaryDecoder.prototype.readInt64, 324 jspb.BinaryEncoder.prototype.writeInt64, 325 1, -Math.pow(2, 63), Math.pow(2, 63) - 513, Math.round); 326 }); 327 328 329 /** 330 * Tests encoding and decoding of floats. 331 */ 332 it('testFloats', function() { 333 /** 334 * @param {number} x 335 * @return {number} 336 */ 337 function truncate(x) { 338 var temp = new Float32Array(1); 339 temp[0] = x; 340 return temp[0]; 341 } 342 doTestSignedValue( 343 jspb.BinaryDecoder.prototype.readFloat, 344 jspb.BinaryEncoder.prototype.writeFloat, 345 jspb.BinaryConstants.FLOAT32_EPS, 346 -jspb.BinaryConstants.FLOAT32_MAX, 347 jspb.BinaryConstants.FLOAT32_MAX, 348 truncate); 349 350 doTestSignedValue( 351 jspb.BinaryDecoder.prototype.readDouble, 352 jspb.BinaryEncoder.prototype.writeDouble, 353 jspb.BinaryConstants.FLOAT64_EPS * 10, 354 -jspb.BinaryConstants.FLOAT64_MAX, 355 jspb.BinaryConstants.FLOAT64_MAX, 356 function(x) { return x; }); 357 }); 358}); 359