1
0
mirror of https://github.com/node-red/node-red.git synced 2023-10-10 13:36:53 +02:00

Messaging API support in Split/Join nodes

This commit is contained in:
Kunihiko Toumura 2020-11-04 21:43:20 +09:00
parent 15a600c763
commit 407cb3e7d5
2 changed files with 246 additions and 45 deletions

View File

@ -17,18 +17,18 @@
module.exports = function(RED) { module.exports = function(RED) {
"use strict"; "use strict";
function sendArray(node,msg,array) { function sendArray(node,msg,array,send) {
for (var i = 0; i < array.length-1; i++) { for (var i = 0; i < array.length-1; i++) {
msg.payload = array[i]; msg.payload = array[i];
msg.parts.index = node.c++; msg.parts.index = node.c++;
if (node.stream !== true) { msg.parts.count = array.length; } if (node.stream !== true) { msg.parts.count = array.length; }
node.send(RED.util.cloneMessage(msg)); send(RED.util.cloneMessage(msg));
} }
if (node.stream !== true) { if (node.stream !== true) {
msg.payload = array[i]; msg.payload = array[i];
msg.parts.index = node.c++; msg.parts.index = node.c++;
msg.parts.count = array.length; msg.parts.count = array.length;
node.send(RED.util.cloneMessage(msg)); send(RED.util.cloneMessage(msg));
node.c = 0; node.c = 0;
} }
else { node.remainder = array[i]; } else { node.remainder = array[i]; }
@ -67,7 +67,8 @@ module.exports = function(RED) {
} }
node.c = 0; node.c = 0;
node.buffer = Buffer.from([]); node.buffer = Buffer.from([]);
this.on("input", function(msg) { node.pendingDones = [];
this.on("input", function(msg, send, done) {
if (msg.hasOwnProperty("payload")) { if (msg.hasOwnProperty("payload")) {
if (msg.hasOwnProperty("parts")) { msg.parts = { parts:msg.parts }; } // push existing parts to a stack if (msg.hasOwnProperty("parts")) { msg.parts = { parts:msg.parts }; } // push existing parts to a stack
else { msg.parts = {}; } else { msg.parts = {}; }
@ -93,14 +94,23 @@ module.exports = function(RED) {
msg.payload = data.substring(pos,pos+node.splt); msg.payload = data.substring(pos,pos+node.splt);
msg.parts.index = node.c++; msg.parts.index = node.c++;
pos += node.splt; pos += node.splt;
node.send(RED.util.cloneMessage(msg)); send(RED.util.cloneMessage(msg));
}
if (count > 1) {
node.pendingDones.forEach(d => d());
node.pendingDones = [];
} }
node.remainder = data.substring(pos); node.remainder = data.substring(pos);
if ((node.stream !== true) || (node.remainder.length === node.splt)) { if ((node.stream !== true) || (node.remainder.length === node.splt)) {
msg.payload = node.remainder; msg.payload = node.remainder;
msg.parts.index = node.c++; msg.parts.index = node.c++;
node.send(RED.util.cloneMessage(msg)); send(RED.util.cloneMessage(msg));
node.pendingDones.forEach(d => d());
node.pendingDones = [];
done();
node.remainder = ""; node.remainder = "";
} else {
node.pendingDones.push(done);
} }
} }
else { else {
@ -115,7 +125,8 @@ module.exports = function(RED) {
a = msg.payload.split(node.splt); a = msg.payload.split(node.splt);
msg.parts.ch = node.splt; // pass the split char to other end for rejoin msg.parts.ch = node.splt; // pass the split char to other end for rejoin
} }
sendArray(node,msg,a); sendArray(node,msg,a,send);
done();
} }
} }
else if (Array.isArray(msg.payload)) { // then split array into messages else if (Array.isArray(msg.payload)) { // then split array into messages
@ -135,8 +146,9 @@ module.exports = function(RED) {
} }
msg.parts.index = i; msg.parts.index = i;
pos += node.arraySplt; pos += node.arraySplt;
node.send(RED.util.cloneMessage(msg)); send(RED.util.cloneMessage(msg));
} }
done();
} }
else if ((typeof msg.payload === "object") && !Buffer.isBuffer(msg.payload)) { else if ((typeof msg.payload === "object") && !Buffer.isBuffer(msg.payload)) {
var j = 0; var j = 0;
@ -152,10 +164,11 @@ module.exports = function(RED) {
msg.parts.key = p; msg.parts.key = p;
msg.parts.index = j; msg.parts.index = j;
msg.parts.count = l; msg.parts.count = l;
node.send(RED.util.cloneMessage(msg)); send(RED.util.cloneMessage(msg));
j += 1; j += 1;
} }
} }
done();
} }
else if (Buffer.isBuffer(msg.payload)) { else if (Buffer.isBuffer(msg.payload)) {
var len = node.buffer.length + msg.payload.length; var len = node.buffer.length + msg.payload.length;
@ -176,14 +189,23 @@ module.exports = function(RED) {
msg.payload = buff.slice(pos,pos+node.splt); msg.payload = buff.slice(pos,pos+node.splt);
msg.parts.index = node.c++; msg.parts.index = node.c++;
pos += node.splt; pos += node.splt;
node.send(RED.util.cloneMessage(msg)); send(RED.util.cloneMessage(msg));
}
if (count > 1) {
node.pendingDones.forEach(d => d());
node.pendingDones = [];
} }
node.buffer = buff.slice(pos); node.buffer = buff.slice(pos);
if ((node.stream !== true) || (node.buffer.length === node.splt)) { if ((node.stream !== true) || (node.buffer.length === node.splt)) {
msg.payload = node.buffer; msg.payload = node.buffer;
msg.parts.index = node.c++; msg.parts.index = node.c++;
node.send(RED.util.cloneMessage(msg)); send(RED.util.cloneMessage(msg));
node.pendingDones.forEach(d => d());
node.pendingDones = [];
done();
node.buffer = Buffer.from([]); node.buffer = Buffer.from([]);
} else {
node.pendingDones.push(done);
} }
} }
else { else {
@ -210,23 +232,34 @@ module.exports = function(RED) {
while (pos > -1) { while (pos > -1) {
msg.payload = buff.slice(p,pos); msg.payload = buff.slice(p,pos);
msg.parts.index = node.c++; msg.parts.index = node.c++;
node.send(RED.util.cloneMessage(msg)); send(RED.util.cloneMessage(msg));
i++; i++;
p = pos+node.splt.length; p = pos+node.splt.length;
pos = buff.indexOf(node.splt,p); pos = buff.indexOf(node.splt,p);
} }
if (count > 1) {
node.pendingDones.forEach(d => d());
node.pendingDones = [];
}
if ((node.stream !== true) && (p < buff.length)) { if ((node.stream !== true) && (p < buff.length)) {
msg.payload = buff.slice(p,buff.length); msg.payload = buff.slice(p,buff.length);
msg.parts.index = node.c++; msg.parts.index = node.c++;
msg.parts.count = node.c++; msg.parts.count = node.c++;
node.send(RED.util.cloneMessage(msg)); send(RED.util.cloneMessage(msg));
node.pendingDones.forEach(d => d());
node.pendingDones = [];
} }
else { else {
node.buffer = buff.slice(p,buff.length); node.buffer = buff.slice(p,buff.length);
node.pendingDones.push(done);
}
if (node.buffer.length == 0) {
done();
} }
} }
} else { // otherwise drop the message.
done();
} }
//else { } // otherwise drop the message.
} }
}); });
} }
@ -264,16 +297,16 @@ module.exports = function(RED) {
} }
function reduceMessageGroup(node,msgs,exp,fixup,count,accumulator,done) { function reduceMessageGroup(node,msgInfos,exp,fixup,count,accumulator,done) {
var msg = msgs.shift(); var msgInfo = msgInfos.shift();
exp.assign("I", msg.parts.index); exp.assign("I", msgInfo.msg.parts.index);
exp.assign("N", count); exp.assign("N", count);
exp.assign("A", accumulator); exp.assign("A", accumulator);
RED.util.evaluateJSONataExpression(exp, msg, (err,result) => { RED.util.evaluateJSONataExpression(exp, msgInfo.msg, (err,result) => {
if (err) { if (err) {
return done(err); return done(err);
} }
if (msgs.length === 0) { if (msgInfos.length === 0) {
if (fixup) { if (fixup) {
fixup.assign("N", count); fixup.assign("N", count);
fixup.assign("A", result); fixup.assign("A", result);
@ -281,39 +314,43 @@ module.exports = function(RED) {
if (err) { if (err) {
return done(err); return done(err);
} }
node.send({payload: result}); msgInfo.send({payload: result});
done(); done();
}); });
} else { } else {
node.send({payload: result}); msgInfo.send({payload: result});
done(); done();
} }
} else { } else {
reduceMessageGroup(node,msgs,exp,fixup,count,result,done); reduceMessageGroup(node,msgInfos,exp,fixup,count,result,done);
} }
}); });
} }
function reduceAndSendGroup(node, group, done) { function reduceAndSendGroup(node, group, done) {
var is_right = node.reduce_right; var is_right = node.reduce_right;
var flag = is_right ? -1 : 1; var flag = is_right ? -1 : 1;
var msgs = group.msgs; var msgInfos = group.msgs;
const preservedMsgInfos = [...msgInfos];
try { try {
RED.util.evaluateNodeProperty(node.exp_init, node.exp_init_type, node, {}, (err,accum) => { RED.util.evaluateNodeProperty(node.exp_init, node.exp_init_type, node, {}, (err,accum) => {
var reduceExpression = node.reduceExpression; var reduceExpression = node.reduceExpression;
var fixupExpression = node.fixupExpression; var fixupExpression = node.fixupExpression;
var count = group.count; var count = group.count;
msgs.sort(function(x,y) { msgInfos.sort(function(x,y) {
var ix = x.parts.index; var ix = x.msg.parts.index;
var iy = y.parts.index; var iy = y.msg.parts.index;
if (ix < iy) {return -flag;} if (ix < iy) {return -flag;}
if (ix > iy) {return flag;} if (ix > iy) {return flag;}
return 0; return 0;
}); });
reduceMessageGroup(node, msgs,reduceExpression,fixupExpression,count,accum,(err,result) => { reduceMessageGroup(node, msgInfos,reduceExpression,fixupExpression,count,accum,(err,result) => {
if (err) { if (err) {
preservedMsgInfos.pop(); // omit last message to emit error message
preservedMsgInfos.forEach(mInfo => mInfo.done());
done(err); done(err);
return; return;
} else { } else {
preservedMsgInfos.forEach(mInfo => mInfo.done());
done(); done();
} }
}) })
@ -323,7 +360,8 @@ module.exports = function(RED) {
} }
} }
function reduceMessage(node, msg, done) { function reduceMessage(node, msgInfo, done) {
let msg = msgInfo.msg;
if (msg.hasOwnProperty('parts')) { if (msg.hasOwnProperty('parts')) {
var parts = msg.parts; var parts = msg.parts;
var pending = node.pending; var pending = node.pending;
@ -344,7 +382,7 @@ module.exports = function(RED) {
if (parts.hasOwnProperty('count') && (group.count === undefined)) { if (parts.hasOwnProperty('count') && (group.count === undefined)) {
group.count = parts.count; group.count = parts.count;
} }
msgs.push(msg); msgs.push(msgInfo);
pending_count++; pending_count++;
var completeProcess = function(err) { var completeProcess = function(err) {
if (err) { if (err) {
@ -353,6 +391,13 @@ module.exports = function(RED) {
node.pending_count = pending_count; node.pending_count = pending_count;
var max_msgs = maxKeptMsgsCount(node); var max_msgs = maxKeptMsgsCount(node);
if ((max_msgs > 0) && (pending_count > max_msgs)) { if ((max_msgs > 0) && (pending_count > max_msgs)) {
Object.values(node.pending).forEach(group => {
group.msgs.forEach(mInfo => {
if (mInfo.msg._msgid !== msgInfo.msg._msgid) {
mInfo.done();
}
});
});
node.pending = {}; node.pending = {};
node.pending_count = 0; node.pending_count = 0;
done(RED._("join.too-many")); done(RED._("join.too-many"));
@ -368,7 +413,8 @@ module.exports = function(RED) {
completeProcess(); completeProcess();
} }
} else { } else {
node.send(msg); msgInfo.send(msg);
msgInfo.done();
done(); done();
} }
} }
@ -480,7 +526,9 @@ module.exports = function(RED) {
delete group.msg.parts; delete group.msg.parts;
} }
delete group.msg.complete; delete group.msg.complete;
node.send(RED.util.cloneMessage(group.msg)); group.send(RED.util.cloneMessage(group.msg));
group.dones.forEach(f => f());
group.dones = [];
} }
var pendingMessages = []; var pendingMessages = [];
@ -489,10 +537,10 @@ module.exports = function(RED) {
// groups may overlap and cause unexpected results. The use of JSONata // groups may overlap and cause unexpected results. The use of JSONata
// means some async processing *might* occur if flow/global context is // means some async processing *might* occur if flow/global context is
// accessed. // accessed.
var processReduceMessageQueue = function(msg) { var processReduceMessageQueue = function(msgInfo) {
if (msg) { if (msgInfo) {
// A new message has arrived - add it to the message queue // A new message has arrived - add it to the message queue
pendingMessages.push(msg); pendingMessages.push(msgInfo);
if (activeMessage !== null) { if (activeMessage !== null) {
// The node is currently processing a message, so do nothing // The node is currently processing a message, so do nothing
// more with this message // more with this message
@ -508,22 +556,23 @@ module.exports = function(RED) {
// There are more messages to process. Get the next message and // There are more messages to process. Get the next message and
// start processing it. Recurse back in to check for any more // start processing it. Recurse back in to check for any more
var nextMsg = pendingMessages.shift(); var nextMsgInfo = pendingMessages.shift();
activeMessage = true; activeMessage = true;
reduceMessage(node, nextMsg, err => { reduceMessage(node, nextMsgInfo, err => {
if (err) { if (err) {
node.error(err,nextMsg); nextMsgInfo.done(err);//.error(err,nextMsg);
} }
activeMessage = null; activeMessage = null;
processReduceMessageQueue(); processReduceMessageQueue();
}) })
} }
this.on("input", function(msg) { this.on("input", function(msg, send, done) {
try { try {
var property; var property;
if (node.mode === 'auto' && (!msg.hasOwnProperty("parts")||!msg.parts.hasOwnProperty("id"))) { if (node.mode === 'auto' && (!msg.hasOwnProperty("parts")||!msg.parts.hasOwnProperty("id"))) {
node.warn("Message missing msg.parts property - cannot join in 'auto' mode") node.warn("Message missing msg.parts property - cannot join in 'auto' mode")
done();
return; return;
} }
@ -535,6 +584,7 @@ module.exports = function(RED) {
property = RED.util.getMessageProperty(msg,node.property); property = RED.util.getMessageProperty(msg,node.property);
} catch(err) { } catch(err) {
node.warn("Message property "+node.property+" not found"); node.warn("Message property "+node.property+" not found");
done();
return; return;
} }
} }
@ -557,7 +607,7 @@ module.exports = function(RED) {
propertyIndex = msg.parts.index; propertyIndex = msg.parts.index;
} }
else if (node.mode === 'reduce') { else if (node.mode === 'reduce') {
return processReduceMessageQueue(msg); return processReduceMessageQueue({msg, send, done});
} }
else { else {
// Use the node configuration to identify all of the group information // Use the node configuration to identify all of the group information
@ -578,9 +628,11 @@ module.exports = function(RED) {
if (inflight[partId].timeout) { if (inflight[partId].timeout) {
clearTimeout(inflight[partId].timeout); clearTimeout(inflight[partId].timeout);
} }
inflight[partId].dones.forEach(f => f());
delete inflight[partId] delete inflight[partId]
} }
return done();
return;
} }
if ((payloadType === 'object') && (propertyKey === null || propertyKey === undefined || propertyKey === "")) { if ((payloadType === 'object') && (propertyKey === null || propertyKey === undefined || propertyKey === "")) {
@ -591,6 +643,7 @@ module.exports = function(RED) {
if (msg.hasOwnProperty('complete')) { if (msg.hasOwnProperty('complete')) {
if (inflight[partId]) { if (inflight[partId]) {
inflight[partId].msg.complete = msg.complete; inflight[partId].msg.complete = msg.complete;
inflight[partId].send = send;
completeSend(partId); completeSend(partId);
} }
} }
@ -598,6 +651,7 @@ module.exports = function(RED) {
node.warn("Message missing key property 'msg."+node.key+"' - cannot add to object") node.warn("Message missing key property 'msg."+node.key+"' - cannot add to object")
} }
} }
done();
return; return;
} }
@ -608,7 +662,9 @@ module.exports = function(RED) {
payload:{}, payload:{},
targetCount:targetCount, targetCount:targetCount,
type:"object", type:"object",
msg:RED.util.cloneMessage(msg) msg:RED.util.cloneMessage(msg),
send: send,
dones: []
}; };
} }
else { else {
@ -617,7 +673,9 @@ module.exports = function(RED) {
payload:[], payload:[],
targetCount:targetCount, targetCount:targetCount,
type:payloadType, type:payloadType,
msg:RED.util.cloneMessage(msg) msg:RED.util.cloneMessage(msg),
send: send,
dones: []
}; };
if (payloadType === 'string') { if (payloadType === 'string') {
inflight[partId].joinChar = joinChar; inflight[partId].joinChar = joinChar;
@ -634,6 +692,7 @@ module.exports = function(RED) {
}, node.timer) }, node.timer)
} }
} }
inflight[partId].dones.push(done);
var group = inflight[partId]; var group = inflight[partId];
if (payloadType === 'buffer') { if (payloadType === 'buffer') {
@ -642,7 +701,7 @@ module.exports = function(RED) {
inflight[partId].bufferLen += property.length; inflight[partId].bufferLen += property.length;
} }
else { else {
node.error(RED._("join.errors.invalid-type",{error:(typeof property)}),msg); done(RED._("join.errors.invalid-type",{error:(typeof property)}));
return; return;
} }
} }
@ -676,6 +735,7 @@ module.exports = function(RED) {
} }
} }
group.msg = Object.assign(group.msg, msg); group.msg = Object.assign(group.msg, msg);
group.send = send;
var tcnt = group.targetCount; var tcnt = group.targetCount;
if (msg.hasOwnProperty("parts")) { tcnt = group.targetCount || msg.parts.count; } if (msg.hasOwnProperty("parts")) { tcnt = group.targetCount || msg.parts.count; }
if ((tcnt > 0 && group.currentCount >= tcnt) || msg.hasOwnProperty('complete')) { if ((tcnt > 0 && group.currentCount >= tcnt) || msg.hasOwnProperty('complete')) {
@ -683,6 +743,7 @@ module.exports = function(RED) {
} }
} }
catch(err) { catch(err) {
done(err);
console.log(err.stack); console.log(err.stack);
} }
}); });
@ -691,6 +752,7 @@ module.exports = function(RED) {
for (var i in inflight) { for (var i in inflight) {
if (inflight.hasOwnProperty(i)) { if (inflight.hasOwnProperty(i)) {
clearTimeout(inflight[i].timeout); clearTimeout(inflight[i].timeout);
inflight[i].dones.forEach(d => d());
} }
} }
}); });

