From 09354db1434859a31a3c81abebcc4018d42f2715 Mon Sep 17 00:00:00 2001 From: Jisi Liu Date: Tue, 18 Jul 2017 15:38:30 -0700 Subject: Merge from Google internal for 3.4 release --- js/binary/encoder.js | 8 +- js/binary/utils.js | 4 - js/binary/writer.js | 10 +- js/compatibility_tests/v3.1.0/binary/arith_test.js | 355 +++++++ .../v3.1.0/binary/decoder_test.js | 334 +++++++ js/compatibility_tests/v3.1.0/binary/proto_test.js | 628 ++++++++++++ .../v3.1.0/binary/reader_test.js | 922 +++++++++++++++++ js/compatibility_tests/v3.1.0/binary/utils_test.js | 668 +++++++++++++ .../v3.1.0/binary/writer_test.js | 122 +++ .../v3.1.0/commonjs/test6/test6.proto | 40 + .../v3.1.0/commonjs/test7/test7.proto | 42 + js/compatibility_tests/v3.1.0/data.proto | 51 + js/compatibility_tests/v3.1.0/debug_test.js | 105 ++ js/compatibility_tests/v3.1.0/maps_test.js | 301 ++++++ js/compatibility_tests/v3.1.0/message_test.js | 1037 ++++++++++++++++++++ js/compatibility_tests/v3.1.0/proto3_test.js | 329 +++++++ js/compatibility_tests/v3.1.0/proto3_test.proto | 89 ++ js/compatibility_tests/v3.1.0/test.proto | 262 +++++ js/compatibility_tests/v3.1.0/test2.proto | 54 + js/compatibility_tests/v3.1.0/test3.proto | 53 + js/compatibility_tests/v3.1.0/test4.proto | 42 + js/compatibility_tests/v3.1.0/test5.proto | 44 + js/compatibility_tests/v3.1.0/testbinary.proto | 212 ++++ js/compatibility_tests/v3.1.0/testempty.proto | 34 + js/message.js | 86 +- js/message_test.js | 18 +- js/proto3_test.proto | 1 + js/test.proto | 8 + 28 files changed, 5801 insertions(+), 58 deletions(-) create mode 100644 js/compatibility_tests/v3.1.0/binary/arith_test.js create mode 100644 js/compatibility_tests/v3.1.0/binary/decoder_test.js create mode 100644 js/compatibility_tests/v3.1.0/binary/proto_test.js create mode 100644 js/compatibility_tests/v3.1.0/binary/reader_test.js create mode 100644 js/compatibility_tests/v3.1.0/binary/utils_test.js create mode 100644 js/compatibility_tests/v3.1.0/binary/writer_test.js create mode 100644 js/compatibility_tests/v3.1.0/commonjs/test6/test6.proto create mode 100644 js/compatibility_tests/v3.1.0/commonjs/test7/test7.proto create mode 100644 js/compatibility_tests/v3.1.0/data.proto create mode 100644 js/compatibility_tests/v3.1.0/debug_test.js create mode 100644 js/compatibility_tests/v3.1.0/maps_test.js create mode 100644 js/compatibility_tests/v3.1.0/message_test.js create mode 100644 js/compatibility_tests/v3.1.0/proto3_test.js create mode 100644 js/compatibility_tests/v3.1.0/proto3_test.proto create mode 100644 js/compatibility_tests/v3.1.0/test.proto create mode 100644 js/compatibility_tests/v3.1.0/test2.proto create mode 100644 js/compatibility_tests/v3.1.0/test3.proto create mode 100644 js/compatibility_tests/v3.1.0/test4.proto create mode 100644 js/compatibility_tests/v3.1.0/test5.proto create mode 100644 js/compatibility_tests/v3.1.0/testbinary.proto create mode 100644 js/compatibility_tests/v3.1.0/testempty.proto (limited to 'js') diff --git a/js/binary/encoder.js b/js/binary/encoder.js index f25935f1..8e9f5bbc 100644 --- a/js/binary/encoder.js +++ b/js/binary/encoder.js @@ -390,11 +390,13 @@ jspb.BinaryEncoder.prototype.writeDouble = function(value) { /** - * Writes a boolean value to the buffer as a varint. - * @param {boolean} value The value to write. + * Writes a boolean value to the buffer as a varint. We allow numbers as input + * because the JSPB code generator uses 0/1 instead of true/false to save space + * in the string representation of the proto. + * @param {boolean|number} value The value to write. */ jspb.BinaryEncoder.prototype.writeBool = function(value) { - goog.asserts.assert(goog.isBoolean(value)); + goog.asserts.assert(goog.isBoolean(value) || goog.isNumber(value)); this.buffer_.push(value ? 1 : 0); }; diff --git a/js/binary/utils.js b/js/binary/utils.js index 7702020b..25131b06 100644 --- a/js/binary/utils.js +++ b/js/binary/utils.js @@ -970,10 +970,6 @@ jspb.utils.byteSourceToUint8Array = function(data) { return /** @type {!Uint8Array} */(new Uint8Array(data)); } - if (data.constructor === Buffer) { - return /** @type {!Uint8Array} */(new Uint8Array(data)); - } - if (data.constructor === Array) { data = /** @type {!Array.} */(data); return /** @type {!Uint8Array} */(new Uint8Array(data)); diff --git a/js/binary/writer.js b/js/binary/writer.js index 672e94bd..037e92b2 100644 --- a/js/binary/writer.js +++ b/js/binary/writer.js @@ -235,7 +235,7 @@ jspb.BinaryWriter.prototype.getResultBuffer = function() { /** - * Converts the encoded data into a bas64-encoded string. + * Converts the encoded data into a base64-encoded string. * @return {string} */ jspb.BinaryWriter.prototype.getResultBase64String = function() { @@ -716,13 +716,15 @@ jspb.BinaryWriter.prototype.writeDouble = function(field, value) { /** - * Writes a boolean field to the buffer. + * Writes a boolean field to the buffer. We allow numbers as input + * because the JSPB code generator uses 0/1 instead of true/false to save space + * in the string representation of the proto. * @param {number} field The field number. - * @param {boolean?} value The value to write. + * @param {boolean?|number?} value The value to write. */ jspb.BinaryWriter.prototype.writeBool = function(field, value) { if (value == null) return; - goog.asserts.assert(goog.isBoolean(value)); + goog.asserts.assert(goog.isBoolean(value) || goog.isNumber(value)); this.writeFieldHeader_(field, jspb.BinaryConstants.WireType.VARINT); this.encoder_.writeBool(value); }; diff --git a/js/compatibility_tests/v3.1.0/binary/arith_test.js b/js/compatibility_tests/v3.1.0/binary/arith_test.js new file mode 100644 index 00000000..89796bf7 --- /dev/null +++ b/js/compatibility_tests/v3.1.0/binary/arith_test.js @@ -0,0 +1,355 @@ +// 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 Int64-manipulation functions. + * + * Test suite is written using Jasmine -- see http://jasmine.github.io/ + * + * @author cfallin@google.com (Chris Fallin) + */ + +goog.require('goog.testing.asserts'); +goog.require('jspb.arith.Int64'); +goog.require('jspb.arith.UInt64'); + + +describe('binaryArithTest', function() { + /** + * Tests comparison operations. + */ + it('testCompare', function() { + var a = new jspb.arith.UInt64(1234, 5678); + var b = new jspb.arith.UInt64(1234, 5678); + assertEquals(a.cmp(b), 0); + assertEquals(b.cmp(a), 0); + b.lo -= 1; + assertEquals(a.cmp(b), 1); + assertEquals(b.cmp(a), -1); + b.lo += 2; + assertEquals(a.cmp(b), -1); + assertEquals(b.cmp(a), 1); + b.lo = a.lo; + b.hi = a.hi - 1; + assertEquals(a.cmp(b), 1); + assertEquals(b.cmp(a), -1); + + assertEquals(a.zero(), false); + assertEquals(a.msb(), false); + assertEquals(a.lsb(), false); + a.hi = 0; + a.lo = 0; + assertEquals(a.zero(), true); + a.hi = 0x80000000; + assertEquals(a.zero(), false); + assertEquals(a.msb(), true); + a.lo = 0x00000001; + assertEquals(a.lsb(), true); + }); + + + /** + * Tests shifts. + */ + it('testShifts', function() { + var a = new jspb.arith.UInt64(1, 0); + assertEquals(a.lo, 1); + assertEquals(a.hi, 0); + var orig = a; + a = a.leftShift(); + assertEquals(orig.lo, 1); // original unmodified. + assertEquals(orig.hi, 0); + assertEquals(a.lo, 2); + assertEquals(a.hi, 0); + a = a.leftShift(); + assertEquals(a.lo, 4); + assertEquals(a.hi, 0); + for (var i = 0; i < 29; i++) { + a = a.leftShift(); + } + assertEquals(a.lo, 0x80000000); + assertEquals(a.hi, 0); + a = a.leftShift(); + assertEquals(a.lo, 0); + assertEquals(a.hi, 1); + a = a.leftShift(); + assertEquals(a.lo, 0); + assertEquals(a.hi, 2); + a = a.rightShift(); + a = a.rightShift(); + assertEquals(a.lo, 0x80000000); + assertEquals(a.hi, 0); + a = a.rightShift(); + assertEquals(a.lo, 0x40000000); + assertEquals(a.hi, 0); + }); + + + /** + * Tests additions. + */ + it('testAdd', function() { + var a = new jspb.arith.UInt64(/* lo = */ 0x89abcdef, + /* hi = */ 0x01234567); + var b = new jspb.arith.UInt64(/* lo = */ 0xff52ab91, + /* hi = */ 0x92fa2123); + // Addition with carry. + var c = a.add(b); + assertEquals(a.lo, 0x89abcdef); // originals unmodified. + assertEquals(a.hi, 0x01234567); + assertEquals(b.lo, 0xff52ab91); + assertEquals(b.hi, 0x92fa2123); + assertEquals(c.lo, 0x88fe7980); + assertEquals(c.hi, 0x941d668b); + + // Simple addition without carry. + a.lo = 2; + a.hi = 0; + b.lo = 3; + b.hi = 0; + c = a.add(b); + assertEquals(c.lo, 5); + assertEquals(c.hi, 0); + }); + + + /** + * Test subtractions. + */ + it('testSub', function() { + var kLength = 10; + var hiValues = [0x1682ef32, + 0x583902f7, + 0xb62f5955, + 0x6ea99bbf, + 0x25a39c20, + 0x0700a08b, + 0x00f7304d, + 0x91a5b5af, + 0x89077fd2, + 0xe09e347c]; + var loValues = [0xe1538b18, + 0xbeacd556, + 0x74100758, + 0x96e3cb26, + 0x56c37c3f, + 0xe00b3f7d, + 0x859f25d7, + 0xc2ee614a, + 0xe1d21cd7, + 0x30aae6a4]; + for (var i = 0; i < kLength; i++) { + for (var j = 0; j < kLength; j++) { + var a = new jspb.arith.UInt64(loValues[i], hiValues[j]); + var b = new jspb.arith.UInt64(loValues[j], hiValues[i]); + var c = a.add(b).sub(b); + assertEquals(c.hi, a.hi); + assertEquals(c.lo, a.lo); + } + } + }); + + + /** + * Tests 32-by-32 multiplication. + */ + it('testMul32x32', function() { + var testData = [ + // a b low(a*b) high(a*b) + [0xc0abe2f8, 0x1607898a, 0x5de711b0, 0x109471b8], + [0x915eb3cb, 0x4fb66d0e, 0xbd0d441a, 0x2d43d0bc], + [0xfe4efe70, 0x80b48c37, 0xbcddea10, 0x7fdada0c], + [0xe222fd4a, 0xe43d524a, 0xd5e0eb64, 0xc99d549c], + [0xd171f469, 0xb94ebd01, 0x4be17969, 0x979bc4fa], + [0x829cc1df, 0xe2598b38, 0xf4157dc8, 0x737c12ad], + [0xf10c3767, 0x8382881e, 0x942b3612, 0x7bd428b8], + [0xb0f6dd24, 0x232597e1, 0x079c98a4, 0x184bbce7], + [0xfcdb05a7, 0x902f55bc, 0x636199a4, 0x8e69f412], + [0x0dd0bfa9, 0x916e27b1, 0x6e2542d9, 0x07d92e65] + ]; + + for (var i = 0; i < testData.length; i++) { + var a = testData[i][0] >>> 0; + var b = testData[i][1] >>> 0; + var cLow = testData[i][2] >>> 0; + var cHigh = testData[i][3] >>> 0; + var c = jspb.arith.UInt64.mul32x32(a, b); + assertEquals(c.lo, cLow); + assertEquals(c.hi, cHigh); + } + }); + + + /** + * Tests 64-by-32 multiplication. + */ + it('testMul', function() { + // 64x32 bits produces 96 bits of product. The multiplication function under + // test truncates the top 32 bits, so we compare against a 64-bit expected + // product. + var testData = [ + // low(a) high(a) low(a*b) high(a*b) + [0xec10955b, 0x360eb168, 0x4b7f3f5b, 0xbfcb7c59, 0x9517da5f], + [0x42b000fc, 0x9d101642, 0x6fa1ab72, 0x2584c438, 0x6a9e6d2b], + [0xf42d4fb4, 0xae366403, 0xa65a1000, 0x92434000, 0x1ff978df], + [0x17e2f56b, 0x25487693, 0xf13f98c7, 0x73794e2d, 0xa96b0c6a], + [0x492f241f, 0x76c0eb67, 0x7377ac44, 0xd4336c3c, 0xfc4b1ebe], + [0xd6b92321, 0xe184fa48, 0xd6e76904, 0x93141584, 0xcbf44da1], + [0x4bf007ea, 0x968c0a9e, 0xf5e4026a, 0x4fdb1ae4, 0x61b9fb7d], + [0x10a83be7, 0x2d685ba6, 0xc9e5fb7f, 0x2ad43499, 0x3742473d], + [0x2f261829, 0x1aca681a, 0x3d3494e3, 0x8213205b, 0x283719f8], + [0xe4f2ce21, 0x2e74b7bd, 0xd801b38b, 0xbc17feeb, 0xc6c44e0f] + ]; + + for (var i = 0; i < testData.length; i++) { + var a = new jspb.arith.UInt64(testData[i][0], testData[i][1]); + var prod = a.mul(testData[i][2]); + assertEquals(prod.lo, testData[i][3]); + assertEquals(prod.hi, testData[i][4]); + } + }); + + + /** + * Tests 64-div-by-32 division. + */ + it('testDiv', function() { + // Compute a/b, yielding quot = a/b and rem = a%b. + var testData = [ + // --- divisors in (0, 2^32-1) to test full divisor range + // low(a) high(a) b low(quot) high(quot) rem + [0x712443f1, 0xe85cefcc, 0xc1a7050b, 0x332c79ad, 0x00000001, 0x92ffa882], + [0x11912915, 0xb2699eb5, 0x30467cbe, 0xb21b4be4, 0x00000003, 0x283465dd], + [0x0d917982, 0x201f2a6e, 0x3f35bf03, 0x8217c8e4, 0x00000000, 0x153402d6], + [0xa072c108, 0x74020c96, 0xc60568fd, 0x95f9613e, 0x00000000, 0x3f4676c2], + [0xd845d5d8, 0xcdd235c4, 0x20426475, 0x6154e78b, 0x00000006, 0x202fb751], + [0xa4dbf71f, 0x9e90465e, 0xf08e022f, 0xa8be947f, 0x00000000, 0xbe43b5ce], + [0x3dbe627f, 0xa791f4b9, 0x28a5bd89, 0x1f5dfe93, 0x00000004, 0x02bf9ed4], + [0x5c1c53ee, 0xccf5102e, 0x198576e7, 0x07e3ae31, 0x00000008, 0x02ea8fb7], + [0xfef1e581, 0x04714067, 0xca6540c1, 0x059e73ec, 0x00000000, 0x31658095], + [0x1e2dd90c, 0x13dd6667, 0x8b2184c3, 0x248d1a42, 0x00000000, 0x4ca6d0c6], + // --- divisors in (0, 2^16-1) to test larger quotient high-words + // low(a) high(a) b low(quot) high(quot) rem + [0x86722b47, 0x2cd57c9a, 0x00003123, 0x2ae41b7a, 0x0000e995, 0x00000f99], + [0x1dd7884c, 0xf5e839bc, 0x00009eeb, 0x5c886242, 0x00018c21, 0x000099b6], + [0x5c53d625, 0x899fc7e5, 0x000087d7, 0xd625007a, 0x0001035c, 0x000019af], + [0x6932d932, 0x9d0a5488, 0x000051fb, 0x9d976143, 0x0001ea63, 0x00004981], + [0x4d18bb85, 0x0c92fb31, 0x00001d9f, 0x03265ab4, 0x00006cac, 0x000001b9], + [0xbe756768, 0xdea67ccb, 0x00008a03, 0x58add442, 0x00019cff, 0x000056a2], + [0xe2466f9a, 0x2521f114, 0x0000c350, 0xa0c0860d, 0x000030ab, 0x0000a48a], + [0xf00ddad1, 0xe2f5446a, 0x00002cfc, 0x762697a6, 0x00050b96, 0x00000b69], + [0xa879152a, 0x0a70e0a5, 0x00007cdf, 0xb44151b3, 0x00001567, 0x0000363d], + [0x7179a74c, 0x46083fff, 0x0000253c, 0x4d39ba6e, 0x0001e17f, 0x00000f84] + ]; + + for (var i = 0; i < testData.length; i++) { + var a = new jspb.arith.UInt64(testData[i][0], testData[i][1]); + var result = a.div(testData[i][2]); + var quotient = result[0]; + var remainder = result[1]; + assertEquals(quotient.lo, testData[i][3]); + assertEquals(quotient.hi, testData[i][4]); + assertEquals(remainder.lo, testData[i][5]); + } + }); + + + /** + * Tests .toString() and .fromString(). + */ + it('testStrings', function() { + var testData = [ + [0x5e84c935, 0xcae33d0e, '14619595947299359029'], + [0x62b3b8b8, 0x93480544, '10612738313170434232'], + [0x319bfb13, 0xc01c4172, '13843011313344445203'], + [0x5b8a65fb, 0xa5885b31, '11927883880638080507'], + [0x6bdb80f1, 0xb0d1b16b, '12741159895737008369'], + [0x4b82b442, 0x2e0d8c97, '3318463081876730946'], + [0x780d5208, 0x7d76752c, '9040542135845999112'], + [0x2e46800f, 0x0993778d, '690026616168284175'], + [0xf00a7e32, 0xcd8e3931, '14811839111111540274'], + [0x1baeccd6, 0x923048c4, '10533999535534820566'], + [0x03669d29, 0xbff3ab72, '13831587386756603177'], + [0x2526073e, 0x01affc81, '121593346566522686'], + [0xc24244e0, 0xd7f40d0e, '15561076969511732448'], + [0xc56a341e, 0xa68b66a7, '12000798502816461854'], + [0x8738d64d, 0xbfe78604, '13828168534871037517'], + [0x5baff03b, 0xd7572aea, '15516918227177304123'], + [0x4a843d8a, 0x864e132b, '9677693725920476554'], + [0x25b4e94d, 0x22b54dc6, '2500990681505655117'], + [0x6bbe664b, 0x55a5cc0e, '6171563226690381387'], + [0xee916c81, 0xb00aabb3, '12685140089732426881'] + ]; + + for (var i = 0; i < testData.length; i++) { + var a = new jspb.arith.UInt64(testData[i][0], testData[i][1]); + var roundtrip = jspb.arith.UInt64.fromString(a.toString()); + assertEquals(roundtrip.lo, a.lo); + assertEquals(roundtrip.hi, a.hi); + assertEquals(a.toString(), testData[i][2]); + } + }); + + + /** + * Tests signed Int64s. These are built on UInt64s, so we only need to test + * the explicit overrides: .toString() and .fromString(). + */ + it('testSignedInt64', function() { + var testStrings = [ + '-7847499644178593666', + '3771946501229139523', + '2872856549054995060', + '-5780049594274350904', + '3383785956695105201', + '2973055184857072610', + '-3879428459215627206', + '4589812431064156631', + '8484075557333689940', + '1075325817098092407', + '-4346697501012292314', + '2488620459718316637', + '6112655187423520672', + '-3655278273928612104', + '3439154019435803196', + '1004112478843763757', + '-6587790776614368413', + '664320065099714586', + '4760412909973292912', + '-7911903989602274672' + ]; + + for (var i = 0; i < testStrings.length; i++) { + var roundtrip = + jspb.arith.Int64.fromString(testStrings[i]).toString(); + assertEquals(roundtrip, testStrings[i]); + } + }); +}); diff --git a/js/compatibility_tests/v3.1.0/binary/decoder_test.js b/js/compatibility_tests/v3.1.0/binary/decoder_test.js new file mode 100644 index 00000000..ac312648 --- /dev/null +++ b/js/compatibility_tests/v3.1.0/binary/decoder_test.js @@ -0,0 +1,334 @@ +// 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'); +goog.require('jspb.BinaryEncoder'); + + +/** + * Tests encoding and decoding of unsigned types. + * @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) { + var encoder = new jspb.BinaryEncoder(); + + // Encode zero and limits. + writeValue.call(encoder, filter(0)); + writeValue.call(encoder, filter(epsilon)); + writeValue.call(encoder, filter(upperLimit)); + + // Encode positive values. + for (var cursor = epsilon; cursor < upperLimit; cursor *= 1.1) { + writeValue.call(encoder, filter(cursor)); + } + + var decoder = jspb.BinaryDecoder.alloc(encoder.end()); + + // Check zero and limits. + assertEquals(filter(0), readValue.call(decoder)); + assertEquals(filter(epsilon), readValue.call(decoder)); + assertEquals(filter(upperLimit), readValue.call(decoder)); + + // Check positive values. + for (var cursor = epsilon; cursor < upperLimit; cursor *= 1.1) { + if (filter(cursor) != readValue.call(decoder)) throw 'fail!'; + } + + // Encoding values outside the valid range should assert. + assertThrows(function() {writeValue.call(encoder, -1);}); + assertThrows(function() {writeValue.call(encoder, upperLimit * 1.1);}); +} + + +/** + * Tests encoding and decoding of signed types. + * @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) { + var encoder = new jspb.BinaryEncoder(); + + // Encode zero and limits. + 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)); + + var inputValues = []; + + // Encode negative values. + for (var cursor = lowerLimit; cursor < -epsilon; cursor /= 1.1) { + var val = filter(cursor); + writeValue.call(encoder, val); + inputValues.push(val); + } + + // Encode positive values. + for (var cursor = epsilon; cursor < upperLimit; cursor *= 1.1) { + var val = filter(cursor); + writeValue.call(encoder, val); + inputValues.push(val); + } + + var decoder = jspb.BinaryDecoder.alloc(encoder.end()); + + // Check zero and limits. + 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)); + + // Verify decoded values. + for (var i = 0; i < inputValues.length; i++) { + assertEquals(inputValues[i], readValue.call(decoder)); + } + + // Encoding values outside the valid range should assert. + assertThrows(function() {writeValue.call(encoder, lowerLimit * 1.1);}); + assertThrows(function() {writeValue.call(encoder, upperLimit * 1.1);}); +} + +describe('binaryDecoderTest', function() { + /** + * Tests the decoder instance cache. + */ + it('testInstanceCache', /** @suppress {visibility} */ function() { + // 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() { + var encoder = new jspb.BinaryEncoder(); + + 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); + + encoder.writeVarintHash64(hashA); + encoder.writeVarintHash64(hashB); + encoder.writeVarintHash64(hashC); + encoder.writeVarintHash64(hashD); + + encoder.writeFixedHash64(hashA); + encoder.writeFixedHash64(hashB); + encoder.writeFixedHash64(hashC); + encoder.writeFixedHash64(hashD); + + var decoder = jspb.BinaryDecoder.alloc(encoder.end()); + + 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()); + }); + + + /** + * 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. + decoder.setBlock([255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 0]); + 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()}); + }); + + + /** + * Tests encoding and decoding of unsigned integers. + */ + it('testUnsignedIntegers', function() { + doTestUnsignedValue( + jspb.BinaryDecoder.prototype.readUint8, + jspb.BinaryEncoder.prototype.writeUint8, + 1, 0xFF, Math.round); + + doTestUnsignedValue( + jspb.BinaryDecoder.prototype.readUint16, + jspb.BinaryEncoder.prototype.writeUint16, + 1, 0xFFFF, Math.round); + + doTestUnsignedValue( + jspb.BinaryDecoder.prototype.readUint32, + jspb.BinaryEncoder.prototype.writeUint32, + 1, 0xFFFFFFFF, Math.round); + + doTestUnsignedValue( + jspb.BinaryDecoder.prototype.readUint64, + jspb.BinaryEncoder.prototype.writeUint64, + 1, Math.pow(2, 64) - 1025, Math.round); + }); + + + /** + * Tests encoding and decoding of signed integers. + */ + it('testSignedIntegers', function() { + doTestSignedValue( + jspb.BinaryDecoder.prototype.readInt8, + jspb.BinaryEncoder.prototype.writeInt8, + 1, -0x80, 0x7F, Math.round); + + doTestSignedValue( + jspb.BinaryDecoder.prototype.readInt16, + jspb.BinaryEncoder.prototype.writeInt16, + 1, -0x8000, 0x7FFF, Math.round); + + doTestSignedValue( + jspb.BinaryDecoder.prototype.readInt32, + jspb.BinaryEncoder.prototype.writeInt32, + 1, -0x80000000, 0x7FFFFFFF, Math.round); + + doTestSignedValue( + jspb.BinaryDecoder.prototype.readInt64, + jspb.BinaryEncoder.prototype.writeInt64, + 1, -Math.pow(2, 63), Math.pow(2, 63) - 513, Math.round); + }); + + + /** + * Tests encoding and decoding of floats. + */ + it('testFloats', function() { + /** + * @param {number} x + * @return {number} + */ + function truncate(x) { + var temp = new Float32Array(1); + temp[0] = x; + return temp[0]; + } + doTestSignedValue( + jspb.BinaryDecoder.prototype.readFloat, + jspb.BinaryEncoder.prototype.writeFloat, + jspb.BinaryConstants.FLOAT32_EPS, + -jspb.BinaryConstants.FLOAT32_MAX, + jspb.BinaryConstants.FLOAT32_MAX, + truncate); + + doTestSignedValue( + jspb.BinaryDecoder.prototype.readDouble, + jspb.BinaryEncoder.prototype.writeDouble, + jspb.BinaryConstants.FLOAT64_EPS * 10, + -jspb.BinaryConstants.FLOAT64_MAX, + jspb.BinaryConstants.FLOAT64_MAX, + function(x) { return x; }); + }); +}); diff --git a/js/compatibility_tests/v3.1.0/binary/proto_test.js b/js/compatibility_tests/v3.1.0/binary/proto_test.js new file mode 100644 index 00000000..26e1d30f --- /dev/null +++ b/js/compatibility_tests/v3.1.0/binary/proto_test.js @@ -0,0 +1,628 @@ +// 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. + +// Test suite is written using Jasmine -- see http://jasmine.github.io/ + +goog.require('goog.crypt.base64'); +goog.require('goog.testing.asserts'); +goog.require('jspb.Message'); + +// CommonJS-LoadFromFile: ../testbinary_pb proto.jspb.test +goog.require('proto.jspb.test.ExtendsWithMessage'); +goog.require('proto.jspb.test.ForeignEnum'); +goog.require('proto.jspb.test.ForeignMessage'); +goog.require('proto.jspb.test.TestAllTypes'); +goog.require('proto.jspb.test.TestExtendable'); +goog.require('proto.jspb.test.extendOptionalBool'); +goog.require('proto.jspb.test.extendOptionalBytes'); +goog.require('proto.jspb.test.extendOptionalDouble'); +goog.require('proto.jspb.test.extendOptionalFixed32'); +goog.require('proto.jspb.test.extendOptionalFixed64'); +goog.require('proto.jspb.test.extendOptionalFloat'); +goog.require('proto.jspb.test.extendOptionalForeignEnum'); +goog.require('proto.jspb.test.extendOptionalInt32'); +goog.require('proto.jspb.test.extendOptionalInt64'); +goog.require('proto.jspb.test.extendOptionalSfixed32'); +goog.require('proto.jspb.test.extendOptionalSfixed64'); +goog.require('proto.jspb.test.extendOptionalSint32'); +goog.require('proto.jspb.test.extendOptionalSint64'); +goog.require('proto.jspb.test.extendOptionalString'); +goog.require('proto.jspb.test.extendOptionalUint32'); +goog.require('proto.jspb.test.extendOptionalUint64'); +goog.require('proto.jspb.test.extendPackedRepeatedBoolList'); +goog.require('proto.jspb.test.extendPackedRepeatedDoubleList'); +goog.require('proto.jspb.test.extendPackedRepeatedFixed32List'); +goog.require('proto.jspb.test.extendPackedRepeatedFixed64List'); +goog.require('proto.jspb.test.extendPackedRepeatedFloatList'); +goog.require('proto.jspb.test.extendPackedRepeatedForeignEnumList'); +goog.require('proto.jspb.test.extendPackedRepeatedInt32List'); +goog.require('proto.jspb.test.extendPackedRepeatedInt64List'); +goog.require('proto.jspb.test.extendPackedRepeatedSfixed32List'); +goog.require('proto.jspb.test.extendPackedRepeatedSfixed64List'); +goog.require('proto.jspb.test.extendPackedRepeatedSint32List'); +goog.require('proto.jspb.test.extendPackedRepeatedSint64List'); +goog.require('proto.jspb.test.extendPackedRepeatedUint32List'); +goog.require('proto.jspb.test.extendPackedRepeatedUint64List'); +goog.require('proto.jspb.test.extendRepeatedBoolList'); +goog.require('proto.jspb.test.extendRepeatedBytesList'); +goog.require('proto.jspb.test.extendRepeatedDoubleList'); +goog.require('proto.jspb.test.extendRepeatedFixed32List'); +goog.require('proto.jspb.test.extendRepeatedFixed64List'); +goog.require('proto.jspb.test.extendRepeatedFloatList'); +goog.require('proto.jspb.test.extendRepeatedForeignEnumList'); +goog.require('proto.jspb.test.extendRepeatedInt32List'); +goog.require('proto.jspb.test.extendRepeatedInt64List'); +goog.require('proto.jspb.test.extendRepeatedSfixed32List'); +goog.require('proto.jspb.test.extendRepeatedSfixed64List'); +goog.require('proto.jspb.test.extendRepeatedSint32List'); +goog.require('proto.jspb.test.extendRepeatedSint64List'); +goog.require('proto.jspb.test.extendRepeatedStringList'); +goog.require('proto.jspb.test.extendRepeatedUint32List'); +goog.require('proto.jspb.test.extendRepeatedUint64List'); + + +var suite = {}; + +var BYTES = new Uint8Array([1, 2, 8, 9]); + +var BYTES_B64 = goog.crypt.base64.encodeByteArray(BYTES); + + +/** + * Helper: fill all fields on a TestAllTypes message. + * @param {proto.jspb.test.TestAllTypes} msg + */ +function fillAllFields(msg) { + msg.setOptionalInt32(-42); + // can be exactly represented by JS number (64-bit double, i.e., 52-bit + // mantissa). + msg.setOptionalInt64(-0x7fffffff00000000); + msg.setOptionalUint32(0x80000000); + msg.setOptionalUint64(0xf000000000000000); + msg.setOptionalSint32(-100); + msg.setOptionalSint64(-0x8000000000000000); + msg.setOptionalFixed32(1234); + msg.setOptionalFixed64(0x1234567800000000); + msg.setOptionalSfixed32(-1234); + msg.setOptionalSfixed64(-0x1234567800000000); + msg.setOptionalFloat(1.5); + msg.setOptionalDouble(-1.5); + msg.setOptionalBool(true); + msg.setOptionalString('hello world'); + msg.setOptionalBytes(BYTES); + msg.setOptionalGroup(new proto.jspb.test.TestAllTypes.OptionalGroup()); + msg.getOptionalGroup().setA(100); + var submsg = new proto.jspb.test.ForeignMessage(); + submsg.setC(16); + msg.setOptionalForeignMessage(submsg); + msg.setOptionalForeignEnum(proto.jspb.test.ForeignEnum.FOREIGN_FOO); + msg.setOneofString('oneof'); + + + msg.setRepeatedInt32List([-42]); + msg.setRepeatedInt64List([-0x7fffffff00000000]); + msg.setRepeatedUint32List([0x80000000]); + msg.setRepeatedUint64List([0xf000000000000000]); + msg.setRepeatedSint32List([-100]); + msg.setRepeatedSint64List([-0x8000000000000000]); + msg.setRepeatedFixed32List([1234]); + msg.setRepeatedFixed64List([0x1234567800000000]); + msg.setRepeatedSfixed32List([-1234]); + msg.setRepeatedSfixed64List([-0x1234567800000000]); + msg.setRepeatedFloatList([1.5]); + msg.setRepeatedDoubleList([-1.5]); + msg.setRepeatedBoolList([true]); + msg.setRepeatedStringList(['hello world']); + msg.setRepeatedBytesList([BYTES, BYTES]); + msg.setRepeatedGroupList([new proto.jspb.test.TestAllTypes.RepeatedGroup()]); + msg.getRepeatedGroupList()[0].setA(100); + submsg = new proto.jspb.test.ForeignMessage(); + submsg.setC(1000); + msg.setRepeatedForeignMessageList([submsg]); + msg.setRepeatedForeignEnumList([proto.jspb.test.ForeignEnum.FOREIGN_FOO]); + + msg.setPackedRepeatedInt32List([-42]); + msg.setPackedRepeatedInt64List([-0x7fffffff00000000]); + msg.setPackedRepeatedUint32List([0x80000000]); + msg.setPackedRepeatedUint64List([0xf000000000000000]); + msg.setPackedRepeatedSint32List([-100]); + msg.setPackedRepeatedSint64List([-0x8000000000000000]); + msg.setPackedRepeatedFixed32List([1234]); + msg.setPackedRepeatedFixed64List([0x1234567800000000]); + msg.setPackedRepeatedSfixed32List([-1234]); + msg.setPackedRepeatedSfixed64List([-0x1234567800000000]); + msg.setPackedRepeatedFloatList([1.5]); + msg.setPackedRepeatedDoubleList([-1.5]); + msg.setPackedRepeatedBoolList([true]); + +} + + +/** + * Helper: compare a bytes field to an expected value + * @param {Uint8Array|string} arr + * @param {Uint8Array} expected + * @return {boolean} + */ +function bytesCompare(arr, expected) { + if (goog.isString(arr)) { + arr = goog.crypt.base64.decodeStringToUint8Array(arr); + } + if (arr.length != expected.length) { + return false; + } + for (var i = 0; i < arr.length; i++) { + if (arr[i] != expected[i]) { + return false; + } + } + return true; +} + + +/** + * Helper: verify contents of given TestAllTypes message as set by + * fillAllFields(). + * @param {proto.jspb.test.TestAllTypes} original + * @param {proto.jspb.test.TestAllTypes} copy + */ +function checkAllFields(original, copy) { + assertTrue(jspb.Message.equals(original, copy)); + + assertEquals(copy.getOptionalInt32(), -42); + assertEquals(copy.getOptionalInt64(), -0x7fffffff00000000); + assertEquals(copy.getOptionalUint32(), 0x80000000); + assertEquals(copy.getOptionalUint64(), 0xf000000000000000); + assertEquals(copy.getOptionalSint32(), -100); + assertEquals(copy.getOptionalSint64(), -0x8000000000000000); + assertEquals(copy.getOptionalFixed32(), 1234); + assertEquals(copy.getOptionalFixed64(), 0x1234567800000000); + assertEquals(copy.getOptionalSfixed32(), -1234); + assertEquals(copy.getOptionalSfixed64(), -0x1234567800000000); + assertEquals(copy.getOptionalFloat(), 1.5); + assertEquals(copy.getOptionalDouble(), -1.5); + assertEquals(copy.getOptionalBool(), true); + assertEquals(copy.getOptionalString(), 'hello world'); + assertEquals(true, bytesCompare(copy.getOptionalBytes(), BYTES)); + assertEquals(true, bytesCompare(copy.getOptionalBytes_asU8(), BYTES)); + assertEquals( + copy.getOptionalBytes_asB64(), goog.crypt.base64.encodeByteArray(BYTES)); + + assertEquals(copy.getOptionalGroup().getA(), 100); + assertEquals(copy.getOptionalForeignMessage().getC(), 16); + assertEquals(copy.getOptionalForeignEnum(), + proto.jspb.test.ForeignEnum.FOREIGN_FOO); + + + assertEquals(copy.getOneofString(), 'oneof'); + assertEquals(copy.getOneofFieldCase(), + proto.jspb.test.TestAllTypes.OneofFieldCase.ONEOF_STRING); + + assertElementsEquals(copy.getRepeatedInt32List(), [-42]); + assertElementsEquals(copy.getRepeatedInt64List(), [-0x7fffffff00000000]); + assertElementsEquals(copy.getRepeatedUint32List(), [0x80000000]); + assertElementsEquals(copy.getRepeatedUint64List(), [0xf000000000000000]); + assertElementsEquals(copy.getRepeatedSint32List(), [-100]); + assertElementsEquals(copy.getRepeatedSint64List(), [-0x8000000000000000]); + assertElementsEquals(copy.getRepeatedFixed32List(), [1234]); + assertElementsEquals(copy.getRepeatedFixed64List(), [0x1234567800000000]); + assertElementsEquals(copy.getRepeatedSfixed32List(), [-1234]); + assertElementsEquals(copy.getRepeatedSfixed64List(), [-0x1234567800000000]); + assertElementsEquals(copy.getRepeatedFloatList(), [1.5]); + assertElementsEquals(copy.getRepeatedDoubleList(), [-1.5]); + assertElementsEquals(copy.getRepeatedBoolList(), [true]); + assertElementsEquals(copy.getRepeatedStringList(), ['hello world']); + assertEquals(copy.getRepeatedBytesList().length, 2); + assertEquals(true, bytesCompare(copy.getRepeatedBytesList_asU8()[0], BYTES)); + assertEquals(true, bytesCompare(copy.getRepeatedBytesList()[0], BYTES)); + assertEquals(true, bytesCompare(copy.getRepeatedBytesList_asU8()[1], BYTES)); + assertEquals(copy.getRepeatedBytesList_asB64()[0], BYTES_B64); + assertEquals(copy.getRepeatedBytesList_asB64()[1], BYTES_B64); + assertEquals(copy.getRepeatedGroupList().length, 1); + assertEquals(copy.getRepeatedGroupList()[0].getA(), 100); + assertEquals(copy.getRepeatedForeignMessageList().length, 1); + assertEquals(copy.getRepeatedForeignMessageList()[0].getC(), 1000); + assertElementsEquals(copy.getRepeatedForeignEnumList(), + [proto.jspb.test.ForeignEnum.FOREIGN_FOO]); + + assertElementsEquals(copy.getPackedRepeatedInt32List(), [-42]); + assertElementsEquals(copy.getPackedRepeatedInt64List(), + [-0x7fffffff00000000]); + assertElementsEquals(copy.getPackedRepeatedUint32List(), [0x80000000]); + assertElementsEquals(copy.getPackedRepeatedUint64List(), + [0xf000000000000000]); + assertElementsEquals(copy.getPackedRepeatedSint32List(), [-100]); + assertElementsEquals(copy.getPackedRepeatedSint64List(), + [-0x8000000000000000]); + assertElementsEquals(copy.getPackedRepeatedFixed32List(), [1234]); + assertElementsEquals(copy.getPackedRepeatedFixed64List(), + [0x1234567800000000]); + assertElementsEquals(copy.getPackedRepeatedSfixed32List(), [-1234]); + assertElementsEquals(copy.getPackedRepeatedSfixed64List(), + [-0x1234567800000000]); + assertElementsEquals(copy.getPackedRepeatedFloatList(), [1.5]); + assertElementsEquals(copy.getPackedRepeatedDoubleList(), [-1.5]); + +} + + +/** + * Helper: verify that all expected extensions are present. + * @param {!proto.jspb.test.TestExtendable} msg + */ +function checkExtensions(msg) { + assertEquals(-42, + msg.getExtension(proto.jspb.test.extendOptionalInt32)); + assertEquals(-0x7fffffff00000000, + msg.getExtension(proto.jspb.test.extendOptionalInt64)); + assertEquals(0x80000000, + msg.getExtension(proto.jspb.test.extendOptionalUint32)); + assertEquals(0xf000000000000000, + msg.getExtension(proto.jspb.test.extendOptionalUint64)); + assertEquals(-100, + msg.getExtension(proto.jspb.test.extendOptionalSint32)); + assertEquals(-0x8000000000000000, + msg.getExtension(proto.jspb.test.extendOptionalSint64)); + assertEquals(1234, + msg.getExtension(proto.jspb.test.extendOptionalFixed32)); + assertEquals(0x1234567800000000, + msg.getExtension(proto.jspb.test.extendOptionalFixed64)); + assertEquals(-1234, + msg.getExtension(proto.jspb.test.extendOptionalSfixed32)); + assertEquals(-0x1234567800000000, + msg.getExtension(proto.jspb.test.extendOptionalSfixed64)); + assertEquals(1.5, + msg.getExtension(proto.jspb.test.extendOptionalFloat)); + assertEquals(-1.5, + msg.getExtension(proto.jspb.test.extendOptionalDouble)); + assertEquals(true, + msg.getExtension(proto.jspb.test.extendOptionalBool)); + assertEquals('hello world', + msg.getExtension(proto.jspb.test.extendOptionalString)); + assertEquals( + true, bytesCompare( + msg.getExtension(proto.jspb.test.extendOptionalBytes), BYTES)); + assertEquals(16, + msg.getExtension( + proto.jspb.test.ExtendsWithMessage.optionalExtension).getFoo()); + + + assertElementsEquals( + msg.getExtension(proto.jspb.test.extendRepeatedInt32List), + [-42]); + assertElementsEquals( + msg.getExtension(proto.jspb.test.extendRepeatedInt64List), + [-0x7fffffff00000000]); + assertElementsEquals( + msg.getExtension(proto.jspb.test.extendRepeatedUint32List), + [0x80000000]); + assertElementsEquals( + msg.getExtension(proto.jspb.test.extendRepeatedUint64List), + [0xf000000000000000]); + assertElementsEquals( + msg.getExtension(proto.jspb.test.extendRepeatedSint32List), + [-100]); + assertElementsEquals( + msg.getExtension(proto.jspb.test.extendRepeatedSint64List), + [-0x8000000000000000]); + assertElementsEquals( + msg.getExtension(proto.jspb.test.extendRepeatedFixed32List), + [1234]); + assertElementsEquals( + msg.getExtension(proto.jspb.test.extendRepeatedFixed64List), + [0x1234567800000000]); + assertElementsEquals( + msg.getExtension(proto.jspb.test.extendRepeatedSfixed32List), + [-1234]); + assertElementsEquals( + msg.getExtension(proto.jspb.test.extendRepeatedSfixed64List), + [-0x1234567800000000]); + assertElementsEquals( + msg.getExtension(proto.jspb.test.extendRepeatedFloatList), + [1.5]); + assertElementsEquals( + msg.getExtension(proto.jspb.test.extendRepeatedDoubleList), + [-1.5]); + assertElementsEquals( + msg.getExtension(proto.jspb.test.extendRepeatedBoolList), + [true]); + assertElementsEquals( + msg.getExtension(proto.jspb.test.extendRepeatedStringList), + ['hello world']); + assertEquals( + true, + bytesCompare( + msg.getExtension(proto.jspb.test.extendRepeatedBytesList)[0], BYTES)); + assertEquals(1000, + msg.getExtension( + proto.jspb.test.ExtendsWithMessage.repeatedExtensionList)[0] + .getFoo()); + assertElementsEquals( + msg.getExtension(proto.jspb.test.extendRepeatedForeignEnumList), + [proto.jspb.test.ForeignEnum.FOREIGN_FOO]); + + + assertElementsEquals( + msg.getExtension(proto.jspb.test.extendPackedRepeatedInt32List), + [-42]); + assertElementsEquals( + msg.getExtension(proto.jspb.test.extendPackedRepeatedInt64List), + [-0x7fffffff00000000]); + assertElementsEquals( + msg.getExtension(proto.jspb.test.extendPackedRepeatedUint32List), + [0x80000000]); + assertElementsEquals( + msg.getExtension(proto.jspb.test.extendPackedRepeatedUint64List), + [0xf000000000000000]); + assertElementsEquals( + msg.getExtension(proto.jspb.test.extendPackedRepeatedSint32List), + [-100]); + assertElementsEquals( + msg.getExtension(proto.jspb.test.extendPackedRepeatedSint64List), + [-0x8000000000000000]); + assertElementsEquals( + msg.getExtension(proto.jspb.test.extendPackedRepeatedFixed32List), + [1234]); + assertElementsEquals( + msg.getExtension(proto.jspb.test.extendPackedRepeatedFixed64List), + [0x1234567800000000]); + assertElementsEquals( + msg.getExtension(proto.jspb.test.extendPackedRepeatedSfixed32List), + [-1234]); + assertElementsEquals( + msg.getExtension(proto.jspb.test.extendPackedRepeatedSfixed64List), + [-0x1234567800000000]); + assertElementsEquals( + msg.getExtension(proto.jspb.test.extendPackedRepeatedFloatList), + [1.5]); + assertElementsEquals( + msg.getExtension(proto.jspb.test.extendPackedRepeatedDoubleList), + [-1.5]); + assertElementsEquals( + msg.getExtension(proto.jspb.test.extendPackedRepeatedBoolList), + [true]); + assertElementsEquals( + msg.getExtension(proto.jspb.test.extendPackedRepeatedForeignEnumList), + [proto.jspb.test.ForeignEnum.FOREIGN_FOO]); + +} + + +describe('protoBinaryTest', function() { + /** + * Tests a basic serialization-deserializaton round-trip with all supported + * field types (on the TestAllTypes message type). + */ + it('testRoundTrip', function() { + var msg = new proto.jspb.test.TestAllTypes(); + fillAllFields(msg); + var encoded = msg.serializeBinary(); + var decoded = proto.jspb.test.TestAllTypes.deserializeBinary(encoded); + checkAllFields(msg, decoded); + }); + + /** + * Test that base64 string and Uint8Array are interchangeable in bytes fields. + */ + it('testBytesFieldsGettersInterop', function() { + var msg = new proto.jspb.test.TestAllTypes(); + // Set from a base64 string and check all the getters work. + msg.setOptionalBytes(BYTES_B64); + assertTrue(bytesCompare(msg.getOptionalBytes_asU8(), BYTES)); + assertTrue(bytesCompare(msg.getOptionalBytes_asB64(), BYTES)); + assertTrue(bytesCompare(msg.getOptionalBytes(), BYTES)); + + // Test binary serialize round trip doesn't break it. + msg = proto.jspb.test.TestAllTypes.deserializeBinary(msg.serializeBinary()); + assertTrue(bytesCompare(msg.getOptionalBytes_asU8(), BYTES)); + assertTrue(bytesCompare(msg.getOptionalBytes_asB64(), BYTES)); + assertTrue(bytesCompare(msg.getOptionalBytes(), BYTES)); + + msg = new proto.jspb.test.TestAllTypes(); + // Set from a Uint8Array and check all the getters work. + msg.setOptionalBytes(BYTES); + assertTrue(bytesCompare(msg.getOptionalBytes_asU8(), BYTES)); + assertTrue(bytesCompare(msg.getOptionalBytes_asB64(), BYTES)); + assertTrue(bytesCompare(msg.getOptionalBytes(), BYTES)); + + }); + + /** + * Test that bytes setters will receive result of any of the getters. + */ + it('testBytesFieldsSettersInterop', function() { + var msg = new proto.jspb.test.TestAllTypes(); + msg.setOptionalBytes(BYTES); + assertTrue(bytesCompare(msg.getOptionalBytes(), BYTES)); + + msg.setOptionalBytes(msg.getOptionalBytes()); + assertTrue(bytesCompare(msg.getOptionalBytes(), BYTES)); + msg.setOptionalBytes(msg.getOptionalBytes_asB64()); + assertTrue(bytesCompare(msg.getOptionalBytes(), BYTES)); + msg.setOptionalBytes(msg.getOptionalBytes_asU8()); + assertTrue(bytesCompare(msg.getOptionalBytes(), BYTES)); + }); + + /** + * Test that bytes setters will receive result of any of the getters. + */ + it('testRepeatedBytesGetters', function() { + var msg = new proto.jspb.test.TestAllTypes(); + + function assertGetters() { + assertTrue(goog.isString(msg.getRepeatedBytesList_asB64()[0])); + assertTrue(goog.isString(msg.getRepeatedBytesList_asB64()[1])); + assertTrue(msg.getRepeatedBytesList_asU8()[0] instanceof Uint8Array); + assertTrue(msg.getRepeatedBytesList_asU8()[1] instanceof Uint8Array); + + assertTrue(bytesCompare(msg.getRepeatedBytesList()[0], BYTES)); + assertTrue(bytesCompare(msg.getRepeatedBytesList()[1], BYTES)); + assertTrue(bytesCompare(msg.getRepeatedBytesList_asB64()[0], BYTES)); + assertTrue(bytesCompare(msg.getRepeatedBytesList_asB64()[1], BYTES)); + assertTrue(bytesCompare(msg.getRepeatedBytesList_asU8()[0], BYTES)); + assertTrue(bytesCompare(msg.getRepeatedBytesList_asU8()[1], BYTES)); + } + + msg.setRepeatedBytesList([BYTES, BYTES]); + assertGetters(); + + msg.setRepeatedBytesList([BYTES_B64, BYTES_B64]); + assertGetters(); + + msg.setRepeatedBytesList([]); + assertEquals(0, msg.getRepeatedBytesList().length); + assertEquals(0, msg.getRepeatedBytesList_asB64().length); + assertEquals(0, msg.getRepeatedBytesList_asU8().length); + }); + + /** + * Helper: fill all extension values. + * @param {proto.jspb.test.TestExtendable} msg + */ + function fillExtensions(msg) { + msg.setExtension( + proto.jspb.test.extendOptionalInt32, -42); + msg.setExtension( + proto.jspb.test.extendOptionalInt64, -0x7fffffff00000000); + msg.setExtension( + proto.jspb.test.extendOptionalUint32, 0x80000000); + msg.setExtension( + proto.jspb.test.extendOptionalUint64, 0xf000000000000000); + msg.setExtension( + proto.jspb.test.extendOptionalSint32, -100); + msg.setExtension( + proto.jspb.test.extendOptionalSint64, -0x8000000000000000); + msg.setExtension( + proto.jspb.test.extendOptionalFixed32, 1234); + msg.setExtension( + proto.jspb.test.extendOptionalFixed64, 0x1234567800000000); + msg.setExtension( + proto.jspb.test.extendOptionalSfixed32, -1234); + msg.setExtension( + proto.jspb.test.extendOptionalSfixed64, -0x1234567800000000); + msg.setExtension( + proto.jspb.test.extendOptionalFloat, 1.5); + msg.setExtension( + proto.jspb.test.extendOptionalDouble, -1.5); + msg.setExtension( + proto.jspb.test.extendOptionalBool, true); + msg.setExtension( + proto.jspb.test.extendOptionalString, 'hello world'); + msg.setExtension(proto.jspb.test.extendOptionalBytes, BYTES); + var submsg = new proto.jspb.test.ExtendsWithMessage(); + submsg.setFoo(16); + msg.setExtension( + proto.jspb.test.ExtendsWithMessage.optionalExtension, submsg); + msg.setExtension( + proto.jspb.test.extendOptionalForeignEnum, + proto.jspb.test.ForeignEnum.FOREIGN_FOO); + + + msg.setExtension( + proto.jspb.test.extendRepeatedInt32List, [-42]); + msg.setExtension( + proto.jspb.test.extendRepeatedInt64List, [-0x7fffffff00000000]); + msg.setExtension( + proto.jspb.test.extendRepeatedUint32List, [0x80000000]); + msg.setExtension( + proto.jspb.test.extendRepeatedUint64List, [0xf000000000000000]); + msg.setExtension( + proto.jspb.test.extendRepeatedSint32List, [-100]); + msg.setExtension( + proto.jspb.test.extendRepeatedSint64List, [-0x8000000000000000]); + msg.setExtension( + proto.jspb.test.extendRepeatedFixed32List, [1234]); + msg.setExtension( + proto.jspb.test.extendRepeatedFixed64List, [0x1234567800000000]); + msg.setExtension( + proto.jspb.test.extendRepeatedSfixed32List, [-1234]); + msg.setExtension( + proto.jspb.test.extendRepeatedSfixed64List, [-0x1234567800000000]); + msg.setExtension( + proto.jspb.test.extendRepeatedFloatList, [1.5]); + msg.setExtension( + proto.jspb.test.extendRepeatedDoubleList, [-1.5]); + msg.setExtension( + proto.jspb.test.extendRepeatedBoolList, [true]); + msg.setExtension( + proto.jspb.test.extendRepeatedStringList, ['hello world']); + msg.setExtension(proto.jspb.test.extendRepeatedBytesList, [BYTES]); + submsg = new proto.jspb.test.ExtendsWithMessage(); + submsg.setFoo(1000); + msg.setExtension( + proto.jspb.test.ExtendsWithMessage.repeatedExtensionList, [submsg]); + msg.setExtension(proto.jspb.test.extendRepeatedForeignEnumList, + [proto.jspb.test.ForeignEnum.FOREIGN_FOO]); + + + msg.setExtension( + proto.jspb.test.extendPackedRepeatedInt32List, [-42]); + msg.setExtension( + proto.jspb.test.extendPackedRepeatedInt64List, [-0x7fffffff00000000]); + msg.setExtension( + proto.jspb.test.extendPackedRepeatedUint32List, [0x80000000]); + msg.setExtension( + proto.jspb.test.extendPackedRepeatedUint64List, [0xf000000000000000]); + msg.setExtension( + proto.jspb.test.extendPackedRepeatedSint32List, [-100]); + msg.setExtension( + proto.jspb.test.extendPackedRepeatedSint64List, [-0x8000000000000000]); + msg.setExtension( + proto.jspb.test.extendPackedRepeatedFixed32List, [1234]); + msg.setExtension( + proto.jspb.test.extendPackedRepeatedFixed64List, [0x1234567800000000]); + msg.setExtension( + proto.jspb.test.extendPackedRepeatedSfixed32List, [-1234]); + msg.setExtension( + proto.jspb.test.extendPackedRepeatedSfixed64List, + [-0x1234567800000000]); + msg.setExtension( + proto.jspb.test.extendPackedRepeatedFloatList, [1.5]); + msg.setExtension( + proto.jspb.test.extendPackedRepeatedDoubleList, [-1.5]); + msg.setExtension( + proto.jspb.test.extendPackedRepeatedBoolList, [true]); + msg.setExtension(proto.jspb.test.extendPackedRepeatedForeignEnumList, + [proto.jspb.test.ForeignEnum.FOREIGN_FOO]); + + } + + + /** + * Tests extension serialization and deserialization. + */ + it('testExtensions', function() { + var msg = new proto.jspb.test.TestExtendable(); + fillExtensions(msg); + var encoded = msg.serializeBinary(); + var decoded = proto.jspb.test.TestExtendable.deserializeBinary(encoded); + checkExtensions(decoded); + }); +}); diff --git a/js/compatibility_tests/v3.1.0/binary/reader_test.js b/js/compatibility_tests/v3.1.0/binary/reader_test.js new file mode 100644 index 00000000..95711385 --- /dev/null +++ b/js/compatibility_tests/v3.1.0/binary/reader_test.js @@ -0,0 +1,922 @@ +// 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 reader. + * + * 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'); +goog.require('jspb.BinaryReader'); +goog.require('jspb.BinaryWriter'); + + + +describe('binaryReaderTest', function() { + /** + * Tests the reader instance cache. + */ + it('testInstanceCaches', /** @suppress {visibility} */ function() { + var writer = new jspb.BinaryWriter(); + var dummyMessage = /** @type {!jspb.BinaryMessage} */({}); + writer.writeMessage(1, dummyMessage, goog.nullFunction); + writer.writeMessage(2, dummyMessage, goog.nullFunction); + + var buffer = writer.getResultBuffer(); + + // Empty the instance caches. + jspb.BinaryReader.instanceCache_ = []; + + // 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); + assertEquals(0, jspb.BinaryReader.instanceCache_.length); + + // Allocating and then freeing a reader should remove one decoder from its + // cache, but it should stay stuck to the reader afterwards since we can't + // have a reader without a decoder. + jspb.BinaryReader.alloc().free(); + + assertEquals(2, jspb.BinaryDecoder.instanceCache_.length); + assertEquals(1, jspb.BinaryReader.instanceCache_.length); + + // Allocating a reader should remove a reader from the cache. + var reader = jspb.BinaryReader.alloc(buffer); + + assertEquals(2, jspb.BinaryDecoder.instanceCache_.length); + assertEquals(0, jspb.BinaryReader.instanceCache_.length); + + // Processing the message reuses the current reader. + reader.nextField(); + assertEquals(1, reader.getFieldNumber()); + reader.readMessage(dummyMessage, function() { + assertEquals(0, jspb.BinaryReader.instanceCache_.length); + }); + + reader.nextField(); + assertEquals(2, reader.getFieldNumber()); + reader.readMessage(dummyMessage, function() { + assertEquals(0, jspb.BinaryReader.instanceCache_.length); + }); + + assertEquals(false, reader.nextField()); + + assertEquals(2, jspb.BinaryDecoder.instanceCache_.length); + assertEquals(0, jspb.BinaryReader.instanceCache_.length); + + // Freeing the reader should put it back into the cache. + reader.free(); + + assertEquals(2, jspb.BinaryDecoder.instanceCache_.length); + assertEquals(1, jspb.BinaryReader.instanceCache_.length); + }); + + + /** + * @param {number} x + * @return {number} + */ + function truncate(x) { + var temp = new Float32Array(1); + temp[0] = x; + return temp[0]; + } + + + /** + * Verifies that misuse of the reader class triggers assertions. + */ + it('testReadErrors', /** @suppress {checkTypes|visibility} */ function() { + // Calling readMessage on a non-delimited field should trigger an + // assertion. + var reader = jspb.BinaryReader.alloc([8, 1]); + var dummyMessage = /** @type {!jspb.BinaryMessage} */({}); + reader.nextField(); + assertThrows(function() { + reader.readMessage(dummyMessage, goog.nullFunction); + }); + + // Reading past the end of the stream should trigger an assertion. + reader = jspb.BinaryReader.alloc([9, 1]); + reader.nextField(); + assertThrows(function() {reader.readFixed64()}); + + // Reading past the end of a submessage should trigger an assertion. + reader = jspb.BinaryReader.alloc([10, 4, 13, 1, 1, 1]); + reader.nextField(); + reader.readMessage(dummyMessage, function() { + reader.nextField(); + assertThrows(function() {reader.readFixed32()}); + }); + + // Skipping an invalid field should trigger an assertion. + reader = jspb.BinaryReader.alloc([12, 1]); + reader.nextWireType_ = 1000; + assertThrows(function() {reader.skipField()}); + + // Reading fields with the wrong wire type should assert. + reader = jspb.BinaryReader.alloc([9, 0, 0, 0, 0, 0, 0, 0, 0]); + reader.nextField(); + assertThrows(function() {reader.readInt32()}); + assertThrows(function() {reader.readInt32String()}); + assertThrows(function() {reader.readInt64()}); + assertThrows(function() {reader.readInt64String()}); + assertThrows(function() {reader.readUint32()}); + assertThrows(function() {reader.readUint32String()}); + assertThrows(function() {reader.readUint64()}); + assertThrows(function() {reader.readUint64String()}); + assertThrows(function() {reader.readSint32()}); + assertThrows(function() {reader.readBool()}); + assertThrows(function() {reader.readEnum()}); + + reader = jspb.BinaryReader.alloc([8, 1]); + reader.nextField(); + assertThrows(function() {reader.readFixed32()}); + assertThrows(function() {reader.readFixed64()}); + assertThrows(function() {reader.readSfixed32()}); + assertThrows(function() {reader.readSfixed64()}); + assertThrows(function() {reader.readFloat()}); + assertThrows(function() {reader.readDouble()}); + + assertThrows(function() {reader.readString()}); + assertThrows(function() {reader.readBytes()}); + }); + + + /** + * Tests encoding and decoding of unsigned field types. + * @param {Function} readField + * @param {Function} writeField + * @param {number} epsilon + * @param {number} upperLimit + * @param {Function} filter + * @private + * @suppress {missingProperties} + */ + var doTestUnsignedField_ = function(readField, + writeField, epsilon, upperLimit, filter) { + assertNotNull(readField); + assertNotNull(writeField); + + var writer = new jspb.BinaryWriter(); + + // Encode zero and limits. + writeField.call(writer, 1, filter(0)); + writeField.call(writer, 2, filter(epsilon)); + writeField.call(writer, 3, filter(upperLimit)); + + // Encode positive values. + for (var cursor = epsilon; cursor < upperLimit; cursor *= 1.1) { + writeField.call(writer, 4, filter(cursor)); + } + + var reader = jspb.BinaryReader.alloc(writer.getResultBuffer()); + + // Check zero and limits. + reader.nextField(); + assertEquals(1, reader.getFieldNumber()); + assertEquals(filter(0), readField.call(reader)); + + reader.nextField(); + assertEquals(2, reader.getFieldNumber()); + assertEquals(filter(epsilon), readField.call(reader)); + + reader.nextField(); + assertEquals(3, reader.getFieldNumber()); + assertEquals(filter(upperLimit), readField.call(reader)); + + // Check positive values. + for (var cursor = epsilon; cursor < upperLimit; cursor *= 1.1) { + reader.nextField(); + if (4 != reader.getFieldNumber()) throw 'fail!'; + if (filter(cursor) != readField.call(reader)) throw 'fail!'; + } + }; + + + /** + * Tests encoding and decoding of signed field types. + * @param {Function} readField + * @param {Function} writeField + * @param {number} epsilon + * @param {number} lowerLimit + * @param {number} upperLimit + * @param {Function} filter + * @private + * @suppress {missingProperties} + */ + var doTestSignedField_ = function(readField, + writeField, epsilon, lowerLimit, upperLimit, filter) { + var writer = new jspb.BinaryWriter(); + + // Encode zero and limits. + writeField.call(writer, 1, filter(lowerLimit)); + writeField.call(writer, 2, filter(-epsilon)); + writeField.call(writer, 3, filter(0)); + writeField.call(writer, 4, filter(epsilon)); + writeField.call(writer, 5, filter(upperLimit)); + + var inputValues = []; + + // Encode negative values. + for (var cursor = lowerLimit; cursor < -epsilon; cursor /= 1.1) { + var val = filter(cursor); + writeField.call(writer, 6, val); + inputValues.push({ + fieldNumber: 6, + value: val + }); + } + + // Encode positive values. + for (var cursor = epsilon; cursor < upperLimit; cursor *= 1.1) { + var val = filter(cursor); + writeField.call(writer, 7, val); + inputValues.push({ + fieldNumber: 7, + value: val + }); + } + + var reader = jspb.BinaryReader.alloc(writer.getResultBuffer()); + + // Check zero and limits. + reader.nextField(); + assertEquals(1, reader.getFieldNumber()); + assertEquals(filter(lowerLimit), readField.call(reader)); + + reader.nextField(); + assertEquals(2, reader.getFieldNumber()); + assertEquals(filter(-epsilon), readField.call(reader)); + + reader.nextField(); + assertEquals(3, reader.getFieldNumber()); + assertEquals(filter(0), readField.call(reader)); + + reader.nextField(); + assertEquals(4, reader.getFieldNumber()); + assertEquals(filter(epsilon), readField.call(reader)); + + reader.nextField(); + assertEquals(5, reader.getFieldNumber()); + assertEquals(filter(upperLimit), readField.call(reader)); + + for (var i = 0; i < inputValues.length; i++) { + var expected = inputValues[i]; + reader.nextField(); + assertEquals(expected.fieldNumber, reader.getFieldNumber()); + assertEquals(expected.value, readField.call(reader)); + } + }; + + + /** + * Tests fields that use varint encoding. + */ + it('testVarintFields', function() { + assertNotUndefined(jspb.BinaryReader.prototype.readUint32); + assertNotUndefined(jspb.BinaryWriter.prototype.writeUint32); + assertNotUndefined(jspb.BinaryReader.prototype.readUint64); + assertNotUndefined(jspb.BinaryWriter.prototype.writeUint64); + assertNotUndefined(jspb.BinaryReader.prototype.readBool); + assertNotUndefined(jspb.BinaryWriter.prototype.writeBool); + doTestUnsignedField_( + jspb.BinaryReader.prototype.readUint32, + jspb.BinaryWriter.prototype.writeUint32, + 1, Math.pow(2, 32) - 1, Math.round); + + doTestUnsignedField_( + jspb.BinaryReader.prototype.readUint64, + jspb.BinaryWriter.prototype.writeUint64, + 1, Math.pow(2, 64) - 1025, Math.round); + + doTestSignedField_( + jspb.BinaryReader.prototype.readInt32, + jspb.BinaryWriter.prototype.writeInt32, + 1, -Math.pow(2, 31), Math.pow(2, 31) - 1, Math.round); + + doTestSignedField_( + jspb.BinaryReader.prototype.readInt64, + jspb.BinaryWriter.prototype.writeInt64, + 1, -Math.pow(2, 63), Math.pow(2, 63) - 513, Math.round); + + doTestSignedField_( + jspb.BinaryReader.prototype.readEnum, + jspb.BinaryWriter.prototype.writeEnum, + 1, -Math.pow(2, 31), Math.pow(2, 31) - 1, Math.round); + + doTestUnsignedField_( + jspb.BinaryReader.prototype.readBool, + jspb.BinaryWriter.prototype.writeBool, + 1, 1, function(x) { return !!x; }); + }); + + + /** + * Tests reading a field from hexadecimal string (format: '08 BE EF'). + * @param {Function} readField + * @param {number} expected + * @param {string} hexString + */ + function doTestHexStringVarint_(readField, expected, hexString) { + var bytesCount = (hexString.length + 1) / 3; + var bytes = new Uint8Array(bytesCount); + for (var i = 0; i < bytesCount; i++) { + bytes[i] = parseInt(hexString.substring(i * 3, i * 3 + 2), 16); + } + var reader = jspb.BinaryReader.alloc(bytes); + reader.nextField(); + assertEquals(expected, readField.call(reader)); + } + + + /** + * Tests non-canonical redundant varint decoding. + */ + it('testRedundantVarintFields', function() { + assertNotNull(jspb.BinaryReader.prototype.readUint32); + assertNotNull(jspb.BinaryReader.prototype.readUint64); + assertNotNull(jspb.BinaryReader.prototype.readSint32); + assertNotNull(jspb.BinaryReader.prototype.readSint64); + + // uint32 and sint32 take no more than 5 bytes + // 08 - field prefix (type = 0 means varint) + doTestHexStringVarint_( + jspb.BinaryReader.prototype.readUint32, + 12, '08 8C 80 80 80 00'); + + // 11 stands for -6 in zigzag encoding + doTestHexStringVarint_( + jspb.BinaryReader.prototype.readSint32, + -6, '08 8B 80 80 80 00'); + + // uint64 and sint64 take no more than 10 bytes + // 08 - field prefix (type = 0 means varint) + doTestHexStringVarint_( + jspb.BinaryReader.prototype.readUint64, + 12, '08 8C 80 80 80 80 80 80 80 80 00'); + + // 11 stands for -6 in zigzag encoding + doTestHexStringVarint_( + jspb.BinaryReader.prototype.readSint64, + -6, '08 8B 80 80 80 80 80 80 80 80 00'); + }); + + + /** + * Tests 64-bit fields that are handled as strings. + */ + it('testStringInt64Fields', function() { + var writer = new jspb.BinaryWriter(); + + var testSignedData = [ + '2730538252207801776', + '-2688470994844604560', + '3398529779486536359', + '3568577411627971000', + '272477188847484900', + '-6649058714086158188', + '-7695254765712060806', + '-4525541438037104029', + '-4993706538836508568', + '4990160321893729138' + ]; + var testUnsignedData = [ + '7822732630241694882', + '6753602971916687352', + '2399935075244442116', + '8724292567325338867', + '16948784802625696584', + '4136275908516066934', + '3575388346793700364', + '5167142028379259461', + '1557573948689737699', + '17100725280812548567' + ]; + + for (var i = 0; i < testSignedData.length; i++) { + writer.writeInt64String(2 * i + 1, testSignedData[i]); + writer.writeUint64String(2 * i + 2, testUnsignedData[i]); + } + + var reader = jspb.BinaryReader.alloc(writer.getResultBuffer()); + + for (var i = 0; i < testSignedData.length; i++) { + reader.nextField(); + assertEquals(2 * i + 1, reader.getFieldNumber()); + assertEquals(testSignedData[i], reader.readInt64String()); + reader.nextField(); + assertEquals(2 * i + 2, reader.getFieldNumber()); + assertEquals(testUnsignedData[i], reader.readUint64String()); + } + }); + + + /** + * Tests fields that use zigzag encoding. + */ + it('testZigzagFields', function() { + doTestSignedField_( + jspb.BinaryReader.prototype.readSint32, + jspb.BinaryWriter.prototype.writeSint32, + 1, -Math.pow(2, 31), Math.pow(2, 31) - 1, Math.round); + + doTestSignedField_( + jspb.BinaryReader.prototype.readSint64, + jspb.BinaryWriter.prototype.writeSint64, + 1, -Math.pow(2, 63), Math.pow(2, 63) - 513, Math.round); + }); + + + /** + * Tests fields that use fixed-length encoding. + */ + it('testFixedFields', function() { + doTestUnsignedField_( + jspb.BinaryReader.prototype.readFixed32, + jspb.BinaryWriter.prototype.writeFixed32, + 1, Math.pow(2, 32) - 1, Math.round); + + doTestUnsignedField_( + jspb.BinaryReader.prototype.readFixed64, + jspb.BinaryWriter.prototype.writeFixed64, + 1, Math.pow(2, 64) - 1025, Math.round); + + doTestSignedField_( + jspb.BinaryReader.prototype.readSfixed32, + jspb.BinaryWriter.prototype.writeSfixed32, + 1, -Math.pow(2, 31), Math.pow(2, 31) - 1, Math.round); + + doTestSignedField_( + jspb.BinaryReader.prototype.readSfixed64, + jspb.BinaryWriter.prototype.writeSfixed64, + 1, -Math.pow(2, 63), Math.pow(2, 63) - 513, Math.round); + }); + + + /** + * Tests floating point fields. + */ + it('testFloatFields', function() { + doTestSignedField_( + jspb.BinaryReader.prototype.readFloat, + jspb.BinaryWriter.prototype.writeFloat, + jspb.BinaryConstants.FLOAT32_MIN, + -jspb.BinaryConstants.FLOAT32_MAX, + jspb.BinaryConstants.FLOAT32_MAX, + truncate); + + doTestSignedField_( + jspb.BinaryReader.prototype.readDouble, + jspb.BinaryWriter.prototype.writeDouble, + jspb.BinaryConstants.FLOAT64_EPS * 10, + -jspb.BinaryConstants.FLOAT64_MIN, + jspb.BinaryConstants.FLOAT64_MIN, + function(x) { return x; }); + }); + + + /** + * Tests length-delimited string fields. + */ + it('testStringFields', function() { + var s1 = 'The quick brown fox jumps over the lazy dog.'; + var s2 = '人人生而自由,在尊嚴和權利上一律平等。'; + + var writer = new jspb.BinaryWriter(); + + writer.writeString(1, s1); + writer.writeString(2, s2); + + var reader = jspb.BinaryReader.alloc(writer.getResultBuffer()); + + reader.nextField(); + assertEquals(1, reader.getFieldNumber()); + assertEquals(s1, reader.readString()); + + reader.nextField(); + assertEquals(2, reader.getFieldNumber()); + assertEquals(s2, reader.readString()); + }); + + + /** + * Tests length-delimited byte fields. + */ + it('testByteFields', function() { + var message = []; + var lowerLimit = 1; + var upperLimit = 256; + var scale = 1.1; + + var writer = new jspb.BinaryWriter(); + + for (var cursor = lowerLimit; cursor < upperLimit; cursor *= 1.1) { + var len = Math.round(cursor); + var bytes = []; + for (var i = 0; i < len; i++) bytes.push(i % 256); + + writer.writeBytes(len, bytes); + } + + var reader = jspb.BinaryReader.alloc(writer.getResultBuffer()); + + for (var cursor = lowerLimit; reader.nextField(); cursor *= 1.1) { + var len = Math.round(cursor); + if (len != reader.getFieldNumber()) throw 'fail!'; + + var bytes = reader.readBytes(); + if (len != bytes.length) throw 'fail!'; + for (var i = 0; i < bytes.length; i++) { + if (i % 256 != bytes[i]) throw 'fail!'; + } + } + }); + + + /** + * Tests nested messages. + */ + it('testNesting', function() { + var writer = new jspb.BinaryWriter(); + var dummyMessage = /** @type {!jspb.BinaryMessage} */({}); + + writer.writeInt32(1, 100); + + // Add one message with 3 int fields. + writer.writeMessage(2, dummyMessage, function() { + writer.writeInt32(3, 300); + writer.writeInt32(4, 400); + writer.writeInt32(5, 500); + }); + + // Add one empty message. + writer.writeMessage(6, dummyMessage, goog.nullFunction); + + writer.writeInt32(7, 700); + + var reader = jspb.BinaryReader.alloc(writer.getResultBuffer()); + + // Validate outermost message. + + reader.nextField(); + assertEquals(1, reader.getFieldNumber()); + assertEquals(100, reader.readInt32()); + + reader.nextField(); + assertEquals(2, reader.getFieldNumber()); + reader.readMessage(dummyMessage, function() { + // Validate embedded message 1. + reader.nextField(); + assertEquals(3, reader.getFieldNumber()); + assertEquals(300, reader.readInt32()); + + reader.nextField(); + assertEquals(4, reader.getFieldNumber()); + assertEquals(400, reader.readInt32()); + + reader.nextField(); + assertEquals(5, reader.getFieldNumber()); + assertEquals(500, reader.readInt32()); + + assertEquals(false, reader.nextField()); + }); + + reader.nextField(); + assertEquals(6, reader.getFieldNumber()); + reader.readMessage(dummyMessage, function() { + // Validate embedded message 2. + + assertEquals(false, reader.nextField()); + }); + + reader.nextField(); + assertEquals(7, reader.getFieldNumber()); + assertEquals(700, reader.readInt32()); + + assertEquals(false, reader.nextField()); + }); + + /** + * Tests skipping fields of each type by interleaving them with sentinel + * values and skipping everything that's not a sentinel. + */ + it('testSkipField', function() { + var writer = new jspb.BinaryWriter(); + + var sentinel = 123456789; + + // Write varint fields of different sizes. + writer.writeInt32(1, sentinel); + writer.writeInt32(1, 1); + writer.writeInt32(1, 1000); + writer.writeInt32(1, 1000000); + writer.writeInt32(1, 1000000000); + + // Write fixed 64-bit encoded fields. + writer.writeInt32(2, sentinel); + writer.writeDouble(2, 1); + writer.writeFixed64(2, 1); + writer.writeSfixed64(2, 1); + + // Write fixed 32-bit encoded fields. + writer.writeInt32(3, sentinel); + writer.writeFloat(3, 1); + writer.writeFixed32(3, 1); + writer.writeSfixed32(3, 1); + + // Write delimited fields. + writer.writeInt32(4, sentinel); + writer.writeBytes(4, [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]); + writer.writeString(4, 'The quick brown fox jumps over the lazy dog'); + + // Write a group with a nested group inside. + writer.writeInt32(5, sentinel); + var dummyMessage = /** @type {!jspb.BinaryMessage} */({}); + writer.writeGroup(5, dummyMessage, function() { + writer.writeInt64(42, 42); + writer.writeGroup(6, dummyMessage, function() { + writer.writeInt64(84, 42); + }); + }); + + // Write final sentinel. + writer.writeInt32(6, sentinel); + + var reader = jspb.BinaryReader.alloc(writer.getResultBuffer()); + + function skip(field, count) { + for (var i = 0; i < count; i++) { + reader.nextField(); + if (field != reader.getFieldNumber()) throw 'fail!'; + reader.skipField(); + } + } + + reader.nextField(); + assertEquals(1, reader.getFieldNumber()); + assertEquals(sentinel, reader.readInt32()); + skip(1, 4); + + reader.nextField(); + assertEquals(2, reader.getFieldNumber()); + assertEquals(sentinel, reader.readInt32()); + skip(2, 3); + + reader.nextField(); + assertEquals(3, reader.getFieldNumber()); + assertEquals(sentinel, reader.readInt32()); + skip(3, 3); + + reader.nextField(); + assertEquals(4, reader.getFieldNumber()); + assertEquals(sentinel, reader.readInt32()); + skip(4, 2); + + reader.nextField(); + assertEquals(5, reader.getFieldNumber()); + assertEquals(sentinel, reader.readInt32()); + skip(5, 1); + + reader.nextField(); + assertEquals(6, reader.getFieldNumber()); + assertEquals(sentinel, reader.readInt32()); + }); + + + /** + * Tests packed fields. + */ + it('testPackedFields', function() { + var writer = new jspb.BinaryWriter(); + + var sentinel = 123456789; + + var unsignedData = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; + var signedData = [-1, 2, -3, 4, -5, 6, -7, 8, -9, 10]; + var floatData = [1.1, 2.2, 3.3, 4.4, 5.5, 6.6, 7.7, 8.8, 9.9, 10.10]; + var doubleData = [1.1, 2.2, 3.3, 4.4, 5.5, 6.6, 7.7, 8.8, 9.9, 10.10]; + var boolData = [true, false, true, true, false, false, true, false]; + + for (var i = 0; i < floatData.length; i++) { + floatData[i] = truncate(floatData[i]); + } + + writer.writeInt32(1, sentinel); + + writer.writePackedInt32(2, signedData); + writer.writePackedInt64(2, signedData); + writer.writePackedUint32(2, unsignedData); + writer.writePackedUint64(2, unsignedData); + writer.writePackedSint32(2, signedData); + writer.writePackedSint64(2, signedData); + writer.writePackedFixed32(2, unsignedData); + writer.writePackedFixed64(2, unsignedData); + writer.writePackedSfixed32(2, signedData); + writer.writePackedSfixed64(2, signedData); + writer.writePackedFloat(2, floatData); + writer.writePackedDouble(2, doubleData); + writer.writePackedBool(2, boolData); + writer.writePackedEnum(2, unsignedData); + + writer.writeInt32(3, sentinel); + + var reader = jspb.BinaryReader.alloc(writer.getResultBuffer()); + + reader.nextField(); + assertEquals(sentinel, reader.readInt32()); + + reader.nextField(); + assertElementsEquals(reader.readPackedInt32(), signedData); + + reader.nextField(); + assertElementsEquals(reader.readPackedInt64(), signedData); + + reader.nextField(); + assertElementsEquals(reader.readPackedUint32(), unsignedData); + + reader.nextField(); + assertElementsEquals(reader.readPackedUint64(), unsignedData); + + reader.nextField(); + assertElementsEquals(reader.readPackedSint32(), signedData); + + reader.nextField(); + assertElementsEquals(reader.readPackedSint64(), signedData); + + reader.nextField(); + assertElementsEquals(reader.readPackedFixed32(), unsignedData); + + reader.nextField(); + assertElementsEquals(reader.readPackedFixed64(), unsignedData); + + reader.nextField(); + assertElementsEquals(reader.readPackedSfixed32(), signedData); + + reader.nextField(); + assertElementsEquals(reader.readPackedSfixed64(), signedData); + + reader.nextField(); + assertElementsEquals(reader.readPackedFloat(), floatData); + + reader.nextField(); + assertElementsEquals(reader.readPackedDouble(), doubleData); + + reader.nextField(); + assertElementsEquals(reader.readPackedBool(), boolData); + + reader.nextField(); + assertElementsEquals(reader.readPackedEnum(), unsignedData); + + reader.nextField(); + assertEquals(sentinel, reader.readInt32()); + }); + + + /** + * Byte blobs inside nested messages should always have their byte offset set + * relative to the start of the outermost blob, not the start of their parent + * blob. + */ + it('testNestedBlobs', function() { + // Create a proto consisting of two nested messages, with the inner one + // containing a blob of bytes. + + var fieldTag = (1 << 3) | jspb.BinaryConstants.WireType.DELIMITED; + var blob = [1, 2, 3, 4, 5]; + var writer = new jspb.BinaryWriter(); + var dummyMessage = /** @type {!jspb.BinaryMessage} */({}); + + writer.writeMessage(1, dummyMessage, function() { + writer.writeMessage(1, dummyMessage, function() { + writer.writeBytes(1, blob); + }); + }); + + // Peel off the outer two message layers. Each layer should have two bytes + // of overhead, one for the field tag and one for the length of the inner + // blob. + + var decoder1 = new jspb.BinaryDecoder(writer.getResultBuffer()); + assertEquals(fieldTag, decoder1.readUnsignedVarint32()); + assertEquals(blob.length + 4, decoder1.readUnsignedVarint32()); + + var decoder2 = new jspb.BinaryDecoder(decoder1.readBytes(blob.length + 4)); + assertEquals(fieldTag, decoder2.readUnsignedVarint32()); + assertEquals(blob.length + 2, decoder2.readUnsignedVarint32()); + + assertEquals(fieldTag, decoder2.readUnsignedVarint32()); + assertEquals(blob.length, decoder2.readUnsignedVarint32()); + var bytes = decoder2.readBytes(blob.length); + + assertElementsEquals(bytes, blob); + }); + + + /** + * Tests read callbacks. + */ + it('testReadCallbacks', function() { + var writer = new jspb.BinaryWriter(); + var dummyMessage = /** @type {!jspb.BinaryMessage} */({}); + + // Add an int, a submessage, and another int. + writer.writeInt32(1, 100); + + writer.writeMessage(2, dummyMessage, function() { + writer.writeInt32(3, 300); + writer.writeInt32(4, 400); + writer.writeInt32(5, 500); + }); + + writer.writeInt32(7, 700); + + // Create the reader and register a custom read callback. + var reader = jspb.BinaryReader.alloc(writer.getResultBuffer()); + + /** + * @param {!jspb.BinaryReader} reader + * @return {*} + */ + function readCallback(reader) { + reader.nextField(); + assertEquals(3, reader.getFieldNumber()); + assertEquals(300, reader.readInt32()); + + reader.nextField(); + assertEquals(4, reader.getFieldNumber()); + assertEquals(400, reader.readInt32()); + + reader.nextField(); + assertEquals(5, reader.getFieldNumber()); + assertEquals(500, reader.readInt32()); + + assertEquals(false, reader.nextField()); + }; + + reader.registerReadCallback('readCallback', readCallback); + + // Read the container message. + reader.nextField(); + assertEquals(1, reader.getFieldNumber()); + assertEquals(100, reader.readInt32()); + + reader.nextField(); + assertEquals(2, reader.getFieldNumber()); + reader.readMessage(dummyMessage, function() { + // Decode the embedded message using the registered callback. + reader.runReadCallback('readCallback'); + }); + + reader.nextField(); + assertEquals(7, reader.getFieldNumber()); + assertEquals(700, reader.readInt32()); + + assertEquals(false, reader.nextField()); + }); +}); diff --git a/js/compatibility_tests/v3.1.0/binary/utils_test.js b/js/compatibility_tests/v3.1.0/binary/utils_test.js new file mode 100644 index 00000000..d27e5ea2 --- /dev/null +++ b/js/compatibility_tests/v3.1.0/binary/utils_test.js @@ -0,0 +1,668 @@ +// 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 helper functions. + * + * Test suite is written using Jasmine -- see http://jasmine.github.io/ + * + * @author aappleby@google.com (Austin Appleby) + */ + +goog.require('goog.crypt.base64'); +goog.require('goog.testing.asserts'); +goog.require('jspb.BinaryConstants'); +goog.require('jspb.BinaryWriter'); +goog.require('jspb.utils'); + + +/** + * @param {number} x + * @return {number} + */ +function truncate(x) { + var temp = new Float32Array(1); + temp[0] = x; + return temp[0]; +} + + +/** + * Converts an 64-bit integer in split representation to a 64-bit hash string + * (8 bits encoded per character). + * @param {number} bitsLow The low 32 bits of the split 64-bit integer. + * @param {number} bitsHigh The high 32 bits of the split 64-bit integer. + * @return {string} The encoded hash string, 8 bits per character. + */ +function toHashString(bitsLow, bitsHigh) { + return String.fromCharCode((bitsLow >>> 0) & 0xFF, + (bitsLow >>> 8) & 0xFF, + (bitsLow >>> 16) & 0xFF, + (bitsLow >>> 24) & 0xFF, + (bitsHigh >>> 0) & 0xFF, + (bitsHigh >>> 8) & 0xFF, + (bitsHigh >>> 16) & 0xFF, + (bitsHigh >>> 24) & 0xFF); +} + + +describe('binaryUtilsTest', function() { + /** + * Tests lossless binary-to-decimal conversion. + */ + it('testDecimalConversion', function() { + // Check some magic numbers. + var result = + jspb.utils.joinUnsignedDecimalString(0x89e80001, 0x8ac72304); + assertEquals('10000000000000000001', result); + + result = jspb.utils.joinUnsignedDecimalString(0xacd05f15, 0x1b69b4b); + assertEquals('123456789123456789', result); + + result = jspb.utils.joinUnsignedDecimalString(0xeb1f0ad2, 0xab54a98c); + assertEquals('12345678901234567890', result); + + result = jspb.utils.joinUnsignedDecimalString(0xe3b70cb1, 0x891087b8); + assertEquals('9876543210987654321', result); + + // Check limits. + result = jspb.utils.joinUnsignedDecimalString(0x00000000, 0x00000000); + assertEquals('0', result); + + result = jspb.utils.joinUnsignedDecimalString(0xFFFFFFFF, 0xFFFFFFFF); + assertEquals('18446744073709551615', result); + + // Check each bit of the low dword. + for (var i = 0; i < 32; i++) { + var low = (1 << i) >>> 0; + result = jspb.utils.joinUnsignedDecimalString(low, 0); + assertEquals('' + Math.pow(2, i), result); + } + + // Check the first 20 bits of the high dword. + for (var i = 0; i < 20; i++) { + var high = (1 << i) >>> 0; + result = jspb.utils.joinUnsignedDecimalString(0, high); + assertEquals('' + Math.pow(2, 32 + i), result); + } + + // V8's internal double-to-string conversion is inaccurate for values above + // 2^52, even if they're representable integers - check the rest of the bits + // manually against the correct string representations of 2^N. + + result = jspb.utils.joinUnsignedDecimalString(0x00000000, 0x00100000); + assertEquals('4503599627370496', result); + + result = jspb.utils.joinUnsignedDecimalString(0x00000000, 0x00200000); + assertEquals('9007199254740992', result); + + result = jspb.utils.joinUnsignedDecimalString(0x00000000, 0x00400000); + assertEquals('18014398509481984', result); + + result = jspb.utils.joinUnsignedDecimalString(0x00000000, 0x00800000); + assertEquals('36028797018963968', result); + + result = jspb.utils.joinUnsignedDecimalString(0x00000000, 0x01000000); + assertEquals('72057594037927936', result); + + result = jspb.utils.joinUnsignedDecimalString(0x00000000, 0x02000000); + assertEquals('144115188075855872', result); + + result = jspb.utils.joinUnsignedDecimalString(0x00000000, 0x04000000); + assertEquals('288230376151711744', result); + + result = jspb.utils.joinUnsignedDecimalString(0x00000000, 0x08000000); + assertEquals('576460752303423488', result); + + result = jspb.utils.joinUnsignedDecimalString(0x00000000, 0x10000000); + assertEquals('1152921504606846976', result); + + result = jspb.utils.joinUnsignedDecimalString(0x00000000, 0x20000000); + assertEquals('2305843009213693952', result); + + result = jspb.utils.joinUnsignedDecimalString(0x00000000, 0x40000000); + assertEquals('4611686018427387904', result); + + result = jspb.utils.joinUnsignedDecimalString(0x00000000, 0x80000000); + assertEquals('9223372036854775808', result); + }); + + + /** + * Going from hash strings to decimal strings should also be lossless. + */ + it('testHashToDecimalConversion', function() { + var result; + var convert = jspb.utils.hash64ToDecimalString; + + result = convert(toHashString(0x00000000, 0x00000000), false); + assertEquals('0', result); + + result = convert(toHashString(0x00000000, 0x00000000), true); + assertEquals('0', result); + + result = convert(toHashString(0xFFFFFFFF, 0xFFFFFFFF), false); + assertEquals('18446744073709551615', result); + + result = convert(toHashString(0xFFFFFFFF, 0xFFFFFFFF), true); + assertEquals('-1', result); + + result = convert(toHashString(0x00000000, 0x80000000), false); + assertEquals('9223372036854775808', result); + + result = convert(toHashString(0x00000000, 0x80000000), true); + assertEquals('-9223372036854775808', result); + + result = convert(toHashString(0xacd05f15, 0x01b69b4b), false); + assertEquals('123456789123456789', result); + + result = convert(toHashString(~0xacd05f15 + 1, ~0x01b69b4b), true); + assertEquals('-123456789123456789', result); + + // And converting arrays of hashes should work the same way. + result = jspb.utils.hash64ArrayToDecimalStrings([ + toHashString(0xFFFFFFFF, 0xFFFFFFFF), + toHashString(0x00000000, 0x80000000), + toHashString(0xacd05f15, 0x01b69b4b)], false); + assertEquals(3, result.length); + assertEquals('18446744073709551615', result[0]); + assertEquals('9223372036854775808', result[1]); + assertEquals('123456789123456789', result[2]); + }); + + /* + * Going from decimal strings to hash strings should be lossless. + */ + it('testDecimalToHashConversion', function() { + var result; + var convert = jspb.utils.decimalStringToHash64; + + result = convert('0'); + assertEquals(String.fromCharCode.apply(null, + [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]), result); + + result = convert('-1'); + assertEquals(String.fromCharCode.apply(null, + [0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF]), result); + + result = convert('18446744073709551615'); + assertEquals(String.fromCharCode.apply(null, + [0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF]), result); + + result = convert('9223372036854775808'); + assertEquals(String.fromCharCode.apply(null, + [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80]), result); + + result = convert('-9223372036854775808'); + assertEquals(String.fromCharCode.apply(null, + [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80]), result); + + result = convert('123456789123456789'); + assertEquals(String.fromCharCode.apply(null, + [0x15, 0x5F, 0xD0, 0xAC, 0x4B, 0x9B, 0xB6, 0x01]), result); + + result = convert('-123456789123456789'); + assertEquals(String.fromCharCode.apply(null, + [0xEB, 0xA0, 0x2F, 0x53, 0xB4, 0x64, 0x49, 0xFE]), result); + }); + + /** + * Going from hash strings to hex strings should be lossless. + */ + it('testHashToHexConversion', function() { + var result; + var convert = jspb.utils.hash64ToHexString; + + result = convert(toHashString(0x00000000, 0x00000000)); + assertEquals('0x0000000000000000', result); + + result = convert(toHashString(0xFFFFFFFF, 0xFFFFFFFF)); + assertEquals('0xffffffffffffffff', result); + + result = convert(toHashString(0x12345678, 0x9ABCDEF0)); + assertEquals('0x9abcdef012345678', result); + }); + + + /** + * Going from hex strings to hash strings should be lossless. + */ + it('testHexToHashConversion', function() { + var result; + var convert = jspb.utils.hexStringToHash64; + + result = convert('0x0000000000000000'); + assertEquals(String.fromCharCode.apply(null, + [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]), result); + + result = convert('0xffffffffffffffff'); + assertEquals(String.fromCharCode.apply(null, + [0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF]), result); + + // Hex string is big-endian, hash string is little-endian. + result = convert('0x123456789ABCDEF0'); + assertEquals(String.fromCharCode.apply(null, + [0xF0, 0xDE, 0xBC, 0x9A, 0x78, 0x56, 0x34, 0x12]), result); + + // Capitalization should not matter. + result = convert('0x0000abcdefABCDEF'); + assertEquals(String.fromCharCode.apply(null, + [0xEF, 0xCD, 0xAB, 0xEF, 0xCD, 0xAB, 0x00, 0x00]), result); + }); + + + /** + * Going from numbers to hash strings should be lossless for up to 53 bits of + * precision. + */ + it('testNumberToHashConversion', function() { + var result; + var convert = jspb.utils.numberToHash64; + + result = convert(0x0000000000000); + assertEquals('0x0000000000000000', jspb.utils.hash64ToHexString(result)); + + result = convert(0xFFFFFFFFFFFFF); + assertEquals('0x000fffffffffffff', jspb.utils.hash64ToHexString(result)); + + result = convert(0x123456789ABCD); + assertEquals('0x000123456789abcd', jspb.utils.hash64ToHexString(result)); + + result = convert(0xDCBA987654321); + assertEquals('0x000dcba987654321', jspb.utils.hash64ToHexString(result)); + + // 53 bits of precision should not be truncated. + result = convert(0x10000000000001); + assertEquals('0x0010000000000001', jspb.utils.hash64ToHexString(result)); + + // 54 bits of precision should be truncated. + result = convert(0x20000000000001); + assertNotEquals( + '0x0020000000000001', jspb.utils.hash64ToHexString(result)); + }); + + + /** + * Sanity check the behavior of Javascript's strings when doing funny things + * with unicode characters. + */ + it('sanityCheckUnicodeStrings', function() { + var strings = new Array(65536); + + // All possible unsigned 16-bit values should be storable in a string, they + // shouldn't do weird things with the length of the string, and they should + // come back out of the string unchanged. + for (var i = 0; i < 65536; i++) { + strings[i] = 'a' + String.fromCharCode(i) + 'a'; + if (3 != strings[i].length) throw 'fail!'; + if (i != strings[i].charCodeAt(1)) throw 'fail!'; + } + + // Each unicode character should compare equal to itself and not equal to a + // different unicode character. + for (var i = 0; i < 65536; i++) { + if (strings[i] != strings[i]) throw 'fail!'; + if (strings[i] == strings[(i + 1) % 65536]) throw 'fail!'; + } + }); + + + /** + * Tests conversion from 32-bit floating point numbers to split64 numbers. + */ + it('testFloat32ToSplit64', function() { + var f32_eps = jspb.BinaryConstants.FLOAT32_EPS; + var f32_min = jspb.BinaryConstants.FLOAT32_MIN; + var f32_max = jspb.BinaryConstants.FLOAT32_MAX; + + // NaN. + jspb.utils.splitFloat32(NaN); + if (!isNaN(jspb.utils.joinFloat32(jspb.utils.split64Low, + jspb.utils.split64High))) { + throw 'fail!'; + } + + /** + * @param {number} x + * @param {number=} opt_bits + */ + function test(x, opt_bits) { + jspb.utils.splitFloat32(x); + if (goog.isDef(opt_bits)) { + if (opt_bits != jspb.utils.split64Low) throw 'fail!'; + } + if (truncate(x) != jspb.utils.joinFloat32(jspb.utils.split64Low, + jspb.utils.split64High)) { + throw 'fail!'; + } + } + + // Positive and negative infinity. + test(Infinity, 0x7f800000); + test(-Infinity, 0xff800000); + + // Positive and negative zero. + test(0, 0x00000000); + test(-0, 0x80000000); + + // Positive and negative epsilon. + test(f32_eps, 0x00000001); + test(-f32_eps, 0x80000001); + + // Positive and negative min. + test(f32_min, 0x00800000); + test(-f32_min, 0x80800000); + + // Positive and negative max. + test(f32_max, 0x7F7FFFFF); + test(-f32_max, 0xFF7FFFFF); + + // Various positive values. + var cursor = f32_eps * 10; + while (cursor != Infinity) { + test(cursor); + cursor *= 1.1; + } + + // Various negative values. + cursor = -f32_eps * 10; + while (cursor != -Infinity) { + test(cursor); + cursor *= 1.1; + } + }); + + + /** + * Tests conversion from 64-bit floating point numbers to split64 numbers. + */ + it('testFloat64ToSplit64', function() { + var f64_eps = jspb.BinaryConstants.FLOAT64_EPS; + var f64_min = jspb.BinaryConstants.FLOAT64_MIN; + var f64_max = jspb.BinaryConstants.FLOAT64_MAX; + + // NaN. + jspb.utils.splitFloat64(NaN); + if (!isNaN(jspb.utils.joinFloat64(jspb.utils.split64Low, + jspb.utils.split64High))) { + throw 'fail!'; + } + + /** + * @param {number} x + * @param {number=} opt_highBits + * @param {number=} opt_lowBits + */ + function test(x, opt_highBits, opt_lowBits) { + jspb.utils.splitFloat64(x); + if (goog.isDef(opt_highBits)) { + if (opt_highBits != jspb.utils.split64High) throw 'fail!'; + } + if (goog.isDef(opt_lowBits)) { + if (opt_lowBits != jspb.utils.split64Low) throw 'fail!'; + } + if (x != jspb.utils.joinFloat64(jspb.utils.split64Low, + jspb.utils.split64High)) { + throw 'fail!'; + } + } + + // Positive and negative infinity. + test(Infinity, 0x7ff00000, 0x00000000); + test(-Infinity, 0xfff00000, 0x00000000); + + // Positive and negative zero. + test(0, 0x00000000, 0x00000000); + test(-0, 0x80000000, 0x00000000); + + // Positive and negative epsilon. + test(f64_eps, 0x00000000, 0x00000001); + test(-f64_eps, 0x80000000, 0x00000001); + + // Positive and negative min. + test(f64_min, 0x00100000, 0x00000000); + test(-f64_min, 0x80100000, 0x00000000); + + // Positive and negative max. + test(f64_max, 0x7FEFFFFF, 0xFFFFFFFF); + test(-f64_max, 0xFFEFFFFF, 0xFFFFFFFF); + + // Various positive values. + var cursor = f64_eps * 10; + while (cursor != Infinity) { + test(cursor); + cursor *= 1.1; + } + + // Various negative values. + cursor = -f64_eps * 10; + while (cursor != -Infinity) { + test(cursor); + cursor *= 1.1; + } + }); + + + /** + * Tests counting packed varints. + */ + it('testCountVarints', function() { + var values = []; + for (var i = 1; i < 1000000000; i *= 1.1) { + values.push(Math.floor(i)); + } + + var writer = new jspb.BinaryWriter(); + writer.writePackedUint64(1, values); + + var buffer = new Uint8Array(writer.getResultBuffer()); + + // We should have two more varints than we started with - one for the field + // tag, one for the packed length. + assertEquals(values.length + 2, + jspb.utils.countVarints(buffer, 0, buffer.length)); + }); + + + /** + * Tests counting matching varint fields. + */ + it('testCountVarintFields', function() { + var writer = new jspb.BinaryWriter(); + + var count = 0; + for (var i = 1; i < 1000000000; i *= 1.1) { + writer.writeUint64(1, Math.floor(i)); + count++; + } + writer.writeString(2, 'terminator'); + + var buffer = new Uint8Array(writer.getResultBuffer()); + assertEquals(count, + jspb.utils.countVarintFields(buffer, 0, buffer.length, 1)); + + writer = new jspb.BinaryWriter(); + + count = 0; + for (var i = 1; i < 1000000000; i *= 1.1) { + writer.writeUint64(123456789, Math.floor(i)); + count++; + } + writer.writeString(2, 'terminator'); + + buffer = new Uint8Array(writer.getResultBuffer()); + assertEquals(count, + jspb.utils.countVarintFields(buffer, 0, buffer.length, 123456789)); + }); + + + /** + * Tests counting matching fixed32 fields. + */ + it('testCountFixed32Fields', function() { + var writer = new jspb.BinaryWriter(); + + var count = 0; + for (var i = 1; i < 1000000000; i *= 1.1) { + writer.writeFixed32(1, Math.floor(i)); + count++; + } + writer.writeString(2, 'terminator'); + + var buffer = new Uint8Array(writer.getResultBuffer()); + assertEquals(count, + jspb.utils.countFixed32Fields(buffer, 0, buffer.length, 1)); + + writer = new jspb.BinaryWriter(); + + count = 0; + for (var i = 1; i < 1000000000; i *= 1.1) { + writer.writeFixed32(123456789, Math.floor(i)); + count++; + } + writer.writeString(2, 'terminator'); + + buffer = new Uint8Array(writer.getResultBuffer()); + assertEquals(count, + jspb.utils.countFixed32Fields(buffer, 0, buffer.length, 123456789)); + }); + + + /** + * Tests counting matching fixed64 fields. + */ + it('testCountFixed64Fields', function() { + var writer = new jspb.BinaryWriter(); + + var count = 0; + for (var i = 1; i < 1000000000; i *= 1.1) { + writer.writeDouble(1, i); + count++; + } + writer.writeString(2, 'terminator'); + + var buffer = new Uint8Array(writer.getResultBuffer()); + assertEquals(count, + jspb.utils.countFixed64Fields(buffer, 0, buffer.length, 1)); + + writer = new jspb.BinaryWriter(); + + count = 0; + for (var i = 1; i < 1000000000; i *= 1.1) { + writer.writeDouble(123456789, i); + count++; + } + writer.writeString(2, 'terminator'); + + buffer = new Uint8Array(writer.getResultBuffer()); + assertEquals(count, + jspb.utils.countFixed64Fields(buffer, 0, buffer.length, 123456789)); + }); + + + /** + * Tests counting matching delimited fields. + */ + it('testCountDelimitedFields', function() { + var writer = new jspb.BinaryWriter(); + + var count = 0; + for (var i = 1; i < 1000; i *= 1.1) { + writer.writeBytes(1, [Math.floor(i)]); + count++; + } + writer.writeString(2, 'terminator'); + + var buffer = new Uint8Array(writer.getResultBuffer()); + assertEquals(count, + jspb.utils.countDelimitedFields(buffer, 0, buffer.length, 1)); + + writer = new jspb.BinaryWriter(); + + count = 0; + for (var i = 1; i < 1000; i *= 1.1) { + writer.writeBytes(123456789, [Math.floor(i)]); + count++; + } + writer.writeString(2, 'terminator'); + + buffer = new Uint8Array(writer.getResultBuffer()); + assertEquals(count, + jspb.utils.countDelimitedFields(buffer, 0, buffer.length, 123456789)); + }); + + + /** + * Tests byte format for debug strings. + */ + it('testDebugBytesToTextFormat', function() { + assertEquals('""', jspb.utils.debugBytesToTextFormat(null)); + assertEquals('"\\x00\\x10\\xff"', + jspb.utils.debugBytesToTextFormat([0, 16, 255])); + }); + + + /** + * Tests converting byte blob sources into byte blobs. + */ + it('testByteSourceToUint8Array', function() { + var convert = jspb.utils.byteSourceToUint8Array; + + var sourceData = []; + for (var i = 0; i < 256; i++) { + sourceData.push(i); + } + + var sourceBytes = new Uint8Array(sourceData); + var sourceBuffer = sourceBytes.buffer; + var sourceBase64 = goog.crypt.base64.encodeByteArray(sourceData); + var sourceString = String.fromCharCode.apply(null, sourceData); + + function check(result) { + assertEquals(Uint8Array, result.constructor); + assertEquals(sourceData.length, result.length); + for (var i = 0; i < result.length; i++) { + assertEquals(sourceData[i], result[i]); + } + } + + // Converting Uint8Arrays into Uint8Arrays should be a no-op. + assertEquals(sourceBytes, convert(sourceBytes)); + + // Converting Array. into Uint8Arrays should work. + check(convert(sourceData)); + + // Converting ArrayBuffers into Uint8Arrays should work. + check(convert(sourceBuffer)); + + // Converting base64-encoded strings into Uint8Arrays should work. + check(convert(sourceBase64)); + }); +}); diff --git a/js/compatibility_tests/v3.1.0/binary/writer_test.js b/js/compatibility_tests/v3.1.0/binary/writer_test.js new file mode 100644 index 00000000..d5dadb41 --- /dev/null +++ b/js/compatibility_tests/v3.1.0/binary/writer_test.js @@ -0,0 +1,122 @@ +// 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 writer. In + * practice BinaryWriter is used to drive the Decoder and Reader test cases, + * so only writer-specific tests are here. + * + * Test suite is written using Jasmine -- see http://jasmine.github.io/ + * + * @author aappleby@google.com (Austin Appleby) + */ + +goog.require('goog.crypt'); +goog.require('goog.testing.asserts'); +goog.require('jspb.BinaryWriter'); + + +/** + * @param {function()} func This function should throw an error when run. + */ +function assertFails(func) { + var e = assertThrows(func); + //assertNotNull(e.toString().match(/Error/)); +} + + +describe('binaryWriterTest', function() { + /** + * Verifies that misuse of the writer class triggers assertions. + */ + it('testWriteErrors', function() { + // Submessages with invalid field indices should assert. + var writer = new jspb.BinaryWriter(); + var dummyMessage = /** @type {!jspb.BinaryMessage} */({}); + + assertFails(function() { + writer.writeMessage(-1, dummyMessage, goog.nullFunction); + }); + + // Writing invalid field indices should assert. + writer = new jspb.BinaryWriter(); + assertFails(function() {writer.writeUint64(-1, 1);}); + + // Writing out-of-range field values should assert. + writer = new jspb.BinaryWriter(); + + assertFails(function() {writer.writeInt32(1, -Infinity);}); + assertFails(function() {writer.writeInt32(1, Infinity);}); + + assertFails(function() {writer.writeInt64(1, -Infinity);}); + assertFails(function() {writer.writeInt64(1, Infinity);}); + + assertFails(function() {writer.writeUint32(1, -1);}); + assertFails(function() {writer.writeUint32(1, Infinity);}); + + assertFails(function() {writer.writeUint64(1, -1);}); + assertFails(function() {writer.writeUint64(1, Infinity);}); + + assertFails(function() {writer.writeSint32(1, -Infinity);}); + assertFails(function() {writer.writeSint32(1, Infinity);}); + + assertFails(function() {writer.writeSint64(1, -Infinity);}); + assertFails(function() {writer.writeSint64(1, Infinity);}); + + assertFails(function() {writer.writeFixed32(1, -1);}); + assertFails(function() {writer.writeFixed32(1, Infinity);}); + + assertFails(function() {writer.writeFixed64(1, -1);}); + assertFails(function() {writer.writeFixed64(1, Infinity);}); + + assertFails(function() {writer.writeSfixed32(1, -Infinity);}); + assertFails(function() {writer.writeSfixed32(1, Infinity);}); + + assertFails(function() {writer.writeSfixed64(1, -Infinity);}); + assertFails(function() {writer.writeSfixed64(1, Infinity);}); + }); + + + /** + * Basic test of retrieving the result as a Uint8Array buffer + */ + it('testGetResultBuffer', function() { + var expected = '0864120b48656c6c6f20776f726c641a0301020320c801'; + + var writer = new jspb.BinaryWriter(); + writer.writeUint32(1, 100); + writer.writeString(2, 'Hello world'); + writer.writeBytes(3, new Uint8Array([1, 2, 3])); + writer.writeUint32(4, 200); + + var buffer = writer.getResultBuffer(); + assertEquals(expected, goog.crypt.byteArrayToHex(buffer)); + }); +}); diff --git a/js/compatibility_tests/v3.1.0/commonjs/test6/test6.proto b/js/compatibility_tests/v3.1.0/commonjs/test6/test6.proto new file mode 100644 index 00000000..a060925f --- /dev/null +++ b/js/compatibility_tests/v3.1.0/commonjs/test6/test6.proto @@ -0,0 +1,40 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2016 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. + +syntax = "proto3"; + +option java_package = "com.google.apps.jspb.proto"; +option java_multiple_files = true; + +package jspb.test.importing; + +message ImportedMessage { + string string_value = 1; +} diff --git a/js/compatibility_tests/v3.1.0/commonjs/test7/test7.proto b/js/compatibility_tests/v3.1.0/commonjs/test7/test7.proto new file mode 100644 index 00000000..f5574a3d --- /dev/null +++ b/js/compatibility_tests/v3.1.0/commonjs/test7/test7.proto @@ -0,0 +1,42 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2016 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. + +syntax = "proto3"; + +option java_package = "com.google.apps.jspb.proto"; +option java_multiple_files = true; + +package jspb.test.framing; + +import "test6/test6.proto"; + +message FramingMessage { + jspb.test.importing.ImportedMessage imported_message = 1; +} diff --git a/js/compatibility_tests/v3.1.0/data.proto b/js/compatibility_tests/v3.1.0/data.proto new file mode 100644 index 00000000..74a8a994 --- /dev/null +++ b/js/compatibility_tests/v3.1.0/data.proto @@ -0,0 +1,51 @@ +// 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. + +// Author: mwr@google.com (Mark Rawling) + +syntax = "proto2"; + +option java_package = "com.google.apps.jspb.proto"; +option java_multiple_files = true; + +package jspb.test; + +// legacy data, must be nested +message data { + message NestedData { + required string str = 1; + } +} + +// new data, does not require nesting +message UnnestedData { + required string str = 1; +} + diff --git a/js/compatibility_tests/v3.1.0/debug_test.js b/js/compatibility_tests/v3.1.0/debug_test.js new file mode 100644 index 00000000..702cc76e --- /dev/null +++ b/js/compatibility_tests/v3.1.0/debug_test.js @@ -0,0 +1,105 @@ +// 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. + +goog.setTestOnly(); + +goog.require('goog.testing.asserts'); + +// CommonJS-LoadFromFile: google-protobuf +goog.require('jspb.debug'); + +// CommonJS-LoadFromFile: test_pb +goog.require('proto.jspb.test.HasExtensions'); +goog.require('proto.jspb.test.IsExtension'); +goog.require('proto.jspb.test.Simple1'); + + + +describe('debugTest', function() { + it('testSimple1', function() { + if (COMPILED) { + return; + } + var message = new proto.jspb.test.Simple1(); + message.setAString('foo'); + assertObjectEquals({ + $name: 'proto.jspb.test.Simple1', + 'aString': 'foo', + 'aRepeatedStringList': [] + }, jspb.debug.dump(message)); + + message.setABoolean(true); + message.setARepeatedStringList(['1', '2']); + + assertObjectEquals({ + $name: 'proto.jspb.test.Simple1', + 'aString': 'foo', + 'aRepeatedStringList': ['1', '2'], + 'aBoolean': true + }, jspb.debug.dump(message)); + + message.clearAString(); + + assertObjectEquals({ + $name: 'proto.jspb.test.Simple1', + 'aRepeatedStringList': ['1', '2'], + 'aBoolean': true + }, jspb.debug.dump(message)); + }); + + + it('testExtensions', function() { + if (COMPILED) { + return; + } + var extension = new proto.jspb.test.IsExtension(); + extension.setExt1('ext1field'); + var extendable = new proto.jspb.test.HasExtensions(); + extendable.setStr1('v1'); + extendable.setStr2('v2'); + extendable.setStr3('v3'); + extendable.setExtension(proto.jspb.test.IsExtension.extField, extension); + + assertObjectEquals({ + '$name': 'proto.jspb.test.HasExtensions', + 'str1': 'v1', + 'str2': 'v2', + 'str3': 'v3', + '$extensions': { + 'extField': { + '$name': 'proto.jspb.test.IsExtension', + 'ext1': 'ext1field' + }, + 'repeatedSimpleList': [] + } + }, jspb.debug.dump(extendable)); + }); + +}); diff --git a/js/compatibility_tests/v3.1.0/maps_test.js b/js/compatibility_tests/v3.1.0/maps_test.js new file mode 100644 index 00000000..0d442f4f --- /dev/null +++ b/js/compatibility_tests/v3.1.0/maps_test.js @@ -0,0 +1,301 @@ +// 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. + +goog.require('goog.testing.asserts'); +goog.require('goog.userAgent'); + +// CommonJS-LoadFromFile: testbinary_pb proto.jspb.test +goog.require('proto.jspb.test.MapValueEnum'); +goog.require('proto.jspb.test.MapValueMessage'); +goog.require('proto.jspb.test.TestMapFields'); + +// CommonJS-LoadFromFile: test_pb proto.jspb.test +goog.require('proto.jspb.test.MapValueMessageNoBinary'); +goog.require('proto.jspb.test.TestMapFieldsNoBinary'); + +/** + * Helper: check that the given map has exactly this set of (sorted) entries. + * @param {!jspb.Map} map + * @param {!Array>} entries + */ +function checkMapEquals(map, entries) { + var arr = map.toArray(); + assertEquals(arr.length, entries.length); + for (var i = 0; i < arr.length; i++) { + assertElementsEquals(arr[i], entries[i]); + } +} + +/** + * Converts an ES6 iterator to an array. + * @template T + * @param {!Iterator} iter an iterator + * @return {!Array} + */ +function toArray(iter) { + var arr = []; + while (true) { + var val = iter.next(); + if (val.done) { + break; + } + arr.push(val.value); + } + return arr; +} + + +/** + * Helper: generate test methods for this TestMapFields class. + * @param {?} msgInfo + * @param {?} submessageCtor + * @param {!string} suffix + */ +function makeTests(msgInfo, submessageCtor, suffix) { + /** + * Helper: fill all maps on a TestMapFields. + * @param {?} msg + */ + var fillMapFields = function(msg) { + msg.getMapStringStringMap().set('asdf', 'jkl;').set('key 2', 'hello world'); + msg.getMapStringInt32Map().set('a', 1).set('b', -2); + msg.getMapStringInt64Map().set('c', 0x100000000).set('d', 0x200000000); + msg.getMapStringBoolMap().set('e', true).set('f', false); + msg.getMapStringDoubleMap().set('g', 3.14159).set('h', 2.71828); + msg.getMapStringEnumMap() + .set('i', proto.jspb.test.MapValueEnum.MAP_VALUE_BAR) + .set('j', proto.jspb.test.MapValueEnum.MAP_VALUE_BAZ); + msg.getMapStringMsgMap() + .set('k', new submessageCtor()) + .set('l', new submessageCtor()); + msg.getMapStringMsgMap().get('k').setFoo(42); + msg.getMapStringMsgMap().get('l').setFoo(84); + msg.getMapInt32StringMap().set(-1, 'a').set(42, 'b'); + msg.getMapInt64StringMap().set(0x123456789abc, 'c').set(0xcba987654321, 'd'); + msg.getMapBoolStringMap().set(false, 'e').set(true, 'f'); + }; + + /** + * Helper: check all maps on a TestMapFields. + * @param {?} msg + */ + var checkMapFields = function(msg) { + checkMapEquals(msg.getMapStringStringMap(), [ + ['asdf', 'jkl;'], + ['key 2', 'hello world'] + ]); + checkMapEquals(msg.getMapStringInt32Map(), [ + ['a', 1], + ['b', -2] + ]); + checkMapEquals(msg.getMapStringInt64Map(), [ + ['c', 0x100000000], + ['d', 0x200000000] + ]); + checkMapEquals(msg.getMapStringBoolMap(), [ + ['e', true], + ['f', false] + ]); + checkMapEquals(msg.getMapStringDoubleMap(), [ + ['g', 3.14159], + ['h', 2.71828] + ]); + checkMapEquals(msg.getMapStringEnumMap(), [ + ['i', proto.jspb.test.MapValueEnum.MAP_VALUE_BAR], + ['j', proto.jspb.test.MapValueEnum.MAP_VALUE_BAZ] + ]); + checkMapEquals(msg.getMapInt32StringMap(), [ + [-1, 'a'], + [42, 'b'] + ]); + checkMapEquals(msg.getMapInt64StringMap(), [ + [0x123456789abc, 'c'], + [0xcba987654321, 'd'] + ]); + checkMapEquals(msg.getMapBoolStringMap(), [ + [false, 'e'], + [true, 'f'] + ]); + + assertEquals(msg.getMapStringMsgMap().getLength(), 2); + assertEquals(msg.getMapStringMsgMap().get('k').getFoo(), 42); + assertEquals(msg.getMapStringMsgMap().get('l').getFoo(), 84); + + var entries = toArray(msg.getMapStringMsgMap().entries()); + assertEquals(entries.length, 2); + entries.forEach(function(entry) { + var key = entry[0]; + var val = entry[1]; + assert(val === msg.getMapStringMsgMap().get(key)); + }); + + msg.getMapStringMsgMap().forEach(function(val, key) { + assert(val === msg.getMapStringMsgMap().get(key)); + }); + }; + + it('testMapStringStringField' + suffix, function() { + var msg = new msgInfo.constructor(); + assertEquals(msg.getMapStringStringMap().getLength(), 0); + assertEquals(msg.getMapStringInt32Map().getLength(), 0); + assertEquals(msg.getMapStringInt64Map().getLength(), 0); + assertEquals(msg.getMapStringBoolMap().getLength(), 0); + assertEquals(msg.getMapStringDoubleMap().getLength(), 0); + assertEquals(msg.getMapStringEnumMap().getLength(), 0); + assertEquals(msg.getMapStringMsgMap().getLength(), 0); + + // Re-create to clear out any internally-cached wrappers, etc. + msg = new msgInfo.constructor(); + var m = msg.getMapStringStringMap(); + assertEquals(m.has('asdf'), false); + assertEquals(m.get('asdf'), undefined); + m.set('asdf', 'hello world'); + assertEquals(m.has('asdf'), true); + assertEquals(m.get('asdf'), 'hello world'); + m.set('jkl;', 'key 2'); + assertEquals(m.has('jkl;'), true); + assertEquals(m.get('jkl;'), 'key 2'); + assertEquals(m.getLength(), 2); + var it = m.entries(); + assertElementsEquals(it.next().value, ['asdf', 'hello world']); + assertElementsEquals(it.next().value, ['jkl;', 'key 2']); + assertEquals(it.next().done, true); + checkMapEquals(m, [ + ['asdf', 'hello world'], + ['jkl;', 'key 2'] + ]); + m.del('jkl;'); + assertEquals(m.has('jkl;'), false); + assertEquals(m.get('jkl;'), undefined); + assertEquals(m.getLength(), 1); + it = m.keys(); + assertEquals(it.next().value, 'asdf'); + assertEquals(it.next().done, true); + it = m.values(); + assertEquals(it.next().value, 'hello world'); + assertEquals(it.next().done, true); + + var count = 0; + m.forEach(function(value, key, map) { + assertEquals(map, m); + assertEquals(key, 'asdf'); + assertEquals(value, 'hello world'); + count++; + }); + assertEquals(count, 1); + + m.clear(); + assertEquals(m.getLength(), 0); + }); + + + /** + * Tests operations on maps with all key and value types. + */ + it('testAllMapTypes' + suffix, function() { + var msg = new msgInfo.constructor(); + fillMapFields(msg); + checkMapFields(msg); + }); + + + if (msgInfo.deserializeBinary) { + /** + * Tests serialization and deserialization in binary format. + */ + it('testBinaryFormat' + suffix, function() { + if (goog.userAgent.IE && !goog.userAgent.isDocumentModeOrHigher(10)) { + // IE8/9 currently doesn't support binary format because they lack + // TypedArray. + return; + } + + // Check that the format is correct. + var msg = new msgInfo.constructor(); + msg.getMapStringStringMap().set('A', 'a'); + var serialized = msg.serializeBinary(); + var expectedSerialized = [ + 0x0a, 0x6, // field 1 (map_string_string), delimited, length 6 + 0x0a, 0x1, // field 1 in submessage (key), delimited, length 1 + 0x41, // ASCII 'A' + 0x12, 0x1, // field 2 in submessage (value), delimited, length 1 + 0x61 // ASCII 'a' + ]; + assertEquals(serialized.length, expectedSerialized.length); + for (var i = 0; i < serialized.length; i++) { + assertEquals(serialized[i], expectedSerialized[i]); + } + + // Check that all map fields successfully round-trip. + msg = new msgInfo.constructor(); + fillMapFields(msg); + serialized = msg.serializeBinary(); + var decoded = msgInfo.deserializeBinary(serialized); + checkMapFields(decoded); + }); + } + + /** + * Exercises the lazy map<->underlying array sync. + */ + it('testLazyMapSync' + suffix, function() { + // Start with a JSPB array containing a few map entries. + var entries = [ + ['a', 'entry 1'], + ['c', 'entry 2'], + ['b', 'entry 3'] + ]; + var msg = new msgInfo.constructor([entries]); + assertEquals(entries.length, 3); + assertEquals(entries[0][0], 'a'); + assertEquals(entries[1][0], 'c'); + assertEquals(entries[2][0], 'b'); + msg.getMapStringStringMap().del('a'); + assertEquals(entries.length, 3); // not yet sync'd + msg.toArray(); // force a sync + assertEquals(entries.length, 2); + assertEquals(entries[0][0], 'b'); // now in sorted order + assertEquals(entries[1][0], 'c'); + + var a = msg.toArray(); + assertEquals(a[0], entries); // retains original reference + }); +} + +describe('mapsTest', function() { + makeTests({ + constructor: proto.jspb.test.TestMapFields, + deserializeBinary: proto.jspb.test.TestMapFields.deserializeBinary + }, proto.jspb.test.MapValueMessage, "_Binary"); + makeTests({ + constructor: proto.jspb.test.TestMapFieldsNoBinary, + deserializeBinary: null + }, proto.jspb.test.MapValueMessageNoBinary, "_NoBinary"); +}); diff --git a/js/compatibility_tests/v3.1.0/message_test.js b/js/compatibility_tests/v3.1.0/message_test.js new file mode 100644 index 00000000..b46e50d3 --- /dev/null +++ b/js/compatibility_tests/v3.1.0/message_test.js @@ -0,0 +1,1037 @@ +// 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. + +// Test suite is written using Jasmine -- see http://jasmine.github.io/ + +goog.setTestOnly(); + +goog.require('goog.json'); +goog.require('goog.testing.asserts'); +goog.require('goog.userAgent'); + +// CommonJS-LoadFromFile: google-protobuf jspb +goog.require('jspb.Message'); + +// CommonJS-LoadFromFile: test5_pb proto.jspb.exttest.beta +goog.require('proto.jspb.exttest.beta.floatingStrField'); + +// CommonJS-LoadFromFile: test3_pb proto.jspb.exttest +goog.require('proto.jspb.exttest.floatingMsgField'); + +// CommonJS-LoadFromFile: test4_pb proto.jspb.exttest +goog.require('proto.jspb.exttest.floatingMsgFieldTwo'); + +// CommonJS-LoadFromFile: test_pb proto.jspb.test +goog.require('proto.jspb.test.CloneExtension'); +goog.require('proto.jspb.test.Complex'); +goog.require('proto.jspb.test.DefaultValues'); +goog.require('proto.jspb.test.Empty'); +goog.require('proto.jspb.test.EnumContainer'); +goog.require('proto.jspb.test.floatingMsgField'); +goog.require('proto.jspb.test.FloatingPointFields'); +goog.require('proto.jspb.test.floatingStrField'); +goog.require('proto.jspb.test.HasExtensions'); +goog.require('proto.jspb.test.IndirectExtension'); +goog.require('proto.jspb.test.IsExtension'); +goog.require('proto.jspb.test.OptionalFields'); +goog.require('proto.jspb.test.OuterEnum'); +goog.require('proto.jspb.test.OuterMessage.Complex'); +goog.require('proto.jspb.test.Simple1'); +goog.require('proto.jspb.test.Simple2'); +goog.require('proto.jspb.test.SpecialCases'); +goog.require('proto.jspb.test.TestClone'); +goog.require('proto.jspb.test.TestEndsWithBytes'); +goog.require('proto.jspb.test.TestGroup'); +goog.require('proto.jspb.test.TestGroup1'); +goog.require('proto.jspb.test.TestMessageWithOneof'); +goog.require('proto.jspb.test.TestReservedNames'); +goog.require('proto.jspb.test.TestReservedNamesExtension'); + +// CommonJS-LoadFromFile: test2_pb proto.jspb.test +goog.require('proto.jspb.test.ExtensionMessage'); +goog.require('proto.jspb.test.TestExtensionsMessage'); + + + + +describe('Message test suite', function() { + it('testEmptyProto', function() { + var empty1 = new proto.jspb.test.Empty([]); + var empty2 = new proto.jspb.test.Empty([]); + assertObjectEquals({}, empty1.toObject()); + assertObjectEquals('Message should not be corrupted:', empty2, empty1); + }); + + it('testTopLevelEnum', function() { + var response = new proto.jspb.test.EnumContainer([]); + response.setOuterEnum(proto.jspb.test.OuterEnum.FOO); + assertEquals(proto.jspb.test.OuterEnum.FOO, response.getOuterEnum()); + }); + + it('testByteStrings', function() { + var data = new proto.jspb.test.DefaultValues([]); + data.setBytesField('some_bytes'); + assertEquals('some_bytes', data.getBytesField()); + }); + + it('testComplexConversion', function() { + var data1 = ['a',,, [, 11], [[, 22], [, 33]],, ['s1', 's2'],, 1]; + var data2 = ['a',,, [, 11], [[, 22], [, 33]],, ['s1', 's2'],, 1]; + var foo = new proto.jspb.test.Complex(data1); + var bar = new proto.jspb.test.Complex(data2); + var result = foo.toObject(); + assertObjectEquals({ + aString: 'a', + anOutOfOrderBool: 1, + aNestedMessage: { + anInt: 11 + }, + aRepeatedMessageList: [{anInt: 22}, {anInt: 33}], + aRepeatedStringList: ['s1', 's2'] + }, result); + + // Now test with the jspb instances included. + result = foo.toObject(true /* opt_includeInstance */); + assertObjectEquals({ + aString: 'a', + anOutOfOrderBool: 1, + aNestedMessage: { + anInt: 11, + $jspbMessageInstance: foo.getANestedMessage() + }, + aRepeatedMessageList: [ + {anInt: 22, $jspbMessageInstance: foo.getARepeatedMessageList()[0]}, + {anInt: 33, $jspbMessageInstance: foo.getARepeatedMessageList()[1]} + ], + aRepeatedStringList: ['s1', 's2'], + $jspbMessageInstance: foo + }, result); + + }); + + it('testMissingFields', function() { + var foo = new proto.jspb.test.Complex([ + undefined, undefined, undefined, [], + undefined, undefined, undefined, undefined]); + var bar = new proto.jspb.test.Complex([ + undefined, undefined, undefined, [], + undefined, undefined, undefined, undefined]); + var result = foo.toObject(); + assertObjectEquals({ + aString: undefined, + anOutOfOrderBool: undefined, + aNestedMessage: { + anInt: undefined + }, + // Note: JsPb converts undefined repeated fields to empty arrays. + aRepeatedMessageList: [], + aRepeatedStringList: [] + }, result); + + }); + + it('testNestedComplexMessage', function() { + // Instantiate the message and set a unique field, just to ensure that we + // are not getting jspb.test.Complex instead. + var msg = new proto.jspb.test.OuterMessage.Complex(); + msg.setInnerComplexField(5); + }); + + it('testSpecialCases', function() { + // Note: Some property names are reserved in JavaScript. + // These names are converted to the Js property named pb_. + var special = + new proto.jspb.test.SpecialCases(['normal', 'default', 'function', + 'var']); + var result = special.toObject(); + assertObjectEquals({ + normal: 'normal', + pb_default: 'default', + pb_function: 'function', + pb_var: 'var' + }, result); + }); + + it('testDefaultValues', function() { + var defaultString = "default<>\'\"abc"; + var response = new proto.jspb.test.DefaultValues(); + + // Test toObject + var expectedObject = { + stringField: defaultString, + boolField: true, + intField: 11, + enumField: 13, + emptyField: '', + bytesField: 'bW9v' + }; + assertObjectEquals(expectedObject, response.toObject()); + + + // Test getters + response = new proto.jspb.test.DefaultValues(); + assertEquals(defaultString, response.getStringField()); + assertEquals(true, response.getBoolField()); + assertEquals(11, response.getIntField()); + assertEquals(13, response.getEnumField()); + assertEquals('', response.getEmptyField()); + assertEquals('bW9v', response.getBytesField()); + + function makeDefault(values) { + return new proto.jspb.test.DefaultValues(values); + } + + // Test with undefined values, + // Use push to workaround IE treating undefined array elements as holes. + response = makeDefault([undefined, undefined, undefined, undefined]); + assertEquals(defaultString, response.getStringField()); + assertEquals(true, response.getBoolField()); + assertEquals(11, response.getIntField()); + assertEquals(13, response.getEnumField()); + assertFalse(response.hasStringField()); + assertFalse(response.hasBoolField()); + assertFalse(response.hasIntField()); + assertFalse(response.hasEnumField()); + + // Test with null values, as would be returned by a JSON serializer. + response = makeDefault([null, null, null, null]); + assertEquals(defaultString, response.getStringField()); + assertEquals(true, response.getBoolField()); + assertEquals(11, response.getIntField()); + assertEquals(13, response.getEnumField()); + assertFalse(response.hasStringField()); + assertFalse(response.hasBoolField()); + assertFalse(response.hasIntField()); + assertFalse(response.hasEnumField()); + + // Test with false-like values. + response = makeDefault(['', false, 0, 0]); + assertEquals('', response.getStringField()); + assertEquals(false, response.getBoolField()); + assertEquals(true, response.getIntField() == 0); + assertEquals(true, response.getEnumField() == 0); + assertTrue(response.hasStringField()); + assertTrue(response.hasBoolField()); + assertTrue(response.hasIntField()); + assertTrue(response.hasEnumField()); + + // Test that clearing the values reverts them to the default state. + response = makeDefault(['blah', false, 111, 77]); + response.clearStringField(); response.clearBoolField(); + response.clearIntField(); response.clearEnumField(); + assertEquals(defaultString, response.getStringField()); + assertEquals(true, response.getBoolField()); + assertEquals(11, response.getIntField()); + assertEquals(13, response.getEnumField()); + assertFalse(response.hasStringField()); + assertFalse(response.hasBoolField()); + assertFalse(response.hasIntField()); + assertFalse(response.hasEnumField()); + + // Test that setFoo(null) clears the values. + response = makeDefault(['blah', false, 111, 77]); + response.setStringField(null); response.setBoolField(null); + response.setIntField(undefined); response.setEnumField(undefined); + assertEquals(defaultString, response.getStringField()); + assertEquals(true, response.getBoolField()); + assertEquals(11, response.getIntField()); + assertEquals(13, response.getEnumField()); + assertFalse(response.hasStringField()); + assertFalse(response.hasBoolField()); + assertFalse(response.hasIntField()); + assertFalse(response.hasEnumField()); + }); + + it('testClearFields', function() { + var data = ['str', true, [11], [[22], [33]], ['s1', 's2']]; + var foo = new proto.jspb.test.OptionalFields(data); + foo.clearAString(); + foo.clearABool(); + foo.clearANestedMessage(); + foo.clearARepeatedMessageList(); + foo.clearARepeatedStringList(); + assertEquals('', foo.getAString()); + assertEquals(false, foo.getABool()); + assertUndefined(foo.getANestedMessage()); + assertFalse(foo.hasAString()); + assertFalse(foo.hasABool()); + assertObjectEquals([], foo.getARepeatedMessageList()); + assertObjectEquals([], foo.getARepeatedStringList()); + // NOTE: We want the missing fields in 'expected' to be undefined, + // but we actually get a sparse array instead. We could use something + // like [1,undefined,2] to avoid this, except that this is still + // sparse on IE. No comment... + var expected = [,,, [], []]; + expected[0] = expected[1] = expected[2] = undefined; + assertObjectEquals(expected, foo.toArray()); + }); + + it('testDifferenceRawObject', /** @suppress {visibility} */ function() { + var p1 = new proto.jspb.test.HasExtensions(['hi', 'diff', {}]); + var p2 = new proto.jspb.test.HasExtensions(['hi', 'what', + {1000: 'unique'}]); + var diff = /** @type {proto.jspb.test.HasExtensions} */ + (jspb.Message.difference(p1, p2)); + assertEquals('', diff.getStr1()); + assertEquals('what', diff.getStr2()); + assertEquals('', diff.getStr3()); + assertEquals('unique', diff.extensionObject_[1000]); + }); + + it('testEqualsSimple', function() { + var s1 = new proto.jspb.test.Simple1(['hi']); + assertTrue(jspb.Message.equals(s1, new proto.jspb.test.Simple1(['hi']))); + assertFalse(jspb.Message.equals(s1, new proto.jspb.test.Simple1(['bye']))); + var s1b = new proto.jspb.test.Simple1(['hi', ['hello']]); + assertTrue(jspb.Message.equals(s1b, + new proto.jspb.test.Simple1(['hi', ['hello']]))); + assertTrue(jspb.Message.equals(s1b, + new proto.jspb.test.Simple1(['hi', ['hello', undefined, + undefined, undefined]]))); + assertFalse(jspb.Message.equals(s1b, + new proto.jspb.test.Simple1(['no', ['hello']]))); + // Test with messages of different types + var s2 = new proto.jspb.test.Simple2(['hi']); + assertFalse(jspb.Message.equals(s1, s2)); + }); + + it('testEquals_softComparison', function() { + var s1 = new proto.jspb.test.Simple1(['hi', [], null]); + assertTrue(jspb.Message.equals(s1, + new proto.jspb.test.Simple1(['hi', []]))); + + var s1b = new proto.jspb.test.Simple1(['hi', [], true]); + assertTrue(jspb.Message.equals(s1b, + new proto.jspb.test.Simple1(['hi', [], 1]))); + }); + + it('testEqualsComplex', function() { + var data1 = ['a',,, [, 11], [[, 22], [, 33]],, ['s1', 's2'],, 1]; + var data2 = ['a',,, [, 11], [[, 22], [, 34]],, ['s1', 's2'],, 1]; + var data3 = ['a',,, [, 11], [[, 22]],, ['s1', 's2'],, 1]; + var data4 = ['hi']; + var c1a = new proto.jspb.test.Complex(data1); + var c1b = new proto.jspb.test.Complex(data1); + var c2 = new proto.jspb.test.Complex(data2); + var c3 = new proto.jspb.test.Complex(data3); + var s1 = new proto.jspb.test.Simple1(data4); + + assertTrue(jspb.Message.equals(c1a, c1b)); + assertFalse(jspb.Message.equals(c1a, c2)); + assertFalse(jspb.Message.equals(c2, c3)); + assertFalse(jspb.Message.equals(c1a, s1)); + }); + + it('testEqualsExtensionsConstructed', function() { + assertTrue(jspb.Message.equals( + new proto.jspb.test.HasExtensions([]), + new proto.jspb.test.HasExtensions([{}]) + )); + assertTrue(jspb.Message.equals( + new proto.jspb.test.HasExtensions(['hi', {100: [{200: 'a'}]}]), + new proto.jspb.test.HasExtensions(['hi', {100: [{200: 'a'}]}]) + )); + assertFalse(jspb.Message.equals( + new proto.jspb.test.HasExtensions(['hi', {100: [{200: 'a'}]}]), + new proto.jspb.test.HasExtensions(['hi', {100: [{200: 'b'}]}]) + )); + assertTrue(jspb.Message.equals( + new proto.jspb.test.HasExtensions([{100: [{200: 'a'}]}]), + new proto.jspb.test.HasExtensions([{100: [{200: 'a'}]}]) + )); + assertTrue(jspb.Message.equals( + new proto.jspb.test.HasExtensions([{100: [{200: 'a'}]}]), + new proto.jspb.test.HasExtensions([,,, {100: [{200: 'a'}]}]) + )); + assertTrue(jspb.Message.equals( + new proto.jspb.test.HasExtensions([,,, {100: [{200: 'a'}]}]), + new proto.jspb.test.HasExtensions([{100: [{200: 'a'}]}]) + )); + assertTrue(jspb.Message.equals( + new proto.jspb.test.HasExtensions(['hi', {100: [{200: 'a'}]}]), + new proto.jspb.test.HasExtensions(['hi',,, {100: [{200: 'a'}]}]) + )); + assertTrue(jspb.Message.equals( + new proto.jspb.test.HasExtensions(['hi',,, {100: [{200: 'a'}]}]), + new proto.jspb.test.HasExtensions(['hi', {100: [{200: 'a'}]}]) + )); + }); + + it('testEqualsExtensionsUnconstructed', function() { + assertTrue(jspb.Message.compareFields([], [{}])); + assertTrue(jspb.Message.compareFields([,,, {}], [])); + assertTrue(jspb.Message.compareFields([,,, {}], [,, {}])); + assertTrue(jspb.Message.compareFields( + ['hi', {100: [{200: 'a'}]}], ['hi', {100: [{200: 'a'}]}])); + assertFalse(jspb.Message.compareFields( + ['hi', {100: [{200: 'a'}]}], ['hi', {100: [{200: 'b'}]}])); + assertTrue(jspb.Message.compareFields( + [{100: [{200: 'a'}]}], [{100: [{200: 'a'}]}])); + assertTrue(jspb.Message.compareFields( + [{100: [{200: 'a'}]}], [,,, {100: [{200: 'a'}]}])); + assertTrue(jspb.Message.compareFields( + [,,, {100: [{200: 'a'}]}], [{100: [{200: 'a'}]}])); + assertTrue(jspb.Message.compareFields( + ['hi', {100: [{200: 'a'}]}], ['hi',,, {100: [{200: 'a'}]}])); + assertTrue(jspb.Message.compareFields( + ['hi',,, {100: [{200: 'a'}]}], ['hi', {100: [{200: 'a'}]}])); + }); + + it('testToMap', function() { + var p1 = new proto.jspb.test.Simple1(['k', ['v']]); + var p2 = new proto.jspb.test.Simple1(['k1', ['v1', 'v2']]); + var soymap = jspb.Message.toMap([p1, p2], + proto.jspb.test.Simple1.prototype.getAString, + proto.jspb.test.Simple1.prototype.toObject); + assertEquals('k', soymap['k'].aString); + assertArrayEquals(['v'], soymap['k'].aRepeatedStringList); + var protomap = jspb.Message.toMap([p1, p2], + proto.jspb.test.Simple1.prototype.getAString); + assertEquals('k', protomap['k'].getAString()); + assertArrayEquals(['v'], protomap['k'].getARepeatedStringList()); + }); + + it('testClone', function() { + var supportsUint8Array = + !goog.userAgent.IE || goog.userAgent.isVersionOrHigher('10'); + var original = new proto.jspb.test.TestClone(); + original.setStr('v1'); + var simple1 = new proto.jspb.test.Simple1(['x1', ['y1', 'z1']]); + var simple2 = new proto.jspb.test.Simple1(['x2', ['y2', 'z2']]); + var simple3 = new proto.jspb.test.Simple1(['x3', ['y3', 'z3']]); + original.setSimple1(simple1); + original.setSimple2List([simple2, simple3]); + var bytes1 = supportsUint8Array ? new Uint8Array([1, 2, 3]) : '123'; + original.setBytesField(bytes1); + var extension = new proto.jspb.test.CloneExtension(); + extension.setExt('e1'); + original.setExtension(proto.jspb.test.IsExtension.extField, extension); + var clone = original.clone(); + assertArrayEquals(['v1',, ['x1', ['y1', 'z1']],, + [['x2', ['y2', 'z2']], ['x3', ['y3', 'z3']]], bytes1,, { 100: [, 'e1'] }], + clone.toArray()); + clone.setStr('v2'); + var simple4 = new proto.jspb.test.Simple1(['a1', ['b1', 'c1']]); + var simple5 = new proto.jspb.test.Simple1(['a2', ['b2', 'c2']]); + var simple6 = new proto.jspb.test.Simple1(['a3', ['b3', 'c3']]); + clone.setSimple1(simple4); + clone.setSimple2List([simple5, simple6]); + if (supportsUint8Array) { + clone.getBytesField()[0] = 4; + assertObjectEquals(bytes1, original.getBytesField()); + } + var bytes2 = supportsUint8Array ? new Uint8Array([4, 5, 6]) : '456'; + clone.setBytesField(bytes2); + var newExtension = new proto.jspb.test.CloneExtension(); + newExtension.setExt('e2'); + clone.setExtension(proto.jspb.test.CloneExtension.extField, newExtension); + assertArrayEquals(['v2',, ['a1', ['b1', 'c1']],, + [['a2', ['b2', 'c2']], ['a3', ['b3', 'c3']]], bytes2,, { 100: [, 'e2'] }], + clone.toArray()); + assertArrayEquals(['v1',, ['x1', ['y1', 'z1']],, + [['x2', ['y2', 'z2']], ['x3', ['y3', 'z3']]], bytes1,, { 100: [, 'e1'] }], + original.toArray()); + }); + + it('testCopyInto', function() { + var supportsUint8Array = + !goog.userAgent.IE || goog.userAgent.isVersionOrHigher('10'); + var original = new proto.jspb.test.TestClone(); + original.setStr('v1'); + var dest = new proto.jspb.test.TestClone(); + dest.setStr('override'); + var simple1 = new proto.jspb.test.Simple1(['x1', ['y1', 'z1']]); + var simple2 = new proto.jspb.test.Simple1(['x2', ['y2', 'z2']]); + var simple3 = new proto.jspb.test.Simple1(['x3', ['y3', 'z3']]); + var destSimple1 = new proto.jspb.test.Simple1(['ox1', ['oy1', 'oz1']]); + var destSimple2 = new proto.jspb.test.Simple1(['ox2', ['oy2', 'oz2']]); + var destSimple3 = new proto.jspb.test.Simple1(['ox3', ['oy3', 'oz3']]); + original.setSimple1(simple1); + original.setSimple2List([simple2, simple3]); + dest.setSimple1(destSimple1); + dest.setSimple2List([destSimple2, destSimple3]); + var bytes1 = supportsUint8Array ? new Uint8Array([1, 2, 3]) : '123'; + var bytes2 = supportsUint8Array ? new Uint8Array([4, 5, 6]) : '456'; + original.setBytesField(bytes1); + dest.setBytesField(bytes2); + var extension = new proto.jspb.test.CloneExtension(); + extension.setExt('e1'); + original.setExtension(proto.jspb.test.CloneExtension.extField, extension); + + jspb.Message.copyInto(original, dest); + assertArrayEquals(original.toArray(), dest.toArray()); + assertEquals('x1', dest.getSimple1().getAString()); + assertEquals('e1', + dest.getExtension(proto.jspb.test.CloneExtension.extField).getExt()); + dest.getSimple1().setAString('new value'); + assertNotEquals(dest.getSimple1().getAString(), + original.getSimple1().getAString()); + if (supportsUint8Array) { + dest.getBytesField()[0] = 7; + assertObjectEquals(bytes1, original.getBytesField()); + assertObjectEquals(new Uint8Array([7, 2, 3]), dest.getBytesField()); + } else { + dest.setBytesField('789'); + assertObjectEquals(bytes1, original.getBytesField()); + assertObjectEquals('789', dest.getBytesField()); + } + dest.getExtension(proto.jspb.test.CloneExtension.extField). + setExt('new value'); + assertNotEquals( + dest.getExtension(proto.jspb.test.CloneExtension.extField).getExt(), + original.getExtension( + proto.jspb.test.CloneExtension.extField).getExt()); + }); + + it('testCopyInto_notSameType', function() { + var a = new proto.jspb.test.TestClone(); + var b = new proto.jspb.test.Simple1(['str', ['s1', 's2']]); + + var e = assertThrows(function() { + jspb.Message.copyInto(a, b); + }); + assertContains('should have the same type', e.message); + }); + + it('testExtensions', function() { + var extension1 = new proto.jspb.test.IsExtension(['ext1field']); + var extension2 = new proto.jspb.test.Simple1(['str', ['s1', 's2']]); + var extendable = new proto.jspb.test.HasExtensions(['v1', 'v2', 'v3']); + extendable.setExtension(proto.jspb.test.IsExtension.extField, extension1); + extendable.setExtension(proto.jspb.test.IndirectExtension.simple, + extension2); + extendable.setExtension(proto.jspb.test.IndirectExtension.str, 'xyzzy'); + extendable.setExtension(proto.jspb.test.IndirectExtension.repeatedStrList, + ['a', 'b']); + var s1 = new proto.jspb.test.Simple1(['foo', ['s1', 's2']]); + var s2 = new proto.jspb.test.Simple1(['bar', ['t1', 't2']]); + extendable.setExtension( + proto.jspb.test.IndirectExtension.repeatedSimpleList, + [s1, s2]); + assertObjectEquals(extension1, + extendable.getExtension(proto.jspb.test.IsExtension.extField)); + assertObjectEquals(extension2, + extendable.getExtension(proto.jspb.test.IndirectExtension.simple)); + assertObjectEquals('xyzzy', + extendable.getExtension(proto.jspb.test.IndirectExtension.str)); + assertObjectEquals(['a', 'b'], extendable.getExtension( + proto.jspb.test.IndirectExtension.repeatedStrList)); + assertObjectEquals([s1, s2], extendable.getExtension( + proto.jspb.test.IndirectExtension.repeatedSimpleList)); + // Not supported yet, but it should work... + extendable.setExtension(proto.jspb.test.IndirectExtension.simple, null); + assertNull( + extendable.getExtension(proto.jspb.test.IndirectExtension.simple)); + extendable.setExtension(proto.jspb.test.IndirectExtension.str, null); + assertNull(extendable.getExtension(proto.jspb.test.IndirectExtension.str)); + + + // Extension fields with jspb.ignore = true are ignored. + assertUndefined(proto.jspb.test.IndirectExtension['ignored']); + assertUndefined(proto.jspb.test.HasExtensions['ignoredFloating']); + }); + + it('testFloatingExtensions', function() { + // From an autogenerated container. + var extendable = new proto.jspb.test.HasExtensions(['v1', 'v2', 'v3']); + var extension = new proto.jspb.test.Simple1(['foo', ['s1', 's2']]); + extendable.setExtension(proto.jspb.test.simple1, extension); + assertObjectEquals(extension, + extendable.getExtension(proto.jspb.test.simple1)); + + // From _lib mode. + extension = new proto.jspb.test.ExtensionMessage(['s1']); + extendable = new proto.jspb.test.TestExtensionsMessage([16]); + extendable.setExtension(proto.jspb.test.floatingMsgField, extension); + extendable.setExtension(proto.jspb.test.floatingStrField, 's2'); + assertObjectEquals(extension, + extendable.getExtension(proto.jspb.test.floatingMsgField)); + assertObjectEquals('s2', + extendable.getExtension(proto.jspb.test.floatingStrField)); + assertNotUndefined(proto.jspb.exttest.floatingMsgField); + assertNotUndefined(proto.jspb.exttest.floatingMsgFieldTwo); + assertNotUndefined(proto.jspb.exttest.beta.floatingStrField); + }); + + it('testToObject_extendedObject', function() { + var extension1 = new proto.jspb.test.IsExtension(['ext1field']); + var extension2 = new proto.jspb.test.Simple1(['str', ['s1', 's2'], true]); + var extendable = new proto.jspb.test.HasExtensions(['v1', 'v2', 'v3']); + extendable.setExtension(proto.jspb.test.IsExtension.extField, extension1); + extendable.setExtension(proto.jspb.test.IndirectExtension.simple, + extension2); + extendable.setExtension(proto.jspb.test.IndirectExtension.str, 'xyzzy'); + extendable.setExtension(proto.jspb.test.IndirectExtension.repeatedStrList, + ['a', 'b']); + var s1 = new proto.jspb.test.Simple1(['foo', ['s1', 's2'], true]); + var s2 = new proto.jspb.test.Simple1(['bar', ['t1', 't2'], false]); + extendable.setExtension( + proto.jspb.test.IndirectExtension.repeatedSimpleList, + [s1, s2]); + assertObjectEquals({ + str1: 'v1', str2: 'v2', str3: 'v3', + extField: { ext1: 'ext1field' }, + simple: { + aString: 'str', aRepeatedStringList: ['s1', 's2'], aBoolean: true + }, + str: 'xyzzy', + repeatedStrList: ['a', 'b'], + repeatedSimpleList: [ + { aString: 'foo', aRepeatedStringList: ['s1', 's2'], aBoolean: true}, + { aString: 'bar', aRepeatedStringList: ['t1', 't2'], aBoolean: false} + ] + }, extendable.toObject()); + + // Now, with instances included. + assertObjectEquals({ + str1: 'v1', str2: 'v2', str3: 'v3', + extField: { + ext1: 'ext1field', + $jspbMessageInstance: + extendable.getExtension(proto.jspb.test.IsExtension.extField) + }, + simple: { + aString: 'str', + aRepeatedStringList: ['s1', 's2'], + aBoolean: true, + $jspbMessageInstance: + extendable.getExtension(proto.jspb.test.IndirectExtension.simple) + }, + str: 'xyzzy', + repeatedStrList: ['a', 'b'], + repeatedSimpleList: [{ + aString: 'foo', + aRepeatedStringList: ['s1', 's2'], + aBoolean: true, + $jspbMessageInstance: s1 + }, { + aString: 'bar', + aRepeatedStringList: ['t1', 't2'], + aBoolean: false, + $jspbMessageInstance: s2 + }], + $jspbMessageInstance: extendable + }, extendable.toObject(true /* opt_includeInstance */)); + }); + + it('testInitialization_emptyArray', function() { + var msg = new proto.jspb.test.HasExtensions([]); + if (jspb.Message.MINIMIZE_MEMORY_ALLOCATIONS) { + assertArrayEquals([], msg.toArray()); + } else { + // Extension object is created past all regular fields. + assertArrayEquals([,,, {}], msg.toArray()); + } + }); + + it('testInitialization_justExtensionObject', function() { + var msg = new proto.jspb.test.Empty([{1: 'hi'}]); + // The extensionObject is not moved from its original location. + assertArrayEquals([{1: 'hi'}], msg.toArray()); + }); + + it('testInitialization_incompleteList', function() { + var msg = new proto.jspb.test.Empty([1, {4: 'hi'}]); + // The extensionObject is not moved from its original location. + assertArrayEquals([1, {4: 'hi'}], msg.toArray()); + }); + + it('testInitialization_forwardCompatible', function() { + var msg = new proto.jspb.test.Empty([1, 2, 3, {1: 'hi'}]); + assertArrayEquals([1, 2, 3, {1: 'hi'}], msg.toArray()); + }); + + it('testExtendedMessageEnsureObject', + /** @suppress {visibility} */ function() { + var data = + new proto.jspb.test.HasExtensions(['str1', {'a_key': 'an_object'}]); + assertEquals('an_object', data.extensionObject_['a_key']); + }); + + it('testToObject_hasExtensionField', function() { + var data = new proto.jspb.test.HasExtensions(['str1', {100: ['ext1']}]); + var obj = data.toObject(); + assertEquals('str1', obj.str1); + assertEquals('ext1', obj.extField.ext1); + }); + + it('testGetExtension', function() { + var data = new proto.jspb.test.HasExtensions(['str1', {100: ['ext1']}]); + assertEquals('str1', data.getStr1()); + var extension = data.getExtension(proto.jspb.test.IsExtension.extField); + assertNotNull(extension); + assertEquals('ext1', extension.getExt1()); + }); + + it('testSetExtension', function() { + var data = new proto.jspb.test.HasExtensions(); + var extensionMessage = new proto.jspb.test.IsExtension(['is_extension']); + data.setExtension(proto.jspb.test.IsExtension.extField, extensionMessage); + var obj = data.toObject(); + assertNotNull( + data.getExtension(proto.jspb.test.IsExtension.extField)); + assertEquals('is_extension', obj.extField.ext1); + }); + + /** + * Note that group is long deprecated, we only support it because JsPb has + * a goal of being able to generate JS classes for all proto descriptors. + */ + it('testGroups', function() { + var group = new proto.jspb.test.TestGroup(); + var someGroup = new proto.jspb.test.TestGroup.RepeatedGroup(); + someGroup.setId('g1'); + someGroup.setSomeBoolList([true, false]); + group.setRepeatedGroupList([someGroup]); + var groups = group.getRepeatedGroupList(); + assertEquals('g1', groups[0].getId()); + assertObjectEquals([true, false], groups[0].getSomeBoolList()); + assertObjectEquals({id: 'g1', someBoolList: [true, false]}, + groups[0].toObject()); + assertObjectEquals({ + repeatedGroupList: [{id: 'g1', someBoolList: [true, false]}], + requiredGroup: {id: undefined}, + optionalGroup: undefined, + requiredSimple: {aRepeatedStringList: [], aString: undefined}, + optionalSimple: undefined, + id: undefined + }, group.toObject()); + var group1 = new proto.jspb.test.TestGroup1(); + group1.setGroup(someGroup); + assertEquals(someGroup, group1.getGroup()); + }); + + it('testNonExtensionFieldsAfterExtensionRange', function() { + var data = [{'1': 'a_string'}]; + var message = new proto.jspb.test.Complex(data); + assertArrayEquals([], message.getARepeatedStringList()); + }); + + it('testReservedGetterNames', function() { + var message = new proto.jspb.test.TestReservedNames(); + message.setExtension$(11); + message.setExtension(proto.jspb.test.TestReservedNamesExtension.foo, 12); + assertEquals(11, message.getExtension$()); + assertEquals(12, message.getExtension( + proto.jspb.test.TestReservedNamesExtension.foo)); + assertObjectEquals({extension: 11, foo: 12}, message.toObject()); + }); + + it('testInitializeMessageWithUnsetOneof', function() { + var message = new proto.jspb.test.TestMessageWithOneof([]); + assertEquals( + proto.jspb.test.TestMessageWithOneof.PartialOneofCase. + PARTIAL_ONEOF_NOT_SET, + message.getPartialOneofCase()); + assertEquals( + proto.jspb.test.TestMessageWithOneof.RecursiveOneofCase. + RECURSIVE_ONEOF_NOT_SET, + message.getRecursiveOneofCase()); + }); + + it('testInitializeMessageWithSingleValueSetInOneof', function() { + var message = new proto.jspb.test.TestMessageWithOneof([,, 'x']); + + assertEquals('x', message.getPone()); + assertEquals('', message.getPthree()); + assertEquals( + proto.jspb.test.TestMessageWithOneof.PartialOneofCase.PONE, + message.getPartialOneofCase()); + }); + + it('testKeepsLastWireValueSetInUnion_multipleValues', function() { + var message = new proto.jspb.test.TestMessageWithOneof([,, 'x',, 'y']); + + assertEquals('', message.getPone()); + assertEquals('y', message.getPthree()); + assertEquals( + proto.jspb.test.TestMessageWithOneof.PartialOneofCase.PTHREE, + message.getPartialOneofCase()); + }); + + it('testSettingOneofFieldClearsOthers', function() { + var message = new proto.jspb.test.TestMessageWithOneof; + assertEquals('', message.getPone()); + assertEquals('', message.getPthree()); + assertFalse(message.hasPone()); + assertFalse(message.hasPthree()); + + message.setPone('hi'); + assertEquals('hi', message.getPone()); + assertEquals('', message.getPthree()); + assertTrue(message.hasPone()); + assertFalse(message.hasPthree()); + + message.setPthree('bye'); + assertEquals('', message.getPone()); + assertEquals('bye', message.getPthree()); + assertFalse(message.hasPone()); + assertTrue(message.hasPthree()); + }); + + it('testSettingOneofFieldDoesNotClearFieldsFromOtherUnions', function() { + var other = new proto.jspb.test.TestMessageWithOneof; + var message = new proto.jspb.test.TestMessageWithOneof; + assertEquals('', message.getPone()); + assertEquals('', message.getPthree()); + assertUndefined(message.getRone()); + assertFalse(message.hasPone()); + assertFalse(message.hasPthree()); + + message.setPone('hi'); + message.setRone(other); + assertEquals('hi', message.getPone()); + assertEquals('', message.getPthree()); + assertEquals(other, message.getRone()); + assertTrue(message.hasPone()); + assertFalse(message.hasPthree()); + + message.setPthree('bye'); + assertEquals('', message.getPone()); + assertEquals('bye', message.getPthree()); + assertEquals(other, message.getRone()); + assertFalse(message.hasPone()); + assertTrue(message.hasPthree()); + }); + + it('testUnsetsOneofCaseWhenFieldIsCleared', function() { + var message = new proto.jspb.test.TestMessageWithOneof; + assertEquals( + proto.jspb.test.TestMessageWithOneof.PartialOneofCase. + PARTIAL_ONEOF_NOT_SET, + message.getPartialOneofCase()); + + message.setPone('hi'); + assertEquals( + proto.jspb.test.TestMessageWithOneof.PartialOneofCase.PONE, + message.getPartialOneofCase()); + + message.clearPone(); + assertEquals( + proto.jspb.test.TestMessageWithOneof.PartialOneofCase. + PARTIAL_ONEOF_NOT_SET, + message.getPartialOneofCase()); + }); + + it('testMessageWithDefaultOneofValues', function() { + var message = new proto.jspb.test.TestMessageWithOneof; + assertEquals(1234, message.getAone()); + assertEquals(0, message.getAtwo()); + assertEquals( + proto.jspb.test.TestMessageWithOneof.DefaultOneofACase + .DEFAULT_ONEOF_A_NOT_SET, + message.getDefaultOneofACase()); + + message.setAone(567); + assertEquals(567, message.getAone()); + assertEquals(0, message.getAtwo()); + assertEquals( + proto.jspb.test.TestMessageWithOneof.DefaultOneofACase.AONE, + message.getDefaultOneofACase()); + + message.setAtwo(890); + assertEquals(1234, message.getAone()); + assertEquals(890, message.getAtwo()); + assertEquals( + proto.jspb.test.TestMessageWithOneof.DefaultOneofACase.ATWO, + message.getDefaultOneofACase()); + + message.clearAtwo(); + assertEquals(1234, message.getAone()); + assertEquals(0, message.getAtwo()); + assertEquals( + proto.jspb.test.TestMessageWithOneof.DefaultOneofACase + .DEFAULT_ONEOF_A_NOT_SET, + message.getDefaultOneofACase()); + }); + + it('testMessageWithDefaultOneofValues_defaultNotOnFirstField', function() { + var message = new proto.jspb.test.TestMessageWithOneof; + assertEquals(0, message.getBone()); + assertEquals(1234, message.getBtwo()); + assertFalse(message.hasBone()); + assertFalse(message.hasBtwo()); + assertEquals( + proto.jspb.test.TestMessageWithOneof.DefaultOneofBCase + .DEFAULT_ONEOF_B_NOT_SET, + message.getDefaultOneofBCase()); + + message.setBone(2); + assertEquals(2, message.getBone()); + assertEquals(1234, message.getBtwo()); + assertTrue(message.hasBone()); + assertFalse(message.hasBtwo()); + assertEquals( + proto.jspb.test.TestMessageWithOneof.DefaultOneofBCase.BONE, + message.getDefaultOneofBCase()); + + message.setBtwo(3); + assertEquals(0, message.getBone()); + assertFalse(message.hasBone()); + assertTrue(message.hasBtwo()); + assertEquals(3, message.getBtwo()); + assertEquals( + proto.jspb.test.TestMessageWithOneof.DefaultOneofBCase.BTWO, + message.getDefaultOneofBCase()); + + message.clearBtwo(); + assertEquals(0, message.getBone()); + assertFalse(message.hasBone()); + assertFalse(message.hasBtwo()); + assertEquals(1234, message.getBtwo()); + assertEquals( + proto.jspb.test.TestMessageWithOneof.DefaultOneofBCase + .DEFAULT_ONEOF_B_NOT_SET, + message.getDefaultOneofBCase()); + }); + + it('testInitializeMessageWithOneofDefaults', function() { + var message = + new proto.jspb.test.TestMessageWithOneof(new Array(9).concat(567)); + assertEquals(567, message.getAone()); + assertEquals(0, message.getAtwo()); + assertEquals( + proto.jspb.test.TestMessageWithOneof.DefaultOneofACase.AONE, + message.getDefaultOneofACase()); + + message = + new proto.jspb.test.TestMessageWithOneof(new Array(10).concat(890)); + assertEquals(1234, message.getAone()); + assertEquals(890, message.getAtwo()); + assertEquals( + proto.jspb.test.TestMessageWithOneof.DefaultOneofACase.ATWO, + message.getDefaultOneofACase()); + + message = + new proto.jspb.test.TestMessageWithOneof(new Array(9).concat(567, 890)); + assertEquals(1234, message.getAone()); + assertEquals(890, message.getAtwo()); + assertEquals( + proto.jspb.test.TestMessageWithOneof.DefaultOneofACase.ATWO, + message.getDefaultOneofACase()); + }); + + it('testInitializeMessageWithOneofDefaults_defaultNotSetOnFirstField', + function() { + var message; + + message = + new proto.jspb.test.TestMessageWithOneof(new Array(11).concat(567)); + assertEquals(567, message.getBone()); + assertEquals(1234, message.getBtwo()); + assertEquals( + proto.jspb.test.TestMessageWithOneof.DefaultOneofBCase.BONE, + message.getDefaultOneofBCase()); + + message = + new proto.jspb.test.TestMessageWithOneof(new Array(12).concat(890)); + assertEquals(0, message.getBone()); + assertEquals(890, message.getBtwo()); + assertEquals( + proto.jspb.test.TestMessageWithOneof.DefaultOneofBCase.BTWO, + message.getDefaultOneofBCase()); + + message = new proto.jspb.test.TestMessageWithOneof( + new Array(11).concat(567, 890)); + assertEquals(0, message.getBone()); + assertEquals(890, message.getBtwo()); + assertEquals( + proto.jspb.test.TestMessageWithOneof.DefaultOneofBCase.BTWO, + message.getDefaultOneofBCase()); + }); + + it('testOneofContainingAnotherMessage', function() { + var message = new proto.jspb.test.TestMessageWithOneof; + assertEquals( + proto.jspb.test.TestMessageWithOneof.RecursiveOneofCase. + RECURSIVE_ONEOF_NOT_SET, + message.getRecursiveOneofCase()); + + var other = new proto.jspb.test.TestMessageWithOneof; + message.setRone(other); + assertEquals(other, message.getRone()); + assertEquals('', message.getRtwo()); + assertEquals( + proto.jspb.test.TestMessageWithOneof.RecursiveOneofCase.RONE, + message.getRecursiveOneofCase()); + + message.setRtwo('hi'); + assertUndefined(message.getRone()); + assertEquals('hi', message.getRtwo()); + assertEquals( + proto.jspb.test.TestMessageWithOneof.RecursiveOneofCase.RTWO, + message.getRecursiveOneofCase()); + }); + + it('testQueryingOneofCaseEnsuresOnlyOneFieldIsSetInUnderlyingArray', + function() { + var message = new proto.jspb.test.TestMessageWithOneof; + message.setPone('x'); + assertEquals('x', message.getPone()); + assertEquals('', message.getPthree()); + assertEquals( + proto.jspb.test.TestMessageWithOneof.PartialOneofCase.PONE, + message.getPartialOneofCase()); + + var array = message.toArray(); + assertEquals('x', array[2]); + assertUndefined(array[4]); + array[4] = 'y'; + + assertEquals( + proto.jspb.test.TestMessageWithOneof.PartialOneofCase.PTHREE, + message.getPartialOneofCase()); + assertUndefined(array[2]); + assertEquals('y', array[4]); + }); + + it('testFloatingPointFieldsSupportNan', function() { + var assertNan = function(x) { + assertTrue('Expected ' + x + ' (' + goog.typeOf(x) + ') to be NaN.', + goog.isNumber(x) && isNaN(x)); + }; + + var message = new proto.jspb.test.FloatingPointFields([ + 'NaN', 'NaN', ['NaN', 'NaN'], 'NaN', + 'NaN', 'NaN', ['NaN', 'NaN'], 'NaN' + ]); + assertNan(message.getOptionalFloatField()); + assertNan(message.getRequiredFloatField()); + assertNan(message.getRepeatedFloatFieldList()[0]); + assertNan(message.getRepeatedFloatFieldList()[1]); + assertNan(message.getDefaultFloatField()); + assertNan(message.getOptionalDoubleField()); + assertNan(message.getRequiredDoubleField()); + assertNan(message.getRepeatedDoubleFieldList()[0]); + assertNan(message.getRepeatedDoubleFieldList()[1]); + assertNan(message.getDefaultDoubleField()); + }); + +}); diff --git a/js/compatibility_tests/v3.1.0/proto3_test.js b/js/compatibility_tests/v3.1.0/proto3_test.js new file mode 100644 index 00000000..3c929eff --- /dev/null +++ b/js/compatibility_tests/v3.1.0/proto3_test.js @@ -0,0 +1,329 @@ +// 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. + +goog.require('goog.crypt.base64'); +goog.require('goog.testing.asserts'); + +// CommonJS-LoadFromFile: testbinary_pb proto.jspb.test +goog.require('proto.jspb.test.ForeignMessage'); + +// CommonJS-LoadFromFile: proto3_test_pb proto.jspb.test +goog.require('proto.jspb.test.Proto3Enum'); +goog.require('proto.jspb.test.TestProto3'); + + +var BYTES = new Uint8Array([1, 2, 8, 9]); +var BYTES_B64 = goog.crypt.base64.encodeByteArray(BYTES); + + +/** + * Helper: compare a bytes field to an expected value + * @param {Uint8Array|string} arr + * @param {Uint8Array} expected + * @return {boolean} + */ +function bytesCompare(arr, expected) { + if (goog.isString(arr)) { + arr = goog.crypt.base64.decodeStringToUint8Array(arr); + } + if (arr.length != expected.length) { + return false; + } + for (var i = 0; i < arr.length; i++) { + if (arr[i] != expected[i]) { + return false; + } + } + return true; +} + + +describe('proto3Test', function() { + /** + * Test defaults for proto3 message fields. + */ + it('testProto3FieldDefaults', function() { + var msg = new proto.jspb.test.TestProto3(); + + assertEquals(msg.getOptionalInt32(), 0); + assertEquals(msg.getOptionalInt64(), 0); + assertEquals(msg.getOptionalUint32(), 0); + assertEquals(msg.getOptionalUint64(), 0); + assertEquals(msg.getOptionalSint32(), 0); + assertEquals(msg.getOptionalSint64(), 0); + assertEquals(msg.getOptionalFixed32(), 0); + assertEquals(msg.getOptionalFixed64(), 0); + assertEquals(msg.getOptionalSfixed32(), 0); + assertEquals(msg.getOptionalSfixed64(), 0); + assertEquals(msg.getOptionalFloat(), 0); + assertEquals(msg.getOptionalDouble(), 0); + assertEquals(msg.getOptionalString(), ''); + + // TODO(b/26173701): when we change bytes fields default getter to return + // Uint8Array, we'll want to switch this assertion to match the u8 case. + assertEquals(typeof msg.getOptionalBytes(), 'string'); + assertEquals(msg.getOptionalBytes_asU8() instanceof Uint8Array, true); + assertEquals(typeof msg.getOptionalBytes_asB64(), 'string'); + assertEquals(msg.getOptionalBytes().length, 0); + assertEquals(msg.getOptionalBytes_asU8().length, 0); + assertEquals(msg.getOptionalBytes_asB64(), ''); + + assertEquals(msg.getOptionalForeignEnum(), + proto.jspb.test.Proto3Enum.PROTO3_FOO); + assertEquals(msg.getOptionalForeignMessage(), undefined); + assertEquals(msg.getOptionalForeignMessage(), undefined); + + assertEquals(msg.getRepeatedInt32List().length, 0); + assertEquals(msg.getRepeatedInt64List().length, 0); + assertEquals(msg.getRepeatedUint32List().length, 0); + assertEquals(msg.getRepeatedUint64List().length, 0); + assertEquals(msg.getRepeatedSint32List().length, 0); + assertEquals(msg.getRepeatedSint64List().length, 0); + assertEquals(msg.getRepeatedFixed32List().length, 0); + assertEquals(msg.getRepeatedFixed64List().length, 0); + assertEquals(msg.getRepeatedSfixed32List().length, 0); + assertEquals(msg.getRepeatedSfixed64List().length, 0); + assertEquals(msg.getRepeatedFloatList().length, 0); + assertEquals(msg.getRepeatedDoubleList().length, 0); + assertEquals(msg.getRepeatedStringList().length, 0); + assertEquals(msg.getRepeatedBytesList().length, 0); + assertEquals(msg.getRepeatedForeignEnumList().length, 0); + assertEquals(msg.getRepeatedForeignMessageList().length, 0); + + }); + + + /** + * Test that all fields can be set and read via a serialization roundtrip. + */ + it('testProto3FieldSetGet', function() { + var msg = new proto.jspb.test.TestProto3(); + + msg.setOptionalInt32(-42); + msg.setOptionalInt64(-0x7fffffff00000000); + msg.setOptionalUint32(0x80000000); + msg.setOptionalUint64(0xf000000000000000); + msg.setOptionalSint32(-100); + msg.setOptionalSint64(-0x8000000000000000); + msg.setOptionalFixed32(1234); + msg.setOptionalFixed64(0x1234567800000000); + msg.setOptionalSfixed32(-1234); + msg.setOptionalSfixed64(-0x1234567800000000); + msg.setOptionalFloat(1.5); + msg.setOptionalDouble(-1.5); + msg.setOptionalBool(true); + msg.setOptionalString('hello world'); + msg.setOptionalBytes(BYTES); + var submsg = new proto.jspb.test.ForeignMessage(); + submsg.setC(16); + msg.setOptionalForeignMessage(submsg); + msg.setOptionalForeignEnum(proto.jspb.test.Proto3Enum.PROTO3_BAR); + + msg.setRepeatedInt32List([-42]); + msg.setRepeatedInt64List([-0x7fffffff00000000]); + msg.setRepeatedUint32List([0x80000000]); + msg.setRepeatedUint64List([0xf000000000000000]); + msg.setRepeatedSint32List([-100]); + msg.setRepeatedSint64List([-0x8000000000000000]); + msg.setRepeatedFixed32List([1234]); + msg.setRepeatedFixed64List([0x1234567800000000]); + msg.setRepeatedSfixed32List([-1234]); + msg.setRepeatedSfixed64List([-0x1234567800000000]); + msg.setRepeatedFloatList([1.5]); + msg.setRepeatedDoubleList([-1.5]); + msg.setRepeatedBoolList([true]); + msg.setRepeatedStringList(['hello world']); + msg.setRepeatedBytesList([BYTES]); + submsg = new proto.jspb.test.ForeignMessage(); + submsg.setC(1000); + msg.setRepeatedForeignMessageList([submsg]); + msg.setRepeatedForeignEnumList([proto.jspb.test.Proto3Enum.PROTO3_BAR]); + + msg.setOneofString('asdf'); + + var serialized = msg.serializeBinary(); + msg = proto.jspb.test.TestProto3.deserializeBinary(serialized); + + assertEquals(msg.getOptionalInt32(), -42); + assertEquals(msg.getOptionalInt64(), -0x7fffffff00000000); + assertEquals(msg.getOptionalUint32(), 0x80000000); + assertEquals(msg.getOptionalUint64(), 0xf000000000000000); + assertEquals(msg.getOptionalSint32(), -100); + assertEquals(msg.getOptionalSint64(), -0x8000000000000000); + assertEquals(msg.getOptionalFixed32(), 1234); + assertEquals(msg.getOptionalFixed64(), 0x1234567800000000); + assertEquals(msg.getOptionalSfixed32(), -1234); + assertEquals(msg.getOptionalSfixed64(), -0x1234567800000000); + assertEquals(msg.getOptionalFloat(), 1.5); + assertEquals(msg.getOptionalDouble(), -1.5); + assertEquals(msg.getOptionalBool(), true); + assertEquals(msg.getOptionalString(), 'hello world'); + assertEquals(true, bytesCompare(msg.getOptionalBytes(), BYTES)); + assertEquals(msg.getOptionalForeignMessage().getC(), 16); + assertEquals(msg.getOptionalForeignEnum(), + proto.jspb.test.Proto3Enum.PROTO3_BAR); + + assertElementsEquals(msg.getRepeatedInt32List(), [-42]); + assertElementsEquals(msg.getRepeatedInt64List(), [-0x7fffffff00000000]); + assertElementsEquals(msg.getRepeatedUint32List(), [0x80000000]); + assertElementsEquals(msg.getRepeatedUint64List(), [0xf000000000000000]); + assertElementsEquals(msg.getRepeatedSint32List(), [-100]); + assertElementsEquals(msg.getRepeatedSint64List(), [-0x8000000000000000]); + assertElementsEquals(msg.getRepeatedFixed32List(), [1234]); + assertElementsEquals(msg.getRepeatedFixed64List(), [0x1234567800000000]); + assertElementsEquals(msg.getRepeatedSfixed32List(), [-1234]); + assertElementsEquals(msg.getRepeatedSfixed64List(), [-0x1234567800000000]); + assertElementsEquals(msg.getRepeatedFloatList(), [1.5]); + assertElementsEquals(msg.getRepeatedDoubleList(), [-1.5]); + assertElementsEquals(msg.getRepeatedBoolList(), [true]); + assertElementsEquals(msg.getRepeatedStringList(), ['hello world']); + assertEquals(msg.getRepeatedBytesList().length, 1); + assertEquals(true, bytesCompare(msg.getRepeatedBytesList()[0], BYTES)); + assertEquals(msg.getRepeatedForeignMessageList().length, 1); + assertEquals(msg.getRepeatedForeignMessageList()[0].getC(), 1000); + assertElementsEquals(msg.getRepeatedForeignEnumList(), + [proto.jspb.test.Proto3Enum.PROTO3_BAR]); + + assertEquals(msg.getOneofString(), 'asdf'); + }); + + + /** + * Test that oneofs continue to have a notion of field presence. + */ + it('testOneofs', function() { + var msg = new proto.jspb.test.TestProto3(); + + assertEquals(msg.getOneofUint32(), 0); + assertEquals(msg.getOneofForeignMessage(), undefined); + assertEquals(msg.getOneofString(), ''); + assertEquals(msg.getOneofBytes(), ''); + assertFalse(msg.hasOneofUint32()); + assertFalse(msg.hasOneofString()); + assertFalse(msg.hasOneofBytes()); + + msg.setOneofUint32(42); + assertEquals(msg.getOneofUint32(), 42); + assertEquals(msg.getOneofForeignMessage(), undefined); + assertEquals(msg.getOneofString(), ''); + assertEquals(msg.getOneofBytes(), ''); + assertTrue(msg.hasOneofUint32()); + assertFalse(msg.hasOneofString()); + assertFalse(msg.hasOneofBytes()); + + + var submsg = new proto.jspb.test.ForeignMessage(); + msg.setOneofForeignMessage(submsg); + assertEquals(msg.getOneofUint32(), 0); + assertEquals(msg.getOneofForeignMessage(), submsg); + assertEquals(msg.getOneofString(), ''); + assertEquals(msg.getOneofBytes(), ''); + assertFalse(msg.hasOneofUint32()); + assertFalse(msg.hasOneofString()); + assertFalse(msg.hasOneofBytes()); + + msg.setOneofString('hello'); + assertEquals(msg.getOneofUint32(), 0); + assertEquals(msg.getOneofForeignMessage(), undefined); + assertEquals(msg.getOneofString(), 'hello'); + assertEquals(msg.getOneofBytes(), ''); + assertFalse(msg.hasOneofUint32()); + assertTrue(msg.hasOneofString()); + assertFalse(msg.hasOneofBytes()); + + msg.setOneofBytes(goog.crypt.base64.encodeString('\u00FF\u00FF')); + assertEquals(msg.getOneofUint32(), 0); + assertEquals(msg.getOneofForeignMessage(), undefined); + assertEquals(msg.getOneofString(), ''); + assertEquals(msg.getOneofBytes_asB64(), + goog.crypt.base64.encodeString('\u00FF\u00FF')); + assertFalse(msg.hasOneofUint32()); + assertFalse(msg.hasOneofString()); + assertTrue(msg.hasOneofBytes()); + }); + + + /** + * Test that "default"-valued primitive fields are not emitted on the wire. + */ + it('testNoSerializeDefaults', function() { + var msg = new proto.jspb.test.TestProto3(); + + // Set each primitive to a non-default value, then back to its default, to + // ensure that the serialization is actually checking the value and not just + // whether it has ever been set. + msg.setOptionalInt32(42); + msg.setOptionalInt32(0); + msg.setOptionalDouble(3.14); + msg.setOptionalDouble(0.0); + msg.setOptionalBool(true); + msg.setOptionalBool(false); + msg.setOptionalString('hello world'); + msg.setOptionalString(''); + msg.setOptionalBytes(goog.crypt.base64.encodeString('\u00FF\u00FF')); + msg.setOptionalBytes(''); + msg.setOptionalForeignMessage(new proto.jspb.test.ForeignMessage()); + msg.setOptionalForeignMessage(null); + msg.setOptionalForeignEnum(proto.jspb.test.Proto3Enum.PROTO3_BAR); + msg.setOptionalForeignEnum(proto.jspb.test.Proto3Enum.PROTO3_FOO); + msg.setOneofUint32(32); + msg.clearOneofUint32(); + + + var serialized = msg.serializeBinary(); + assertEquals(0, serialized.length); + }); + + /** + * Test that base64 string and Uint8Array are interchangeable in bytes fields. + */ + it('testBytesFieldsInterop', function() { + var msg = new proto.jspb.test.TestProto3(); + // Set as a base64 string and check all the getters work. + msg.setOptionalBytes(BYTES_B64); + assertTrue(bytesCompare(msg.getOptionalBytes_asU8(), BYTES)); + assertTrue(bytesCompare(msg.getOptionalBytes_asB64(), BYTES)); + assertTrue(bytesCompare(msg.getOptionalBytes(), BYTES)); + + // Test binary serialize round trip doesn't break it. + msg = proto.jspb.test.TestProto3.deserializeBinary(msg.serializeBinary()); + assertTrue(bytesCompare(msg.getOptionalBytes_asU8(), BYTES)); + assertTrue(bytesCompare(msg.getOptionalBytes_asB64(), BYTES)); + assertTrue(bytesCompare(msg.getOptionalBytes(), BYTES)); + + msg = new proto.jspb.test.TestProto3(); + // Set as a Uint8Array and check all the getters work. + msg.setOptionalBytes(BYTES); + assertTrue(bytesCompare(msg.getOptionalBytes_asU8(), BYTES)); + assertTrue(bytesCompare(msg.getOptionalBytes_asB64(), BYTES)); + assertTrue(bytesCompare(msg.getOptionalBytes(), BYTES)); + + }); +}); diff --git a/js/compatibility_tests/v3.1.0/proto3_test.proto b/js/compatibility_tests/v3.1.0/proto3_test.proto new file mode 100644 index 00000000..acb67164 --- /dev/null +++ b/js/compatibility_tests/v3.1.0/proto3_test.proto @@ -0,0 +1,89 @@ +// 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. + +syntax = "proto3"; + +import "testbinary.proto"; + +package jspb.test; + +message TestProto3 { + int32 optional_int32 = 1; + int64 optional_int64 = 2; + uint32 optional_uint32 = 3; + uint64 optional_uint64 = 4; + sint32 optional_sint32 = 5; + sint64 optional_sint64 = 6; + fixed32 optional_fixed32 = 7; + fixed64 optional_fixed64 = 8; + sfixed32 optional_sfixed32 = 9; + sfixed64 optional_sfixed64 = 10; + float optional_float = 11; + double optional_double = 12; + bool optional_bool = 13; + string optional_string = 14; + bytes optional_bytes = 15; + + ForeignMessage optional_foreign_message = 19; + Proto3Enum optional_foreign_enum = 22; + + repeated int32 repeated_int32 = 31; + repeated int64 repeated_int64 = 32; + repeated uint32 repeated_uint32 = 33; + repeated uint64 repeated_uint64 = 34; + repeated sint32 repeated_sint32 = 35; + repeated sint64 repeated_sint64 = 36; + repeated fixed32 repeated_fixed32 = 37; + repeated fixed64 repeated_fixed64 = 38; + repeated sfixed32 repeated_sfixed32 = 39; + repeated sfixed64 repeated_sfixed64 = 40; + repeated float repeated_float = 41; + repeated double repeated_double = 42; + repeated bool repeated_bool = 43; + repeated string repeated_string = 44; + repeated bytes repeated_bytes = 45; + + repeated ForeignMessage repeated_foreign_message = 49; + repeated Proto3Enum repeated_foreign_enum = 52; + + + oneof oneof_field { + uint32 oneof_uint32 = 111; + ForeignMessage oneof_foreign_message = 112; + string oneof_string = 113; + bytes oneof_bytes = 114; + } +} + +enum Proto3Enum { + PROTO3_FOO = 0; + PROTO3_BAR = 1; + PROTO3_BAZ = 2; +} diff --git a/js/compatibility_tests/v3.1.0/test.proto b/js/compatibility_tests/v3.1.0/test.proto new file mode 100644 index 00000000..48cb37e1 --- /dev/null +++ b/js/compatibility_tests/v3.1.0/test.proto @@ -0,0 +1,262 @@ +// 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. + +// Author: mwr@google.com (Mark Rawling) + +syntax = "proto2"; + +option java_package = "com.google.apps.jspb.proto"; +option java_multiple_files = true; + +import "google/protobuf/descriptor.proto"; + +package jspb.test; + +message Empty { +} + +enum OuterEnum { + FOO = 1; + BAR = 2; +} + +message EnumContainer { + optional OuterEnum outer_enum = 1; +} + +message Simple1 { + required string a_string = 1; + repeated string a_repeated_string = 2; + optional bool a_boolean = 3; +} + +// A message that differs from Simple1 only by name +message Simple2 { + required string a_string = 1; + repeated string a_repeated_string = 2; +} + +message SpecialCases { + required string normal = 1; + // Examples of Js reserved names that are converted to pb_. + required string default = 2; + required string function = 3; + required string var = 4; +} + +message OptionalFields { + message Nested { + optional int32 an_int = 1; + } + optional string a_string = 1; + required bool a_bool = 2; + optional Nested a_nested_message = 3; + repeated Nested a_repeated_message = 4; + repeated string a_repeated_string = 5; +} + +message HasExtensions { + optional string str1 = 1; + optional string str2 = 2; + optional string str3 = 3; + extensions 10 to max; +} + +message Complex { + message Nested { + required int32 an_int = 2; + } + required string a_string = 1; + required bool an_out_of_order_bool = 9; + optional Nested a_nested_message = 4; + repeated Nested a_repeated_message = 5; + repeated string a_repeated_string = 7; +} + +message OuterMessage { + // Make sure this doesn't conflict with the other Complex message. + message Complex { + optional int32 inner_complex_field = 1; + } +} + +message IsExtension { + extend HasExtensions { + optional IsExtension ext_field = 100; + } + optional string ext1 = 1; + + // Extensions of proto2 Descriptor messages will be ignored. + extend google.protobuf.EnumOptions { + optional string simple_option = 42113038; + } +} + +message IndirectExtension { + extend HasExtensions { + optional Simple1 simple = 101; + optional string str = 102; + repeated string repeated_str = 103; + repeated Simple1 repeated_simple = 104; + } +} + +extend HasExtensions { + optional Simple1 simple1 = 105; +} + +message DefaultValues { + enum Enum { + E1 = 13; + E2 = 77; + } + optional string string_field = 1 [default="default<>\'\"abc"]; + optional bool bool_field = 2 [default=true]; + optional int64 int_field = 3 [default=11]; + optional Enum enum_field = 4 [default=E1]; + optional string empty_field = 6 [default=""]; + optional bytes bytes_field = 8 [default="moo"]; // Base64 encoding is "bW9v" +} + +message FloatingPointFields { + optional float optional_float_field = 1; + required float required_float_field = 2; + repeated float repeated_float_field = 3; + optional float default_float_field = 4 [default = 2.0]; + optional double optional_double_field = 5; + required double required_double_field = 6; + repeated double repeated_double_field = 7; + optional double default_double_field = 8 [default = 2.0]; +} + +message TestClone { + optional string str = 1; + optional Simple1 simple1 = 3; + repeated Simple1 simple2 = 5; + optional bytes bytes_field = 6; + optional string unused = 7; + extensions 10 to max; +} + +message CloneExtension { + extend TestClone { + optional CloneExtension ext_field = 100; + } + optional string ext = 2; +} + +message TestGroup { + repeated group RepeatedGroup = 1 { + required string id = 1; + repeated bool some_bool = 2; + } + required group RequiredGroup = 2 { + required string id = 1; + } + optional group OptionalGroup = 3 { + required string id = 1; + } + optional string id = 4; + required Simple2 required_simple = 5; + optional Simple2 optional_simple = 6; +} + +message TestGroup1 { + optional TestGroup.RepeatedGroup group = 1; +} + +message TestReservedNames { + optional int32 extension = 1; + extensions 10 to max; +} + +message TestReservedNamesExtension { + extend TestReservedNames { + optional int32 foo = 10; + } +} + +message TestMessageWithOneof { + + oneof partial_oneof { + string pone = 3; + string pthree = 5; + } + + oneof recursive_oneof { + TestMessageWithOneof rone = 6; + string rtwo = 7; + } + + optional bool normal_field = 8; + repeated string repeated_field = 9; + + oneof default_oneof_a { + int32 aone = 10 [default = 1234]; + int32 atwo = 11; + } + + oneof default_oneof_b { + int32 bone = 12; + int32 btwo = 13 [default = 1234]; + } +} + +message TestEndsWithBytes { + optional int32 value = 1; + optional bytes data = 2; +} + +message TestMapFieldsNoBinary { + map map_string_string = 1; + map map_string_int32 = 2; + map map_string_int64 = 3; + map map_string_bool = 4; + map map_string_double = 5; + map map_string_enum = 6; + map map_string_msg = 7; + + map map_int32_string = 8; + map map_int64_string = 9; + map map_bool_string = 10; + + optional TestMapFieldsNoBinary test_map_fields = 11; + map map_string_testmapfields = 12; +} + +enum MapValueEnumNoBinary { + MAP_VALUE_FOO_NOBINARY = 0; + MAP_VALUE_BAR_NOBINARY = 1; + MAP_VALUE_BAZ_NOBINARY = 2; +} + +message MapValueMessageNoBinary { + optional int32 foo = 1; +} diff --git a/js/compatibility_tests/v3.1.0/test2.proto b/js/compatibility_tests/v3.1.0/test2.proto new file mode 100644 index 00000000..44e55eff --- /dev/null +++ b/js/compatibility_tests/v3.1.0/test2.proto @@ -0,0 +1,54 @@ +// 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. + +syntax = "proto2"; + +option java_package = "com.google.apps.jspb.proto"; +option java_multiple_files = true; + +package jspb.test; + +message TestExtensionsMessage { + optional int32 intfield = 1; + extensions 100 to max; +} + +message ExtensionMessage { + extend TestExtensionsMessage { + optional ExtensionMessage ext_field = 100; + } + optional string ext1 = 1; +} + +// Floating extensions are only supported when generating a _lib.js library. +extend TestExtensionsMessage { + optional ExtensionMessage floating_msg_field = 101; + optional string floating_str_field = 102; +} diff --git a/js/compatibility_tests/v3.1.0/test3.proto b/js/compatibility_tests/v3.1.0/test3.proto new file mode 100644 index 00000000..940a552e --- /dev/null +++ b/js/compatibility_tests/v3.1.0/test3.proto @@ -0,0 +1,53 @@ +// 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. + +syntax = "proto2"; + +option java_package = "com.google.apps.jspb.proto"; +option java_multiple_files = true; + +package jspb.exttest; + +message TestExtensionsMessage { + optional int32 intfield = 1; + extensions 100 to max; +} + +message ExtensionMessage { + extend TestExtensionsMessage { + optional ExtensionMessage ext_field = 100; + } + optional string ext1 = 1; +} + +extend TestExtensionsMessage { + optional ExtensionMessage floating_msg_field = 101; + optional string floating_str_field = 102; +} diff --git a/js/compatibility_tests/v3.1.0/test4.proto b/js/compatibility_tests/v3.1.0/test4.proto new file mode 100644 index 00000000..cf2451e9 --- /dev/null +++ b/js/compatibility_tests/v3.1.0/test4.proto @@ -0,0 +1,42 @@ +// 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. + +syntax = "proto2"; + +option java_package = "com.google.apps.jspb.proto"; +option java_multiple_files = true; + +package jspb.exttest; + +import "test3.proto"; + +extend TestExtensionsMessage { + optional ExtensionMessage floating_msg_field_two = 103; +} diff --git a/js/compatibility_tests/v3.1.0/test5.proto b/js/compatibility_tests/v3.1.0/test5.proto new file mode 100644 index 00000000..34979517 --- /dev/null +++ b/js/compatibility_tests/v3.1.0/test5.proto @@ -0,0 +1,44 @@ +// 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. + +syntax = "proto2"; + +option java_package = "com.google.apps.jspb.proto"; +option java_multiple_files = true; + +package jspb.exttest.beta; + +message TestBetaExtensionsMessage { + extensions 100 to max; +} + +extend TestBetaExtensionsMessage { + optional string floating_str_field = 101; +} diff --git a/js/compatibility_tests/v3.1.0/testbinary.proto b/js/compatibility_tests/v3.1.0/testbinary.proto new file mode 100644 index 00000000..116f17fb --- /dev/null +++ b/js/compatibility_tests/v3.1.0/testbinary.proto @@ -0,0 +1,212 @@ +// 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. + +// LINT: ALLOW_GROUPS + +syntax = "proto2"; + + +package jspb.test; + +// These types are borrowed from `unittest.proto` in the protobuf tree. We want +// to ensure that the binary-format support will handle all field types +// properly. +message TestAllTypes { + optional int32 optional_int32 = 1; + optional int64 optional_int64 = 2; + optional uint32 optional_uint32 = 3; + optional uint64 optional_uint64 = 4; + optional sint32 optional_sint32 = 5; + optional sint64 optional_sint64 = 6; + optional fixed32 optional_fixed32 = 7; + optional fixed64 optional_fixed64 = 8; + optional sfixed32 optional_sfixed32 = 9; + optional sfixed64 optional_sfixed64 = 10; + optional float optional_float = 11; + optional double optional_double = 12; + optional bool optional_bool = 13; + optional string optional_string = 14; + optional bytes optional_bytes = 15; + optional group OptionalGroup = 16 { + optional int32 a = 17; + } + + optional ForeignMessage optional_foreign_message = 19; + optional ForeignEnum optional_foreign_enum = 22; + + // Repeated + repeated int32 repeated_int32 = 31; + repeated int64 repeated_int64 = 32; + repeated uint32 repeated_uint32 = 33; + repeated uint64 repeated_uint64 = 34; + repeated sint32 repeated_sint32 = 35; + repeated sint64 repeated_sint64 = 36; + repeated fixed32 repeated_fixed32 = 37; + repeated fixed64 repeated_fixed64 = 38; + repeated sfixed32 repeated_sfixed32 = 39; + repeated sfixed64 repeated_sfixed64 = 40; + repeated float repeated_float = 41; + repeated double repeated_double = 42; + repeated bool repeated_bool = 43; + repeated string repeated_string = 44; + repeated bytes repeated_bytes = 45; + + repeated group RepeatedGroup = 46 { + optional int32 a = 47; + } + + repeated ForeignMessage repeated_foreign_message = 49; + repeated ForeignEnum repeated_foreign_enum = 52; + + // Packed repeated + repeated int32 packed_repeated_int32 = 61 [packed=true]; + repeated int64 packed_repeated_int64 = 62 [packed=true]; + repeated uint32 packed_repeated_uint32 = 63 [packed=true]; + repeated uint64 packed_repeated_uint64 = 64 [packed=true]; + repeated sint32 packed_repeated_sint32 = 65 [packed=true]; + repeated sint64 packed_repeated_sint64 = 66 [packed=true]; + repeated fixed32 packed_repeated_fixed32 = 67 [packed=true]; + repeated fixed64 packed_repeated_fixed64 = 68 [packed=true]; + repeated sfixed32 packed_repeated_sfixed32 = 69 [packed=true]; + repeated sfixed64 packed_repeated_sfixed64 = 70 [packed=true]; + repeated float packed_repeated_float = 71 [packed=true]; + repeated double packed_repeated_double = 72 [packed=true]; + repeated bool packed_repeated_bool = 73 [packed=true]; + + oneof oneof_field { + uint32 oneof_uint32 = 111; + ForeignMessage oneof_foreign_message = 112; + string oneof_string = 113; + bytes oneof_bytes = 114; + } + +} + +message ForeignMessage { + optional int32 c = 1; +} + +enum ForeignEnum { + FOREIGN_FOO = 4; + FOREIGN_BAR = 5; + FOREIGN_BAZ = 6; +} + +message TestExtendable { + extensions 1 to max; +} + +message ExtendsWithMessage { + extend TestExtendable { + optional ExtendsWithMessage optional_extension = 19; + repeated ExtendsWithMessage repeated_extension = 49; + } + optional int32 foo = 1; +} + +extend TestExtendable { + optional int32 extend_optional_int32 = 1; + optional int64 extend_optional_int64 = 2; + optional uint32 extend_optional_uint32 = 3; + optional uint64 extend_optional_uint64 = 4; + optional sint32 extend_optional_sint32 = 5; + optional sint64 extend_optional_sint64 = 6; + optional fixed32 extend_optional_fixed32 = 7; + optional fixed64 extend_optional_fixed64 = 8; + optional sfixed32 extend_optional_sfixed32 = 9; + optional sfixed64 extend_optional_sfixed64 = 10; + optional float extend_optional_float = 11; + optional double extend_optional_double = 12; + optional bool extend_optional_bool = 13; + optional string extend_optional_string = 14; + optional bytes extend_optional_bytes = 15; + optional ForeignEnum extend_optional_foreign_enum = 22; + + repeated int32 extend_repeated_int32 = 31; + repeated int64 extend_repeated_int64 = 32; + repeated uint32 extend_repeated_uint32 = 33; + repeated uint64 extend_repeated_uint64 = 34; + repeated sint32 extend_repeated_sint32 = 35; + repeated sint64 extend_repeated_sint64 = 36; + repeated fixed32 extend_repeated_fixed32 = 37; + repeated fixed64 extend_repeated_fixed64 = 38; + repeated sfixed32 extend_repeated_sfixed32 = 39; + repeated sfixed64 extend_repeated_sfixed64 = 40; + repeated float extend_repeated_float = 41; + repeated double extend_repeated_double = 42; + repeated bool extend_repeated_bool = 43; + repeated string extend_repeated_string = 44; + repeated bytes extend_repeated_bytes = 45; + repeated ForeignEnum extend_repeated_foreign_enum = 52; + + repeated int32 extend_packed_repeated_int32 = 61 [packed=true]; + repeated int64 extend_packed_repeated_int64 = 62 [packed=true]; + repeated uint32 extend_packed_repeated_uint32 = 63 [packed=true]; + repeated uint64 extend_packed_repeated_uint64 = 64 [packed=true]; + repeated sint32 extend_packed_repeated_sint32 = 65 [packed=true]; + repeated sint64 extend_packed_repeated_sint64 = 66 [packed=true]; + repeated fixed32 extend_packed_repeated_fixed32 = 67 [packed=true]; + repeated fixed64 extend_packed_repeated_fixed64 = 68 [packed=true]; + repeated sfixed32 extend_packed_repeated_sfixed32 = 69 [packed=true]; + repeated sfixed64 extend_packed_repeated_sfixed64 = 70 [packed=true]; + repeated float extend_packed_repeated_float = 71 [packed=true]; + repeated double extend_packed_repeated_double = 72 [packed=true]; + repeated bool extend_packed_repeated_bool = 73 [packed=true]; + repeated ForeignEnum extend_packed_repeated_foreign_enum = 82 + [packed=true]; + +} + +message TestMapFields { + map map_string_string = 1; + map map_string_int32 = 2; + map map_string_int64 = 3; + map map_string_bool = 4; + map map_string_double = 5; + map map_string_enum = 6; + map map_string_msg = 7; + + map map_int32_string = 8; + map map_int64_string = 9; + map map_bool_string = 10; + + optional TestMapFields test_map_fields = 11; + map map_string_testmapfields = 12; +} + +enum MapValueEnum { + MAP_VALUE_FOO = 0; + MAP_VALUE_BAR = 1; + MAP_VALUE_BAZ = 2; +} + +message MapValueMessage { + optional int32 foo = 1; +} diff --git a/js/compatibility_tests/v3.1.0/testempty.proto b/js/compatibility_tests/v3.1.0/testempty.proto new file mode 100644 index 00000000..960bce4e --- /dev/null +++ b/js/compatibility_tests/v3.1.0/testempty.proto @@ -0,0 +1,34 @@ +// 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. + +syntax = "proto2"; + +package javatests.com.google.apps.jspb; + diff --git a/js/message.js b/js/message.js index 1f6bf16b..f7dcfa96 100644 --- a/js/message.js +++ b/js/message.js @@ -41,7 +41,6 @@ goog.provide('jspb.Message'); goog.require('goog.array'); goog.require('goog.asserts'); goog.require('goog.crypt.base64'); -goog.require('goog.json'); goog.require('jspb.Map'); // Not needed in compilation units that have no protos with xids. @@ -192,8 +191,8 @@ goog.define('jspb.Message.GENERATE_FROM_OBJECT', !goog.DISALLOW_TEST_ONLY_CODE); /** * @define {boolean} Whether to generate toString methods for objects. Turn - * this off if you do use toString in your project and want to trim it from - * compiled JS. + * this off if you do not use toString in your project and want to trim it + * from the compiled JS. */ goog.define('jspb.Message.GENERATE_TO_STRING', true); @@ -351,7 +350,7 @@ jspb.Message.initialize = function( // which would otherwise go unused. msg.arrayIndexOffset_ = messageId === 0 ? -1 : 0; msg.array = data; - jspb.Message.materializeExtensionObject_(msg, suggestedPivot); + jspb.Message.initPivotAndExtensionObject_(msg, suggestedPivot); msg.convertedFloatingPointFields_ = {}; if (repeatedFields) { @@ -364,6 +363,7 @@ jspb.Message.initialize = function( jspb.Message.EMPTY_LIST_SENTINEL_ : []); } else { + jspb.Message.maybeInitEmptyExtensionObject_(msg); msg.extensionObject_[fieldNumber] = msg.extensionObject_[fieldNumber] || (jspb.Message.MINIMIZE_MEMORY_ALLOCATIONS ? @@ -408,17 +408,16 @@ jspb.Message.isArray_ = function(o) { /** - * Ensures that the array contains an extension object if necessary. * If the array contains an extension object in its last position, then the - * object is kept in place and its position is used as the pivot. If not, then - * create an extension object using suggestedPivot. If suggestedPivot is -1, - * we don't have an extension object at all, in which case all fields are stored - * in the array. + * object is kept in place and its position is used as the pivot. If not, + * decides the pivot of the message based on suggestedPivot without + * materializing the extension object. + * * @param {!jspb.Message} msg The JsPb proto to modify. * @param {number} suggestedPivot See description for initialize(). * @private */ -jspb.Message.materializeExtensionObject_ = function(msg, suggestedPivot) { +jspb.Message.initPivotAndExtensionObject_ = function(msg, suggestedPivot) { if (msg.array.length) { var foundIndex = msg.array.length - 1; var obj = msg.array[foundIndex]; @@ -434,26 +433,17 @@ jspb.Message.materializeExtensionObject_ = function(msg, suggestedPivot) { return; } } - // This complexity exists because we keep all extension fields in the - // extensionObject_ regardless of proto field number. Changing this would - // simplify the code here, but it would require changing the serialization - // format from the server, which is not backwards compatible. - // TODO(jshneier): Should we just treat extension fields the same as - // non-extension fields, and select whether they appear in the object or in - // the array purely based on tag number? This would allow simplifying all the - // get/setExtension logic, but it would require the breaking change described - // above. + if (suggestedPivot > -1) { msg.pivot_ = suggestedPivot; - var pivotIndex = jspb.Message.getIndex_(msg, suggestedPivot); - if (!jspb.Message.MINIMIZE_MEMORY_ALLOCATIONS) { - msg.extensionObject_ = msg.array[pivotIndex] = {}; - } else { - // Initialize to null to avoid changing the shape of the proto when it - // gets eventually set. - msg.extensionObject_ = null; - } + // Avoid changing the shape of the proto with an empty extension object by + // deferring the materialization of the extension object until the first + // time a field set into it (may be due to getting a repeated proto field + // from it, in which case a new empty array is set into it at first). + msg.extensionObject_ = null; } else { + // suggestedPivot is -1, which means that we don't have an extension object + // at all, in which case all fields are stored in the array. msg.pivot_ = Number.MAX_VALUE; } }; @@ -513,7 +503,7 @@ jspb.Message.toObjectExtension = function(proto, obj, extensions, for (var fieldNumber in extensions) { var fieldInfo = extensions[fieldNumber]; var value = getExtensionFn.call(proto, fieldInfo); - if (goog.isDefAndNotNull(value)) { + if (value != null) { for (var name in fieldInfo.fieldName) { if (fieldInfo.fieldName.hasOwnProperty(name)) { break; // the compiled field name @@ -557,7 +547,7 @@ jspb.Message.serializeBinaryExtensions = function(proto, writer, extensions, 'without binary serialization support'); } var value = getExtensionFn.call(proto, fieldInfo); - if (goog.isDefAndNotNull(value)) { + if (value != null) { if (fieldInfo.isMessageType()) { // If the message type of the extension was generated without binary // support, there may not be a binary message serializer function, and @@ -647,6 +637,9 @@ jspb.Message.getField = function(msg, fieldNumber) { } return val; } else { + if (!msg.extensionObject_) { + return undefined; + } var val = msg.extensionObject_[fieldNumber]; if (val === jspb.Message.EMPTY_LIST_SENTINEL_) { return msg.extensionObject_[fieldNumber] = []; @@ -656,6 +649,32 @@ jspb.Message.getField = function(msg, fieldNumber) { }; +/** + * Gets the value of a non-extension repeated field. + * @param {!jspb.Message} msg A jspb proto. + * @param {number} fieldNumber The field number. + * @return {!Array} + * The field's value. + * @protected + */ +jspb.Message.getRepeatedField = function(msg, fieldNumber) { + if (fieldNumber < msg.pivot_) { + var index = jspb.Message.getIndex_(msg, fieldNumber); + var val = msg.array[index]; + if (val === jspb.Message.EMPTY_LIST_SENTINEL_) { + return msg.array[index] = []; + } + return val; + } + + var val = msg.extensionObject_[fieldNumber]; + if (val === jspb.Message.EMPTY_LIST_SENTINEL_) { + return msg.extensionObject_[fieldNumber] = []; + } + return val; +}; + + /** * Gets the value of an optional float or double field. * @param {!jspb.Message} msg A jspb proto. @@ -678,7 +697,7 @@ jspb.Message.getOptionalFloatingPointField = function(msg, fieldNumber) { * @protected */ jspb.Message.getRepeatedFloatingPointField = function(msg, fieldNumber) { - var values = jspb.Message.getField(msg, fieldNumber); + var values = jspb.Message.getRepeatedField(msg, fieldNumber); if (!msg.convertedFloatingPointFields_) { msg.convertedFloatingPointFields_ = {}; } @@ -864,6 +883,7 @@ jspb.Message.setField = function(msg, fieldNumber, value) { if (fieldNumber < msg.pivot_) { msg.array[jspb.Message.getIndex_(msg, fieldNumber)] = value; } else { + jspb.Message.maybeInitEmptyExtensionObject_(msg); msg.extensionObject_[fieldNumber] = value; } }; @@ -878,7 +898,7 @@ jspb.Message.setField = function(msg, fieldNumber, value) { * @protected */ jspb.Message.addToRepeatedField = function(msg, fieldNumber, value, opt_index) { - var arr = jspb.Message.getField(msg, fieldNumber); + var arr = jspb.Message.getRepeatedField(msg, fieldNumber); if (opt_index != undefined) { arr.splice(opt_index, 0, value); } else { @@ -1006,7 +1026,7 @@ jspb.Message.wrapRepeatedField_ = function(msg, ctor, fieldNumber) { msg.wrappers_ = {}; } if (!msg.wrappers_[fieldNumber]) { - var data = jspb.Message.getField(msg, fieldNumber); + var data = jspb.Message.getRepeatedField(msg, fieldNumber); for (var wrappers = [], i = 0; i < data.length; i++) { wrappers[i] = new ctor(data[i]); } @@ -1101,7 +1121,7 @@ jspb.Message.addToRepeatedWrapperField = function( wrapperArray = msg.wrappers_[fieldNumber] = []; } var insertedValue = value ? value : new ctor(); - var array = jspb.Message.getField(msg, fieldNumber); + var array = jspb.Message.getRepeatedField(msg, fieldNumber); if (index != undefined) { wrapperArray.splice(index, 0, insertedValue); array.splice(index, 0, insertedValue.toArray()); diff --git a/js/message_test.js b/js/message_test.js index dc0ae211..dd3d9267 100644 --- a/js/message_test.js +++ b/js/message_test.js @@ -33,6 +33,7 @@ goog.setTestOnly(); goog.require('goog.json'); +goog.require('goog.string'); goog.require('goog.testing.asserts'); goog.require('goog.userAgent'); @@ -64,11 +65,13 @@ goog.require('proto.jspb.test.floatingStrField'); goog.require('proto.jspb.test.HasExtensions'); goog.require('proto.jspb.test.IndirectExtension'); goog.require('proto.jspb.test.IsExtension'); +goog.require('proto.jspb.test.MessageWithLargeFieldTags'); goog.require('proto.jspb.test.OptionalFields'); goog.require('proto.jspb.test.OuterEnum'); goog.require('proto.jspb.test.OuterMessage.Complex'); goog.require('proto.jspb.test.Simple1'); goog.require('proto.jspb.test.Simple2'); +goog.require('proto.jspb.test.SingularsWithLargeFieldTags'); goog.require('proto.jspb.test.SpecialCases'); goog.require('proto.jspb.test.TestClone'); goog.require('proto.jspb.test.TestEndsWithBytes'); @@ -83,8 +86,6 @@ goog.require('proto.jspb.test.ExtensionMessage'); goog.require('proto.jspb.test.TestExtensionsMessage'); - - describe('Message test suite', function() { it('testEmptyProto', function() { var empty1 = new proto.jspb.test.Empty([]); @@ -273,12 +274,6 @@ describe('Message test suite', function() { assertFalse(response.hasEnumField()); }); - it('testMessageRegistration', /** @suppress {visibility} */ function() { - // goog.require(SomeResponse) will include its library, which will in - // turn add SomeResponse to the message registry. - assertEquals(jspb.Message.registry_['res'], proto.jspb.test.SomeResponse); - }); - it('testClearFields', function() { var data = ['str', true, [11], [[22], [33]], ['s1', 's2']]; var foo = new proto.jspb.test.OptionalFields(data); @@ -661,12 +656,7 @@ describe('Message test suite', function() { it('testInitialization_emptyArray', function() { var msg = new proto.jspb.test.HasExtensions([]); - if (jspb.Message.MINIMIZE_MEMORY_ALLOCATIONS) { - assertArrayEquals([], msg.toArray()); - } else { - // Extension object is created past all regular fields. - assertArrayEquals([,,, {}], msg.toArray()); - } + assertArrayEquals([], msg.toArray()); }); it('testInitialization_justExtensionObject', function() { diff --git a/js/proto3_test.proto b/js/proto3_test.proto index acb67164..0d073ea0 100644 --- a/js/proto3_test.proto +++ b/js/proto3_test.proto @@ -86,4 +86,5 @@ enum Proto3Enum { PROTO3_FOO = 0; PROTO3_BAR = 1; PROTO3_BAZ = 2; + MSG_PROTO3_BAH = 3; } diff --git a/js/test.proto b/js/test.proto index 2be5b8c1..7c881c0d 100644 --- a/js/test.proto +++ b/js/test.proto @@ -235,6 +235,13 @@ message TestEndsWithBytes { } +message Int64Types { + optional int64 int64_normal = 1 [jstype=JS_NORMAL]; + optional sint64 int64_string = 2 [jstype=JS_STRING]; + optional uint64 int64_number = 3 [jstype=JS_NUMBER]; + +} + message TestMapFieldsNoBinary { map map_string_string = 1; @@ -271,3 +278,4 @@ message Deeply { } } } + -- cgit v1.2.3