summaryrefslogtreecommitdiff
path: root/test/async-hooks/verify-graph.js
blob: 638edd03a4ab246d9897b02ac0b7623d1a1d01fc (plain)
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) {
    if (!typeSeen[x.type]) 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 = Object.create(null);
  for (let i = 0; i < graph.length; i++) {
    if (expTypes[graph[i].type] == null) 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(procesNode);

  function procesNode(x) {
    const key = x.type.replace(/WRAP/, '').toLowerCase();
    if (!ids[key]) 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);
};