View File

@ -1646,4 +1646,143 @@ describe('JOIN node', function() {
}); });
}); });
describe('messaging API', function() {
function mapiDoneSplitTestHelper(done, splt, spltType, stream, msgAndTimings) {
const completeNode = require("nr-test-utils").require("@node-red/nodes/core/common/24-complete.js");
const catchNode = require("nr-test-utils").require("@node-red/nodes/core/common/25-catch.js");
const flow = [
{ id: "splitNode1", type:"split", splt, spltType, stream, wires: [[]]},
{ id: "completeNode1", type: "complete", scope: ["splitNode1"], uncaught: false, wires: [["helperNode1"]] },
{ id: "catchNode1", type: "catch", scope: ["splitNode1"], uncaught: false, wires: [["helperNode1"]] },
{ id: "helperNode1", type: "helper", wires: [[]] }];
const numMsgs = msgAndTimings.length;
helper.load([splitNode, completeNode, catchNode], flow, function () {
const splitNode1 = helper.getNode("splitNode1");
const helperNode1 = helper.getNode("helperNode1");
RED.settings.nodeMessageBufferMaxLength = 2;
const t = Date.now();
let c = 0;
helperNode1.on("input", function (msg) {
msg.should.have.a.property('payload');
(Date.now() - t).should.be.approximately(msgAndTimings[msg.seq].avr, msgAndTimings[msg.seq].var);
c += 1;
if (c === numMsgs) {
done();
}
});
for (let i = 0; i < numMsgs; i++) {
setTimeout(function () { splitNode1.receive(msgAndTimings[i].msg); }, msgAndTimings[i].delay);
}
});
}
it('should call done() when message is sent (string)', function (done) {
mapiDoneSplitTestHelper(done, 2, "len", false, [
{ msg: { seq: 0, payload: "12345" }, delay: 0, avr: 0, var: 100 },
]);
});
it('should call done() when message is sent (array)', function (done) {
mapiDoneSplitTestHelper(done, 2, "len", false, [
{ msg: { seq: 0, payload: [0,1,2,3,4] }, delay: 0, avr: 0, var: 100 },
]);
});
it('should call done() when message is sent (object)', function (done) {
mapiDoneSplitTestHelper(done, 2, "len", false, [
{ msg: { seq: 0, payload: {a:1,b:2}}, delay: 0, avr: 0, var: 100 },
]);
});
it('should call done() when consolidated message is emitted (string, len)', function (done) {
mapiDoneSplitTestHelper(done, 5, "len", true, [
{ msg: { seq: 0, payload: "12"}, delay: 0, avr: 500, var: 100 },
{ msg: { seq: 1, payload: "34"}, delay: 200, avr: 500, var: 100 },
{ msg: { seq: 2, payload: "5"}, delay: 500, avr: 500, var: 100 }
]);
});
it('should call done() when consolidated message is emitted (Buffer, len)', function (done) {
mapiDoneSplitTestHelper(done, 5, "len", true, [
{ msg: { seq: 0, payload: Buffer.from("12")}, delay: 0, avr: 500, var: 100 },
{ msg: { seq: 1, payload: Buffer.from("34")}, delay: 200, avr: 500, var: 100 },
{ msg: { seq: 2, payload: Buffer.from("5")}, delay: 500, avr: 500, var: 100 }
]);
});
it('should call done() when consolidated message is emitted (Buffer, str)', function (done) {
mapiDoneSplitTestHelper(done, "5", "str", true, [
{ msg: { seq: 0, payload: Buffer.from("12")}, delay: 0, avr: 500, var: 100 },
{ msg: { seq: 1, payload: Buffer.from("34")}, delay: 200, avr: 500, var: 100 },
{ msg: { seq: 2, payload: Buffer.from("5")}, delay: 500, avr: 500, var: 100 }
]);
});
it('should call done() when consolidated message is emitted (Buffer, bin)', function (done) {
mapiDoneSplitTestHelper(done, "[53]", "bin", true, [
{ msg: { seq: 0, payload: Buffer.from("12")}, delay: 0, avr: 500, var: 100 },
{ msg: { seq: 1, payload: Buffer.from("34")}, delay: 200, avr: 500, var: 100 },
{ msg: { seq: 2, payload: Buffer.from("5")}, delay: 500, avr: 500, var: 100 }
]);
});
function mapiDoneJoinTestHelper(done, joinNodeSetting, msgAndTimings) {
const completeNode = require("nr-test-utils").require("@node-red/nodes/core/common/24-complete.js");
const catchNode = require("nr-test-utils").require("@node-red/nodes/core/common/25-catch.js");
const flow = [
{ ...joinNodeSetting, id: "joinNode1", type:"join", wires: [[]]},
{ id: "completeNode1", type: "complete", scope: ["joinNode1"], uncaught: false, wires: [["helperNode1"]] },
{ id: "catchNode1", type: "catch", scope: ["joinNode1"], uncaught: false, wires: [["helperNode1"]] },
{ id: "helperNode1", type: "helper", wires: [[]] }];
const numMsgs = msgAndTimings.length;
helper.load([joinNode, completeNode, catchNode], flow, function () {
const joinNode1 = helper.getNode("joinNode1");
const helperNode1 = helper.getNode("helperNode1");
RED.settings.nodeMessageBufferMaxLength = 3;
const t = Date.now();
let c = 0;
helperNode1.on("input", function (msg) {
msg.should.have.a.property('payload');
(Date.now() - t).should.be.approximately(msgAndTimings[msg.seq].avr, msgAndTimings[msg.seq].var);
c += 1;
if (c === numMsgs) {
done();
}
});
for (let i = 0; i < numMsgs; i++) {
setTimeout(function () { joinNode1.receive(msgAndTimings[i].msg); }, msgAndTimings[i].delay);
}
});
}
it('should call done() when all messages are joined', function (done) {
mapiDoneJoinTestHelper(done, {mode:"auto", timeout:1}, [
{ msg: {seq:0, payload:"A", parts:{id:1, type:"string", ch:",", index:0, count:3}}, delay:0, avr:500, var:100},
{ msg: {seq:1, payload:"B", parts:{id:1, type:"string", ch:",", index:1, count:3}}, delay:200, avr:500, var:100},
{ msg: {seq:2, payload:"C", parts:{id:1, type:"string", ch:",", index:2, count:3}}, delay:500, avr:500, var:100}
]);
});
it('should call done() when the node is reset', function (done) {
mapiDoneJoinTestHelper(done, {mode:"auto", timeout:1}, [
{ msg: {seq:0, payload:"A", parts:{id:1, type:"string", ch:",", index:0, count:3}}, delay:0, avr:500, var:100},
{ msg: {seq:1, payload:"B", parts:{id:1, type:"string", ch:",", index:1, count:3}}, delay:200, avr:500, var:100},
{ msg: {seq:2, payload:"dummy", reset: true, parts:{id:1}}, delay:500, avr:500, var:100}
]);
});
it('should call done() when timed out', function (done) {
mapiDoneJoinTestHelper(done, {mode:"custom", joiner:",", build:"string", timeout:0.5}, [
{ msg: {seq:0, payload:"A"}, delay:0, avr:500, var:100},
{ msg: {seq:1, payload:"B"}, delay:200, avr:500, var:100},
]);
});
it('should call done() when all messages are reduced', function (done) {
mapiDoneJoinTestHelper(done, {mode:"reduce", reduceRight:false, reduceExp:"$A+payload", reduceInit:"0",
reduceInitType:"num", reduceFixup:undefined}, [
{ msg: {seq:0, payload:3, parts: {index:2, count:3, id:222}}, delay:0, avr:500, var:100},
{ msg: {seq:1, payload:2, parts: {index:1, count:3, id:222}}, delay:200, avr:500, var:100},
{ msg: {seq:2, payload:4, parts: {index:0, count:3, id:222}}, delay:500, avr:500, var:100}
]);
});
it('should call done() regardless of buffer overflow', function (done) {
mapiDoneJoinTestHelper(done, {mode:"reduce", reduceRight:false, reduceExp:"$A+payload", reduceInit:"0",
reduceInitType:"num", reduceFixup:undefined}, [
{ msg: {seq:0, payload:3, parts: {index:2, count:5, id:222}}, delay:0, avr:600, var:100},
{ msg: {seq:1, payload:2, parts: {index:1, count:5, id:222}}, delay:200, avr:600, var:100},
{ msg: {seq:2, payload:4, parts: {index:0, count:5, id:222}}, delay:400, avr:600, var:100},
{ msg: {seq:3, payload:1, parts: {index:3, count:5, id:222}}, delay:600, avr:600, var:100},
]);
});
});
}); });