You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@qpid.apache.org by kp...@apache.org on 2016/11/04 19:42:14 UTC

qpid-interop-test git commit: QPIDIT-47: Add Rhea javascript client. Also includes minor adjustments to python and c++ shims owing to expended tests and not using message ids as a part of the test.

Repository: qpid-interop-test
Updated Branches:
  refs/heads/master 5579efd4b -> 156ac5a56


QPIDIT-47: Add Rhea javascript client. Also includes minor adjustments to python and c++ shims owing to expended tests and not using message ids as a part of the test.


Project: http://git-wip-us.apache.org/repos/asf/qpid-interop-test/repo
Commit: http://git-wip-us.apache.org/repos/asf/qpid-interop-test/commit/156ac5a5
Tree: http://git-wip-us.apache.org/repos/asf/qpid-interop-test/tree/156ac5a5
Diff: http://git-wip-us.apache.org/repos/asf/qpid-interop-test/diff/156ac5a5

Branch: refs/heads/master
Commit: 156ac5a56c2fdf88efb5ea5e5d43dbc313d3da76
Parents: 5579efd
Author: Kim van der Riet <kp...@apache.org>
Authored: Fri Nov 4 15:41:52 2016 -0400
Committer: Kim van der Riet <kp...@apache.org>
Committed: Fri Nov 4 15:41:52 2016 -0400

----------------------------------------------------------------------
 .../src/qpidit/amqp_types_test/Receiver.cpp     |   3 +-
 .../src/qpidit/amqp_types_test/Receiver.hpp     |   2 +-
 .../src/amqp_types_test/Receiver.py             |   2 +-
 shims/rhea-js/amqp_types_test/.gitignore        |   1 +
 shims/rhea-js/amqp_types_test/README            |  47 +++
 shims/rhea-js/amqp_types_test/Receiver.js       | 196 +++++++++++
 shims/rhea-js/amqp_types_test/Sender.js         | 340 +++++++++++++++++++
 .../amqp_types_test/rhea-long-ulong.patch       |  50 +++
 src/python/qpid_interop_test/amqp_types_test.py | 127 +++++--
 src/python/qpid_interop_test/shims.py           |   9 +
 10 files changed, 738 insertions(+), 39 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/qpid-interop-test/blob/156ac5a5/shims/qpid-proton-cpp/src/qpidit/amqp_types_test/Receiver.cpp
