blob: 193c878f2da49a282db4d59bcd5e563172a7b0e7 [file] [log] [blame]
#!/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");
}