11cb0ef41Sopenharmony_ci'use strict';
21cb0ef41Sopenharmony_ci
31cb0ef41Sopenharmony_cirequire('../common');
41cb0ef41Sopenharmony_ciconst assert = require('assert');
51cb0ef41Sopenharmony_ciconst util = require('util');
61cb0ef41Sopenharmony_ci
71cb0ef41Sopenharmony_cifunction findInGraph(graph, type, n) {
81cb0ef41Sopenharmony_ci  let found = 0;
91cb0ef41Sopenharmony_ci  for (let i = 0; i < graph.length; i++) {
101cb0ef41Sopenharmony_ci    const node = graph[i];
111cb0ef41Sopenharmony_ci    if (node.type === type) found++;
121cb0ef41Sopenharmony_ci    if (found === n) return node;
131cb0ef41Sopenharmony_ci  }
141cb0ef41Sopenharmony_ci}
151cb0ef41Sopenharmony_ci
161cb0ef41Sopenharmony_cifunction pruneTickObjects(activities) {
171cb0ef41Sopenharmony_ci  // Remove one TickObject on each pass until none is left anymore
181cb0ef41Sopenharmony_ci  // not super efficient, but simplest especially to handle
191cb0ef41Sopenharmony_ci  // multiple TickObjects in a row
201cb0ef41Sopenharmony_ci  const tickObject = {
211cb0ef41Sopenharmony_ci    found: true,
221cb0ef41Sopenharmony_ci    index: null,
231cb0ef41Sopenharmony_ci    data: null,
241cb0ef41Sopenharmony_ci  };
251cb0ef41Sopenharmony_ci
261cb0ef41Sopenharmony_ci  if (!Array.isArray(activities))
271cb0ef41Sopenharmony_ci    return activities;
281cb0ef41Sopenharmony_ci
291cb0ef41Sopenharmony_ci  while (tickObject.found) {
301cb0ef41Sopenharmony_ci    for (let i = 0; i < activities.length; i++) {
311cb0ef41Sopenharmony_ci      if (activities[i].type === 'TickObject') {
321cb0ef41Sopenharmony_ci        tickObject.index = i;
331cb0ef41Sopenharmony_ci        break;
341cb0ef41Sopenharmony_ci      } else if (i + 1 >= activities.length) {
351cb0ef41Sopenharmony_ci        tickObject.found = false;
361cb0ef41Sopenharmony_ci      }
371cb0ef41Sopenharmony_ci    }
381cb0ef41Sopenharmony_ci
391cb0ef41Sopenharmony_ci    if (tickObject.found) {
401cb0ef41Sopenharmony_ci      // Point all triggerAsyncIds that point to the tickObject
411cb0ef41Sopenharmony_ci      // to its triggerAsyncId and finally remove it from the activities
421cb0ef41Sopenharmony_ci      tickObject.data = activities[tickObject.index];
431cb0ef41Sopenharmony_ci      const triggerId = {
441cb0ef41Sopenharmony_ci        new: tickObject.data.triggerAsyncId,
451cb0ef41Sopenharmony_ci        old: tickObject.data.uid,
461cb0ef41Sopenharmony_ci      };
471cb0ef41Sopenharmony_ci
481cb0ef41Sopenharmony_ci      activities.forEach(function repointTriggerId(x) {
491cb0ef41Sopenharmony_ci        if (x.triggerAsyncId === triggerId.old)
501cb0ef41Sopenharmony_ci          x.triggerAsyncId = triggerId.new;
511cb0ef41Sopenharmony_ci      });
521cb0ef41Sopenharmony_ci
531cb0ef41Sopenharmony_ci      activities.splice(tickObject.index, 1);
541cb0ef41Sopenharmony_ci    }
551cb0ef41Sopenharmony_ci  }
561cb0ef41Sopenharmony_ci  return activities;
571cb0ef41Sopenharmony_ci}
581cb0ef41Sopenharmony_ci
591cb0ef41Sopenharmony_cimodule.exports = function verifyGraph(hooks, graph) {
601cb0ef41Sopenharmony_ci  pruneTickObjects(hooks);
611cb0ef41Sopenharmony_ci
621cb0ef41Sopenharmony_ci  // Map actual ids to standin ids defined in the graph
631cb0ef41Sopenharmony_ci  const idtouid = {};
641cb0ef41Sopenharmony_ci  const uidtoid = {};
651cb0ef41Sopenharmony_ci  const typeSeen = {};
661cb0ef41Sopenharmony_ci  const errors = [];
671cb0ef41Sopenharmony_ci
681cb0ef41Sopenharmony_ci  const activities = pruneTickObjects(hooks.activities);
691cb0ef41Sopenharmony_ci  activities.forEach(processActivity);
701cb0ef41Sopenharmony_ci
711cb0ef41Sopenharmony_ci  function processActivity(x) {
721cb0ef41Sopenharmony_ci    if (!typeSeen[x.type]) typeSeen[x.type] = 0;
731cb0ef41Sopenharmony_ci    typeSeen[x.type]++;
741cb0ef41Sopenharmony_ci
751cb0ef41Sopenharmony_ci    const node = findInGraph(graph, x.type, typeSeen[x.type]);
761cb0ef41Sopenharmony_ci    if (node == null) return;
771cb0ef41Sopenharmony_ci
781cb0ef41Sopenharmony_ci    idtouid[node.id] = x.uid;
791cb0ef41Sopenharmony_ci    uidtoid[x.uid] = node.id;
801cb0ef41Sopenharmony_ci    if (node.triggerAsyncId == null) return;
811cb0ef41Sopenharmony_ci
821cb0ef41Sopenharmony_ci    const tid = idtouid[node.triggerAsyncId];
831cb0ef41Sopenharmony_ci    if (x.triggerAsyncId === tid) return;
841cb0ef41Sopenharmony_ci
851cb0ef41Sopenharmony_ci    errors.push({
861cb0ef41Sopenharmony_ci      id: node.id,
871cb0ef41Sopenharmony_ci      expectedTid: node.triggerAsyncId,
881cb0ef41Sopenharmony_ci      actualTid: uidtoid[x.triggerAsyncId],
891cb0ef41Sopenharmony_ci    });
901cb0ef41Sopenharmony_ci  }
911cb0ef41Sopenharmony_ci
921cb0ef41Sopenharmony_ci  if (errors.length) {
931cb0ef41Sopenharmony_ci    errors.forEach((x) =>
941cb0ef41Sopenharmony_ci      console.error(
951cb0ef41Sopenharmony_ci        `'${x.id}' expected to be triggered by '${x.expectedTid}', ` +
961cb0ef41Sopenharmony_ci        `but was triggered by '${x.actualTid}' instead.`,
971cb0ef41Sopenharmony_ci      ),
981cb0ef41Sopenharmony_ci    );
991cb0ef41Sopenharmony_ci  }
1001cb0ef41Sopenharmony_ci  assert.strictEqual(errors.length, 0);
1011cb0ef41Sopenharmony_ci
1021cb0ef41Sopenharmony_ci  // Verify that all expected types are present (but more/others are allowed)
1031cb0ef41Sopenharmony_ci  const expTypes = Object.create(null);
1041cb0ef41Sopenharmony_ci  for (let i = 0; i < graph.length; i++) {
1051cb0ef41Sopenharmony_ci    if (expTypes[graph[i].type] == null) expTypes[graph[i].type] = 0;
1061cb0ef41Sopenharmony_ci    expTypes[graph[i].type]++;
1071cb0ef41Sopenharmony_ci  }
1081cb0ef41Sopenharmony_ci
1091cb0ef41Sopenharmony_ci  for (const type in expTypes) {
1101cb0ef41Sopenharmony_ci    assert.ok(typeSeen[type] >= expTypes[type],
1111cb0ef41Sopenharmony_ci              `Type '${type}': expecting: ${expTypes[type]} ` +
1121cb0ef41Sopenharmony_ci              `found: ${typeSeen[type]}`);
1131cb0ef41Sopenharmony_ci  }
1141cb0ef41Sopenharmony_ci};
1151cb0ef41Sopenharmony_ci
1161cb0ef41Sopenharmony_ci//
1171cb0ef41Sopenharmony_ci// Helper to generate the input to the verifyGraph tests
1181cb0ef41Sopenharmony_ci//
1191cb0ef41Sopenharmony_cifunction inspect(obj, depth) {
1201cb0ef41Sopenharmony_ci  console.error(util.inspect(obj, false, depth || 5, true));
1211cb0ef41Sopenharmony_ci}
1221cb0ef41Sopenharmony_ci
1231cb0ef41Sopenharmony_cimodule.exports.printGraph = function printGraph(hooks) {
1241cb0ef41Sopenharmony_ci  const ids = {};
1251cb0ef41Sopenharmony_ci  const uidtoid = {};
1261cb0ef41Sopenharmony_ci  const activities = pruneTickObjects(hooks.activities);
1271cb0ef41Sopenharmony_ci  const graph = [];
1281cb0ef41Sopenharmony_ci  activities.forEach(procesNode);
1291cb0ef41Sopenharmony_ci
1301cb0ef41Sopenharmony_ci  function procesNode(x) {
1311cb0ef41Sopenharmony_ci    const key = x.type.replace(/WRAP/, '').toLowerCase();
1321cb0ef41Sopenharmony_ci    if (!ids[key]) ids[key] = 1;
1331cb0ef41Sopenharmony_ci    const id = `${key}:${ids[key]++}`;
1341cb0ef41Sopenharmony_ci    uidtoid[x.uid] = id;
1351cb0ef41Sopenharmony_ci    const triggerAsyncId = uidtoid[x.triggerAsyncId] || null;
1361cb0ef41Sopenharmony_ci    graph.push({ type: x.type, id, triggerAsyncId });
1371cb0ef41Sopenharmony_ci  }
1381cb0ef41Sopenharmony_ci  inspect(graph);
1391cb0ef41Sopenharmony_ci};
140