----------------------------------------------------------------------
diff --git a/shims/qpid-proton-cpp/src/qpidit/amqp_types_test/Receiver.cpp b/shims/qpid-proton-cpp/src/qpidit/amqp_types_test/Receiver.cpp
index 22f4aed..53819a7 100644
--- a/shims/qpid-proton-cpp/src/qpidit/amqp_types_test/Receiver.cpp
+++ b/shims/qpid-proton-cpp/src/qpidit/amqp_types_test/Receiver.cpp
@@ -60,7 +60,8 @@ namespace qpidit
         }
 
         void Receiver::on_message(proton::delivery &d, proton::message &m) {
-            if (proton::get<uint64_t>(m.id()) < _received) return; // ignore duplicate
+            // TODO: Ignore m.id() if it does not exist on the message
+            //if (proton::get<uint64_t>(m.id()) < _received) return; // ignore duplicate
             if (_received < _expected) {
                 if (_amqpType.compare("null") == 0) {
                     checkMessageType(m, proton::NULL_TYPE);

http://git-wip-us.apache.org/repos/asf/qpid-interop-test/blob/156ac5a5/shims/qpid-proton-cpp/src/qpidit/amqp_types_test/Receiver.hpp
----------------------------------------------------------------------
diff --git a/shims/qpid-proton-cpp/src/qpidit/amqp_types_test/Receiver.hpp b/shims/qpid-proton-cpp/src/qpidit/amqp_types_test/Receiver.hpp
index 3e180d3..9435a99 100644
--- a/shims/qpid-proton-cpp/src/qpidit/amqp_types_test/Receiver.hpp
+++ b/shims/qpid-proton-cpp/src/qpidit/amqp_types_test/Receiver.hpp
@@ -69,7 +69,7 @@ namespace qpidit
                 if (fillFlag) {
                     oss << std::setw(sizeof(T)*2) << std::setfill('0');
                 }
-                oss << (sizeof(T) == 1 ? (int)val & 0xff : sizeof(T) == 2 ? val & 0xffff : sizeof(T) == 4 ? val & 0xffffffff : val);
+                oss << (sizeof(T) == 1 ? (int)val & 0xff : sizeof(T) == 2 ? val & 0xffff : /*sizeof(T) == 4 ? val & 0xffffffff :*/ val);
                 return oss.str();
             }
 

http://git-wip-us.apache.org/repos/asf/qpid-interop-test/blob/156ac5a5/shims/qpid-proton-python/src/amqp_types_test/Receiver.py
----------------------------------------------------------------------
diff --git a/shims/qpid-proton-python/src/amqp_types_test/Receiver.py b/shims/qpid-proton-python/src/amqp_types_test/Receiver.py
index f075977..4533124 100755
--- a/shims/qpid-proton-python/src/amqp_types_test/Receiver.py
+++ b/shims/qpid-proton-python/src/amqp_types_test/Receiver.py
@@ -94,7 +94,7 @@ class AmqpTypesTestReceiver(MessagingHandler):
             elif self.amqp_type == 'decimal128':
                 self.received_value_list.append('0x' + ''.join(['%02x' % ord(c) for c in event.message.body]).strip())
             elif self.amqp_type == 'char':
-                if ord(event.message.body) < 0x80 and event.message.body in digits + letters + punctuation:
+                if ord(event.message.body) < 0x80 and event.message.body in digits + letters + punctuation + " ":
                     self.received_value_list.append(event.message.body)
                 else:
                     self.received_value_list.append(hex(ord(event.message.body)))

http://git-wip-us.apache.org/repos/asf/qpid-interop-test/blob/156ac5a5/shims/rhea-js/amqp_types_test/.gitignore
----------------------------------------------------------------------
diff --git a/shims/rhea-js/amqp_types_test/.gitignore b/shims/rhea-js/amqp_types_test/.gitignore
new file mode 100644
index 0000000..2ccbe46
--- /dev/null
+++ b/shims/rhea-js/amqp_types_test/.gitignore
@@ -0,0 +1 @@
+/node_modules/

http://git-wip-us.apache.org/repos/asf/qpid-interop-test/blob/156ac5a5/shims/rhea-js/amqp_types_test/README
----------------------------------------------------------------------
diff --git a/shims/rhea-js/amqp_types_test/README b/shims/rhea-js/amqp_types_test/README
new file mode 100644
index 0000000..308a141
--- /dev/null
+++ b/shims/rhea-js/amqp_types_test/README
@@ -0,0 +1,47 @@
+These are the Sender and Receiver shims for qpid-interop-test AMQP types test.
+They send and receive messages via an AMQP broker which is assumed to be running
+at a well-known location/port.
+
+These are invoked on the command-line from the test program using the following
+command-line options:
+
+1. broker address (ip-addr:port)
+2. queue name
+3. AMQP type under test
+4. JSON list of stringified test values (Sender) "[\"val1\", \"val2\", ... ]"
+   or
+   A number indicating how many messages to expect (Receiver)
+
+Any errors/exceptions are sent to stderr.
+The Sender does not print anything onto stdout.
+The Receiver prints two items onto stdout:
+
+1. The AMQP type under test
+2. JSON list of stringified test values received (matching that sent to the
+   Sender)
+
+The top-level test program (amqp_types_test.py) will launch each shim as needed
+with its command-line parameters and will receive output from stdout and
+stderr. These outputs will be checked to determine pass/fail. By default, the
+JSON sequende sent to the Sender must match the JSON sequence received from the
+receiver for the test to pass.
+
+The AMQP types and test values are all set within the top-level test program.
+
+Required modules
+----------------
+ rhea (https://github.com/grs/rhea.git)
+ node-uuid (via npm)
+
+NOTE: For the tests to pass, a patch is needed for the Rhea javascript client.
+This patch is contained in the file rhea-long-ulong.patch. It addresses a
+problem in Javascript in which numbers are represented internally as floating
+point numbers. This means that the precision available for representing large
+integer values is limited, and can round up or down to a representation that
+is not exact and can cause tests to fail. (For example, the unsigned long
+number 0x7fffffffffffffff will round to 0x8000000000000000). The patch allows
+numbers stored in Buffer objects to be used to set a long or ulong values in
+message bodies, which avoids this problem. If the patch is not applied, the
+long and ulong tests will fail. (If this patch is accepted into Rhea, then
+this file will be removed.)
+

http://git-wip-us.apache.org/repos/asf/qpid-interop-test/blob/156ac5a5/shims/rhea-js/amqp_types_test/Receiver.js
----------------------------------------------------------------------
diff --git a/shims/rhea-js/amqp_types_test/Receiver.js b/shims/rhea-js/amqp_types_test/Receiver.js
new file mode 100755
index 0000000..dc4ec57
--- /dev/null
+++ b/shims/rhea-js/amqp_types_test/Receiver.js
@@ -0,0 +1,196 @@
+#!/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.
+ *
+ */
+
+"use strict";
+
+//Check if the environment is Node.js and if not log an error and exit.
+if (typeof process !== 'object' || typeof require !== 'function') {
+    console.error("Receiver.js should be run in Node.js");
+    process.exit(-1);
+}
+
+var uuid = require("node-uuid");
+
+var args = process.argv.slice(2);
+if (args.length !== 4) {
+    console.error("ERROR: Sender.js needs 4 arguments:");
+    console.error(" 1. Broker address (ip-addr:port)");
+    console.error(" 2. Queue name");
+    console.error(" 3. AMQP type");
+    console.error(" 4. Number of expected values");
+    process.exit(-1);
+}
+
+function Receiver(brokerAddr, brokerPort, queueName, amqpType, numTestValues) {
+    this.amqpType = amqpType;
+    this.received = 0;
+    this.expected = numTestValues;
+    this.receivedValueList = [];
+    this.container = require('rhea');
+
+    this.container.connect({'host':brokerAddr, 'port':brokerPort}).open_receiver(queueName);
+
+    this.processMessage = function(msgBody) {
+//        console.log("processMessage: amqpType=" + this.amqpType + "; msgBody=" + msgBody);
+        switch(this.amqpType) {
+        case "null": this.receivedValueList.push(this.decodeNull(msgBody)); break;
+        case "boolean": this.receivedValueList.push(this.decodeBoolean(msgBody)); break;
+        case "ubyte":
+        case "ushort":
+        case "uint":
+        case "ulong":
+        case "decimal32":
+        case "decimal64":
+        case "decimal128":
+        case "timestamp": this.receivedValueList.push(this.decodeUnsigned(msgBody)); break;
+        case "byte":
+        case "short":
+        case "int":
+        case "long": this.receivedValueList.push(this.decodeSigned(msgBody)); break;
+        case "float": this.receivedValueList.push(this.decodeFloat(msgBody)); break;
+        case "double": this.receivedValueList.push(this.decodeDouble(msgBody)); break;
+        case "char": this.receivedValueList.push(this.decodeChar(msgBody)); break;
+        case "uuid": this.receivedValueList.push(this.decodeUuid(msgBody)); break;
+        case "binary": this.receivedValueList.push(this.decodeBinary(msgBody)); break;
+        case "string": this.receivedValueList.push(this.decodeString(msgBody)); break;
+        case "symbol": this.receivedValueList.push(this.decodeSymbol(msgBody)); break;
+        case "list": this.receivedValueList.push(this.decodeList(msgBody)); break;
+        case "map": this.receivedValueList.push(this.decodeMap(msgBody)); break;
+        case "array": this.receivedValueList.push(this.decodeArray(msgBody)); break;
+        default: throw "Unknown AMQP type: " + this.amqpType;
+        }
+    };
+
+    this.decodeNull = function (msgBody) {
+        return "None";
+    };
+
+    this.decodeBoolean = function(msgBody) {
+        return msgBody ? "True" : "False";
+    };
+
+    this.decodeUnsigned = function(msgBody) {
+        return "0x" +   msgBody.toString(Buffer.isBuffer(msgBody) ? 'hex' : 16);
+    };
+
+    this.decodeSigned = function(msgBody) {
+        if (Buffer.isBuffer(msgBody)) {
+            if (msgBody[0] & 0x80) { // sign bit set
+                msgBody[0] &= 0x80;
+                return "-0x" + msgBody.toString('hex');
+            } else {
+                return "0x" + msgBody.toString('hex');
+            }
+        } else {
+            if (msgBody < 0) {
+                return "-0x" + (-msgBody).toString(16);
+            } else {
+                return "0x" + msgBody.toString(16);
+            }
+        }
+    };
+
+    this.decodeFloat = function(msgBody) {
+        // Buffer.writeFloatBE() does not support -NaN (ignores sign)
+        var buf = new Buffer(4);
+        buf.writeFloatBE(msgBody);
+        return "0x" + buf.toString('hex');
+    };
+
+    this.decodeDouble = function(msgBody) {
+        // Buffer.writeDoubleBE() does not support -NaN (ignores sign)
+        var buf = new Buffer(8);
+        buf.writeDoubleBE(msgBody);
+        return "0x" + buf.toString('hex');
+    };
+
+    // UTF32LE char per AMQP spec
+    this.decodeChar = function(msgBody) {
+        if (Buffer.isBuffer(msgBody)) {
+            if (msgBody[0] === 0 && msgBody[1] === 0 && msgBody[2] === 0 && msgBody[3] >= 32 && msgBody[3] <= 126) {
+                // Printable single ASCII char - return just the char
+                return String.fromCharCode(msgBody[3]);
+            }
+            return "0x" + this.buffer2HexString(msgBody, false);
+        } else {
+            throw "AMQP type char message body is not Buffer";
+        }
+    };
+
+    this.decodeUuid = function(msgBody) {
+        return uuid.unparse(msgBody);
+    };
+
+    this.decodeBinary = function(msgBody) {
+        return msgBody.toString();
+    };
+
+    this.decodeString = function(msgBody) {
+        return msgBody;
+    };
+
+    this.decodeSymbol = function(msgBody) {
+        return msgBody;
+    };
+
+    this.decodeList = function(msgBody) {
+        return msgBody; // TODO: decode list
+    };
+
+    this.decodeMap = function(msgBody) {
+        return msgBody; // TODO: decode map
+    };
+
+    this.decodeArray = function(msgBody) {
+        return msgBody; // TODO: decode array
+    };
+
+    this.buffer2HexString = function(buff, pad) {
+        var hexStr = "";
+        var first = true;
+        for (var i = 0; i < buff.length; i++) {
+            if (pad || buff[i] > 0) {
+                hexStr += (pad || !first) ? ("0" + buff[i].toString(16)).substr(-2) : buff[i].toString(16);
+                first = false;
+            }
+        }
+        return hexStr;
+    };
+
+    this.printResult = function() {
+        console.log(this.amqpType);
+        console.log(JSON.stringify(this.receivedValueList));
+    };
+
+    this.container.on('message', function (context) {
+        if (receiver.expected === 0 || receiver.received < receiver.expected) {
+            receiver.processMessage(context.message.body);
+            if (++receiver.received === receiver.expected) {
+                context.receiver.detach();
+                context.connection.close();
+                receiver.printResult();
+            }
+        }
+    });
+}
+
+var colonPos = args[0].indexOf(":");
+var receiver = new Receiver(args[0].slice(0, colonPos), args[0].slice(colonPos+1), args[1], args[2], parseInt(args[3], 10));

http://git-wip-us.apache.org/repos/asf/qpid-interop-test/blob/156ac5a5/shims/rhea-js/amqp_types_test/Sender.js
----------------------------------------------------------------------
diff --git a/shims/rhea-js/amqp_types_test/Sender.js b/shims/rhea-js/amqp_types_test/Sender.js
new file mode 100755
index 0000000..475d6b5
--- /dev/null
+++ b/shims/rhea-js/amqp_types_test/Sender.js
@@ -0,0 +1,340 @@
+#!/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.
+ *
+ */
+
+"use strict";
+
+var uuid = require("node-uuid");
+var amqp_types = require('rhea/lib/types.js');
+
+//Check if the environment is Node.js and if not log an error and exit.
+if (typeof process !== 'object' || typeof require !== 'function') {
+    console.error("Receiver.js should be run in Node.js");
+    process.exit(-1);
+}
+
+var args = process.argv.slice(2);
+if (args.length !== 4) {
+    console.error("ERROR: Sender.js needs 4 arguments:");
+    console.error(" 1. Broker address (ip-addr:port)");
+    console.error(" 2. Queue name");
+    console.error(" 3. AMQP type");
+    console.error(" 4. JSON string containing test values");
+    process.exit(-1);
+}
+
+var AmqpArrayTypes = {
+    "boolean": 0x56,
+    "ubyte":   0x50,
+    "ushort":  0x60,
+    "uint":    0x70,
+    "ulong":   0x80,
+    "byte":    0x51,
+    "short":   0x61,
+    "int":     0x71,
+    "long":    0x81,
+    "float":   0x72,
+    "double":  0x82,
+    "decimal32":  0x74,
+    "decimal64":  0x84,
+    "decimal128": 0x94,
+    "char":       0x73,
+    "timestamp":  0x83,
+    "uuid":       0x98,
+    "binary":     0xb0,
+    "string":     0xb1,
+    "symbol":     0xb3
+};
+
+function Sender(brokerAddr, brokerPort, queueName, amqpType, testValues) {
+    this.amqpType = amqpType;
+    this.testValues = testValues;
+    this.sent = 0;
+    this.confirmed = 0;
+    this.total = testValues.length;
+    this.container = require('rhea');
+
+    this.connection = this.container.connect({'host':brokerAddr, 'port':brokerPort}); //.open_sender(queueName);
+    this.connection.open_sender(queueName);
+
+    this.createMessage = function(testValue) {
+        //console.log("createMessage: amqpType=" + this.amqpType + "; testValue=" + testValue);
+        switch(this.amqpType) {
+        case "null": return {body: this.encodeNull(testValue)};
+        case "boolean": return {body: amqp_types.wrap_boolean(this.encodeBoolean(testValue))};
+        case "ubyte": return {body: amqp_types.wrap_ubyte(this.encodeUbyte(testValue))};
+        case "ushort": return {body: amqp_types.wrap_ushort(this.encodeUshort(testValue))};
+        case "uint": return {body: amqp_types.wrap_uint(this.encodeUint(testValue))};
+        case "ulong": return {body: amqp_types.wrap_ulong(this.encodeUlong(testValue))};
+        case "byte": return {body: amqp_types.wrap_byte(this.encodeByte(testValue))};
+        case "short": return {body: amqp_types.wrap_short(this.encodeShort(testValue))};
+        case "int": return {body: amqp_types.wrap_int(this.encodeInt(testValue))};
+        case "long": return {body: amqp_types.wrap_long(this.encodeLong(testValue))};
+        case "float": return {body: amqp_types.wrap_float(this.encodeFloat(testValue))};
+        case "double": return {body: amqp_types.wrap_double(this.encodeDouble(testValue))};
+        case "decimal32": return {body: new amqp_types.Decimal32(this.encodeDecimal32(testValue))};
+        case "decimal64": return {body: new amqp_types.Decimal64(this.encodeDecimal64(testValue))};
+        case "decimal128": return {body: new amqp_types.Decimal128(this.encodeDecimal128(testValue))};
+        case "char": return {body: new amqp_types.CharUTF32(this.encodeChar(testValue))};
+        case "timestamp": return {body: amqp_types.wrap_timestamp(this.encodeTimestamp(testValue))};
+        case "uuid": return {body: new amqp_types.Uuid(this.encodeUuid(testValue))};
+        case "binary": return {body: amqp_types.wrap_binary(this.encodeBinary(testValue))};
+        case "string": return {body: amqp_types.wrap_string(this.encodeString(testValue))};
+        case "symbol": return {body: amqp_types.wrap_symbol(this.encodeSymbol(testValue))};
+        case "list": return {body: amqp_types.wrap_list(this.encodeList(testValue))};
+        case "map": return {body: amqp_types.wrap_map(this.encodeMap(testValue))};
+        case "array": return {body: amqp_types.wrap_array(this.encodeArray(testValue.slice(1), AmqpArrayTypes[testValue[0]]))};
+        default: throw "Unknown AMQP type: \"" + this.amqpType + "\"";
+        }
+    };
+
+    // static
+    this.encodeAmqpType = function(amqpType, testValue) {
+        switch(amqpType) {
+        case "null": return this.encodeNull(testValue);
+        case "boolean": return this.encodeBoolean(testValue);
+        case "ubyte": return this.encodeUbyte(testValue);
+        case "ushort": return this.encodeUshort(testValue);
+        case "uint": return this.encodeUint(testValue);
+        case "ulong": return this.encodeUlong(testValue);
+        case "byte": return this.encodeByte(testValue);
+        case "short": return this.encodeShort(testValue);
+        case "int": return this.encodeInt(testValue);
+        case "long": return this.encodeLong(testValue);
+        case "float": return this.encodeFloat(testValue);
+        case "double": return this.encodeDouble(testValue);
+        case "decimal32": return this.encodeDecimal32(testValue);
+        case "decimal64": return this.encodeDecimal64(testValue);
+        case "decimal128": return this.encodeDecimal128(testValue);
+        case "char": return this.encodeChar(testValue);
+        case "timestamp": return this.encodeTimestamp(testValue);
+        case "uuid": return this.encodeUuid(testValue);
+        case "binary": return this.encodeBinary(testValue);
+        case "string": return this.encodeString(testValue);
+        case "symbol": return this.encodeSymbol(testValue);
+        case "list": return this.encodeList(testValue);
+        case "map": return this.encodeMap(testValue);
+        case "array": return this.encodeArray(testValue);
+        default: throw "Unknown AMQP type: \"" + this.amqpType + "\"";
+        }
+    };
+
+    // These functions encode the incoming JSON string representation
+    // of the test values into appropriate test values for the message
+    // bodies.
+
+    this.encodeNull = function(testValue) {
+        if (testValue === "None") return null;
+        this.handleEncodeError("null", testValue);
+    };
+
+    this.encodeBoolean = function(testValue) {
+        if (testValue === "True") return true;
+        if (testValue === "False") return false;
+        this.handleEncodeError("boolean", testValue);
+    };
+
+    this.encodeUbyte = function(testValue) {
+        try { return parseInt(testValue, 16); }
+        catch (err) { this.handleEncodeError("ubyte", testValue); }
+    };
+
+    this.encodeUshort = function(testValue) {
+        try { return parseInt(testValue, 16); }
+        catch (err) { this.handleEncodeError("ushort", testValue); }
+    };
+
+    this.encodeUint = function(testValue) {
+        try { return parseInt(testValue, 16); }
+        catch (err) { this.handleEncodeError("uint", testValue); }
+    };
+
+    this.encodeUlong = function(testValue) {
+        try { return new Buffer(this.hexString2ByteArray(testValue.slice(2), 8)); }
+        catch (err) { this.handleEncodeError("ulong", testValue, err); }
+    };
+
+    this.encodeByte = function(testValue) {
+        try { return parseInt(testValue, 16); }
+        catch (err) { this.handleEncodeError("byte", testValue); }
+    };
+
+    this.encodeShort = function(testValue) {
+        try { return parseInt(testValue, 16); }
+        catch (err) { this.handleEncodeError("short", testValue); }
+    };
+
+    this.encodeInt = function(testValue) {
+        try { return parseInt(testValue, 16); }
+        catch (err) { this.handleEncodeError("int", testValue); }
+    };
+
+    this.encodeLong = function(testValue) {
+        try {
+            var negFlag = testValue.charAt(0) === "-";
+            var ba = this.hexString2ByteArray(testValue.slice(negFlag ? 3 : 2), 8);
+            if (negFlag) this.byteArray2sCompl(ba);
+            return new Buffer(ba);
+        }
+        catch (err) { this.handleEncodeError("long", testValue); }
+    };
+
+    this.encodeFloat = function(testValue) {
+        // NOTE: Buffer.readFloatBE() does not support -NaN (ignores sign)
+//        var buf = new Buffer(testValue.substr(2), "hex");
+//        return buf.readFloatBE(0);
+        // This method gets the -NaN with sign onto the wire correctly.
+        var dv = new DataView(new ArrayBuffer(4));
+        dv.setUint32(0, parseInt(testValue.substr(2), 16));
+        return dv.getFloat32(0);
+    };
+
+    this.encodeDouble = function(testValue) {
+        // NOTE: Buffer.readDoubleBE() does not support -NaN (ignores sign)
+//        var buf = new Buffer(testValue.substr(2), "hex");
+//        return buf.readDoubleBE(0);
+        // This method gets the -NaN with sign onto the wire correctly.
+        var dv = new DataView(new ArrayBuffer(8));
+        dv.setUint32(0, parseInt(testValue.substr(2, 8), 16));
+        dv.setUint32(4, parseInt(testValue.substr(10), 16));
+        return dv.getFloat64(0);
+    };
+
+    this.encodeDecimal32 = function(testValue) {
+        try { return new Buffer(this.hexString2ByteArray(testValue.slice(2), 4)); }
+        catch (err) { this.handleEncodeError("decimal32", testValue, err); }
+    };
+
+    this.encodeDecimal64 = function(testValue) {
+        try { return new Buffer(this.hexString2ByteArray(testValue.slice(2), 8)); }
+        catch (err) { this.handleEncodeError("decimal64", testValue, err); }
+    };
+
+    this.encodeDecimal128 = function(testValue) {
+        try { return new Buffer(this.hexString2ByteArray(testValue.slice(2), 16)); }
+        catch (err) { this.handleEncodeError("decimal128", testValue, err); }
+    };
+
+    this.encodeChar = function(testValue) {
+        var val = null;
+        try {
+            if (testValue.length === 1) { // Single char format 'a'
+                val = [0, 0, 0, testValue.charCodeAt(0)];
+            } else { // Hex format '0xNNNN'
+                val = this.hexString2ByteArray(testValue.slice(2), 4);
+            }
+        }
+        catch (err) { this.handleEncodeError("char", testValue); }
+        return new Buffer(val);
+    };
+
+    this.encodeTimestamp = function(testValue) {
+        try { return parseInt(testValue, 16); }
+        catch (err) { this.handleEncodeError("timestamp", testValue); }
+    };
+
+    this.encodeUuid = function(testValue) {
+        var buff = new Buffer(16);
+        try { uuid.parse(testValue, buff); }
+        catch (err) { this.handleEncodeError("uuid", testValue); }
+        return buff;
+    };
+
+    this.encodeBinary = function(testValue) {
+        return testValue;
+    };
+
+    this.encodeString = function(testValue) {
+        return testValue;
+    };
+
+    this.encodeSymbol = function(testValue) {
+        return testValue;
+    };
+
+    this.encodeList = function(testValue) {
+        return testValue; // TODO: encode list
+    };
+
+    this.encodeMap = function(testValue) {
+        return testValue; // TODO: encode map
+    };
+
+    // testValue format for arrays: ['type', 'val1', 'val2', ... ]
+    this.encodeArray = function(testValue) {
+        return testValue.slice(1); // TODO: encode array
+    };
+
+    this.handleEncodeError = function(amqpType, testValue, err) {
+        var errStr = "Invalid string value for type " + amqpType + ": \"" + testValue + "\"";
+        if (err) {
+            errStr += " (" + err.message + ")";
+        }
+        throw new Error(errStr);
+    };
+
+    // NOTE: hexStr must not include '0x' prefix
+    this.hexString2ByteArray = function(hexStr, len) {
+        var result = [];
+        var oddFlag = hexStr.length !== 2*Math.round(hexStr.length/2.0); // odd number of hex digits
+        for (var i = len - 1; i >= 0; i--) {
+            if (2 * i < hexStr.length) {
+                var ssIndex = oddFlag ? 0 : hexStr.length - 2*(i+1);
+                var ssLen = oddFlag ? 1 : 2;
+                result.push(parseInt(hexStr.substr(ssIndex, ssLen), 16));
+                oddFlag = false;
+            } else {
+                result.push(0); // leading zeros
+            }
+        }
+        return result;
+    };
+
+    this.byteArray2sCompl = function(ba) {
+        var carry = 1;
+        for (var i=ba.length-1; i>=0; i--) {
+            var val = (ba[i] ^ 0xff) + carry;
+            ba[i] = val & 0xff;
+            carry = val >> 8;
+        }
+    };
+
+    this.container.on('sendable', function (context) {
+        while (context.sender.sendable() && sender.sent < sender.total) {
+            for (var i=0; i<sender.testValues.length; ++i) {
+                context.sender.send(sender.createMessage(sender.testValues[i]));
+                sender.sent++;
+            }
+        }
+    });
+
+    this.container.on('accepted', function (context) {
+        if (++sender.confirmed === sender.total) {
+            context.connection.close();
+        }
+    });
+
+    this.container.on('disconnected', function (context) {
+        sender.sent = sender.confirmed;
+    });
+}
+
+var colonPos = args[0].indexOf(":");
+var sender = new Sender(args[0].slice(0, colonPos), args[0].slice(colonPos+1), args[1], args[2], JSON.parse(args[3]));

http://git-wip-us.apache.org/repos/asf/qpid-interop-test/blob/156ac5a5/shims/rhea-js/amqp_types_test/rhea-long-ulong.patch
----------------------------------------------------------------------
diff --git a/shims/rhea-js/amqp_types_test/rhea-long-ulong.patch b/shims/rhea-js/amqp_types_test/rhea-long-ulong.patch
new file mode 100644
index 0000000..c7497c3
--- /dev/null
+++ b/shims/rhea-js/amqp_types_test/rhea-long-ulong.patch
@@ -0,0 +1,50 @@
+diff --git lib/types.js lib/types.js
+index 985607f..f1d5514 100644
+--- lib/types.js
++++ lib/types.js
+@@ -270,6 +270,12 @@ function is_one_of(o, typelist) {
+     }
+     return false;
+ }
++function buffer_zero(b, len, neg) {
++    for (var i = 0; i < len && i < b.length; i++) {
++        if (b[i] !== (neg ? 0xff : 0)) return false;
++    }
++    return true;
++}
+ types.is_ulong = function(o) {
+     return is_one_of(o, [types.Ulong, types.Ulong0, types.SmallUlong]);
+ };
+@@ -290,8 +296,13 @@ types.wrap_boolean = function(v) {
+     return v ? new types.True() : new types.False();
+ };
+ types.wrap_ulong = function(l) {
+-    if (l === 0) return new types.Ulong0();
+-    else return l > 255 ? new types.Ulong(l) : new types.SmallUlong(l);
++    if (Buffer.isBuffer(l)) {
++        if (buffer_zero(l, 8, false)) return new types.Ulong0();
++        return buffer_zero(l, 7, false) ? new types.SmallUlong(l[7]) : new types.Ulong(l);
++    } else {
++        if (l === 0) return new types.Ulong0();
++        else return l > 255 ? new types.Ulong(l) : new types.SmallUlong(l);
++    }
+ };
+ types.wrap_uint = function(l) {
+     if (l === 0) return new types.Uint0();
+@@ -304,7 +315,15 @@ types.wrap_ubyte = function(l) {
+     return new types.Ubyte(l);
+ };
+ types.wrap_long = function(l) {
+-    return l > 127 || l < -128 ? new types.Long(l) : new types.SmallLong(l);
++    if (Buffer.isBuffer(l)) {
++        var negFlag = (l[0] & 0x80) !== 0;
++        if (buffer_zero(l, 7, negFlag) && (l[7] & 0x80) === (negFlag ? 0x80 : 0)) {
++            return new types.SmallLong(negFlag ? -((l[7] ^ 0xff) + 1) : l[7]);
++        }
++        return new types.Long(l);
++    } else {
++        return l > 127 || l < -128 ? new types.Long(l) : new types.SmallLong(l);
++    }
+ };
+ types.wrap_int = function(l) {
+     return l > 127 || l < -128 ? new types.Int(l) : new types.SmallInt(l);

http://git-wip-us.apache.org/repos/asf/qpid-interop-test/blob/156ac5a5/src/python/qpid_interop_test/amqp_types_test.py
----------------------------------------------------------------------
diff --git a/src/python/qpid_interop_test/amqp_types_test.py b/src/python/qpid_interop_test/amqp_types_test.py
index b876619..fbd2c0c 100755
--- a/src/python/qpid_interop_test/amqp_types_test.py
+++ b/src/python/qpid_interop_test/amqp_types_test.py
@@ -70,6 +70,7 @@ class AmqpPrimitiveTypes(TestTypeMap):
                   '0x1',
                   '0xff',
                   '0x100',
+                  '0x102030405',
                   '0x7fffffffffffffff',
                   '0x8000000000000000',
                   '0xffffffffffffffff'],
@@ -86,12 +87,14 @@ class AmqpPrimitiveTypes(TestTypeMap):
                 '0x0',
                 '0x7fffffff'],
         'long': ['-0x8000000000000000',
+                 '-0x102030405',
                  '-0x81',
                  '-0x80',
                  '-0x1',
                  '0x0',
                  '0x7f',
                  '0x80',
+                 '0x102030405',
                  '0x7fffffffffffffff'],
         # float and double: Because of difficulty with rounding of floating point numbers, we use the binary
         # representation instead which should be exact when comparing sent and received values.
@@ -110,7 +113,8 @@ class AmqpPrimitiveTypes(TestTypeMap):
                   #'0x7f800000', # +Infinity # PROTON-1149 - fails on RHEL7
                   #'0xff800000', # -Infinity # PROTON-1149 - fails on RHEL7
                   '0x7fc00000', # +NaN
-                  '0xffc00000'], # -NaN
+                  #'0xffc00000', # -NaN # Not supported in Javascript
+                 ],
         'double': ['0x0000000000000000', # 0.0
                    '0x8000000000000000', # -0.0
                    '0x400921fb54442eea', # pi (3.14159265359) positive decimal
@@ -126,7 +130,8 @@ class AmqpPrimitiveTypes(TestTypeMap):
                    '0x7ff0000000000000', # +Infinity
                    '0xfff0000000000000', # -Infinity
                    '0x7ff8000000000000', # +NaN
-                   '0xfff8000000000000'], # -NaN
+                   #'0xfff8000000000000', # -NaN # Not supported in Javascript
+                  ],
         # decimal32, decimal64, decimal128:
         # Until more formal support for decimal32, decimal64 and decimal128 are included in Python, we use
         # a hex format for basic tests, and treat the data as a binary blob.
