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'); 48goog.require('jspb.utils'); 49 50 51/** 52 * Tests encoding and decoding of unsigned types. 53 * @param {Function} readValue 54 * @param {Function} writeValue 55 * @param {number} epsilon 56 * @param {number} upperLimit 57 * @param {Function} filter 58 * @suppress {missingProperties|visibility} 59 */ 60function doTestUnsignedValue(readValue, 61 writeValue, epsilon, upperLimit, filter) { 62 var encoder = new jspb.BinaryEncoder(); 63 64 // Encode zero and limits. 65 writeValue.call(encoder, filter(0)); 66 writeValue.call(encoder, filter(epsilon)); 67 writeValue.call(encoder, filter(upperLimit)); 68 69 // Encode positive values. 70 for (var cursor = epsilon; cursor < upperLimit; cursor *= 1.1) { 71 writeValue.call(encoder, filter(cursor)); 72 } 73 74 var decoder = jspb.BinaryDecoder.alloc(encoder.end()); 75 76 // Check zero and limits. 77 assertEquals(filter(0), readValue.call(decoder)); 78 assertEquals(filter(epsilon), readValue.call(decoder)); 79 assertEquals(filter(upperLimit), readValue.call(decoder)); 80 81 // Check positive values. 82 for (var cursor = epsilon; cursor < upperLimit; cursor *= 1.1) { 83 if (filter(cursor) != readValue.call(decoder)) throw 'fail!'; 84 } 85 86 // Encoding values outside the valid range should assert. 87 assertThrows(function() {writeValue.call(encoder, -1);}); 88 assertThrows(function() {writeValue.call(encoder, upperLimit * 1.1);}); 89} 90 91 92/** 93 * Tests encoding and decoding of signed types. 94 * @param {Function} readValue 95 * @param {Function} writeValue 96 * @param {number} epsilon 97 * @param {number} lowerLimit 98 * @param {number} upperLimit 99 * @param {Function} filter 100 * @suppress {missingProperties} 101 */ 102function doTestSignedValue(readValue, 103 writeValue, epsilon, lowerLimit, upperLimit, filter) { 104 var encoder = new jspb.BinaryEncoder(); 105 106 // Encode zero and limits. 107 writeValue.call(encoder, filter(lowerLimit)); 108 writeValue.call(encoder, filter(-epsilon)); 109 writeValue.call(encoder, filter(0)); 110 writeValue.call(encoder, filter(epsilon)); 111 writeValue.call(encoder, filter(upperLimit)); 112 113 var inputValues = []; 114 115 // Encode negative values. 116 for (var cursor = lowerLimit; cursor < -epsilon; cursor /= 1.1) { 117 var val = filter(cursor); 118 writeValue.call(encoder, val); 119 inputValues.push(val); 120 } 121 122 // Encode positive values. 123 for (var cursor = epsilon; cursor < upperLimit; cursor *= 1.1) { 124 var val = filter(cursor); 125 writeValue.call(encoder, val); 126 inputValues.push(val); 127 } 128 129 var decoder = jspb.BinaryDecoder.alloc(encoder.end()); 130 131 // Check zero and limits. 132 assertEquals(filter(lowerLimit), readValue.call(decoder)); 133 assertEquals(filter(-epsilon), readValue.call(decoder)); 134 assertEquals(filter(0), readValue.call(decoder)); 135 assertEquals(filter(epsilon), readValue.call(decoder)); 136 assertEquals(filter(upperLimit), readValue.call(decoder)); 137 138 // Verify decoded values. 139 for (var i = 0; i < inputValues.length; i++) { 140 assertEquals(inputValues[i], readValue.call(decoder)); 141 } 142 143 // Encoding values outside the valid range should assert. 144 var pastLowerLimit = lowerLimit * 1.1; 145 var pastUpperLimit = upperLimit * 1.1; 146 if (pastLowerLimit !== -Infinity) { 147 expect(() => void writeValue.call(encoder, pastLowerLimit)).toThrow(); 148 } 149 if (pastUpperLimit !== Infinity) { 150 expect(() => void writeValue.call(encoder, pastUpperLimit)).toThrow(); 151 } 152} 153 154describe('binaryDecoderTest', function() { 155 /** 156 * Tests the decoder instance cache. 157 */ 158 it('testInstanceCache', /** @suppress {visibility} */ function() { 159 // Empty the instance caches. 160 jspb.BinaryDecoder.instanceCache_ = []; 161 162 // Allocating and then freeing a decoder should put it in the instance 163 // cache. 164 jspb.BinaryDecoder.alloc().free(); 165 166 assertEquals(1, jspb.BinaryDecoder.instanceCache_.length); 167 168 // Allocating and then freeing three decoders should leave us with three in 169 // the cache. 170 171 var decoder1 = jspb.BinaryDecoder.alloc(); 172 var decoder2 = jspb.BinaryDecoder.alloc(); 173 var decoder3 = jspb.BinaryDecoder.alloc(); 174 decoder1.free(); 175 decoder2.free(); 176 decoder3.free(); 177 178 assertEquals(3, jspb.BinaryDecoder.instanceCache_.length); 179 }); 180 181 182 describe('varint64', function() { 183 var /** !jspb.BinaryEncoder */ encoder; 184 var /** !jspb.BinaryDecoder */ decoder; 185 186 var hashA = String.fromCharCode(0x00, 0x00, 0x00, 0x00, 187 0x00, 0x00, 0x00, 0x00); 188 var hashB = String.fromCharCode(0x12, 0x34, 0x00, 0x00, 189 0x00, 0x00, 0x00, 0x00); 190 var hashC = String.fromCharCode(0x12, 0x34, 0x56, 0x78, 191 0x87, 0x65, 0x43, 0x21); 192 var hashD = String.fromCharCode(0xFF, 0xFF, 0xFF, 0xFF, 193 0xFF, 0xFF, 0xFF, 0xFF); 194 beforeEach(function() { 195 encoder = new jspb.BinaryEncoder(); 196 197 encoder.writeVarintHash64(hashA); 198 encoder.writeVarintHash64(hashB); 199 encoder.writeVarintHash64(hashC); 200 encoder.writeVarintHash64(hashD); 201 202 encoder.writeFixedHash64(hashA); 203 encoder.writeFixedHash64(hashB); 204 encoder.writeFixedHash64(hashC); 205 encoder.writeFixedHash64(hashD); 206 207 decoder = jspb.BinaryDecoder.alloc(encoder.end()); 208 }); 209 210 it('reads 64-bit integers as hash strings', function() { 211 assertEquals(hashA, decoder.readVarintHash64()); 212 assertEquals(hashB, decoder.readVarintHash64()); 213 assertEquals(hashC, decoder.readVarintHash64()); 214 assertEquals(hashD, decoder.readVarintHash64()); 215 216 assertEquals(hashA, decoder.readFixedHash64()); 217 assertEquals(hashB, decoder.readFixedHash64()); 218 assertEquals(hashC, decoder.readFixedHash64()); 219 assertEquals(hashD, decoder.readFixedHash64()); 220 }); 221 222 it('reads split 64 bit integers', function() { 223 function hexJoin(bitsLow, bitsHigh) { 224 return `0x${(bitsHigh >>> 0).toString(16)}:0x${ 225 (bitsLow >>> 0).toString(16)}`; 226 } 227 function hexJoinHash(hash64) { 228 jspb.utils.splitHash64(hash64); 229 return hexJoin(jspb.utils.split64Low, jspb.utils.split64High); 230 } 231 232 expect(decoder.readSplitVarint64(hexJoin)).toEqual(hexJoinHash(hashA)); 233 expect(decoder.readSplitVarint64(hexJoin)).toEqual(hexJoinHash(hashB)); 234 expect(decoder.readSplitVarint64(hexJoin)).toEqual(hexJoinHash(hashC)); 235 expect(decoder.readSplitVarint64(hexJoin)).toEqual(hexJoinHash(hashD)); 236 237 expect(decoder.readSplitFixed64(hexJoin)).toEqual(hexJoinHash(hashA)); 238 expect(decoder.readSplitFixed64(hexJoin)).toEqual(hexJoinHash(hashB)); 239 expect(decoder.readSplitFixed64(hexJoin)).toEqual(hexJoinHash(hashC)); 240 expect(decoder.readSplitFixed64(hexJoin)).toEqual(hexJoinHash(hashD)); 241 }); 242 }); 243 244 describe('sint64', function() { 245 var /** !jspb.BinaryDecoder */ decoder; 246 247 var hashA = 248 String.fromCharCode(0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00); 249 var hashB = 250 String.fromCharCode(0x12, 0x34, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00); 251 var hashC = 252 String.fromCharCode(0x12, 0x34, 0x56, 0x78, 0x87, 0x65, 0x43, 0x21); 253 var hashD = 254 String.fromCharCode(0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF); 255 beforeEach(function() { 256 var encoder = new jspb.BinaryEncoder(); 257 258 encoder.writeZigzagVarintHash64(hashA); 259 encoder.writeZigzagVarintHash64(hashB); 260 encoder.writeZigzagVarintHash64(hashC); 261 encoder.writeZigzagVarintHash64(hashD); 262 263 decoder = jspb.BinaryDecoder.alloc(encoder.end()); 264 }); 265 266 it('reads 64-bit integers as decimal strings', function() { 267 const signed = true; 268 expect(decoder.readZigzagVarint64String()) 269 .toEqual(jspb.utils.hash64ToDecimalString(hashA, signed)); 270 expect(decoder.readZigzagVarint64String()) 271 .toEqual(jspb.utils.hash64ToDecimalString(hashB, signed)); 272 expect(decoder.readZigzagVarint64String()) 273 .toEqual(jspb.utils.hash64ToDecimalString(hashC, signed)); 274 expect(decoder.readZigzagVarint64String()) 275 .toEqual(jspb.utils.hash64ToDecimalString(hashD, signed)); 276 }); 277 278 it('reads 64-bit integers as hash strings', function() { 279 expect(decoder.readZigzagVarintHash64()).toEqual(hashA); 280 expect(decoder.readZigzagVarintHash64()).toEqual(hashB); 281 expect(decoder.readZigzagVarintHash64()).toEqual(hashC); 282 expect(decoder.readZigzagVarintHash64()).toEqual(hashD); 283 }); 284 285 it('reads split 64 bit zigzag integers', function() { 286 function hexJoin(bitsLow, bitsHigh) { 287 return `0x${(bitsHigh >>> 0).toString(16)}:0x${ 288 (bitsLow >>> 0).toString(16)}`; 289 } 290 function hexJoinHash(hash64) { 291 jspb.utils.splitHash64(hash64); 292 return hexJoin(jspb.utils.split64Low, jspb.utils.split64High); 293 } 294 295 expect(decoder.readSplitZigzagVarint64(hexJoin)) 296 .toEqual(hexJoinHash(hashA)); 297 expect(decoder.readSplitZigzagVarint64(hexJoin)) 298 .toEqual(hexJoinHash(hashB)); 299 expect(decoder.readSplitZigzagVarint64(hexJoin)) 300 .toEqual(hexJoinHash(hashC)); 301 expect(decoder.readSplitZigzagVarint64(hexJoin)) 302 .toEqual(hexJoinHash(hashD)); 303 }); 304 305 it('does zigzag encoding properly', function() { 306 // Test cases directly from the protobuf dev guide. 307 // https://engdoc.corp.google.com/eng/howto/protocolbuffers/developerguide/encoding.shtml?cl=head#types 308 var testCases = [ 309 {original: '0', zigzag: '0'}, 310 {original: '-1', zigzag: '1'}, 311 {original: '1', zigzag: '2'}, 312 {original: '-2', zigzag: '3'}, 313 {original: '2147483647', zigzag: '4294967294'}, 314 {original: '-2147483648', zigzag: '4294967295'}, 315 // 64-bit extremes, not in dev guide. 316 {original: '9223372036854775807', zigzag: '18446744073709551614'}, 317 {original: '-9223372036854775808', zigzag: '18446744073709551615'}, 318 ]; 319 var encoder = new jspb.BinaryEncoder(); 320 testCases.forEach(function(c) { 321 encoder.writeZigzagVarint64String(c.original); 322 }); 323 var buffer = encoder.end(); 324 var zigzagDecoder = jspb.BinaryDecoder.alloc(buffer); 325 var varintDecoder = jspb.BinaryDecoder.alloc(buffer); 326 testCases.forEach(function(c) { 327 expect(zigzagDecoder.readZigzagVarint64String()).toEqual(c.original); 328 expect(varintDecoder.readUnsignedVarint64String()).toEqual(c.zigzag); 329 }); 330 }); 331 }); 332 333 /** 334 * Tests reading and writing large strings 335 */ 336 it('testLargeStrings', function() { 337 var encoder = new jspb.BinaryEncoder(); 338 339 var len = 150000; 340 var long_string = ''; 341 for (var i = 0; i < len; i++) { 342 long_string += 'a'; 343 } 344 345 encoder.writeString(long_string); 346 347 var decoder = jspb.BinaryDecoder.alloc(encoder.end()); 348 349 assertEquals(long_string, decoder.readString(len)); 350 }); 351 352 /** 353 * Test encoding and decoding utf-8. 354 */ 355 it('testUtf8', function() { 356 var encoder = new jspb.BinaryEncoder(); 357 358 var ascii = "ASCII should work in 3, 2, 1..."; 359 var utf8_two_bytes = "©"; 360 var utf8_three_bytes = "❄"; 361 var utf8_four_bytes = "��"; 362 363 encoder.writeString(ascii); 364 encoder.writeString(utf8_two_bytes); 365 encoder.writeString(utf8_three_bytes); 366 encoder.writeString(utf8_four_bytes); 367 368 var decoder = jspb.BinaryDecoder.alloc(encoder.end()); 369 370 assertEquals(ascii, decoder.readString(ascii.length)); 371 assertEquals(utf8_two_bytes, decoder.readString(utf8_two_bytes.length)); 372 assertEquals(utf8_three_bytes, decoder.readString(utf8_three_bytes.length)); 373 assertEquals(utf8_four_bytes, decoder.readString(utf8_four_bytes.length)); 374 }); 375 376 /** 377 * Verifies that misuse of the decoder class triggers assertions. 378 */ 379 it('testDecodeErrors', function() { 380 // Reading a value past the end of the stream should trigger an assertion. 381 var decoder = jspb.BinaryDecoder.alloc([0, 1, 2]); 382 assertThrows(function() {decoder.readUint64()}); 383 384 // Overlong varints should trigger assertions. 385 decoder.setBlock([255, 255, 255, 255, 255, 255, 386 255, 255, 255, 255, 255, 0]); 387 assertThrows(function() {decoder.readUnsignedVarint64()}); 388 decoder.reset(); 389 assertThrows(function() {decoder.readSignedVarint64()}); 390 decoder.reset(); 391 assertThrows(function() {decoder.readZigzagVarint64()}); 392 decoder.reset(); 393 assertThrows(function() {decoder.readUnsignedVarint32()}); 394 }); 395 396 397 /** 398 * Tests encoding and decoding of unsigned integers. 399 */ 400 it('testUnsignedIntegers', function() { 401 doTestUnsignedValue( 402 jspb.BinaryDecoder.prototype.readUint8, 403 jspb.BinaryEncoder.prototype.writeUint8, 404 1, 0xFF, Math.round); 405 406 doTestUnsignedValue( 407 jspb.BinaryDecoder.prototype.readUint16, 408 jspb.BinaryEncoder.prototype.writeUint16, 409 1, 0xFFFF, Math.round); 410 411 doTestUnsignedValue( 412 jspb.BinaryDecoder.prototype.readUint32, 413 jspb.BinaryEncoder.prototype.writeUint32, 414 1, 0xFFFFFFFF, Math.round); 415 416 doTestUnsignedValue( 417 jspb.BinaryDecoder.prototype.readUint64, 418 jspb.BinaryEncoder.prototype.writeUint64, 419 1, Math.pow(2, 64) - 1025, Math.round); 420 }); 421 422 423 /** 424 * Tests encoding and decoding of signed integers. 425 */ 426 it('testSignedIntegers', function() { 427 doTestSignedValue( 428 jspb.BinaryDecoder.prototype.readInt8, 429 jspb.BinaryEncoder.prototype.writeInt8, 430 1, -0x80, 0x7F, Math.round); 431 432 doTestSignedValue( 433 jspb.BinaryDecoder.prototype.readInt16, 434 jspb.BinaryEncoder.prototype.writeInt16, 435 1, -0x8000, 0x7FFF, Math.round); 436 437 doTestSignedValue( 438 jspb.BinaryDecoder.prototype.readInt32, 439 jspb.BinaryEncoder.prototype.writeInt32, 440 1, -0x80000000, 0x7FFFFFFF, Math.round); 441 442 doTestSignedValue( 443 jspb.BinaryDecoder.prototype.readInt64, 444 jspb.BinaryEncoder.prototype.writeInt64, 445 1, -Math.pow(2, 63), Math.pow(2, 63) - 513, Math.round); 446 }); 447 448 449 /** 450 * Tests encoding and decoding of floats. 451 */ 452 it('testFloats', function() { 453 /** 454 * @param {number} x 455 * @return {number} 456 */ 457 function truncate(x) { 458 var temp = new Float32Array(1); 459 temp[0] = x; 460 return temp[0]; 461 } 462 doTestSignedValue( 463 jspb.BinaryDecoder.prototype.readFloat, 464 jspb.BinaryEncoder.prototype.writeFloat, 465 jspb.BinaryConstants.FLOAT32_EPS, 466 -jspb.BinaryConstants.FLOAT32_MAX, 467 jspb.BinaryConstants.FLOAT32_MAX, 468 truncate); 469 470 doTestSignedValue( 471 jspb.BinaryDecoder.prototype.readDouble, 472 jspb.BinaryEncoder.prototype.writeDouble, 473 jspb.BinaryConstants.FLOAT64_EPS * 10, 474 -jspb.BinaryConstants.FLOAT64_MAX, 475 jspb.BinaryConstants.FLOAT64_MAX, 476 function(x) { return x; }); 477 }); 478}); 479