diff options
Diffstat (limited to 'devtools/shared/heapsnapshot/tests/unit/head_heapsnapshot.js')
-rw-r--r-- | devtools/shared/heapsnapshot/tests/unit/head_heapsnapshot.js | 448 |
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 }; -} |