@@ -140,12 +145,18 @@ class AmqpPrimitiveTypes(TestTypeMap):
                       '0xffefffffffffffff'],
         'decimal128': ['0x00000000000000000000000000000000',
                        '0xff0102030405060708090a0b0c0d0e0f'],
-        'char': [u'a',
-                 u'Z',
-                 u'0x1',
+        'char': [u' ', # single ASCII chars
+                 u'0',
+                 u'A',
+                 u'z',
+                 u'~',
+                 u'0x1', # Hex representation
                  u'0x7f',
                  u'0x16b5', # Rune 'G'
-                 u'0x10ffff'],
+                 u'0x10203',
+                 u'0x10ffff',
+                 #u'0x12345678' # 32-bit number, not real char # Disabled until Python can handle it
+                ],
         # timestamp: Must be in milliseconds since the Unix epoch
         'timestamp': ['0x0',
                       '0x%x' % int(mktime((2000, 1, 1, 0, 0, 0, 5, 1, 0))*1000),
@@ -169,7 +180,8 @@ class AmqpPrimitiveTypes(TestTypeMap):
                   ],
         'symbol': ['',
                    'myDomain.123',
-                   'domain.0123456789.' * 100],
+                   'domain.0123456789.' * 100,
+                  ],
         'list': [[],
                  ['ubyte:1', 'int:-2', 'float:3.14'],
                  ['string:a', 'string:b', 'string:c'],
@@ -200,35 +212,47 @@ class AmqpPrimitiveTypes(TestTypeMap):
                   'short:8',
                   'short:9'] * 10
                 ],
