blob: 5c330791aeaeb58ef6212c3c24b07979c6538a37 [file] [log] [blame]
Feng Xiaoe841bac2015-12-11 17:09:20 -08001// 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 helper functions.
33 *
34 * Test suite is written using Jasmine -- see http://jasmine.github.io/
35 *
36 * @author aappleby@google.com (Austin Appleby)
37 */
38
39goog.require('goog.crypt.base64');
40goog.require('goog.testing.asserts');
41goog.require('jspb.BinaryConstants');
42goog.require('jspb.BinaryWriter');
43goog.require('jspb.utils');
44
45
46/**
47 * @param {number} x
48 * @return {number}
49 */
50function truncate(x) {
51 var temp = new Float32Array(1);
52 temp[0] = x;
53 return temp[0];
54}
55
56
57/**
58 * Converts an 64-bit integer in split representation to a 64-bit hash string
59 * (8 bits encoded per character).
60 * @param {number} bitsLow The low 32 bits of the split 64-bit integer.
61 * @param {number} bitsHigh The high 32 bits of the split 64-bit integer.
62 * @return {string} The encoded hash string, 8 bits per character.
63 */
64function toHashString(bitsLow, bitsHigh) {
65 return String.fromCharCode((bitsLow >>> 0) & 0xFF,
66 (bitsLow >>> 8) & 0xFF,
67 (bitsLow >>> 16) & 0xFF,
68 (bitsLow >>> 24) & 0xFF,
69 (bitsHigh >>> 0) & 0xFF,
70 (bitsHigh >>> 8) & 0xFF,
71 (bitsHigh >>> 16) & 0xFF,
72 (bitsHigh >>> 24) & 0xFF);
73}
74
75
76describe('binaryUtilsTest', function() {
77 /**
78 * Tests lossless binary-to-decimal conversion.
79 */
80 it('testDecimalConversion', function() {
81 // Check some magic numbers.
82 var result =
83 jspb.utils.joinUnsignedDecimalString(0x89e80001, 0x8ac72304);
84 assertEquals('10000000000000000001', result);
85
86 result = jspb.utils.joinUnsignedDecimalString(0xacd05f15, 0x1b69b4b);
87 assertEquals('123456789123456789', result);
88
89 result = jspb.utils.joinUnsignedDecimalString(0xeb1f0ad2, 0xab54a98c);
90 assertEquals('12345678901234567890', result);
91
92 result = jspb.utils.joinUnsignedDecimalString(0xe3b70cb1, 0x891087b8);
93 assertEquals('9876543210987654321', result);
94
95 // Check limits.
96 result = jspb.utils.joinUnsignedDecimalString(0x00000000, 0x00000000);
97 assertEquals('0', result);
98
99 result = jspb.utils.joinUnsignedDecimalString(0xFFFFFFFF, 0xFFFFFFFF);
100 assertEquals('18446744073709551615', result);
101
102 // Check each bit of the low dword.
103 for (var i = 0; i < 32; i++) {
104 var low = (1 << i) >>> 0;
105 result = jspb.utils.joinUnsignedDecimalString(low, 0);
106 assertEquals('' + Math.pow(2, i), result);
107 }
108
109 // Check the first 20 bits of the high dword.
110 for (var i = 0; i < 20; i++) {
111 var high = (1 << i) >>> 0;
112 result = jspb.utils.joinUnsignedDecimalString(0, high);
113 assertEquals('' + Math.pow(2, 32 + i), result);
114 }
115
116 // V8's internal double-to-string conversion is inaccurate for values above
117 // 2^52, even if they're representable integers - check the rest of the bits
118 // manually against the correct string representations of 2^N.
119
120 result = jspb.utils.joinUnsignedDecimalString(0x00000000, 0x00100000);
121 assertEquals('4503599627370496', result);
122
123 result = jspb.utils.joinUnsignedDecimalString(0x00000000, 0x00200000);
124 assertEquals('9007199254740992', result);
125
126 result = jspb.utils.joinUnsignedDecimalString(0x00000000, 0x00400000);
127 assertEquals('18014398509481984', result);
128
129 result = jspb.utils.joinUnsignedDecimalString(0x00000000, 0x00800000);
130 assertEquals('36028797018963968', result);
131
132 result = jspb.utils.joinUnsignedDecimalString(0x00000000, 0x01000000);
133 assertEquals('72057594037927936', result);
134
135 result = jspb.utils.joinUnsignedDecimalString(0x00000000, 0x02000000);
136 assertEquals('144115188075855872', result);
137
138 result = jspb.utils.joinUnsignedDecimalString(0x00000000, 0x04000000);
139 assertEquals('288230376151711744', result);
140
141 result = jspb.utils.joinUnsignedDecimalString(0x00000000, 0x08000000);
142 assertEquals('576460752303423488', result);
143
144 result = jspb.utils.joinUnsignedDecimalString(0x00000000, 0x10000000);
145 assertEquals('1152921504606846976', result);
146
147 result = jspb.utils.joinUnsignedDecimalString(0x00000000, 0x20000000);
148 assertEquals('2305843009213693952', result);
149
150 result = jspb.utils.joinUnsignedDecimalString(0x00000000, 0x40000000);
151 assertEquals('4611686018427387904', result);
152
153 result = jspb.utils.joinUnsignedDecimalString(0x00000000, 0x80000000);
154 assertEquals('9223372036854775808', result);
155 });
156
157
158 /**
159 * Going from hash strings to decimal strings should also be lossless.
160 */
161 it('testHashToDecimalConversion', function() {
162 var result;
163 var convert = jspb.utils.hash64ToDecimalString;
164
165 result = convert(toHashString(0x00000000, 0x00000000), false);
166 assertEquals('0', result);
167
168 result = convert(toHashString(0x00000000, 0x00000000), true);
169 assertEquals('0', result);
170
171 result = convert(toHashString(0xFFFFFFFF, 0xFFFFFFFF), false);
172 assertEquals('18446744073709551615', result);
173
174 result = convert(toHashString(0xFFFFFFFF, 0xFFFFFFFF), true);
175 assertEquals('-1', result);
176
177 result = convert(toHashString(0x00000000, 0x80000000), false);
178 assertEquals('9223372036854775808', result);
179
180 result = convert(toHashString(0x00000000, 0x80000000), true);
181 assertEquals('-9223372036854775808', result);
182
183 result = convert(toHashString(0xacd05f15, 0x01b69b4b), false);
184 assertEquals('123456789123456789', result);
185
186 result = convert(toHashString(~0xacd05f15 + 1, ~0x01b69b4b), true);
187 assertEquals('-123456789123456789', result);
188
189 // And converting arrays of hashes should work the same way.
190 result = jspb.utils.hash64ArrayToDecimalStrings([
191 toHashString(0xFFFFFFFF, 0xFFFFFFFF),
192 toHashString(0x00000000, 0x80000000),
193 toHashString(0xacd05f15, 0x01b69b4b)], false);
194 assertEquals(3, result.length);
195 assertEquals('18446744073709551615', result[0]);
196 assertEquals('9223372036854775808', result[1]);
197 assertEquals('123456789123456789', result[2]);
198 });
199
200
201 /**
202 * Going from hash strings to hex strings should be lossless.
203 */
204 it('testHashToHexConversion', function() {
205 var result;
206 var convert = jspb.utils.hash64ToHexString;
207
208 result = convert(toHashString(0x00000000, 0x00000000));
209 assertEquals('0x0000000000000000', result);
210
211 result = convert(toHashString(0xFFFFFFFF, 0xFFFFFFFF));
212 assertEquals('0xffffffffffffffff', result);
213
214 result = convert(toHashString(0x12345678, 0x9ABCDEF0));
215 assertEquals('0x9abcdef012345678', result);
216 });
217
218
219 /**
220 * Going from hex strings to hash strings should be lossless.
221 */
222 it('testHexToHashConversion', function() {
223 var result;
224 var convert = jspb.utils.hexStringToHash64;
225
226 result = convert('0x0000000000000000');
227 assertEquals(String.fromCharCode.apply(null,
228 [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]), result);
229
230 result = convert('0xffffffffffffffff');
231 assertEquals(String.fromCharCode.apply(null,
232 [0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF]), result);
233
234 // Hex string is big-endian, hash string is little-endian.
235 result = convert('0x123456789ABCDEF0');
236 assertEquals(String.fromCharCode.apply(null,
237 [0xF0, 0xDE, 0xBC, 0x9A, 0x78, 0x56, 0x34, 0x12]), result);
238
239 // Capitalization should not matter.
240 result = convert('0x0000abcdefABCDEF');
241 assertEquals(String.fromCharCode.apply(null,
242 [0xEF, 0xCD, 0xAB, 0xEF, 0xCD, 0xAB, 0x00, 0x00]), result);
243 });
244
245
246 /**
247 * Going from numbers to hash strings should be lossless for up to 53 bits of
248 * precision.
249 */
250 it('testNumberToHashConversion', function() {
251 var result;
252 var convert = jspb.utils.numberToHash64;
253
254 result = convert(0x0000000000000);
255 assertEquals('0x0000000000000000', jspb.utils.hash64ToHexString(result));
256
257 result = convert(0xFFFFFFFFFFFFF);
258 assertEquals('0x000fffffffffffff', jspb.utils.hash64ToHexString(result));
259
260 result = convert(0x123456789ABCD);
261 assertEquals('0x000123456789abcd', jspb.utils.hash64ToHexString(result));
262
263 result = convert(0xDCBA987654321);
264 assertEquals('0x000dcba987654321', jspb.utils.hash64ToHexString(result));
265
266 // 53 bits of precision should not be truncated.
267 result = convert(0x10000000000001);
268 assertEquals('0x0010000000000001', jspb.utils.hash64ToHexString(result));
269
270 // 54 bits of precision should be truncated.
271 result = convert(0x20000000000001);
272 assertNotEquals(
273 '0x0020000000000001', jspb.utils.hash64ToHexString(result));
274 });
275
276
277 /**
278 * Sanity check the behavior of Javascript's strings when doing funny things
279 * with unicode characters.
280 */
281 it('sanityCheckUnicodeStrings', function() {
282 var strings = new Array(65536);
283
284 // All possible unsigned 16-bit values should be storable in a string, they
285 // shouldn't do weird things with the length of the string, and they should
286 // come back out of the string unchanged.
287 for (var i = 0; i < 65536; i++) {
288 strings[i] = 'a' + String.fromCharCode(i) + 'a';
289 if (3 != strings[i].length) throw 'fail!';
290 if (i != strings[i].charCodeAt(1)) throw 'fail!';
291 }
292
293 // Each unicode character should compare equal to itself and not equal to a
294 // different unicode character.
295 for (var i = 0; i < 65536; i++) {
296 if (strings[i] != strings[i]) throw 'fail!';
297 if (strings[i] == strings[(i + 1) % 65536]) throw 'fail!';
298 }
299 });
300
301
302 /**
303 * Tests conversion from 32-bit floating point numbers to split64 numbers.
304 */
305 it('testFloat32ToSplit64', function() {
306 var f32_eps = jspb.BinaryConstants.FLOAT32_EPS;
307 var f32_min = jspb.BinaryConstants.FLOAT32_MIN;
308 var f32_max = jspb.BinaryConstants.FLOAT32_MAX;
309
310 // NaN.
311 jspb.utils.splitFloat32(NaN);
312 if (!isNaN(jspb.utils.joinFloat32(jspb.utils.split64Low,
313 jspb.utils.split64High))) {
314 throw 'fail!';
315 }
316
317 /**
318 * @param {number} x
319 * @param {number=} opt_bits
320 */
321 function test(x, opt_bits) {
322 jspb.utils.splitFloat32(x);
323 if (goog.isDef(opt_bits)) {
324 if (opt_bits != jspb.utils.split64Low) throw 'fail!';
325 }
326 if (truncate(x) != jspb.utils.joinFloat32(jspb.utils.split64Low,
327 jspb.utils.split64High)) {
328 throw 'fail!';
329 }
330 }
331
332 // Positive and negative infinity.
333 test(Infinity, 0x7f800000);
334 test(-Infinity, 0xff800000);
335
336 // Positive and negative zero.
337 test(0, 0x00000000);
338 test(-0, 0x80000000);
339
340 // Positive and negative epsilon.
341 test(f32_eps, 0x00000001);
342 test(-f32_eps, 0x80000001);
343
344 // Positive and negative min.
345 test(f32_min, 0x00800000);
346 test(-f32_min, 0x80800000);
347
348 // Positive and negative max.
349 test(f32_max, 0x7F7FFFFF);
350 test(-f32_max, 0xFF7FFFFF);
351
352 // Various positive values.
353 var cursor = f32_eps * 10;
354 while (cursor != Infinity) {
355 test(cursor);
356 cursor *= 1.1;
357 }
358
359 // Various negative values.
360 cursor = -f32_eps * 10;
361 while (cursor != -Infinity) {
362 test(cursor);
363 cursor *= 1.1;
364 }
365 });
366
367
368 /**
369 * Tests conversion from 64-bit floating point numbers to split64 numbers.
370 */
371 it('testFloat64ToSplit64', function() {
372 var f64_eps = jspb.BinaryConstants.FLOAT64_EPS;
373 var f64_min = jspb.BinaryConstants.FLOAT64_MIN;
374 var f64_max = jspb.BinaryConstants.FLOAT64_MAX;
375
376 // NaN.
377 jspb.utils.splitFloat64(NaN);
378 if (!isNaN(jspb.utils.joinFloat64(jspb.utils.split64Low,
379 jspb.utils.split64High))) {
380 throw 'fail!';
381 }
382
383 /**
384 * @param {number} x
385 * @param {number=} opt_highBits
386 * @param {number=} opt_lowBits
387 */
388 function test(x, opt_highBits, opt_lowBits) {
389 jspb.utils.splitFloat64(x);
390 if (goog.isDef(opt_highBits)) {
391 if (opt_highBits != jspb.utils.split64High) throw 'fail!';
392 }
393 if (goog.isDef(opt_lowBits)) {
394 if (opt_lowBits != jspb.utils.split64Low) throw 'fail!';
395 }
396 if (x != jspb.utils.joinFloat64(jspb.utils.split64Low,
397 jspb.utils.split64High)) {
398 throw 'fail!';
399 }
400 }
401
402 // Positive and negative infinity.
403 test(Infinity, 0x7ff00000, 0x00000000);
404 test(-Infinity, 0xfff00000, 0x00000000);
405
406 // Positive and negative zero.
407 test(0, 0x00000000, 0x00000000);
408 test(-0, 0x80000000, 0x00000000);
409
410 // Positive and negative epsilon.
411 test(f64_eps, 0x00000000, 0x00000001);
412 test(-f64_eps, 0x80000000, 0x00000001);
413
414 // Positive and negative min.
415 test(f64_min, 0x00100000, 0x00000000);
416 test(-f64_min, 0x80100000, 0x00000000);
417
418 // Positive and negative max.
419 test(f64_max, 0x7FEFFFFF, 0xFFFFFFFF);
420 test(-f64_max, 0xFFEFFFFF, 0xFFFFFFFF);
421
422 // Various positive values.
423 var cursor = f64_eps * 10;
424 while (cursor != Infinity) {
425 test(cursor);
426 cursor *= 1.1;
427 }
428
429 // Various negative values.
430 cursor = -f64_eps * 10;
431 while (cursor != -Infinity) {
432 test(cursor);
433 cursor *= 1.1;
434 }
435 });
436
437
438 /**
439 * Tests counting packed varints.
440 */
441 it('testCountVarints', function() {
442 var writer = new jspb.BinaryWriter();
443
444 var count = 0;
445 for (var i = 1; i < 1000000000; i *= 1.1) {
446 writer.rawWriteVarint(Math.floor(i));
447 count++;
448 }
449
450 var buffer = new Uint8Array(writer.getResultBuffer());
451 assertEquals(count, jspb.utils.countVarints(buffer, 0, buffer.length));
452 });
453
454
455 /**
456 * Tests counting matching varint fields.
457 */
458 it('testCountVarintFields', function() {
459 var writer = new jspb.BinaryWriter();
460
461 var count = 0;
462 for (var i = 1; i < 1000000000; i *= 1.1) {
463 writer.writeUint64(1, Math.floor(i));
464 count++;
465 }
466 writer.writeString(2, 'terminator');
467
468 var buffer = new Uint8Array(writer.getResultBuffer());
469 assertEquals(count,
470 jspb.utils.countVarintFields(buffer, 0, buffer.length, 1));
471
472 writer = new jspb.BinaryWriter();
473
474 count = 0;
475 for (var i = 1; i < 1000000000; i *= 1.1) {
476 writer.writeUint64(123456789, Math.floor(i));
477 count++;
478 }
479 writer.writeString(2, 'terminator');
480
481 buffer = new Uint8Array(writer.getResultBuffer());
482 assertEquals(count,
483 jspb.utils.countVarintFields(buffer, 0, buffer.length, 123456789));
484 });
485
486
487 /**
488 * Tests counting matching fixed32 fields.
489 */
490 it('testCountFixed32Fields', function() {
491 var writer = new jspb.BinaryWriter();
492
493 var count = 0;
494 for (var i = 1; i < 1000000000; i *= 1.1) {
495 writer.writeFixed32(1, Math.floor(i));
496 count++;
497 }
498 writer.writeString(2, 'terminator');
499
500 var buffer = new Uint8Array(writer.getResultBuffer());
501 assertEquals(count,
502 jspb.utils.countFixed32Fields(buffer, 0, buffer.length, 1));
503
504 writer = new jspb.BinaryWriter();
505
506 count = 0;
507 for (var i = 1; i < 1000000000; i *= 1.1) {
508 writer.writeFixed32(123456789, Math.floor(i));
509 count++;
510 }
511 writer.writeString(2, 'terminator');
512
513 buffer = new Uint8Array(writer.getResultBuffer());
514 assertEquals(count,
515 jspb.utils.countFixed32Fields(buffer, 0, buffer.length, 123456789));
516 });
517
518
519 /**
520 * Tests counting matching fixed64 fields.
521 */
522 it('testCountFixed64Fields', function() {
523 var writer = new jspb.BinaryWriter();
524
525 var count = 0;
526 for (var i = 1; i < 1000000000; i *= 1.1) {
527 writer.writeDouble(1, i);
528 count++;
529 }
530 writer.writeString(2, 'terminator');
531
532 var buffer = new Uint8Array(writer.getResultBuffer());
533 assertEquals(count,
534 jspb.utils.countFixed64Fields(buffer, 0, buffer.length, 1));
535
536 writer = new jspb.BinaryWriter();
537
538 count = 0;
539 for (var i = 1; i < 1000000000; i *= 1.1) {
540 writer.writeDouble(123456789, i);
541 count++;
542 }
543 writer.writeString(2, 'terminator');
544
545 buffer = new Uint8Array(writer.getResultBuffer());
546 assertEquals(count,
547 jspb.utils.countFixed64Fields(buffer, 0, buffer.length, 123456789));
548 });
549
550
551 /**
552 * Tests counting matching delimited fields.
553 */
554 it('testCountDelimitedFields', function() {
555 var writer = new jspb.BinaryWriter();
556
557 var count = 0;
558 for (var i = 1; i < 1000; i *= 1.1) {
559 writer.writeBytes(1, [Math.floor(i)]);
560 count++;
561 }
562 writer.writeString(2, 'terminator');
563
564 var buffer = new Uint8Array(writer.getResultBuffer());
565 assertEquals(count,
566 jspb.utils.countDelimitedFields(buffer, 0, buffer.length, 1));
567
568 writer = new jspb.BinaryWriter();
569
570 count = 0;
571 for (var i = 1; i < 1000; i *= 1.1) {
572 writer.writeBytes(123456789, [Math.floor(i)]);
573 count++;
574 }
575 writer.writeString(2, 'terminator');
576
577 buffer = new Uint8Array(writer.getResultBuffer());
578 assertEquals(count,
579 jspb.utils.countDelimitedFields(buffer, 0, buffer.length, 123456789));
580 });
581
582
583 /**
584 * Tests byte format for debug strings.
585 */
586 it('testDebugBytesToTextFormat', function() {
587 assertEquals('""', jspb.utils.debugBytesToTextFormat(null));
588 assertEquals('"\\x00\\x10\\xff"',
589 jspb.utils.debugBytesToTextFormat([0, 16, 255]));
590 });
591
592
593 /**
594 * Tests converting byte blob sources into byte blobs.
595 */
596 it('testByteSourceToUint8Array', function() {
597 var convert = jspb.utils.byteSourceToUint8Array;
598
599 var sourceData = [];
600 for (var i = 0; i < 256; i++) {
601 sourceData.push(i);
602 }
603
604 var sourceBytes = new Uint8Array(sourceData);
605 var sourceBuffer = sourceBytes.buffer;
606 var sourceBase64 = goog.crypt.base64.encodeByteArray(sourceData);
607 var sourceString = String.fromCharCode.apply(null, sourceData);
608
609 function check(result) {
610 assertEquals(Uint8Array, result.constructor);
611 assertEquals(sourceData.length, result.length);
612 for (var i = 0; i < result.length; i++) {
613 assertEquals(sourceData[i], result[i]);
614 }
615 }
616
617 // Converting Uint8Arrays into Uint8Arrays should be a no-op.
618 assertEquals(sourceBytes, convert(sourceBytes));
619
620 // Converting Array.<numbers> into Uint8Arrays should work.
621 check(convert(sourceData));
622
623 // Converting ArrayBuffers into Uint8Arrays should work.
624 check(convert(sourceBuffer));
625
626 // Converting base64-encoded strings into Uint8Arrays should work.
627 check(convert(sourceBase64));
628
629 // Converting binary-data strings into Uint8Arrays should work.
630 check(convert(sourceString, /* opt_stringIsRawBytes = */ true));
631 });
632});