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