-        'map': [
-            # Enpty map
-            {},
-            # Map with string keys
-            {'string:one': 'ubyte:1',
-             'string:two': 'ushort:2'},
-            # Map with other AMQP simple types as keys
-            {'none:': 'string:None',
-             'string:None': 'none:',
-             'string:One': 'long:-1234567890',
-             'short:2': 'int:2',
-             'boolean:True': 'string:True',
-             'string:False': 'boolean:False',
-             #['string:AAA', 'ushort:5951']: 'string:list value',
-             #{'byte:-55': 'ubyte:200',
-             # 'boolean:True': 'string:Hello, world!'}: 'symbol:map.value',
-             #'string:list': [],
-             'string:map': {'char:A': 'int:1',
-                            'char:B': 'int:2'}},
-            ],
-        # TODO: Support all AMQP types in array (including keys)
-        #'array': [[],
-        #          [1, 2, 3],
-        #          ['Hello', 'world'],
-        #          [[1, 2, 3],
-        #           ['a', 'b', 'c'],
-        #           [2.3, 3.4, 4,5],
-        #           [True, False, True, True]]
-        #          ]
+        'map': [{}, # Enpty map
+                # Map with string keys
+                {'string:one': 'ubyte:1',
+                 'string:two': 'ushort:2'},
+                # Map with other AMQP simple types as keys
+                {'none:': 'string:None',
+                 'string:None': 'none:',
+                 'string:One': 'long:-1234567890',
+                 'short:2': 'int:2',
+                 'boolean:True': 'string:True',
+                 'string:False': 'boolean:False',
+                 #['string:AAA', 'ushort:5951']: 'string:list value',
+                 #{'byte:-55': 'ubyte:200',
+                 # 'boolean:True': 'string:Hello, world!'}: 'symbol:map.value',
+                 #'string:list': [],
+                 'string:map': {'char:A': 'int:1',
+                                'char:B': 'int:2'}},
+               ],
+        # array: Each array is constructed from the test values in this map. This list contains
+        # the keys to the array value types to be included in the test. See function create_test_arrays()
+        # for the top-level function that performs the array creation.
+        #'array': ['boolean',
+        #          'ubyte',
+        #          'ushort',
+        #          'uint',
+        #          'ulong',
+        #          'byte',
+        #          'short',
+        #          'int',
+        #          'long',
+        #          'float',
+        #          'double',
+        #          'decimal32',
+        #          'decimal64',
+        #          'decimal128',
+        #          'char',
+        #          'uuid',
+        #          'binary',
+        #          'string',
+        #          'symbol',
+        #         ],
         }
 
     # This section contains tests that should be skipped because of know issues that would cause the test to fail.
