| #!/usr/bin/env node |
| /* |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, |
| * software distributed under the License is distributed on an |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| * KIND, either express or implied. See the License for the |
| * specific language governing permissions and limitations |
| * under the License. |
| * |
| */ |
| |
| /** |
| * This is a fairly literal JavaScript port of codec.py used to unit test the |
| * proton.Data wrapper class. This suite of tests is actually testing the low |
| * level implementation methods used to access the AMQP type system and in |
| * practice most normal users wouldn't need to call these directly, rather users |
| * should simply use the putObject() and getObject() methods. |
| */ |
| |
| // Check if the environment is Node.js and if not log an error and exit. |
| if (typeof process === 'object' && typeof require === 'function') { |
| var unittest = require("./unittest.js"); |
| var assert = require("assert"); |
| |
| // Increase the virtual heap available to the emscripten compiled C runtime. |
| // This allows us to test a really big string. |
| PROTON_TOTAL_MEMORY = 140000000; |
| PROTON_TOTAL_STACK = 25000000; // Needs to be bigger than the biggest string. |
| var proton = require("qpid-proton-messenger"); |
| |
| // Extend TestCase by creating a prototype instance and adding test methods as properties. |
| var DataTest = new unittest.TestCase(); |
| |
| DataTest.setUp = function() { |
| this.data = new proton.Data(); |
| }; |
| |
| DataTest.tearDown = function() { |
| this.data.free(); |
| this.data = null; |
| }; |
| |
| DataTest.testTopLevelNext = function() { |
| console.log("testTopLevelNext"); |
| assert(this.data.next() === null); |
| this.data.putNULL(); |
| this.data.putBOOL(false); |
| this.data.putINT(0); |
| assert(this.data.next() === null); |
| this.data.rewind(); |
| assert(this.data.next() === proton.Data.NULL); |
| assert(this.data.next() === proton.Data.BOOL); |
| assert(this.data.next() === proton.Data.INT); |
| assert(this.data.next() === null); |
| console.log("OK\n"); |
| }; |
| |
| DataTest.testNestedNext = function() { |
| console.log("testNestedNext"); |
| assert(this.data.next() === null); |
| this.data.putNULL(); |
| assert(this.data.next() === null); |
| this.data.putLISTNODE(); |
| assert(this.data.next() === null); |
| this.data.putBOOL(false); |
| assert(this.data.next() === null); |
| this.data.rewind(); |
| assert(this.data.next() === proton.Data.NULL); |
| assert(this.data.next() === proton.Data.LIST); |
| this.data.enter(); |
| assert(this.data.next() === null); |
| this.data.putUBYTE(0); |
| assert(this.data.next() === null); |
| this.data.putUINT(0); |
| assert(this.data.next() === null); |
| this.data.putINT(0); |
| assert(this.data.next() === null); |
| this.data.exit(); |
| assert(this.data.next() === proton.Data.BOOL); |
| assert(this.data.next() === null); |
| |
| this.data.rewind(); |
| assert(this.data.next() === proton.Data.NULL); |
| assert(this.data.next() === proton.Data.LIST); |
| assert(this.data.enter()); |
| assert(this.data.next() === proton.Data.UBYTE); |
| assert(this.data.next() === proton.Data.UINT); |
| assert(this.data.next() === proton.Data.INT); |
| assert(this.data.next() === null); |
| assert(this.data.exit()); |
| assert(this.data.next() === proton.Data.BOOL); |
| assert(this.data.next() === null); |
| console.log("OK\n"); |
| }; |
| |
| DataTest.testEnterExit = function() { |
| console.log("testEnterExit"); |
| assert(this.data.next() === null); |
| assert(!this.data.enter()); |
| this.data.putLISTNODE(); |
| assert(this.data.enter()); |
| assert(this.data.next() === null); |
| this.data.putLISTNODE(); |
| assert(this.data.enter()); |
| this.data.putLISTNODE(); |
| assert(this.data.enter()); |
| assert(this.data.exit()); |
| assert(this.data.getLISTNODE() === 0); |
| assert(this.data.exit()); |
| assert(this.data.getLISTNODE() === 1); |
| assert(this.data.exit()); |
| assert(this.data.getLISTNODE() === 1); |
| assert(!this.data.exit()); |
| assert(this.data.getLISTNODE() === 1); |
| assert(this.data.next() === null); |
| |
| this.data.rewind(); |
| assert(this.data.next() === proton.Data.LIST); |
| assert(this.data.getLISTNODE() === 1); |
| assert(this.data.enter()); |
| assert(this.data.next() === proton.Data.LIST); |
| assert(this.data.getLISTNODE() === 1); |
| assert(this.data.enter()); |
| assert(this.data.next() === proton.Data.LIST); |
| assert(this.data.getLISTNODE() === 0); |
| assert(this.data.enter()); |
| assert(this.data.next() === null); |
| assert(this.data.exit()); |
| assert(this.data.getLISTNODE() === 0); |
| assert(this.data.exit()); |
| assert(this.data.getLISTNODE() === 1); |
| assert(this.data.exit()); |
| assert(this.data.getLISTNODE() === 1); |
| assert(!this.data.exit()); |
| console.log("OK\n"); |
| }; |
| |
| /** |
| * This tests the "low level" putARRAYNODE/getARRAYNODE methods. |
| * In general though applications would create a proton.Data.Array and use the |
| * higher level putARRAY/getARRAY |
| */ |
| DataTest._testArray = function(dtype, descriptor, atype, values) { |
| var values = Array.prototype.slice.apply(arguments, [3]); |
| dtype = (dtype == null) ? null : dtype.toUpperCase(); |
| atype = atype.toUpperCase(); |
| |
| // Create an array node, enter it and put the descriptor (if present) and values. |
| this.data.putARRAYNODE(dtype != null, proton.Data[atype]); |
| this.data.enter(); |
| if (dtype != null) { |
| var putter = 'put' + dtype; |
| this.data[putter](descriptor); |
| } |
| var putter = 'put' + atype; |
| for (var i = 0; i < values.length; i++) { |
| this.data[putter](values[i]); |
| } |
| this.data.exit(); |
| |
| // Check that we did indeed add an Array node |
| this.data.rewind(); |
| assert(this.data.next() === proton.Data.ARRAY); |
| |
| // Get the count, described and type metadata from the array node and compare |
| // with the values we passed to putARRAYNODE. |
| var metadata = this.data.getARRAYNODE(); |
| var count = metadata.count; |
| var described = metadata.described; |
| var type = metadata.type; |
| |
| assert(count === values.length); |
| if (dtype == null) { |
| assert(described === false); |
| } else { |
| assert(described === true); |
| } |
| assert(type === proton.Data[atype]); |
| |
| // Enter the array node and compare the descriptor and values with those that |
| // we put into the array. |
| assert(this.data.enter()); |
| if (described) { |
| assert(this.data.next() === proton.Data[dtype]); |
| var getter = 'get' + dtype; |
| var gotten = this.data[getter](); |
| assert(gotten.toString() === descriptor.toString()); |
| } |
| var getter = 'get' + atype; |
| for (var i = 0; i < values.length; i++) { |
| assert(this.data.next() === proton.Data[atype]); |
| var gotten = this.data[getter](); |
| assert(gotten.toString() === values[i].toString()); |
| } |
| assert(this.data.next() === null); |
| assert(this.data.exit()); |
| }; |
| |
| DataTest.testStringArray = function() { |
| console.log("testStringArray"); |
| this._testArray(null, null, "string", "one", "two", "three"); |
| |
| // Now try using the proton.Data.Array class. |
| this.data.clear(); |
| var put = new proton.Data.Array("STRING", ["four", "five", "six"]); |
| this.data.putARRAY(put); |
| var get = this.data.getARRAY(); |
| assert(get.equals(put)); |
| console.log("OK\n"); |
| }; |
| |
| DataTest.testDescribedStringArray = function() { |
| console.log("testDescribedStringArray"); |
| this._testArray("symbol", "url", "string", "one", "two", "three"); |
| |
| // Now try using the proton.Data.Array class. |
| this.data.clear(); |
| var put = new proton.Data.Array("STRING", ["four", "five", "six"], new proton.Data.Symbol("url")); |
| this.data.putARRAY(put); |
| var get = this.data.getARRAY(); |
| assert(get.equals(put)); |
| console.log("OK\n"); |
| }; |
| |
| DataTest.testIntArray = function() { |
| console.log("testIntArray"); |
| this._testArray(null, null, "int", 1, 2, 3); |
| |
| // Now try using the proton.Data.Array class. |
| this.data.clear(); |
| var put = new proton.Data.Array("INT", [4, 5, 6]); |
| this.data.putARRAY(put); |
| var get = this.data.getARRAY(); |
| assert(get.equals(put)); |
| console.log("OK\n"); |
| }; |
| |
| DataTest.testUUIDArray = function() { |
| console.log("testUUIDArray"); |
| this._testArray(null, null, "uuid", new proton.Data.Uuid(), new proton.Data.Uuid(), new proton.Data.Uuid()); |
| |
| // Now try using the proton.Data.Array class. |
| this.data.clear(); |
| var put = new proton.Data.Array("UUID", [new proton.Data.Uuid(), new proton.Data.Uuid(), new proton.Data.Uuid()]); |
| this.data.putARRAY(put); |
| var get = this.data.getARRAY(); |
| assert(get.equals(put)); |
| console.log("OK\n"); |
| }; |
| |
| DataTest.testEmptyArray = function() { |
| console.log("testEmptyArray"); |
| this._testArray(null, null, "null"); |
| |
| // Now try using the proton.Data.Array class. |
| this.data.clear(); |
| var put = new proton.Data.Array(); |
| this.data.putARRAY(put); |
| var get = this.data.getARRAY(); |
| assert(get.equals(put)); |
| console.log("OK\n"); |
| }; |
| |
| DataTest.testDescribedEmptyArray = function() { |
| console.log("testDescribedEmptyArray"); |
| this._testArray("long", 0, "null"); |
| |
| // Now try using the proton.Data.Array class. |
| this.data.clear(); |
| var put = new proton.Data.Array((0).long()); |
| this.data.putARRAY(put); |
| var get = this.data.getARRAY(); |
| assert(get.equals(put)); |
| console.log("OK\n"); |
| }; |
| |
| DataTest._test = function(dtype, values) { |
| var values = Array.prototype.slice.apply(arguments, [1]); |
| var lastValue = values[values.length - 1]; |
| |
| // Default equality function. Note that we use valueOf because some of the |
| // types we are trying to compare (Symbol, Timestamp, Uuid etc.) are object |
| // types and we want to compare their value not whether they are the same object. |
| var eq = function(x, y) {return x.valueOf() === y.valueOf();}; |
| |
| if (typeof lastValue === 'function') { |
| eq = values.pop(); |
| } |
| |
| dtype = dtype.toUpperCase(); |
| var ntype = proton.Data[dtype]; |
| var putter = 'put' + dtype; |
| var getter = 'get' + dtype; |
| |
| for (var i = 0; i < values.length; i++) { |
| var v = values[i]; |
| /* |
| * Replace the array element with its value. We do this to make testing |
| * simpler for Binary elements. In the case of Binary putBINARY "consumes" |
| * the data, in other words ownership of the underlying raw data transfers |
| * to the Data object so the v object becomes "empty" after calling the |
| * putter. Calling its valueOf() happens to call its toString() which |
| * provides a stringified version of the Binary whilst also working for |
| * the other data types we want to test too. |
| */ |
| values[i] = v.valueOf(); |
| this.data[putter](v); |
| var gotten = this.data[getter](); |
| assert(eq(gotten, values[i])); |
| } |
| |
| this.data.rewind(); |
| |
| for (var i = 0; i < values.length; i++) { |
| var v = values[i]; |
| var vtype = this.data.next(); |
| assert(vtype === ntype); |
| var gotten = this.data[getter](); |
| assert(eq(gotten, v)); |
| } |
| |
| // Test encode and decode methods. |
| var encoded = this.data.encode(); |
| var copy = new proton.Data(); |
| while (encoded) { |
| encoded = copy.decode(encoded); |
| } |
| copy.rewind(); |
| |
| for (var i = 0; i < values.length; i++) { |
| var v = values[i]; |
| var vtype = copy.next(); |
| assert(vtype === ntype); |
| var gotten = copy[getter](); |
| assert(eq(gotten, v)); |
| } |
| copy.free(); |
| }; |
| |
| DataTest.testInt = function() { |
| console.log("testInt"); |
| this._test("int", 1, 2, 3, -1, -2, -3); |
| console.log("OK\n"); |
| |
| }; |
| |
| DataTest.testString = function() { |
| console.log("testString"); |
| this._test("string", "one", "two", "three", "this is a test", ""); |
| console.log("OK\n"); |
| }; |
| |
| DataTest.testBigString = function() { |
| // Try a 2MB string, this is about as big as we can cope with using the default |
| // emscripten heap size. |
| console.log("testBigString"); |
| var data = ""; |
| for (var i = 0; i < 2000000; i++) { |
| data += "*"; |
| } |
| var string = "start\n" + data + "\nfinish\n"; |
| this._test("string", string); |
| console.log("OK\n"); |
| }; |
| |
| /* TODO - currently emscripten isn't respecting Module['TOTAL_STACK'] so setting PROTON_TOTAL_STACK doesn't actually increase the stack size. |
| DataTest.testReallyBigString = function() { |
| // Try a 20MB string, this needs a bigger emscripten heap size and more stack |
| // as the default stack is 5MB and strings are allocated on the stack. |
| console.log("testReallyBigString"); |
| var data = ""; |
| for (var i = 0; i < 20000000; i++) { |
| data += "*"; |
| } |
| var string = "start\n" + data + "\nfinish\n"; |
| this._test("string", string); |
| console.log("OK\n"); |
| }; |
| */ |
| |
| DataTest.testFloat = function() { |
| console.log("testFloat"); |
| // We have to use a special comparison here because JavaScript internally |
| // only uses doubles and converting between floats and doubles is imprecise. |
| this._test("float", 0, 1, 2, 3, 0.1, 0.2, 0.3, -1, -2, -3, -0.1, -0.2, -0.3, |
| function(x, y) {return (x - y < 0.000001);}); |
| console.log("OK\n"); |
| }; |
| |
| DataTest.testDouble = function() { |
| console.log("testDouble"); |
| this._test("double", 0, 1, 2, 3, 0.1, 0.2, 0.3, -1, -2, -3, -0.1, -0.2, -0.3); |
| console.log("OK\n"); |
| }; |
| |
| DataTest.testBinary = function() { |
| console.log("testBinary"); |
| this._test("binary", new proton.Data.Binary(["t".char(), "h".char(), "i".char(), "s".char()]), |
| new proton.Data.Binary("is"), new proton.Data.Binary("a"), new proton.Data.Binary("test"), |
| new proton.Data.Binary("of"), new proton.Data.Binary("двоичные данные")); |
| console.log("OK\n"); |
| }; |
| |
| DataTest.testSymbol = function() { |
| console.log("testSymbol"); |
| this._test("symbol", new proton.Data.Symbol("this is a symbol test"), |
| new proton.Data.Symbol("bleh"), new proton.Data.Symbol("blah")); |
| console.log("OK\n"); |
| }; |
| |
| DataTest.testTimestamp = function() { |
| console.log("testTimestamp"); |
| this._test("timestamp", new Date(0), new Date(12345), new Date(1000000)); |
| console.log("OK\n"); |
| }; |
| |
| DataTest.testChar = function() { |
| console.log("testChar"); |
| this._test("char", 'a', 'b', 'c', '\u1234'); |
| console.log("OK\n"); |
| }; |
| |
| DataTest.testUUID = function() { |
| console.log("testUUID"); |
| this._test("uuid", new proton.Data.Uuid(), new proton.Data.Uuid(), new proton.Data.Uuid()); |
| console.log("OK\n"); |
| }; |
| |
| /* TODO |
| DataTest.testDecimal32 = function() { |
| console.log("testDecimal32"); |
| //this._test("decimal32", 0, 1, 2, 3, 4, Math.pow(2, 30)); |
| }; |
| |
| DataTest.testDecimal64 = function() { |
| console.log("testDecimal64"); |
| //this._test("decimal64", 0, 1, 2, 3, 4, Math.pow(2, 60)); |
| }; |
| |
| DataTest.testDecimal128 = function() { |
| console.log("testDecimal128"); |
| // TODO |
| }; |
| */ |
| |
| DataTest.testCopy = function() { |
| console.log("testCopy"); |
| this.data.putDESCRIBEDNODE(); |
| this.data.enter(); |
| this.data.putULONG(123); |
| this.data.putMAPNODE(); |
| this.data.enter(); |
| this.data.putSTRING("pi"); |
| this.data.putDOUBLE(3.14159265359); |
| this.data.exit(); |
| this.data.exit(); |
| |
| var dst = this.data.copy(); |
| var copy = dst.format(); |
| var orig = this.data.format(); |
| assert(copy === orig); |
| dst.free(); |
| console.log("OK\n"); |
| }; |
| |
| DataTest.testCopyNested = function() { |
| console.log("testCopyNested"); |
| var nested = [1, 2, 3, [4, 5, 6], 7, 8, 9]; |
| this.data.putObject(nested); |
| var dst = this.data.copy(); |
| assert(dst.format() === this.data.format()); |
| dst.free(); |
| console.log("OK\n"); |
| }; |
| |
| DataTest.testCopyNestedArray = function() { |
| console.log("testCopyNestedArray"); |
| var nested = [new proton.Data.Array("LIST", [ |
| ["first", [new proton.Data.Array("INT", [1, 2, 3]), "this"]], |
| ["second", [new proton.Data.Array("INT", [1, 2, 3]), "is"]], |
| ["third", [new proton.Data.Array("INT", [1, 2, 3]), "fun"]] |
| ]), |
| "end"]; |
| this.data.putObject(nested); |
| var dst = this.data.copy(); |
| assert(dst.format() === this.data.format()); |
| dst.free(); |
| console.log("OK\n"); |
| }; |
| |
| DataTest.testRoundTrip = function() { |
| console.log("testRoundTrip"); |
| var obj = {key: new Date(1234), |
| 123: "blah", |
| c: "bleh", |
| desc: new proton.Data.Described("http://example.org", new proton.Data.Symbol("url")), |
| array: new proton.Data.Array("INT", [1, 2, 3]), |
| list: [1, 2, 3, null, 4], |
| boolean: true}; |
| // Serialise obj into this.data. |
| this.data.putObject(obj); |
| |
| // Encode this.data into a Binary representation. |
| var enc = this.data.encode(); |
| |
| // Create a new Data instance and decode from the Binary representation |
| // consuming the Binary contents in the process. |
| var data = new proton.Data(); |
| data.decode(enc); |
| |
| assert(data.format() === this.data.format()); |
| |
| // Deserialise from the copied Data instance into a new JavaScript Object. |
| data.rewind(); |
| assert(data.next()); |
| var copy = data.getObject(); |
| |
| // Create yet another Data instance and serialise the new Object into that. |
| var data1 = new proton.Data(); |
| data1.putObject(copy); |
| |
| // Compare the round tripped Data with the original one. |
| assert(data1.format() === this.data.format()); |
| |
| data.free(); |
| data1.free(); |
| console.log("OK\n"); |
| }; |
| |
| DataTest.testLookup = function() { |
| console.log("testLookup"); |
| var obj = {key: "value", |
| pi: 3.14159, |
| list: [1, 2, 3, 4]}; |
| // Serialise obj into this.data. |
| this.data.putObject(obj); |
| this.data.rewind(); |
| this.data.next(); |
| this.data.enter(); |
| this.data.narrow(); |
| assert(this.data.lookup("pi")); |
| assert(this.data.getObject() === 3.14159); |
| this.data.rewind(); |
| assert(this.data.lookup("key")); |
| assert(this.data.getObject() === "value"); |
| this.data.rewind(); |
| assert(this.data.lookup("list")); |
| assert(this.data.getObject().toString() === "1,2,3,4"); |
| this.data.widen(); |
| this.data.rewind(); |
| assert(!this.data.lookup("pi")); |
| console.log("OK\n"); |
| }; |
| |
| DataTest.run(); |
| } else { |
| console.error("codec.js should be run in Node.js"); |
| } |
| |