--- /dev/null
+/*\r
+ Copyright 2013 Daniel Wirtz <dcode@dcode.io>\r
+\r
+ Licensed under the Apache License, Version 2.0 (the "License");\r
+ you may not use this file except in compliance with the License.\r
+ You may obtain a copy of the License at\r
+\r
+ http://www.apache.org/licenses/LICENSE-2.0\r
+\r
+ Unless required by applicable law or agreed to in writing, software\r
+ distributed under the License is distributed on an "AS IS" BASIS,\r
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+ See the License for the specific language governing permissions and\r
+ limitations under the License.\r
+ */\r
+\r
+/**\r
+ * ProtoBuf.js Test Suite.\r
+ * @author Daniel Wirtz <dcode@dcode.io>\r
+ */\r
+(function(global) {\r
+\r
+ var FILE = "protobuf.js";\r
+ var BROWSER = !!global.window;\r
+ var StdOutFixture = require('fixture-stdout');\r
+ var fixture = new StdOutFixture();\r
+\r
+ var ProtoBuf = BROWSER ? global.dcodeIO.ProtoBuf : require(__dirname+"/../dist/"+FILE),\r
+ ByteBuffer = BROWSER ? global.dcodeIO.ByteBuffer : ByteBuffer || require("bytebuffer"),\r
+ util = BROWSER ? null : require("util"),\r
+ fs = BROWSER ? null : require("fs");\r
+\r
+ if (typeof __dirname == 'undefined') {\r
+ __dirname = document.location.href.replace(/[\/\\][^\/\\]*$/, "");\r
+ }\r
+\r
+ /**\r
+ * Constructs a new Sandbox for module loaders and shim testing.\r
+ * @param {Object.<string,*>} properties Additional properties to set\r
+ * @constructor\r
+ */\r
+ var Sandbox = function(properties) {\r
+ this.ByteBuffer = function() {};\r
+ for (var i in properties) {\r
+ this[i] = properties[i];\r
+ }\r
+ this.console = {\r
+ log: function(s) {\r
+ console.log(s);\r
+ }\r
+ };\r
+ };\r
+\r
+ function fail(e) {\r
+ throw(e);\r
+ }\r
+\r
+ /**\r
+ * Validates the complexDotProto and complexInline tests.\r
+ * @param {*} test Nodeunit test\r
+ * @param {Object} Game Game namespace\r
+ */\r
+ function validateComplex(test, Game) {\r
+ var Car = Game.Cars.Car,\r
+ Vendor = Car.Vendor,\r
+ Speed = Car.Speed;\r
+\r
+ var vendor;\r
+ // Car from class with argument list properties\r
+ var car = new Car(\r
+ "Rusty",\r
+ // Vendor from class with object properties\r
+ vendor = new Vendor({\r
+ "name": "Iron Inc.",\r
+ // Address from object\r
+ "address": {\r
+ "country": "US"\r
+ },\r
+ "models": ["m1"]\r
+ }),\r
+ // Speed from enum object\r
+ Speed.SUPERFAST\r
+ );\r
+ test.equal(car.model, "Rusty");\r
+ test.equal(car.vendor.name, "Iron Inc.");\r
+ test.equal(car.vendor.address.country, "US");\r
+ test.equal(car.vendor.address.country, car.getVendor().get_address().country);\r
+ var bb = new ByteBuffer(32);\r
+ car.encode(bb);\r
+ test.equal(bb.flip().toString("debug"), "<0A 05 52 75 73 74 79 12 15 0A 09 49 72 6F 6E 20 49 6E 63 2E 12 04 0A 02 55 53 1A 02 6D 31 18 02>");\r
+ var carDec = Car.decode(bb);\r
+ test.equal(carDec.model, "Rusty");\r
+ test.equal(carDec.vendor.name, "Iron Inc.");\r
+ test.equal(carDec.vendor.address.country, "US");\r
+ test.equal(carDec.vendor.address.country, carDec.getVendor().get_address().country);\r
+ test.equal(carDec.vendor.models[0], "m1");\r
+ }\r
+\r
+ /**\r
+ * Test suite.\r
+ * @type {Object.<string,function>}\r
+ */\r
+ var suite = {\r
+\r
+ "init": function(test) {\r
+ test.ok(typeof ProtoBuf == "object");\r
+ test.ok(typeof ProtoBuf.Reflect == 'object');\r
+ test.ok(typeof ProtoBuf.loadProto == "function");\r
+ test.ok(typeof ProtoBuf.loadProtoFile == "function");\r
+ test.strictEqual(ProtoBuf.loadProto, ProtoBuf.protoFromString);\r
+ test.strictEqual(ProtoBuf.loadProtoFile, ProtoBuf.protoFromFile);\r
+ test.ok(ProtoBuf.ByteBuffer);\r
+ test.done();\r
+ },\r
+\r
+ "IS_NODE": function(test) {\r
+ test.ok(ProtoBuf.Util.IS_NODE);\r
+ test.done();\r
+ },\r
+\r
+ // Example "A Simple Message" from the protobuf docs\r
+ // https://developers.google.com/protocol-buffers/docs/encoding#simple\r
+ "example1": function(test) {\r
+ try{\r
+ var builder = ProtoBuf.loadProtoFile(__dirname+"/example1.proto");\r
+ var Test1 = builder.build("Test1");\r
+ test.ok(typeof Test1 == 'function');\r
+ var inst = new Test1(150);\r
+ test.ok(inst instanceof ProtoBuf.Builder.Message);\r
+ test.equal(inst.a, 150);\r
+ test.equal(inst.getA(), 150);\r
+ test.equal(inst.get_a(), 150);\r
+ inst.setA(151);\r
+ test.equal(inst.a, 151);\r
+ test.equal(inst.getA(), 151);\r
+ test.equal(inst.get_a(), 151);\r
+ inst.set_a(152);\r
+ test.equal(inst.a, 152);\r
+ test.equal(inst.toString(), ".Test1");\r
+ test.throws(function() {\r
+ inst.setA(null); // required\r
+ });\r
+ test.throws(function() {\r
+ inst.setA([]);\r
+ });\r
+ var size = inst.calculate();\r
+ var bb = new ByteBuffer(3);\r
+ inst.encode(bb);\r
+ test.strictEqual(bb.offset, size);\r
+ test.equal(bb.flip().toString("debug"), "<08 98 01>");\r
+ var instDec = Test1.decode(bb);\r
+ test.equal(instDec.a, 152);\r
+\r
+ } catch (e) {\r
+ fail(e);\r
+ }\r
+ test.done();\r
+ },\r
+\r
+ // Basically the same as example1, but with an unsigned value.\r
+ "example1u": function(test) {\r
+ try{\r
+ var builder = ProtoBuf.loadProtoFile(__dirname+"/example1u.proto");\r
+ var Test1u = builder.build("Test1u");\r
+ test.ok(typeof Test1u == 'function');\r
+ var inst = new Test1u(-1);\r
+ test.strictEqual(inst.a, 4294967295);\r
+ var bb = new ByteBuffer(6);\r
+ var size = inst.calculate();\r
+ inst.encode(bb);\r
+ test.strictEqual(bb.offset, size);\r
+ test.equal(bb.flip().toString("debug"), "<08 FF FF FF FF 0F>");\r
+ var instDec = Test1u.decode(bb);\r
+ test.strictEqual(instDec.a, 4294967295);\r
+\r
+ } catch (e) {\r
+ fail(e);\r
+ }\r
+ test.done();\r
+ },\r
+\r
+ // Example "Strings" from the protobuf docs\r
+ // https://developers.google.com/protocol-buffers/docs/encoding#types\r
+ "example2": function(test) {\r
+ try {\r
+ var builder = ProtoBuf.loadProtoFile(__dirname+"/example2.proto");\r
+ var Test2 = builder.build("Test2");\r
+ var inst = new Test2("testing");\r
+ var bb = new ByteBuffer(9);\r
+ var size = inst.calculate();\r
+ inst.encode(bb);\r
+ test.strictEqual(bb.offset, size);\r
+ test.equal(bb.flip().toString("debug"), "<12 07 74 65 73 74 69 6E 67>");\r
+ var instDec = Test2.decode(bb);\r
+ test.equal(instDec.b, "testing");\r
+ } catch (e) {\r
+ fail(e);\r
+ }\r
+ test.done();\r
+ },\r
+\r
+ // Example "Embedded Messages" from the protobuf docs\r
+ // https://developers.google.com/protocol-buffers/docs/encoding#embedded\r
+ "example3": function(test) {\r
+ try {\r
+ var builder = ProtoBuf.loadProtoFile(__dirname+"/example3.proto");\r
+ var root = builder.build();\r
+ var Test1 = root.Test1;\r
+ var Test3 = root.Test3;\r
+ var inst = new Test3(new Test1(150));\r
+ var bb = new ByteBuffer(5);\r
+ test.equal(inst.c.a, 150);\r
+ var size = inst.calculate();\r
+ inst.encode(bb);\r
+ test.strictEqual(bb.offset, size);\r
+ test.equal(bb.flip().toString("debug"), "<1A 03 08 96 01>");\r
+ var instDec = Test3.decode(bb);\r
+ test.equal(instDec.c.a, 150);\r
+ } catch(e) {\r
+ fail(e);\r
+ }\r
+ test.done();\r
+ },\r
+\r
+ "example4": function(test) {\r
+ try {\r
+ var builder = ProtoBuf.loadProtoFile(__dirname+"/example4.proto");\r
+ var Test4 = builder.build("Test4");\r
+ var inst = new Test4([3, 270, 86942]);\r
+ var bb = new ByteBuffer(8);\r
+ test.equal(inst.d.length, 3);\r
+ var size = inst.calculate();\r
+ inst.encode(bb);\r
+ test.strictEqual(bb.offset, size);\r
+ test.equal(bb.flip().toString("debug"), "<22 06 03 8E 02 9E A7 05>");\r
+ var instDec = Test4.decode(bb);\r
+ test.equal(bb.toString("debug"), "22 06 03 8E 02 9E A7 05|");\r
+ test.equal(instDec.d.length, 3);\r
+ test.equal(instDec.d[2], 86942);\r
+ } catch(e) {\r
+ fail(e);\r
+ }\r
+ test.done();\r
+ },\r
+\r
+ "example5": function(test) {\r
+ try {\r
+ var builder = ProtoBuf.loadProtoFile(__dirname+"/example5.proto");\r
+ builder.build();\r
+ } catch(e) {\r
+ fail(e);\r
+ }\r
+ test.done();\r
+ },\r
+\r
+ "constructor": function(test) {\r
+ var builder = ProtoBuf.loadProtoFile(__dirname+"/example1.proto");\r
+ var Test1 = builder.build("Test1");\r
+ var t1 = new Test1(123),\r
+ t2 = new Test1({a: 123}),\r
+ t3 = new Test1(t1);\r
+ test.deepEqual(t1, t2);\r
+ test.deepEqual(t2, t3);\r
+ test.done();\r
+ },\r
+ \r
+ "constructorWithOneofs": function(test) {\r
+ try {\r
+ var builder = ProtoBuf.loadProtoFile(__dirname+"/oneof.proto"),\r
+ MyOneOf = builder.build("MyOneOf"),\r
+ TOneOf = builder.lookup(".MyOneOf");\r
+ test.ok(TOneOf.getChild("my_oneof"));\r
+ \r
+ var myOneOf = new MyOneOf();\r
+ test.strictEqual(myOneOf.my_oneof, null);\r
+ myOneOf.set("id", 1);\r
+ test.strictEqual(myOneOf.my_oneof, "id");\r
+ myOneOf.set("name", "me");\r
+ test.strictEqual(myOneOf.my_oneof, "name");\r
+ test.strictEqual(myOneOf.id, null);\r
+ \r
+ var copy = new MyOneOf(myOneOf); // this line is what was failing\r
+ // Error: .MyOneOf#my_oneof is not a field: undefined\r
+ \r
+ test.deepEqual(myOneOf, copy);\r
+ \r
+ // Test same things are there\r
+ test.strictEqual(copy.my_oneof, "name");\r
+ test.strictEqual(copy.name, "me");\r
+ test.strictEqual(copy.id, null);\r
+ } catch (e) {\r
+ fail(e);\r
+ }\r
+ test.done();\r
+ },\r
+\r
+ "numberFormats": function(test) {\r
+ try {\r
+ var builder = ProtoBuf.loadProtoFile(__dirname+"/numberformats.proto");\r
+ var Formats = builder.build("Formats");\r
+ test.strictEqual(Formats.DEC, 1);\r
+ test.strictEqual(Formats.HEX, 31);\r
+ test.strictEqual(Formats.OCT, 15);\r
+ var Msg = builder.build("Msg");\r
+ var msg = new Msg();\r
+ test.strictEqual(msg.dec, -1);\r
+ test.strictEqual(msg.hex, -31);\r
+ test.strictEqual(msg.hexUC, 521);\r
+ test.strictEqual(msg.oct, -15);\r
+ test.strictEqual(msg.exp, 0.1e5);\r
+ test.strictEqual(msg.nod, 1.);\r
+ test.strictEqual(msg.exn, 1e8);\r
+ test.strictEqual(msg.sp1, Infinity);\r
+ test.strictEqual(msg.sp2, -Infinity);\r
+ test.ok(isNaN(msg.sp3));\r
+ } catch (e) {\r
+ fail(e);\r
+ }\r
+ test.done();\r
+ },\r
+\r
+ // Check encode/decode against a table of known correct pairs.\r
+ // Note that javascript ArrayBuffer does not support signed Zero or NaN\r
+ // bertdouglas (https://github.com/bertdouglas)\r
+ "float": function(test) {\r
+ try {\r
+ var str_proto = "message Float {"\r
+ + " required float f = 1;"\r
+ + "}";\r
+ var builder = ProtoBuf.loadProto(str_proto);\r
+ var root = builder.build();\r
+ var Float = root.Float;\r
+\r
+ var in_tolerance = function (reference,actual) {\r
+ var tol = 1e-6;\r
+ var scale = 1.0;\r
+ if (reference != 0.0 ) {\r
+ scale = reference;\r
+ };\r
+ var err = Math.abs(reference - actual)/scale;\r
+ return err < tol;\r
+ };\r
+\r
+ var f_vals = [\r
+ // hex values are shown here in big-endian following IEEE754 notation\r
+ // protobuf is little-endian\r
+ // { f: -0.0 , b: "80 00 00 00" },\r
+ { f: +0.0 , b: "00 00 00 00" },\r
+ { f: -1e-10 , b: "AE DB E6 FF" },\r
+ { f: +1e-10 , b: "2E DB E6 FF" },\r
+ { f: -2e+10 , b: "D0 95 02 F9" },\r
+ { f: +2e+10 , b: "50 95 02 F9" },\r
+ { f: -3e-30 , b: "8E 73 63 90" },\r
+ { f: +3e-30 , b: "0E 73 63 90" },\r
+ { f: -4e+30 , b: "F2 49 F2 CA" },\r
+ { f: +4e+30 , b: "72 49 F2 CA" },\r
+ { f: -123456789.0 , b: "CC EB 79 A3" },\r
+ { f: +123456789.0 , b: "4C EB 79 A3" },\r
+ { f: -0.987654321 , b: "BF 7C D6 EA" },\r
+ { f: +0.987654321 , b: "3F 7C D6 EA" },\r
+ { f: -Infinity , b: "FF 80 00 00" },\r
+ { f: +Infinity , b: "7F 80 00 00" }\r
+ // { f: -NaN , b: "FF C0 00 00>" },\r
+ // { f: +NaN , b: "7F C0 00 00" }\r
+ ];\r
+\r
+ f_vals.map( function(x) {\r
+ // check encode\r
+ var m1 = new Float();\r
+ var b1 = new ByteBuffer();\r
+ m1.f = x.f;\r
+ m1.encode(b1);\r
+ var q1 = b1.slice(1,5).compact().reverse();\r
+ test.strictEqual('<' + x.b + '>', q1.toString("debug"));\r
+\r
+ // check decode\r
+ var b2 = new ByteBuffer();\r
+ var s1 = x.b + ' 0D';\r
+ var s2 = s1.split(" ");\r
+ var s3 = s2.reverse();\r
+ var i1 = s3.map(function(y) { return parseInt(y,16) } );\r
+ i1.map(function(y) { b2.writeUint8(y) });\r
+ b2.limit = b2.offset;\r
+ b2.offset = 0;\r
+ var m2 = Float.decode(b2);\r
+\r
+ var s4 = "" + x.f +" " + m2.f;\r
+ if ( isNaN(x.f) ) {\r
+ test.ok( isNaN(m2.f), s4 );\r
+ }\r
+ else if ( ! isFinite( x.f) ) {\r
+ test.ok( x.f === m2.f, s4 );\r
+ }\r
+ else {\r
+ test.ok( in_tolerance(x.f, m2.f), s4 );\r
+ }\r
+ });\r
+ } catch(e) {\r
+ fail(e);\r
+ }\r
+ test.done();\r
+ },\r
+\r
+ "bytes": function(test) {\r
+ try {\r
+ var str_proto = "message Test { required bytes b = 1; }";\r
+ var builder = ProtoBuf.loadProto(str_proto);\r
+ var Test = builder.build("Test");\r
+ var bb = new ByteBuffer(4).writeUint32(0x12345678).flip();\r
+ var myTest = new Test(bb);\r
+ test.strictEqual(myTest.b.array, bb.array);\r
+ var bb2 = new ByteBuffer(6);\r
+ var size = myTest.calculate();\r
+ myTest.encode(bb2);\r
+ test.strictEqual(bb2.offset, size);\r
+ test.equal(bb2.flip().toString("debug"), "<0A 04 12 34 56 78>");\r
+ myTest = Test.decode(bb2);\r
+ test.equal(myTest.b.BE().readUint32(), 0x12345678);\r
+ } catch (e) {\r
+ fail(e);\r
+ }\r
+ test.done();\r
+ },\r
+\r
+ "bytesFromFile": function(test) {\r
+ try {\r
+ var builder = ProtoBuf.loadProto("message Image { required bytes data = 1; }"),\r
+ Image = builder.build("Image"),\r
+ data = fs.readFileSync(__dirname+"/../protobuf.png"),\r
+ image = new Image({ data: data }),\r
+ bb = image.encode(),\r
+ imageDec = Image.decode(bb),\r
+ dataDec = imageDec.data.toBuffer();\r
+ test.strictEqual(data.length, dataDec.length);\r
+ test.deepEqual(data, dataDec);\r
+ } catch (e) {\r
+ fail(e);\r
+ }\r
+ test.done();\r
+ },\r
+\r
+ "notEnoughBytes": function(test) {\r
+ var builder = ProtoBuf.loadProto("message Test { required bytes b = 1; }");\r
+ var Test = builder.build("Test");\r
+ var bb = new ByteBuffer().writeUint32(0x12345678).flip();\r
+ var encoded = new ByteBuffer(6);\r
+ new Test(bb).encode(encoded);\r
+ test.equal(encoded.flip().toString("debug"), "<0A 04 12 34 56 78>");\r
+ encoded = encoded.slice(0, 5); // chop off the last byte\r
+ var err = null;\r
+ try {\r
+ Test.decode(encoded);\r
+ } catch (caught) {\r
+ err = caught;\r
+ }\r
+ test.ok(err && err.message && err.message.indexOf(": 4 required but got only 3") >= 0);\r
+ test.done();\r
+ },\r
+\r
+ "bool": function(test) {\r
+ try {\r
+ var builder = ProtoBuf.loadProto("message Test { optional bool ok = 1 [ default = false ]; }"),\r
+ Test = builder.build("Test"),\r
+ t = new Test();\r
+ test.strictEqual(t.ok, null); // Not set as it is optional\r
+ t.setOk(true);\r
+ test.strictEqual(t.ok, true);\r
+ test.strictEqual(Test.decode(t.encode()).ok, true);\r
+ t.setOk(false);\r
+ test.strictEqual(t.ok, false);\r
+ t.setOk(null); // Not set\r
+ test.strictEqual(Test.decode(t.encode()).ok, false); // = default when missing\r
+ } catch (err) {\r
+ fail(err);\r
+ }\r
+ test.done();\r
+ },\r
+\r
+ // As mentioned by Bill Katz\r
+ "T139": function(test) {\r
+ try{\r
+ var builder = ProtoBuf.loadProtoFile(__dirname+"/T139.proto");\r
+ var T139 = builder.build("T139");\r
+ test.ok(typeof T139 == 'function');\r
+ var inst = new T139(139,139);\r
+ test.equal(inst.a, 139);\r
+ test.equal(inst.b, 139);\r
+ inst.setA(139);\r
+ inst.setB(139);\r
+ test.equal(inst.a, 139);\r
+ test.equal(inst.b, 139);\r
+ var bb = new ByteBuffer(3);\r
+ inst.encode(bb);\r
+ test.equal(bb.flip().toString("debug"), "<08 8B 01 10 8B 01>");\r
+ var instDec = T139.decode(bb);\r
+ test.equal(instDec.a, 139);\r
+ test.equal(instDec.b, 139);\r
+ } catch (e) {\r
+ fail(e);\r
+ }\r
+ test.done();\r
+ },\r
+\r
+ "emptyDefaultString": function(test) {\r
+ try {\r
+ var builder = ProtoBuf.loadProto("message Test1 { required string test = 1 [default = \"\"]; }");\r
+ var Test1;\r
+ test.doesNotThrow(function() {\r
+ Test1 = builder.build("Test1");\r
+ });\r
+ var test1 = new Test1();\r
+ test.strictEqual(test1.test, "");\r
+ } catch (e) {\r
+ fail(e);\r
+ }\r
+ test.done();\r
+ },\r
+\r
+ "trailingSemicolon": function(test) {\r
+ try {\r
+ var builder = ProtoBuf.loadProto("message Test1 { optional string test = 1; };");\r
+ test.doesNotThrow(function() {\r
+ var Test1 = builder.build("Test1");\r
+ });\r
+ } catch (e) {\r
+ fail(e);\r
+ }\r
+ test.done();\r
+ },\r
+\r
+ "inner": {\r
+\r
+ "longstr": function(test) {\r
+ try {\r
+ var builder = ProtoBuf.loadProto("message Test { required Inner a = 1; message Inner { required string b = 1; } }");\r
+ var Test = builder.build("Test");\r
+ var t = new Test();\r
+ var data = "0123456789"; // 10: 20, 40, 80, 160, 320 bytes\r
+ for (var i=0; i<5; i++) data += data;\r
+ test.equal(data.length, 320);\r
+ t.a = new Test.Inner(data);\r
+ var bb = t.encode();\r
+ var t2 = Test.decode(bb);\r
+ test.equal(t2.a.b.length, 320);\r
+ test.equal(data, t2.a.b);\r
+ } catch (e) {\r
+ fail(e);\r
+ }\r
+ test.done();\r
+ },\r
+\r
+ "multiple": function(test) {\r
+ try {\r
+ var str = "";\r
+ for (var i=0; i<200; i++) str += 'a';\r
+ var builder = ProtoBuf.loadProtoFile(__dirname+"/inner.proto");\r
+ var fooCls = builder.build("Foo");\r
+ var barCls = builder.build("Bar");\r
+ var bazCls = builder.build("Baz");\r
+ var foo = new fooCls(new barCls(str), new bazCls(str));\r
+ var fooEncoded = foo.encode();\r
+ test.doesNotThrow(function() {\r
+ fooCls.decode(fooEncoded);\r
+ });\r
+ } catch (e) {\r
+ fail(e);\r
+ }\r
+ test.done();\r
+ },\r
+\r
+ "float": function(test) {\r
+ try {\r
+ var builder = ProtoBuf.loadProto("message Foo { required Bar bar = 1; } message Bar { required float baz = 1; }");\r
+ var root = builder.build();\r
+ var foo = new root.Foo(new root.Bar(4));\r
+ var bb = foo.encode();\r
+ var foo2 = root.Foo.decode(bb);\r
+ test.equal(foo.bar.baz, 4);\r
+ test.equal(foo2.bar.baz, foo.bar.baz);\r
+ } catch (e) {\r
+ fail(e);\r
+ }\r
+ test.done();\r
+ }\r
+\r
+ },\r
+\r
+ "truncated": function(test) {\r
+ try {\r
+ var builder = ProtoBuf.loadProto("message Test { required int32 a = 1; required int32 b = 2; }");\r
+ var Test = builder.build("Test");\r
+ var t = new Test(), bb = new ByteBuffer(2);\r
+ t.setA(1);\r
+ try {\r
+ bb = t.encode(bb).flip();\r
+ test.ok(false);\r
+ } catch (e) {\r
+ test.ok(e.encoded);\r
+ bb = e.encoded.flip();\r
+ test.equal(bb.toString("debug"), "<08 01>");\r
+ }\r
+ var t2;\r
+ try /* to decode truncated message */ {\r
+ t2 = Test.decode(bb);\r
+ test.ok(false); // ^ throws\r
+ } catch (e) {\r
+ // But still be able to access the rest\r
+ var t3 = e.decoded;\r
+ test.strictEqual(t3.a, 1);\r
+ test.strictEqual(t3.b, null);\r
+ }\r
+ test.strictEqual(t2, undefined);\r
+ } catch (e) {\r
+ fail(e);\r
+ }\r
+ test.done();\r
+ },\r
+\r
+ // Options on all levels\r
+ "options": {\r
+\r
+ "parse": function(test) {\r
+ try {\r
+ var parser = new ProtoBuf.DotProto.Parser(ProtoBuf.Util.fetch(__dirname+"/options.proto"));\r
+ var root = parser.parse();\r
+ test.equal(root["package"], "My");\r
+ test.strictEqual(root["options"]["(toplevel_1)"], 10);\r
+ test.equal(root["options"]["(toplevel_2)"], "Hello world!");\r
+ var opt = root["messages"][0]["fields"][0]["options"];\r
+ test.equal(opt["default"], "Max");\r
+ opt = root["messages"][0]["options"];\r
+ test.strictEqual(opt["(inmessage)"], "My.Test");\r
+ test.strictEqual(opt["(foo.my_option).bar"], false);\r
+ opt = root["messages"][0]["fields"][1]["options"];\r
+ test.strictEqual(opt["default"], "Shouldn't mix quotes");\r
+ opt = root["messages"][0]["fields"][2]["options"];\r
+ test.strictEqual(opt["default"], 'Shouldn"t mix quotes');\r
+ opt = root["messages"][0]["fields"][3]["options"];\r
+ test.strictEqual(opt["(foo_options).opt1"], 123);\r
+ test.strictEqual(opt["(foo_options).opt2"], "baz");\r
+ } catch (e) {\r
+ fail(e);\r
+ }\r
+ test.done();\r
+ },\r
+\r
+ "export": function(test) {\r
+ try {\r
+ var builder = ProtoBuf.loadProtoFile(__dirname+"/options.proto");\r
+ var My = builder.build("My");\r
+ test.deepEqual(My.$options, {\r
+ "(toplevel_1)": 10,\r
+ "(toplevel_2)": "Hello world!"\r
+ });\r
+ test.strictEqual(My.$options['(toplevel_1)'], 10);\r
+ test.deepEqual(My.Test.$options, {\r
+ "(inmessage)": "My.Test",\r
+ "(foo.my_option).bar": false\r
+ });\r
+ } catch (e) {\r
+ fail(e);\r
+ }\r
+ test.done();\r
+ }\r
+ },\r
+\r
+ // Comments\r
+ "comments": function(test) {\r
+ try {\r
+ var tn = new ProtoBuf.DotProto.Tokenizer(ProtoBuf.Util.fetch(__dirname+'/comments.proto'));\r
+ var token, tokens = [];\r
+ do {\r
+ token = tn.next();\r
+ tokens.push(token);\r
+ } while (token !== null);\r
+ test.deepEqual(tokens, ['message', 'TestC', '{', 'required', 'int32', 'a', '=', '1', ';', '}', null]);\r
+ } catch (e) {\r
+ fail(e);\r
+ }\r
+ test.done();\r
+ },\r
+\r
+ // A more or less complex proto with type references\r
+ "complexProto": function(test) {\r
+ try {\r
+ var builder = ProtoBuf.loadProtoFile(__dirname+"/complex.proto");\r
+ validateComplex(test, builder.build("Game"));\r
+ var TCars = builder.lookup("Game.Cars");\r
+ test.strictEqual(TCars.fqn(), ".Game.Cars");\r
+ } catch(e) {\r
+ fail(e);\r
+ }\r
+ test.done();\r
+ },\r
+\r
+ // The same created without calling upon the parser to do so\r
+ "complexJSON": function(test) {\r
+ try {\r
+ var builder = ProtoBuf.loadJsonFile(__dirname+"/complex.json");\r
+ validateComplex(test, builder.build("Game"));\r
+ } catch (e) {\r
+ fail(e);\r
+ }\r
+ test.done();\r
+ },\r
+\r
+ // Test error messages\r
+ "errorMessage": function(test) {\r
+ test.throws(function() {\r
+ var builder = ProtoBuf.loadJsonFile(__dirname+"/complex.json");\r
+ var Game = builder.build("Game");\r
+ var car = new Game.Cars.Car();\r
+ car.speed = "hello";\r
+ car.encode();\r
+ }, /Illegal value for speed/);\r
+ test.done();\r
+ },\r
+\r
+ // Builder reused to add definitions from multiple sources\r
+ "multiBuilder": function(test) {\r
+ try {\r
+ var builder = ProtoBuf.loadProtoFile(__dirname+"/example1.proto");\r
+ ProtoBuf.loadProtoFile(__dirname+"/example2.proto", builder);\r
+ var ns = builder.build();\r
+ test.ok(!!ns.Test1);\r
+ test.ok(!!ns.Test2);\r
+ } catch (e) {\r
+ fail(e);\r
+ }\r
+ test.done();\r
+ },\r
+\r
+ // Inner messages test\r
+ "inner": function(test) {\r
+ try {\r
+ var builder = ProtoBuf.loadProtoFile(__dirname+"/repeated.proto");\r
+ var root = builder.build(),\r
+ Outer = root.Outer,\r
+ Inner = root.Inner;\r
+ // Empty\r
+ var outer = new Outer();\r
+ var bb = new ByteBuffer(1).fill(0).flip();\r
+ outer.encode(bb);\r
+ test.equal(bb.flip().toString("debug"), "|00");\r
+ var douter = Outer.decode(bb);\r
+ test.ok(douter.inner instanceof Array);\r
+ test.equal(douter.inner.length, 0);\r
+ // Multiple\r
+ outer = new Outer({ inner: [new Inner(1), new Inner(2)] });\r
+ bb = new ByteBuffer(8);\r
+ var size = outer.calculate();\r
+ outer.encode(bb);\r
+ test.strictEqual(bb.offset, size);\r
+ test.equal(bb.flip().toString("debug"), "<0A 02 08 01 0A 02 08 02>");\r
+ douter = Outer.decode(bb);\r
+ test.ok(douter.inner instanceof Array);\r
+ test.equal(douter.inner.length, 2);\r
+ test.equal(douter.inner[0].inner_value, 1);\r
+ test.equal(douter.inner[1].inner_value, 2);\r
+ } catch (e) {\r
+ fail(e);\r
+ }\r
+ test.done();\r
+ },\r
+\r
+ // Packed vs. not packed repeated fields test\r
+ "packed": function(test) {\r
+ try {\r
+ var builder = ProtoBuf.loadProtoFile(__dirname+"/packed.proto");\r
+ var Message = builder.build("Message");\r
+ // Both empty\r
+ var message = new Message();\r
+ var bb = new ByteBuffer(1).fill(0).flip();\r
+ var size = message.calculate();\r
+ message.encode(bb);\r
+ test.strictEqual(bb.offset, size);\r
+ test.equal(bb.flip().toString("debug"), "|00");\r
+ message = Message.decode(bb);\r
+ test.ok(message.a instanceof Array);\r
+ test.equal(message.a.length, 0);\r
+ test.ok(message.b instanceof Array);\r
+ test.equal(message.b.length, 0);\r
+ // Both non-empty\r
+ message = new Message([1,2,3], [1,2,3]);\r
+ size = message.calculate();\r
+ message.encode(bb.resize(11));\r
+ test.strictEqual(bb.offset, size);\r
+ test.equal(bb.flip().toString("debug"), "<0A 03 01 02 03 10 01 10 02 10 03>");\r
+ message = Message.decode(bb);\r
+ test.ok(message.a instanceof Array);\r
+ test.equal(message.a.length, 3);\r
+ test.deepEqual(message.a, [1,2,3]);\r
+ test.ok(message.b instanceof Array);\r
+ test.equal(message.b.length, 3);\r
+ test.deepEqual(message.b, [1,2,3]);\r
+ } catch (e) {\r
+ fail(e);\r
+ }\r
+ test.done();\r
+ },\r
+\r
+ // Legacy groups test\r
+ "groups": function(test) {\r
+ try {\r
+ var builder = ProtoBuf.loadProtoFile(__dirname+"/groups.proto");\r
+ var root = builder.build();\r
+ var Outer = root.Outer;\r
+ var TOuter = builder.ns.getChild("Outer");\r
+ var TInner = TOuter.getChild("MyInner");\r
+ test.ok(TInner instanceof ProtoBuf.Reflect.Message);\r
+ test.strictEqual(TInner.isGroup, true);\r
+ var Tinner = TOuter.getChild("myinner");\r
+ test.ok(Tinner instanceof ProtoBuf.Reflect.Message.Field);\r
+ test.strictEqual(Tinner.id, 2);\r
+ test.deepEqual(Tinner.options, { "deprecated": true });\r
+ var Inner = root.Outer.MyInner;\r
+ var outer = new Outer("a", [new Inner("hello")], "b", new Inner("world"));\r
+ var bb = new ByteBuffer();\r
+ var size = outer.calculate();\r
+ outer.encode(bb);\r
+ test.strictEqual(bb.offset, size);\r
+ bb.flip().compact();\r
+ var wiredMsg = [\r
+ "0A", // 1|010 = id 1, wire type 2 (ldelim)\r
+ "01", // length 1\r
+ "61", // "a"\r
+ "13", // 10|011 = id 2, wire type 3 (start group)\r
+ "1A", // 11|010 = id 3, wire type 2 (ldelim)\r
+ "05", // length 5\r
+ "68 65 6C 6C 6F", // "hello"\r
+ "14", // 10|100 = id 2, wire type 4 (end group)\r
+ "22", // 100|010 = id 4, wire type 2 (ldelim)\r
+ "01", // length 1\r
+ "62", // "b"\r
+ "2B", // 101|011 = id 5, wire type = 3 (start group)\r
+ "1A", // 11|010 = id 3, wire type = 2 (ldelim)\r
+ "05", // length 5\r
+ "77 6F 72 6C 64", // "world"\r
+ "2C" // 101|100 = id 5, wire type = 4 (end group)\r
+ ];\r
+ test.equal(bb.toString("debug"), "<" +wiredMsg.join(" ") + ">");\r
+ var douter = Outer.decode(bb);\r
+ test.strictEqual(douter.before, "a");\r
+ test.strictEqual(douter.myinner.length, 1);\r
+ test.strictEqual(douter.myinner[0].a, "hello");\r
+ test.strictEqual(douter.after, "b");\r
+ bb.offset = 0;\r
+ douter = root.OuterSparse.decode(bb);\r
+ test.strictEqual(bb.offset, bb.limit);\r
+ test.strictEqual(douter.before, "a");\r
+ test.strictEqual(douter.after, "b");\r
+ } catch (e) {\r
+ fail(e);\r
+ }\r
+ test.done();\r
+ },\r
+\r
+ "x64Fixed": function(test) {\r
+ try {\r
+ var builder = ProtoBuf.loadProtoFile(__dirname+"/x64.proto");\r
+ var Test = builder.build("Test");\r
+ var myTest = new Test();\r
+ test.ok(myTest.val instanceof ByteBuffer.Long);\r
+ test.equal(myTest.val.unsigned, false);\r
+ test.equal(myTest.val.toNumber(), -1);\r
+ test.ok(myTest.uval instanceof ByteBuffer.Long);\r
+ test.equal(myTest.uval.unsigned, true);\r
+ test.equal(myTest.uval.toNumber(), 1);\r
+ myTest.setVal(-2);\r
+ myTest.setUval(2);\r
+ var bb = new ByteBuffer(18); // 2x tag + 2x 64bit\r
+ var size = myTest.calculate();\r
+ myTest.encode(bb);\r
+ test.strictEqual(bb.offset, size);\r
+ test.equal(bb.flip().toString("debug"), "<09 FE FF FF FF FF FF FF FF 11 02 00 00 00 00 00 00 00>");\r
+ // ^ wireType=1, id=1 ^ wireType=1, id=2\r
+ myTest = Test.decode(bb);\r
+ test.ok(myTest.val instanceof ByteBuffer.Long);\r
+ test.equal(myTest.val.unsigned, false);\r
+ test.equal(myTest.val.toNumber(), -2);\r
+ test.ok(myTest.uval instanceof ByteBuffer.Long);\r
+ test.equal(myTest.uval.unsigned, true);\r
+ test.equal(myTest.uval.toNumber(), 2);\r
+ } catch (e) {\r
+ fail(e);\r
+ }\r
+ test.done();\r
+ },\r
+\r
+ "x64Varint": function(test) {\r
+ try {\r
+ var builder = ProtoBuf.loadProtoFile(__dirname+"/x64.proto");\r
+ var Test = builder.build("Test2");\r
+ var Test = builder.build("Test2");\r
+ var myTest = new Test();\r
+ test.ok(myTest.val instanceof ByteBuffer.Long);\r
+ test.equal(myTest.val.unsigned, false);\r
+ test.equal(myTest.val.toNumber(), -1);\r
+ test.ok(myTest.uval instanceof ByteBuffer.Long);\r
+ test.equal(myTest.uval.unsigned, true);\r
+ test.equal(myTest.uval.toNumber(), 1);\r
+ test.ok(myTest.sval instanceof ByteBuffer.Long);\r
+ test.equal(myTest.sval.unsigned, false);\r
+ test.equal(myTest.sval.toNumber(), -2);\r
+\r
+ myTest.setVal(-2);\r
+ myTest.setUval(2);\r
+ myTest.setSval(-3);\r
+ var bb = new ByteBuffer(3+10+2); // 3x tag + 1x varint 10byte + 2x varint 1byte\r
+ var size = myTest.calculate();\r
+ myTest.encode(bb);\r
+ test.strictEqual(bb.offset, size);\r
+ test.equal(bb.flip().toString("debug"), "<08 FE FF FF FF FF FF FF FF FF 01 10 02 18 05>");\r
+ // 08: wireType=0, id=1, 18: wireType=0, id=2, ?: wireType=0, id=3\r
+ myTest = Test.decode(bb);\r
+ test.ok(myTest.val instanceof ByteBuffer.Long);\r
+ test.equal(myTest.val.unsigned, false);\r
+ test.equal(myTest.val.toNumber(), -2);\r
+ test.ok(myTest.uval instanceof ByteBuffer.Long);\r
+ test.equal(myTest.uval.unsigned, true);\r
+ test.equal(myTest.uval.toNumber(), 2);\r
+ test.ok(myTest.sval instanceof ByteBuffer.Long);\r
+ test.equal(myTest.sval.unsigned, false);\r
+ test.equal(myTest.sval.toNumber(), -3);\r
+ } catch (e) {\r
+ fail(e);\r
+ }\r
+ test.done();\r
+ },\r
+\r
+ "keywords": function(test) {\r
+ try {\r
+ var builder = ProtoBuf.loadProto("message Reserved { optional string get = 1; }");\r
+ var My = builder.build();\r
+ var myTest = new My.Reserved("a");\r
+ test.doesNotThrow(function() {\r
+ myTest.encode();\r
+ });\r
+ } catch (e) {\r
+ fail(e);\r
+ }\r
+ test.done();\r
+ },\r
+\r
+ "imports": function(test) {\r
+ try {\r
+ var builder = ProtoBuf.loadProtoFile(__dirname+"/imports.proto");\r
+ var root = builder.build();\r
+ test.ok(!!root.Test1);\r
+ test.ok(!!root.Test2);\r
+ test.ok(!!root.My.Test3);\r
+ test.notEqual(root.Test2, root.My.Test2);\r
+ } catch (e) {\r
+ fail(e);\r
+ }\r
+ test.done();\r
+ },\r
+\r
+ "weakImports": function(test) {\r
+ try {\r
+ var builder = ProtoBuf.loadProtoFile(__dirname+"/imports-weak.proto");\r
+ var root = builder.build();\r
+ } catch (e) {\r
+ test.ok(e.message.indexOf("unresolvable type reference") >= 0);\r
+ test.done();\r
+ return;\r
+ }\r
+ var e = new Error("Weak import was imported.");\r
+ fail(e);\r
+ },\r
+\r
+ "importExtensions": function(test) {\r
+ var x = "package x; \\r
+ message Test { \\r
+ extensions 1 to 10; \\r
+ } \\r
+ extend Test { \\r
+ optional int32 first_val = 1; \\r
+ }";\r
+ var y = "package y; \\r
+ extend x.Test { \\r
+ optional int32 second_val = 2; \\r
+ }";\r
+ var builder = ProtoBuf.newBuilder();\r
+ ProtoBuf.loadProto(x, builder);\r
+ ProtoBuf.loadProto(y, builder);\r
+ var Test = builder.build('x.Test');\r
+ var inst = new Test();\r
+ test.strictEqual(inst[".x.first_val"], null);\r
+ test.strictEqual(inst[".y.second_val"], null);\r
+ test.done();\r
+ },\r
+\r
+ "toplevel": function(test) {\r
+ try {\r
+ var builder = ProtoBuf.loadProtoFile(__dirname+"/toplevel.proto");\r
+ var My = builder.build("My");\r
+ test.ok(!!My.MyEnum);\r
+ test.equal(My.MyEnum.ONE, 1);\r
+ test.equal(My.MyEnum.TWO, 2);\r
+ test.ok(!!My.Test);\r
+ var myTest = new My.Test();\r
+ test.equal(myTest.num, My.MyEnum.ONE);\r
+ } catch (e) {\r
+ fail(e);\r
+ }\r
+ test.done();\r
+ },\r
+\r
+ "importsToplevel": function(test) {\r
+ try {\r
+ var builder = ProtoBuf.loadProtoFile(__dirname+"/imports-toplevel.proto");\r
+ var My = builder.build("My");\r
+ test.ok(!!My.MyEnum);\r
+ test.equal(My.MyEnum1.ONE, 1);\r
+ test.equal(My.MyEnum1.TWO, 2);\r
+ test.ok(!!My.Test1);\r
+ var myTest = new My.Test1();\r
+ test.equal(myTest.num, My.MyEnum.ONE);\r
+ test.equal(myTest.num1, My.MyEnum1.ONE);\r
+ } catch (e) {\r
+ fail(e);\r
+ }\r
+ test.done();\r
+ },\r
+\r
+ "importDuplicate": function(test) {\r
+ try {\r
+ var builder = ProtoBuf.loadProtoFile(__dirname+"/import_a.proto");\r
+ test.doesNotThrow(function() {\r
+ ProtoBuf.loadProtoFile(__dirname+"/import_b.proto", builder);\r
+ });\r
+ var root = builder.build();\r
+ test.ok(root.A);\r
+ test.ok(root.B);\r
+ test.ok(root.Common);\r
+ } catch (e) {\r
+ fail(e);\r
+ }\r
+ test.done();\r
+ },\r
+\r
+ "importDuplicateDifferentBuilder": function(test) {\r
+ try {\r
+ var builderA = ProtoBuf.loadProtoFile(__dirname+"/import_a.proto");\r
+ var builderB;\r
+ test.doesNotThrow(function() {\r
+ builderB = ProtoBuf.loadProtoFile(__dirname+"/import_b.proto");\r
+ });\r
+ var rootA = builderA.build();\r
+ var rootB = builderB.build();\r
+ test.ok(rootA.A);\r
+ test.ok(rootB.B);\r
+ test.ok(rootA.Common);\r
+ test.ok(rootB.Common);\r
+ } catch (e) {\r
+ fail(e);\r
+ }\r
+ test.done();\r
+ },\r
+\r
+ "dupimport": function(test) {\r
+ try {\r
+ // Suppress logging result to stdout\r
+ fixture.capture(function() { return false;});\r
+ require(__dirname+"/../cli/pbjs.js").main(["node", "bin/pbjs", __dirname+"/dupimport/main.proto", "--quiet"]);\r
+ fixture.release();\r
+ } catch (e) {\r
+ fixture.release();\r
+ fail(e);\r
+ }\r
+ test.done();\r
+ },\r
+\r
+ "field_name_same_as_package": function(test) {\r
+ try {\r
+ fixture.capture(function() { return false;});\r
+ require(__dirname+"/../cli/pbjs.js").main(["node", "bin/pbjs", __dirname+"/field_name_same_as_package/main.proto", "--quiet"]);\r
+ fixture.release();\r
+ } catch (e) {\r
+ fixture.release();\r
+ fail(e);\r
+ }\r
+ test.done();\r
+ },\r
+\r
+ "importRoot": function(test) {\r
+ try {\r
+ var builder = ProtoBuf.loadProtoFile({\r
+ root: __dirname,\r
+ file: "importRoot/file1.proto"\r
+ });\r
+ var Test = builder.build("Test");\r
+ test.ok(new Test() instanceof ProtoBuf.Builder.Message);\r
+ } catch (e) {\r
+ fail(e);\r
+ }\r
+ test.done();\r
+ },\r
+\r
+ "extend": function(test) {\r
+ try {\r
+ var ast = new ProtoBuf.DotProto.Parser(fs.readFileSync(__dirname+"/extend.proto")).parse();\r
+ test.deepEqual(ast, { package: null,\r
+ messages:\r
+ [ { ref: 'google.protobuf.MessageOptions',\r
+ fields:\r
+ [ { rule: 'optional',\r
+ type: 'int32',\r
+ name: 'foo',\r
+ options: {},\r
+ id: 1001 } ] },\r
+ { name: 'Foo',\r
+ fields: [],\r
+ enums: [],\r
+ messages: [],\r
+ options: {},\r
+ services: [],\r
+ oneofs: {},\r
+ extensions: [ [ 2, 536870911 ] ] },\r
+ { ref: 'Foo',\r
+ fields:\r
+ [ { rule: 'optional',\r
+ type: 'string',\r
+ name: 'bar',\r
+ options: {},\r
+ id: 2 } ] },\r
+ { name: 'Bar',\r
+ fields: [],\r
+ enums: [],\r
+ messages:\r
+ [ { name: 'Foo',\r
+ fields: [],\r
+ enums: [],\r
+ messages: [],\r
+ options: {},\r
+ services: [],\r
+ oneofs: {} },\r
+ { ref: '.Foo',\r
+ fields: [ { rule: 'optional', type: 'Foo', name: 'foo', options: {}, id: 3 } ] } ],\r
+ options: {},\r
+ services: [],\r
+ oneofs: {} } ],\r
+ enums: [],\r
+ imports: [ 'google/protobuf/descriptor.proto' ],\r
+ options: {},\r
+ services: [] }\r
+ );\r
+\r
+ var builder = ProtoBuf.loadProtoFile(__dirname+"/extend.proto");\r
+ var TFoo = builder.lookup(".Foo"),\r
+ TBar = builder.lookup(".Bar"),\r
+ TBarFoo = builder.lookup(".Bar.Foo"),\r
+ fields = TFoo.getChildren(ProtoBuf.Reflect.Message.Field);\r
+ test.strictEqual(fields.length, 2);\r
+ test.strictEqual(fields[0].name, ".bar");\r
+ test.strictEqual(fields[0].id, 2);\r
+ test.strictEqual(fields[1].name, ".Bar.foo");\r
+ test.strictEqual(fields[1].id, 3);\r
+ test.deepEqual(TFoo.extensions, [[2, ProtoBuf.ID_MAX]]); // explicitly defined\r
+ test.strictEqual(TBar.extensions, undefined); // none defined\r
+ test.deepEqual(TBar.getChild("foo"), { builder: builder, parent: TBar, name: "foo", field: TFoo.getChild('.Bar.foo') });\r
+ test.strictEqual(TBar.getChildren(ProtoBuf.Reflect.Message.Field).length, 0);\r
+ var root = builder.build();\r
+ test.strictEqual(TFoo.getChild(".Bar.foo").resolvedType, TBarFoo); // .Bar.Foo, not .Foo\r
+ var foo = new root.Foo(),\r
+ bar = new root.Bar();\r
+ foo['.bar'] = "123";\r
+ foo['.Bar.foo'] = bar;\r
+ test.equal(foo.encode().compact().toString("debug"), "<12 03 31 32 33 1A 00>");\r
+ } catch (e) {\r
+ fail(e);\r
+ }\r
+ test.done();\r
+ },\r
+\r
+ // Custom options on all levels\r
+ // victorr (https://github.com/victorr)\r
+ "customOptions": function(test) {\r
+ try {\r
+ var parser = new ProtoBuf.DotProto.Parser(ProtoBuf.Util.fetch(__dirname+"/custom-options.proto"));\r
+ var root = parser.parse();\r
+ test.equal(root["options"]["(my_file_option)"], "Hello world!");\r
+ test.equal(root["messages"][7]["options"]["(my_message_option)"], 1234);\r
+ test.equal(root["messages"][7]["fields"][0]["options"]["(my_field_option)"], 4.5);\r
+ // test.equal(root["services"]["MyService"]["options"]["my_service_option"], "FOO");\r
+ // TODO: add tests for my_enum_option, my_enum_value_option\r
+ } catch (e) {\r
+ fail(e);\r
+ }\r
+ test.done();\r
+ },\r
+\r
+ "oneofs": function(test) {\r
+ try {\r
+ var builder = ProtoBuf.loadProtoFile(__dirname+"/oneof.proto"),\r
+ MyOneOf = builder.build("MyOneOf"),\r
+ TOneOf = builder.lookup(".MyOneOf");\r
+ test.ok(TOneOf.getChild("my_oneof"));\r
+ var myOneOf = new MyOneOf();\r
+ test.strictEqual(myOneOf.my_oneof, null);\r
+ myOneOf.set("id", 1);\r
+ test.strictEqual(myOneOf.my_oneof, "id");\r
+ myOneOf.set("name", "me");\r
+ test.strictEqual(myOneOf.my_oneof, "name");\r
+ test.strictEqual(myOneOf.id, null);\r
+ var bb = myOneOf.encode().compact();\r
+ test.strictEqual(bb.toString("debug"), "<12 02 6D 65>"); // id 2, wt 2, len 2\r
+ myOneOf = MyOneOf.decode(bb);\r
+ test.strictEqual(myOneOf.my_oneof, "name");\r
+ test.strictEqual(myOneOf.name, "me");\r
+ test.strictEqual(myOneOf.id, null);\r
+ } catch (e) {\r
+ fail(e);\r
+ }\r
+ test.done();\r
+ },\r
+\r
+ "services": function(test) {\r
+ try {\r
+ var parser = new ProtoBuf.DotProto.Parser(ProtoBuf.Util.fetch(__dirname+"/custom-options.proto"));\r
+ var root = parser.parse();\r
+ test.deepEqual(root["services"], [{\r
+ "name": "MyService",\r
+ "rpc": {\r
+ "MyMethod": {\r
+ "request": "RequestType",\r
+ "response": "ResponseType",\r
+ "request_stream": false,\r
+ "response_stream": false,\r
+ "options": {\r
+ "(my_method_option).foo": 567,\r
+ "(my_method_option).bar": "Some string"\r
+ }\r
+ }\r
+ },\r
+ "options": {\r
+ "(my_service_option)": "FOO"\r
+ }\r
+ }]);\r
+\r
+ var builder = ProtoBuf.loadProtoFile(__dirname+"/custom-options.proto");\r
+ var root = builder.build(),\r
+ MyService = root.MyService,\r
+ RequestType = root.RequestType,\r
+ ResponseType = root.ResponseType,\r
+ called = false;\r
+\r
+ test.deepEqual(MyService.$options, {\r
+ "(my_service_option)": "FOO"\r
+ });\r
+ test.deepEqual(MyService.MyMethod.$options, {\r
+ "(my_method_option).foo": 567,\r
+ "(my_method_option).bar": "Some string"\r
+ });\r
+\r
+ // Provide the service with your actual RPC implementation based on whatever framework you like most.\r
+ var myService = new MyService(function(method, req, callback) {\r
+ test.strictEqual(method, ".MyService.MyMethod");\r
+ test.ok(req instanceof RequestType);\r
+ called = true;\r
+\r
+ // In this case we just return no error and our pre-built response. This must be properly async!\r
+ setTimeout(callback.bind(this, null, (new ResponseType()).encode() /* as raw bytes for debugging */ ));\r
+ });\r
+\r
+ test.deepEqual(myService.$options, MyService.$options);\r
+ test.deepEqual(myService.MyMethod.$options, MyService.MyMethod.$options);\r
+\r
+ // Call the service with your request message and provide a callback. This will call your actual service\r
+ // implementation to perform the request and gather a response before calling the callback. If the\r
+ // request or response type is invalid i.e. not an instance of RequestType or ResponseType, your\r
+ // implementation will not be called as ProtoBuf.js handles this case internally and directly hands the\r
+ // error to your callback below.\r
+ myService.MyMethod(new RequestType(), function(err, res) {\r
+ // We get: err = null, res = our prebuilt response. And that's it.\r
+ if (err !== null) {\r
+ fail(err);\r
+ }\r
+ test.strictEqual(called, true);\r
+ test.ok(res instanceof ResponseType);\r
+ test.done();\r
+ });\r
+ myService.MyMethod(new RequestType().encode(), function(err, res) {\r
+ // We get: err = null, res = our prebuilt response. And that's it.\r
+ if (err !== null) {\r
+ fail(err);\r
+ }\r
+ test.strictEqual(called, true);\r
+ test.ok(res instanceof ResponseType);\r
+ test.done();\r
+ });\r
+ } catch (e) {\r
+ fail(e);\r
+ }\r
+ },\r
+\r
+ // Properly ignore "syntax" and "extensions" keywords\r
+ // The corresponding .proto file has been removed upon request\r
+ /* "gtfs-realtime": function(test) {\r
+ try {\r
+ test.doesNotThrow(function() {\r
+ ProtoBuf.loadProtoFile(__dirname+"/gtfs-realtime.proto");\r
+ });\r
+ } catch (e) {\r
+ fail(e);\r
+ }\r
+ test.done();\r
+ }, */\r
+\r
+ "delimited": function(test) {\r
+ try {\r
+ var builder = ProtoBuf.loadProto("message Position { required int32 x = 1; required int32 y = 2; }");\r
+ var Position = builder.build("Position");\r
+ var bb = new ByteBuffer();\r
+ for (var i=0; i<2; i++) {\r
+ var position = new Position(10,10);\r
+ position.encodeDelimited(bb);\r
+ }\r
+ bb.flip();\r
+ for (i=0; i<2; i++) {\r
+ position = Position.decodeDelimited(bb);\r
+ test.strictEqual(position.x, 10);\r
+ test.strictEqual(position.y, 10);\r
+ }\r
+ } catch (e) {\r
+ fail(e);\r
+ }\r
+ test.done();\r
+ },\r
+\r
+ "stringify": function(test) {\r
+ try {\r
+ var builder = ProtoBuf.loadProto("message Position { required int32 x = 1; required int64 y = 2; }");\r
+ var Position = builder.build("Position");\r
+ var position = new Position(1, ProtoBuf.Long.fromNumber(2));\r
+ var json = JSON.stringify(position);\r
+ test.strictEqual(json, '{"x":1,"y":{"low":2,"high":0,"unsigned":false}}');\r
+ position = new Position(JSON.parse(json));\r
+ test.strictEqual(position.x, 1);\r
+ test.ok(position.y instanceof ProtoBuf.Long);\r
+ test.deepEqual(position.y, {"low":2,"high":0,"unsigned":false});\r
+ // Also test if this encodes and decodes properly\r
+ position = Position.decode(position.encode());\r
+ test.ok(position.y instanceof ProtoBuf.Long);\r
+ test.deepEqual(position.y, {"low": 2, "high": 0, "unsigned": false });\r
+ } catch (e) {\r
+ fail(e);\r
+ }\r
+ test.done();\r
+ },\r
+\r
+ "fields": function(test) {\r
+ try {\r
+ var builder = ProtoBuf.loadProtoFile(__dirname+"/optional.proto");\r
+ var Test1 = builder.build("Test1");\r
+ var test1 = new Test1();\r
+ test.strictEqual(test1.a, null);\r
+ test.deepEqual(Object.keys(test1), ['a','b']);\r
+ var bb = test1.encode();\r
+ test1 = Test1.decode(bb);\r
+ test.strictEqual(test1.a, null);\r
+ test.deepEqual(Object.keys(test1), ['a','b']);\r
+ } catch (e) {\r
+ fail(e);\r
+ }\r
+ test.done();\r
+ },\r
+\r
+ "fieldsToCamelCase": function(test) {\r
+ try {\r
+ ProtoBuf.convertFieldsToCamelCase = true;\r
+ var builder = ProtoBuf.loadProtoFile(__dirname+"/camelcase.proto");\r
+ var Test = builder.build("Test"),\r
+ TTest = builder.lookup("Test");\r
+ var msg = new Test();\r
+\r
+ // Reverted collision on 1st\r
+ test.strictEqual(msg.some_field, null);\r
+ test.strictEqual(msg.someField, null);\r
+ test.equal(TTest.getChild("some_field").id, 1);\r
+ test.equal(TTest.getChild("someField").id, 2);\r
+\r
+\r
+ // Reverted collision on 2nd\r
+ test.strictEqual(msg.aField, null);\r
+ test.strictEqual(msg.a_field, null);\r
+ test.equal(TTest.getChild("aField").id, 3);\r
+ test.equal(TTest.getChild("a_field").id, 4);\r
+\r
+ // No collision\r
+ test.strictEqual(msg.itsAField, null);\r
+ test.equal(TTest.getChild("itsAField").id, 5);\r
+\r
+ test.ok(typeof msg.set_its_a_field === "function");\r
+ test.ok(typeof msg.setItsAField === "function");\r
+\r
+ ProtoBuf.convertFieldsToCamelCase = false;\r
+ } catch (e) {\r
+ ProtoBuf.convertFieldsToCamelCase = false;\r
+ fail(e);\r
+ }\r
+ test.done();\r
+ },\r
+\r
+ "setarray": function(test) {\r
+ try {\r
+ var builder = ProtoBuf.loadProtoFile(__dirname+"/setarray.proto");\r
+ var root = builder.build(),\r
+ Outer = root.Outer,\r
+ Inner = root.Inner,\r
+ inners = [];\r
+\r
+ // Array of repeated messages\r
+ inners.push(new Inner("a"), new Inner("b"), new Inner("c"));\r
+ var outer = new Outer();\r
+ outer.setInners(inners);\r
+ test.deepEqual(outer.inners, inners);\r
+\r
+ // Array of repeated message objects\r
+ inners = [];\r
+ inners.push({ str: 'a' }, { str: 'b' }, { str: 'c' });\r
+ outer.setInners(inners); // Converts\r
+ test.ok(outer.inners[0] instanceof Inner);\r
+ test.deepEqual(outer.inners, inners);\r
+ } catch (e) {\r
+ fail(e);\r
+ }\r
+ test.done();\r
+ },\r
+\r
+\r
+ // Make sure that our example at https://github.com/dcodeIO/ProtoBuf.js/wiki is not nonsense\r
+ "pingexample": function(test) {\r
+ try {\r
+ var builder = ProtoBuf.loadProtoFile(__dirname+"/PingExample.proto");\r
+ var Message = builder.build("Message");\r
+ var msg = new Message();\r
+ msg.ping = new Message.Ping(123456789);\r
+ var bb = msg.encode();\r
+ test.strictEqual(bb.limit, 7);\r
+ msg = Message.decode(bb);\r
+ test.ok(msg.ping);\r
+ test.notOk(msg.pong);\r
+ test.strictEqual(msg.ping.time, 123456789);\r
+ } catch (e) {\r
+ fail(e);\r
+ }\r
+ test.done();\r
+ },\r
+\r
+ "negInt32": function(test) {\r
+ try {\r
+ var builder = ProtoBuf.loadProto("message Test { required int32 value = 2; }");\r
+ var Test = builder.build("Test");\r
+ var t = new Test(-1);\r
+ var size = t.calculate();\r
+ var bb = t.encode(); // flips\r
+ test.strictEqual(bb.remaining(), size);\r
+ test.strictEqual(bb.toBase64(), "EP///////////wE=");\r
+ t = Test.decode(bb);\r
+ test.strictEqual(t.value, -1);\r
+ } catch (e) {\r
+ fail(e);\r
+ }\r
+ test.done();\r
+ },\r
+\r
+ "negEnumId": function(test) {\r
+ try {\r
+ test.doesNotThrow(function() {\r
+ var builder = ProtoBuf.loadProtoFile(__dirname+"/negid.proto");\r
+ var Test = builder.build("Test");\r
+ test.strictEqual(Test.LobbyType.INVALID, -1);\r
+ var t = new Test(Test.LobbyType.INVALID);\r
+ test.strictEqual(t.type, -1);\r
+ var size = t.calculate();\r
+ var bb = t.encode(); // flips\r
+ test.strictEqual(bb.remaining(), size);\r
+ t = Test.decode(bb);\r
+ test.strictEqual(t.type, -1);\r
+ });\r
+ } catch (e) {\r
+ fail(e);\r
+ }\r
+ test.done();\r
+ },\r
+\r
+ "base64": function(test) {\r
+ try {\r
+ var Message = ProtoBuf.loadProto("message Message { required string s = 1; }").build("Message");\r
+ var msg = new Message("ProtoBuf.js");\r
+ var b64 = msg.toBase64();\r
+ test.strictEqual(b64, "CgtQcm90b0J1Zi5qcw==");\r
+ var msg2 = Message.decode64(b64);\r
+ test.deepEqual(msg, msg2);\r
+ msg2 = Message.decode(b64, "base64");\r
+ test.deepEqual(msg, msg2);\r
+ } catch (e) {\r
+ fail(e);\r
+ }\r
+ test.done();\r
+ },\r
+\r
+ "hex": function(test) {\r
+ try {\r
+ var Message = ProtoBuf.loadProto("message Message { required string s = 1; }").build("Message");\r
+ var msg = new Message("ProtoBuf.js");\r
+ var hex = msg.toHex();\r
+ test.strictEqual(hex, "0a0b50726f746f4275662e6a73");\r
+ var msg2 = Message.decodeHex(hex);\r
+ test.deepEqual(msg, msg2);\r
+ msg2 = Message.decode(hex, "hex");\r
+ test.deepEqual(msg, msg2);\r
+ } catch (e) {\r
+ fail(e);\r
+ }\r
+ test.done();\r
+ },\r
+\r
+ "forwardComp": function(test) {\r
+ try {\r
+ var Message = ProtoBuf.loadProto("message Message { required int32 a = 1; required string b = 2; required float c = 3; }").build("Message");\r
+ var msg = new Message(123, "abc", 0.123);\r
+ var bb = msg.encode();\r
+ Message = ProtoBuf.loadProto("message Message {}").build("Message");\r
+ test.doesNotThrow(function() {\r
+ Message.decode(bb);\r
+ });\r
+ test.strictEqual(bb.offset, bb.limit);\r
+ } catch (e) {\r
+ fail(e);\r
+ }\r
+ test.done();\r
+ },\r
+\r
+ "tokenizerLine": function(test) {\r
+ try {\r
+ var parser = new ProtoBuf.DotProto.Parser("package test;\n\nmessage Message {\n\trequired string invalid = 1;}ERROR\n"),\r
+ ast = null, err = null;\r
+ try {\r
+ ast = parser.parse();\r
+ } catch (caught) {\r
+ err = caught;\r
+ }\r
+ test.ok(err);\r
+ test.notOk(ast);\r
+ test.ok(err.message.indexOf("line 4:") >= 0);\r
+ } catch (e) {\r
+ fail(e);\r
+ }\r
+ test.done();\r
+ },\r
+\r
+ "excludeFields": function(test) {\r
+ try {\r
+ var builder = ProtoBuf.loadProto("message A { required int32 i = 1; } message B { required A A = 1; }");\r
+ builder.build();\r
+ } catch (e) {\r
+ fail(e);\r
+ }\r
+ test.done();\r
+ },\r
+\r
+ "proto2jsExtend": function(test) {\r
+ try {\r
+ var builder = ProtoBuf.loadJsonFile(__dirname+"/proto2js/Bar.json");\r
+ builder.build();\r
+ } catch (e) {\r
+ fail(e);\r
+ }\r
+ test.done();\r
+ },\r
+\r
+ "emptyMessage": function(test) {\r
+ try {\r
+ var builder = ProtoBuf.loadProto("message EmptyMessage {}"),\r
+ EmptyMessage = builder.build("EmptyMessage");\r
+\r
+ var msg = new EmptyMessage(),\r
+ ab = msg.toArrayBuffer();\r
+ test.strictEqual(ab.byteLength, 0);\r
+ } catch (e) {\r
+ fail(e);\r
+ }\r
+ test.done();\r
+ },\r
+\r
+ "toRaw": function(test) {\r
+ try {\r
+ var builder = ProtoBuf.loadProto("message MyMessage { required int32 a = 1; required int32 b = 2; required bytes c = 3; }"),\r
+ MyMessage = builder.build("MyMessage");\r
+ var raw = { a: 1, b: 2, c: "YWJj" },\r
+ myMessage = new MyMessage(raw);\r
+ test.deepEqual(myMessage.c.toBase64(), raw.c);\r
+ test.deepEqual(myMessage.toRaw(true), raw);\r
+ } catch (e) {\r
+ fail(e);\r
+ }\r
+ test.done();\r
+ },\r
+\r
+ "singleQuotedString": function(test) {\r
+ try{\r
+ var builder = ProtoBuf.loadProtoFile(__dirname+"/string_single_quote.proto");\r
+ var TestSingleQuoteString = builder.build("TestSingleQuoteString");\r
+ test.ok(typeof TestSingleQuoteString == 'function');\r
+ } catch (e) {\r
+ fail(e);\r
+ }\r
+ test.done();\r
+ },\r
+\r
+ "importDuplicateSingleQuote": function(test) {\r
+ try {\r
+ var builder = ProtoBuf.loadProtoFile(__dirname+"/import_a_single_quote.proto");\r
+ test.doesNotThrow(function() {\r
+ ProtoBuf.loadProtoFile(__dirname+"/import_b.proto", builder);\r
+ });\r
+ var root = builder.build();\r
+ test.ok(root.A);\r
+ test.ok(root.B);\r
+ test.ok(root.Common);\r
+ } catch (e) {\r
+ fail(e);\r
+ }\r
+ test.done();\r
+ },\r
+\r
+ "importStringSuccessively": function(test) {\r
+ try {\r
+ var proto1 = "message A { required string a = 1; };";\r
+ var proto2 = "import \"proto1.proto\"; message B { required A a = 1; };";\r
+ var builder = ProtoBuf.loadProto(proto1, "proto1.proto");\r
+ ProtoBuf.loadProto(proto2, builder, "proto2.proto");\r
+ var root = builder.build();\r
+ test.ok(root.A);\r
+ test.ok(root.B);\r
+ } catch (e) {\r
+ fail(e);\r
+ }\r
+ test.done();\r
+ },\r
+\r
+ "multilineString": function(test) {\r
+ try {\r
+ var proto = "message TestMessage { required string test = 1 [default = \"1\" \"2\"\n\"3\"];}";\r
+ var builder = ProtoBuf.loadProto(proto, "multilineString.proto");\r
+ var TestMessage = builder.build("TestMessage"),\r
+ testMessage = new TestMessage();\r
+ test.strictEqual(testMessage.test, "123");\r
+ test.done();\r
+ } catch (e) {\r
+ fail(e);\r
+ }\r
+ test.done();\r
+ },\r
+\r
+ "packable": function(test) {\r
+ try {\r
+ var proto = 'message Inner { required int32 id=2; }\nmessage Outer { repeated Inner inner = 1 [packed=true]; }';\r
+ var builder = ProtoBuf.loadProto(proto);\r
+ var root = builder.build();\r
+ var inner = new root.Inner(1),\r
+ outer = new root.Outer(inner);\r
+ var bb = outer.encode().compact();\r
+ test.strictEqual(bb.toDebug(), "<0A 02 10 01>");\r
+ // 0A: wt 2, id 1\r
+ // 02: len 2\r
+ // 10: wt 0, id 2\r
+ // 01: 1\r
+ var outer2 = root.Outer.decode(bb);\r
+ test.strictEqual(outer2.inner.id, 1);\r
+ } catch (e) {\r
+ fail(e);\r
+ }\r
+ test.done();\r
+ },\r
+\r
+ "$type": function(test) {\r
+ var builder = ProtoBuf.loadProto("message Test {}");\r
+ var Test = builder.build("Test"),\r
+ TTest = builder.lookup("Test");\r
+ test.strictEqual(new Test().$type, TTest);\r
+ test.done();\r
+ },\r
+\r
+ "descriptor": function(test) {\r
+ try {\r
+ var proto = 'import "./google/protobuf/descriptor.proto";';\r
+ var builder = ProtoBuf.loadProto(proto, "tests/proto.proto");\r
+ var root = builder.build("google.protobuf");\r
+ test.ok(root.FileDescriptorSet);\r
+ } catch (e) {\r
+ fail(e);\r
+ }\r
+ test.done();\r
+ },\r
+\r
+ "mismatchedNesting": function(test) {\r
+ try {\r
+ var proto = "message Child { optional uint32 foo = 1; } message FakeChild { optional uint32 foo = 1; } message Parent { optional Child child = 1; }";\r
+ var builder = ProtoBuf.loadProto(proto, "tests/mismatchedNesting.proto");\r
+ var root = builder.build();\r
+ var foo = new root.Parent({ child: new root.FakeChild({ foo: 1 })});\r
+ } catch (e) {\r
+ fail(e);\r
+ }\r
+ test.done();\r
+ },\r
+\r
+ /* "mismatchedType": function(test) {\r
+ try {\r
+ var proto = "message Test1 { optional string foo = 1; }";\r
+ proto += "message Test2 { optional int32 foo = 1; }";\r
+ var builder = ProtoBuf.loadProto(proto, "tests/mistmatchedType.proto");\r
+ var root = builder.build();\r
+ var test1 = new root.Test1({ foo: 'bar' });\r
+ var test2 = root.Test2.decode(test1.encode());\r
+ } catch (e) {\r
+ fail(e);\r
+ }\r
+ test.done();\r
+ }, */\r
+\r
+ "builderOptions": function(test) {\r
+ try {\r
+ var proto = "message Foo { optional uint32 foo_bar = 1; }";\r
+ var builder = ProtoBuf.newBuilder({\r
+ convertFieldsToCamelCase: true\r
+ });\r
+ ProtoBuf.loadProto(proto, builder, "tests/builderOptions.proto");\r
+ var Foo = builder.build("Foo");\r
+ test.strictEqual(ProtoBuf.convertFieldsToCamelCase, false);\r
+ test.strictEqual(builder.options.convertFieldsToCamelCase, true);\r
+ var foo = new Foo();\r
+ test.ok(typeof foo.fooBar !== 'undefined');\r
+ test.ok(typeof foo.foo_bar === 'undefined');\r
+ test.done();\r
+ } catch (e) {\r
+ fail(e);\r
+ }\r
+ test.done();\r
+ },\r
+\r
+ "proto3": function(test) {\r
+ try {\r
+ var builder = ProtoBuf.loadProtoFile(__dirname+"/proto3.proto");\r
+ test.doesNotThrow(function() {\r
+ ProtoBuf.loadProtoFile(__dirname+"/proto3.proto", builder);\r
+ });\r
+ var root = builder.build();\r
+ test.ok(root.test.Foo.$type.syntax === 'proto3');\r
+ } catch (e) {\r
+ fail(e);\r
+ }\r
+ test.done();\r
+ },\r
+\r
+ // FIXME: This test relied on some bloated builder functionality that has been removed.\r
+ // Is it even mandatory to strictly disallow proto2/3 mixing, even if that would be illegal in the official\r
+ // implementation?\r
+ /* "proto3DisallowedFeatures": function(test) {\r
+ try {\r
+ // Required field\r
+ var proto = "syntax = \"proto3\"; message Foo { required int32 field = 1; }";\r
+ var builder = ProtoBuf.newBuilder();\r
+ ProtoBuf.loadProto(proto, builder, "tests/proto3DisallowedFeatures.proto");\r
+ test.ok(false); // ^ should throw\r
+ } catch (e) {\r
+ test.ok(/^Not a valid definition/.test(e.message));\r
+ }\r
+\r
+ try {\r
+ // Field with default value\r
+ var proto = "syntax = \"proto3\"; message Foo { int32 field = 1 [default=42]; }";\r
+ var builder = ProtoBuf.newBuilder();\r
+ ProtoBuf.loadProto(proto, builder, "tests/proto3DisallowedFeatures.proto");\r
+ test.ok(false); // ^ should throw\r
+ } catch (e) {\r
+ test.ok(/^Not a valid definition/.test(e.message));\r
+ }\r
+\r
+ try {\r
+ // Message with extension range\r
+ var proto = "syntax = \"proto3\"; message Foo { extensions 100 to max; } ";\r
+ var builder = ProtoBuf.newBuilder();\r
+ ProtoBuf.loadProto(proto, builder, "tests/proto3DisallowedFeatures.proto");\r
+ test.ok(false); // ^ should throw\r
+ } catch (e) {\r
+ test.ok(/^Not a valid definition/.test(e.message));\r
+ }\r
+\r
+ try {\r
+ // Message with extension\r
+ var proto = "syntax = \"proto3\"; message Foo { extensions 100 to max; } " +\r
+ "message Bar { extend Foo { optional Bar bar = 100; } }";\r
+ var builder = ProtoBuf.newBuilder();\r
+ ProtoBuf.loadProto(proto, builder, "tests/proto3DisallowedFeatures.proto");\r
+ test.ok(false); // ^ should throw\r
+ } catch (e) {\r
+ test.ok(/^Not a valid definition/.test(e.message));\r
+ }\r
+\r
+ try {\r
+ // Enum with non-zero first entry.\r
+ var proto = "syntax = \"proto3\"; enum E { A = 1; B = 2; }";\r
+ var builder = ProtoBuf.newBuilder();\r
+ ProtoBuf.loadProto(proto, builder, "tests/proto3DisallowedFeatures.proto");\r
+ test.ok(false); // ^ should throw\r
+ } catch (e) {\r
+ test.ok(/^Not a valid definition/.test(e.message));\r
+ }\r
+\r
+ try {\r
+ // Proto3 message referring to proto2 enum.\r
+ var proto2 = "syntax = \"proto2\"; enum E { A = 1; B = 2; }";\r
+ var proto3 = "syntax = \"proto3\"; message Test { E enum_field = 1; }";\r
+ var builder = ProtoBuf.newBuilder();\r
+ ProtoBuf.loadProto(proto2, builder, "tests/proto3DisallowedFeatures1.proto");\r
+ ProtoBuf.loadProto(proto3, builder, "tests/proto3DisallowedFeatures3.proto");\r
+ test.ok(false); // ^ should throw\r
+ } catch (e) {\r
+ test.ok(/^Proto3 message refers to proto2 enum/.test(e.message));\r
+ }\r
+\r
+ test.done();\r
+ }, */\r
+\r
+ "proto3FieldPresence": function(test) {\r
+ var proto =\r
+ "syntax = \"proto3\";\n" +\r
+ "message Test {\n" +\r
+ " int32 field_int32 = 1;\n" +\r
+ " int64 field_int64 = 2;\n" +\r
+ " string field_str = 3;\n" +\r
+ " bytes field_bytes = 4;\n" +\r
+ " Test field_msg = 5;\n" +\r
+ " Enum field_enum = 6;\n" +\r
+ " repeated int32 rpt_int32 = 11;\n" +\r
+ " repeated int64 rpt_int64 = 12;\n" +\r
+ " repeated string rpt_str = 13;\n" +\r
+ " repeated bytes rpt_bytes = 14;\n" +\r
+ " repeated Test rpt_msg = 15;\n" +\r
+ " repeated Enum rpt_enum = 16;\n" +\r
+ " oneof oneof_type { bool oneof_bool = 17; };\n" +\r
+ "}\n" +\r
+ "enum Enum { Default = 0; A = 1; B = 2; }\n";\r
+ var builder = ProtoBuf.newBuilder();\r
+ ProtoBuf.loadProto(proto, builder, "test/proto3FieldPresence.proto");\r
+ var Test = builder.build('Test'),\r
+ Enum = builder.build('Enum');\r
+\r
+ var testMsg = new Test();\r
+ test.strictEqual(testMsg.field_int32, 0);\r
+ test.strictEqual(testMsg.field_int64.low, 0);\r
+ test.strictEqual(testMsg.field_int64.high, 0);\r
+ test.strictEqual(testMsg.field_str, "");\r
+ test.strictEqual(testMsg.field_msg, null);\r
+ test.ok(testMsg.field_bytes instanceof ByteBuffer);\r
+ test.strictEqual(testMsg.field_bytes.remaining(), 0);\r
+ test.strictEqual(testMsg.rpt_int32.length, 0);\r
+ test.strictEqual(testMsg.oneof_type, null);\r
+ test.strictEqual(testMsg.oneof_bool, false);\r
+\r
+ // No fields should go on the wire, even though they're set\r
+ var encoded = testMsg.encode();\r
+ test.strictEqual(encoded.remaining(), 0);\r
+ testMsg.field_int32 = 42;\r
+ encoded = testMsg.encode();\r
+ test.strictEqual(encoded.remaining(), 2);\r
+ testMsg.field_int32 = 0;\r
+ encoded = testMsg.encode();\r
+ test.strictEqual(encoded.remaining(), 0);\r
+\r
+ // Enum fields should be able to carry arbitrary values.\r
+ testMsg.field_enum = 42;\r
+ test.strictEqual(testMsg.field_enum, 42);\r
+ encoded = testMsg.encode();\r
+ testMsg = Test.decode(encoded);\r
+ test.strictEqual(testMsg.field_enum, 42);\r
+\r
+ // Explicitly set fields that are part of an oneof should\r
+ // be encoded even if set to their default value\r
+ testMsg = new Test();\r
+ testMsg.set("oneof_bool", false);\r
+ test.strictEqual(testMsg.oneof_type, "oneof_bool");\r
+ encoded = testMsg.encode().compact();\r
+ test.strictEqual(encoded.toString("debug"), "<88 01 00>"); // 17|varint (0term) + varint 0\r
+ var decoded = Test.decode(encoded);\r
+ test.strictEqual(decoded.oneof_type, "oneof_bool");\r
+ test.strictEqual(decoded.oneof_bool, false);\r
+\r
+ test.done();\r
+ },\r
+\r
+ "mapContainer": function(test) {\r
+ var proto =\r
+ "message Test {\n" +\r
+ " map<string, int32> map_string_int32 = 1;\n" +\r
+ " map<string, int64> map_string_int64 = 2;\n" +\r
+ " map<string, string> map_string_string = 3;\n" +\r
+ " map<string, Test> map_string_msg = 4;\n" +\r
+ " map<string, Enum> map_string_enum = 5;\n" +\r
+ " map<int32, string> map_int32_string = 6;\n" +\r
+ " map<int64, string> map_int64_string = 7;\n" +\r
+ " map<bool, string> map_bool_string = 9;\n" +\r
+ "}\n" +\r
+ "enum Enum { Default = 0; A = 1; B = 2; }\n";\r
+ var builder = ProtoBuf.newBuilder();\r
+ ProtoBuf.loadProto(proto, builder, "test/mapContainer.proto");\r
+\r
+ var map_string_int32 =\r
+ new ProtoBuf.Map(builder.lookup("Test.map_string_int32"));\r
+ test.strictEqual(map_string_int32.size, 0);\r
+ test.strictEqual(map_string_int32.has("asdf"), false);\r
+ test.strictEqual(map_string_int32.get("asdf"), undefined);\r
+ map_string_int32.set("asdf", 42);\r
+ test.strictEqual(map_string_int32.has("asdf"), true);\r
+ test.strictEqual(map_string_int32.get("asdf"), 42);\r
+\r
+ var it = map_string_int32.keys();\r
+ var itVal = it.next();\r
+ test.strictEqual(itVal.done, false);\r
+ test.strictEqual(itVal.value, "asdf");\r
+ itVal = it.next();\r
+ test.strictEqual(itVal.done, true);\r
+\r
+ it = map_string_int32.values();\r
+ itVal = it.next();\r
+ test.strictEqual(itVal.done, false);\r
+ test.strictEqual(itVal.value, 42);\r
+ itVal = it.next();\r
+ test.strictEqual(itVal.done, true);\r
+\r
+ it = map_string_int32.entries();\r
+ itVal = it.next();\r
+ test.strictEqual(itVal.done, false);\r
+ test.deepEqual(itVal.value, ["asdf", 42]);\r
+ itVal = it.next();\r
+ test.strictEqual(itVal.done, true);\r
+\r
+ map_string_int32.set("jkl;", 84);\r
+ test.strictEqual(map_string_int32.has("jkl;"), true);\r
+ test.strictEqual(map_string_int32.has("asdf"), true);\r
+ test.strictEqual(map_string_int32.size, 2);\r
+ map_string_int32.delete("jkl;");\r
+ test.strictEqual(map_string_int32.has("jkl;"), false);\r
+ test.strictEqual(map_string_int32.get("jkl;"), undefined);\r
+ test.strictEqual(map_string_int32.size, 1);\r
+\r
+ map_string_int32.clear();\r
+ test.strictEqual(map_string_int32.size, 0);\r
+\r
+ try {\r
+ map_string_int32.set("asdf", 42.1);\r
+ test.ok(false); // ^ should throw\r
+ } catch(e) {\r
+ test.ok(e.message.match(/not an integer/));\r
+ }\r
+\r
+ try {\r
+ map_string_int32.set(42, 42);\r
+ test.ok(false); // ^ should throw\r
+ } catch(e) {\r
+ test.ok(e.message.match(/not a string/));\r
+ }\r
+\r
+ // Test various key types to ensure that value->string->value\r
+ // conversion works.\r
+ var map_int32_string =\r
+ new ProtoBuf.Map(builder.lookup("Test.map_int32_string"));\r
+ test.strictEqual(map_int32_string.size, 0);\r
+ map_int32_string.set(12345678, "asdf");\r
+ test.strictEqual(map_int32_string.size, 1);\r
+ test.strictEqual(map_int32_string.has(12345678), true);\r
+ test.strictEqual(map_int32_string.get(12345678), "asdf");\r
+\r
+ var map_int64_string =\r
+ new ProtoBuf.Map(builder.lookup("Test.map_int64_string"));\r
+ test.strictEqual(map_int64_string.size, 0);\r
+ map_int64_string.set("9223372036854775807", "asdf");\r
+ test.strictEqual(map_int64_string.size, 1);\r
+ test.strictEqual(map_int64_string.has("9223372036854775807"), true);\r
+ test.strictEqual(map_int64_string.get("9223372036854775807"), "asdf");\r
+\r
+ // Ensure that initialization from a raw object works.\r
+ var map_int32_string =\r
+ new ProtoBuf.Map(builder.lookup("Test.map_int32_string"),\r
+ { 42: "asdf" });\r
+ test.strictEqual(map_int32_string.size, 1);\r
+ test.strictEqual(map_int32_string.keys().next().value, 42);\r
+\r
+ var map_int64_string =\r
+ new ProtoBuf.Map(builder.lookup("Test.map_int64_string"),\r
+ { "9223372036854775807": "asdf" });\r
+ test.strictEqual(map_int64_string.size, 1);\r
+ var i64 = map_int64_string.keys().next().value;\r
+ test.ok(i64 instanceof ProtoBuf.Long);\r
+ test.strictEqual(i64.toString(), "9223372036854775807");\r
+\r
+ test.done();\r
+ },\r
+\r
+ "mapField": function(test) {\r
+ var proto =\r
+ "message Test {\n" +\r
+ " map<string, int32> map_string_int32 = 1;\n" +\r
+ " map<string, int64> map_string_int64 = 2;\n" +\r
+ " map<string, string> map_string_string = 3;\n" +\r
+ " map<string, Test> map_string_msg = 4;\n" +\r
+ " map<string, Enum> map_string_enum = 5;\n" +\r
+ " map<int32, string> map_int32_string = 6;\n" +\r
+ " map<int64, string> map_int64_string = 7;\n" +\r
+ " map<bool, string> map_bool_string = 9;\n" +\r
+ "}\n" +\r
+ "enum Enum { Default = 0; A = 1; B = 2; }\n";\r
+ var builder = ProtoBuf.newBuilder();\r
+ ProtoBuf.loadProto(proto, builder, "test/mapField.proto");\r
+ var Test = builder.build('Test'),\r
+ Enum = builder.build('Enum');\r
+\r
+ var testMsg = new Test();\r
+ test.strictEqual(testMsg.map_string_int32.size, 0);\r
+ test.strictEqual(testMsg.map_string_int64.size, 0);\r
+ test.strictEqual(testMsg.map_string_string.size, 0);\r
+ test.strictEqual(testMsg.map_string_msg.size, 0);\r
+ test.strictEqual(testMsg.map_string_enum.size, 0);\r
+ test.strictEqual(testMsg.map_int32_string.size, 0);\r
+ test.strictEqual(testMsg.map_int64_string.size, 0);\r
+ test.strictEqual(testMsg.map_bool_string.size, 0);\r
+\r
+ testMsg.$set('map_string_int32', { 'asdf': 42 });\r
+\r
+ try {\r
+ testMsg.$set('map_string_int32', { 'asdf': 42.1 });\r
+ test.ok(false); // ^ should throw\r
+ } catch (e) {\r
+ test.ok(e.message.match(/Illegal/));\r
+ }\r
+\r
+ test.done();\r
+ },\r
+\r
+ "mapEncodeDecode": function(test) {\r
+ var proto =\r
+ "message Test {\n" +\r
+ " map<string, int32> map_string_int32 = 1;\n" +\r
+ " map<string, int64> map_string_int64 = 2;\n" +\r
+ " map<string, string> map_string_string = 3;\n" +\r
+ " map<string, Test> map_string_msg = 4;\n" +\r
+ " map<string, Enum> map_string_enum = 5;\n" +\r
+ " map<int32, string> map_int32_string = 6;\n" +\r
+ " map<int64, string> map_int64_string = 7;\n" +\r
+ " map<bool, string> map_bool_string = 9;\n" +\r
+ "}\n" +\r
+ "enum Enum { Default = 0; A = 1; B = 2; }\n";\r
+ var builder = ProtoBuf.newBuilder();\r
+ ProtoBuf.loadProto(proto, builder, "test/mapField.proto");\r
+ var Test = builder.build('Test'),\r
+ Enum = builder.build('Enum');\r
+\r
+ var testMsg = new Test();\r
+ testMsg.map_string_int32.set("a", 1);\r
+ testMsg.map_string_int32.set("b", 2);\r
+ testMsg.map_string_int64.set("c", "12345678901234");\r
+ testMsg.map_string_int64.set("d", "98765432109876");\r
+ testMsg.map_string_string.set("e", "asdf");\r
+ testMsg.map_string_string.set("f", "jkl;");\r
+ testMsg.map_string_enum.set("g", Enum.A);\r
+ testMsg.map_string_enum.set("h", Enum.B);\r
+ testMsg.map_int32_string.set(9, "a");\r
+ testMsg.map_int32_string.set(10, "b");\r
+ testMsg.map_int64_string.set("12345678901234", "a");\r
+ testMsg.map_int64_string.set("98765432109876", "b");\r
+ testMsg.map_bool_string.set(false, "a");\r
+ testMsg.map_bool_string.set(true, "b");\r
+\r
+ var encoded = testMsg.encode();\r
+ testMsg = Test.decode(encoded);\r
+\r
+ test.strictEqual(testMsg.map_string_int32.get("a"), 1);\r
+ test.strictEqual(testMsg.map_string_int32.get("b"), 2);\r
+ test.strictEqual(testMsg.map_string_int64.get("c").toString(), "12345678901234");\r
+ test.strictEqual(testMsg.map_string_int64.get("d").toString(), "98765432109876");\r
+ test.strictEqual(testMsg.map_string_string.get("e"), "asdf");\r
+ test.strictEqual(testMsg.map_string_string.get("f"), "jkl;");\r
+ test.strictEqual(testMsg.map_string_enum.get("g"), Enum.A);\r
+ test.strictEqual(testMsg.map_string_enum.get("h"), Enum.B);\r
+ test.strictEqual(testMsg.map_int32_string.get(9), "a");\r
+ test.strictEqual(testMsg.map_int32_string.get(10), "b");\r
+ test.strictEqual(testMsg.map_int64_string.get("12345678901234"), "a");\r
+ test.strictEqual(testMsg.map_int64_string.get("98765432109876"), "b");\r
+ test.strictEqual(testMsg.map_bool_string.get(false), "a");\r
+ test.strictEqual(testMsg.map_bool_string.get(true), "b");\r
+\r
+ test.done();\r
+ },\r
+\r
+ "proto3Json": function(test) {\r
+ var proto =\r
+ "syntax = \"proto3\";\n" +\r
+ "message Test {\n" +\r
+ " int32 optional_int32 = 1;\n" +\r
+ " int64 optional_int64 = 2;\n" +\r
+ " string optional_string = 3;\n" +\r
+ " bytes optional_bytes = 4;\n" +\r
+ " bool optional_bool = 5;\n" +\r
+ " Enum optional_enum = 6;\n" +\r
+ " repeated int32 repeated_int32 = 11;\n" +\r
+ " repeated int64 repeated_int64 = 12;\n" +\r
+ " repeated string repeated_string = 13;\n" +\r
+ " repeated bytes repeated_bytes = 14;\n" +\r
+ " repeated bool repeated_bool = 15;\n" +\r
+ " repeated Enum repeated_enum = 16;\n" +\r
+ " map<string, int32> map_string_int32 = 20;\n" +\r
+ " map<string, int64> map_string_int64 = 21;\n" +\r
+ " map<string, string> map_string_string = 22;\n" +\r
+ " map<string, Enum> map_string_enum = 24;\n" +\r
+ " map<int32, string> map_int32_string = 25;\n" +\r
+ " map<int64, string> map_int64_string = 26;\n" +\r
+ " map<bool, string> map_bool_string = 27;\n" +\r
+ "}\n" +\r
+ "enum Enum { Default = 0; A = 1; B = 2; }\n";\r
+ var builder = ProtoBuf.newBuilder();\r
+ ProtoBuf.loadProto(proto, builder, "test/mapField.proto");\r
+ var Test = builder.build('Test'),\r
+ Enum = builder.build('Enum');\r
+\r
+ var testMsg = new Test();\r
+ testMsg.optional_int32 = 1;\r
+ testMsg.optional_int64 = "12345678901234";\r
+ testMsg.optional_string = "hello";\r
+ testMsg.optional_bytes = ProtoBuf.ByteBuffer.fromBinary("\x00\xFF\x80");\r
+ testMsg.optional_bool = true;\r
+ testMsg.optional_enum = Enum.A;\r
+ testMsg.repeated_int32.push(1);\r
+ testMsg.repeated_int64.push("12345678901234");\r
+ testMsg.repeated_string.push("hello");\r
+ testMsg.repeated_bytes.push(ProtoBuf.ByteBuffer.fromBinary("\x00\xFF\x80"));\r
+ testMsg.repeated_bool.push(true);\r
+ testMsg.repeated_enum.push(Enum.A);\r
+ testMsg.map_string_int32.set("a", 1);\r
+ testMsg.map_string_int32.set("b", 2);\r
+ testMsg.map_string_int64.set("c", "12345678901234");\r
+ testMsg.map_string_int64.set("d", "98765432109876");\r
+ testMsg.map_string_string.set("e", "asdf");\r
+ testMsg.map_string_string.set("f", "jkl;");\r
+ testMsg.map_string_enum.set("g", Enum.A);\r
+ testMsg.map_string_enum.set("h", Enum.B);\r
+ testMsg.map_int32_string.set(9, "a");\r
+ testMsg.map_int32_string.set(10, "b");\r
+ testMsg.map_int64_string.set("12345678901234", "a");\r
+ testMsg.map_int64_string.set("98765432109876", "b");\r
+ testMsg.map_bool_string.set(false, "a");\r
+ testMsg.map_bool_string.set(true, "b");\r
+\r
+ var jsonObj = JSON.parse(testMsg.encodeJSON());\r
+ test.deepEqual(jsonObj,\r
+ {\r
+ optional_int32: 1,\r
+ optional_int64: "12345678901234",\r
+ optional_string: "hello",\r
+ optional_bytes: "AP+A", // base64\r
+ optional_bool: true,\r
+ optional_enum: "A",\r
+ repeated_int32: [1],\r
+ repeated_int64: ["12345678901234"],\r
+ repeated_string: ["hello"],\r
+ repeated_bytes: ["AP+A"], // base64\r
+ repeated_bool: [true],\r
+ repeated_enum: ["A"],\r
+ map_string_int32: { "a": 1, "b": 2 },\r
+ map_string_int64: { "c": "12345678901234", "d": "98765432109876" },\r
+ map_string_string: { "e": "asdf", "f": "jkl;" },\r
+ map_string_enum: { "g": "A", "h": "B" },\r
+ map_int32_string: { "9": "a", "10": "b" },\r
+ map_int64_string: { "12345678901234": "a", "98765432109876": "b" },\r
+ map_bool_string: { "false": "a", "true": "b" },\r
+ });\r
+\r
+ var testMsg2 = Test.decodeJSON(testMsg.encodeJSON());\r
+ test.strictEqual(testMsg2.encodeJSON(), testMsg.encodeJSON());\r
+\r
+ test.done();\r
+ },\r
+\r
+ // Node.js only\r
+ "loaders": BROWSER ? {} : {\r
+\r
+ "commonjs": function(test) {\r
+ var fs = require("fs")\r
+ , vm = require("vm")\r
+ , util = require('util');\r
+\r
+ var code = fs.readFileSync(__dirname+"/../dist/"+FILE);\r
+ var exports = {};\r
+ var sandbox = new Sandbox({\r
+ module: {\r
+ exports: exports,\r
+ id: "protobufjs"\r
+ },\r
+ exports: exports,\r
+ require: (function() {\r
+ function require(mod) {\r
+ if (mod == 'bytebuffer') require.called = true;\r
+ return ByteBuffer;\r
+ }\r
+ require.called = false;\r
+ return require;\r
+ })()\r
+ });\r
+ vm.runInNewContext(code, sandbox, "ProtoBuf.js in CommonJS-VM");\r
+ // console.log(util.inspect(sandbox));\r
+ test.ok(typeof sandbox.module.exports == 'object');\r
+ test.ok(typeof sandbox.require != 'undefined' && sandbox.require.called);\r
+ test.done();\r
+ },\r
+\r
+ "amd": function(test) {\r
+ var fs = require("fs")\r
+ , vm = require("vm")\r
+ , util = require('util');\r
+\r
+ var code = fs.readFileSync(__dirname+"/../dist/"+FILE);\r
+ var sandbox = new Sandbox({\r
+ define: (function() {\r
+ function define() {\r
+ define.called = true;\r
+ }\r
+ define.amd = true;\r
+ define.called = false;\r
+ return define;\r
+ })()\r
+ });\r
+ vm.runInNewContext(code, sandbox, "ProtoBuf.js in AMD-VM");\r
+ // console.log(util.inspect(sandbox));\r
+ test.ok(sandbox.define.called == true);\r
+ test.done();\r
+ },\r
+\r
+ "shim": function(test) {\r
+ var fs = require("fs")\r
+ , vm = require("vm")\r
+ , util = require('util');\r
+\r
+ var code = fs.readFileSync(__dirname+"/../dist/"+FILE);\r
+ var sandbox = new Sandbox({\r
+ dcodeIO: {\r
+ ByteBuffer: ByteBuffer\r
+ }\r
+ });\r
+ vm.runInNewContext(code, sandbox, "ProtoBuf.js in shim-VM");\r
+ // console.log(util.inspect(sandbox));\r
+ test.ok(typeof sandbox.dcodeIO != 'undefined' && typeof sandbox.dcodeIO.ProtoBuf != 'undefined');\r
+ test.done();\r
+ }\r
+ }\r
+ };\r
+\r
+ if (typeof module != 'undefined' && module.exports) {\r
+ module.exports = suite;\r
+ } else {\r
+ global["suite"] = suite;\r
+ }\r
+\r
+})(this);\r