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 writer. In 33 * practice BinaryWriter is used to drive the Decoder and Reader test cases, 34 * so only writer-specific tests are here. 35 * 36 * Test suite is written using Jasmine -- see http://jasmine.github.io/ 37 * 38 * @author aappleby@google.com (Austin Appleby) 39 */ 40 41goog.require('goog.crypt'); 42goog.require('goog.testing.asserts'); 43goog.require('jspb.BinaryConstants'); 44goog.require('jspb.BinaryReader'); 45goog.require('jspb.BinaryWriter'); 46goog.require('jspb.utils'); 47goog.require('goog.crypt.base64'); 48goog.requireType('jspb.BinaryMessage'); 49 50/** 51 * @param {function()} func This function should throw an error when run. 52 */ 53function assertFails(func) { 54 assertThrows(func); 55} 56 57 58describe('binaryWriterTest', function() { 59 /** 60 * Verifies that misuse of the writer class triggers assertions. 61 */ 62 it('testWriteErrors', function() { 63 // Submessages with invalid field indices should assert. 64 var writer = new jspb.BinaryWriter(); 65 var dummyMessage = /** @type {!jspb.BinaryMessage} */ ({}); 66 67 assertFails(function() { 68 writer.writeMessage(-1, dummyMessage, goog.nullFunction); 69 }); 70 71 // Writing invalid field indices should assert. 72 writer = new jspb.BinaryWriter(); 73 assertFails(function() { 74 writer.writeUint64(-1, 1); 75 }); 76 77 // Writing out-of-range field values should assert. 78 writer = new jspb.BinaryWriter(); 79 80 assertFails(function() { 81 writer.writeInt32(1, -Infinity); 82 }); 83 assertFails(function() { 84 writer.writeInt32(1, Infinity); 85 }); 86 87 assertFails(function() { 88 writer.writeInt64(1, -Infinity); 89 }); 90 assertFails(function() { 91 writer.writeInt64(1, Infinity); 92 }); 93 94 assertFails(function() { 95 writer.writeUint32(1, -1); 96 }); 97 assertFails(function() { 98 writer.writeUint32(1, Infinity); 99 }); 100 101 assertFails(function() { 102 writer.writeUint64(1, -1); 103 }); 104 assertFails(function() { 105 writer.writeUint64(1, Infinity); 106 }); 107 108 assertFails(function() { 109 writer.writeSint32(1, -Infinity); 110 }); 111 assertFails(function() { 112 writer.writeSint32(1, Infinity); 113 }); 114 115 assertFails(function() { 116 writer.writeSint64(1, -Infinity); 117 }); 118 assertFails(function() { 119 writer.writeSint64(1, Infinity); 120 }); 121 122 assertFails(function() { 123 writer.writeFixed32(1, -1); 124 }); 125 assertFails(function() { 126 writer.writeFixed32(1, Infinity); 127 }); 128 129 assertFails(function() { 130 writer.writeFixed64(1, -1); 131 }); 132 assertFails(function() { 133 writer.writeFixed64(1, Infinity); 134 }); 135 136 assertFails(function() { 137 writer.writeSfixed32(1, -Infinity); 138 }); 139 assertFails(function() { 140 writer.writeSfixed32(1, Infinity); 141 }); 142 143 assertFails(function() { 144 writer.writeSfixed64(1, -Infinity); 145 }); 146 assertFails(function() { 147 writer.writeSfixed64(1, Infinity); 148 }); 149 }); 150 151 152 /** 153 * Basic test of retrieving the result as a Uint8Array buffer 154 */ 155 it('testGetResultBuffer', function() { 156 var expected = '0864120b48656c6c6f20776f726c641a0301020320c801'; 157 158 var writer = new jspb.BinaryWriter(); 159 writer.writeUint32(1, 100); 160 writer.writeString(2, 'Hello world'); 161 writer.writeBytes(3, new Uint8Array([1, 2, 3])); 162 writer.writeUint32(4, 200); 163 164 var buffer = writer.getResultBuffer(); 165 assertEquals(expected, goog.crypt.byteArrayToHex(buffer)); 166 }); 167 168 169 /** 170 * Tests websafe encodings for base64 strings. 171 */ 172 it('testWebSafeOption', function() { 173 var writer = new jspb.BinaryWriter(); 174 writer.writeBytes(1, new Uint8Array([127])); 175 assertEquals('CgF/', writer.getResultBase64String()); 176 assertEquals( 177 'CgF/', 178 writer.getResultBase64String(goog.crypt.base64.Alphabet.DEFAULT)); 179 assertEquals( 180 'CgF_', 181 writer.getResultBase64String( 182 goog.crypt.base64.Alphabet.WEBSAFE_NO_PADDING)); 183 }); 184 185 it('writes split 64 fields', function() { 186 var writer = new jspb.BinaryWriter(); 187 writer.writeSplitVarint64(1, 0x1, 0x2); 188 writer.writeSplitVarint64(1, 0xFFFFFFFF, 0xFFFFFFFF); 189 writer.writeSplitFixed64(2, 0x1, 0x2); 190 writer.writeSplitFixed64(2, 0xFFFFFFF0, 0xFFFFFFFF); 191 function lo(i) { 192 return i + 1; 193 } 194 function hi(i) { 195 return i + 2; 196 } 197 writer.writeRepeatedSplitVarint64(3, [0, 1, 2], lo, hi); 198 writer.writeRepeatedSplitFixed64(4, [0, 1, 2], lo, hi); 199 writer.writePackedSplitVarint64(5, [0, 1, 2], lo, hi); 200 writer.writePackedSplitFixed64(6, [0, 1, 2], lo, hi); 201 202 function bitsAsArray(lowBits, highBits) { 203 return [lowBits >>> 0, highBits >>> 0]; 204 } 205 var reader = jspb.BinaryReader.alloc(writer.getResultBuffer()); 206 reader.nextField(); 207 expect(reader.getFieldNumber()).toEqual(1); 208 expect(reader.readSplitVarint64(bitsAsArray)).toEqual([0x1, 0x2]); 209 210 reader.nextField(); 211 expect(reader.getFieldNumber()).toEqual(1); 212 expect(reader.readSplitVarint64(bitsAsArray)).toEqual([ 213 0xFFFFFFFF, 0xFFFFFFFF 214 ]); 215 216 reader.nextField(); 217 expect(reader.getFieldNumber()).toEqual(2); 218 expect(reader.readSplitFixed64(bitsAsArray)).toEqual([0x1, 0x2]); 219 220 reader.nextField(); 221 expect(reader.getFieldNumber()).toEqual(2); 222 expect(reader.readSplitFixed64(bitsAsArray)).toEqual([ 223 0xFFFFFFF0, 0xFFFFFFFF 224 ]); 225 226 for (let i = 0; i < 3; i++) { 227 reader.nextField(); 228 expect(reader.getFieldNumber()).toEqual(3); 229 expect(reader.readSplitVarint64(bitsAsArray)).toEqual([i + 1, i + 2]); 230 } 231 232 for (let i = 0; i < 3; i++) { 233 reader.nextField(); 234 expect(reader.getFieldNumber()).toEqual(4); 235 expect(reader.readSplitFixed64(bitsAsArray)).toEqual([i + 1, i + 2]); 236 } 237 238 reader.nextField(); 239 expect(reader.getFieldNumber()).toEqual(5); 240 expect(reader.readPackedInt64String()).toEqual([ 241 String(2 * 2 ** 32 + 1), 242 String(3 * 2 ** 32 + 2), 243 String(4 * 2 ** 32 + 3), 244 ]); 245 246 reader.nextField(); 247 expect(reader.getFieldNumber()).toEqual(6); 248 expect(reader.readPackedFixed64String()).toEqual([ 249 String(2 * 2 ** 32 + 1), 250 String(3 * 2 ** 32 + 2), 251 String(4 * 2 ** 32 + 3), 252 ]); 253 }); 254 255 it('writes zigzag 64 fields', function() { 256 // Test cases directly from the protobuf dev guide. 257 // https://engdoc.corp.google.com/eng/howto/protocolbuffers/developerguide/encoding.shtml?cl=head#types 258 var testCases = [ 259 {original: '0', zigzag: '0'}, 260 {original: '-1', zigzag: '1'}, 261 {original: '1', zigzag: '2'}, 262 {original: '-2', zigzag: '3'}, 263 {original: '2147483647', zigzag: '4294967294'}, 264 {original: '-2147483648', zigzag: '4294967295'}, 265 // 64-bit extremes, not in dev guide. 266 {original: '9223372036854775807', zigzag: '18446744073709551614'}, 267 {original: '-9223372036854775808', zigzag: '18446744073709551615'}, 268 ]; 269 function decimalToLowBits(v) { 270 jspb.utils.splitDecimalString(v); 271 return jspb.utils.split64Low >>> 0; 272 } 273 function decimalToHighBits(v) { 274 jspb.utils.splitDecimalString(v); 275 return jspb.utils.split64High >>> 0; 276 } 277 278 var writer = new jspb.BinaryWriter(); 279 testCases.forEach(function(c) { 280 writer.writeSint64String(1, c.original); 281 writer.writeSintHash64(1, jspb.utils.decimalStringToHash64(c.original)); 282 jspb.utils.splitDecimalString(c.original); 283 writer.writeSplitZigzagVarint64( 284 1, jspb.utils.split64Low, jspb.utils.split64High); 285 }); 286 287 writer.writeRepeatedSint64String(2, testCases.map(function(c) { 288 return c.original; 289 })); 290 291 writer.writeRepeatedSintHash64(3, testCases.map(function(c) { 292 return jspb.utils.decimalStringToHash64(c.original); 293 })); 294 295 writer.writeRepeatedSplitZigzagVarint64( 296 4, testCases.map(function(c) { 297 return c.original; 298 }), 299 decimalToLowBits, decimalToHighBits); 300 301 writer.writePackedSint64String(5, testCases.map(function(c) { 302 return c.original; 303 })); 304 305 writer.writePackedSintHash64(6, testCases.map(function(c) { 306 return jspb.utils.decimalStringToHash64(c.original); 307 })); 308 309 writer.writePackedSplitZigzagVarint64( 310 7, testCases.map(function(c) { 311 return c.original; 312 }), 313 decimalToLowBits, decimalToHighBits); 314 315 // Verify by reading the stream as normal int64 fields and checking with 316 // the canonical zigzag encoding of each value. 317 var reader = jspb.BinaryReader.alloc(writer.getResultBuffer()); 318 testCases.forEach(function(c) { 319 reader.nextField(); 320 expect(reader.getFieldNumber()).toEqual(1); 321 expect(reader.readUint64String()).toEqual(c.zigzag); 322 reader.nextField(); 323 expect(reader.getFieldNumber()).toEqual(1); 324 expect(reader.readUint64String()).toEqual(c.zigzag); 325 reader.nextField(); 326 expect(reader.getFieldNumber()).toEqual(1); 327 expect(reader.readUint64String()).toEqual(c.zigzag); 328 }); 329 330 testCases.forEach(function(c) { 331 reader.nextField(); 332 expect(reader.getFieldNumber()).toEqual(2); 333 expect(reader.readUint64String()).toEqual(c.zigzag); 334 }); 335 336 testCases.forEach(function(c) { 337 reader.nextField(); 338 expect(reader.getFieldNumber()).toEqual(3); 339 expect(reader.readUint64String()).toEqual(c.zigzag); 340 }); 341 342 testCases.forEach(function(c) { 343 reader.nextField(); 344 expect(reader.getFieldNumber()).toEqual(4); 345 expect(reader.readUint64String()).toEqual(c.zigzag); 346 }); 347 348 reader.nextField(); 349 expect(reader.getFieldNumber()).toEqual(5); 350 expect(reader.readPackedUint64String()).toEqual(testCases.map(function(c) { 351 return c.zigzag; 352 })); 353 354 reader.nextField(); 355 expect(reader.getFieldNumber()).toEqual(6); 356 expect(reader.readPackedUint64String()).toEqual(testCases.map(function(c) { 357 return c.zigzag; 358 })); 359 360 reader.nextField(); 361 expect(reader.getFieldNumber()).toEqual(7); 362 expect(reader.readPackedUint64String()).toEqual(testCases.map(function(c) { 363 return c.zigzag; 364 })); 365 }); 366 367 it('writes float32 fields', function() { 368 var testCases = [ 369 0, 1, -1, jspb.BinaryConstants.FLOAT32_MIN, 370 -jspb.BinaryConstants.FLOAT32_MIN, jspb.BinaryConstants.FLOAT32_MAX, 371 -jspb.BinaryConstants.FLOAT32_MAX, 3.1415927410125732, Infinity, 372 -Infinity, NaN 373 ]; 374 var writer = new jspb.BinaryWriter(); 375 testCases.forEach(function(f) { 376 writer.writeFloat(1, f); 377 }); 378 var reader = jspb.BinaryReader.alloc(writer.getResultBuffer()); 379 testCases.forEach(function(f) { 380 reader.nextField(); 381 expect(reader.getFieldNumber()).toEqual(1); 382 if (isNaN(f)) { 383 expect(isNaN(reader.readFloat())).toEqual(true); 384 } else { 385 expect(reader.readFloat()).toEqual(f); 386 } 387 }); 388 }); 389 390 it('writes double fields', function() { 391 var testCases = [ 392 0, 1, -1, Number.MAX_SAFE_INTEGER, Number.MIN_SAFE_INTEGER, 393 Number.MAX_VALUE, Number.MIN_VALUE, jspb.BinaryConstants.FLOAT32_MIN, 394 -jspb.BinaryConstants.FLOAT32_MIN, jspb.BinaryConstants.FLOAT32_MAX, 395 -jspb.BinaryConstants.FLOAT32_MAX, Math.PI, Infinity, -Infinity, NaN 396 ]; 397 var writer = new jspb.BinaryWriter(); 398 testCases.forEach(function(f) { 399 writer.writeDouble(1, f); 400 }); 401 var reader = jspb.BinaryReader.alloc(writer.getResultBuffer()); 402 testCases.forEach(function(f) { 403 reader.nextField(); 404 expect(reader.getFieldNumber()).toEqual(1); 405 if (isNaN(f)) { 406 expect(isNaN(reader.readDouble())).toEqual(true); 407 } else { 408 expect(reader.readDouble()).toEqual(f); 409 } 410 }); 411 }); 412}); 413