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