summaryrefslogtreecommitdiffstats
path: root/devtools/shared/heapsnapshot/tests/unit/head_heapsnapshot.js
diff options
context:
space:
mode:
Diffstat (limited to 'devtools/shared/heapsnapshot/tests/unit/head_heapsnapshot.js')
-rw-r--r--devtools/shared/heapsnapshot/tests/unit/head_heapsnapshot.js448
1 files changed, 0 insertions, 448 deletions
diff --git a/devtools/shared/heapsnapshot/tests/unit/head_heapsnapshot.js b/devtools/shared/heapsnapshot/tests/unit/head_heapsnapshot.js
deleted file mode 100644
index 3171c8a6f..000000000
--- a/devtools/shared/heapsnapshot/tests/unit/head_heapsnapshot.js
+++ /dev/null
@@ -1,448 +0,0 @@
-/* Any copyright is dedicated to the Public Domain.
- http://creativecommons.org/publicdomain/zero/1.0/ */
-
-"use strict";
-
-var Cc = Components.classes;
-var Ci = Components.interfaces;
-var Cu = Components.utils;
-var Cr = Components.results;
-var CC = Components.Constructor;
-
-const { require } = Cu.import("resource://devtools/shared/Loader.jsm", {});
-const { Match } = Cu.import("resource://test/Match.jsm", {});
-const { Census } = Cu.import("resource://test/Census.jsm", {});
-const { addDebuggerToGlobal } =
- Cu.import("resource://gre/modules/jsdebugger.jsm", {});
-const { Task } = require("devtools/shared/task");
-
-const DevToolsUtils = require("devtools/shared/DevToolsUtils");
-const flags = require("devtools/shared/flags");
-const HeapAnalysesClient =
- require("devtools/shared/heapsnapshot/HeapAnalysesClient");
-const Services = require("Services");
-const { censusReportToCensusTreeNode } = require("devtools/shared/heapsnapshot/census-tree-node");
-const CensusUtils = require("devtools/shared/heapsnapshot/CensusUtils");
-const DominatorTreeNode = require("devtools/shared/heapsnapshot/DominatorTreeNode");
-const { deduplicatePaths } = require("devtools/shared/heapsnapshot/shortest-paths");
-const { LabelAndShallowSizeVisitor } = DominatorTreeNode;
-
-
-// Always log packets when running tests. runxpcshelltests.py will throw
-// the output away anyway, unless you give it the --verbose flag.
-if (Services.appinfo.processType == Services.appinfo.PROCESS_TYPE_DEFAULT) {
- Services.prefs.setBoolPref("devtools.debugger.log", true);
-}
-flags.wantLogging = true;
-
-const SYSTEM_PRINCIPAL = Cc["@mozilla.org/systemprincipal;1"]
- .createInstance(Ci.nsIPrincipal);
-
-function dumpn(msg) {
- dump("HEAPSNAPSHOT-TEST: " + msg + "\n");
-}
-
-function addTestingFunctionsToGlobal(global) {
- global.eval(
- `
- const testingFunctions = Components.utils.getJSTestingFunctions();
- for (let k in testingFunctions) {
- this[k] = testingFunctions[k];
- }
- `
- );
- if (!global.print) {
- global.print = do_print;
- }
- if (!global.newGlobal) {
- global.newGlobal = newGlobal;
- }
- if (!global.Debugger) {
- addDebuggerToGlobal(global);
- }
-}
-
-addTestingFunctionsToGlobal(this);
-
-/**
- * Create a new global, with all the JS shell testing functions. Similar to the
- * newGlobal function exposed to JS shells, and useful for porting JS shell
- * tests to xpcshell tests.
- */
-function newGlobal() {
- const global = new Cu.Sandbox(SYSTEM_PRINCIPAL, { freshZone: true });
- addTestingFunctionsToGlobal(global);
- return global;
-}
-
-function assertThrowsValue(f, val, msg) {
- var fullmsg;
- try {
- f();
- } catch (exc) {
- if ((exc === val) === (val === val) && (val !== 0 || 1 / exc === 1 / val))
- return;
- fullmsg = "Assertion failed: expected exception " + val + ", got " + exc;
- }
- if (fullmsg === undefined)
- fullmsg = "Assertion failed: expected exception " + val + ", no exception thrown";
- if (msg !== undefined)
- fullmsg += " - " + msg;
- throw new Error(fullmsg);
-}
-
-/**
- * Returns the full path of the file with the specified name in a
- * platform-independent and URL-like form.
- */
-function getFilePath(aName, aAllowMissing = false, aUsePlatformPathSeparator = false)
-{
- let file = do_get_file(aName, aAllowMissing);
- let path = Services.io.newFileURI(file).spec;
- let filePrePath = "file://";
- if ("nsILocalFileWin" in Ci &&
- file instanceof Ci.nsILocalFileWin) {
- filePrePath += "/";
- }
-
- path = path.slice(filePrePath.length);
-
- if (aUsePlatformPathSeparator && path.match(/^\w:/)) {
- path = path.replace(/\//g, "\\");
- }
-
- return path;
-}
-
-function saveNewHeapSnapshot(opts = { runtime: true }) {
- const filePath = ChromeUtils.saveHeapSnapshot(opts);
- ok(filePath, "Should get a file path to save the core dump to.");
- ok(true, "Saved a heap snapshot to " + filePath);
- return filePath;
-}
-
-function readHeapSnapshot(filePath) {
- const snapshot = ChromeUtils.readHeapSnapshot(filePath);
- ok(snapshot, "Should have read a heap snapshot back from " + filePath);
- ok(snapshot instanceof HeapSnapshot, "snapshot should be an instance of HeapSnapshot");
- return snapshot;
-}
-
-/**
- * Save a heap snapshot to the file with the given name in the current
- * directory, read it back as a HeapSnapshot instance, and then take a census of
- * the heap snapshot's serialized heap graph with the provided census options.
- *
- * @param {Object|undefined} censusOptions
- * Options that should be passed through to the takeCensus method. See
- * js/src/doc/Debugger/Debugger.Memory.md for details.
- *
- * @param {Debugger|null} dbg
- * If a Debugger object is given, only serialize the subgraph covered by
- * the Debugger's debuggees. If null, serialize the whole heap graph.
- *
- * @param {String} fileName
- * The file name to save the heap snapshot's core dump file to, within
- * the current directory.
- *
- * @returns Census
- */
-function saveHeapSnapshotAndTakeCensus(dbg = null, censusOptions = undefined) {
- const snapshotOptions = dbg ? { debugger: dbg } : { runtime: true };
- const filePath = saveNewHeapSnapshot(snapshotOptions);
- const snapshot = readHeapSnapshot(filePath);
-
- equal(typeof snapshot.takeCensus, "function", "snapshot should have a takeCensus method");
-
- return snapshot.takeCensus(censusOptions);
-}
-
-/**
- * Save a heap snapshot to disk, read it back as a HeapSnapshot instance, and
- * then compute its dominator tree.
- *
- * @param {Debugger|null} dbg
- * If a Debugger object is given, only serialize the subgraph covered by
- * the Debugger's debuggees. If null, serialize the whole heap graph.
- *
- * @returns {DominatorTree}
- */
-function saveHeapSnapshotAndComputeDominatorTree(dbg = null) {
- const snapshotOptions = dbg ? { debugger: dbg } : { runtime: true };
- const filePath = saveNewHeapSnapshot(snapshotOptions);
- const snapshot = readHeapSnapshot(filePath);
-
- equal(typeof snapshot.computeDominatorTree, "function",
- "snapshot should have a `computeDominatorTree` method");
-
- const dominatorTree = snapshot.computeDominatorTree();
-
- ok(dominatorTree, "Should be able to compute a dominator tree");
- ok(dominatorTree instanceof DominatorTree, "Should be an instance of DominatorTree");
-
- return dominatorTree;
-}
-
-function isSavedFrame(obj) {
- return Object.prototype.toString.call(obj) === "[object SavedFrame]";
-}
-
-function savedFrameReplacer(key, val) {
- if (isSavedFrame(val)) {
- return `<SavedFrame '${val.toString().split(/\n/g).shift()}'>`;
- } else {
- return val;
- }
-}
-
-/**
- * Assert that creating a CensusTreeNode from the given `report` with the
- * specified `breakdown` creates the given `expected` CensusTreeNode.
- *
- * @param {Object} breakdown
- * The census breakdown.
- *
- * @param {Object} report
- * The census report.
- *
- * @param {Object} expected
- * The expected CensusTreeNode result.
- *
- * @param {Object} options
- * The options to pass through to `censusReportToCensusTreeNode`.
- */
-function compareCensusViewData(breakdown, report, expected, options) {
- dumpn("Generating CensusTreeNode from report:");
- dumpn("breakdown: " + JSON.stringify(breakdown, null, 4));
- dumpn("report: " + JSON.stringify(report, null, 4));
- dumpn("expected: " + JSON.stringify(expected, savedFrameReplacer, 4));
-
- const actual = censusReportToCensusTreeNode(breakdown, report, options);
- dumpn("actual: " + JSON.stringify(actual, savedFrameReplacer, 4));
-
- assertStructurallyEquivalent(actual, expected);
-}
-
-// Deep structural equivalence that can handle Map objects in addition to plain
-// objects.
-function assertStructurallyEquivalent(actual, expected, path = "root") {
- if (actual === expected) {
- equal(actual, expected, "actual and expected are the same");
- return;
- }
-
- equal(typeof actual, typeof expected, `${path}: typeof should be the same`);
-
- if (actual && typeof actual === "object") {
- const actualProtoString = Object.prototype.toString.call(actual);
- const expectedProtoString = Object.prototype.toString.call(expected);
- equal(actualProtoString, expectedProtoString,
- `${path}: Object.prototype.toString.call() should be the same`);
-
- if (actualProtoString === "[object Map]") {
- const expectedKeys = new Set([...expected.keys()]);
-
- for (let key of actual.keys()) {
- ok(expectedKeys.has(key),
- `${path}: every key in actual should exist in expected: ${String(key).slice(0, 10)}`);
- expectedKeys.delete(key);
-
- assertStructurallyEquivalent(actual.get(key), expected.get(key),
- path + ".get(" + String(key).slice(0, 20) + ")");
- }
-
- equal(expectedKeys.size, 0,
- `${path}: every key in expected should also exist in actual, did not see ${[...expectedKeys]}`);
- } else if (actualProtoString === "[object Set]") {
- const expectedItems = new Set([...expected]);
-
- for (let item of actual) {
- ok(expectedItems.has(item),
- `${path}: every set item in actual should exist in expected: ${item}`);
- expectedItems.delete(item);
- }
-
- equal(expectedItems.size, 0,
- `${path}: every set item in expected should also exist in actual, did not see ${[...expectedItems]}`);
- } else {
- const expectedKeys = new Set(Object.keys(expected));
-
- for (let key of Object.keys(actual)) {
- ok(expectedKeys.has(key),
- `${path}: every key in actual should exist in expected: ${key}`);
- expectedKeys.delete(key);
-
- assertStructurallyEquivalent(actual[key], expected[key], path + "." + key);
- }
-
- equal(expectedKeys.size, 0,
- `${path}: every key in expected should also exist in actual, did not see ${[...expectedKeys]}`);
- }
- } else {
- equal(actual, expected, `${path}: primitives should be equal`);
- }
-}
-
-/**
- * Assert that creating a diff of the `first` and `second` census reports
- * creates the `expected` delta-report.
- *
- * @param {Object} breakdown
- * The census breakdown.
- *
- * @param {Object} first
- * The first census report.
- *
- * @param {Object} second
- * The second census report.
- *
- * @param {Object} expected
- * The expected delta-report.
- */
-function assertDiff(breakdown, first, second, expected) {
- dumpn("Diffing census reports:");
- dumpn("Breakdown: " + JSON.stringify(breakdown, null, 4));
- dumpn("First census report: " + JSON.stringify(first, null, 4));
- dumpn("Second census report: " + JSON.stringify(second, null, 4));
- dumpn("Expected delta-report: " + JSON.stringify(expected, null, 4));
-
- const actual = CensusUtils.diff(breakdown, first, second);
- dumpn("Actual delta-report: " + JSON.stringify(actual, null, 4));
-
- assertStructurallyEquivalent(actual, expected);
-}
-
-/**
- * Assert that creating a label and getting a shallow size from the given node
- * description with the specified breakdown is as expected.
- *
- * @param {Object} breakdown
- * @param {Object} givenDescription
- * @param {Number} expectedShallowSize
- * @param {Object} expectedLabel
- */
-function assertLabelAndShallowSize(breakdown, givenDescription, expectedShallowSize, expectedLabel) {
- dumpn("Computing label and shallow size from node description:");
- dumpn("Breakdown: " + JSON.stringify(breakdown, null, 4));
- dumpn("Given description: " + JSON.stringify(givenDescription, null, 4));
-
- const visitor = new LabelAndShallowSizeVisitor();
- CensusUtils.walk(breakdown, description, visitor);
-
- dumpn("Expected shallow size: " + expectedShallowSize);
- dumpn("Actual shallow size: " + visitor.shallowSize());
- equal(visitor.shallowSize(), expectedShallowSize, "Shallow size should be correct");
-
- dumpn("Expected label: " + JSON.stringify(expectedLabel, null, 4));
- dumpn("Actual label: " + JSON.stringify(visitor.label(), null, 4));
- assertStructurallyEquivalent(visitor.label(), expectedLabel);
-}
-
-// Counter for mock DominatorTreeNode ids.
-let TEST_NODE_ID_COUNTER = 0;
-
-/**
- * Create a mock DominatorTreeNode for testing, with sane defaults. Override any
- * property by providing it on `opts`. Optionally pass child nodes as well.
- *
- * @param {Object} opts
- * @param {Array<DominatorTreeNode>?} children
- *
- * @returns {DominatorTreeNode}
- */
-function makeTestDominatorTreeNode(opts, children) {
- const nodeId = TEST_NODE_ID_COUNTER++;
-
- const node = Object.assign({
- nodeId,
- label: undefined,
- shallowSize: 1,
- retainedSize: (children || []).reduce((size, c) => size + c.retainedSize, 1),
- parentId: undefined,
- children,
- moreChildrenAvailable: true,
- }, opts);
-
- if (children && children.length) {
- children.map(c => c.parentId = node.nodeId);
- }
-
- return node;
-}
-
-/**
- * Insert `newChildren` into the given dominator `tree` as specified by the
- * `path` from the root to the node the `newChildren` should be inserted
- * beneath. Assert that the resulting tree matches `expected`.
- */
-function assertDominatorTreeNodeInsertion(tree, path, newChildren, moreChildrenAvailable, expected) {
- dumpn("Inserting new children into a dominator tree:");
- dumpn("Dominator tree: " + JSON.stringify(tree, null, 2));
- dumpn("Path: " + JSON.stringify(path, null, 2));
- dumpn("New children: " + JSON.stringify(newChildren, null, 2));
- dumpn("Expected resulting tree: " + JSON.stringify(expected, null, 2));
-
- const actual = DominatorTreeNode.insert(tree, path, newChildren, moreChildrenAvailable);
- dumpn("Actual resulting tree: " + JSON.stringify(actual, null, 2));
-
- assertStructurallyEquivalent(actual, expected);
-}
-
-function assertDeduplicatedPaths({ target, paths, expectedNodes, expectedEdges }) {
- dumpn("Deduplicating paths:");
- dumpn("target = " + target);
- dumpn("paths = " + JSON.stringify(paths, null, 2));
- dumpn("expectedNodes = " + expectedNodes);
- dumpn("expectedEdges = " + JSON.stringify(expectedEdges, null, 2));
-
- const { nodes, edges } = deduplicatePaths(target, paths);
-
- dumpn("Actual nodes = " + nodes);
- dumpn("Actual edges = " + JSON.stringify(edges, null, 2));
-
- equal(nodes.length, expectedNodes.length,
- "actual number of nodes is equal to the expected number of nodes");
-
- equal(edges.length, expectedEdges.length,
- "actual number of edges is equal to the expected number of edges");
-
- const expectedNodeSet = new Set(expectedNodes);
- const nodeSet = new Set(nodes);
- ok(nodeSet.size === nodes.length,
- "each returned node should be unique");
-
- for (let node of nodes) {
- ok(expectedNodeSet.has(node), `the ${node} node was expected`);
- }
-
- for (let expectedEdge of expectedEdges) {
- let count = 0;
- for (let edge of edges) {
- if (edge.from === expectedEdge.from &&
- edge.to === expectedEdge.to &&
- edge.name === expectedEdge.name) {
- count++;
- }
- }
- equal(count, 1,
- "should have exactly one matching edge for the expected edge = " + JSON.stringify(edge));
- }
-}
-
-function assertCountToBucketBreakdown(breakdown, expected) {
- dumpn("count => bucket breakdown");
- dumpn("Initial breakdown = ", JSON.stringify(breakdown, null, 2));
- dumpn("Expected results = ", JSON.stringify(expected, null, 2));
-
- const actual = CensusUtils.countToBucketBreakdown(breakdown);
- dumpn("Actual results = ", JSON.stringify(actual, null, 2));
-
- assertStructurallyEquivalent(actual, expected);
-}
-
-/**
- * Create a mock path entry for the given predecessor and edge.
- */
-function pathEntry(predecessor, edge) {
- return { predecessor, edge };
-}