decoder_test.js 12.5 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc.  All rights reserved.
// https://developers.google.com/protocol-buffers/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
//     * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
//     * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
//     * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

/**
 * @fileoverview Test cases for jspb's binary protocol buffer decoder.
 *
 * There are two particular magic numbers that need to be pointed out -
 * 2^64-1025 is the largest number representable as both a double and an
 * unsigned 64-bit integer, and 2^63-513 is the largest number representable as
 * both a double and a signed 64-bit integer.
 *
 * Test suite is written using Jasmine -- see http://jasmine.github.io/
 *
 * @author aappleby@google.com (Austin Appleby)
 */

goog.require('goog.testing.asserts');
goog.require('jspb.BinaryConstants');
goog.require('jspb.BinaryDecoder');
47
goog.require('jspb.BinaryEncoder');
48 49 50


/**
51
 * Tests encoding and decoding of unsigned types.
52 53 54 55 56 57 58 59 60
 * @param {Function} readValue
 * @param {Function} writeValue
 * @param {number} epsilon
 * @param {number} upperLimit
 * @param {Function} filter
 * @suppress {missingProperties|visibility}
 */
function doTestUnsignedValue(readValue,
    writeValue, epsilon, upperLimit, filter) {
61
  var encoder = new jspb.BinaryEncoder();
62 63

  // Encode zero and limits.
64 65 66
  writeValue.call(encoder, filter(0));
  writeValue.call(encoder, filter(epsilon));
  writeValue.call(encoder, filter(upperLimit));
67 68 69

  // Encode positive values.
  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 77 78
  assertEquals(filter(0), readValue.call(decoder));
  assertEquals(filter(epsilon), readValue.call(decoder));
  assertEquals(filter(upperLimit), readValue.call(decoder));
79 80 81

  // Check positive values.
  for (var cursor = epsilon; cursor < upperLimit; cursor *= 1.1) {
82
    if (filter(cursor) != readValue.call(decoder)) throw 'fail!';
83
  }
84 85 86 87

  // Encoding values outside the valid range should assert.
  assertThrows(function() {writeValue.call(encoder, -1);});
  assertThrows(function() {writeValue.call(encoder, upperLimit * 1.1);});
88 89 90 91
}


/**
92
 * Tests encoding and decoding of signed types.
93 94 95 96 97 98 99 100 101 102
 * @param {Function} readValue
 * @param {Function} writeValue
 * @param {number} epsilon
 * @param {number} lowerLimit
 * @param {number} upperLimit
 * @param {Function} filter
 * @suppress {missingProperties}
 */
function doTestSignedValue(readValue,
    writeValue, epsilon, lowerLimit, upperLimit, filter) {
103
  var encoder = new jspb.BinaryEncoder();
104 105

  // Encode zero and limits.
106 107 108 109 110
  writeValue.call(encoder, filter(lowerLimit));
  writeValue.call(encoder, filter(-epsilon));
  writeValue.call(encoder, filter(0));
  writeValue.call(encoder, filter(epsilon));
  writeValue.call(encoder, filter(upperLimit));
111 112 113 114 115 116

  var inputValues = [];

  // Encode negative values.
  for (var cursor = lowerLimit; cursor < -epsilon; cursor /= 1.1) {
    var val = filter(cursor);
117
    writeValue.call(encoder, val);
118 119 120 121 122 123
    inputValues.push(val);
  }

  // Encode positive values.
  for (var cursor = epsilon; cursor < upperLimit; cursor *= 1.1) {
    var val = filter(cursor);
124
    writeValue.call(encoder, val);
125 126 127
    inputValues.push(val);
  }

128
  var decoder = jspb.BinaryDecoder.alloc(encoder.end());
129 130

  // Check zero and limits.
131 132 133 134 135
  assertEquals(filter(lowerLimit), readValue.call(decoder));
  assertEquals(filter(-epsilon), readValue.call(decoder));
  assertEquals(filter(0), readValue.call(decoder));
  assertEquals(filter(epsilon), readValue.call(decoder));
  assertEquals(filter(upperLimit), readValue.call(decoder));
136 137 138

  // Verify decoded values.
  for (var i = 0; i < inputValues.length; i++) {
139
    assertEquals(inputValues[i], readValue.call(decoder));
140
  }
141 142 143 144

  // Encoding values outside the valid range should assert.
  assertThrows(function() {writeValue.call(encoder, lowerLimit * 1.1);});
  assertThrows(function() {writeValue.call(encoder, upperLimit * 1.1);});
145 146 147 148 149 150
}

