-
-
Notifications
You must be signed in to change notification settings - Fork 31.4k
/
Copy pathverify-graph.js
139 lines (117 loc) Β· 3.6 KB
/
verify-graph.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
'use strict';
require('../common');
const assert = require('assert');
const util = require('util');
function findInGraph(graph, type, n) {
let found = 0;
for (let i = 0; i < graph.length; i++) {
const node = graph[i];
if (node.type === type) found++;
if (found === n) return node;
}
}
function pruneTickObjects(activities) {
// Remove one TickObject on each pass until none is left anymore
// not super efficient, but simplest especially to handle
// multiple TickObjects in a row
const tickObject = {
found: true,
index: null,
data: null,
};
if (!Array.isArray(activities))
return activities;
while (tickObject.found) {
for (let i = 0; i < activities.length; i++) {
if (activities[i].type === 'TickObject') {
tickObject.index = i;
break;
} else if (i + 1 >= activities.length) {
tickObject.found = false;
}
}
if (tickObject.found) {
// Point all triggerAsyncIds that point to the tickObject
// to its triggerAsyncId and finally remove it from the activities
tickObject.data = activities[tickObject.index];
const triggerId = {
new: tickObject.data.triggerAsyncId,
old: tickObject.data.uid,
};
activities.forEach(function repointTriggerId(x) {
if (x.triggerAsyncId === triggerId.old)
x.triggerAsyncId = triggerId.new;
});
activities.splice(tickObject.index, 1);
}
}
return activities;
}
module.exports = function verifyGraph(hooks, graph) {
pruneTickObjects(hooks);
// Map actual ids to standin ids defined in the graph
const idtouid = {};
const uidtoid = {};
const typeSeen = {};
const errors = [];
const activities = pruneTickObjects(hooks.activities);
activities.forEach(processActivity);
function processActivity(x) {
typeSeen[x.type] ||= 0;
typeSeen[x.type]++;
const node = findInGraph(graph, x.type, typeSeen[x.type]);
if (node == null) return;
idtouid[node.id] = x.uid;
uidtoid[x.uid] = node.id;
if (node.triggerAsyncId == null) return;
const tid = idtouid[node.triggerAsyncId];
if (x.triggerAsyncId === tid) return;
errors.push({
id: node.id,
expectedTid: node.triggerAsyncId,
actualTid: uidtoid[x.triggerAsyncId],
});
}
if (errors.length) {
errors.forEach((x) =>
console.error(
`'${x.id}' expected to be triggered by '${x.expectedTid}', ` +
`but was triggered by '${x.actualTid}' instead.`,
),
);
}
assert.strictEqual(errors.length, 0);
// Verify that all expected types are present (but more/others are allowed)
const expTypes = { __proto__: null };
for (let i = 0; i < graph.length; i++) {
expTypes[graph[i].type] ??= 0;
expTypes[graph[i].type]++;
}
for (const type in expTypes) {
assert.ok(typeSeen[type] >= expTypes[type],
`Type '${type}': expecting: ${expTypes[type]} ` +
`found: ${typeSeen[type]}`);
}
};
//
// Helper to generate the input to the verifyGraph tests
//
function inspect(obj, depth) {
console.error(util.inspect(obj, false, depth || 5, true));
}
module.exports.printGraph = function printGraph(hooks) {
const ids = {};
const uidtoid = {};
const activities = pruneTickObjects(hooks.activities);
const graph = [];
activities.forEach(processNode);
function processNode(x) {
const key = x.type.replace(/WRAP/, '').toLowerCase();
ids[key] ||= 1;
const id = `${key}:${ids[key]++}`;
uidtoid[x.uid] = id;
const triggerAsyncId = uidtoid[x.triggerAsyncId] || null;
graph.push({ type: x.type, id, triggerAsyncId });
}
inspect(graph);
};