diff options
Diffstat (limited to 'devtools/client/memory/test/chrome')
18 files changed, 1470 insertions, 0 deletions
diff --git a/devtools/client/memory/test/chrome/chrome.ini b/devtools/client/memory/test/chrome/chrome.ini new file mode 100644 index 000000000..7803bcda3 --- /dev/null +++ b/devtools/client/memory/test/chrome/chrome.ini @@ -0,0 +1,20 @@ +[DEFAULT] +support-files = + head.js + +[test_CensusTreeItem_01.html] +[test_DominatorTree_01.html] +[test_DominatorTree_02.html] +[test_DominatorTree_03.html] +[test_DominatorTreeItem_01.html] +[test_Heap_01.html] +[test_Heap_02.html] +[test_Heap_03.html] +[test_Heap_04.html] +[test_Heap_05.html] +[test_List_01.html] +[test_ShortestPaths_01.html] +[test_ShortestPaths_02.html] +[test_SnapshotListItem_01.html] +[test_Toolbar_01.html] +[test_TreeMap_01.html] diff --git a/devtools/client/memory/test/chrome/head.js b/devtools/client/memory/test/chrome/head.js new file mode 100644 index 000000000..4ca5a7a7e --- /dev/null +++ b/devtools/client/memory/test/chrome/head.js @@ -0,0 +1,335 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ +"use strict"; + +var { classes: Cc, interfaces: Ci, utils: Cu, results: Cr } = Components; + +var { BrowserLoader } = Cu.import("resource://devtools/client/shared/browser-loader.js", {}); +var { require } = BrowserLoader({ + baseURI: "resource://devtools/client/memory/", + window +}); +var { Assert } = require("resource://testing-common/Assert.jsm"); +var Services = require("Services"); +var { Task } = require("devtools/shared/task"); + +var EXPECTED_DTU_ASSERT_FAILURE_COUNT = 0; + +SimpleTest.registerCleanupFunction(function () { + if (DevToolsUtils.assertionFailureCount !== EXPECTED_DTU_ASSERT_FAILURE_COUNT) { + ok(false, "Should have had the expected number of DevToolsUtils.assert() failures. Expected " + + EXPECTED_DTU_ASSERT_FAILURE_COUNT + ", got " + DevToolsUtils.assertionFailureCount); + } +}); + +var DevToolsUtils = require("devtools/shared/DevToolsUtils"); +var { immutableUpdate } = DevToolsUtils; +var flags = require("devtools/shared/flags"); +flags.testing = true; + +var constants = require("devtools/client/memory/constants"); +var { + censusDisplays, + diffingState, + labelDisplays, + dominatorTreeState, + snapshotState, + viewState, + censusState +} = constants; + +const { + L10N, +} = require("devtools/client/memory/utils"); + +var models = require("devtools/client/memory/models"); + +var Immutable = require("devtools/client/shared/vendor/immutable"); +var React = require("devtools/client/shared/vendor/react"); +var ReactDOM = require("devtools/client/shared/vendor/react-dom"); +var Heap = React.createFactory(require("devtools/client/memory/components/heap")); +var CensusTreeItem = React.createFactory(require("devtools/client/memory/components/census-tree-item")); +var DominatorTreeComponent = React.createFactory(require("devtools/client/memory/components/dominator-tree")); +var DominatorTreeItem = React.createFactory(require("devtools/client/memory/components/dominator-tree-item")); +var ShortestPaths = React.createFactory(require("devtools/client/memory/components/shortest-paths")); +var TreeMap = React.createFactory(require("devtools/client/memory/components/tree-map")); +var SnapshotListItem = React.createFactory(require("devtools/client/memory/components/snapshot-list-item")); +var List = React.createFactory(require("devtools/client/memory/components/list")); +var Toolbar = React.createFactory(require("devtools/client/memory/components/toolbar")); + +// All tests are asynchronous. +SimpleTest.waitForExplicitFinish(); + +var noop = () => {}; + +var TEST_CENSUS_TREE_ITEM_PROPS = Object.freeze({ + item: Object.freeze({ + bytes: 10, + count: 1, + totalBytes: 10, + totalCount: 1, + name: "foo", + children: [ + Object.freeze({ + bytes: 10, + count: 1, + totalBytes: 10, + totalCount: 1, + name: "bar", + }) + ] + }), + depth: 0, + arrow: ">", + focused: true, + getPercentBytes: () => 50, + getPercentCount: () => 50, + showSign: false, + onViewSourceInDebugger: noop, + inverted: false, +}); + +// Counter for mock DominatorTreeNode ids. +var 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: ["other", "SomeType"], + 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; +} + +var TEST_DOMINATOR_TREE = Object.freeze({ + dominatorTreeId: 666, + root: (function makeTree(depth = 0) { + let children; + if (depth <= 3) { + children = [ + makeTree(depth + 1), + makeTree(depth + 1), + makeTree(depth + 1), + ]; + } + return makeTestDominatorTreeNode({}, children); + }()), + expanded: new Set(), + focused: null, + error: null, + display: labelDisplays.coarseType, + activeFetchRequestCount: null, + state: dominatorTreeState.LOADED, +}); + +var TEST_DOMINATOR_TREE_PROPS = Object.freeze({ + dominatorTree: TEST_DOMINATOR_TREE, + onLoadMoreSiblings: noop, + onViewSourceInDebugger: noop, + onExpand: noop, + onCollapse: noop, +}); + +var TEST_SHORTEST_PATHS_PROPS = Object.freeze({ + graph: Object.freeze({ + nodes: [ + { id: 1, label: ["other", "SomeType"] }, + { id: 2, label: ["other", "SomeType"] }, + { id: 3, label: ["other", "SomeType"] }, + ], + edges: [ + { from: 1, to: 2, name: "1->2" }, + { from: 1, to: 3, name: "1->3" }, + { from: 2, to: 3, name: "2->3" }, + ], + }), +}); + +var TEST_SNAPSHOT = Object.freeze({ + id: 1337, + selected: true, + path: "/fake/path/to/snapshot", + census: Object.freeze({ + report: Object.freeze({ + objects: Object.freeze({ count: 4, bytes: 400 }), + scripts: Object.freeze({ count: 3, bytes: 300 }), + strings: Object.freeze({ count: 2, bytes: 200 }), + other: Object.freeze({ count: 1, bytes: 100 }), + }), + display: Object.freeze({ + displayName: "Test Display", + tooltip: "Test display tooltup", + inverted: false, + breakdown: Object.freeze({ + by: "coarseType", + objects: Object.freeze({ by: "count", count: true, bytes: true }), + scripts: Object.freeze({ by: "count", count: true, bytes: true }), + strings: Object.freeze({ by: "count", count: true, bytes: true }), + other: Object.freeze({ by: "count", count: true, bytes: true }), + }), + }), + state: censusState.SAVED, + inverted: false, + filter: null, + expanded: new Set(), + focused: null, + parentMap: Object.freeze(Object.create(null)) + }), + dominatorTree: TEST_DOMINATOR_TREE, + error: null, + imported: false, + creationTime: 0, + state: snapshotState.READ, +}); + +var TEST_HEAP_PROPS = Object.freeze({ + onSnapshotClick: noop, + onLoadMoreSiblings: noop, + onCensusExpand: noop, + onCensusCollapse: noop, + onDominatorTreeExpand: noop, + onDominatorTreeCollapse: noop, + onCensusFocus: noop, + onDominatorTreeFocus: noop, + onViewSourceInDebugger: noop, + diffing: null, + view: { state: viewState.CENSUS, }, + snapshot: TEST_SNAPSHOT, + sizes: Object.freeze({ shortestPathsSize: .5 }), + onShortestPathsResize: noop, +}); + +var TEST_TOOLBAR_PROPS = Object.freeze({ + censusDisplays: [ + censusDisplays.coarseType, + censusDisplays.allocationStack, + censusDisplays.invertedAllocationStack, + ], + censusDisplay: censusDisplays.coarseType, + onTakeSnapshotClick: noop, + onImportClick: noop, + onCensusDisplayChange: noop, + onToggleRecordAllocationStacks: noop, + allocations: models.allocations, + onToggleInverted: noop, + inverted: false, + filterString: null, + setFilterString: noop, + diffing: null, + onToggleDiffing: noop, + view: { state: viewState.CENSUS, }, + onViewChange: noop, + labelDisplays: [ + labelDisplays.coarseType, + labelDisplays.allocationStack, + ], + labelDisplay: labelDisplays.coarseType, + onLabelDisplayChange: noop, + snapshots: [], +}); + +function makeTestCensusNode() { + return { + name: "Function", + bytes: 100, + totalBytes: 100, + count: 100, + totalCount: 100, + children: [] + }; +} + +var TEST_TREE_MAP_PROPS = Object.freeze({ + treeMap: Object.freeze({ + report: { + name: null, + bytes: 0, + totalBytes: 400, + count: 0, + totalCount: 400, + children: [ + { + name: "objects", + bytes: 0, + totalBytes: 200, + count: 0, + totalCount: 200, + children: [ makeTestCensusNode(), makeTestCensusNode() ] + }, + { + name: "other", + bytes: 0, + totalBytes: 200, + count: 0, + totalCount: 200, + children: [ makeTestCensusNode(), makeTestCensusNode() ], + } + ] + } + }) +}); + +var TEST_SNAPSHOT_LIST_ITEM_PROPS = Object.freeze({ + onClick: noop, + onSave: noop, + onDelete: noop, + item: TEST_SNAPSHOT, + index: 1234, +}); + +function onNextAnimationFrame(fn) { + return () => + requestAnimationFrame(() => + requestAnimationFrame(fn)); +} + +/** + * Render the provided ReactElement in the provided HTML container. + * Returns a Promise that will resolve the rendered element as a React + * component. + */ +function renderComponent(element, container) { + return new Promise(resolve => { + let component = ReactDOM.render(element, container, + onNextAnimationFrame(() => { + dumpn("Rendered = " + container.innerHTML); + resolve(component); + })); + }); +} + +function setState(component, newState) { + return new Promise(resolve => { + component.setState(newState, onNextAnimationFrame(resolve)); + }); +} + +function setProps(component, newProps) { + return new Promise(resolve => { + component.setProps(newProps, onNextAnimationFrame(resolve)); + }); +} + +function dumpn(msg) { + dump(`MEMORY-TEST: ${msg}\n`); +} diff --git a/devtools/client/memory/test/chrome/test_CensusTreeItem_01.html b/devtools/client/memory/test/chrome/test_CensusTreeItem_01.html new file mode 100644 index 000000000..fef996330 --- /dev/null +++ b/devtools/client/memory/test/chrome/test_CensusTreeItem_01.html @@ -0,0 +1,65 @@ +<!DOCTYPE HTML> +<html> +<!-- +Test that children pointers show up at the correct times. +--> +<head> + <meta charset="utf-8"> + <title>Tree component test</title> + <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"> +</head> +<body> + <!-- Give the container height so that the whole tree is rendered. --> + <div id="container" style="height: 900px;"></div> + + <pre id="test"> + <script src="head.js" type="application/javascript;version=1.8"></script> + <script type="application/javascript;version=1.8"> + window.onload = Task.async(function* () { + try { + const container = document.getElementById("container"); + + yield renderComponent(CensusTreeItem(immutableUpdate(TEST_CENSUS_TREE_ITEM_PROPS, { + inverted: true, + depth: 0, + })), container); + + ok(!container.querySelector(".children-pointer"), + "Don't show children pointer for roots when we are inverted"); + + yield renderComponent(CensusTreeItem(immutableUpdate(TEST_CENSUS_TREE_ITEM_PROPS, { + inverted: true, + depth: 1, + })), container); + + ok(container.querySelector(".children-pointer"), + "Do show children pointer for non-roots when we are inverted"); + + yield renderComponent(CensusTreeItem(immutableUpdate(TEST_CENSUS_TREE_ITEM_PROPS, { + inverted: false, + item: immutableUpdate(TEST_CENSUS_TREE_ITEM_PROPS.item, { children: undefined }), + })), container); + + ok(!container.querySelector(".children-pointer"), + "Don't show children pointer when non-inverted and no children"); + + yield renderComponent(CensusTreeItem(immutableUpdate(TEST_CENSUS_TREE_ITEM_PROPS, { + inverted: false, + depth: 0, + item: immutableUpdate(TEST_CENSUS_TREE_ITEM_PROPS.item, { children: [{}] }), + })), container); + + ok(container.querySelector(".children-pointer"), + "Do show children pointer when non-inverted and have children"); + + } catch(e) { + ok(false, "Got an error: " + DevToolsUtils.safeErrorString(e)); + } finally { + SimpleTest.finish(); + } + }); + </script> + </pre> +</body> +</html> diff --git a/devtools/client/memory/test/chrome/test_DominatorTreeItem_01.html b/devtools/client/memory/test/chrome/test_DominatorTreeItem_01.html new file mode 100644 index 000000000..56cba7391 --- /dev/null +++ b/devtools/client/memory/test/chrome/test_DominatorTreeItem_01.html @@ -0,0 +1,45 @@ +<!DOCTYPE HTML> +<html> +<!-- +Test that we don't display `JS::ubi::RootList` for the root, and instead show "GC Roots". +--> +<head> + <meta charset="utf-8"> + <title>Tree component test</title> + <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"> +</head> +<body> + <!-- Give the container height so that the whole tree is rendered. --> + <div id="container" style="height: 900px;"></div> + + <pre id="test"> + <script src="head.js" type="application/javascript;version=1.8"></script> + <script type="application/javascript;version=1.8"> + window.onload = Task.async(function* () { + try { + const container = document.getElementById("container"); + + yield renderComponent(DominatorTreeItem({ + item: makeTestDominatorTreeNode({ label: ["other", "JS::ubi::RootList"] }), + depth: 0, + arrow: React.DOM.div(), + focused: true, + getPercentSize: _ => 50, + onViewSourceInDebugger: _ => { }, + }), container); + + ok(container.textContent.indexOf("JS::ubi::RootList") == -1, + "Should not display `JS::ubi::RootList`"); + ok(container.textContent.indexOf("GC Roots") >= 0, + "Should display `GC Roots` instead"); + } catch(e) { + ok(false, "Got an error: " + DevToolsUtils.safeErrorString(e)); + } finally { + SimpleTest.finish(); + } + }); + </script> + </pre> +</body> +</html> diff --git a/devtools/client/memory/test/chrome/test_DominatorTree_01.html b/devtools/client/memory/test/chrome/test_DominatorTree_01.html new file mode 100644 index 000000000..582576e49 --- /dev/null +++ b/devtools/client/memory/test/chrome/test_DominatorTree_01.html @@ -0,0 +1,50 @@ +<!DOCTYPE HTML> +<html> +<!-- +Test that we show a place holder for a subtree we are lazily fetching. +--> +<head> + <meta charset="utf-8"> + <title>Tree component test</title> + <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"> +</head> +<body> + <!-- Give the container height so that the whole tree is rendered. --> + <div id="container" style="height: 900px;"></div> + + <pre id="test"> + <script src="head.js" type="application/javascript;version=1.8"></script> + <script type="application/javascript;version=1.8"> + window.onload = Task.async(function* () { + try { + const container = document.getElementById("container"); + + const root = makeTestDominatorTreeNode({ moreChildrenAvailable: true}); + ok(!root.children); + + const expanded = new Set(); + expanded.add(root.nodeId); + + yield renderComponent(DominatorTreeComponent(immutableUpdate(TEST_DOMINATOR_TREE_PROPS, { + dominatorTree: immutableUpdate(TEST_DOMINATOR_TREE_PROPS.dominatorTree, { + expanded, + root, + state: dominatorTreeState.INCREMENTAL_FETCHING, + activeFetchRequestCount: 1, + }), + })), container); + + ok(container.querySelector(".subtree-fetching"), + "Expanded nodes with more children available, but no children " + + "loaded, should get a placeholder"); + } catch(e) { + ok(false, "Got an error: " + DevToolsUtils.safeErrorString(e)); + } finally { + SimpleTest.finish(); + } + }); + </script> + </pre> +</body> +</html> diff --git a/devtools/client/memory/test/chrome/test_DominatorTree_02.html b/devtools/client/memory/test/chrome/test_DominatorTree_02.html new file mode 100644 index 000000000..ffdac3263 --- /dev/null +++ b/devtools/client/memory/test/chrome/test_DominatorTree_02.html @@ -0,0 +1,50 @@ +<!DOCTYPE HTML> +<html> +<!-- +Test that we show a link to load more children when some (but not all) are loaded. +--> +<head> + <meta charset="utf-8"> + <title>Tree component test</title> + <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"> +</head> +<body> + <!-- Give the container height so that the whole tree is rendered. --> + <div id="container" style="height: 900px;"></div> + + <pre id="test"> + <script src="head.js" type="application/javascript;version=1.8"></script> + <script type="application/javascript;version=1.8"> + window.onload = Task.async(function* () { + try { + const container = document.getElementById("container"); + + const root = makeTestDominatorTreeNode({ moreChildrenAvailable: true }, [ + makeTestDominatorTreeNode({}), + ]); + ok(root.children); + ok(root.moreChildrenAvailable); + + const expanded = new Set(); + expanded.add(root.nodeId); + + yield renderComponent(DominatorTreeComponent(immutableUpdate(TEST_DOMINATOR_TREE_PROPS, { + dominatorTree: immutableUpdate(TEST_DOMINATOR_TREE_PROPS.dominatorTree, { + expanded, + root, + }), + })), container); + + ok(container.querySelector(".more-children"), + "Should get a link to load more children"); + } catch(e) { + ok(false, "Got an error: " + DevToolsUtils.safeErrorString(e)); + } finally { + SimpleTest.finish(); + } + }); + </script> + </pre> +</body> +</html> diff --git a/devtools/client/memory/test/chrome/test_DominatorTree_03.html b/devtools/client/memory/test/chrome/test_DominatorTree_03.html new file mode 100644 index 000000000..e9656dad8 --- /dev/null +++ b/devtools/client/memory/test/chrome/test_DominatorTree_03.html @@ -0,0 +1,75 @@ +<!DOCTYPE HTML> +<html> +<!-- +Test that expanded DominatorTreeItems are correctly rendered and updated +--> +<head> + <meta charset="utf-8"> + <title>Tree component test</title> + <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"> +</head> +<body> + <!-- Give the container height so that the whole tree is rendered. --> + <div id="container" style="height: 900px;"></div> + + <pre id="test"> + <script src="head.js" type="application/javascript;version=1.8"></script> + <script type="application/javascript;version=1.8"> + window.onload = Task.async(function* () { + try { + const container = document.getElementById("container"); + + // simple tree with one root and one child + const root = makeTestDominatorTreeNode( + { moreChildrenAvailable: false }, + [ + makeTestDominatorTreeNode({ moreChildrenAvailable: false }), + ]); + ok(root.children); + + // root node is expanded + const expanded = new Set(); + expanded.add(root.nodeId); + + let component = yield renderComponent( + DominatorTreeComponent(immutableUpdate( + TEST_DOMINATOR_TREE_PROPS, + { + dominatorTree: immutableUpdate( + TEST_DOMINATOR_TREE_PROPS.dominatorTree, + { expanded, root } + ), + })), container); + ok(true, "Dominator tree rendered"); + + is(container.querySelectorAll(".tree-node").length, 2, + "Should display two rows"); + is(container.querySelectorAll(".arrow.open").length, 1, + "Should display one expanded arrow"); + + yield setProps(component, immutableUpdate( + TEST_DOMINATOR_TREE_PROPS, + { + dominatorTree: immutableUpdate( + TEST_DOMINATOR_TREE_PROPS.dominatorTree, + { expanded: new Set(), root } + ) + })); + ok(true, "Dominator tree props updated to collapse all nodes"); + + is(container.querySelectorAll(".tree-node").length, 1, + "Should display only one row"); + is(container.querySelectorAll(".arrow.open").length, 0, + "Should display no expanded arrow"); + + } catch(e) { + ok(false, "Got an error: " + DevToolsUtils.safeErrorString(e)); + } finally { + SimpleTest.finish(); + } + }); + </script> + </pre> +</body> +</html> diff --git a/devtools/client/memory/test/chrome/test_Heap_01.html b/devtools/client/memory/test/chrome/test_Heap_01.html new file mode 100644 index 000000000..5d5e72389 --- /dev/null +++ b/devtools/client/memory/test/chrome/test_Heap_01.html @@ -0,0 +1,50 @@ +<!DOCTYPE HTML> +<html> +<!-- +Test that rendering a dominator tree error is handled correctly. +--> +<head> + <meta charset="utf-8"> + <title>Tree component test</title> + <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"> +</head> +<body> + <div id="container"></div> + <pre id="test"> + <script src="head.js" type="application/javascript;version=1.8"></script> + + <script type="application/javascript;version=1.8"> + window.onload = Task.async(function* () { + try { + ok(React, "Should get React"); + ok(Heap, "Should get Heap"); + + const errorMessage = "Something went wrong!"; + const container = document.getElementById("container"); + + const props = immutableUpdate(TEST_HEAP_PROPS, { + view: { state: viewState.DOMINATOR_TREE, }, + snapshot: immutableUpdate(TEST_HEAP_PROPS.snapshot, { + dominatorTree: { + error: new Error(errorMessage), + state: dominatorTreeState.ERROR, + } + }) + }); + + yield renderComponent(Heap(props), container); + + ok(container.querySelector(".error"), "Should render an error view"); + ok(container.textContent.indexOf(errorMessage) !== -1, + "Should see our error message"); + } catch(e) { + ok(false, "Got an error: " + DevToolsUtils.safeErrorString(e)); + } finally { + SimpleTest.finish(); + } + }); + </script> + </pre> +</body> +</html> diff --git a/devtools/client/memory/test/chrome/test_Heap_02.html b/devtools/client/memory/test/chrome/test_Heap_02.html new file mode 100644 index 000000000..800f1044c --- /dev/null +++ b/devtools/client/memory/test/chrome/test_Heap_02.html @@ -0,0 +1,78 @@ +<!DOCTYPE HTML> +<html> +<!-- +Test that the currently selected view is rendered. +--> +<head> + <meta charset="utf-8"> + <title>Tree component test</title> + <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"> +</head> +<body> + <div id="container"></div> + <pre id="test"> + <script src="head.js" type="application/javascript;version=1.8"></script> + <script type="application/javascript;version=1.8"> + window.onload = Task.async(function* () { + try { + ok(React, "Should get React"); + ok(Heap, "Should get Heap"); + + const errorMessage = "Something went wrong!"; + const container = document.getElementById("container"); + + // Dominator tree view. + + yield renderComponent(Heap(immutableUpdate(TEST_HEAP_PROPS, { + view: { state: viewState.DOMINATOR_TREE, }, + })), container); + + ok(container.querySelector(`[data-state=${dominatorTreeState.LOADED}]`), + "Should render the dominator tree."); + + // Census view. + + yield renderComponent(Heap(immutableUpdate(TEST_HEAP_PROPS, { + view: { state: viewState.CENSUS, }, + })), container); + + ok(container.querySelector(`[data-state=${censusState.SAVED}]`), + "Should render the census."); + + // Diffing view. + + yield renderComponent(Heap(immutableUpdate(TEST_HEAP_PROPS, { + view: { state: viewState.DIFFING, }, + snapshot: null, + diffing: { + firstSnapshotId: null, + secondSnapshotId: null, + census: null, + error: null, + state: diffingState.SELECTING, + }, + })), container); + + ok(container.querySelector(`[data-state=${diffingState.SELECTING}]`), + "Should render the diffing."); + + // Initial view. + + yield renderComponent(Heap(immutableUpdate(TEST_HEAP_PROPS, { + snapshot: null, + diffing: null, + })), container); + + ok(container.querySelector("[data-state=initial]"), + "With no snapshot, nor a diffing, should render initial prompt."); + } catch(e) { + ok(false, "Got an error: " + DevToolsUtils.safeErrorString(e)); + } finally { + SimpleTest.finish(); + } + }); + </script> + </pre> +</body> +</html> diff --git a/devtools/client/memory/test/chrome/test_Heap_03.html b/devtools/client/memory/test/chrome/test_Heap_03.html new file mode 100644 index 000000000..7f0f52255 --- /dev/null +++ b/devtools/client/memory/test/chrome/test_Heap_03.html @@ -0,0 +1,74 @@ +<!DOCTYPE HTML> +<html> +<!-- +Test that we show a throbber while computing and fetching dominator trees, +but not in other dominator tree states. +--> +<head> + <meta charset="utf-8"> + <title>Tree component test</title> + <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"> +</head> +<body> + <div id="container"></div> + <pre id="test"> + <script src="head.js" type="application/javascript;version=1.8"></script> + <script type="application/javascript;version=1.8"> + window.onload = Task.async(function* () { + try { + const container = document.getElementById("container"); + + for (let state of [dominatorTreeState.COMPUTING, dominatorTreeState.FETCHING]) { + yield renderComponent(Heap(immutableUpdate(TEST_HEAP_PROPS, { + view: { state: viewState.DOMINATOR_TREE, }, + snapshot: immutableUpdate(TEST_HEAP_PROPS.snapshot, { + dominatorTree: immutableUpdate(TEST_HEAP_PROPS.snapshot.dominatorTree, { + state, + root: null, + dominatorTreeId: state === dominatorTreeState.FETCHING ? 1 : null, + }), + }), + })), container); + + ok(container.querySelector(".devtools-throbber"), + `Should show a throbber for state = ${state}`); + } + + for (let state of [dominatorTreeState.LOADED, dominatorTreeState.INCREMENTAL_FETCHING]) { + yield renderComponent(Heap(immutableUpdate(TEST_HEAP_PROPS, { + view: { state: viewState.DOMINATOR_TREE, }, + snapshot: immutableUpdate(TEST_HEAP_PROPS.snapshot, { + dominatorTree: immutableUpdate(TEST_HEAP_PROPS.snapshot.dominatorTree, { + state, + activeFetchRequestCount: state === dominatorTreeState.INCREMENTAL_FETCHING ? 1 : undefined, + }), + }), + })), container); + + ok(!container.querySelector(".devtools-throbber"), + `Should not show a throbber for state = ${state}`); + } + + yield renderComponent(Heap(immutableUpdate(TEST_HEAP_PROPS, { + view: { state: viewState.DOMINATOR_TREE, }, + snapshot: immutableUpdate(TEST_HEAP_PROPS.snapshot, { + dominatorTree: { + state: dominatorTreeState.ERROR, + error: new Error("example error for testing"), + }, + }), + })), container); + + ok(!container.querySelector(".devtools-throbber"), + `Should not show a throbber for ERROR state`); + } catch(e) { + ok(false, "Got an error: " + DevToolsUtils.safeErrorString(e)); + } finally { + SimpleTest.finish(); + } + }); + </script> + </pre> +</body> +</html> diff --git a/devtools/client/memory/test/chrome/test_Heap_04.html b/devtools/client/memory/test/chrome/test_Heap_04.html new file mode 100644 index 000000000..ccf4c9c6d --- /dev/null +++ b/devtools/client/memory/test/chrome/test_Heap_04.html @@ -0,0 +1,121 @@ +<!DOCTYPE HTML> +<html> +<!-- +Test that we show the "hey you're not recording allocation stacks" message at the appropriate times. +--> +<head> + <meta charset="utf-8"> + <title>Tree component test</title> + <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"> +</head> +<body> + <div id="container"></div> + <pre id="test"> + <script src="head.js" type="application/javascript;version=1.8"></script> + <script type="application/javascript;version=1.8"> + window.onload = Task.async(function* () { + try { + const container = document.getElementById("container"); + + yield renderComponent(Heap(immutableUpdate(TEST_HEAP_PROPS, { + snapshot: immutableUpdate(TEST_HEAP_PROPS.snapshot, { + census: immutableUpdate(TEST_HEAP_PROPS.snapshot.census, { + report: { + bytes: 1, + totalBytes: 1, + count: 1, + totalCount: 1, + id: 1, + parent: undefined, + children: [ + { + name: "noStack", + bytes: 1, + totalBytes: 1, + count: 1, + totalCount: 1, + children: undefined, + id: 3, + parent: 1, + } + ] + }, + display: censusDisplays.allocationStack, + }), + }), + })), container); + + ok(container.querySelector(".no-allocation-stacks"), + "When there are no allocation stacks, we should show the message"); + + yield renderComponent(Heap(immutableUpdate(TEST_HEAP_PROPS, { + snapshot: immutableUpdate(TEST_HEAP_PROPS.snapshot, { + census: immutableUpdate(TEST_HEAP_PROPS.snapshot.census, { + report: { + bytes: 1, + totalBytes: 1, + count: 1, + totalCount: 1, + id: 1, + parent: undefined, + children: [ + { + name: Cu.getJSTestingFunctions().saveStack(), + bytes: 1, + totalBytes: 1, + count: 1, + totalCount: 1, + children: undefined, + id: 2, + parent: 1, + }, + { + name: "noStack", + bytes: 1, + totalBytes: 1, + count: 1, + totalCount: 1, + children: undefined, + id: 3, + parent: 1, + } + ] + }, + display: censusDisplays.allocationStack, + }), + }), + })), container); + + ok(!container.querySelector(".no-allocation-stacks"), + "When there are allocation stacks, we should not show the message"); + + yield renderComponent(Heap(immutableUpdate(TEST_HEAP_PROPS, { + snapshot: immutableUpdate(TEST_HEAP_PROPS.snapshot, { + census: immutableUpdate(TEST_HEAP_PROPS.snapshot.census, { + report: { + bytes: 1, + totalBytes: 1, + count: 1, + totalCount: 1, + id: 1, + parent: undefined, + children: undefined + }, + display: censusDisplays.allocationStack, + }), + }), + })), container); + + ok(!container.querySelector(".no-allocation-stacks"), + "When there isn't census data, we should not show the message"); + } catch(e) { + ok(false, "Got an error: " + DevToolsUtils.safeErrorString(e)); + } finally { + SimpleTest.finish(); + } + }); + </script> + </pre> +</body> +</html> diff --git a/devtools/client/memory/test/chrome/test_Heap_05.html b/devtools/client/memory/test/chrome/test_Heap_05.html new file mode 100644 index 000000000..14365e3ab --- /dev/null +++ b/devtools/client/memory/test/chrome/test_Heap_05.html @@ -0,0 +1,132 @@ +<!DOCTYPE HTML> +<html> +<!-- +Test that we show a message when the census results are empty. +--> +<head> + <meta charset="utf-8"> + <title>Tree component test</title> + <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"> +</head> +<body> + <div id="container"></div> + <pre id="test"> + <script src="head.js" type="application/javascript;version=1.8"></script> + <script type="application/javascript;version=1.8"> + window.onload = Task.async(function* () { + try { + const container = document.getElementById("container"); + + yield renderComponent(Heap(immutableUpdate(TEST_HEAP_PROPS, { + snapshot: immutableUpdate(TEST_HEAP_PROPS.snapshot, { + census: immutableUpdate(TEST_HEAP_PROPS.snapshot.census, { + report: { + bytes: 1, + totalBytes: 1, + count: 1, + totalCount: 1, + id: 1, + parent: undefined, + children: [ + { + name: Cu.getJSTestingFunctions().saveStack(), + bytes: 1, + totalBytes: 1, + count: 1, + totalCount: 1, + children: undefined, + id: 2, + parent: 1, + }, + { + name: "noStack", + bytes: 1, + totalBytes: 1, + count: 1, + totalCount: 1, + children: undefined, + id: 3, + parent: 1, + } + ] + }, + display: censusDisplays.allocationStack, + }), + }), + })), container); + + ok(!container.querySelector(".empty"), + "When the report is not empty, we should not show the empty message"); + + // Empty Census Report + + const emptyCensus = { + report: { + bytes: 0, + totalBytes: 0, + count: 0, + totalCount: 0, + id: 1, + parent: undefined, + children: undefined, + }, + parentMap: Object.create(null), + display: censusDisplays.allocationStack, + filter: null, + expanded: new Immutable.Set(), + focused: null, + state: censusState.SAVED, + }; + + yield renderComponent(Heap(immutableUpdate(TEST_HEAP_PROPS, { + snapshot: immutableUpdate(TEST_HEAP_PROPS.snapshot, { + census: immutableUpdate(TEST_HEAP_PROPS.snapshot.census, emptyCensus), + }), + })), container); + + ok(container.querySelector(".empty"), + "When the report is empty in census view, we show the empty message"); + ok(container.textContent.indexOf(L10N.getStr("heapview.empty")) >= 0); + + // Empty Diffing Report + + yield renderComponent(Heap(immutableUpdate(TEST_HEAP_PROPS, { + view: { state: viewState.DIFFING, }, + diffing: { + firstSnapshotId: 1, + secondSnapshotId: 2, + census: emptyCensus, + state: diffingState.TOOK_DIFF, + }, + snapshot: null, + })), container); + + ok(container.querySelector(".empty"), + "When the report is empty in diffing view, the empty message is shown"); + ok(container.textContent.indexOf(L10N.getStr("heapview.no-difference")) >= 0); + + // Empty Filtered Census + + yield renderComponent(Heap(immutableUpdate(TEST_HEAP_PROPS, { + snapshot: immutableUpdate(TEST_HEAP_PROPS.snapshot, { + census: immutableUpdate(TEST_HEAP_PROPS.snapshot.census, immutableUpdate(emptyCensus, { + filter: "zzzz" + })), + }), + })), container); + + ok(container.querySelector(".empty"), + "When the report is empty in census view w/ filter, we show the empty message"); + ok(container.textContent.indexOf(L10N.getStr("heapview.none-match")) >= 0); + + } catch(e) { + ok(false, "Got an error: " + DevToolsUtils.safeErrorString(e)); + } finally { + SimpleTest.finish(); + } + }); + </script> + </pre> +</body> +</html> diff --git a/devtools/client/memory/test/chrome/test_List_01.html b/devtools/client/memory/test/chrome/test_List_01.html new file mode 100644 index 000000000..911a7bc77 --- /dev/null +++ b/devtools/client/memory/test/chrome/test_List_01.html @@ -0,0 +1,74 @@ +<!DOCTYPE HTML> +<html> +<!-- +Test to verify the delete button calls the onDelete handler for an item +--> +<head> + <meta charset="utf-8"> + <title>Tree component test</title> + <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"> +</head> +<body> + <div id="container"></div> + + <pre id="test"> + <script src="head.js" type="application/javascript;version=1.8"></script> + <script type="application/javascript;version=1.8"> + window.onload = Task.async(function* () { + try { + const container = document.getElementById("container"); + + let deletedSnapshots = []; + + let snapshots = [ TEST_SNAPSHOT, TEST_SNAPSHOT, TEST_SNAPSHOT ] + .map((snapshot, index) => immutableUpdate(snapshot, { + index: snapshot.index + index + })); + + yield renderComponent( + List({ + itemComponent: SnapshotListItem, + onClick: noop, + onDelete: (item) => deletedSnapshots.push(item), + items: snapshots + }), + container + ); + + let deleteButtons = container.querySelectorAll('.delete'); + + is(container.querySelectorAll('.snapshot-list-item').length, 3, + "There are 3 list items\n"); + is(deletedSnapshots.length, 0, + "Not snapshots have been deleted\n"); + + deleteButtons[1].click(); + + is(deletedSnapshots.length, 1, "One snapshot was deleted\n"); + is(deletedSnapshots[0], snapshots[1], + "Deleted snapshot was added to the deleted list\n"); + + deleteButtons[0].click(); + + is(deletedSnapshots.length, 2, "Two snapshots were deleted\n"); + is(deletedSnapshots[1], snapshots[0], + "Deleted snapshot was added to the deleted list\n"); + + deleteButtons[2].click(); + + is(deletedSnapshots.length, 3, "Three snapshots were deleted\n"); + is(deletedSnapshots[2], snapshots[2], + "Deleted snapshot was added to the deleted list\n"); + + + } catch(e) { + ok(false, "Got an error: " + DevToolsUtils.safeErrorString(e)); + } finally { + SimpleTest.finish(); + } + }); + </script> + </pre> +</body> +</html> diff --git a/devtools/client/memory/test/chrome/test_ShortestPaths_01.html b/devtools/client/memory/test/chrome/test_ShortestPaths_01.html new file mode 100644 index 000000000..e2ad1867a --- /dev/null +++ b/devtools/client/memory/test/chrome/test_ShortestPaths_01.html @@ -0,0 +1,112 @@ +<!DOCTYPE HTML> +<html> +<!-- +Test that the ShortestPaths component properly renders a graph of the merged shortest paths. +--> +<head> + <meta charset="utf-8"> + <title>Tree component test</title> + <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"> + + <script type="application/javascript" + src="chrome://devtools/content/shared/vendor/d3.js"> + </script> + <script type="application/javascript" + src="chrome://devtools/content/shared/vendor/dagre-d3.js"> + </script> +</head> +<body> + <!-- Give the container height so that the whole tree is rendered. --> + <div id="container" style="height: 900px;"></div> + + <pre id="test"> + <script src="head.js" type="application/javascript;version=1.8"></script> + <script type="application/javascript;version=1.8"> + window.onload = Task.async(function* () { + try { + const container = document.getElementById("container"); + + yield renderComponent(ShortestPaths(TEST_SHORTEST_PATHS_PROPS), container); + + let found1 = false; + let found2 = false; + let found3 = false; + + let found1to2 = false; + let found1to3 = false; + let found2to3 = false; + + const tspans = [...container.querySelectorAll("tspan")]; + for (let el of tspans) { + const text = el.textContent.trim(); + dumpn("tspan's text = " + text); + + switch (text) { + // Nodes + + case "other › SomeType @ 0x1": { + ok(!found1, "Should only find node 1 once"); + found1 = true; + break; + } + + case "other › SomeType @ 0x2": { + ok(!found2, "Should only find node 2 once"); + found2 = true; + break; + } + + case "other › SomeType @ 0x3": { + ok(!found3, "Should only find node 3 once"); + found3 = true; + break; + } + + // Edges + + case "1->2": { + ok(!found1to2, "Should only find edge 1->2 once"); + found1to2 = true; + break; + } + + case "1->3": { + ok(!found1to3, "Should only find edge 1->3 once"); + found1to3 = true; + break; + } + + case "2->3": { + ok(!found2to3, "Should only find edge 2->3 once"); + found2to3 = true; + break; + } + + // Unexpected + + default: { + ok(false, `Unexpected tspan: ${text}`); + break; + } + } + } + + ok(found1, "Should have rendered node 1"); + ok(found2, "Should have rendered node 2"); + ok(found3, "Should have rendered node 3"); + + ok(found1to2, "Should have rendered edge 1->2"); + ok(found1to3, "Should have rendered edge 1->3"); + ok(found2to3, "Should have rendered edge 2->3"); + + } catch(e) { + ok(false, "Got an error: " + DevToolsUtils.safeErrorString(e)); + } finally { + SimpleTest.finish(); + } + }); + </script> + </pre> +</body> +</html> diff --git a/devtools/client/memory/test/chrome/test_ShortestPaths_02.html b/devtools/client/memory/test/chrome/test_ShortestPaths_02.html new file mode 100644 index 000000000..cb6d48faa --- /dev/null +++ b/devtools/client/memory/test/chrome/test_ShortestPaths_02.html @@ -0,0 +1,45 @@ +<!DOCTYPE HTML> +<html> +<!-- +Test that the ShortestPaths component renders a suggestion to select a node when there is no graph. +--> +<head> + <meta charset="utf-8"> + <title>Tree component test</title> + <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"> + + <script type="application/javascript" + src="chrome://devtools/content/shared/vendor/d3.js"> + </script> + <script type="application/javascript" + src="chrome://devtools/content/shared/vendor/dagre-d3.js"> + </script> +</head> +<body> + <!-- Give the container height so that the whole tree is rendered. --> + <div id="container" style="height: 900px;"></div> + + <pre id="test"> + <script src="head.js" type="application/javascript;version=1.8"></script> + <script type="application/javascript;version=1.8"> + window.onload = Task.async(function* () { + try { + const container = document.getElementById("container"); + + yield renderComponent(ShortestPaths(immutableUpdate(TEST_SHORTEST_PATHS_PROPS, + { graph: null })), + container); + + ok(container.textContent.indexOf(L10N.getStr("shortest-paths.select-node")) !== -1, + "The node selection prompt is displayed"); + } catch(e) { + ok(false, "Got an error: " + DevToolsUtils.safeErrorString(e)); + } finally { + SimpleTest.finish(); + } + }); + </script> + </pre> +</body> +</html> diff --git a/devtools/client/memory/test/chrome/test_SnapshotListItem_01.html b/devtools/client/memory/test/chrome/test_SnapshotListItem_01.html new file mode 100644 index 000000000..0081496ce --- /dev/null +++ b/devtools/client/memory/test/chrome/test_SnapshotListItem_01.html @@ -0,0 +1,53 @@ +<!DOCTYPE HTML> +<html> +<!-- +Test to verify that the delete button only shows up for a snapshot when it has a +path. +--> +<head> + <meta charset="utf-8"> + <title>Tree component test</title> + <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"> +</head> +<body> + <div id="container"></div> + + <pre id="test"> + <script src="head.js" type="application/javascript;version=1.8"></script> + <script type="application/javascript;version=1.8"> + window.onload = Task.async(function* () { + try { + const container = document.getElementById("container"); + + yield renderComponent( + SnapshotListItem(TEST_SNAPSHOT_LIST_ITEM_PROPS), + container + ); + + ok(container.querySelector('.delete'), + "Should have delete button when there is a path"); + + const pathlessProps = immutableUpdate( + TEST_SNAPSHOT_LIST_ITEM_PROPS, + {item: immutableUpdate(TEST_SNAPSHOT, {path: null})} + ); + + yield renderComponent( + SnapshotListItem(pathlessProps), + container + ); + + ok(!container.querySelector('.delete'), + "No delete button should be found if there is no path\n"); + + } catch(e) { + ok(false, "Got an error: " + DevToolsUtils.safeErrorString(e)); + } finally { + SimpleTest.finish(); + } + }); + </script> + </pre> +</body> +</html> diff --git a/devtools/client/memory/test/chrome/test_Toolbar_01.html b/devtools/client/memory/test/chrome/test_Toolbar_01.html new file mode 100644 index 000000000..57546df83 --- /dev/null +++ b/devtools/client/memory/test/chrome/test_Toolbar_01.html @@ -0,0 +1,47 @@ +<!DOCTYPE HTML> +<html> +<!-- +Test that the Toolbar component shows the view switcher only at the appropriate times. +--> +<head> + <meta charset="utf-8"> + <title>Tree component test</title> + <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"> +</head> +<body> + <div id="container"></div> + <pre id="test"> + <script src="head.js" type="application/javascript;version=1.8"></script> + <script type="application/javascript;version=1.8"> + window.onload = Task.async(function* () { + try { + const container = document.getElementById("container"); + + // Census and dominator tree views. + + for (let view of [viewState.CENSUS, viewState.DOMINATOR_TREE]) { + yield renderComponent(Toolbar(immutableUpdate(TEST_TOOLBAR_PROPS, { + view: { state: view }, + })), container); + + ok(container.querySelector("#select-view"), + `The view selector is shown in view = ${view}`); + } + + yield renderComponent(Toolbar(immutableUpdate(TEST_TOOLBAR_PROPS, { + view: { state: viewState.DIFFING, }, + })), container); + + ok(!container.querySelector("#select-view"), + "The view selector is NOT shown in the DIFFING view"); + } catch(e) { + ok(false, "Got an error: " + DevToolsUtils.safeErrorString(e)); + } finally { + SimpleTest.finish(); + } + }); + </script> + </pre> +</body> +</html> diff --git a/devtools/client/memory/test/chrome/test_TreeMap_01.html b/devtools/client/memory/test/chrome/test_TreeMap_01.html new file mode 100644 index 000000000..cdc293854 --- /dev/null +++ b/devtools/client/memory/test/chrome/test_TreeMap_01.html @@ -0,0 +1,44 @@ +<!DOCTYPE HTML> +<html> +<!-- +Test that the Tree Map correctly renders onto 2 managed canvases. +--> +<head> + <meta charset="utf-8"> + <title>Tree component test</title> + <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"> + + <script type="application/javascript" + src="chrome://devtools/content/shared/vendor/d3.js"> + </script> +</head> +<body> + <!-- Give the container height so that the whole tree is rendered. --> + <div id="container" style="height: 900px;"></div> + + <pre id="test"> + <script src="head.js" type="application/javascript;version=1.8"></script> + <script type="application/javascript;version=1.8"> + window.onload = Task.async(function*() { + try { + const container = document.getElementById("container"); + + yield renderComponent(TreeMap(TEST_TREE_MAP_PROPS), container); + + let treeMapContainer = container.querySelector(".tree-map-container"); + ok(treeMapContainer, "Component creates a container"); + + let canvases = treeMapContainer.querySelectorAll("canvas"); + is(canvases.length, 2, "Creates 2 canvases"); + + } catch(e) { + ok(false, "Got an error: " + DevToolsUtils.safeErrorString(e)); + } finally { + SimpleTest.finish(); + } + }); + </script> + </pre> +</body> +</html> |