@machinemetrics/io-adapter-lib 2.35.1 → 2.36.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +3 -0
- package/lib/config/branchPreprocessor.js +123 -0
- package/lib/config/engineConfigV2.js +5 -0
- package/lib/expressionService.js +6 -1
- package/lib/transform/branch.js +274 -0
- package/lib/transform/index.js +2 -0
- package/package.json +1 -1
- package/test/configFiles/transform/branch.yml +117 -0
- package/test/configFiles/transform/invalid-branch-else-only.yml +11 -0
- package/test/configFiles/transform/invalid-branch-elseif-no-condition.yml +14 -0
- package/test/configFiles/transform/invalid-branch-elseif-only.yml +12 -0
- package/test/configFiles/transform/invalid-branch-if-no-condition.yml +11 -0
- package/test/configFiles/transform/invalid-branch-unrelated-op.yml +18 -0
- package/test/transform/branch-invalid.test.js +57 -0
- package/test/transform/branch.test.js +357 -0
package/CHANGELOG.md
CHANGED
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const _ = require('lodash');
|
|
4
|
+
const ConfigError = require('./configError');
|
|
5
|
+
|
|
6
|
+
const BRANCH_KEYS = ['if', 'else-if', 'else'];
|
|
7
|
+
|
|
8
|
+
function isBranchKey(key) {
|
|
9
|
+
return BRANCH_KEYS.includes(key);
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
function getBranchKey(item) {
|
|
13
|
+
if (!_.isObject(item) || _.isArray(item)) {
|
|
14
|
+
return null;
|
|
15
|
+
}
|
|
16
|
+
const keys = _.keys(item);
|
|
17
|
+
if (keys.length !== 1) {
|
|
18
|
+
return null;
|
|
19
|
+
}
|
|
20
|
+
const key = keys[0];
|
|
21
|
+
return isBranchKey(key) ? key : null;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Preprocesses a variable's transform list to collapse consecutive if/else-if/else
|
|
26
|
+
* into a single branch transform. The user-facing YAML syntax allows:
|
|
27
|
+
*
|
|
28
|
+
* - if:
|
|
29
|
+
* - condition: this == 1
|
|
30
|
+
* - op1
|
|
31
|
+
* - else-if:
|
|
32
|
+
* - condition: this == 2
|
|
33
|
+
* - op1
|
|
34
|
+
* - else:
|
|
35
|
+
* - op1
|
|
36
|
+
*
|
|
37
|
+
* This gets rewritten to:
|
|
38
|
+
*
|
|
39
|
+
* - branch:
|
|
40
|
+
* - if: [...]
|
|
41
|
+
* - else-if: [...]
|
|
42
|
+
* - else: [...]
|
|
43
|
+
*/
|
|
44
|
+
function preprocessBranchTransforms(transformList, sectionPath) {
|
|
45
|
+
if (!_.isArray(transformList)) {
|
|
46
|
+
return transformList;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
const result = [];
|
|
50
|
+
let i = 0;
|
|
51
|
+
|
|
52
|
+
while (i < transformList.length) {
|
|
53
|
+
const key = getBranchKey(transformList[i]);
|
|
54
|
+
|
|
55
|
+
if (key !== 'if') {
|
|
56
|
+
result.push(transformList[i]);
|
|
57
|
+
i += 1;
|
|
58
|
+
} else {
|
|
59
|
+
const branchComponents = [];
|
|
60
|
+
let seenElse = false;
|
|
61
|
+
|
|
62
|
+
while (i < transformList.length) {
|
|
63
|
+
const currentKey = getBranchKey(transformList[i]);
|
|
64
|
+
|
|
65
|
+
if (currentKey === null) {
|
|
66
|
+
break;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
if (currentKey === 'if') {
|
|
70
|
+
if (branchComponents.length > 0) {
|
|
71
|
+
break;
|
|
72
|
+
}
|
|
73
|
+
const item = transformList[i];
|
|
74
|
+
const body = item[currentKey];
|
|
75
|
+
const condition = body[0];
|
|
76
|
+
const chainTransforms = preprocessBranchTransforms(body.slice(1), `${sectionPath}.${i}.if`);
|
|
77
|
+
branchComponents.push({ [currentKey]: [condition, ...chainTransforms] });
|
|
78
|
+
i += 1;
|
|
79
|
+
} else if (currentKey === 'else-if') {
|
|
80
|
+
if (branchComponents.length === 0) {
|
|
81
|
+
throw new ConfigError('else-if must follow if or else-if')
|
|
82
|
+
.atPath(sectionPath);
|
|
83
|
+
}
|
|
84
|
+
if (seenElse) {
|
|
85
|
+
throw new ConfigError('else-if cannot follow else')
|
|
86
|
+
.atPath(sectionPath);
|
|
87
|
+
}
|
|
88
|
+
const item = transformList[i];
|
|
89
|
+
const body = item[currentKey];
|
|
90
|
+
const condition = body[0];
|
|
91
|
+
const chainTransforms = preprocessBranchTransforms(body.slice(1), `${sectionPath}.${i}.else-if`);
|
|
92
|
+
branchComponents.push({ [currentKey]: [condition, ...chainTransforms] });
|
|
93
|
+
i += 1;
|
|
94
|
+
} else if (currentKey === 'else') {
|
|
95
|
+
if (branchComponents.length === 0) {
|
|
96
|
+
throw new ConfigError('else must follow if or else-if')
|
|
97
|
+
.atPath(sectionPath);
|
|
98
|
+
}
|
|
99
|
+
if (seenElse) {
|
|
100
|
+
throw new ConfigError('only one else allowed per branch')
|
|
101
|
+
.atPath(sectionPath);
|
|
102
|
+
}
|
|
103
|
+
seenElse = true;
|
|
104
|
+
const item = transformList[i];
|
|
105
|
+
const body = item[currentKey];
|
|
106
|
+
const chainTransforms = preprocessBranchTransforms(body, `${sectionPath}.${i}.else`);
|
|
107
|
+
branchComponents.push({ [currentKey]: chainTransforms });
|
|
108
|
+
i += 1;
|
|
109
|
+
} else {
|
|
110
|
+
break;
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
result.push({ branch: branchComponents });
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
return result;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
module.exports = {
|
|
122
|
+
preprocessBranchTransforms,
|
|
123
|
+
};
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
const _ = require('lodash');
|
|
4
4
|
const ConfigError = require('./configError');
|
|
5
5
|
const TransformConfigUtil = require('./transformConfigUtil');
|
|
6
|
+
const { preprocessBranchTransforms } = require('./branchPreprocessor');
|
|
6
7
|
|
|
7
8
|
class EngineConfig {
|
|
8
9
|
constructor(expressionService, config = {}, device = {}) {
|
|
@@ -48,6 +49,10 @@ class EngineConfig {
|
|
|
48
49
|
return variable;
|
|
49
50
|
});
|
|
50
51
|
|
|
52
|
+
this.config.variables = _.mapValues(this.config.variables, (variable, name) => {
|
|
53
|
+
return preprocessBranchTransforms(variable, `variables.${name}`);
|
|
54
|
+
});
|
|
55
|
+
|
|
51
56
|
_.each(this.getExpressions(this.config), name => expressionService.addExpression(name));
|
|
52
57
|
_.each(this.getStringExpressions(this.config), name => expressionService.addStringExpression(name));
|
|
53
58
|
}
|
package/lib/expressionService.js
CHANGED
|
@@ -259,7 +259,7 @@ class ExpressionService {
|
|
|
259
259
|
if (tokens) {
|
|
260
260
|
const first = tokens[0];
|
|
261
261
|
if (first === 'this' || this.channelsLookup[first]) {
|
|
262
|
-
|
|
262
|
+
const nested = _.reduceRight(_.tail(tokens), (sub, item) => {
|
|
263
263
|
if (_.isNumber(item)) {
|
|
264
264
|
return [
|
|
265
265
|
..._.times(Math.max(0, item - 1), () => 0),
|
|
@@ -268,6 +268,11 @@ class ExpressionService {
|
|
|
268
268
|
}
|
|
269
269
|
return { [item]: sub };
|
|
270
270
|
}, 0);
|
|
271
|
+
if (_.isArray(nested) || _.isArray(accum[first])) {
|
|
272
|
+
accum[first] = nested;
|
|
273
|
+
} else {
|
|
274
|
+
accum[first] = _.merge(accum[first] || {}, nested);
|
|
275
|
+
}
|
|
271
276
|
} else {
|
|
272
277
|
accum[first] = 0;
|
|
273
278
|
}
|
|
@@ -0,0 +1,274 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const _ = require('lodash');
|
|
4
|
+
const TransformState = require('./transformState');
|
|
5
|
+
|
|
6
|
+
class BranchChain {
|
|
7
|
+
constructor(chain) {
|
|
8
|
+
this.chain = chain;
|
|
9
|
+
this.opResult = null;
|
|
10
|
+
|
|
11
|
+
const end = this.chain.chainEnd();
|
|
12
|
+
end.on('update', (value) => {
|
|
13
|
+
this.opResult = value;
|
|
14
|
+
});
|
|
15
|
+
end.on('unavailable', () => {
|
|
16
|
+
this.opResult = null;
|
|
17
|
+
});
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
update(context, value, time) {
|
|
21
|
+
this.chain.update(context, value, time);
|
|
22
|
+
return this.opResult;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
probe(context, time) {
|
|
26
|
+
this.chain.probe(context, time);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
probeOrForward(context, time) {
|
|
30
|
+
this.chain.probeOrForward(context, time);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
setUnavailable(context, time) {
|
|
34
|
+
this.chain.setUnavailable(context, time);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Conditional branching transform. Evaluates conditions in sequence and runs the first
|
|
40
|
+
* matching branch's transform chain. Analogous to if/else-if/else.
|
|
41
|
+
*
|
|
42
|
+
* Config format (after preprocessing):
|
|
43
|
+
* - branch:
|
|
44
|
+
* - if:
|
|
45
|
+
* - condition: this == 1 or x > y
|
|
46
|
+
* - op1
|
|
47
|
+
* - op2
|
|
48
|
+
* - else-if:
|
|
49
|
+
* - condition: this == 2
|
|
50
|
+
* - op1
|
|
51
|
+
* - else:
|
|
52
|
+
* - op1
|
|
53
|
+
*/
|
|
54
|
+
class BranchFilter extends TransformState {
|
|
55
|
+
constructor({ engine, args: { conditions, branchChains, elseChain } }, builder) {
|
|
56
|
+
super();
|
|
57
|
+
|
|
58
|
+
this.engine = engine;
|
|
59
|
+
this.conditions = conditions;
|
|
60
|
+
this.branchChains = _.map(branchChains, (transforms) => {
|
|
61
|
+
const chain = builder.createTransformChain(engine, transforms);
|
|
62
|
+
return new BranchChain(chain);
|
|
63
|
+
});
|
|
64
|
+
this.elseChain = elseChain
|
|
65
|
+
? new BranchChain(builder.createTransformChain(engine, elseChain))
|
|
66
|
+
: null;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
static op = 'branch';
|
|
70
|
+
|
|
71
|
+
static create(args) {
|
|
72
|
+
return new BranchFilter(args, args.builder);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
static parseConfig(configUtil, defn) {
|
|
76
|
+
const components = defn.args;
|
|
77
|
+
if (!_.isArray(components)) {
|
|
78
|
+
configUtil.throwConfigError('branch must contain a list of if/else-if/else components', this.op);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
const conditions = [];
|
|
82
|
+
const branchChains = [];
|
|
83
|
+
let elseChain = null;
|
|
84
|
+
let lastWasConditional = false;
|
|
85
|
+
|
|
86
|
+
components.forEach((comp, i) => {
|
|
87
|
+
if (!_.isObject(comp) || _.isArray(comp)) {
|
|
88
|
+
configUtil.throwConfigError('Invalid branch component', this.op);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
const key = _.first(_.keys(comp));
|
|
92
|
+
const body = comp[key];
|
|
93
|
+
|
|
94
|
+
if (!_.isArray(body)) {
|
|
95
|
+
configUtil.throwConfigError(`${key} must contain a list of transforms`, this.op);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
if (key === 'if' || key === 'else-if') {
|
|
99
|
+
if (body.length === 0) {
|
|
100
|
+
configUtil.throwConfigError(`${key} must have at least a condition`, this.op);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
const first = body[0];
|
|
104
|
+
let conditionExpr = null;
|
|
105
|
+
if (_.isObject(first) && !_.isArray(first) && _.has(first, 'condition')) {
|
|
106
|
+
conditionExpr = first.condition;
|
|
107
|
+
if (_.isObject(conditionExpr) && _.has(conditionExpr, 'expression')) {
|
|
108
|
+
conditionExpr = conditionExpr.expression;
|
|
109
|
+
}
|
|
110
|
+
conditionExpr = conditionExpr.toString();
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
if (!conditionExpr) {
|
|
114
|
+
configUtil.throwConfigError(`${key} first element must be condition: <expression>`, this.op);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
const compiledExpression = configUtil.compileExpression(conditionExpr, this.op, { this: 0 });
|
|
118
|
+
conditions.push({
|
|
119
|
+
expression: conditionExpr,
|
|
120
|
+
compiledExpression,
|
|
121
|
+
expressionSources: configUtil.expressionService.expressionTriggers(`${defn.path}.${i}.condition`),
|
|
122
|
+
hasThis: configUtil.expressionService.findName(conditionExpr, 'this'),
|
|
123
|
+
sourceCache: {},
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
const chainTransforms = body.slice(1);
|
|
127
|
+
const { transform } = configUtil.loadTransformList(chainTransforms, `${defn.path}.${i}.${key}`, false);
|
|
128
|
+
branchChains.push(transform);
|
|
129
|
+
lastWasConditional = true;
|
|
130
|
+
} else if (key === 'else') {
|
|
131
|
+
if (!lastWasConditional) {
|
|
132
|
+
configUtil.throwConfigError('else must follow if or else-if', this.op);
|
|
133
|
+
}
|
|
134
|
+
if (elseChain !== null) {
|
|
135
|
+
configUtil.throwConfigError('only one else allowed per branch', this.op);
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
const { transform } = configUtil.loadTransformList(body, `${defn.path}.${i}.else`, false);
|
|
139
|
+
elseChain = transform;
|
|
140
|
+
lastWasConditional = false;
|
|
141
|
+
} else {
|
|
142
|
+
configUtil.throwConfigError(`Unknown branch component: ${key}`, this.op);
|
|
143
|
+
}
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
defn.args = {
|
|
147
|
+
conditions,
|
|
148
|
+
branchChains,
|
|
149
|
+
elseChain,
|
|
150
|
+
};
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
static getExpressions(configUtil, body, info) {
|
|
154
|
+
const { varName, index, attribute } = info;
|
|
155
|
+
const parts = [];
|
|
156
|
+
|
|
157
|
+
if (!_.isArray(body)) {
|
|
158
|
+
return [];
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
_.each(body, (comp, compIndex) => {
|
|
162
|
+
if (!_.isObject(comp) || _.isArray(comp)) {
|
|
163
|
+
return;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
const key = _.first(_.keys(comp));
|
|
167
|
+
const compBody = comp[key];
|
|
168
|
+
|
|
169
|
+
if (key === 'if' || key === 'else-if') {
|
|
170
|
+
if (_.isArray(compBody) && compBody.length > 0) {
|
|
171
|
+
const first = compBody[0];
|
|
172
|
+
if (_.isObject(first) && _.has(first, 'condition')) {
|
|
173
|
+
const expr = first.condition?.toString?.() ?? first.condition;
|
|
174
|
+
if (expr) {
|
|
175
|
+
parts.push({
|
|
176
|
+
path: `variables.${varName}.${index}.${attribute}.${compIndex}.condition`,
|
|
177
|
+
expression: expr,
|
|
178
|
+
}, {
|
|
179
|
+
path: `variables.${varName}`,
|
|
180
|
+
expression: expr,
|
|
181
|
+
});
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
if (key === 'if' || key === 'else-if' || key === 'else') {
|
|
188
|
+
const chainTransforms = key === 'else'
|
|
189
|
+
? compBody
|
|
190
|
+
: compBody.slice(1);
|
|
191
|
+
const normalized = _.map(chainTransforms, (t) => (_.isString(t) ? { [t]: [] } : t));
|
|
192
|
+
// Include key (if/else-if/else) so expression paths match loadTransformList (used by parseConfig)
|
|
193
|
+
const chainVarName = `${varName}.${index}.${attribute}.${compIndex}.${key}`;
|
|
194
|
+
const chainExprs = configUtil.getExpressionsFromChain(
|
|
195
|
+
normalized,
|
|
196
|
+
chainVarName,
|
|
197
|
+
[...(info.ancestorVars || []), varName]
|
|
198
|
+
);
|
|
199
|
+
parts.push(..._.flatten(chainExprs));
|
|
200
|
+
}
|
|
201
|
+
});
|
|
202
|
+
|
|
203
|
+
return parts;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
supportsPendingChanges() {
|
|
207
|
+
const chains = [...this.branchChains, this.elseChain].filter(Boolean);
|
|
208
|
+
return _.some(chains, (c) => c.chain.chainSupportsPendingChange?.());
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
selectBranch(context, value, time) {
|
|
212
|
+
for (let i = 0; i < this.conditions.length; i += 1) {
|
|
213
|
+
const rule = this.conditions[i];
|
|
214
|
+
|
|
215
|
+
if (rule.hasThis) {
|
|
216
|
+
rule.sourceCache.this = value;
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
const unavailTerm = _.reduce(rule.expressionSources, (res, name) => {
|
|
220
|
+
const state = this.engine.getState(name);
|
|
221
|
+
if (state) {
|
|
222
|
+
rule.sourceCache[name] = state.value;
|
|
223
|
+
}
|
|
224
|
+
return res || !state || !state.available;
|
|
225
|
+
}, false);
|
|
226
|
+
|
|
227
|
+
if (unavailTerm) {
|
|
228
|
+
return -1;
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
try {
|
|
232
|
+
if (rule.compiledExpression.evaluate(rule.sourceCache)) {
|
|
233
|
+
return i;
|
|
234
|
+
}
|
|
235
|
+
} catch (err) {
|
|
236
|
+
this.recordError(context, err, time);
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
return this.elseChain !== null ? this.conditions.length : -1;
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
update(context, value, time) {
|
|
244
|
+
if (value === 'UNAVAILABLE') {
|
|
245
|
+
this.setUnavailable(context, time);
|
|
246
|
+
} else {
|
|
247
|
+
super.update(context, value, time);
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
filter(context, value, time) {
|
|
252
|
+
const branchIndex = this.selectBranch(context, value, time);
|
|
253
|
+
|
|
254
|
+
if (branchIndex < 0) {
|
|
255
|
+
this.commitValue(context, value, time);
|
|
256
|
+
return;
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
const chain = branchIndex < this.branchChains.length ? this.branchChains[branchIndex] : this.elseChain;
|
|
260
|
+
|
|
261
|
+
const result = chain.update(context, value, time);
|
|
262
|
+
if (result !== null && result !== undefined) {
|
|
263
|
+
this.commitValue(context, result, time);
|
|
264
|
+
} else {
|
|
265
|
+
this.setUnavailable(context, time);
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
setUnavailable(context, time) {
|
|
270
|
+
super.setUnavailable(context, time);
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
module.exports = BranchFilter;
|
package/lib/transform/index.js
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
const AccumulateFilter = require('./accumulate');
|
|
4
4
|
const AverageFilter = require('./average');
|
|
5
|
+
const BranchFilter = require('./branch');
|
|
5
6
|
const DebounceFilter = require('./debounce');
|
|
6
7
|
const DownsampleFilter = require('./downsample');
|
|
7
8
|
const EdgeFilter = require('./edge');
|
|
@@ -48,6 +49,7 @@ const WindowCountFilter = require('./windowCount');
|
|
|
48
49
|
module.exports = {
|
|
49
50
|
accumulate: AccumulateFilter,
|
|
50
51
|
average: AverageFilter,
|
|
52
|
+
branch: BranchFilter,
|
|
51
53
|
debounce: DebounceFilter,
|
|
52
54
|
downsample: DownsampleFilter,
|
|
53
55
|
edge: EdgeFilter,
|
package/package.json
CHANGED
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
version: 2
|
|
2
|
+
device: mtconnect-adapter
|
|
3
|
+
endpoint: localhost:8001
|
|
4
|
+
mtconnect-port: 8002
|
|
5
|
+
declare-keys:
|
|
6
|
+
- counter
|
|
7
|
+
- x
|
|
8
|
+
- y
|
|
9
|
+
- z
|
|
10
|
+
- obj
|
|
11
|
+
variables:
|
|
12
|
+
branch1:
|
|
13
|
+
- source: counter
|
|
14
|
+
- if:
|
|
15
|
+
- condition: this == 1
|
|
16
|
+
- expression: 100
|
|
17
|
+
- else-if:
|
|
18
|
+
- condition: this == 2
|
|
19
|
+
- expression: 200
|
|
20
|
+
- else:
|
|
21
|
+
- expression: 0
|
|
22
|
+
branch2simple:
|
|
23
|
+
- source: counter
|
|
24
|
+
- if:
|
|
25
|
+
- condition: this == 1
|
|
26
|
+
- expression: 10
|
|
27
|
+
- else:
|
|
28
|
+
- expression: 0
|
|
29
|
+
branch3:
|
|
30
|
+
- source: counter
|
|
31
|
+
- if:
|
|
32
|
+
- condition: x > y
|
|
33
|
+
- expression: 1
|
|
34
|
+
- else-if:
|
|
35
|
+
- condition: x < y
|
|
36
|
+
- expression: -1
|
|
37
|
+
- else:
|
|
38
|
+
- expression: 0
|
|
39
|
+
ifOnly:
|
|
40
|
+
- source: counter
|
|
41
|
+
- if:
|
|
42
|
+
- condition: this == 1
|
|
43
|
+
- expression: 100
|
|
44
|
+
ifElseIf:
|
|
45
|
+
- source: counter
|
|
46
|
+
- if:
|
|
47
|
+
- condition: this == 1
|
|
48
|
+
- expression: 100
|
|
49
|
+
- else-if:
|
|
50
|
+
- condition: this == 2
|
|
51
|
+
- expression: 200
|
|
52
|
+
twoElseIfs:
|
|
53
|
+
- source: counter
|
|
54
|
+
- if:
|
|
55
|
+
- condition: this == 1
|
|
56
|
+
- expression: 10
|
|
57
|
+
- else-if:
|
|
58
|
+
- condition: this == 2
|
|
59
|
+
- expression: 20
|
|
60
|
+
- else-if:
|
|
61
|
+
- condition: this == 3
|
|
62
|
+
- expression: 30
|
|
63
|
+
- else:
|
|
64
|
+
- expression: 0
|
|
65
|
+
ifElseIfConsecutive:
|
|
66
|
+
- source: counter
|
|
67
|
+
- if:
|
|
68
|
+
- condition: this == 1
|
|
69
|
+
- expression: 100
|
|
70
|
+
- else:
|
|
71
|
+
- expression: 2
|
|
72
|
+
- if:
|
|
73
|
+
- condition: counter == 2
|
|
74
|
+
- expression: 200
|
|
75
|
+
branchWithThis:
|
|
76
|
+
- source: counter
|
|
77
|
+
- if:
|
|
78
|
+
- condition: this == 1
|
|
79
|
+
- expression: 100
|
|
80
|
+
- else:
|
|
81
|
+
- expression: this
|
|
82
|
+
branchUnavailable:
|
|
83
|
+
- source: counter
|
|
84
|
+
- if:
|
|
85
|
+
- condition: this == 1
|
|
86
|
+
- expression: x + 1
|
|
87
|
+
- else:
|
|
88
|
+
- expression: this
|
|
89
|
+
branchWithInnerVar:
|
|
90
|
+
- source: counter
|
|
91
|
+
- if:
|
|
92
|
+
- condition: obj.a > 5
|
|
93
|
+
- expression: obj.a
|
|
94
|
+
- else:
|
|
95
|
+
- expression: obj.b
|
|
96
|
+
nested:
|
|
97
|
+
- source: counter
|
|
98
|
+
- if:
|
|
99
|
+
- condition: this > 0
|
|
100
|
+
- if:
|
|
101
|
+
- condition: this > 2
|
|
102
|
+
- expression: z
|
|
103
|
+
- else:
|
|
104
|
+
- expression: y
|
|
105
|
+
- else:
|
|
106
|
+
- expression: x
|
|
107
|
+
nestedInnerVar:
|
|
108
|
+
- source: counter
|
|
109
|
+
- if:
|
|
110
|
+
- condition: this > 0
|
|
111
|
+
- if:
|
|
112
|
+
- condition: obj.a > obj.b
|
|
113
|
+
- expression: obj.a + obj.b
|
|
114
|
+
- else:
|
|
115
|
+
- expression: y
|
|
116
|
+
- else:
|
|
117
|
+
- expression: x
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
version: 2
|
|
2
|
+
device: mtconnect-adapter
|
|
3
|
+
endpoint: localhost:8001
|
|
4
|
+
mtconnect-port: 8002
|
|
5
|
+
declare-keys:
|
|
6
|
+
- counter
|
|
7
|
+
variables:
|
|
8
|
+
badVar:
|
|
9
|
+
- source: counter
|
|
10
|
+
- if:
|
|
11
|
+
- condition: this == 1
|
|
12
|
+
- expression: 100
|
|
13
|
+
- invert
|
|
14
|
+
- else-if:
|
|
15
|
+
- condition: this == 2
|
|
16
|
+
- expression: 200
|
|
17
|
+
- else:
|
|
18
|
+
- expression: 0
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const expect = require('chai').expect;
|
|
4
|
+
const ConfigError = require('../../lib/config/configError');
|
|
5
|
+
const testUtils = require('../util/testUtils');
|
|
6
|
+
|
|
7
|
+
describe('branch transform invalid config tests', function () {
|
|
8
|
+
it('rejects else by itself', async function () {
|
|
9
|
+
try {
|
|
10
|
+
await testUtils.loadConfig('transform/invalid-branch-else-only.yml');
|
|
11
|
+
expect.fail('Config with else by itself should have thrown');
|
|
12
|
+
} catch (err) {
|
|
13
|
+
expect(err).to.be.instanceof(ConfigError);
|
|
14
|
+
expect(err.message).to.include('else');
|
|
15
|
+
}
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
it('rejects else-if by itself', async function () {
|
|
19
|
+
try {
|
|
20
|
+
await testUtils.loadConfig('transform/invalid-branch-elseif-only.yml');
|
|
21
|
+
expect.fail('Config with else-if by itself should have thrown');
|
|
22
|
+
} catch (err) {
|
|
23
|
+
expect(err).to.be.instanceof(ConfigError);
|
|
24
|
+
expect(err.message).to.include('else-if');
|
|
25
|
+
}
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
it('rejects if without condition as first transform', async function () {
|
|
29
|
+
try {
|
|
30
|
+
await testUtils.loadConfig('transform/invalid-branch-if-no-condition.yml');
|
|
31
|
+
expect.fail('Config with if without condition should have thrown');
|
|
32
|
+
} catch (err) {
|
|
33
|
+
expect(err).to.be.instanceof(ConfigError);
|
|
34
|
+
expect(err.message).to.include('condition');
|
|
35
|
+
}
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
it('rejects else-if without condition as first transform', async function () {
|
|
39
|
+
try {
|
|
40
|
+
await testUtils.loadConfig('transform/invalid-branch-elseif-no-condition.yml');
|
|
41
|
+
expect.fail('Config with else-if without condition should have thrown');
|
|
42
|
+
} catch (err) {
|
|
43
|
+
expect(err).to.be.instanceof(ConfigError);
|
|
44
|
+
expect(err.message).to.include('condition');
|
|
45
|
+
}
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
it('rejects unrelated op between if/else-if/else', async function () {
|
|
49
|
+
try {
|
|
50
|
+
await testUtils.loadConfig('transform/invalid-branch-unrelated-op.yml');
|
|
51
|
+
expect.fail('Config with unrelated op between if/else-if/else should have thrown');
|
|
52
|
+
} catch (err) {
|
|
53
|
+
expect(err).to.be.instanceof(ConfigError);
|
|
54
|
+
expect(err.message).to.match(/else-if|else|Unsupported/);
|
|
55
|
+
}
|
|
56
|
+
});
|
|
57
|
+
});
|
|
@@ -0,0 +1,357 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const expect = require('chai').expect;
|
|
4
|
+
const EngineV2 = require('../../lib/engine/engineV2');
|
|
5
|
+
const Builder = require('../../lib/engine/transformBuilderV2');
|
|
6
|
+
const testUtils = require('../util/testUtils');
|
|
7
|
+
|
|
8
|
+
describe('branch transform tests', function () {
|
|
9
|
+
let config;
|
|
10
|
+
before(async () => {
|
|
11
|
+
config = await testUtils.loadConfig('transform/branch.yml');
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
it('selects if branch when condition matches', function () {
|
|
15
|
+
const engine = new EngineV2(config);
|
|
16
|
+
const builder = new Builder(config);
|
|
17
|
+
builder.build(engine);
|
|
18
|
+
|
|
19
|
+
const source = testUtils.valueSource();
|
|
20
|
+
testUtils.attachEngineTransformValidator(engine, engine.variablePool.branch1, source);
|
|
21
|
+
|
|
22
|
+
source.sendValue('counter', 1, 0);
|
|
23
|
+
|
|
24
|
+
engine.validateFilter([
|
|
25
|
+
[100, 0],
|
|
26
|
+
]);
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
it('selects else-if branch when condition matches', function () {
|
|
30
|
+
const engine = new EngineV2(config);
|
|
31
|
+
const builder = new Builder(config);
|
|
32
|
+
builder.build(engine);
|
|
33
|
+
|
|
34
|
+
const source = testUtils.valueSource();
|
|
35
|
+
testUtils.attachEngineTransformValidator(engine, engine.variablePool.branch1, source);
|
|
36
|
+
|
|
37
|
+
source.sendValue('counter', 2, 0);
|
|
38
|
+
|
|
39
|
+
engine.validateFilter([
|
|
40
|
+
[200, 0],
|
|
41
|
+
]);
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
it('selects else branch when no condition matches', function () {
|
|
45
|
+
const engine = new EngineV2(config);
|
|
46
|
+
const builder = new Builder(config);
|
|
47
|
+
builder.build(engine);
|
|
48
|
+
|
|
49
|
+
const source = testUtils.valueSource();
|
|
50
|
+
testUtils.attachEngineTransformValidator(engine, engine.variablePool.branch1, source);
|
|
51
|
+
|
|
52
|
+
source.sendValue('counter', 5, 0);
|
|
53
|
+
|
|
54
|
+
engine.validateFilter([
|
|
55
|
+
[0, 0],
|
|
56
|
+
]);
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
it('evaluates constant expression in branch', function () {
|
|
60
|
+
const engine = new EngineV2(config);
|
|
61
|
+
const builder = new Builder(config);
|
|
62
|
+
builder.build(engine);
|
|
63
|
+
|
|
64
|
+
const source = testUtils.valueSource();
|
|
65
|
+
testUtils.attachEngineTransformValidator(engine, engine.variablePool.branch2simple, source);
|
|
66
|
+
|
|
67
|
+
source.sendValue('counter', 1, 0);
|
|
68
|
+
|
|
69
|
+
engine.validateFilter([
|
|
70
|
+
[10, 0],
|
|
71
|
+
]);
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
it('uses external variables in conditions', function () {
|
|
75
|
+
const engine = new EngineV2(config);
|
|
76
|
+
const builder = new Builder(config);
|
|
77
|
+
builder.build(engine);
|
|
78
|
+
|
|
79
|
+
const source = testUtils.valueSource();
|
|
80
|
+
testUtils.attachEngineTransformValidator(engine, engine.variablePool.branch3, source);
|
|
81
|
+
|
|
82
|
+
source.sendValue('x', 10, 0);
|
|
83
|
+
source.sendValue('y', 5, 0);
|
|
84
|
+
source.sendValue('counter', 0, 0);
|
|
85
|
+
|
|
86
|
+
engine.validateFilter([
|
|
87
|
+
[1, 0],
|
|
88
|
+
]);
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
it('selects else-if when x < y', function () {
|
|
92
|
+
const engine = new EngineV2(config);
|
|
93
|
+
const builder = new Builder(config);
|
|
94
|
+
builder.build(engine);
|
|
95
|
+
|
|
96
|
+
const source = testUtils.valueSource();
|
|
97
|
+
testUtils.attachEngineTransformValidator(engine, engine.variablePool.branch3, source);
|
|
98
|
+
|
|
99
|
+
source.sendValue('x', 5, 0);
|
|
100
|
+
source.sendValue('y', 10, 0);
|
|
101
|
+
source.sendValue('counter', 0, 0);
|
|
102
|
+
|
|
103
|
+
engine.validateFilter([
|
|
104
|
+
[-1, 0],
|
|
105
|
+
]);
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
it('selects else when x == y', function () {
|
|
109
|
+
const engine = new EngineV2(config);
|
|
110
|
+
const builder = new Builder(config);
|
|
111
|
+
builder.build(engine);
|
|
112
|
+
|
|
113
|
+
const source = testUtils.valueSource();
|
|
114
|
+
testUtils.attachEngineTransformValidator(engine, engine.variablePool.branch3, source);
|
|
115
|
+
|
|
116
|
+
source.sendValue('x', 5, 0);
|
|
117
|
+
source.sendValue('y', 5, 0);
|
|
118
|
+
source.sendValue('counter', 0, 0);
|
|
119
|
+
|
|
120
|
+
engine.validateFilter([
|
|
121
|
+
[0, 0],
|
|
122
|
+
]);
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
it('if only: selects branch when condition matches', function () {
|
|
126
|
+
const engine = new EngineV2(config);
|
|
127
|
+
const builder = new Builder(config);
|
|
128
|
+
builder.build(engine);
|
|
129
|
+
|
|
130
|
+
const source = testUtils.valueSource();
|
|
131
|
+
testUtils.attachEngineTransformValidator(engine, engine.variablePool.ifOnly, source);
|
|
132
|
+
|
|
133
|
+
source.sendValue('counter', 1, 0);
|
|
134
|
+
|
|
135
|
+
engine.validateFilter([
|
|
136
|
+
[100, 0],
|
|
137
|
+
]);
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
it('if only: passes through when condition does not match', function () {
|
|
141
|
+
const engine = new EngineV2(config);
|
|
142
|
+
const builder = new Builder(config);
|
|
143
|
+
builder.build(engine);
|
|
144
|
+
|
|
145
|
+
const source = testUtils.valueSource();
|
|
146
|
+
testUtils.attachEngineTransformValidator(engine, engine.variablePool.ifOnly, source);
|
|
147
|
+
|
|
148
|
+
source.sendValue('counter', 5, 0);
|
|
149
|
+
|
|
150
|
+
engine.validateFilter([
|
|
151
|
+
[5, 0],
|
|
152
|
+
]);
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
it('if/else-if without else: selects first branch', function () {
|
|
156
|
+
const engine = new EngineV2(config);
|
|
157
|
+
const builder = new Builder(config);
|
|
158
|
+
builder.build(engine);
|
|
159
|
+
|
|
160
|
+
const source = testUtils.valueSource();
|
|
161
|
+
testUtils.attachEngineTransformValidator(engine, engine.variablePool.ifElseIf, source);
|
|
162
|
+
|
|
163
|
+
source.sendValue('counter', 1, 0);
|
|
164
|
+
|
|
165
|
+
engine.validateFilter([
|
|
166
|
+
[100, 0],
|
|
167
|
+
]);
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
it('if/else-if without else: selects second branch', function () {
|
|
171
|
+
const engine = new EngineV2(config);
|
|
172
|
+
const builder = new Builder(config);
|
|
173
|
+
builder.build(engine);
|
|
174
|
+
|
|
175
|
+
const source = testUtils.valueSource();
|
|
176
|
+
testUtils.attachEngineTransformValidator(engine, engine.variablePool.ifElseIf, source);
|
|
177
|
+
|
|
178
|
+
source.sendValue('counter', 2, 0);
|
|
179
|
+
|
|
180
|
+
engine.validateFilter([
|
|
181
|
+
[200, 0],
|
|
182
|
+
]);
|
|
183
|
+
});
|
|
184
|
+
|
|
185
|
+
it('if/else-if without else: passes through when neither matches', function () {
|
|
186
|
+
const engine = new EngineV2(config);
|
|
187
|
+
const builder = new Builder(config);
|
|
188
|
+
builder.build(engine);
|
|
189
|
+
|
|
190
|
+
const source = testUtils.valueSource();
|
|
191
|
+
testUtils.attachEngineTransformValidator(engine, engine.variablePool.ifElseIf, source);
|
|
192
|
+
|
|
193
|
+
source.sendValue('counter', 5, 0);
|
|
194
|
+
|
|
195
|
+
engine.validateFilter([
|
|
196
|
+
[5, 0],
|
|
197
|
+
]);
|
|
198
|
+
});
|
|
199
|
+
|
|
200
|
+
it('two else-ifs: selects each branch correctly', function () {
|
|
201
|
+
const engine = new EngineV2(config);
|
|
202
|
+
const builder = new Builder(config);
|
|
203
|
+
builder.build(engine);
|
|
204
|
+
|
|
205
|
+
const source = testUtils.valueSource();
|
|
206
|
+
testUtils.attachEngineTransformValidator(engine, engine.variablePool.twoElseIfs, source);
|
|
207
|
+
|
|
208
|
+
source.sendValue('counter', 1, 0);
|
|
209
|
+
source.sendValue('counter', 2, 1);
|
|
210
|
+
source.sendValue('counter', 3, 2);
|
|
211
|
+
source.sendValue('counter', 5, 3);
|
|
212
|
+
|
|
213
|
+
engine.validateFilter([[10, 0], [20, 1], [30, 2], [0, 3]]);
|
|
214
|
+
});
|
|
215
|
+
|
|
216
|
+
it('if/else/if: two consecutive branches', function () {
|
|
217
|
+
const engine = new EngineV2(config);
|
|
218
|
+
const builder = new Builder(config);
|
|
219
|
+
builder.build(engine);
|
|
220
|
+
|
|
221
|
+
const source = testUtils.valueSource();
|
|
222
|
+
testUtils.attachEngineTransformValidator(engine, engine.variablePool.ifElseIfConsecutive, source);
|
|
223
|
+
|
|
224
|
+
source.sendValue('counter', 1, 0);
|
|
225
|
+
source.sendValue('counter', 2, 1);
|
|
226
|
+
source.sendValue('counter', 3, 2);
|
|
227
|
+
|
|
228
|
+
engine.validateFilter([[100, 0], [200, 1], [2, 2]]);
|
|
229
|
+
});
|
|
230
|
+
|
|
231
|
+
it('expression: this in else branch passes through input value', function () {
|
|
232
|
+
const engine = new EngineV2(config);
|
|
233
|
+
const builder = new Builder(config);
|
|
234
|
+
builder.build(engine);
|
|
235
|
+
|
|
236
|
+
const source = testUtils.valueSource();
|
|
237
|
+
testUtils.attachEngineTransformValidator(engine, engine.variablePool.branchWithThis, source);
|
|
238
|
+
|
|
239
|
+
source.sendValue('counter', 1, 0);
|
|
240
|
+
source.sendValue('counter', 5, 1);
|
|
241
|
+
|
|
242
|
+
engine.validateFilter([[100, 0], [5, 1]]);
|
|
243
|
+
});
|
|
244
|
+
|
|
245
|
+
it('unavailable in taken branch propagates; unavailable in not-taken branch has no effect', function () {
|
|
246
|
+
const engine = new EngineV2(config);
|
|
247
|
+
const builder = new Builder(config);
|
|
248
|
+
builder.build(engine);
|
|
249
|
+
|
|
250
|
+
const source = testUtils.valueSource();
|
|
251
|
+
engine.addValueSource(source);
|
|
252
|
+
|
|
253
|
+
const filterUpdates = [];
|
|
254
|
+
const endFilter = engine.variablePool.branchUnavailable.chainEnd();
|
|
255
|
+
endFilter.on('update', (value, time) => {
|
|
256
|
+
filterUpdates.push({ time, value });
|
|
257
|
+
});
|
|
258
|
+
endFilter.on('unavailable', (time) => {
|
|
259
|
+
filterUpdates.push({ time, value: 'UNAVAILABLE' });
|
|
260
|
+
});
|
|
261
|
+
|
|
262
|
+
const expectFilter = (expected) => {
|
|
263
|
+
const ref = expected.map(([value, time]) => ({ time, value }));
|
|
264
|
+
expect(filterUpdates.length).to.eq(ref.length);
|
|
265
|
+
ref.forEach((r, i) => {
|
|
266
|
+
expect(filterUpdates[i].time).to.be.closeTo(r.time, 0.000001);
|
|
267
|
+
expect(filterUpdates[i].value).to.deep.eq(r.value);
|
|
268
|
+
});
|
|
269
|
+
};
|
|
270
|
+
|
|
271
|
+
source.sendValue('x', 10, 0);
|
|
272
|
+
source.sendValue('counter', 1, 0);
|
|
273
|
+
source.sendValue('x', 'UNAVAILABLE', 1);
|
|
274
|
+
source.sendValue('counter', 5, 2);
|
|
275
|
+
source.sendValue('x', 'UNAVAILABLE', 2);
|
|
276
|
+
|
|
277
|
+
expectFilter([['UNAVAILABLE', 0], [11, 0], ['UNAVAILABLE', 1], ['UNAVAILABLE', 1], [5, 2]]);
|
|
278
|
+
});
|
|
279
|
+
|
|
280
|
+
it('nested if/else inside branch', function () {
|
|
281
|
+
const engine = new EngineV2(config);
|
|
282
|
+
const builder = new Builder(config);
|
|
283
|
+
builder.build(engine);
|
|
284
|
+
|
|
285
|
+
const source = testUtils.valueSource();
|
|
286
|
+
testUtils.attachEngineTransformValidator(engine, engine.variablePool.nested, source);
|
|
287
|
+
|
|
288
|
+
source.sendValue('x', 0, 0);
|
|
289
|
+
source.sendValue('y', 150, 0);
|
|
290
|
+
source.sendValue('z', 200, 0);
|
|
291
|
+
|
|
292
|
+
source.sendValue('counter', 0, 0);
|
|
293
|
+
source.sendValue('counter', 1, 1);
|
|
294
|
+
source.sendValue('counter', 2, 2);
|
|
295
|
+
source.sendValue('counter', 3, 3);
|
|
296
|
+
|
|
297
|
+
engine.validateFilter([[0, 0], [150, 1], [150, 2], [200, 3]]);
|
|
298
|
+
});
|
|
299
|
+
|
|
300
|
+
it('nested branch re-evaluates when expression variable in taken path updates', function () {
|
|
301
|
+
const engine = new EngineV2(config);
|
|
302
|
+
const builder = new Builder(config);
|
|
303
|
+
builder.build(engine);
|
|
304
|
+
|
|
305
|
+
const source = testUtils.valueSource();
|
|
306
|
+
testUtils.attachEngineTransformValidator(engine, engine.variablePool.nested, source);
|
|
307
|
+
|
|
308
|
+
source.sendValue('x', 0, 0);
|
|
309
|
+
source.sendValue('y', 150, 0);
|
|
310
|
+
source.sendValue('z', 200, 0);
|
|
311
|
+
|
|
312
|
+
source.sendValue('counter', 0, 0);
|
|
313
|
+
source.sendValue('x', 1, 1);
|
|
314
|
+
source.sendValue('counter', 1, 2);
|
|
315
|
+
source.sendValue('y', 151, 3);
|
|
316
|
+
source.sendValue('counter', 3, 4);
|
|
317
|
+
source.sendValue('z', 201, 5);
|
|
318
|
+
|
|
319
|
+
engine.validateFilter([[0, 0], [1, 1], [150, 2], [151, 3], [200, 4], [201, 5]]);
|
|
320
|
+
});
|
|
321
|
+
|
|
322
|
+
it('branchWithInnerVar selects branch by obj.a and emits obj.a or obj.b', function () {
|
|
323
|
+
const engine = new EngineV2(config);
|
|
324
|
+
const builder = new Builder(config);
|
|
325
|
+
builder.build(engine);
|
|
326
|
+
|
|
327
|
+
const source = testUtils.valueSource();
|
|
328
|
+
testUtils.attachEngineTransformValidator(engine, engine.variablePool.branchWithInnerVar, source);
|
|
329
|
+
|
|
330
|
+
source.sendValue('obj', { a: 10, b: 1 }, 0);
|
|
331
|
+
source.sendValue('counter', 1, 0);
|
|
332
|
+
source.sendValue('obj', { a: 3, b: 2 }, 1);
|
|
333
|
+
source.sendValue('counter', 2, 1);
|
|
334
|
+
|
|
335
|
+
engine.validateFilter([[10, 0], [2, 1]]);
|
|
336
|
+
});
|
|
337
|
+
|
|
338
|
+
it('nestedInnerVar selects nested branches by this and obj.a, obj.b', function () {
|
|
339
|
+
const engine = new EngineV2(config);
|
|
340
|
+
const builder = new Builder(config);
|
|
341
|
+
builder.build(engine);
|
|
342
|
+
|
|
343
|
+
const source = testUtils.valueSource();
|
|
344
|
+
testUtils.attachEngineTransformValidator(engine, engine.variablePool.nestedInnerVar, source);
|
|
345
|
+
|
|
346
|
+
source.sendValue('x', 0, 0);
|
|
347
|
+
source.sendValue('y', 150, 0);
|
|
348
|
+
source.sendValue('obj', { a: 10, b: 5 }, 0);
|
|
349
|
+
|
|
350
|
+
source.sendValue('counter', 0, 0);
|
|
351
|
+
source.sendValue('counter', 1, 1);
|
|
352
|
+
source.sendValue('obj', { a: 3, b: 8 }, 2);
|
|
353
|
+
source.sendValue('counter', 2, 2);
|
|
354
|
+
|
|
355
|
+
engine.validateFilter([[0, 0], [15, 1], [150, 2]]);
|
|
356
|
+
});
|
|
357
|
+
});
|