describe('binaryDecoderTest', function() {
  /**
   * Tests the decoder instance cache.
   */
151
  it('testInstanceCache', /** @suppress {visibility} */ function() {
152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178
    // Empty the instance caches.
    jspb.BinaryDecoder.instanceCache_ = [];

    // Allocating and then freeing a decoder should put it in the instance
    // cache.
    jspb.BinaryDecoder.alloc().free();

    assertEquals(1, jspb.BinaryDecoder.instanceCache_.length);

    // Allocating and then freeing three decoders should leave us with three in
    // the cache.

    var decoder1 = jspb.BinaryDecoder.alloc();
    var decoder2 = jspb.BinaryDecoder.alloc();
    var decoder3 = jspb.BinaryDecoder.alloc();
    decoder1.free();
    decoder2.free();
    decoder3.free();

    assertEquals(3, jspb.BinaryDecoder.instanceCache_.length);
  });


  /**
   * Tests reading 64-bit integers as hash strings.
   */
  it('testHashStrings', function() {
179
    var encoder = new jspb.BinaryEncoder();
180 181 182 183 184 185 186 187 188 189

    var hashA = String.fromCharCode(0x00, 0x00, 0x00, 0x00,
                                    0x00, 0x00, 0x00, 0x00);
    var hashB = String.fromCharCode(0x12, 0x34, 0x00, 0x00,
                                    0x00, 0x00, 0x00, 0x00);
    var hashC = String.fromCharCode(0x12, 0x34, 0x56, 0x78,
                                    0x87, 0x65, 0x43, 0x21);
    var hashD = String.fromCharCode(0xFF, 0xFF, 0xFF, 0xFF,
                                    0xFF, 0xFF, 0xFF, 0xFF);

190 191 192 193
    encoder.writeVarintHash64(hashA);
    encoder.writeVarintHash64(hashB);
    encoder.writeVarintHash64(hashC);
    encoder.writeVarintHash64(hashD);
194

195 196 197 198
    encoder.writeFixedHash64(hashA);
    encoder.writeFixedHash64(hashB);
    encoder.writeFixedHash64(hashC);
    encoder.writeFixedHash64(hashD);
199

200
    var decoder = jspb.BinaryDecoder.alloc(encoder.end());
201 202 203 204 205 206 207 208 209 210 211

    assertEquals(hashA, decoder.readVarintHash64());
    assertEquals(hashB, decoder.readVarintHash64());
    assertEquals(hashC, decoder.readVarintHash64());
    assertEquals(hashD, decoder.readVarintHash64());

    assertEquals(hashA, decoder.readFixedHash64());
    assertEquals(hashB, decoder.readFixedHash64());
    assertEquals(hashC, decoder.readFixedHash64());
    assertEquals(hashD, decoder.readFixedHash64());
  });
212

213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231
  /**
   * Tests reading and writing large strings
   */
  it('testLargeStrings', function() {
    var encoder = new jspb.BinaryEncoder();

    var len = 150000;
    var long_string = '';
    for (var i = 0; i < len; i++) {
      long_string += 'a';
    }

    encoder.writeString(long_string);

    var decoder = jspb.BinaryDecoder.alloc(encoder.end());

    assertEquals(long_string, decoder.readString(len));
  });

232 233 234 235 236
  /**
   * Test encoding and decoding utf-8.
   */
   it('testUtf8', function() {
    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 244
    encoder.writeString(ascii);
    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 251
    assertEquals(ascii, decoder.readString(ascii.length));
    assertEquals(utf8_two_bytes, decoder.readString(utf8_two_bytes.length));
252
    assertEquals(utf8_three_bytes, decoder.readString(utf8_three_bytes.length));
253 254
    assertEquals(utf8_four_bytes, decoder.readString(utf8_four_bytes.length));
   });
255 256 257 258 259 260 261 262 263 264 265

  /**
   * Verifies that misuse of the decoder class triggers assertions.
   * @suppress {checkTypes|visibility}
   */
  it('testDecodeErrors', function() {
    // Reading a value past the end of the stream should trigger an assertion.
    var decoder = jspb.BinaryDecoder.alloc([0, 1, 2]);
    assertThrows(function() {decoder.readUint64()});

    // Overlong varints should trigger assertions.
266 267
    decoder.setBlock([255, 255, 255, 255, 255, 255,
                      255, 255, 255, 255, 255, 0]);
268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295
    assertThrows(function() {decoder.readUnsignedVarint64()});
    decoder.reset();
    assertThrows(function() {decoder.readSignedVarint64()});
    decoder.reset();
    assertThrows(function() {decoder.readZigzagVarint64()});

    // Positive 32-bit varints encoded with 1 bits in positions 33 through 35
    // should trigger assertions.
    decoder.setBlock([255, 255, 255, 255, 0x1F]);
    assertThrows(function() {decoder.readUnsignedVarint32()});

    decoder.setBlock([255, 255, 255, 255, 0x2F]);
    assertThrows(function() {decoder.readUnsignedVarint32()});

    decoder.setBlock([255, 255, 255, 255, 0x4F]);
    assertThrows(function() {decoder.readUnsignedVarint32()});

    // Negative 32-bit varints encoded with non-1 bits in the high dword should
    // trigger assertions.
    decoder.setBlock([255, 255, 255, 255, 255, 255, 0, 255, 255, 1]);
    assertThrows(function() {decoder.readUnsignedVarint32()});

    decoder.setBlock([255, 255, 255, 255, 255, 255, 255, 255, 255, 0]);
    assertThrows(function() {decoder.readUnsignedVarint32()});
  });


  /**
296
   * Tests encoding and decoding of unsigned integers.
297
   */
298
  it('testUnsignedIntegers', function() {
299 300
    doTestUnsignedValue(
        jspb.BinaryDecoder.prototype.readUint8,
301
        jspb.BinaryEncoder.prototype.writeUint8,
302 303 304 305
        1, 0xFF, Math.round);

    doTestUnsignedValue(
        jspb.BinaryDecoder.prototype.readUint16,
306
        jspb.BinaryEncoder.prototype.writeUint16,
307 308 309 310
        1, 0xFFFF, Math.round);

    doTestUnsignedValue(
        jspb.BinaryDecoder.prototype.readUint32,
311
        jspb.BinaryEncoder.prototype.writeUint32,
312 313 314 315
        1, 0xFFFFFFFF, Math.round);

    doTestUnsignedValue(
        jspb.BinaryDecoder.prototype.readUint64,
316
        jspb.BinaryEncoder.prototype.writeUint64,
317 318 319 320 321
        1, Math.pow(2, 64) - 1025, Math.round);
  });


  /**
322
   * Tests encoding and decoding of signed integers.
323
   */
324
  it('testSignedIntegers', function() {
325 326
    doTestSignedValue(
        jspb.BinaryDecoder.prototype.readInt8,
327
        jspb.BinaryEncoder.prototype.writeInt8,
328 329 330 331
        1, -0x80, 0x7F, Math.round);

    doTestSignedValue(
        jspb.BinaryDecoder.prototype.readInt16,
332
        jspb.BinaryEncoder.prototype.writeInt16,
333 334 335 336
        1, -0x8000, 0x7FFF, Math.round);

    doTestSignedValue(
        jspb.BinaryDecoder.prototype.readInt32,
337
        jspb.BinaryEncoder.prototype.writeInt32,
338 339 340 341
        1, -0x80000000, 0x7FFFFFFF, Math.round);

    doTestSignedValue(
        jspb.BinaryDecoder.prototype.readInt64,
342
        jspb.BinaryEncoder.prototype.writeInt64,
343 344 345 346 347
        1, -Math.pow(2, 63), Math.pow(2, 63) - 513, Math.round);
  });


  /**
348
   * Tests encoding and decoding of floats.
349
   */
350
  it('testFloats', function() {
351 352 353 354 355 356 357 358 359 360 361
    /**
     * @param {number} x
     * @return {number}
     */
    function truncate(x) {
      var temp = new Float32Array(1);
      temp[0] = x;
      return temp[0];
    }
    doTestSignedValue(
        jspb.BinaryDecoder.prototype.readFloat,
362
        jspb.BinaryEncoder.prototype.writeFloat,
363 364 365 366 367 368 369
        jspb.BinaryConstants.FLOAT32_EPS,
        -jspb.BinaryConstants.FLOAT32_MAX,
        jspb.BinaryConstants.FLOAT32_MAX,
        truncate);

    doTestSignedValue(
        jspb.BinaryDecoder.prototype.readDouble,
370
        jspb.BinaryEncoder.prototype.writeDouble,
371 372 373 374 375 376
        jspb.BinaryConstants.FLOAT64_EPS * 10,
        -jspb.BinaryConstants.FLOAT64_MAX,
        jspb.BinaryConstants.FLOAT64_MAX,
        function(x) { return x; });
  });
});