@@ -250,6 +274,31 @@ class AmqpPrimitiveTypes(TestTypeMap):
                    'double': {'apache-activemq-artemis': '-NaN is stripped of its sign: ENTMQ-1686'},
                   }
 
+    def create_array(self, amqp_type, repeat):
+        """
+        Create a single test array for a given AMQP type from the test values for that type. It can be optionally
+        repeated for greater number of elements.
+        """
+        test_array = [amqp_type]
+        for _ in range(repeat):
+            for val in self.TYPE_MAP[amqp_type]:
+                test_array.append(val)
+        return test_array
+
+    def create_test_arrays(self):
+        """ Method to synthesize the test arrays from the values used in the previous type tests """
+        test_arrays = []
+        for amqp_type in self.TYPE_MAP['array']:
+            test_arrays.append(self.create_array(amqp_type, 1))
+        print test_arrays
+        return test_arrays
+
+    def get_test_values(self, amqp_type):
+        """ Overload the parent method so that arrays can be synthesized rather than read directly """
+        if amqp_type == 'array':
+            return self.create_test_arrays()
+        return super(AmqpPrimitiveTypes, self).get_test_values(amqp_type)
+
 
 class AmqpTypeTestCase(unittest.TestCase):
     """
@@ -353,11 +402,17 @@ PROTON_PYTHON_RECEIVER_SHIM = path.join(QPID_INTEROP_TEST_HOME, 'shims', 'qpid-p
                                         'Receiver.py')
 PROTON_PYTHON_SENDER_SHIM = path.join(QPID_INTEROP_TEST_HOME, 'shims', 'qpid-proton-python', 'src', 'amqp_types_test',
                                       'Sender.py')
+PROTON_RHEAJS_RECEIVER_SHIM = path.join(QPID_INTEROP_TEST_HOME, 'shims', 'rhea-js', 'amqp_types_test',
+                                        'Receiver.js')
+PROTON_RHEAJS_SENDER_SHIM = path.join(QPID_INTEROP_TEST_HOME, 'shims', 'rhea-js', 'amqp_types_test',
+                                      'Sender.js')
 
 SHIM_MAP = {qpid_interop_test.shims.ProtonCppShim.NAME: \
                 qpid_interop_test.shims.ProtonCppShim(PROTON_CPP_SENDER_SHIM, PROTON_CPP_RECEIVER_SHIM),
             qpid_interop_test.shims.ProtonPythonShim.NAME: \
                 qpid_interop_test.shims.ProtonPythonShim(PROTON_PYTHON_SENDER_SHIM, PROTON_PYTHON_RECEIVER_SHIM),
+            qpid_interop_test.shims.RheaJsShim.NAME: \
+                qpid_interop_test.shims.RheaJsShim(PROTON_RHEAJS_SENDER_SHIM, PROTON_RHEAJS_RECEIVER_SHIM),
            }
 
 

http://git-wip-us.apache.org/repos/asf/qpid-interop-test/blob/156ac5a5/src/python/qpid_interop_test/shims.py
----------------------------------------------------------------------
diff --git a/src/python/qpid_interop_test/shims.py b/src/python/qpid_interop_test/shims.py
index 5d824ad..210f9b6 100644
--- a/src/python/qpid_interop_test/shims.py
+++ b/src/python/qpid_interop_test/shims.py
@@ -186,6 +186,15 @@ class ProtonCppShim(Shim):
         self.receive_params = [self.receiver_shim]
 
 
+class RheaJsShim(Shim):
+    """Shim for Rhea Javascript client"""
+    NAME = 'RheaJs'
+    def __init__(self, sender_shim, receiver_shim):
+        super(RheaJsShim, self).__init__(sender_shim, receiver_shim)
+        self.send_params = [self.sender_shim]
+        self.receive_params = [self.receiver_shim]
+
+
 class QpidJmsShim(Shim):
     """Shim for qpid-jms JMS client"""
     NAME = 'QpidJms'


---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@qpid.apache.org
For additional commands, e-mail: commits-help@qpid.apache.org