/* Any copyright is dedicated to the Public Domain.
   http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";

// Sanity test that we can compute shortest paths.
//
// Because the actual heap graph is too unpredictable and likely to drastically
// change as various implementation bits change, we don't test exact paths
// here. See js/src/jsapi-tests/testUbiNode.cpp for such tests, where we can
// control the specific graph shape and structure and so testing exact paths is
// reliable.

function run_test() {
  const path = ChromeUtils.saveHeapSnapshot({ runtime: true });
  const snapshot = ChromeUtils.readHeapSnapshot(path);

  const dominatorTree = snapshot.computeDominatorTree();
  const dominatedByRoot = dominatorTree
    .getImmediatelyDominated(dominatorTree.root)
    .slice(0, 10);
  ok(dominatedByRoot);
  ok(dominatedByRoot.length);

  const targetSet = new Set(dominatedByRoot);

  const shortestPaths = snapshot.computeShortestPaths(
    dominatorTree.root,
    dominatedByRoot,
    2
  );
  ok(shortestPaths);
  ok(shortestPaths instanceof Map);
  Assert.strictEqual(shortestPaths.size, targetSet.size);

  for (const [target, paths] of shortestPaths) {
    ok(targetSet.has(target), "We should only get paths for our targets");
    targetSet.delete(target);

    ok(
      !!paths.length,
      "We must have at least one path, since the target is dominated by the root"
    );
    Assert.lessOrEqual(
      paths.length,
      2,
      "Should not have recorded more paths than the max requested"
    );

    dumpn("---------------------");
    dumpn("Shortest paths for 0x" + target.toString(16) + ":");
    for (const pth of paths) {
      dumpn("    path =");
      for (const part of pth) {
        dumpn(
          "        predecessor: 0x" +
            part.predecessor.toString(16) +
            "; edge: " +
            part.edge
        );
      }
    }
    dumpn("---------------------");

    for (const path2 of paths) {
      ok(!!path2.length, "Cannot have zero length paths");
      Assert.strictEqual(
        path2[0].predecessor,
        dominatorTree.root,
        "The first predecessor is always our start node"
      );

      for (const part of path2) {
        ok(part.predecessor, "Each part of a path has a predecessor");
        ok(
          !!snapshot.describeNode(
            { by: "count", count: true, bytes: true },
            part.predecessor
          ),
          "The predecessor is in the heap snapshot"
        );
        ok("edge" in part, "Each part has an (potentially null) edge property");
      }
    }
  }

  Assert.strictEqual(
    targetSet.size,
    0,
    "We found paths for all of our targets"
  );

  do_test_finished();
}
