mirror of
https://repo.dactyloidae.xyz/Dactyloidae/UXP.git
synced 2026-09-06 07:48:38 +09:00
import FIREFOX_52_6_0esr_RELEASE from mozilla-esr52 hg repo
This commit is contained in:
commit
dcd9973243
150858 changed files with 23884658 additions and 0 deletions
6
devtools/client/memory/test/browser/.eslintrc.js
Normal file
6
devtools/client/memory/test/browser/.eslintrc.js
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
"use strict";
|
||||
|
||||
module.exports = {
|
||||
// Extend from the shared list of defined globals for mochitests.
|
||||
"extends": "../../../../.eslintrc.mochitests.js"
|
||||
};
|
||||
31
devtools/client/memory/test/browser/browser.ini
Normal file
31
devtools/client/memory/test/browser/browser.ini
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
[DEFAULT]
|
||||
tags = devtools devtools-memory
|
||||
subsuite = devtools
|
||||
support-files =
|
||||
head.js
|
||||
doc_big_tree.html
|
||||
doc_empty.html
|
||||
doc_steady_allocation.html
|
||||
!/devtools/client/framework/test/shared-head.js
|
||||
!/devtools/client/framework/test/shared-redux-head.js
|
||||
|
||||
[browser_memory_allocationStackDisplay_01.js]
|
||||
skip-if = debug # bug 1219554
|
||||
[browser_memory_displays_01.js]
|
||||
[browser_memory_clear_snapshots.js]
|
||||
[browser_memory_diff_01.js]
|
||||
[browser_memory_dominator_trees_01.js]
|
||||
[browser_memory_dominator_trees_02.js]
|
||||
[browser_memory_filter_01.js]
|
||||
[browser_memory_individuals_01.js]
|
||||
[browser_memory_keyboard.js]
|
||||
[browser_memory_keyboard-snapshot-list.js]
|
||||
[browser_memory_no_allocation_stacks.js]
|
||||
[browser_memory_no_auto_expand.js]
|
||||
skip-if = debug # bug 1219554
|
||||
[browser_memory_percents_01.js]
|
||||
[browser_memory_refresh_does_not_leak.js]
|
||||
[browser_memory_simple_01.js]
|
||||
[browser_memory_transferHeapSnapshot_e10s_01.js]
|
||||
[browser_memory_tree_map-01.js]
|
||||
[browser_memory_tree_map-02.js]
|
||||
|
|
@ -0,0 +1,39 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
// Sanity test that we can show allocation stack displays in the tree.
|
||||
|
||||
"use strict";
|
||||
|
||||
const { toggleRecordingAllocationStacks } = require("devtools/client/memory/actions/allocations");
|
||||
const { takeSnapshotAndCensus } = require("devtools/client/memory/actions/snapshot");
|
||||
const censusDisplayActions = require("devtools/client/memory/actions/census-display");
|
||||
const { viewState } = require("devtools/client/memory/constants");
|
||||
const { changeView } = require("devtools/client/memory/actions/view");
|
||||
|
||||
const TEST_URL = "http://example.com/browser/devtools/client/memory/test/browser/doc_steady_allocation.html";
|
||||
|
||||
this.test = makeMemoryTest(TEST_URL, function* ({ tab, panel }) {
|
||||
const heapWorker = panel.panelWin.gHeapAnalysesClient;
|
||||
const front = panel.panelWin.gFront;
|
||||
const { getState, dispatch } = panel.panelWin.gStore;
|
||||
const doc = panel.panelWin.document;
|
||||
|
||||
dispatch(changeView(viewState.CENSUS));
|
||||
|
||||
dispatch(censusDisplayActions.setCensusDisplay(censusDisplays.invertedAllocationStack));
|
||||
is(getState().censusDisplay.breakdown.by, "allocationStack");
|
||||
|
||||
yield dispatch(toggleRecordingAllocationStacks(front));
|
||||
ok(getState().allocations.recording);
|
||||
|
||||
// Let some allocations build up.
|
||||
yield waitForTime(500);
|
||||
|
||||
yield dispatch(takeSnapshotAndCensus(front, heapWorker));
|
||||
|
||||
const names = [...doc.querySelectorAll(".frame-link-function-display-name")];
|
||||
ok(names.length, "Should have rendered some allocation stack tree items");
|
||||
ok(names.some(e => !!e.textContent.trim()),
|
||||
"And at least some of them should have functionDisplayNames");
|
||||
});
|
||||
|
|
@ -0,0 +1,36 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
/**
|
||||
* Tests taking and then clearing snapshots.
|
||||
*/
|
||||
|
||||
const { treeMapState } = require("devtools/client/memory/constants");
|
||||
const TEST_URL = "http://example.com/browser/devtools/client/memory/test/browser/doc_steady_allocation.html";
|
||||
|
||||
this.test = makeMemoryTest(TEST_URL, function* ({ tab, panel }) {
|
||||
const { gStore, document } = panel.panelWin;
|
||||
const { getState, dispatch } = gStore;
|
||||
|
||||
let snapshotEls = document.querySelectorAll("#memory-tool-container .list li");
|
||||
is(getState().snapshots.length, 0, "Starts with no snapshots in store");
|
||||
is(snapshotEls.length, 0, "No snapshots visible");
|
||||
|
||||
info("Take two snapshots");
|
||||
takeSnapshot(panel.panelWin);
|
||||
takeSnapshot(panel.panelWin);
|
||||
yield waitUntilState(gStore, state =>
|
||||
state.snapshots.length === 2 &&
|
||||
state.snapshots[0].treeMap && state.snapshots[1].treeMap &&
|
||||
state.snapshots[0].treeMap.state === treeMapState.SAVED &&
|
||||
state.snapshots[1].treeMap.state === treeMapState.SAVED);
|
||||
|
||||
snapshotEls = document.querySelectorAll("#memory-tool-container .list li");
|
||||
is(snapshotEls.length, 2, "Two snapshots visible");
|
||||
|
||||
info("Click on Clear Snapshots");
|
||||
yield clearSnapshots(panel.panelWin);
|
||||
is(getState().snapshots.length, 0, "No snapshots in store");
|
||||
snapshotEls = document.querySelectorAll("#memory-tool-container .list li");
|
||||
is(snapshotEls.length, 0, "No snapshot visible");
|
||||
});
|
||||
|
|
@ -0,0 +1,74 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
// Test diffing.
|
||||
|
||||
"use strict";
|
||||
|
||||
const {
|
||||
snapshotState,
|
||||
diffingState,
|
||||
treeMapState
|
||||
} = require("devtools/client/memory/constants");
|
||||
|
||||
const TEST_URL = "http://example.com/browser/devtools/client/memory/test/browser/doc_steady_allocation.html";
|
||||
|
||||
this.test = makeMemoryTest(TEST_URL, function* ({ tab, panel }) {
|
||||
const heapWorker = panel.panelWin.gHeapAnalysesClient;
|
||||
const front = panel.panelWin.gFront;
|
||||
const store = panel.panelWin.gStore;
|
||||
const { getState, dispatch } = store;
|
||||
const doc = panel.panelWin.document;
|
||||
|
||||
ok(!getState().diffing, "Not diffing by default.");
|
||||
|
||||
// Take two snapshots.
|
||||
const takeSnapshotButton = doc.getElementById("take-snapshot");
|
||||
EventUtils.synthesizeMouseAtCenter(takeSnapshotButton, {}, panel.panelWin);
|
||||
yield waitForTime(1000);
|
||||
EventUtils.synthesizeMouseAtCenter(takeSnapshotButton, {}, panel.panelWin);
|
||||
|
||||
// Enable diffing mode.
|
||||
const diffButton = doc.getElementById("diff-snapshots");
|
||||
EventUtils.synthesizeMouseAtCenter(diffButton, {}, panel.panelWin);
|
||||
yield waitUntilState(store,
|
||||
state =>
|
||||
!!state.diffing &&
|
||||
state.diffing.state === diffingState.SELECTING);
|
||||
ok(true, "Clicking the diffing button put us into the diffing state.");
|
||||
is(getDisplayedSnapshotStatus(doc), "Select the baseline snapshot");
|
||||
|
||||
yield waitUntilState(store, state =>
|
||||
state.snapshots.length === 2 &&
|
||||
state.snapshots[0].treeMap && state.snapshots[1].treeMap &&
|
||||
state.snapshots[0].treeMap.state === treeMapState.SAVED &&
|
||||
state.snapshots[1].treeMap.state === treeMapState.SAVED);
|
||||
|
||||
const listItems = [...doc.querySelectorAll(".snapshot-list-item")];
|
||||
is(listItems.length, 2, "Should have two snapshot list items");
|
||||
|
||||
// Select the first snapshot.
|
||||
EventUtils.synthesizeMouseAtCenter(listItems[0], {}, panel.panelWin);
|
||||
yield waitUntilState(store,
|
||||
state =>
|
||||
state.diffing.state === diffingState.SELECTING &&
|
||||
state.diffing.firstSnapshotId);
|
||||
is(getDisplayedSnapshotStatus(doc),
|
||||
"Select the snapshot to compare to the baseline");
|
||||
|
||||
// Select the second snapshot.
|
||||
EventUtils.synthesizeMouseAtCenter(listItems[1], {}, panel.panelWin);
|
||||
yield waitUntilState(store,
|
||||
state =>
|
||||
state.diffing.state === diffingState.TAKING_DIFF);
|
||||
ok(true, "Selecting two snapshots for diffing triggers computing the diff");
|
||||
|
||||
// .startsWith because the ellipsis is lost in translation.
|
||||
ok(getDisplayedSnapshotStatus(doc).startsWith("Computing difference"));
|
||||
|
||||
yield waitUntilState(store,
|
||||
state => state.diffing.state === diffingState.TOOK_DIFF);
|
||||
ok(true, "And that diff is computed successfully");
|
||||
is(getDisplayedSnapshotStatus(doc), null, "No status text anymore");
|
||||
ok(doc.querySelector(".heap-tree-item"), "And instead we should be showing the tree");
|
||||
});
|
||||
|
|
@ -0,0 +1,41 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
"use strict";
|
||||
|
||||
/**
|
||||
* Tests that the heap tree renders rows based on the display
|
||||
*/
|
||||
|
||||
const TEST_URL = "http://example.com/browser/devtools/client/memory/test/browser/doc_steady_allocation.html";
|
||||
const { viewState, censusState } = require("devtools/client/memory/constants");
|
||||
const { changeView } = require("devtools/client/memory/actions/view");
|
||||
|
||||
this.test = makeMemoryTest(TEST_URL, function* ({ tab, panel }) {
|
||||
const { gStore, document } = panel.panelWin;
|
||||
|
||||
const { dispatch } = panel.panelWin.gStore;
|
||||
|
||||
function $$(selector) {
|
||||
return [...document.querySelectorAll(selector)];
|
||||
}
|
||||
dispatch(changeView(viewState.CENSUS));
|
||||
|
||||
yield takeSnapshot(panel.panelWin);
|
||||
|
||||
yield waitUntilState(gStore, state =>
|
||||
state.snapshots[0].census &&
|
||||
state.snapshots[0].census.state === censusState.SAVED);
|
||||
|
||||
info("Check coarse type heap view");
|
||||
["Function", "js::Shape", "Object", "strings"].forEach(findNameCell);
|
||||
|
||||
yield setCensusDisplay(panel.panelWin, censusDisplays.allocationStack);
|
||||
info("Check allocation stack heap view");
|
||||
[L10N.getStr("tree-item.nostack")].forEach(findNameCell);
|
||||
|
||||
function findNameCell(name) {
|
||||
const el = $$(".tree .heap-tree-item-name span")
|
||||
.find(e => e.textContent === name);
|
||||
ok(el, `Found heap tree item cell for ${name}.`);
|
||||
}
|
||||
});
|
||||
|
|
@ -0,0 +1,147 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
// Sanity test for dominator trees, their focused nodes, and keyboard navigating
|
||||
// through nodes across incrementally fetching subtrees.
|
||||
|
||||
"use strict";
|
||||
|
||||
const {
|
||||
dominatorTreeState,
|
||||
viewState,
|
||||
} = require("devtools/client/memory/constants");
|
||||
const {
|
||||
expandDominatorTreeNode,
|
||||
} = require("devtools/client/memory/actions/snapshot");
|
||||
const { changeView } = require("devtools/client/memory/actions/view");
|
||||
|
||||
const TEST_URL = "http://example.com/browser/devtools/client/memory/test/browser/doc_big_tree.html";
|
||||
|
||||
this.test = makeMemoryTest(TEST_URL, function* ({ tab, panel }) {
|
||||
// Taking snapshots and computing dominator trees is slow :-/
|
||||
requestLongerTimeout(4);
|
||||
|
||||
const store = panel.panelWin.gStore;
|
||||
const { getState, dispatch } = store;
|
||||
const doc = panel.panelWin.document;
|
||||
|
||||
dispatch(changeView(viewState.DOMINATOR_TREE));
|
||||
|
||||
// Take a snapshot.
|
||||
|
||||
const takeSnapshotButton = doc.getElementById("take-snapshot");
|
||||
EventUtils.synthesizeMouseAtCenter(takeSnapshotButton, {}, panel.panelWin);
|
||||
|
||||
// Wait for the dominator tree to be computed and fetched.
|
||||
|
||||
yield waitUntilDominatorTreeState(store, [dominatorTreeState.LOADED]);
|
||||
ok(true, "Computed and fetched the dominator tree.");
|
||||
|
||||
// Expand all the dominator tree nodes that are eagerly fetched, except for
|
||||
// the leaves which will trigger fetching their lazily loaded subtrees.
|
||||
|
||||
const id = getState().snapshots[0].id;
|
||||
const root = getState().snapshots[0].dominatorTree.root;
|
||||
(function expandAllEagerlyFetched(node = root) {
|
||||
if (!node.moreChildrenAvailable || node.children) {
|
||||
dispatch(expandDominatorTreeNode(id, node));
|
||||
}
|
||||
|
||||
if (node.children) {
|
||||
for (let child of node.children) {
|
||||
expandAllEagerlyFetched(child);
|
||||
}
|
||||
}
|
||||
}());
|
||||
|
||||
// Find the deepest eagerly loaded node: one which has more children but none
|
||||
// of them are loaded.
|
||||
|
||||
const deepest = (function findDeepest(node = root) {
|
||||
if (node.moreChildrenAvailable && !node.children) {
|
||||
return node;
|
||||
}
|
||||
|
||||
if (node.children) {
|
||||
for (let child of node.children) {
|
||||
const found = findDeepest(child);
|
||||
if (found) {
|
||||
return found;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}());
|
||||
|
||||
ok(deepest, "Found the deepest node");
|
||||
ok(!getState().snapshots[0].dominatorTree.expanded.has(deepest.nodeId),
|
||||
"The deepest node should not be expanded");
|
||||
|
||||
// Select the deepest node.
|
||||
|
||||
EventUtils.synthesizeMouseAtCenter(doc.querySelector(`.node-${deepest.nodeId}`),
|
||||
{},
|
||||
panel.panelWin);
|
||||
yield waitUntilState(store, state =>
|
||||
state.snapshots[0].dominatorTree.focused.nodeId === deepest.nodeId);
|
||||
ok(doc.querySelector(`.node-${deepest.nodeId}`).classList.contains("focused"),
|
||||
"The deepest node should be focused now");
|
||||
|
||||
// Expand the deepest node, which triggers an incremental fetch of its lazily
|
||||
// loaded subtree.
|
||||
|
||||
EventUtils.synthesizeKey("VK_RIGHT", {}, panel.panelWin);
|
||||
yield waitUntilState(store, state =>
|
||||
state.snapshots[0].dominatorTree.expanded.has(deepest.nodeId));
|
||||
is(getState().snapshots[0].dominatorTree.state,
|
||||
dominatorTreeState.INCREMENTAL_FETCHING,
|
||||
"Expanding the deepest node should start an incremental fetch of its subtree");
|
||||
ok(doc.querySelector(`.node-${deepest.nodeId}`).classList.contains("focused"),
|
||||
"The deepest node should still be focused after expansion");
|
||||
|
||||
// Wait for the incremental fetch to complete.
|
||||
|
||||
yield waitUntilState(store, state =>
|
||||
state.snapshots[0].dominatorTree.state === dominatorTreeState.LOADED);
|
||||
ok(true, "And the incremental fetch completes.");
|
||||
ok(doc.querySelector(`.node-${deepest.nodeId}`).classList.contains("focused"),
|
||||
"The deepest node should still be focused after we have loaded its children");
|
||||
|
||||
// Find the most up-to-date version of the node whose children we just
|
||||
// incrementally fetched.
|
||||
|
||||
const newDeepest = (function findNewDeepest(node = getState().snapshots[0].dominatorTree.root) {
|
||||
if (node.nodeId === deepest.nodeId) {
|
||||
return node;
|
||||
}
|
||||
|
||||
if (node.children) {
|
||||
for (let child of node.children) {
|
||||
const found = findNewDeepest(child);
|
||||
if (found) {
|
||||
return found;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}());
|
||||
|
||||
ok(newDeepest, "We found the up-to-date version of deepest");
|
||||
ok(newDeepest.children, "And its children are loaded");
|
||||
ok(newDeepest.children.length, "And there are more than 0 children");
|
||||
|
||||
const firstChild = newDeepest.children[0];
|
||||
ok(firstChild, "deepest should have a first child");
|
||||
ok(doc.querySelector(`.node-${firstChild.nodeId}`),
|
||||
"and the first child should exist in the dom");
|
||||
|
||||
// Select the newly loaded first child by pressing the right arrow once more.
|
||||
|
||||
EventUtils.synthesizeKey("VK_RIGHT", {}, panel.panelWin);
|
||||
yield waitUntilState(store, state =>
|
||||
state.snapshots[0].dominatorTree.focused === firstChild);
|
||||
ok(doc.querySelector(`.node-${firstChild.nodeId}`).classList.contains("focused"),
|
||||
"The first child should now be focused");
|
||||
});
|
||||
|
|
@ -0,0 +1,64 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
// Integration test for mouse interaction in the dominator tree
|
||||
|
||||
"use strict";
|
||||
|
||||
const {
|
||||
dominatorTreeState,
|
||||
viewState,
|
||||
} = require("devtools/client/memory/constants");
|
||||
const { changeView } = require("devtools/client/memory/actions/view");
|
||||
|
||||
const TEST_URL = "http://example.com/browser/devtools/client/memory/test/browser/doc_steady_allocation.html";
|
||||
|
||||
function clickOnNodeArrow(node, panel) {
|
||||
EventUtils.synthesizeMouseAtCenter(node.querySelector(".arrow"),
|
||||
{}, panel.panelWin);
|
||||
}
|
||||
|
||||
this.test = makeMemoryTest(TEST_URL, function* ({ panel }) {
|
||||
// Taking snapshots and computing dominator trees is slow :-/
|
||||
requestLongerTimeout(4);
|
||||
|
||||
const store = panel.panelWin.gStore;
|
||||
const { getState, dispatch } = store;
|
||||
const doc = panel.panelWin.document;
|
||||
|
||||
dispatch(changeView(viewState.DOMINATOR_TREE));
|
||||
|
||||
// Take a snapshot.
|
||||
const takeSnapshotButton = doc.getElementById("take-snapshot");
|
||||
EventUtils.synthesizeMouseAtCenter(takeSnapshotButton, {}, panel.panelWin);
|
||||
|
||||
// Wait for the dominator tree to be computed and fetched.
|
||||
yield waitUntilState(store, state =>
|
||||
state.snapshots[0] &&
|
||||
state.snapshots[0].dominatorTree &&
|
||||
state.snapshots[0].dominatorTree.state === dominatorTreeState.LOADED);
|
||||
ok(true, "Computed and fetched the dominator tree.");
|
||||
|
||||
const root = getState().snapshots[0].dominatorTree.root;
|
||||
ok(getState().snapshots[0].dominatorTree.expanded.has(root.nodeId),
|
||||
"Root node is expanded by default");
|
||||
|
||||
// Click on root arrow to collapse the root element
|
||||
const rootNode = doc.querySelector(`.node-${root.nodeId}`);
|
||||
clickOnNodeArrow(rootNode, panel);
|
||||
|
||||
yield waitUntilState(store, state =>
|
||||
state.snapshots[0] &&
|
||||
state.snapshots[0].dominatorTree &&
|
||||
!state.snapshots[0].dominatorTree.expanded.has(root.nodeId));
|
||||
ok(true, "Root node collapsed");
|
||||
|
||||
// Click on root arrow to expand it again
|
||||
clickOnNodeArrow(rootNode, panel);
|
||||
|
||||
yield waitUntilState(store, state =>
|
||||
state.snapshots[0] &&
|
||||
state.snapshots[0].dominatorTree &&
|
||||
state.snapshots[0].dominatorTree.expanded.has(root.nodeId));
|
||||
ok(true, "Root node is expanded again");
|
||||
});
|
||||
|
|
@ -0,0 +1,81 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
// Sanity test that we can show allocation stack displays in the tree.
|
||||
|
||||
"use strict";
|
||||
|
||||
const {
|
||||
dominatorTreeState,
|
||||
snapshotState,
|
||||
viewState,
|
||||
censusState,
|
||||
} = require("devtools/client/memory/constants");
|
||||
const { changeViewAndRefresh, changeView } = require("devtools/client/memory/actions/view");
|
||||
|
||||
const TEST_URL = "http://example.com/browser/devtools/client/memory/test/browser/doc_steady_allocation.html";
|
||||
|
||||
this.test = makeMemoryTest(TEST_URL, function* ({ tab, panel }) {
|
||||
const heapWorker = panel.panelWin.gHeapAnalysesClient;
|
||||
const front = panel.panelWin.gFront;
|
||||
const store = panel.panelWin.gStore;
|
||||
const { getState, dispatch } = store;
|
||||
const doc = panel.panelWin.document;
|
||||
|
||||
dispatch(changeView(viewState.CENSUS));
|
||||
|
||||
const takeSnapshotButton = doc.getElementById("take-snapshot");
|
||||
EventUtils.synthesizeMouseAtCenter(takeSnapshotButton, {}, panel.panelWin);
|
||||
|
||||
yield waitUntilState(store, state =>
|
||||
state.snapshots.length === 1 &&
|
||||
state.snapshots[0].census &&
|
||||
state.snapshots[0].census.state === censusState.SAVING);
|
||||
|
||||
let filterInput = doc.getElementById("filter");
|
||||
EventUtils.synthesizeMouseAtCenter(filterInput, {}, panel.panelWin);
|
||||
EventUtils.sendString("js::Shape", panel.panelWin);
|
||||
|
||||
yield waitUntilState(store, state =>
|
||||
state.snapshots.length === 1 &&
|
||||
state.snapshots[0].census &&
|
||||
state.snapshots[0].census.state === censusState.SAVING);
|
||||
ok(true, "adding a filter string should trigger census recompute");
|
||||
|
||||
yield waitUntilState(store, state =>
|
||||
state.snapshots.length === 1 &&
|
||||
state.snapshots[0].census &&
|
||||
state.snapshots[0].census.state === censusState.SAVED);
|
||||
|
||||
let nameElem = doc.querySelector(".heap-tree-item-field.heap-tree-item-name");
|
||||
ok(nameElem, "Should get a tree item row with a name");
|
||||
is(nameElem.textContent.trim(), "js::Shape", "the tree item should be the one we filtered for");
|
||||
is(filterInput.value, "js::Shape",
|
||||
"and filter input contains the user value");
|
||||
|
||||
// Now switch the dominator view, then switch back to census view
|
||||
// and check that the filter word is still correctly applied
|
||||
dispatch(changeViewAndRefresh(viewState.DOMINATOR_TREE, heapWorker));
|
||||
ok(true, "change view to dominator tree");
|
||||
|
||||
// Wait for the dominator tree to be computed and fetched.
|
||||
yield waitUntilDominatorTreeState(store, [dominatorTreeState.LOADED]);
|
||||
ok(true, "computed and fetched the dominator tree.");
|
||||
|
||||
dispatch(changeViewAndRefresh(viewState.CENSUS, heapWorker));
|
||||
ok(true, "change view back to census");
|
||||
|
||||
yield waitUntilState(store, state =>
|
||||
state.snapshots.length === 1 &&
|
||||
state.snapshots[0].census &&
|
||||
state.snapshots[0].census.state === censusState.SAVED);
|
||||
|
||||
nameElem = doc.querySelector(".heap-tree-item-field.heap-tree-item-name");
|
||||
filterInput = doc.getElementById("filter");
|
||||
|
||||
ok(nameElem, "Should still get a tree item row with a name");
|
||||
is(nameElem.textContent.trim(), "js::Shape",
|
||||
"the tree item should still be the one we filtered for");
|
||||
is(filterInput.value, "js::Shape",
|
||||
"and filter input still contains the user value");
|
||||
});
|
||||
|
|
@ -0,0 +1,67 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
// Sanity test that we can show census group individuals, and then go back to
|
||||
// the previous view.
|
||||
|
||||
"use strict";
|
||||
|
||||
const {
|
||||
individualsState,
|
||||
viewState,
|
||||
censusState,
|
||||
} = require("devtools/client/memory/constants");
|
||||
const { changeViewAndRefresh, changeView } = require("devtools/client/memory/actions/view");
|
||||
|
||||
const TEST_URL = "http://example.com/browser/devtools/client/memory/test/browser/doc_steady_allocation.html";
|
||||
|
||||
this.test = makeMemoryTest(TEST_URL, function* ({ tab, panel }) {
|
||||
const heapWorker = panel.panelWin.gHeapAnalysesClient;
|
||||
const front = panel.panelWin.gFront;
|
||||
const store = panel.panelWin.gStore;
|
||||
const { getState, dispatch } = store;
|
||||
const doc = panel.panelWin.document;
|
||||
|
||||
dispatch(changeView(viewState.CENSUS));
|
||||
|
||||
// Take a snapshot and wait for the census to finish.
|
||||
|
||||
const takeSnapshotButton = doc.getElementById("take-snapshot");
|
||||
EventUtils.synthesizeMouseAtCenter(takeSnapshotButton, {}, panel.panelWin);
|
||||
|
||||
yield waitUntilState(store, state => {
|
||||
return state.snapshots.length === 1 &&
|
||||
state.snapshots[0].census &&
|
||||
state.snapshots[0].census.state === censusState.SAVED;
|
||||
});
|
||||
|
||||
// Click on the first individuals button found, and wait for the individuals
|
||||
// to be fetched.
|
||||
|
||||
const individualsButton = doc.querySelector(".individuals-button");
|
||||
EventUtils.synthesizeMouseAtCenter(individualsButton, {}, panel.panelWin);
|
||||
|
||||
yield waitUntilState(store, state => {
|
||||
return state.view.state === viewState.INDIVIDUALS &&
|
||||
state.individuals &&
|
||||
state.individuals.state === individualsState.FETCHED;
|
||||
});
|
||||
|
||||
ok(doc.getElementById("shortest-paths"),
|
||||
"Should be showing the shortest paths component");
|
||||
ok(doc.querySelector(".heap-tree-item"),
|
||||
"Should be showing the individuals");
|
||||
|
||||
// Go back to the previous view.
|
||||
|
||||
const popViewButton = doc.getElementById("pop-view-button");
|
||||
ok(popViewButton, "Should be showing the #pop-view-button");
|
||||
EventUtils.synthesizeMouseAtCenter(popViewButton, {}, panel.panelWin);
|
||||
|
||||
yield waitUntilState(store, state => {
|
||||
return state.view.state === viewState.CENSUS;
|
||||
});
|
||||
|
||||
ok(!doc.getElementById("shortest-paths"),
|
||||
"Should not be showing the shortest paths component anymore");
|
||||
});
|
||||
|
|
@ -0,0 +1,99 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
// Test that using ACCEL+UP/DOWN, the user can navigate between snapshots.
|
||||
|
||||
"use strict";
|
||||
|
||||
const {
|
||||
snapshotState,
|
||||
viewState,
|
||||
censusState
|
||||
} = require("devtools/client/memory/constants");
|
||||
const {
|
||||
takeSnapshotAndCensus
|
||||
} = require("devtools/client/memory/actions/snapshot");
|
||||
const { changeView } = require("devtools/client/memory/actions/view");
|
||||
|
||||
const TEST_URL = "http://example.com/browser/devtools/client/memory/test/browser/doc_steady_allocation.html";
|
||||
|
||||
this.test = makeMemoryTest(TEST_URL, function* ({ panel }) {
|
||||
// Creating snapshots already takes ~25 seconds on linux 32 debug machines
|
||||
// which makes the test very likely to go over the allowed timeout
|
||||
requestLongerTimeout(2);
|
||||
|
||||
const heapWorker = panel.panelWin.gHeapAnalysesClient;
|
||||
const front = panel.panelWin.gFront;
|
||||
const store = panel.panelWin.gStore;
|
||||
const { dispatch } = store;
|
||||
const doc = panel.panelWin.document;
|
||||
|
||||
dispatch(changeView(viewState.CENSUS));
|
||||
|
||||
info("Take 3 snapshots");
|
||||
dispatch(takeSnapshotAndCensus(front, heapWorker));
|
||||
dispatch(takeSnapshotAndCensus(front, heapWorker));
|
||||
dispatch(takeSnapshotAndCensus(front, heapWorker));
|
||||
|
||||
yield waitUntilState(store, state =>
|
||||
state.snapshots.length == 3 &&
|
||||
state.snapshots.every(s => s.census && s.census.state === censusState.SAVED));
|
||||
ok(true, "All snapshots censuses are in SAVED state");
|
||||
|
||||
yield waitUntilSnapshotSelected(store, 2);
|
||||
ok(true, "Third snapshot selected after creating all snapshots.");
|
||||
|
||||
info("Press ACCEL+UP key, expect second snapshot selected.");
|
||||
EventUtils.synthesizeKey("VK_UP", { accelKey: true }, panel.panelWin);
|
||||
yield waitUntilSnapshotSelected(store, 1);
|
||||
ok(true, "Second snapshot selected after alt+UP.");
|
||||
|
||||
info("Press ACCEL+UP key, expect first snapshot selected.");
|
||||
EventUtils.synthesizeKey("VK_UP", { accelKey: true }, panel.panelWin);
|
||||
yield waitUntilSnapshotSelected(store, 0);
|
||||
ok(true, "First snapshot is selected after ACCEL+UP");
|
||||
|
||||
info("Check ACCEL+UP is a noop when the first snapshot is selected.");
|
||||
EventUtils.synthesizeKey("VK_UP", { accelKey: true }, panel.panelWin);
|
||||
// We assume the snapshot selection should be synchronous here.
|
||||
is(getSelectedSnapshotIndex(store), 0, "First snapshot is still selected");
|
||||
|
||||
info("Press ACCEL+DOWN key, expect second snapshot selected.");
|
||||
EventUtils.synthesizeKey("VK_DOWN", { accelKey: true }, panel.panelWin);
|
||||
yield waitUntilSnapshotSelected(store, 1);
|
||||
ok(true, "Second snapshot is selected after ACCEL+DOWN");
|
||||
|
||||
info("Click on first node.");
|
||||
let firstNode = doc.querySelector(".tree .heap-tree-item-name");
|
||||
EventUtils.synthesizeMouseAtCenter(firstNode, {}, panel.panelWin);
|
||||
yield waitUntilState(store, state => state.snapshots[1].census.focused ===
|
||||
state.snapshots[1].census.report.children[0]
|
||||
);
|
||||
ok(true, "First root is selected after click.");
|
||||
|
||||
info("Press DOWN key, expect second root focused.");
|
||||
EventUtils.synthesizeKey("VK_DOWN", {}, panel.panelWin);
|
||||
yield waitUntilState(store, state => state.snapshots[1].census.focused ===
|
||||
state.snapshots[1].census.report.children[1]
|
||||
);
|
||||
ok(true, "Second root is selected after pressing DOWN.");
|
||||
is(getSelectedSnapshotIndex(store), 1, "Second snapshot is still selected");
|
||||
|
||||
info("Press UP key, expect second root focused.");
|
||||
EventUtils.synthesizeKey("VK_UP", {}, panel.panelWin);
|
||||
yield waitUntilState(store, state => state.snapshots[1].census.focused ===
|
||||
state.snapshots[1].census.report.children[0]
|
||||
);
|
||||
ok(true, "First root is selected after pressing UP.");
|
||||
is(getSelectedSnapshotIndex(store), 1, "Second snapshot is still selected");
|
||||
|
||||
info("Press ACCEL+DOWN key, expect third snapshot selected.");
|
||||
EventUtils.synthesizeKey("VK_DOWN", { accelKey: true }, panel.panelWin);
|
||||
yield waitUntilSnapshotSelected(store, 2);
|
||||
ok(true, "Thirdˆ snapshot is selected after ACCEL+DOWN");
|
||||
|
||||
info("Check ACCEL+DOWN is a noop when the last snapshot is selected.");
|
||||
EventUtils.synthesizeKey("VK_DOWN", { accelKey: true }, panel.panelWin);
|
||||
// We assume the snapshot selection should be synchronous here.
|
||||
is(getSelectedSnapshotIndex(store), 2, "Third snapshot is still selected");
|
||||
});
|
||||
107
devtools/client/memory/test/browser/browser_memory_keyboard.js
Normal file
107
devtools/client/memory/test/browser/browser_memory_keyboard.js
Normal file
|
|
@ -0,0 +1,107 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
// Bug 1246570 - Check that when pressing on LEFT arrow, the parent tree node
|
||||
// gets focused.
|
||||
|
||||
"use strict";
|
||||
|
||||
const {
|
||||
snapshotState,
|
||||
censusState,
|
||||
viewState
|
||||
} = require("devtools/client/memory/constants");
|
||||
const {
|
||||
takeSnapshotAndCensus
|
||||
} = require("devtools/client/memory/actions/snapshot");
|
||||
const { changeView } = require("devtools/client/memory/actions/view");
|
||||
|
||||
const TEST_URL = "http://example.com/browser/devtools/client/memory/test/browser/doc_steady_allocation.html";
|
||||
|
||||
function waitUntilFocused(store, node) {
|
||||
return waitUntilState(store, state =>
|
||||
state.snapshots.length === 1 &&
|
||||
state.snapshots[0].census &&
|
||||
state.snapshots[0].census.state === censusState.SAVED &&
|
||||
state.snapshots[0].census.focused &&
|
||||
state.snapshots[0].census.focused === node
|
||||
);
|
||||
}
|
||||
|
||||
function waitUntilExpanded(store, node) {
|
||||
return waitUntilState(store, state =>
|
||||
state.snapshots[0] &&
|
||||
state.snapshots[0].census &&
|
||||
state.snapshots[0].census.expanded.has(node.id));
|
||||
}
|
||||
|
||||
this.test = makeMemoryTest(TEST_URL, function* ({ tab, panel }) {
|
||||
const heapWorker = panel.panelWin.gHeapAnalysesClient;
|
||||
const front = panel.panelWin.gFront;
|
||||
const store = panel.panelWin.gStore;
|
||||
const { getState, dispatch } = store;
|
||||
const doc = panel.panelWin.document;
|
||||
|
||||
dispatch(changeView(viewState.CENSUS));
|
||||
|
||||
is(getState().censusDisplay.breakdown.by, "coarseType");
|
||||
|
||||
yield dispatch(takeSnapshotAndCensus(front, heapWorker));
|
||||
let census = getState().snapshots[0].census;
|
||||
let root1 = census.report.children[0];
|
||||
let root2 = census.report.children[0];
|
||||
let root3 = census.report.children[0];
|
||||
let root4 = census.report.children[0];
|
||||
let child1 = root1.children[0];
|
||||
|
||||
info("Click on first node.");
|
||||
let firstNode = doc.querySelector(".tree .heap-tree-item-name");
|
||||
EventUtils.synthesizeMouseAtCenter(firstNode, {}, panel.panelWin);
|
||||
yield waitUntilFocused(store, root1);
|
||||
ok(true, "First root is selected after click.");
|
||||
|
||||
info("Press DOWN key, expect second root focused.");
|
||||
EventUtils.synthesizeKey("VK_DOWN", {}, panel.panelWin);
|
||||
yield waitUntilFocused(store, root2);
|
||||
ok(true, "Second root is selected after pressing DOWN arrow.");
|
||||
|
||||
info("Press DOWN key, expect third root focused.");
|
||||
EventUtils.synthesizeKey("VK_DOWN", {}, panel.panelWin);
|
||||
yield waitUntilFocused(store, root3);
|
||||
ok(true, "Third root is selected after pressing DOWN arrow.");
|
||||
|
||||
info("Press DOWN key, expect fourth root focused.");
|
||||
EventUtils.synthesizeKey("VK_DOWN", {}, panel.panelWin);
|
||||
yield waitUntilFocused(store, root4);
|
||||
ok(true, "Fourth root is selected after pressing DOWN arrow.");
|
||||
|
||||
info("Press UP key, expect third root focused.");
|
||||
EventUtils.synthesizeKey("VK_UP", {}, panel.panelWin);
|
||||
yield waitUntilFocused(store, root3);
|
||||
ok(true, "Third root is selected after pressing UP arrow.");
|
||||
|
||||
info("Press UP key, expect second root focused.");
|
||||
EventUtils.synthesizeKey("VK_UP", {}, panel.panelWin);
|
||||
yield waitUntilFocused(store, root2);
|
||||
ok(true, "Second root is selected after pressing UP arrow.");
|
||||
|
||||
info("Press UP key, expect first root focused.");
|
||||
EventUtils.synthesizeKey("VK_UP", {}, panel.panelWin);
|
||||
yield waitUntilFocused(store, root1);
|
||||
ok(true, "First root is selected after pressing UP arrow.");
|
||||
|
||||
info("Press RIGHT key");
|
||||
EventUtils.synthesizeKey("VK_RIGHT", {}, panel.panelWin);
|
||||
yield waitUntilExpanded(store, root1);
|
||||
ok(true, "Root node is expanded.");
|
||||
|
||||
info("Press RIGHT key, expect first child focused.");
|
||||
EventUtils.synthesizeKey("VK_RIGHT", {}, panel.panelWin);
|
||||
yield waitUntilFocused(store, child1);
|
||||
ok(true, "First child is selected after pressing RIGHT arrow.");
|
||||
|
||||
info("Press LEFT key, expect first root focused.");
|
||||
EventUtils.synthesizeKey("VK_LEFT", {}, panel.panelWin);
|
||||
yield waitUntilFocused(store, root1);
|
||||
ok(true, "First root is selected after pressing LEFT arrow.");
|
||||
});
|
||||
|
|
@ -0,0 +1,39 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
// Sanity test that we can show allocation stack displays in the tree.
|
||||
|
||||
"use strict";
|
||||
|
||||
const { takeSnapshotAndCensus } = require("devtools/client/memory/actions/snapshot");
|
||||
const censusDisplayActions = require("devtools/client/memory/actions/census-display");
|
||||
const { viewState } = require("devtools/client/memory/constants");
|
||||
const { changeView } = require("devtools/client/memory/actions/view");
|
||||
|
||||
const TEST_URL = "http://example.com/browser/devtools/client/memory/test/browser/doc_steady_allocation.html";
|
||||
|
||||
this.test = makeMemoryTest(TEST_URL, function* ({ tab, panel }) {
|
||||
const heapWorker = panel.panelWin.gHeapAnalysesClient;
|
||||
const front = panel.panelWin.gFront;
|
||||
const { getState, dispatch } = panel.panelWin.gStore;
|
||||
const doc = panel.panelWin.document;
|
||||
|
||||
dispatch(changeView(viewState.CENSUS));
|
||||
|
||||
ok(!getState().allocations.recording,
|
||||
"Should not be recording allocagtions");
|
||||
|
||||
yield dispatch(takeSnapshotAndCensus(front, heapWorker));
|
||||
yield dispatch(censusDisplayActions.setCensusDisplayAndRefresh(
|
||||
heapWorker,
|
||||
censusDisplays.allocationStack));
|
||||
|
||||
is(getState().censusDisplay.breakdown.by, "allocationStack",
|
||||
"Should be using allocation stack breakdown");
|
||||
|
||||
ok(!getState().allocations.recording,
|
||||
"Should still not be recording allocagtions");
|
||||
|
||||
ok(doc.querySelector(".no-allocation-stacks"),
|
||||
"Because we did not record allocations, the no-allocation-stack warning should be visible");
|
||||
});
|
||||
|
|
@ -0,0 +1,37 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
// Bug 1221150 - Ensure that census trees do not accidentally auto expand
|
||||
// when clicking on the allocation stacks checkbox.
|
||||
|
||||
"use strict";
|
||||
|
||||
const { takeSnapshotAndCensus } = require("devtools/client/memory/actions/snapshot");
|
||||
const { viewState } = require("devtools/client/memory/constants");
|
||||
const { changeView } = require("devtools/client/memory/actions/view");
|
||||
|
||||
const TEST_URL = "http://example.com/browser/devtools/client/memory/test/browser/doc_steady_allocation.html";
|
||||
|
||||
this.test = makeMemoryTest(TEST_URL, function* ({ tab, panel }) {
|
||||
const heapWorker = panel.panelWin.gHeapAnalysesClient;
|
||||
const front = panel.panelWin.gFront;
|
||||
const { getState, dispatch } = panel.panelWin.gStore;
|
||||
const doc = panel.panelWin.document;
|
||||
|
||||
dispatch(changeView(viewState.CENSUS));
|
||||
|
||||
yield dispatch(takeSnapshotAndCensus(front, heapWorker));
|
||||
|
||||
is(getState().allocations.recording, false);
|
||||
const recordingCheckbox = doc.getElementById("record-allocation-stacks-checkbox");
|
||||
EventUtils.synthesizeMouseAtCenter(recordingCheckbox, {}, panel.panelWin);
|
||||
is(getState().allocations.recording, true);
|
||||
|
||||
const nameElems = [...doc.querySelectorAll(".heap-tree-item-field.heap-tree-item-name")];
|
||||
|
||||
for (let el of nameElems) {
|
||||
dumpn(`Found ${el.textContent.trim()}`);
|
||||
is(el.style.marginInlineStart, "0px",
|
||||
"None of the elements should be an indented/expanded child");
|
||||
}
|
||||
});
|
||||
|
|
@ -0,0 +1,47 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
// Sanity test that we calculate percentages in the tree.
|
||||
|
||||
"use strict";
|
||||
|
||||
const { takeSnapshotAndCensus } = require("devtools/client/memory/actions/snapshot");
|
||||
const { viewState } = require("devtools/client/memory/constants");
|
||||
const { changeView } = require("devtools/client/memory/actions/view");
|
||||
|
||||
const TEST_URL = "http://example.com/browser/devtools/client/memory/test/browser/doc_steady_allocation.html";
|
||||
|
||||
function checkCells(cells) {
|
||||
ok(cells.length > 1, "Should have found some");
|
||||
// Ignore the first header cell.
|
||||
for (let cell of cells.slice(1)) {
|
||||
const percent = cell.querySelector(".heap-tree-percent");
|
||||
ok(percent, "should have a percent cell");
|
||||
ok(percent.textContent.match(/^\d?\d%$/), "should be of the form nn% or n%");
|
||||
}
|
||||
}
|
||||
|
||||
this.test = makeMemoryTest(TEST_URL, function* ({ tab, panel }) {
|
||||
const heapWorker = panel.panelWin.gHeapAnalysesClient;
|
||||
const front = panel.panelWin.gFront;
|
||||
const { getState, dispatch } = panel.panelWin.gStore;
|
||||
const doc = panel.panelWin.document;
|
||||
|
||||
dispatch(changeView(viewState.CENSUS));
|
||||
|
||||
yield dispatch(takeSnapshotAndCensus(front, heapWorker));
|
||||
is(getState().censusDisplay.breakdown.by, "coarseType",
|
||||
"Should be using coarse type breakdown");
|
||||
|
||||
const bytesCells = [...doc.querySelectorAll(".heap-tree-item-bytes")];
|
||||
checkCells(bytesCells);
|
||||
|
||||
const totalBytesCells = [...doc.querySelectorAll(".heap-tree-item-total-bytes")];
|
||||
checkCells(totalBytesCells);
|
||||
|
||||
const countCells = [...doc.querySelectorAll(".heap-tree-item-count")];
|
||||
checkCells(countCells);
|
||||
|
||||
const totalCountCells = [...doc.querySelectorAll(".heap-tree-item-total-count")];
|
||||
checkCells(totalCountCells);
|
||||
});
|
||||
|
|
@ -0,0 +1,108 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
// Test that refreshing the page with devtools open does not leak the old
|
||||
// windows from previous navigations.
|
||||
//
|
||||
// IF THIS TEST STARTS FAILING, YOU ARE LEAKING EVERY WINDOW EVER NAVIGATED TO
|
||||
// WHILE DEVTOOLS ARE OPEN! THIS IS NOT SPECIFIC TO THE MEMORY TOOL ONLY!
|
||||
|
||||
"use strict";
|
||||
|
||||
const HeapSnapshotFileUtils = require("devtools/shared/heapsnapshot/HeapSnapshotFileUtils");
|
||||
const { getLabelAndShallowSize } = require("devtools/shared/heapsnapshot/DominatorTreeNode");
|
||||
|
||||
const TEST_URL = "http://example.com/browser/devtools/client/memory/test/browser/doc_empty.html";
|
||||
|
||||
function* getWindowsInSnapshot(front) {
|
||||
dumpn("Taking snapshot.");
|
||||
const path = yield front.saveHeapSnapshot();
|
||||
dumpn("Took snapshot with path = " + path);
|
||||
const snapshot = ChromeUtils.readHeapSnapshot(path);
|
||||
dumpn("Read snapshot into memory, taking census.");
|
||||
const report = snapshot.takeCensus({
|
||||
breakdown: {
|
||||
by: "objectClass",
|
||||
then: { by: "bucket" },
|
||||
other: { by: "count", count: true, bytes: false },
|
||||
}
|
||||
});
|
||||
dumpn("Took census, window count = " + report.Window.count);
|
||||
return report.Window;
|
||||
}
|
||||
|
||||
const DESCRIPTION = {
|
||||
by: "coarseType",
|
||||
objects: {
|
||||
by: "objectClass",
|
||||
then: { by: "count", count: true, bytes: false },
|
||||
other: { by: "count", count: true, bytes: false },
|
||||
},
|
||||
strings: { by: "count", count: true, bytes: false },
|
||||
scripts: {
|
||||
by: "internalType",
|
||||
then: { by: "count", count: true, bytes: false },
|
||||
},
|
||||
other: {
|
||||
by: "internalType",
|
||||
then: { by: "count", count: true, bytes: false },
|
||||
}
|
||||
};
|
||||
|
||||
this.test = makeMemoryTest(TEST_URL, function* ({ tab, panel }) {
|
||||
const heapWorker = panel.panelWin.gHeapAnalysesClient;
|
||||
const front = panel.panelWin.gFront;
|
||||
const store = panel.panelWin.gStore;
|
||||
const { getState, dispatch } = store;
|
||||
const doc = panel.panelWin.document;
|
||||
|
||||
const startWindows = yield getWindowsInSnapshot(front);
|
||||
dumpn("Initial windows found = " + startWindows.map(w => "0x" + w.toString(16)).join(", "));
|
||||
is(startWindows.length, 1);
|
||||
|
||||
yield refreshTab(tab);
|
||||
|
||||
const endWindows = yield getWindowsInSnapshot(front);
|
||||
is(endWindows.length, 1);
|
||||
|
||||
if (endWindows.length === 1) {
|
||||
return;
|
||||
}
|
||||
|
||||
dumpn("Test failed, diagnosing leaking windows.");
|
||||
dumpn("(This may fail if a moving GC has relocated the initial Window objects.)");
|
||||
|
||||
dumpn("Taking full runtime snapshot.");
|
||||
const path = yield front.saveHeapSnapshot({ boundaries: { runtime: true } });
|
||||
dumpn("Full runtime's snapshot path = " + path);
|
||||
|
||||
dumpn("Reading full runtime heap snapshot.");
|
||||
const snapshot = ChromeUtils.readHeapSnapshot(path);
|
||||
dumpn("Done reading full runtime heap snapshot.");
|
||||
|
||||
const dominatorTree = snapshot.computeDominatorTree();
|
||||
const paths = snapshot.computeShortestPaths(dominatorTree.root, startWindows, 50);
|
||||
|
||||
for (let i = 0; i < startWindows.length; i++) {
|
||||
dumpn("Shortest retaining paths for leaking Window 0x" + startWindows[i].toString(16) + " =========================");
|
||||
let j = 0;
|
||||
for (let retainingPath of paths.get(startWindows[i])) {
|
||||
if (retainingPath.find(part => part.predecessor === startWindows[i])) {
|
||||
// Skip paths that loop out from the target window and back to it again.
|
||||
continue;
|
||||
}
|
||||
|
||||
dumpn(" Path #" + (++j) + ": --------------------------------------------------------------------");
|
||||
for (let part of retainingPath) {
|
||||
const { label } = getLabelAndShallowSize(part.predecessor, snapshot, DESCRIPTION);
|
||||
dumpn(" 0x" + part.predecessor.toString(16) +
|
||||
" (" + label.join(" > ") + ")");
|
||||
dumpn(" |");
|
||||
dumpn(" " + part.edge);
|
||||
dumpn(" |");
|
||||
dumpn(" V");
|
||||
}
|
||||
dumpn(" 0x" + startWindows[i].toString(16) + " (objects > Window)");
|
||||
}
|
||||
}
|
||||
});
|
||||
|
|
@ -0,0 +1,40 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
/**
|
||||
* Tests taking snapshots and default states.
|
||||
*/
|
||||
|
||||
const TEST_URL = "http://example.com/browser/devtools/client/memory/test/browser/doc_steady_allocation.html";
|
||||
const { viewState, censusState } = require("devtools/client/memory/constants");
|
||||
const { changeView } = require("devtools/client/memory/actions/view");
|
||||
|
||||
this.test = makeMemoryTest(TEST_URL, function* ({ tab, panel }) {
|
||||
const { gStore, document } = panel.panelWin;
|
||||
const { getState, dispatch } = gStore;
|
||||
|
||||
dispatch(changeView(viewState.CENSUS));
|
||||
|
||||
let snapshotEls = document.querySelectorAll("#memory-tool-container .list li");
|
||||
is(getState().snapshots.length, 0, "Starts with no snapshots in store");
|
||||
is(snapshotEls.length, 0, "No snapshots rendered");
|
||||
|
||||
yield takeSnapshot(panel.panelWin);
|
||||
snapshotEls = document.querySelectorAll("#memory-tool-container .list li");
|
||||
is(getState().snapshots.length, 1, "One snapshot was created in store");
|
||||
is(snapshotEls.length, 1, "One snapshot was rendered");
|
||||
ok(snapshotEls[0].classList.contains("selected"), "Only snapshot has `selected` class");
|
||||
|
||||
yield takeSnapshot(panel.panelWin);
|
||||
snapshotEls = document.querySelectorAll("#memory-tool-container .list li");
|
||||
is(getState().snapshots.length, 2, "Two snapshots created in store");
|
||||
is(snapshotEls.length, 2, "Two snapshots rendered");
|
||||
ok(!snapshotEls[0].classList.contains("selected"), "First snapshot no longer has `selected` class");
|
||||
ok(snapshotEls[1].classList.contains("selected"), "Second snapshot has `selected` class");
|
||||
|
||||
yield waitUntilCensusState(gStore, s => s.census, [censusState.SAVED,
|
||||
censusState.SAVED]);
|
||||
|
||||
ok(document.querySelector(".heap-tree-item-name"),
|
||||
"Should have rendered some tree items");
|
||||
});
|
||||
|
|
@ -0,0 +1,28 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
// Test that we can save a heap snapshot and transfer it over the RDP in e10s
|
||||
// where the child process is sandboxed and so we have to use
|
||||
// HeapSnapshotFileActor to get the heap snapshot file.
|
||||
|
||||
"use strict";
|
||||
|
||||
const TEST_URL = "data:text/html,<html><body></body></html>";
|
||||
|
||||
this.test = makeMemoryTest(TEST_URL, function* ({ tab, panel }) {
|
||||
const memoryFront = panel.panelWin.gFront;
|
||||
ok(memoryFront, "Should get the MemoryFront");
|
||||
|
||||
const snapshotFilePath = yield memoryFront.saveHeapSnapshot({
|
||||
// Force a copy so that we go through the HeapSnapshotFileActor's
|
||||
// transferHeapSnapshot request and exercise this code path on e10s.
|
||||
forceCopy: true
|
||||
});
|
||||
|
||||
ok(!!(yield OS.File.stat(snapshotFilePath)),
|
||||
"Should have the heap snapshot file");
|
||||
|
||||
const snapshot = ChromeUtils.readHeapSnapshot(snapshotFilePath);
|
||||
ok(snapshot instanceof HeapSnapshot,
|
||||
"And we should be able to read a HeapSnapshot instance from the file");
|
||||
});
|
||||
|
|
@ -0,0 +1,102 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
* You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
// Make sure the canvases are created correctly
|
||||
|
||||
"use strict";
|
||||
|
||||
const CanvasUtils = require("devtools/client/memory/components/tree-map/canvas-utils");
|
||||
const D3_SCRIPT = '<script type="application/javascript" ' +
|
||||
'src="chrome://devtools/content/shared/vendor/d3.js>';
|
||||
const TEST_URL = `data:text/html,<html><body>${D3_SCRIPT}</body></html>`;
|
||||
|
||||
this.test = makeMemoryTest(TEST_URL, function* ({ tab, panel }) {
|
||||
const document = panel.panelWin.document;
|
||||
const window = panel.panelWin;
|
||||
const div = document.createElement("div");
|
||||
|
||||
Object.assign(div.style, {
|
||||
width: "100px",
|
||||
height: "200px",
|
||||
position: "absolute"
|
||||
});
|
||||
|
||||
document.body.appendChild(div);
|
||||
|
||||
info("Create the canvases");
|
||||
|
||||
let canvases = new CanvasUtils(div, 0);
|
||||
|
||||
info("Test the shape of the returned object");
|
||||
|
||||
is(typeof canvases, "object", "Canvases create an object");
|
||||
is(typeof canvases.emit, "function", "Decorated with an EventEmitter");
|
||||
is(typeof canvases.on, "function", "Decorated with an EventEmitter");
|
||||
is(div.children[0], canvases.container, "Div has the container");
|
||||
ok(canvases.main.canvas instanceof window.HTMLCanvasElement,
|
||||
"Creates the main canvas");
|
||||
ok(canvases.zoom.canvas instanceof window.HTMLCanvasElement,
|
||||
"Creates the zoom canvas");
|
||||
ok(canvases.main.ctx instanceof window.CanvasRenderingContext2D,
|
||||
"Creates the main canvas context");
|
||||
ok(canvases.zoom.ctx instanceof window.CanvasRenderingContext2D,
|
||||
"Creates the zoom canvas context");
|
||||
|
||||
info("Test resizing");
|
||||
|
||||
let timesResizeCalled = 0;
|
||||
canvases.on("resize", function () {
|
||||
timesResizeCalled++;
|
||||
});
|
||||
|
||||
let main = canvases.main.canvas;
|
||||
let zoom = canvases.zoom.canvas;
|
||||
let ratio = window.devicePixelRatio;
|
||||
|
||||
is(main.width, 100 * ratio,
|
||||
"Main canvas width is the same as the parent div");
|
||||
is(main.height, 200 * ratio,
|
||||
"Main canvas height is the same as the parent div");
|
||||
is(zoom.width, 100 * ratio,
|
||||
"Zoom canvas width is the same as the parent div");
|
||||
is(zoom.height, 200 * ratio,
|
||||
"Zoom canvas height is the same as the parent div");
|
||||
is(timesResizeCalled, 0,
|
||||
"Resize was not emitted");
|
||||
|
||||
div.style.width = "500px";
|
||||
div.style.height = "700px";
|
||||
|
||||
window.dispatchEvent(new Event("resize"));
|
||||
|
||||
is(main.width, 500 * ratio,
|
||||
"Main canvas width is resized to be the same as the parent div");
|
||||
is(main.height, 700 * ratio,
|
||||
"Main canvas height is resized to be the same as the parent div");
|
||||
is(zoom.width, 500 * ratio,
|
||||
"Zoom canvas width is resized to be the same as the parent div");
|
||||
is(zoom.height, 700 * ratio,
|
||||
"Zoom canvas height is resized to be the same as the parent div");
|
||||
is(timesResizeCalled, 1,
|
||||
"'resize' was emitted was emitted");
|
||||
|
||||
div.style.width = "1100px";
|
||||
div.style.height = "1300px";
|
||||
|
||||
canvases.destroy();
|
||||
window.dispatchEvent(new Event("resize"));
|
||||
|
||||
is(main.width, 500 * ratio,
|
||||
"Main canvas width is not resized after destroy");
|
||||
is(main.height, 700 * ratio,
|
||||
"Main canvas height is not resized after destroy");
|
||||
is(zoom.width, 500 * ratio,
|
||||
"Zoom canvas width is not resized after destroy");
|
||||
is(zoom.height, 700 * ratio,
|
||||
"Zoom canvas height is not resized after destroy");
|
||||
is(timesResizeCalled, 1,
|
||||
"onResize was not called again");
|
||||
|
||||
document.body.removeChild(div);
|
||||
});
|
||||
|
|
@ -0,0 +1,161 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
* You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
// Test the drag and zooming behavior
|
||||
|
||||
"use strict";
|
||||
|
||||
const CanvasUtils = require("devtools/client/memory/components/tree-map/canvas-utils");
|
||||
const DragZoom = require("devtools/client/memory/components/tree-map/drag-zoom");
|
||||
|
||||
const TEST_URL = "data:text/html,<html><body></body></html>";
|
||||
const PIXEL_SCROLL_MODE = 0;
|
||||
const PIXEL_DELTA = 10;
|
||||
const MAX_RAF_LOOP = 1000;
|
||||
|
||||
this.test = makeMemoryTest(TEST_URL, function* ({ tab, panel }) {
|
||||
const panelWin = panel.panelWin;
|
||||
const panelDoc = panelWin.document;
|
||||
const div = panelDoc.createElement("div");
|
||||
|
||||
Object.assign(div.style, {
|
||||
width: "100px",
|
||||
height: "200px",
|
||||
position: "absolute",
|
||||
left:0,
|
||||
top:0
|
||||
});
|
||||
|
||||
let rafMock = createRAFMock();
|
||||
|
||||
panelDoc.body.appendChild(div);
|
||||
|
||||
let canvases = new CanvasUtils(div, 0);
|
||||
let dragZoom = new DragZoom(canvases.container, 0, rafMock.raf);
|
||||
let style = canvases.container.style;
|
||||
|
||||
info("Check initial state of dragZoom");
|
||||
{
|
||||
is(dragZoom.zoom, 0, "Zooming starts at 0");
|
||||
is(dragZoom.smoothZoom, 0, "Smoothed zooming starts at 0");
|
||||
is(rafMock.timesCalled, 0, "No RAFs have been queued");
|
||||
is(style.transform, "translate(0px, 0px) scale(1)",
|
||||
"No transforms have been done.");
|
||||
|
||||
canvases.container.dispatchEvent(new WheelEvent("wheel", {
|
||||
deltaY: -PIXEL_DELTA,
|
||||
deltaMode: PIXEL_SCROLL_MODE
|
||||
}));
|
||||
|
||||
is(style.transform, "translate(0px, 0px) scale(1.05)",
|
||||
"The div has been slightly scaled.");
|
||||
is(dragZoom.zoom, PIXEL_DELTA * dragZoom.ZOOM_SPEED,
|
||||
"The zoom was increased");
|
||||
ok(floatEquality(dragZoom.smoothZoom, 0.05),
|
||||
"The smooth zoom is between the initial value and the target");
|
||||
is(rafMock.timesCalled, 1, "A RAF has been queued");
|
||||
}
|
||||
|
||||
info("RAF will eventually stop once the smooth values approach the target");
|
||||
{
|
||||
let i;
|
||||
let lastCallCount;
|
||||
for (i = 0; i < MAX_RAF_LOOP; i++) {
|
||||
if (lastCallCount === rafMock.timesCalled) {
|
||||
break;
|
||||
}
|
||||
lastCallCount = rafMock.timesCalled;
|
||||
rafMock.nextFrame();
|
||||
}
|
||||
is(style.transform, "translate(0px, 0px) scale(1.1)",
|
||||
"The scale has been fully applied");
|
||||
is(dragZoom.zoom, dragZoom.smoothZoom,
|
||||
"The smooth and target zoom values match");
|
||||
isnot(MAX_RAF_LOOP, i,
|
||||
"The RAF loop correctly stopped");
|
||||
}
|
||||
|
||||
info("Dragging correctly translates the div");
|
||||
{
|
||||
let initialX = dragZoom.translateX;
|
||||
let initialY = dragZoom.translateY;
|
||||
div.dispatchEvent(new MouseEvent("mousemove", {
|
||||
clientX: 10,
|
||||
clientY: 10,
|
||||
}));
|
||||
div.dispatchEvent(new MouseEvent("mousedown"));
|
||||
div.dispatchEvent(new MouseEvent("mousemove", {
|
||||
clientX: 20,
|
||||
clientY: 20,
|
||||
}));
|
||||
div.dispatchEvent(new MouseEvent("mouseup"));
|
||||
|
||||
is(style.transform, "translate(2.5px, 5px) scale(1.1)",
|
||||
"The style is correctly translated");
|
||||
ok(floatEquality(dragZoom.translateX, 5),
|
||||
"Translate X moved by some pixel amount");
|
||||
ok(floatEquality(dragZoom.translateY, 10),
|
||||
"Translate Y moved by some pixel amount");
|
||||
}
|
||||
|
||||
info("Zooming centers around the mouse");
|
||||
{
|
||||
canvases.container.dispatchEvent(new WheelEvent("wheel", {
|
||||
deltaY: -PIXEL_DELTA,
|
||||
deltaMode: PIXEL_SCROLL_MODE
|
||||
}));
|
||||
// Run through the RAF loop to zoom in towards that value.
|
||||
let lastCallCount;
|
||||
for (let i = 0; i < MAX_RAF_LOOP; i++) {
|
||||
if (lastCallCount === rafMock.timesCalled) {
|
||||
break;
|
||||
}
|
||||
lastCallCount = rafMock.timesCalled;
|
||||
rafMock.nextFrame();
|
||||
}
|
||||
is(style.transform, "translate(8.18182px, 18.1818px) scale(1.2)",
|
||||
"Zooming affects the translation to keep the mouse centered");
|
||||
ok(floatEquality(dragZoom.translateX, 8.181818181818185),
|
||||
"Translate X was affected by the mouse position");
|
||||
ok(floatEquality(dragZoom.translateY, 18.18181818181817),
|
||||
"Translate Y was affected by the mouse position");
|
||||
is(dragZoom.zoom, 0.2, "Zooming starts at 0");
|
||||
}
|
||||
|
||||
dragZoom.destroy();
|
||||
|
||||
info("Scroll isn't tracked after destruction");
|
||||
{
|
||||
let previousZoom = dragZoom.zoom;
|
||||
let previousSmoothZoom = dragZoom.smoothZoom;
|
||||
|
||||
canvases.container.dispatchEvent(new WheelEvent("wheel", {
|
||||
deltaY: -PIXEL_DELTA,
|
||||
deltaMode: PIXEL_SCROLL_MODE
|
||||
}));
|
||||
|
||||
is(dragZoom.zoom, previousZoom,
|
||||
"The zoom stayed the same");
|
||||
is(dragZoom.smoothZoom, previousSmoothZoom,
|
||||
"The smooth zoom stayed the same");
|
||||
}
|
||||
|
||||
info("Translation isn't tracked after destruction");
|
||||
{
|
||||
let initialX = dragZoom.translateX;
|
||||
let initialY = dragZoom.translateY;
|
||||
|
||||
div.dispatchEvent(new MouseEvent("mousedown"));
|
||||
div.dispatchEvent(new MouseEvent("mousemove"), {
|
||||
clientX: 40,
|
||||
clientY: 40,
|
||||
});
|
||||
div.dispatchEvent(new MouseEvent("mouseup"));
|
||||
is(dragZoom.translateX, initialX,
|
||||
"The translationX didn't change");
|
||||
is(dragZoom.translateY, initialY,
|
||||
"The translationY didn't change");
|
||||
}
|
||||
panelDoc.body.removeChild(div);
|
||||
});
|
||||
15
devtools/client/memory/test/browser/doc_big_tree.html
Normal file
15
devtools/client/memory/test/browser/doc_big_tree.html
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<body>
|
||||
<script>
|
||||
window.big = (function makeBig(depth = 0) {
|
||||
var big = Array(5);
|
||||
big.fill(undefined);
|
||||
if (depth < 5) {
|
||||
big = big.map(_ => makeBig(depth + 1));
|
||||
}
|
||||
return big;
|
||||
}());
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
9
devtools/client/memory/test/browser/doc_empty.html
Normal file
9
devtools/client/memory/test/browser/doc_empty.html
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8"/>
|
||||
</head>
|
||||
<body>
|
||||
This is an empty window.
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<body>
|
||||
<script>
|
||||
var objects = window.objects = [];
|
||||
|
||||
var allocate = this.allocate = function allocate() {
|
||||
for (var i = 0; i < 100; i++)
|
||||
objects.push({});
|
||||
setTimeout(allocate, 10);
|
||||
}
|
||||
|
||||
allocate();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
248
devtools/client/memory/test/browser/head.js
Normal file
248
devtools/client/memory/test/browser/head.js
Normal file
|
|
@ -0,0 +1,248 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
"use strict";
|
||||
|
||||
// Load the shared test helpers into this compartment.
|
||||
Services.scriptloader.loadSubScript(
|
||||
"chrome://mochitests/content/browser/devtools/client/framework/test/shared-head.js",
|
||||
this);
|
||||
|
||||
// Load the shared Redux helpers into this compartment.
|
||||
Services.scriptloader.loadSubScript(
|
||||
"chrome://mochitests/content/browser/devtools/client/framework/test/shared-redux-head.js",
|
||||
this);
|
||||
|
||||
var { censusDisplays, snapshotState: states } = require("devtools/client/memory/constants");
|
||||
var { L10N } = require("devtools/client/memory/utils");
|
||||
|
||||
Services.prefs.setBoolPref("devtools.memory.enabled", true);
|
||||
|
||||
/**
|
||||
* Open the memory panel for the given tab.
|
||||
*/
|
||||
this.openMemoryPanel = Task.async(function* (tab) {
|
||||
info("Opening memory panel.");
|
||||
const target = TargetFactory.forTab(tab);
|
||||
const toolbox = yield gDevTools.showToolbox(target, "memory");
|
||||
info("Memory panel shown successfully.");
|
||||
let panel = toolbox.getCurrentPanel();
|
||||
return { tab, panel };
|
||||
});
|
||||
|
||||
/**
|
||||
* Close the memory panel for the given tab.
|
||||
*/
|
||||
this.closeMemoryPanel = Task.async(function* (tab) {
|
||||
info("Closing memory panel.");
|
||||
const target = TargetFactory.forTab(tab);
|
||||
const toolbox = gDevTools.getToolbox(target);
|
||||
yield toolbox.destroy();
|
||||
info("Closed memory panel successfully.");
|
||||
});
|
||||
|
||||
/**
|
||||
* Return a test function that adds a tab with the given url, opens the memory
|
||||
* panel, runs the given generator, closes the memory panel, removes the tab,
|
||||
* and finishes.
|
||||
*
|
||||
* Example usage:
|
||||
*
|
||||
* this.test = makeMemoryTest(TEST_URL, function* ({ tab, panel }) {
|
||||
* // Your tests go here...
|
||||
* });
|
||||
*/
|
||||
function makeMemoryTest(url, generator) {
|
||||
return Task.async(function* () {
|
||||
waitForExplicitFinish();
|
||||
|
||||
// It can take a long time to save a snapshot to disk, read the snapshots
|
||||
// back from disk, and finally perform analyses on them.
|
||||
requestLongerTimeout(2);
|
||||
|
||||
const tab = yield addTab(url);
|
||||
const results = yield openMemoryPanel(tab);
|
||||
|
||||
try {
|
||||
yield* generator(results);
|
||||
} catch (err) {
|
||||
ok(false, "Got an error: " + DevToolsUtils.safeErrorString(err));
|
||||
}
|
||||
|
||||
yield closeMemoryPanel(tab);
|
||||
yield removeTab(tab);
|
||||
|
||||
finish();
|
||||
});
|
||||
}
|
||||
|
||||
function dumpn(msg) {
|
||||
dump(`MEMORY-TEST: ${msg}\n`);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a promise that will resolve when the provided store matches
|
||||
* the expected array. expectedStates is an array of dominatorTree states.
|
||||
* Expectations :
|
||||
* - store.getState().snapshots.length == expected.length
|
||||
* - snapshots[i].dominatorTree.state == expected[i]
|
||||
*
|
||||
* @param {Store} store
|
||||
* @param {Array<string>} expectedStates [description]
|
||||
* @return {Promise}
|
||||
*/
|
||||
function waitUntilDominatorTreeState(store, expected) {
|
||||
let predicate = () => {
|
||||
let snapshots = store.getState().snapshots;
|
||||
return snapshots.length === expected.length &&
|
||||
expected.every((state, i) => {
|
||||
return snapshots[i].dominatorTree &&
|
||||
snapshots[i].dominatorTree.state === state;
|
||||
});
|
||||
};
|
||||
info(`Waiting for dominator trees to be of state: ${expected}`);
|
||||
return waitUntilState(store, predicate);
|
||||
}
|
||||
|
||||
function takeSnapshot(window) {
|
||||
let { gStore, document } = window;
|
||||
let snapshotCount = gStore.getState().snapshots.length;
|
||||
info("Taking snapshot...");
|
||||
document.querySelector(".devtools-toolbar .take-snapshot").click();
|
||||
return waitUntilState(gStore, () => gStore.getState().snapshots.length === snapshotCount + 1);
|
||||
}
|
||||
|
||||
function clearSnapshots(window) {
|
||||
let { gStore, document } = window;
|
||||
document.querySelector(".devtools-toolbar .clear-snapshots").click();
|
||||
return waitUntilState(gStore, () => gStore.getState().snapshots.every(
|
||||
(snapshot) => snapshot.state !== states.READ)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the current requested display and waits for the selected snapshot to use
|
||||
* it and complete the new census that entails.
|
||||
*/
|
||||
function setCensusDisplay(window, display) {
|
||||
info(`Setting census display to ${display}...`);
|
||||
let { gStore, gHeapAnalysesClient } = window;
|
||||
// XXX: Should handle this via clicking the DOM, but React doesn't
|
||||
// fire the onChange event, so just change it in the store.
|
||||
// window.document.querySelector(`.select-display`).value = type;
|
||||
gStore.dispatch(require("devtools/client/memory/actions/census-display")
|
||||
.setCensusDisplayAndRefresh(gHeapAnalysesClient, display));
|
||||
|
||||
return waitUntilState(window.gStore, () => {
|
||||
let selected = window.gStore.getState().snapshots.find(s => s.selected);
|
||||
return selected.state === states.READ &&
|
||||
selected.census &&
|
||||
selected.census.state === censusState.SAVED &&
|
||||
selected.census.display === display;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the snapshot tatus text currently displayed, or null if none is
|
||||
* displayed.
|
||||
*
|
||||
* @param {Document} document
|
||||
*/
|
||||
function getDisplayedSnapshotStatus(document) {
|
||||
const status = document.querySelector(".snapshot-status");
|
||||
return status ? status.textContent.trim() : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the index of the currently selected snapshot.
|
||||
*
|
||||
* @return {Number}
|
||||
*/
|
||||
function getSelectedSnapshotIndex(store) {
|
||||
let snapshots = store.getState().snapshots;
|
||||
let selectedSnapshot = snapshots.find(s => s.selected);
|
||||
return snapshots.indexOf(selectedSnapshot);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a promise that will resolve when the snapshot with provided index
|
||||
* becomes selected.
|
||||
*
|
||||
* @return {Promise}
|
||||
*/
|
||||
function waitUntilSnapshotSelected(store, snapshotIndex) {
|
||||
return waitUntilState(store, state =>
|
||||
state.snapshots[snapshotIndex] &&
|
||||
state.snapshots[snapshotIndex].selected === true);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Wait until the state has censuses in a certain state.
|
||||
*
|
||||
* @return {Promise}
|
||||
*/
|
||||
function waitUntilCensusState(store, getCensus, expected) {
|
||||
let predicate = () => {
|
||||
let snapshots = store.getState().snapshots;
|
||||
|
||||
info("Current census state:" +
|
||||
snapshots.map(x => getCensus(x) ? getCensus(x).state : null));
|
||||
|
||||
return snapshots.length === expected.length &&
|
||||
expected.every((state, i) => {
|
||||
let census = getCensus(snapshots[i]);
|
||||
return (state === "*") ||
|
||||
(!census && !state) ||
|
||||
(census && census.state === state);
|
||||
});
|
||||
};
|
||||
info(`Waiting for snapshot censuses to be of state: ${expected}`);
|
||||
return waitUntilState(store, predicate);
|
||||
}
|
||||
|
||||
/**
|
||||
* Mock out the requestAnimationFrame.
|
||||
*
|
||||
* @return {Object}
|
||||
* @function nextFrame
|
||||
* Call the last queued function
|
||||
* @function raf
|
||||
* The mocked raf function
|
||||
* @function timesCalled
|
||||
* How many times the RAF has been called
|
||||
*/
|
||||
function createRAFMock() {
|
||||
let queuedFns = [];
|
||||
let mock = { timesCalled: 0 };
|
||||
|
||||
mock.nextFrame = function () {
|
||||
let thisQueue = queuedFns;
|
||||
queuedFns = [];
|
||||
for (var i = 0; i < thisQueue.length; i++) {
|
||||
thisQueue[i]();
|
||||
}
|
||||
};
|
||||
|
||||
mock.raf = function (fn) {
|
||||
mock.timesCalled++;
|
||||
queuedFns.push(fn);
|
||||
};
|
||||
return mock;
|
||||
}
|
||||
|
||||
/**
|
||||
* Test to see if two floats are equivalent.
|
||||
*
|
||||
* @param {Float} a
|
||||
* @param {Float} b
|
||||
* @return {Boolean}
|
||||
*/
|
||||
function floatEquality(a, b) {
|
||||
const EPSILON = 0.00000000001;
|
||||
const equals = Math.abs(a - b) < EPSILON;
|
||||
if (!equals) {
|
||||
info(`${a} not equal to ${b}`);
|
||||
}
|
||||
return equals;
|
||||
}
|
||||
20
devtools/client/memory/test/chrome/chrome.ini
Normal file
20
devtools/client/memory/test/chrome/chrome.ini
Normal file
|
|
@ -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]
|
||||
335
devtools/client/memory/test/chrome/head.js
Normal file
335
devtools/client/memory/test/chrome/head.js
Normal file
|
|
@ -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`);
|
||||
}
|
||||
|
|
@ -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>
|
||||
|
|
@ -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>
|
||||
|
|
@ -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>
|
||||
|
|
@ -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>
|
||||
|
|
@ -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>
|
||||
50
devtools/client/memory/test/chrome/test_Heap_01.html
Normal file
50
devtools/client/memory/test/chrome/test_Heap_01.html
Normal file
|
|
@ -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>
|
||||
78
devtools/client/memory/test/chrome/test_Heap_02.html
Normal file
78
devtools/client/memory/test/chrome/test_Heap_02.html
Normal file
|
|
@ -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>
|
||||
74
devtools/client/memory/test/chrome/test_Heap_03.html
Normal file
74
devtools/client/memory/test/chrome/test_Heap_03.html
Normal file
|
|
@ -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>
|
||||
121
devtools/client/memory/test/chrome/test_Heap_04.html
Normal file
121
devtools/client/memory/test/chrome/test_Heap_04.html
Normal file
|
|
@ -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>
|
||||
132
devtools/client/memory/test/chrome/test_Heap_05.html
Normal file
132
devtools/client/memory/test/chrome/test_Heap_05.html
Normal file
|
|
@ -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>
|
||||
74
devtools/client/memory/test/chrome/test_List_01.html
Normal file
74
devtools/client/memory/test/chrome/test_List_01.html
Normal file
|
|
@ -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>
|
||||
112
devtools/client/memory/test/chrome/test_ShortestPaths_01.html
Normal file
112
devtools/client/memory/test/chrome/test_ShortestPaths_01.html
Normal file
|
|
@ -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>
|
||||
|
|
@ -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>
|
||||
|
|
@ -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>
|
||||
47
devtools/client/memory/test/chrome/test_Toolbar_01.html
Normal file
47
devtools/client/memory/test/chrome/test_Toolbar_01.html
Normal file
|
|
@ -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>
|
||||
44
devtools/client/memory/test/chrome/test_TreeMap_01.html
Normal file
44
devtools/client/memory/test/chrome/test_TreeMap_01.html
Normal file
|
|
@ -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>
|
||||
6
devtools/client/memory/test/unit/.eslintrc.js
Normal file
6
devtools/client/memory/test/unit/.eslintrc.js
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
"use strict";
|
||||
|
||||
module.exports = {
|
||||
// Extend from the shared list of defined globals for mochitests.
|
||||
"extends": "../../../../.eslintrc.xpcshell.js"
|
||||
};
|
||||
128
devtools/client/memory/test/unit/head.js
Normal file
128
devtools/client/memory/test/unit/head.js
Normal file
|
|
@ -0,0 +1,128 @@
|
|||
/* 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 { console } = Cu.import("resource://gre/modules/Console.jsm", {});
|
||||
var { require } = Cu.import("resource://devtools/shared/Loader.jsm", {});
|
||||
|
||||
var Services = require("Services");
|
||||
var DevToolsUtils = require("devtools/shared/DevToolsUtils");
|
||||
var flags = require("devtools/shared/flags");
|
||||
flags.testing = true;
|
||||
flags.wantLogging = true;
|
||||
flags.wantVerbose = false;
|
||||
|
||||
var { OS } = require("resource://gre/modules/osfile.jsm");
|
||||
var { FileUtils } = require("resource://gre/modules/FileUtils.jsm");
|
||||
var { TargetFactory } = require("devtools/client/framework/target");
|
||||
var promise = require("promise");
|
||||
var defer = require("devtools/shared/defer");
|
||||
var { Task } = require("devtools/shared/task");
|
||||
var { expectState } = require("devtools/server/actors/common");
|
||||
var HeapSnapshotFileUtils = require("devtools/shared/heapsnapshot/HeapSnapshotFileUtils");
|
||||
var HeapAnalysesClient = require("devtools/shared/heapsnapshot/HeapAnalysesClient");
|
||||
var { addDebuggerToGlobal } = require("resource://gre/modules/jsdebugger.jsm");
|
||||
var Store = require("devtools/client/memory/store");
|
||||
var { L10N } = require("devtools/client/memory/utils");
|
||||
var SYSTEM_PRINCIPAL = Cc["@mozilla.org/systemprincipal;1"].createInstance(Ci.nsIPrincipal);
|
||||
|
||||
var EXPECTED_DTU_ASSERT_FAILURE_COUNT = 0;
|
||||
|
||||
do_register_cleanup(function () {
|
||||
equal(DevToolsUtils.assertionFailureCount, EXPECTED_DTU_ASSERT_FAILURE_COUNT,
|
||||
"Should have had the expected number of DevToolsUtils.assert() failures.");
|
||||
});
|
||||
|
||||
function dumpn(msg) {
|
||||
dump(`MEMORY-TEST: ${msg}\n`);
|
||||
}
|
||||
|
||||
function initDebugger() {
|
||||
let global = new Cu.Sandbox(SYSTEM_PRINCIPAL, { freshZone: true });
|
||||
addDebuggerToGlobal(global);
|
||||
return new global.Debugger();
|
||||
}
|
||||
|
||||
function StubbedMemoryFront() {
|
||||
this.state = "detached";
|
||||
this.recordingAllocations = false;
|
||||
this.dbg = initDebugger();
|
||||
}
|
||||
|
||||
StubbedMemoryFront.prototype.attach = Task.async(function* () {
|
||||
this.state = "attached";
|
||||
});
|
||||
|
||||
StubbedMemoryFront.prototype.detach = Task.async(function* () {
|
||||
this.state = "detached";
|
||||
});
|
||||
|
||||
StubbedMemoryFront.prototype.saveHeapSnapshot = expectState("attached", Task.async(function* () {
|
||||
return ThreadSafeChromeUtils.saveHeapSnapshot({ runtime: true });
|
||||
}), "saveHeapSnapshot");
|
||||
|
||||
StubbedMemoryFront.prototype.startRecordingAllocations = expectState("attached", Task.async(function* () {
|
||||
this.recordingAllocations = true;
|
||||
}));
|
||||
|
||||
StubbedMemoryFront.prototype.stopRecordingAllocations = expectState("attached", Task.async(function* () {
|
||||
this.recordingAllocations = false;
|
||||
}));
|
||||
|
||||
function waitUntilSnapshotState(store, expected) {
|
||||
let predicate = () => {
|
||||
let snapshots = store.getState().snapshots;
|
||||
do_print(snapshots.map(x => x.state));
|
||||
return snapshots.length === expected.length &&
|
||||
expected.every((state, i) => state === "*" || snapshots[i].state === state);
|
||||
};
|
||||
do_print(`Waiting for snapshots to be of state: ${expected}`);
|
||||
return waitUntilState(store, predicate);
|
||||
}
|
||||
|
||||
function findReportLeafIndex(node, name = null) {
|
||||
if (node.reportLeafIndex && (!name || node.name === name)) {
|
||||
return node.reportLeafIndex;
|
||||
}
|
||||
|
||||
if (node.children) {
|
||||
for (let child of node.children) {
|
||||
const found = findReportLeafIndex(child);
|
||||
if (found) {
|
||||
return found;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
function waitUntilCensusState(store, getCensus, expected) {
|
||||
let predicate = () => {
|
||||
let snapshots = store.getState().snapshots;
|
||||
|
||||
do_print("Current census state:" +
|
||||
snapshots.map(x => getCensus(x) ? getCensus(x).state : null));
|
||||
|
||||
return snapshots.length === expected.length &&
|
||||
expected.every((state, i) => {
|
||||
let census = getCensus(snapshots[i]);
|
||||
return (state === "*") ||
|
||||
(!census && !state) ||
|
||||
(census && census.state === state);
|
||||
});
|
||||
};
|
||||
do_print(`Waiting for snapshots' censuses to be of state: ${expected}`);
|
||||
return waitUntilState(store, predicate);
|
||||
}
|
||||
|
||||
function* createTempFile() {
|
||||
let file = FileUtils.getFile("TmpD", ["tmp.fxsnapshot"]);
|
||||
file.createUnique(Ci.nsIFile.NORMAL_FILE_TYPE, FileUtils.PERMS_FILE);
|
||||
let destPath = file.path;
|
||||
let stat = yield OS.File.stat(destPath);
|
||||
ok(stat.size === 0, "new file is 0 bytes at start");
|
||||
return destPath;
|
||||
}
|
||||
|
|
@ -0,0 +1,38 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
// Test clearSnapshots deletes snapshots with READ censuses
|
||||
|
||||
let { takeSnapshotAndCensus, clearSnapshots } = require("devtools/client/memory/actions/snapshot");
|
||||
let { snapshotState: states, actions } = require("devtools/client/memory/constants");
|
||||
const { treeMapState } = require("devtools/client/memory/constants");
|
||||
|
||||
function run_test() {
|
||||
run_next_test();
|
||||
}
|
||||
|
||||
add_task(function* () {
|
||||
let front = new StubbedMemoryFront();
|
||||
let heapWorker = new HeapAnalysesClient();
|
||||
yield front.attach();
|
||||
let store = Store();
|
||||
const { getState, dispatch } = store;
|
||||
|
||||
dispatch(takeSnapshotAndCensus(front, heapWorker));
|
||||
yield waitUntilCensusState(store, s => s.treeMap, [treeMapState.SAVED]);
|
||||
ok(true, "snapshot created");
|
||||
|
||||
ok(true, "dispatch clearSnapshots action");
|
||||
let deleteEvents = Promise.all([
|
||||
waitUntilAction(store, actions.DELETE_SNAPSHOTS_START),
|
||||
waitUntilAction(store, actions.DELETE_SNAPSHOTS_END)
|
||||
]);
|
||||
dispatch(clearSnapshots(heapWorker));
|
||||
yield deleteEvents;
|
||||
ok(true, "received delete snapshots events");
|
||||
|
||||
equal(getState().snapshots.length, 0, "no snapshot remaining");
|
||||
|
||||
heapWorker.destroy();
|
||||
yield front.detach();
|
||||
});
|
||||
|
|
@ -0,0 +1,47 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
// Test clearSnapshots preserves snapshots with state != READ or ERROR
|
||||
|
||||
let { takeSnapshotAndCensus, clearSnapshots, takeSnapshot } = require("devtools/client/memory/actions/snapshot");
|
||||
let { snapshotState: states, treeMapState, actions } = require("devtools/client/memory/constants");
|
||||
|
||||
function run_test() {
|
||||
run_next_test();
|
||||
}
|
||||
|
||||
add_task(function* () {
|
||||
let front = new StubbedMemoryFront();
|
||||
let heapWorker = new HeapAnalysesClient();
|
||||
yield front.attach();
|
||||
let store = Store();
|
||||
const { getState, dispatch } = store;
|
||||
|
||||
ok(true, "create a snapshot with a census in SAVED state");
|
||||
dispatch(takeSnapshotAndCensus(front, heapWorker));
|
||||
ok(true, "create a snapshot in SAVED state");
|
||||
dispatch(takeSnapshot(front));
|
||||
yield waitUntilSnapshotState(store, [states.SAVED, states.SAVED]);
|
||||
yield waitUntilCensusState(store, snapshot => snapshot.treeMap,
|
||||
[treeMapState.SAVED, null]);
|
||||
ok(true, "snapshots created with expected states");
|
||||
|
||||
ok(true, "dispatch clearSnapshots action");
|
||||
let deleteEvents = Promise.all([
|
||||
waitUntilAction(store, actions.DELETE_SNAPSHOTS_START),
|
||||
waitUntilAction(store, actions.DELETE_SNAPSHOTS_END)
|
||||
]);
|
||||
dispatch(clearSnapshots(heapWorker));
|
||||
yield deleteEvents;
|
||||
ok(true, "received delete snapshots events");
|
||||
|
||||
equal(getState().snapshots.length, 1, "one snapshot remaining");
|
||||
let remainingSnapshot = getState().snapshots[0];
|
||||
equal(remainingSnapshot.treeMap, undefined,
|
||||
"remaining snapshot doesn't have a treeMap property");
|
||||
equal(remainingSnapshot.census, undefined,
|
||||
"remaining snapshot doesn't have a census property");
|
||||
|
||||
heapWorker.destroy();
|
||||
yield front.detach();
|
||||
});
|
||||
|
|
@ -0,0 +1,46 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
// Test clearSnapshots deletes snapshots with state ERROR
|
||||
|
||||
let { takeSnapshotAndCensus, clearSnapshots } = require("devtools/client/memory/actions/snapshot");
|
||||
let { snapshotState: states, treeMapState, actions } = require("devtools/client/memory/constants");
|
||||
|
||||
function run_test() {
|
||||
run_next_test();
|
||||
}
|
||||
|
||||
add_task(function* () {
|
||||
let front = new StubbedMemoryFront();
|
||||
let heapWorker = new HeapAnalysesClient();
|
||||
yield front.attach();
|
||||
let store = Store();
|
||||
const { getState, dispatch } = store;
|
||||
|
||||
ok(true, "create a snapshot with a treeMap");
|
||||
dispatch(takeSnapshotAndCensus(front, heapWorker));
|
||||
yield waitUntilSnapshotState(store, [states.SAVED]);
|
||||
ok(true, "snapshot created with a SAVED state");
|
||||
yield waitUntilCensusState(store, snapshot => snapshot.treeMap,
|
||||
[treeMapState.SAVED]);
|
||||
ok(true, "treeMap created with a SAVED state");
|
||||
|
||||
ok(true, "set snapshot state to error");
|
||||
let id = getState().snapshots[0].id;
|
||||
dispatch({ type: actions.SNAPSHOT_ERROR, id, error: new Error("_") });
|
||||
yield waitUntilSnapshotState(store, [states.ERROR]);
|
||||
ok(true, "snapshot set to error state");
|
||||
|
||||
ok(true, "dispatch clearSnapshots action");
|
||||
let deleteEvents = Promise.all([
|
||||
waitUntilAction(store, actions.DELETE_SNAPSHOTS_START),
|
||||
waitUntilAction(store, actions.DELETE_SNAPSHOTS_END)
|
||||
]);
|
||||
dispatch(clearSnapshots(heapWorker));
|
||||
yield deleteEvents;
|
||||
ok(true, "received delete snapshots events");
|
||||
equal(getState().snapshots.length, 0, "error snapshot deleted");
|
||||
|
||||
heapWorker.destroy();
|
||||
yield front.detach();
|
||||
});
|
||||
|
|
@ -0,0 +1,49 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
// Test clearSnapshots deletes several snapshots
|
||||
|
||||
let { takeSnapshotAndCensus, clearSnapshots } = require("devtools/client/memory/actions/snapshot");
|
||||
let { snapshotState: states, actions, treeMapState } = require("devtools/client/memory/constants");
|
||||
|
||||
function run_test() {
|
||||
run_next_test();
|
||||
}
|
||||
|
||||
add_task(function* () {
|
||||
let front = new StubbedMemoryFront();
|
||||
let heapWorker = new HeapAnalysesClient();
|
||||
yield front.attach();
|
||||
let store = Store();
|
||||
const { getState, dispatch } = store;
|
||||
|
||||
ok(true, "create 3 snapshots with a saved census");
|
||||
dispatch(takeSnapshotAndCensus(front, heapWorker));
|
||||
dispatch(takeSnapshotAndCensus(front, heapWorker));
|
||||
dispatch(takeSnapshotAndCensus(front, heapWorker));
|
||||
yield waitUntilCensusState(store, snapshot => snapshot.treeMap,
|
||||
[treeMapState.SAVED, treeMapState.SAVED,
|
||||
treeMapState.SAVED]);
|
||||
ok(true, "snapshots created with a saved census");
|
||||
|
||||
ok(true, "set first snapshot state to error");
|
||||
let id = getState().snapshots[0].id;
|
||||
dispatch({ type: actions.SNAPSHOT_ERROR, id, error: new Error("_") });
|
||||
yield waitUntilSnapshotState(store,
|
||||
[states.ERROR, states.READ, states.READ]);
|
||||
ok(true, "first snapshot set to error state");
|
||||
|
||||
ok(true, "dispatch clearSnapshots action");
|
||||
let deleteEvents = Promise.all([
|
||||
waitUntilAction(store, actions.DELETE_SNAPSHOTS_START),
|
||||
waitUntilAction(store, actions.DELETE_SNAPSHOTS_END)
|
||||
]);
|
||||
dispatch(clearSnapshots(heapWorker));
|
||||
yield deleteEvents;
|
||||
ok(true, "received delete snapshots events");
|
||||
|
||||
equal(getState().snapshots.length, 0, "no snapshot remaining");
|
||||
|
||||
heapWorker.destroy();
|
||||
yield front.detach();
|
||||
});
|
||||
|
|
@ -0,0 +1,47 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
// Test clearSnapshots deletes several snapshots
|
||||
|
||||
let { takeSnapshotAndCensus, clearSnapshots } = require("devtools/client/memory/actions/snapshot");
|
||||
let { snapshotState: states, actions, treeMapState } = require("devtools/client/memory/constants");
|
||||
|
||||
function run_test() {
|
||||
run_next_test();
|
||||
}
|
||||
|
||||
add_task(function* () {
|
||||
let front = new StubbedMemoryFront();
|
||||
let heapWorker = new HeapAnalysesClient();
|
||||
yield front.attach();
|
||||
let store = Store();
|
||||
const { getState, dispatch } = store;
|
||||
|
||||
ok(true, "create 2 snapshots with a saved census");
|
||||
dispatch(takeSnapshotAndCensus(front, heapWorker));
|
||||
dispatch(takeSnapshotAndCensus(front, heapWorker));
|
||||
ok(true, "snapshots created with a saved census");
|
||||
yield waitUntilCensusState(store, snapshot => snapshot.treeMap,
|
||||
[treeMapState.SAVED, treeMapState.SAVED]);
|
||||
|
||||
let errorHeapWorker = {
|
||||
deleteHeapSnapshot: function () {
|
||||
return Promise.reject("_");
|
||||
}
|
||||
};
|
||||
|
||||
ok(true, "dispatch clearSnapshots action");
|
||||
let deleteEvents = Promise.all([
|
||||
waitUntilAction(store, actions.DELETE_SNAPSHOTS_START),
|
||||
waitUntilAction(store, actions.DELETE_SNAPSHOTS_END),
|
||||
waitUntilAction(store, actions.SNAPSHOT_ERROR),
|
||||
waitUntilAction(store, actions.SNAPSHOT_ERROR),
|
||||
]);
|
||||
dispatch(clearSnapshots(errorHeapWorker));
|
||||
yield deleteEvents;
|
||||
ok(true, "received delete snapshots and snapshot error events");
|
||||
equal(getState().snapshots.length, 0, "no snapshot remaining");
|
||||
|
||||
heapWorker.destroy();
|
||||
yield front.detach();
|
||||
});
|
||||
|
|
@ -0,0 +1,65 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
"use strict";
|
||||
|
||||
// Test that clearSnapshots disables diffing when deleting snapshots
|
||||
|
||||
const {
|
||||
takeSnapshotAndCensus,
|
||||
clearSnapshots
|
||||
} = require("devtools/client/memory/actions/snapshot");
|
||||
const {
|
||||
snapshotState: states,
|
||||
actions,
|
||||
treeMapState
|
||||
} = require("devtools/client/memory/constants");
|
||||
const {
|
||||
toggleDiffing,
|
||||
selectSnapshotForDiffingAndRefresh
|
||||
} = require("devtools/client/memory/actions/diffing");
|
||||
|
||||
function run_test() {
|
||||
run_next_test();
|
||||
}
|
||||
|
||||
add_task(function* () {
|
||||
let front = new StubbedMemoryFront();
|
||||
let heapWorker = new HeapAnalysesClient();
|
||||
yield front.attach();
|
||||
let store = Store();
|
||||
const { getState, dispatch } = store;
|
||||
|
||||
ok(true, "create 2 snapshots with a saved census");
|
||||
dispatch(takeSnapshotAndCensus(front, heapWorker));
|
||||
dispatch(takeSnapshotAndCensus(front, heapWorker));
|
||||
yield waitUntilCensusState(store, snapshot => snapshot.treeMap,
|
||||
[treeMapState.SAVED, treeMapState.SAVED]);
|
||||
ok(true, "snapshots created with a saved census");
|
||||
|
||||
dispatch(toggleDiffing());
|
||||
dispatch(selectSnapshotForDiffingAndRefresh(heapWorker,
|
||||
getState().snapshots[0]));
|
||||
dispatch(selectSnapshotForDiffingAndRefresh(heapWorker,
|
||||
getState().snapshots[1]));
|
||||
|
||||
ok(getState().diffing, "We should be in diffing view");
|
||||
|
||||
yield waitUntilAction(store, actions.TAKE_CENSUS_DIFF_END);
|
||||
ok(true, "Received TAKE_CENSUS_DIFF_END action");
|
||||
|
||||
ok(true, "Dispatch clearSnapshots action");
|
||||
let deleteEvents = Promise.all([
|
||||
waitUntilAction(store, actions.DELETE_SNAPSHOTS_START),
|
||||
waitUntilAction(store, actions.DELETE_SNAPSHOTS_END)
|
||||
]);
|
||||
dispatch(clearSnapshots(heapWorker));
|
||||
yield deleteEvents;
|
||||
ok(true, "received delete snapshots events");
|
||||
|
||||
ok(getState().snapshots.length === 0, "Snapshots array should be empty");
|
||||
ok(!getState().diffing, "We should no longer be diffing");
|
||||
|
||||
heapWorker.destroy();
|
||||
yield front.detach();
|
||||
});
|
||||
|
|
@ -0,0 +1,39 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
// Test exporting a snapshot to a user specified location on disk.
|
||||
|
||||
let { exportSnapshot } = require("devtools/client/memory/actions/io");
|
||||
let { takeSnapshotAndCensus } = require("devtools/client/memory/actions/snapshot");
|
||||
let { snapshotState: states, actions, treeMapState } = require("devtools/client/memory/constants");
|
||||
|
||||
function run_test() {
|
||||
run_next_test();
|
||||
}
|
||||
|
||||
add_task(function* () {
|
||||
let front = new StubbedMemoryFront();
|
||||
let heapWorker = new HeapAnalysesClient();
|
||||
yield front.attach();
|
||||
let store = Store();
|
||||
const { getState, dispatch } = store;
|
||||
|
||||
let destPath = yield createTempFile();
|
||||
dispatch(takeSnapshotAndCensus(front, heapWorker));
|
||||
yield waitUntilCensusState(store, snapshot => snapshot.treeMap,
|
||||
[treeMapState.SAVED]);
|
||||
|
||||
let exportEvents = Promise.all([
|
||||
waitUntilAction(store, actions.EXPORT_SNAPSHOT_START),
|
||||
waitUntilAction(store, actions.EXPORT_SNAPSHOT_END)
|
||||
]);
|
||||
dispatch(exportSnapshot(getState().snapshots[0], destPath));
|
||||
yield exportEvents;
|
||||
|
||||
stat = yield OS.File.stat(destPath);
|
||||
do_print(stat.size);
|
||||
ok(stat.size > 0, "destination file is more than 0 bytes");
|
||||
|
||||
heapWorker.destroy();
|
||||
yield front.detach();
|
||||
});
|
||||
23
devtools/client/memory/test/unit/test_action-filter-01.js
Normal file
23
devtools/client/memory/test/unit/test_action-filter-01.js
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
// Test setting the filter string.
|
||||
|
||||
let { setFilterString } = require("devtools/client/memory/actions/filter");
|
||||
|
||||
function run_test() {
|
||||
run_next_test();
|
||||
}
|
||||
|
||||
add_task(function* () {
|
||||
let store = Store();
|
||||
const { getState, dispatch } = store;
|
||||
|
||||
equal(getState().filter, null, "no filter by default");
|
||||
|
||||
dispatch(setFilterString("my filter"));
|
||||
equal(getState().filter, "my filter", "now we have the expected filter");
|
||||
|
||||
dispatch(setFilterString(""));
|
||||
equal(getState().filter, null, "no filter again");
|
||||
});
|
||||
74
devtools/client/memory/test/unit/test_action-filter-02.js
Normal file
74
devtools/client/memory/test/unit/test_action-filter-02.js
Normal file
|
|
@ -0,0 +1,74 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
"use strict";
|
||||
|
||||
// Test that changing filter state properly refreshes the selected census.
|
||||
|
||||
let { snapshotState: states, viewState, censusState } = require("devtools/client/memory/constants");
|
||||
let { setFilterStringAndRefresh } = require("devtools/client/memory/actions/filter");
|
||||
let { takeSnapshotAndCensus, selectSnapshotAndRefresh } = require("devtools/client/memory/actions/snapshot");
|
||||
let { setCensusDisplay } = require("devtools/client/memory/actions/census-display");
|
||||
let { changeView } = require("devtools/client/memory/actions/view");
|
||||
|
||||
function run_test() {
|
||||
run_next_test();
|
||||
}
|
||||
|
||||
add_task(function* () {
|
||||
let front = new StubbedMemoryFront();
|
||||
let heapWorker = new HeapAnalysesClient();
|
||||
yield front.attach();
|
||||
let store = Store();
|
||||
let { getState, dispatch } = store;
|
||||
|
||||
dispatch(changeView(viewState.CENSUS));
|
||||
|
||||
equal(getState().filter, null, "no filter by default");
|
||||
|
||||
dispatch(takeSnapshotAndCensus(front, heapWorker));
|
||||
dispatch(takeSnapshotAndCensus(front, heapWorker));
|
||||
dispatch(takeSnapshotAndCensus(front, heapWorker));
|
||||
|
||||
yield waitUntilCensusState(store, snapshot => snapshot.census,
|
||||
[censusState.SAVED,
|
||||
censusState.SAVED,
|
||||
censusState.SAVED]);
|
||||
ok(true, "saved 3 snapshots and took a census of each of them");
|
||||
|
||||
dispatch(setFilterStringAndRefresh("str", heapWorker));
|
||||
yield waitUntilCensusState(store, snapshot => snapshot.census,
|
||||
[censusState.SAVED,
|
||||
censusState.SAVED,
|
||||
censusState.SAVING]);
|
||||
ok(true, "setting filter string should recompute the selected snapshot's census");
|
||||
|
||||
equal(getState().filter, "str", "now inverted");
|
||||
|
||||
yield waitUntilCensusState(store, snapshot => snapshot.census,
|
||||
[censusState.SAVED,
|
||||
censusState.SAVED,
|
||||
censusState.SAVED]);
|
||||
|
||||
equal(getState().snapshots[0].census.filter, null);
|
||||
equal(getState().snapshots[1].census.filter, null);
|
||||
equal(getState().snapshots[2].census.filter, "str");
|
||||
|
||||
dispatch(selectSnapshotAndRefresh(heapWorker, getState().snapshots[1].id));
|
||||
yield waitUntilCensusState(store, snapshot => snapshot.census,
|
||||
[censusState.SAVED,
|
||||
censusState.SAVING,
|
||||
censusState.SAVED]);
|
||||
ok(true, "selecting non-inverted census should trigger a recompute");
|
||||
|
||||
yield waitUntilCensusState(store, snapshot => snapshot.census,
|
||||
[censusState.SAVED,
|
||||
censusState.SAVED,
|
||||
censusState.SAVED]);
|
||||
|
||||
equal(getState().snapshots[0].census.filter, null);
|
||||
equal(getState().snapshots[1].census.filter, "str");
|
||||
equal(getState().snapshots[2].census.filter, "str");
|
||||
|
||||
heapWorker.destroy();
|
||||
yield front.detach();
|
||||
});
|
||||
52
devtools/client/memory/test/unit/test_action-filter-03.js
Normal file
52
devtools/client/memory/test/unit/test_action-filter-03.js
Normal file
|
|
@ -0,0 +1,52 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
// Test that changing filter state in the middle of taking a snapshot results in
|
||||
// the properly fitered census.
|
||||
|
||||
let { snapshotState: states, censusState, viewState } = require("devtools/client/memory/constants");
|
||||
let { setFilterString, setFilterStringAndRefresh } = require("devtools/client/memory/actions/filter");
|
||||
let { takeSnapshotAndCensus } = require("devtools/client/memory/actions/snapshot");
|
||||
let { changeView } = require("devtools/client/memory/actions/view");
|
||||
|
||||
function run_test() {
|
||||
run_next_test();
|
||||
}
|
||||
|
||||
add_task(function* () {
|
||||
let front = new StubbedMemoryFront();
|
||||
let heapWorker = new HeapAnalysesClient();
|
||||
yield front.attach();
|
||||
let store = Store();
|
||||
let { getState, dispatch } = store;
|
||||
|
||||
dispatch(changeView(viewState.CENSUS));
|
||||
|
||||
dispatch(takeSnapshotAndCensus(front, heapWorker));
|
||||
yield waitUntilSnapshotState(store, [states.SAVING]);
|
||||
|
||||
dispatch(setFilterString("str"));
|
||||
|
||||
yield waitUntilCensusState(store, snapshot => snapshot.census,
|
||||
[censusState.SAVED]);
|
||||
equal(getState().filter, "str",
|
||||
"should want filtered trees");
|
||||
equal(getState().snapshots[0].census.filter, "str",
|
||||
"snapshot-we-were-in-the-middle-of-saving's census should be filtered");
|
||||
|
||||
dispatch(setFilterStringAndRefresh("", heapWorker));
|
||||
yield waitUntilCensusState(store, snapshot => snapshot.census,
|
||||
[censusState.SAVING]);
|
||||
ok(true, "changing filter string retriggers census");
|
||||
ok(!getState().filter, "no longer filtering");
|
||||
|
||||
dispatch(setFilterString("obj"));
|
||||
yield waitUntilCensusState(store, snapshot => snapshot.census,
|
||||
[censusState.SAVED]);
|
||||
equal(getState().filter, "obj", "filtering for obj now");
|
||||
equal(getState().snapshots[0].census.filter, "obj",
|
||||
"census-we-were-in-the-middle-of-recomputing should be filtered again");
|
||||
|
||||
heapWorker.destroy();
|
||||
yield front.detach();
|
||||
});
|
||||
|
|
@ -0,0 +1,98 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
"use strict";
|
||||
|
||||
/**
|
||||
* Tests the task creator `importSnapshotAndCensus()` for the whole flow of
|
||||
* importing a snapshot, and its sub-actions.
|
||||
*/
|
||||
|
||||
let { actions, snapshotState: states, treeMapState } = require("devtools/client/memory/constants");
|
||||
let { exportSnapshot, importSnapshotAndCensus } = require("devtools/client/memory/actions/io");
|
||||
let { takeSnapshotAndCensus } = require("devtools/client/memory/actions/snapshot");
|
||||
|
||||
function run_test() {
|
||||
run_next_test();
|
||||
}
|
||||
|
||||
add_task(function* () {
|
||||
let front = new StubbedMemoryFront();
|
||||
let heapWorker = new HeapAnalysesClient();
|
||||
yield front.attach();
|
||||
let store = Store();
|
||||
let { subscribe, dispatch, getState } = store;
|
||||
|
||||
let destPath = yield createTempFile();
|
||||
dispatch(takeSnapshotAndCensus(front, heapWorker));
|
||||
yield waitUntilCensusState(store, s => s.treeMap, [treeMapState.SAVED]);
|
||||
|
||||
let exportEvents = Promise.all([
|
||||
waitUntilAction(store, actions.EXPORT_SNAPSHOT_START),
|
||||
waitUntilAction(store, actions.EXPORT_SNAPSHOT_END)
|
||||
]);
|
||||
dispatch(exportSnapshot(getState().snapshots[0], destPath));
|
||||
yield exportEvents;
|
||||
|
||||
// Now import our freshly exported snapshot
|
||||
let snapshotI = 0;
|
||||
let censusI = 0;
|
||||
let snapshotStates = ["IMPORTING", "READING", "READ"];
|
||||
let censusStates = ["SAVING", "SAVED"];
|
||||
let expectStates = () => {
|
||||
let snapshot = getState().snapshots[1];
|
||||
if (!snapshot) {
|
||||
return;
|
||||
}
|
||||
if (snapshotI < snapshotStates.length) {
|
||||
let isCorrectState = snapshot.state === states[snapshotStates[snapshotI]];
|
||||
if (isCorrectState) {
|
||||
ok(true, `Found expected snapshot state ${snapshotStates[snapshotI]}`);
|
||||
snapshotI++;
|
||||
}
|
||||
}
|
||||
if (snapshot.treeMap && censusI < censusStates.length) {
|
||||
if (snapshot.treeMap.state === treeMapState[censusStates[censusI]]) {
|
||||
ok(true, `Found expected census state ${censusStates[censusI]}`);
|
||||
censusI++;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
let unsubscribe = subscribe(expectStates);
|
||||
dispatch(importSnapshotAndCensus(heapWorker, destPath));
|
||||
|
||||
yield waitUntilState(store, () => { return snapshotI === snapshotStates.length &&
|
||||
censusI === censusStates.length; });
|
||||
unsubscribe();
|
||||
equal(snapshotI, snapshotStates.length, "importSnapshotAndCensus() produces the correct sequence of states in a snapshot");
|
||||
equal(getState().snapshots[1].state, states.READ, "imported snapshot is in READ state");
|
||||
equal(censusI, censusStates.length, "importSnapshotAndCensus() produces the correct sequence of states in a census");
|
||||
equal(getState().snapshots[1].treeMap.state, treeMapState.SAVED, "imported snapshot is in READ state");
|
||||
ok(getState().snapshots[1].selected, "imported snapshot is selected");
|
||||
|
||||
// Check snapshot data
|
||||
let snapshot1 = getState().snapshots[0];
|
||||
let snapshot2 = getState().snapshots[1];
|
||||
|
||||
equal(snapshot1.treeMap.display, snapshot2.treeMap.display,
|
||||
"imported snapshot has correct display");
|
||||
|
||||
// Clone the census data so we can destructively remove the ID/parents to compare
|
||||
// equal census data
|
||||
let census1 = stripUnique(JSON.parse(JSON.stringify(snapshot1.treeMap.report)));
|
||||
let census2 = stripUnique(JSON.parse(JSON.stringify(snapshot2.treeMap.report)));
|
||||
|
||||
equal(JSON.stringify(census1), JSON.stringify(census2), "Imported snapshot has correct census");
|
||||
|
||||
function stripUnique(obj) {
|
||||
let children = obj.children || [];
|
||||
for (let child of children) {
|
||||
delete child.id;
|
||||
delete child.parent;
|
||||
stripUnique(child);
|
||||
}
|
||||
delete obj.id;
|
||||
delete obj.parent;
|
||||
return obj;
|
||||
}
|
||||
});
|
||||
|
|
@ -0,0 +1,84 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
"use strict";
|
||||
|
||||
/**
|
||||
* Tests `importSnapshotAndCensus()` when importing snapshots from the dominator
|
||||
* tree view. The snapshot is expected to be loaded and its dominator tree
|
||||
* should be computed.
|
||||
*/
|
||||
|
||||
let { snapshotState, dominatorTreeState, viewState, treeMapState } =
|
||||
require("devtools/client/memory/constants");
|
||||
let { importSnapshotAndCensus } = require("devtools/client/memory/actions/io");
|
||||
let { changeViewAndRefresh } = require("devtools/client/memory/actions/view");
|
||||
|
||||
function run_test() {
|
||||
run_next_test();
|
||||
}
|
||||
|
||||
add_task(function* () {
|
||||
let front = new StubbedMemoryFront();
|
||||
let heapWorker = new HeapAnalysesClient();
|
||||
yield front.attach();
|
||||
let store = Store();
|
||||
let { subscribe, dispatch, getState } = store;
|
||||
|
||||
dispatch(changeViewAndRefresh(viewState.DOMINATOR_TREE, heapWorker));
|
||||
equal(getState().view.state, viewState.DOMINATOR_TREE,
|
||||
"We should now be in the DOMINATOR_TREE view");
|
||||
|
||||
let i = 0;
|
||||
let expected = [
|
||||
"IMPORTING",
|
||||
"READING",
|
||||
"READ",
|
||||
"treeMap:SAVING",
|
||||
"treeMap:SAVED",
|
||||
"dominatorTree:COMPUTING",
|
||||
"dominatorTree:FETCHING",
|
||||
"dominatorTree:LOADED",
|
||||
];
|
||||
let expectStates = () => {
|
||||
let snapshot = getState().snapshots[0];
|
||||
if (snapshot && hasExpectedState(snapshot, expected[i])) {
|
||||
ok(true, `Found expected state ${expected[i]}`);
|
||||
i++;
|
||||
}
|
||||
};
|
||||
|
||||
let unsubscribe = subscribe(expectStates);
|
||||
const snapshotPath = yield front.saveHeapSnapshot();
|
||||
dispatch(importSnapshotAndCensus(heapWorker, snapshotPath));
|
||||
|
||||
yield waitUntilState(store, () => i === expected.length);
|
||||
unsubscribe();
|
||||
equal(i, expected.length, "importSnapshotAndCensus() produces the correct " +
|
||||
"sequence of states in a snapshot");
|
||||
equal(getState().snapshots[0].dominatorTree.state, dominatorTreeState.LOADED,
|
||||
"imported snapshot's dominator tree is in LOADED state");
|
||||
ok(getState().snapshots[0].selected, "imported snapshot is selected");
|
||||
});
|
||||
|
||||
/**
|
||||
* Check that the provided snapshot is in the expected state. The expected state
|
||||
* is a snapshotState by default. If the expected state is prefixed by
|
||||
* dominatorTree, a dominatorTree is expected on the provided snapshot, in the
|
||||
* corresponding state from dominatorTreeState.
|
||||
*/
|
||||
function hasExpectedState(snapshot, expectedState) {
|
||||
let isDominatorState = expectedState.indexOf("dominatorTree:") === 0;
|
||||
if (isDominatorState) {
|
||||
let state = dominatorTreeState[expectedState.replace("dominatorTree:", "")];
|
||||
return snapshot.dominatorTree && snapshot.dominatorTree.state === state;
|
||||
}
|
||||
|
||||
let isTreeMapState = expectedState.indexOf("treeMap:") === 0;
|
||||
if (isTreeMapState) {
|
||||
let state = treeMapState[expectedState.replace("treeMap:", "")];
|
||||
return snapshot.treeMap && snapshot.treeMap.state === state;
|
||||
}
|
||||
|
||||
let state = snapshotState[expectedState];
|
||||
return snapshot.state === state;
|
||||
}
|
||||
|
|
@ -0,0 +1,37 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
/**
|
||||
* Tests the reducer responding to the action `selectSnapshot(snapshot)`
|
||||
*/
|
||||
|
||||
let actions = require("devtools/client/memory/actions/snapshot");
|
||||
let { snapshotState: states } = require("devtools/client/memory/constants");
|
||||
|
||||
function run_test() {
|
||||
run_next_test();
|
||||
}
|
||||
|
||||
add_task(function* () {
|
||||
let front = new StubbedMemoryFront();
|
||||
yield front.attach();
|
||||
let store = Store();
|
||||
|
||||
for (let i = 0; i < 5; i++) {
|
||||
store.dispatch(actions.takeSnapshot(front));
|
||||
}
|
||||
|
||||
yield waitUntilState(store, ({ snapshots }) => snapshots.length === 5 && snapshots.every(isDone));
|
||||
|
||||
for (let i = 0; i < 5; i++) {
|
||||
do_print(`Selecting snapshot[${i}]`);
|
||||
store.dispatch(actions.selectSnapshot(store.getState().snapshots[i].id));
|
||||
yield waitUntilState(store, ({ snapshots }) => snapshots[i].selected);
|
||||
|
||||
let { snapshots } = store.getState();
|
||||
ok(snapshots[i].selected, `snapshot[${i}] selected`);
|
||||
equal(snapshots.filter(s => !s.selected).length, 4, "All other snapshots are unselected");
|
||||
}
|
||||
});
|
||||
|
||||
function isDone(s) { return s.state === states.SAVED; }
|
||||
|
|
@ -0,0 +1,118 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
"use strict";
|
||||
|
||||
/**
|
||||
* Tests the task creator `setCensusDisplayAndRefreshAndRefresh()` for display
|
||||
* changing. We test this rather than `setCensusDisplayAndRefresh` directly, as
|
||||
* we use the refresh action in the app itself composed from
|
||||
* `setCensusDisplayAndRefresh`.
|
||||
*/
|
||||
|
||||
let { censusDisplays, snapshotState: states, censusState, viewState } = require("devtools/client/memory/constants");
|
||||
let { setCensusDisplayAndRefresh } = require("devtools/client/memory/actions/census-display");
|
||||
let { takeSnapshotAndCensus, selectSnapshotAndRefresh } = require("devtools/client/memory/actions/snapshot");
|
||||
const { changeView } = require("devtools/client/memory/actions/view");
|
||||
|
||||
function run_test() {
|
||||
run_next_test();
|
||||
}
|
||||
|
||||
// We test setting an invalid display, which triggers an assertion failure.
|
||||
EXPECTED_DTU_ASSERT_FAILURE_COUNT = 1;
|
||||
|
||||
add_task(function* () {
|
||||
let front = new StubbedMemoryFront();
|
||||
let heapWorker = new HeapAnalysesClient();
|
||||
yield front.attach();
|
||||
let store = Store();
|
||||
let { getState, dispatch } = store;
|
||||
|
||||
dispatch(changeView(viewState.CENSUS));
|
||||
|
||||
// Test default display with no snapshots
|
||||
equal(getState().censusDisplay.breakdown.by, "coarseType",
|
||||
"default coarseType display selected at start.");
|
||||
dispatch(setCensusDisplayAndRefresh(heapWorker,
|
||||
censusDisplays.allocationStack));
|
||||
equal(getState().censusDisplay.breakdown.by, "allocationStack",
|
||||
"display changed with no snapshots");
|
||||
|
||||
// Test invalid displays
|
||||
ok(getState().errors.length === 0, "No error actions in the queue.");
|
||||
dispatch(setCensusDisplayAndRefresh(heapWorker, {}));
|
||||
yield waitUntilState(store, () => getState().errors.length === 1);
|
||||
ok(true, "Emits an error action when passing in an invalid display object");
|
||||
|
||||
equal(getState().censusDisplay.breakdown.by, "allocationStack",
|
||||
"current display unchanged when passing invalid display");
|
||||
|
||||
// Test new snapshots
|
||||
dispatch(takeSnapshotAndCensus(front, heapWorker));
|
||||
yield waitUntilCensusState(store, snapshot => snapshot.census,
|
||||
[censusState.SAVED]);
|
||||
|
||||
equal(getState().snapshots[0].census.display, censusDisplays.allocationStack,
|
||||
"New snapshot's census uses correct display");
|
||||
|
||||
// Updates when changing display during `SAVING`
|
||||
dispatch(takeSnapshotAndCensus(front, heapWorker));
|
||||
yield waitUntilCensusState(store, snapshot => snapshot.census,
|
||||
[censusState.SAVED, censusState.SAVING]);
|
||||
dispatch(setCensusDisplayAndRefresh(heapWorker, censusDisplays.coarseType));
|
||||
yield waitUntilCensusState(store, snapshot => snapshot.census,
|
||||
[censusState.SAVED, censusState.SAVED]);
|
||||
equal(getState().snapshots[1].census.display, censusDisplays.coarseType,
|
||||
"Changing display while saving a snapshot results in a census using the new display");
|
||||
|
||||
|
||||
// Updates when changing display during `SAVING_CENSUS`
|
||||
dispatch(takeSnapshotAndCensus(front, heapWorker));
|
||||
yield waitUntilCensusState(store, snapshot => snapshot.census,
|
||||
[censusState.SAVED,
|
||||
censusState.SAVED,
|
||||
censusState.SAVING]);
|
||||
dispatch(setCensusDisplayAndRefresh(heapWorker, censusDisplays.allocationStack));
|
||||
yield waitUntilCensusState(store, snapshot => snapshot.census,
|
||||
[censusState.SAVED,
|
||||
censusState.SAVED,
|
||||
censusState.SAVED]);
|
||||
equal(getState().snapshots[2].census.display, censusDisplays.allocationStack,
|
||||
"Display can be changed while saving census, stores updated display in snapshot");
|
||||
|
||||
// Updates census on currently selected snapshot when changing display
|
||||
ok(getState().snapshots[2].selected, "Third snapshot currently selected");
|
||||
dispatch(setCensusDisplayAndRefresh(heapWorker, censusDisplays.coarseType));
|
||||
yield waitUntilState(store, state => state.snapshots[2].census.state === censusState.SAVING);
|
||||
yield waitUntilState(store, state => state.snapshots[2].census.state === censusState.SAVED);
|
||||
equal(getState().snapshots[2].census.display, censusDisplays.coarseType,
|
||||
"Snapshot census updated when changing displays after already generating one census");
|
||||
|
||||
dispatch(setCensusDisplayAndRefresh(heapWorker, censusDisplays.allocationStack));
|
||||
yield waitUntilState(store, state => state.snapshots[2].census.state === censusState.SAVED);
|
||||
equal(getState().snapshots[2].census.display, censusDisplays.allocationStack,
|
||||
"Snapshot census updated when changing displays after already generating one census");
|
||||
|
||||
// Does not update unselected censuses.
|
||||
ok(!getState().snapshots[1].selected, "Second snapshot selected currently");
|
||||
equal(getState().snapshots[1].census.display, censusDisplays.coarseType,
|
||||
"Second snapshot using `coarseType` display still and not yet updated to correct display");
|
||||
|
||||
// Updates to current display when switching to stale snapshot.
|
||||
dispatch(selectSnapshotAndRefresh(heapWorker, getState().snapshots[1].id));
|
||||
yield waitUntilCensusState(store, snapshot => snapshot.census,
|
||||
[censusState.SAVED,
|
||||
censusState.SAVING,
|
||||
censusState.SAVED]);
|
||||
yield waitUntilCensusState(store, snapshot => snapshot.census,
|
||||
[censusState.SAVED,
|
||||
censusState.SAVED,
|
||||
censusState.SAVED]);
|
||||
|
||||
ok(getState().snapshots[1].selected, "Second snapshot selected currently");
|
||||
equal(getState().snapshots[1].census.display, censusDisplays.allocationStack,
|
||||
"Second snapshot using `allocationStack` display and updated to correct display");
|
||||
|
||||
heapWorker.destroy();
|
||||
yield front.detach();
|
||||
});
|
||||
|
|
@ -0,0 +1,53 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
"use strict";
|
||||
|
||||
/**
|
||||
* Tests the task creator `setCensusDisplayAndRefreshAndRefresh()` for custom
|
||||
* displays.
|
||||
*/
|
||||
|
||||
let { snapshotState: states, censusState, viewState } = require("devtools/client/memory/constants");
|
||||
let { setCensusDisplayAndRefresh } = require("devtools/client/memory/actions/census-display");
|
||||
let { takeSnapshotAndCensus } = require("devtools/client/memory/actions/snapshot");
|
||||
let { changeView } = require("devtools/client/memory/actions/view");
|
||||
|
||||
let CUSTOM = {
|
||||
displayName: "Custom",
|
||||
tooltip: "Custom tooltip",
|
||||
inverted: false,
|
||||
breakdown: {
|
||||
by: "internalType",
|
||||
then: { by: "count", bytes: true, count: false }
|
||||
}
|
||||
};
|
||||
|
||||
function run_test() {
|
||||
run_next_test();
|
||||
}
|
||||
|
||||
add_task(function* () {
|
||||
let front = new StubbedMemoryFront();
|
||||
let heapWorker = new HeapAnalysesClient();
|
||||
yield front.attach();
|
||||
let store = Store();
|
||||
let { getState, dispatch } = store;
|
||||
|
||||
dispatch(changeView(viewState.CENSUS));
|
||||
dispatch(setCensusDisplayAndRefresh(heapWorker, CUSTOM));
|
||||
equal(getState().censusDisplay, CUSTOM,
|
||||
"CUSTOM display stored in display state.");
|
||||
|
||||
dispatch(takeSnapshotAndCensus(front, heapWorker));
|
||||
yield waitUntilCensusState(store, s => s.census, [censusState.SAVED]);
|
||||
|
||||
equal(getState().snapshots[0].census.display, CUSTOM,
|
||||
"New snapshot stored CUSTOM display when done taking census");
|
||||
ok(getState().snapshots[0].census.report.children.length, "Census has some children");
|
||||
// Ensure we don't have `count` in any results
|
||||
ok(getState().snapshots[0].census.report.children.every(c => !c.count),
|
||||
"Census used CUSTOM display without counts");
|
||||
// Ensure we do have `bytes` in the results
|
||||
ok(getState().snapshots[0].census.report.children.every(c => typeof c.bytes === "number"),
|
||||
"Census used CUSTOM display with bytes");
|
||||
});
|
||||
55
devtools/client/memory/test/unit/test_action-set-display.js
Normal file
55
devtools/client/memory/test/unit/test_action-set-display.js
Normal file
|
|
@ -0,0 +1,55 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
"use strict";
|
||||
|
||||
/**
|
||||
* Tests the action creator `setCensusDisplay()` for display changing. Does not
|
||||
* test refreshing the census information, check `setCensusDisplayAndRefresh`
|
||||
* action for that.
|
||||
*/
|
||||
|
||||
let { censusDisplays, snapshotState: states, censusState, viewState } = require("devtools/client/memory/constants");
|
||||
let { setCensusDisplay } = require("devtools/client/memory/actions/census-display");
|
||||
let { takeSnapshotAndCensus } = require("devtools/client/memory/actions/snapshot");
|
||||
const { changeView } = require("devtools/client/memory/actions/view");
|
||||
|
||||
function run_test() {
|
||||
run_next_test();
|
||||
}
|
||||
|
||||
// We test setting an invalid display, which triggers an assertion failure.
|
||||
EXPECTED_DTU_ASSERT_FAILURE_COUNT = 1;
|
||||
|
||||
add_task(function* () {
|
||||
let front = new StubbedMemoryFront();
|
||||
let heapWorker = new HeapAnalysesClient();
|
||||
yield front.attach();
|
||||
let store = Store();
|
||||
let { getState, dispatch } = store;
|
||||
|
||||
dispatch(changeView(viewState.CENSUS));
|
||||
|
||||
// Test default display with no snapshots
|
||||
equal(getState().censusDisplay.breakdown.by, "coarseType",
|
||||
"default coarseType display selected at start.");
|
||||
|
||||
dispatch(setCensusDisplay(censusDisplays.allocationStack));
|
||||
equal(getState().censusDisplay.breakdown.by, "allocationStack",
|
||||
"display changed with no snapshots");
|
||||
|
||||
// Test invalid displays
|
||||
try {
|
||||
dispatch(setCensusDisplay({}));
|
||||
ok(false, "Throws when passing in an invalid display object");
|
||||
} catch (e) {
|
||||
ok(true, "Throws when passing in an invalid display object");
|
||||
}
|
||||
equal(getState().censusDisplay.breakdown.by, "allocationStack",
|
||||
"current display unchanged when passing invalid display");
|
||||
|
||||
// Test new snapshots
|
||||
dispatch(takeSnapshotAndCensus(front, heapWorker));
|
||||
yield waitUntilCensusState(store, s => s.census, [censusState.SAVED]);
|
||||
equal(getState().snapshots[0].census.display, censusDisplays.allocationStack,
|
||||
"New snapshots use the current, non-default display");
|
||||
});
|
||||
59
devtools/client/memory/test/unit/test_action-take-census.js
Normal file
59
devtools/client/memory/test/unit/test_action-take-census.js
Normal file
|
|
@ -0,0 +1,59 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
/**
|
||||
* Tests the async reducer responding to the action `takeCensus(heapWorker, snapshot)`
|
||||
*/
|
||||
|
||||
var { snapshotState: states, censusDisplays, censusState, censusState, viewState } = require("devtools/client/memory/constants");
|
||||
var actions = require("devtools/client/memory/actions/snapshot");
|
||||
var { changeView } = require("devtools/client/memory/actions/view");
|
||||
|
||||
|
||||
function run_test() {
|
||||
run_next_test();
|
||||
}
|
||||
|
||||
// This tests taking a census on a snapshot that is still being read, which
|
||||
// triggers an assertion failure.
|
||||
EXPECTED_DTU_ASSERT_FAILURE_COUNT = 1;
|
||||
|
||||
add_task(function* () {
|
||||
let front = new StubbedMemoryFront();
|
||||
let heapWorker = new HeapAnalysesClient();
|
||||
yield front.attach();
|
||||
let store = Store();
|
||||
|
||||
store.dispatch(changeView(viewState.CENSUS));
|
||||
|
||||
store.dispatch(actions.takeSnapshot(front));
|
||||
yield waitUntilState(store, () => {
|
||||
let snapshots = store.getState().snapshots;
|
||||
return snapshots.length === 1 && snapshots[0].state === states.SAVED;
|
||||
});
|
||||
|
||||
let snapshot = store.getState().snapshots[0];
|
||||
equal(snapshot.census, null, "No census data exists yet on the snapshot.");
|
||||
|
||||
// Test error case of wrong state.
|
||||
store.dispatch(actions.takeCensus(heapWorker, snapshot.id));
|
||||
yield waitUntilState(store, () => store.getState().errors.length === 1);
|
||||
|
||||
dumpn("Found error: " + store.getState().errors[0]);
|
||||
ok(/Assertion failure/.test(store.getState().errors[0]),
|
||||
"Error thrown when taking a census of a snapshot that has not been read.");
|
||||
|
||||
store.dispatch(actions.readSnapshot(heapWorker, snapshot.id));
|
||||
yield waitUntilState(store, () => store.getState().snapshots[0].state === states.READ);
|
||||
|
||||
store.dispatch(actions.takeCensus(heapWorker, snapshot.id));
|
||||
yield waitUntilCensusState(store, s => s.census, [censusState.SAVING]);
|
||||
yield waitUntilCensusState(store, s => s.census, [censusState.SAVED]);
|
||||
|
||||
snapshot = store.getState().snapshots[0];
|
||||
ok(snapshot.census, "Snapshot has census after saved census");
|
||||
ok(snapshot.census.report.children.length, "Census is in tree node form");
|
||||
equal(snapshot.census.display, censusDisplays.coarseType,
|
||||
"Snapshot stored correct display used for the census");
|
||||
|
||||
});
|
||||
|
|
@ -0,0 +1,58 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
/**
|
||||
* Tests the task creator `takeSnapshotAndCensus()` for the whole flow of
|
||||
* taking a snapshot, and its sub-actions.
|
||||
*/
|
||||
|
||||
let { snapshotState: states, treeMapState } = require("devtools/client/memory/constants");
|
||||
let actions = require("devtools/client/memory/actions/snapshot");
|
||||
|
||||
function run_test() {
|
||||
run_next_test();
|
||||
}
|
||||
|
||||
add_task(function* () {
|
||||
let front = new StubbedMemoryFront();
|
||||
let heapWorker = new HeapAnalysesClient();
|
||||
yield front.attach();
|
||||
let store = Store();
|
||||
|
||||
let snapshotI = 0;
|
||||
let censusI = 0;
|
||||
let snapshotStates = ["SAVING", "SAVED", "READING", "READ"];
|
||||
let censusStates = ["SAVING", "SAVED"];
|
||||
let expectStates = () => {
|
||||
let snapshot = store.getState().snapshots[0];
|
||||
if (!snapshot) {
|
||||
return;
|
||||
}
|
||||
if (snapshotI < snapshotStates.length) {
|
||||
let isCorrectState = snapshot.state === states[snapshotStates[snapshotI]];
|
||||
if (isCorrectState) {
|
||||
ok(true, `Found expected snapshot state ${snapshotStates[snapshotI]}`);
|
||||
snapshotI++;
|
||||
}
|
||||
}
|
||||
if (snapshot.treeMap && censusI < censusStates.length) {
|
||||
if (snapshot.treeMap.state === treeMapState[censusStates[censusI]]) {
|
||||
ok(true, `Found expected census state ${censusStates[censusI]}`);
|
||||
censusI++;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
let unsubscribe = store.subscribe(expectStates);
|
||||
store.dispatch(actions.takeSnapshotAndCensus(front, heapWorker));
|
||||
|
||||
yield waitUntilState(store, () => { return snapshotI === snapshotStates.length &&
|
||||
censusI === censusStates.length; });
|
||||
unsubscribe();
|
||||
|
||||
ok(true, "takeSnapshotAndCensus() produces the correct sequence of states in a snapshot");
|
||||
let snapshot = store.getState().snapshots[0];
|
||||
ok(snapshot.treeMap, "snapshot has tree map census data");
|
||||
ok(snapshot.selected, "snapshot is selected");
|
||||
});
|
||||
|
|
@ -0,0 +1,54 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
/**
|
||||
* Tests the async reducer responding to the action `takeSnapshot(front)`
|
||||
*/
|
||||
|
||||
let actions = require("devtools/client/memory/actions/snapshot");
|
||||
let { snapshotState: states } = require("devtools/client/memory/constants");
|
||||
|
||||
function run_test() {
|
||||
run_next_test();
|
||||
}
|
||||
|
||||
add_task(function* () {
|
||||
let front = new StubbedMemoryFront();
|
||||
yield front.attach();
|
||||
let store = Store();
|
||||
|
||||
let unsubscribe = store.subscribe(checkState);
|
||||
|
||||
let foundPendingState = false;
|
||||
let foundDoneState = false;
|
||||
|
||||
function checkState() {
|
||||
let { snapshots } = store.getState();
|
||||
let lastSnapshot = snapshots[snapshots.length - 1];
|
||||
|
||||
if (lastSnapshot.state === states.SAVING) {
|
||||
foundPendingState = true;
|
||||
ok(foundPendingState, "Got state change for pending heap snapshot request");
|
||||
ok(!lastSnapshot.path, "Snapshot does not yet have a path");
|
||||
ok(!lastSnapshot.census, "Has no census data when loading");
|
||||
}
|
||||
else if (lastSnapshot.state === states.SAVED) {
|
||||
foundDoneState = true;
|
||||
ok(foundDoneState, "Got state change for completed heap snapshot request");
|
||||
ok(foundPendingState, "SAVED state occurs after SAVING state");
|
||||
ok(lastSnapshot.path, "Snapshot fetched with a path");
|
||||
ok(snapshots.every(s => s.selected === (s.id === lastSnapshot.id)),
|
||||
"Only recent snapshot is selected");
|
||||
}
|
||||
}
|
||||
|
||||
for (let i = 0; i < 4; i++) {
|
||||
store.dispatch(actions.takeSnapshot(front));
|
||||
yield waitUntilState(store, () => foundPendingState && foundDoneState);
|
||||
|
||||
// reset state trackers
|
||||
foundDoneState = foundPendingState = false;
|
||||
}
|
||||
|
||||
unsubscribe();
|
||||
});
|
||||
|
|
@ -0,0 +1,82 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
// Test that changing displays with different inverted state properly
|
||||
// refreshes the selected census.
|
||||
|
||||
const {
|
||||
censusDisplays,
|
||||
snapshotState: states,
|
||||
censusState,
|
||||
viewState
|
||||
} = require("devtools/client/memory/constants");
|
||||
const {
|
||||
setCensusDisplayAndRefresh
|
||||
} = require("devtools/client/memory/actions/census-display");
|
||||
const {
|
||||
takeSnapshotAndCensus,
|
||||
selectSnapshotAndRefresh,
|
||||
} = require("devtools/client/memory/actions/snapshot");
|
||||
const { changeView } = require("devtools/client/memory/actions/view");
|
||||
|
||||
function run_test() {
|
||||
run_next_test();
|
||||
}
|
||||
|
||||
add_task(function* () {
|
||||
let front = new StubbedMemoryFront();
|
||||
let heapWorker = new HeapAnalysesClient();
|
||||
yield front.attach();
|
||||
let store = Store();
|
||||
let { getState, dispatch } = store;
|
||||
|
||||
dispatch(changeView(viewState.CENSUS));
|
||||
|
||||
// Select a non-inverted display.
|
||||
dispatch(setCensusDisplayAndRefresh(heapWorker, censusDisplays.allocationStack));
|
||||
equal(getState().censusDisplay.inverted, false, "not inverted by default");
|
||||
|
||||
dispatch(takeSnapshotAndCensus(front, heapWorker));
|
||||
dispatch(takeSnapshotAndCensus(front, heapWorker));
|
||||
dispatch(takeSnapshotAndCensus(front, heapWorker));
|
||||
|
||||
yield waitUntilCensusState(store, s => s.census, [censusState.SAVED,
|
||||
censusState.SAVED,
|
||||
censusState.SAVED]);
|
||||
ok(true, "saved 3 snapshots and took a census of each of them");
|
||||
|
||||
// Select an inverted display.
|
||||
dispatch(setCensusDisplayAndRefresh(heapWorker, censusDisplays.invertedAllocationStack));
|
||||
|
||||
yield waitUntilCensusState(store, s => s.census, [censusState.SAVED,
|
||||
censusState.SAVED,
|
||||
censusState.SAVING]);
|
||||
ok(true, "toggling inverted should recompute the selected snapshot's census");
|
||||
|
||||
equal(getState().censusDisplay.inverted, true, "now inverted");
|
||||
|
||||
yield waitUntilCensusState(store, s => s.census, [censusState.SAVED,
|
||||
censusState.SAVED,
|
||||
censusState.SAVED]);
|
||||
|
||||
equal(getState().snapshots[0].census.display.inverted, false);
|
||||
equal(getState().snapshots[1].census.display.inverted, false);
|
||||
equal(getState().snapshots[2].census.display.inverted, true);
|
||||
|
||||
dispatch(selectSnapshotAndRefresh(heapWorker, getState().snapshots[1].id));
|
||||
yield waitUntilCensusState(store, s => s.census, [censusState.SAVED,
|
||||
censusState.SAVING,
|
||||
censusState.SAVED]);
|
||||
ok(true, "selecting non-inverted census should trigger a recompute");
|
||||
|
||||
yield waitUntilCensusState(store, s => s.census, [censusState.SAVED,
|
||||
censusState.SAVED,
|
||||
censusState.SAVED]);
|
||||
|
||||
equal(getState().snapshots[0].census.display.inverted, false);
|
||||
equal(getState().snapshots[1].census.display.inverted, true);
|
||||
equal(getState().snapshots[2].census.display.inverted, true);
|
||||
|
||||
heapWorker.destroy();
|
||||
yield front.detach();
|
||||
});
|
||||
|
|
@ -0,0 +1,58 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
"use strict";
|
||||
|
||||
// Test that changing inverted state in the middle of taking a snapshot results
|
||||
// in an inverted census.
|
||||
|
||||
const { censusDisplays, snapshotState: states, censusState, viewState } = require("devtools/client/memory/constants");
|
||||
const { takeSnapshotAndCensus } = require("devtools/client/memory/actions/snapshot");
|
||||
const {
|
||||
setCensusDisplay,
|
||||
setCensusDisplayAndRefresh,
|
||||
} = require("devtools/client/memory/actions/census-display");
|
||||
const { changeView } = require("devtools/client/memory/actions/view");
|
||||
|
||||
function run_test() {
|
||||
run_next_test();
|
||||
}
|
||||
|
||||
add_task(function* () {
|
||||
let front = new StubbedMemoryFront();
|
||||
let heapWorker = new HeapAnalysesClient();
|
||||
yield front.attach();
|
||||
let store = Store();
|
||||
let { getState, dispatch } = store;
|
||||
|
||||
dispatch(changeView(viewState.CENSUS));
|
||||
|
||||
dispatch(setCensusDisplay(censusDisplays.allocationStack));
|
||||
equal(getState().censusDisplay.inverted, false,
|
||||
"Should not have an inverted census display");
|
||||
|
||||
dispatch(takeSnapshotAndCensus(front, heapWorker));
|
||||
yield waitUntilSnapshotState(store, [states.SAVING]);
|
||||
|
||||
dispatch(setCensusDisplayAndRefresh(heapWorker, censusDisplays.invertedAllocationStack));
|
||||
|
||||
yield waitUntilCensusState(store, s => s.census, [censusState.SAVED]);
|
||||
|
||||
ok(getState().censusDisplay.inverted,
|
||||
"should want inverted trees");
|
||||
ok(getState().snapshots[0].census.display.inverted,
|
||||
"snapshot-we-were-in-the-middle-of-saving's census should be inverted");
|
||||
|
||||
dispatch(setCensusDisplayAndRefresh(heapWorker, censusDisplays.allocationStack));
|
||||
yield waitUntilCensusState(store, s => s.census, [censusState.SAVING]);
|
||||
ok(true, "toggling inverted retriggers census");
|
||||
ok(!getState().censusDisplay.inverted, "no longer inverted");
|
||||
|
||||
dispatch(setCensusDisplayAndRefresh(heapWorker, censusDisplays.invertedAllocationStack));
|
||||
yield waitUntilCensusState(store, s => s.census, [censusState.SAVED]);
|
||||
ok(getState().censusDisplay.inverted, "inverted again");
|
||||
ok(getState().snapshots[0].census.display.inverted,
|
||||
"census-we-were-in-the-middle-of-recomputing should be inverted again");
|
||||
|
||||
heapWorker.destroy();
|
||||
yield front.detach();
|
||||
});
|
||||
|
|
@ -0,0 +1,28 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
"use strict";
|
||||
|
||||
// Test toggling the top level inversion state of the tree.
|
||||
|
||||
const { censusDisplays } = require("devtools/client/memory/constants");
|
||||
const { setCensusDisplay } = require("devtools/client/memory/actions/census-display");
|
||||
|
||||
function run_test() {
|
||||
run_next_test();
|
||||
}
|
||||
|
||||
add_task(function* () {
|
||||
let store = Store();
|
||||
const { getState, dispatch } = store;
|
||||
|
||||
dispatch(setCensusDisplay(censusDisplays.allocationStack));
|
||||
equal(getState().censusDisplay.inverted, false,
|
||||
"not inverted initially");
|
||||
|
||||
dispatch(setCensusDisplay(censusDisplays.invertedAllocationStack));
|
||||
equal(getState().censusDisplay.inverted, true, "now inverted after toggling");
|
||||
|
||||
dispatch(setCensusDisplay(censusDisplays.allocationStack));
|
||||
equal(getState().censusDisplay.inverted, false,
|
||||
"not inverted again after toggling again");
|
||||
});
|
||||
|
|
@ -0,0 +1,42 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
"use strict";
|
||||
|
||||
/**
|
||||
* Test toggling the recording of allocation stacks.
|
||||
*/
|
||||
|
||||
let { toggleRecordingAllocationStacks } = require("devtools/client/memory/actions/allocations");
|
||||
|
||||
function run_test() {
|
||||
run_next_test();
|
||||
}
|
||||
|
||||
add_task(function* () {
|
||||
let front = new StubbedMemoryFront();
|
||||
yield front.attach();
|
||||
let store = Store();
|
||||
const { getState, dispatch } = store;
|
||||
|
||||
equal(getState().allocations.recording, false, "not recording by default");
|
||||
equal(getState().allocations.togglingInProgress, false,
|
||||
"not in the process of toggling by default");
|
||||
|
||||
dispatch(toggleRecordingAllocationStacks(front));
|
||||
yield waitUntilState(store, () => getState().allocations.togglingInProgress);
|
||||
ok(true, "`togglingInProgress` set to true when toggling on");
|
||||
yield waitUntilState(store, () => !getState().allocations.togglingInProgress);
|
||||
|
||||
equal(getState().allocations.recording, true, "now we are recording");
|
||||
ok(front.recordingAllocations, "front is recording too");
|
||||
|
||||
dispatch(toggleRecordingAllocationStacks(front));
|
||||
yield waitUntilState(store, () => getState().allocations.togglingInProgress);
|
||||
ok(true, "`togglingInProgress` set to true when toggling off");
|
||||
yield waitUntilState(store, () => !getState().allocations.togglingInProgress);
|
||||
|
||||
equal(getState().allocations.recording, false, "now we are not recording");
|
||||
ok(front.recordingAllocations, "front is not recording anymore");
|
||||
|
||||
yield front.detach();
|
||||
});
|
||||
29
devtools/client/memory/test/unit/test_action_diffing_01.js
Normal file
29
devtools/client/memory/test/unit/test_action_diffing_01.js
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
// Test toggling of diffing.
|
||||
|
||||
const { toggleDiffing } = require("devtools/client/memory/actions/diffing");
|
||||
|
||||
function run_test() {
|
||||
run_next_test();
|
||||
}
|
||||
|
||||
add_task(function* () {
|
||||
let front = new StubbedMemoryFront();
|
||||
let heapWorker = new HeapAnalysesClient();
|
||||
yield front.attach();
|
||||
let store = Store();
|
||||
const { getState, dispatch } = store;
|
||||
|
||||
equal(getState().diffing, null, "not diffing by default");
|
||||
|
||||
dispatch(toggleDiffing());
|
||||
ok(getState().diffing, "now diffing after toggling");
|
||||
|
||||
dispatch(toggleDiffing());
|
||||
equal(getState().diffing, null, "not diffing again after toggling again");
|
||||
|
||||
heapWorker.destroy();
|
||||
yield front.detach();
|
||||
});
|
||||
46
devtools/client/memory/test/unit/test_action_diffing_02.js
Normal file
46
devtools/client/memory/test/unit/test_action_diffing_02.js
Normal file
|
|
@ -0,0 +1,46 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
// Test that toggling diffing unselects all snapshots.
|
||||
|
||||
const { snapshotState, censusState, viewState } = require("devtools/client/memory/constants");
|
||||
const { toggleDiffing } = require("devtools/client/memory/actions/diffing");
|
||||
const { takeSnapshotAndCensus } = require("devtools/client/memory/actions/snapshot");
|
||||
const { changeView } = require("devtools/client/memory/actions/view");
|
||||
|
||||
function run_test() {
|
||||
run_next_test();
|
||||
}
|
||||
|
||||
add_task(function* () {
|
||||
let front = new StubbedMemoryFront();
|
||||
let heapWorker = new HeapAnalysesClient();
|
||||
yield front.attach();
|
||||
let store = Store();
|
||||
const { getState, dispatch } = store;
|
||||
|
||||
dispatch(changeView(viewState.CENSUS));
|
||||
|
||||
equal(getState().diffing, null, "not diffing by default");
|
||||
|
||||
dispatch(takeSnapshotAndCensus(front, heapWorker));
|
||||
dispatch(takeSnapshotAndCensus(front, heapWorker));
|
||||
dispatch(takeSnapshotAndCensus(front, heapWorker));
|
||||
yield waitUntilCensusState(store, s => s.census, [censusState.SAVED,
|
||||
censusState.SAVED,
|
||||
censusState.SAVED]);
|
||||
|
||||
ok(getState().snapshots.some(s => s.selected),
|
||||
"One of the new snapshots is selected");
|
||||
|
||||
dispatch(toggleDiffing());
|
||||
ok(getState().diffing, "now diffing after toggling");
|
||||
|
||||
for (let s of getState().snapshots) {
|
||||
ok(!s.selected,
|
||||
"No snapshot should be selected after entering diffing mode");
|
||||
}
|
||||
|
||||
heapWorker.destroy();
|
||||
yield front.detach();
|
||||
});
|
||||
104
devtools/client/memory/test/unit/test_action_diffing_03.js
Normal file
104
devtools/client/memory/test/unit/test_action_diffing_03.js
Normal file
|
|
@ -0,0 +1,104 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
// Test selecting snapshots for diffing.
|
||||
|
||||
const { diffingState, snapshotState, viewState } = require("devtools/client/memory/constants");
|
||||
const {
|
||||
toggleDiffing,
|
||||
selectSnapshotForDiffing
|
||||
} = require("devtools/client/memory/actions/diffing");
|
||||
const { takeSnapshot } = require("devtools/client/memory/actions/snapshot");
|
||||
const { changeView } = require("devtools/client/memory/actions/view");
|
||||
|
||||
function run_test() {
|
||||
run_next_test();
|
||||
}
|
||||
|
||||
// We test that you (1) cannot select a snapshot that is not in a diffable
|
||||
// state, and (2) cannot select more than 2 snapshots for diffing. Both attempts
|
||||
// trigger assertion failures.
|
||||
EXPECTED_DTU_ASSERT_FAILURE_COUNT = 2;
|
||||
|
||||
add_task(function* () {
|
||||
let front = new StubbedMemoryFront();
|
||||
let heapWorker = new HeapAnalysesClient();
|
||||
yield front.attach();
|
||||
let store = Store();
|
||||
const { getState, dispatch } = store;
|
||||
|
||||
dispatch(changeView(viewState.CENSUS));
|
||||
equal(getState().diffing, null, "not diffing by default");
|
||||
|
||||
dispatch(takeSnapshot(front, heapWorker));
|
||||
dispatch(takeSnapshot(front, heapWorker));
|
||||
dispatch(takeSnapshot(front, heapWorker));
|
||||
|
||||
yield waitUntilSnapshotState(store, [snapshotState.SAVED,
|
||||
snapshotState.SAVED,
|
||||
snapshotState.SAVED]);
|
||||
dispatch(takeSnapshot(front));
|
||||
|
||||
// Start diffing.
|
||||
dispatch(toggleDiffing());
|
||||
ok(getState().diffing, "now diffing after toggling");
|
||||
equal(getState().diffing.firstSnapshotId, null,
|
||||
"no first snapshot selected");
|
||||
equal(getState().diffing.secondSnapshotId, null,
|
||||
"no second snapshot selected");
|
||||
equal(getState().diffing.state, diffingState.SELECTING,
|
||||
"should be in diffing state SELECTING");
|
||||
|
||||
// Can't select a snapshot that is not in a diffable state.
|
||||
equal(getState().snapshots[3].state, snapshotState.SAVING,
|
||||
"the last snapshot is still in the process of being saved");
|
||||
dumpn("Expecting exception:");
|
||||
let threw = false;
|
||||
try {
|
||||
dispatch(selectSnapshotForDiffing(getState().snapshots[3]));
|
||||
} catch (error) {
|
||||
threw = true;
|
||||
}
|
||||
ok(threw, "Should not be able to select snapshots that aren't ready for diffing");
|
||||
|
||||
// Select first snapshot for diffing.
|
||||
dispatch(selectSnapshotForDiffing(getState().snapshots[0]));
|
||||
ok(getState().diffing, "now diffing after toggling");
|
||||
equal(getState().diffing.firstSnapshotId, getState().snapshots[0].id,
|
||||
"first snapshot selected");
|
||||
equal(getState().diffing.secondSnapshotId, null,
|
||||
"no second snapshot selected");
|
||||
equal(getState().diffing.state, diffingState.SELECTING,
|
||||
"should still be in diffing state SELECTING");
|
||||
|
||||
// Can't diff first snapshot with itself; this is a noop.
|
||||
dispatch(selectSnapshotForDiffing(getState().snapshots[0]));
|
||||
ok(getState().diffing, "still diffing");
|
||||
equal(getState().diffing.firstSnapshotId, getState().snapshots[0].id,
|
||||
"first snapshot still selected");
|
||||
equal(getState().diffing.secondSnapshotId, null,
|
||||
"still no second snapshot selected");
|
||||
equal(getState().diffing.state, diffingState.SELECTING,
|
||||
"should still be in diffing state SELECTING");
|
||||
|
||||
// Select second snapshot for diffing.
|
||||
dispatch(selectSnapshotForDiffing(getState().snapshots[1]));
|
||||
ok(getState().diffing, "still diffing");
|
||||
equal(getState().diffing.firstSnapshotId, getState().snapshots[0].id,
|
||||
"first snapshot still selected");
|
||||
equal(getState().diffing.secondSnapshotId, getState().snapshots[1].id,
|
||||
"second snapshot selected");
|
||||
|
||||
// Can't select more than two snapshots for diffing.
|
||||
dumpn("Expecting exception:");
|
||||
threw = false;
|
||||
try {
|
||||
dispatch(selectSnapshotForDiffing(getState().snapshots[2]));
|
||||
} catch (error) {
|
||||
threw = true;
|
||||
}
|
||||
ok(threw, "Can't select more than two snapshots for diffing");
|
||||
|
||||
heapWorker.destroy();
|
||||
yield front.detach();
|
||||
});
|
||||
78
devtools/client/memory/test/unit/test_action_diffing_04.js
Normal file
78
devtools/client/memory/test/unit/test_action_diffing_04.js
Normal file
|
|
@ -0,0 +1,78 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
// Test that we compute census diffs.
|
||||
|
||||
const {
|
||||
diffingState,
|
||||
snapshotState,
|
||||
viewState
|
||||
} = require("devtools/client/memory/constants");
|
||||
const {
|
||||
toggleDiffing,
|
||||
selectSnapshotForDiffingAndRefresh
|
||||
} = require("devtools/client/memory/actions/diffing");
|
||||
const {
|
||||
takeSnapshot,
|
||||
readSnapshot
|
||||
} = require("devtools/client/memory/actions/snapshot");
|
||||
const { changeView } = require("devtools/client/memory/actions/view");
|
||||
|
||||
function run_test() {
|
||||
run_next_test();
|
||||
}
|
||||
|
||||
add_task(function* () {
|
||||
let front = new StubbedMemoryFront();
|
||||
let heapWorker = new HeapAnalysesClient();
|
||||
yield front.attach();
|
||||
let store = Store();
|
||||
const { getState, dispatch } = store;
|
||||
dispatch(changeView(viewState.CENSUS));
|
||||
|
||||
equal(getState().diffing, null, "not diffing by default");
|
||||
|
||||
const s1 = yield dispatch(takeSnapshot(front, heapWorker));
|
||||
const s2 = yield dispatch(takeSnapshot(front, heapWorker));
|
||||
const s3 = yield dispatch(takeSnapshot(front, heapWorker));
|
||||
dispatch(readSnapshot(heapWorker, s1));
|
||||
dispatch(readSnapshot(heapWorker, s2));
|
||||
dispatch(readSnapshot(heapWorker, s3));
|
||||
yield waitUntilSnapshotState(store, [snapshotState.READ,
|
||||
snapshotState.READ,
|
||||
snapshotState.READ]);
|
||||
|
||||
dispatch(toggleDiffing());
|
||||
dispatch(selectSnapshotForDiffingAndRefresh(heapWorker,
|
||||
getState().snapshots[0]));
|
||||
dispatch(selectSnapshotForDiffingAndRefresh(heapWorker,
|
||||
getState().snapshots[1]));
|
||||
|
||||
ok(getState().diffing, "We should be diffing.");
|
||||
equal(getState().diffing.firstSnapshotId, getState().snapshots[0].id,
|
||||
"First snapshot selected.");
|
||||
equal(getState().diffing.secondSnapshotId, getState().snapshots[1].id,
|
||||
"Second snapshot selected.");
|
||||
|
||||
yield waitUntilState(store,
|
||||
state =>
|
||||
state.diffing.state === diffingState.TAKING_DIFF);
|
||||
ok(true,
|
||||
"Selecting two snapshots for diffing should trigger computing a diff.");
|
||||
|
||||
yield waitUntilState(store,
|
||||
state => state.diffing.state === diffingState.TOOK_DIFF);
|
||||
ok(true, "And then the diff should complete.");
|
||||
ok(getState().diffing.census, "And we should have a census.");
|
||||
ok(getState().diffing.census.report, "And that census should have a report.");
|
||||
equal(getState().diffing.census.display, getState().censusDisplay,
|
||||
"And that census should have the correct display");
|
||||
equal(getState().diffing.census.filter, getState().filter,
|
||||
"And that census should have the correct filter");
|
||||
equal(getState().diffing.census.display.inverted,
|
||||
getState().censusDisplay.inverted,
|
||||
"And that census should have the correct inversion");
|
||||
|
||||
heapWorker.destroy();
|
||||
yield front.detach();
|
||||
});
|
||||
112
devtools/client/memory/test/unit/test_action_diffing_05.js
Normal file
112
devtools/client/memory/test/unit/test_action_diffing_05.js
Normal file
|
|
@ -0,0 +1,112 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
// Test that we recompute census diffs at the appropriate times.
|
||||
|
||||
const {
|
||||
diffingState,
|
||||
snapshotState,
|
||||
censusDisplays,
|
||||
viewState,
|
||||
} = require("devtools/client/memory/constants");
|
||||
const {
|
||||
setCensusDisplayAndRefresh,
|
||||
} = require("devtools/client/memory/actions/census-display");
|
||||
const {
|
||||
toggleDiffing,
|
||||
selectSnapshotForDiffingAndRefresh,
|
||||
} = require("devtools/client/memory/actions/diffing");
|
||||
const {
|
||||
setFilterStringAndRefresh,
|
||||
} = require("devtools/client/memory/actions/filter");
|
||||
const {
|
||||
takeSnapshot,
|
||||
readSnapshot,
|
||||
} = require("devtools/client/memory/actions/snapshot");
|
||||
const { changeView } = require("devtools/client/memory/actions/view");
|
||||
|
||||
function run_test() {
|
||||
run_next_test();
|
||||
}
|
||||
|
||||
add_task(function* () {
|
||||
let front = new StubbedMemoryFront();
|
||||
let heapWorker = new HeapAnalysesClient();
|
||||
yield front.attach();
|
||||
let store = Store();
|
||||
const { getState, dispatch } = store;
|
||||
dispatch(changeView(viewState.CENSUS));
|
||||
|
||||
yield dispatch(setCensusDisplayAndRefresh(heapWorker,
|
||||
censusDisplays.allocationStack));
|
||||
equal(getState().censusDisplay.inverted, false,
|
||||
"not inverted at start");
|
||||
|
||||
equal(getState().diffing, null, "not diffing by default");
|
||||
|
||||
const s1 = yield dispatch(takeSnapshot(front, heapWorker));
|
||||
const s2 = yield dispatch(takeSnapshot(front, heapWorker));
|
||||
const s3 = yield dispatch(takeSnapshot(front, heapWorker));
|
||||
dispatch(readSnapshot(heapWorker, s1));
|
||||
dispatch(readSnapshot(heapWorker, s2));
|
||||
dispatch(readSnapshot(heapWorker, s3));
|
||||
yield waitUntilSnapshotState(store, [snapshotState.READ,
|
||||
snapshotState.READ,
|
||||
snapshotState.READ]);
|
||||
|
||||
yield dispatch(toggleDiffing());
|
||||
dispatch(selectSnapshotForDiffingAndRefresh(heapWorker,
|
||||
getState().snapshots[0]));
|
||||
dispatch(selectSnapshotForDiffingAndRefresh(heapWorker,
|
||||
getState().snapshots[1]));
|
||||
yield waitUntilState(store,
|
||||
state => state.diffing.state === diffingState.TOOK_DIFF);
|
||||
|
||||
const shouldTriggerRecompute = [
|
||||
{
|
||||
name: "toggling inversion",
|
||||
func: () => dispatch(setCensusDisplayAndRefresh(
|
||||
heapWorker,
|
||||
censusDisplays.invertedAllocationStack))
|
||||
},
|
||||
{
|
||||
name: "filtering",
|
||||
func: () => dispatch(setFilterStringAndRefresh("scr", heapWorker))
|
||||
},
|
||||
{
|
||||
name: "changing displays",
|
||||
func: () =>
|
||||
dispatch(setCensusDisplayAndRefresh(heapWorker,
|
||||
censusDisplays.coarseType))
|
||||
}
|
||||
];
|
||||
|
||||
for (let { name, func } of shouldTriggerRecompute) {
|
||||
dumpn(`Testing that "${name}" triggers a diff recompute`);
|
||||
func();
|
||||
|
||||
yield waitUntilState(store,
|
||||
state =>
|
||||
state.diffing.state === diffingState.TAKING_DIFF);
|
||||
ok(true, "triggered diff recompute.");
|
||||
|
||||
yield waitUntilState(store,
|
||||
state =>
|
||||
state.diffing.state === diffingState.TOOK_DIFF);
|
||||
ok(true, "And then the diff should complete.");
|
||||
ok(getState().diffing.census, "And we should have a census.");
|
||||
ok(getState().diffing.census.report,
|
||||
"And that census should have a report.");
|
||||
equal(getState().diffing.census.display,
|
||||
getState().censusDisplay,
|
||||
"And that census should have the correct display");
|
||||
equal(getState().diffing.census.filter, getState().filter,
|
||||
"And that census should have the correct filter");
|
||||
equal(getState().diffing.census.display.inverted,
|
||||
getState().censusDisplay.inverted,
|
||||
"And that census should have the correct inversion");
|
||||
}
|
||||
|
||||
heapWorker.destroy();
|
||||
yield front.detach();
|
||||
});
|
||||
61
devtools/client/memory/test/unit/test_dominator_trees_01.js
Normal file
61
devtools/client/memory/test/unit/test_dominator_trees_01.js
Normal file
|
|
@ -0,0 +1,61 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
// Test that we can compute and fetch the dominator tree for a snapshot.
|
||||
|
||||
let {
|
||||
snapshotState: states,
|
||||
dominatorTreeState,
|
||||
treeMapState,
|
||||
} = require("devtools/client/memory/constants");
|
||||
let {
|
||||
takeSnapshotAndCensus,
|
||||
computeAndFetchDominatorTree,
|
||||
} = require("devtools/client/memory/actions/snapshot");
|
||||
|
||||
function run_test() {
|
||||
run_next_test();
|
||||
}
|
||||
|
||||
add_task(function* () {
|
||||
let front = new StubbedMemoryFront();
|
||||
let heapWorker = new HeapAnalysesClient();
|
||||
yield front.attach();
|
||||
let store = Store();
|
||||
let { getState, dispatch } = store;
|
||||
|
||||
dispatch(takeSnapshotAndCensus(front, heapWorker));
|
||||
yield waitUntilCensusState(store, s => s.treeMap, [treeMapState.SAVED]);
|
||||
ok(!getState().snapshots[0].dominatorTree,
|
||||
"There shouldn't be a dominator tree model yet since it is not computed " +
|
||||
"until we switch to the dominators view.");
|
||||
|
||||
// Change to the dominator tree view.
|
||||
dispatch(computeAndFetchDominatorTree(heapWorker, getState().snapshots[0].id));
|
||||
ok(getState().snapshots[0].dominatorTree,
|
||||
"Should now have a dominator tree model for the selected snapshot");
|
||||
|
||||
// Wait for the dominator tree to start being computed.
|
||||
yield waitUntilState(store, state =>
|
||||
state.snapshots[0].dominatorTree.state === dominatorTreeState.COMPUTING);
|
||||
ok(true, "The dominator tree started computing");
|
||||
ok(!getState().snapshots[0].dominatorTree.root,
|
||||
"When the dominator tree is computing, we should not have its root");
|
||||
|
||||
// Wait for the dominator tree to finish computing and start being fetched.
|
||||
yield waitUntilState(store, state =>
|
||||
state.snapshots[0].dominatorTree.state === dominatorTreeState.FETCHING);
|
||||
ok(true, "The dominator tree started fetching");
|
||||
ok(!getState().snapshots[0].dominatorTree.root,
|
||||
"When the dominator tree is fetching, we should not have its root");
|
||||
|
||||
// Wait for the dominator tree to finish being fetched.
|
||||
yield waitUntilState(store, state =>
|
||||
state.snapshots[0].dominatorTree.state === dominatorTreeState.LOADED);
|
||||
ok(true, "The dominator tree was fetched");
|
||||
ok(getState().snapshots[0].dominatorTree.root,
|
||||
"When the dominator tree is loaded, we should have its root");
|
||||
|
||||
heapWorker.destroy();
|
||||
yield front.detach();
|
||||
});
|
||||
64
devtools/client/memory/test/unit/test_dominator_trees_02.js
Normal file
64
devtools/client/memory/test/unit/test_dominator_trees_02.js
Normal file
|
|
@ -0,0 +1,64 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
// Test that selecting the dominator tree view automatically kicks off fetching
|
||||
// and computing dominator trees.
|
||||
|
||||
const {
|
||||
snapshotState: states,
|
||||
dominatorTreeState,
|
||||
viewState,
|
||||
treeMapState,
|
||||
} = require("devtools/client/memory/constants");
|
||||
const {
|
||||
takeSnapshotAndCensus,
|
||||
} = require("devtools/client/memory/actions/snapshot");
|
||||
const {
|
||||
changeViewAndRefresh
|
||||
} = require("devtools/client/memory/actions/view");
|
||||
|
||||
function run_test() {
|
||||
run_next_test();
|
||||
}
|
||||
|
||||
add_task(function* () {
|
||||
let front = new StubbedMemoryFront();
|
||||
let heapWorker = new HeapAnalysesClient();
|
||||
yield front.attach();
|
||||
let store = Store();
|
||||
let { getState, dispatch } = store;
|
||||
|
||||
dispatch(takeSnapshotAndCensus(front, heapWorker));
|
||||
yield waitUntilCensusState(store, s => s.treeMap, [treeMapState.SAVED]);
|
||||
ok(!getState().snapshots[0].dominatorTree,
|
||||
"There shouldn't be a dominator tree model yet since it is not computed " +
|
||||
"until we switch to the dominators view.");
|
||||
|
||||
dispatch(changeViewAndRefresh(viewState.DOMINATOR_TREE, heapWorker));
|
||||
ok(getState().snapshots[0].dominatorTree,
|
||||
"Should now have a dominator tree model for the selected snapshot");
|
||||
|
||||
// Wait for the dominator tree to start being computed.
|
||||
yield waitUntilState(store, state =>
|
||||
state.snapshots[0].dominatorTree.state === dominatorTreeState.COMPUTING);
|
||||
ok(true, "The dominator tree started computing");
|
||||
ok(!getState().snapshots[0].dominatorTree.root,
|
||||
"When the dominator tree is computing, we should not have its root");
|
||||
|
||||
// Wait for the dominator tree to finish computing and start being fetched.
|
||||
yield waitUntilState(store, state =>
|
||||
state.snapshots[0].dominatorTree.state === dominatorTreeState.FETCHING);
|
||||
ok(true, "The dominator tree started fetching");
|
||||
ok(!getState().snapshots[0].dominatorTree.root,
|
||||
"When the dominator tree is fetching, we should not have its root");
|
||||
|
||||
// Wait for the dominator tree to finish being fetched.
|
||||
yield waitUntilState(store, state =>
|
||||
state.snapshots[0].dominatorTree.state === dominatorTreeState.LOADED);
|
||||
ok(true, "The dominator tree was fetched");
|
||||
ok(getState().snapshots[0].dominatorTree.root,
|
||||
"When the dominator tree is loaded, we should have its root");
|
||||
|
||||
heapWorker.destroy();
|
||||
yield front.detach();
|
||||
});
|
||||
61
devtools/client/memory/test/unit/test_dominator_trees_03.js
Normal file
61
devtools/client/memory/test/unit/test_dominator_trees_03.js
Normal file
|
|
@ -0,0 +1,61 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
// Test that selecting the dominator tree view and then taking a snapshot
|
||||
// properly kicks off fetching and computing dominator trees.
|
||||
|
||||
const {
|
||||
snapshotState: states,
|
||||
dominatorTreeState,
|
||||
viewState,
|
||||
} = require("devtools/client/memory/constants");
|
||||
const {
|
||||
takeSnapshotAndCensus,
|
||||
} = require("devtools/client/memory/actions/snapshot");
|
||||
const {
|
||||
changeView
|
||||
} = require("devtools/client/memory/actions/view");
|
||||
|
||||
function run_test() {
|
||||
run_next_test();
|
||||
}
|
||||
|
||||
add_task(function* () {
|
||||
let front = new StubbedMemoryFront();
|
||||
let heapWorker = new HeapAnalysesClient();
|
||||
yield front.attach();
|
||||
let store = Store();
|
||||
let { getState, dispatch } = store;
|
||||
|
||||
dispatch(changeView(viewState.DOMINATOR_TREE));
|
||||
equal(getState().view.state, viewState.DOMINATOR_TREE,
|
||||
"We should now be in the DOMINATOR_TREE view");
|
||||
|
||||
dispatch(takeSnapshotAndCensus(front, heapWorker));
|
||||
|
||||
// Wait for the dominator tree to start being computed.
|
||||
yield waitUntilState(store, state =>
|
||||
state.snapshots[0] && state.snapshots[0].dominatorTree);
|
||||
equal(getState().snapshots[0].dominatorTree.state,
|
||||
dominatorTreeState.COMPUTING,
|
||||
"The dominator tree started computing");
|
||||
ok(!getState().snapshots[0].dominatorTree.root,
|
||||
"When the dominator tree is computing, we should not have its root");
|
||||
|
||||
// Wait for the dominator tree to finish computing and start being fetched.
|
||||
yield waitUntilState(store, state =>
|
||||
state.snapshots[0].dominatorTree.state === dominatorTreeState.FETCHING);
|
||||
ok(true, "The dominator tree started fetching");
|
||||
ok(!getState().snapshots[0].dominatorTree.root,
|
||||
"When the dominator tree is fetching, we should not have its root");
|
||||
|
||||
// Wait for the dominator tree to finish being fetched.
|
||||
yield waitUntilState(store, state =>
|
||||
state.snapshots[0].dominatorTree.state === dominatorTreeState.LOADED);
|
||||
ok(true, "The dominator tree was fetched");
|
||||
ok(getState().snapshots[0].dominatorTree.root,
|
||||
"When the dominator tree is loaded, we should have its root");
|
||||
|
||||
heapWorker.destroy();
|
||||
yield front.detach();
|
||||
});
|
||||
69
devtools/client/memory/test/unit/test_dominator_trees_04.js
Normal file
69
devtools/client/memory/test/unit/test_dominator_trees_04.js
Normal file
|
|
@ -0,0 +1,69 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
// Test that selecting the dominator tree view while in the middle of taking a
|
||||
// snapshot properly kicks off fetching and computing dominator trees.
|
||||
|
||||
const {
|
||||
snapshotState: states,
|
||||
dominatorTreeState,
|
||||
viewState,
|
||||
} = require("devtools/client/memory/constants");
|
||||
const {
|
||||
takeSnapshotAndCensus,
|
||||
} = require("devtools/client/memory/actions/snapshot");
|
||||
const {
|
||||
changeView
|
||||
} = require("devtools/client/memory/actions/view");
|
||||
|
||||
function run_test() {
|
||||
run_next_test();
|
||||
}
|
||||
|
||||
add_task(function* () {
|
||||
let front = new StubbedMemoryFront();
|
||||
let heapWorker = new HeapAnalysesClient();
|
||||
yield front.attach();
|
||||
|
||||
for (let intermediateSnapshotState of [states.SAVING,
|
||||
states.READING,
|
||||
states.READ]) {
|
||||
dumpn(`Testing switching to the DOMINATOR_TREE view in the middle of the ${intermediateSnapshotState} snapshot state`);
|
||||
|
||||
let store = Store();
|
||||
let { getState, dispatch } = store;
|
||||
|
||||
dispatch(takeSnapshotAndCensus(front, heapWorker));
|
||||
yield waitUntilSnapshotState(store, [intermediateSnapshotState]);
|
||||
|
||||
dispatch(changeView(viewState.DOMINATOR_TREE));
|
||||
equal(getState().view.state, viewState.DOMINATOR_TREE,
|
||||
"We should now be in the DOMINATOR_TREE view");
|
||||
|
||||
// Wait for the dominator tree to start being computed.
|
||||
yield waitUntilState(store, state =>
|
||||
state.snapshots[0] && state.snapshots[0].dominatorTree);
|
||||
equal(getState().snapshots[0].dominatorTree.state,
|
||||
dominatorTreeState.COMPUTING,
|
||||
"The dominator tree started computing");
|
||||
ok(!getState().snapshots[0].dominatorTree.root,
|
||||
"When the dominator tree is computing, we should not have its root");
|
||||
|
||||
// Wait for the dominator tree to finish computing and start being fetched.
|
||||
yield waitUntilState(store, state =>
|
||||
state.snapshots[0].dominatorTree.state === dominatorTreeState.FETCHING);
|
||||
ok(true, "The dominator tree started fetching");
|
||||
ok(!getState().snapshots[0].dominatorTree.root,
|
||||
"When the dominator tree is fetching, we should not have its root");
|
||||
|
||||
// Wait for the dominator tree to finish being fetched.
|
||||
yield waitUntilState(store, state =>
|
||||
state.snapshots[0].dominatorTree.state === dominatorTreeState.LOADED);
|
||||
ok(true, "The dominator tree was fetched");
|
||||
ok(getState().snapshots[0].dominatorTree.root,
|
||||
"When the dominator tree is loaded, we should have its root");
|
||||
}
|
||||
|
||||
heapWorker.destroy();
|
||||
yield front.detach();
|
||||
});
|
||||
59
devtools/client/memory/test/unit/test_dominator_trees_05.js
Normal file
59
devtools/client/memory/test/unit/test_dominator_trees_05.js
Normal file
|
|
@ -0,0 +1,59 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
// Test that changing the currently selected snapshot to a snapshot that does
|
||||
// not have a dominator tree will automatically compute and fetch one for it.
|
||||
|
||||
let {
|
||||
snapshotState: states,
|
||||
dominatorTreeState,
|
||||
viewState,
|
||||
treeMapState,
|
||||
} = require("devtools/client/memory/constants");
|
||||
let {
|
||||
takeSnapshotAndCensus,
|
||||
selectSnapshotAndRefresh,
|
||||
} = require("devtools/client/memory/actions/snapshot");
|
||||
|
||||
let { changeView } = require("devtools/client/memory/actions/view");
|
||||
|
||||
function run_test() {
|
||||
run_next_test();
|
||||
}
|
||||
|
||||
add_task(function* () {
|
||||
let front = new StubbedMemoryFront();
|
||||
let heapWorker = new HeapAnalysesClient();
|
||||
yield front.attach();
|
||||
let store = Store();
|
||||
let { getState, dispatch } = store;
|
||||
|
||||
dispatch(takeSnapshotAndCensus(front, heapWorker));
|
||||
dispatch(takeSnapshotAndCensus(front, heapWorker));
|
||||
yield waitUntilCensusState(store, s => s.treeMap, [treeMapState.SAVED,
|
||||
treeMapState.SAVED]);
|
||||
|
||||
ok(getState().snapshots[1].selected, "The second snapshot is selected");
|
||||
|
||||
// Change to the dominator tree view.
|
||||
dispatch(changeView(viewState.DOMINATOR_TREE));
|
||||
|
||||
// Wait for the dominator tree to finish being fetched.
|
||||
yield waitUntilState(store, state =>
|
||||
state.snapshots[1].dominatorTree &&
|
||||
state.snapshots[1].dominatorTree.state === dominatorTreeState.LOADED);
|
||||
ok(true, "The second snapshot's dominator tree was fetched");
|
||||
|
||||
// Select the first snapshot.
|
||||
dispatch(selectSnapshotAndRefresh(heapWorker, getState().snapshots[0].id));
|
||||
|
||||
// And now the first snapshot should have its dominator tree fetched and
|
||||
// computed because of the new selection.
|
||||
yield waitUntilState(store, state =>
|
||||
state.snapshots[0].dominatorTree &&
|
||||
state.snapshots[0].dominatorTree.state === dominatorTreeState.LOADED);
|
||||
ok(true, "The first snapshot's dominator tree was fetched");
|
||||
|
||||
heapWorker.destroy();
|
||||
yield front.detach();
|
||||
});
|
||||
127
devtools/client/memory/test/unit/test_dominator_trees_06.js
Normal file
127
devtools/client/memory/test/unit/test_dominator_trees_06.js
Normal file
|
|
@ -0,0 +1,127 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
// Test that we can incrementally fetch a subtree of a dominator tree.
|
||||
|
||||
const {
|
||||
snapshotState: states,
|
||||
dominatorTreeState,
|
||||
viewState,
|
||||
} = require("devtools/client/memory/constants");
|
||||
const {
|
||||
takeSnapshotAndCensus,
|
||||
selectSnapshotAndRefresh,
|
||||
fetchImmediatelyDominated,
|
||||
} = require("devtools/client/memory/actions/snapshot");
|
||||
const DominatorTreeLazyChildren
|
||||
= require("devtools/client/memory/dominator-tree-lazy-children");
|
||||
|
||||
const { changeView } = require("devtools/client/memory/actions/view");
|
||||
|
||||
function run_test() {
|
||||
run_next_test();
|
||||
}
|
||||
|
||||
add_task(function* () {
|
||||
let front = new StubbedMemoryFront();
|
||||
let heapWorker = new HeapAnalysesClient();
|
||||
yield front.attach();
|
||||
let store = Store();
|
||||
let { getState, dispatch } = store;
|
||||
|
||||
dispatch(changeView(viewState.DOMINATOR_TREE));
|
||||
dispatch(takeSnapshotAndCensus(front, heapWorker));
|
||||
|
||||
// Wait for the dominator tree to finish being fetched.
|
||||
yield waitUntilState(store, state =>
|
||||
state.snapshots[0] &&
|
||||
state.snapshots[0].dominatorTree &&
|
||||
state.snapshots[0].dominatorTree.state === dominatorTreeState.LOADED);
|
||||
ok(getState().snapshots[0].dominatorTree.root,
|
||||
"The dominator tree was fetched");
|
||||
|
||||
// Find a node that has children, but none of them are loaded.
|
||||
|
||||
function findNode(node) {
|
||||
if (node.moreChildrenAvailable && !node.children) {
|
||||
return node;
|
||||
}
|
||||
|
||||
if (node.children) {
|
||||
for (let child of node.children) {
|
||||
const found = findNode(child);
|
||||
if (found) {
|
||||
return found;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
const oldRoot = getState().snapshots[0].dominatorTree.root;
|
||||
const oldNode = findNode(oldRoot);
|
||||
ok(oldNode,
|
||||
"Should have found a node with children that are not loaded since we " +
|
||||
"only send partial dominator trees across initially and load the rest " +
|
||||
"on demand");
|
||||
ok(oldNode !== oldRoot, "But the node should not be the root");
|
||||
|
||||
const lazyChildren = new DominatorTreeLazyChildren(oldNode.nodeId, 0);
|
||||
dispatch(fetchImmediatelyDominated(heapWorker, getState().snapshots[0].id, lazyChildren));
|
||||
|
||||
equal(getState().snapshots[0].dominatorTree.state,
|
||||
dominatorTreeState.INCREMENTAL_FETCHING,
|
||||
"Fetching immediately dominated children should put us in the " +
|
||||
"INCREMENTAL_FETCHING state");
|
||||
|
||||
yield waitUntilState(store, state =>
|
||||
state.snapshots[0].dominatorTree.state === dominatorTreeState.LOADED);
|
||||
ok(true,
|
||||
"The dominator tree should go back to LOADED after the incremental " +
|
||||
"fetching is done.");
|
||||
|
||||
const newRoot = getState().snapshots[0].dominatorTree.root;
|
||||
ok(oldRoot !== newRoot,
|
||||
"When we insert new nodes, we get a new tree");
|
||||
equal(oldRoot.children.length, newRoot.children.length,
|
||||
"The new tree's root should have the same number of children as the " +
|
||||
"old root's");
|
||||
|
||||
let differentChildrenCount = 0;
|
||||
for (let i = 0; i < oldRoot.children.length; i++) {
|
||||
if (oldRoot.children[i] !== newRoot.children[i]) {
|
||||
differentChildrenCount++;
|
||||
}
|
||||
}
|
||||
equal(differentChildrenCount, 1,
|
||||
"All subtrees except the subtree we inserted incrementally fetched " +
|
||||
"children into should be the same because we use persistent updates");
|
||||
|
||||
// Find the new node which has the children inserted.
|
||||
|
||||
function findNewNode(node) {
|
||||
if (node.nodeId === oldNode.nodeId) {
|
||||
return node;
|
||||
}
|
||||
|
||||
if (node.children) {
|
||||
for (let child of node.children) {
|
||||
const found = findNewNode(child);
|
||||
if (found) {
|
||||
return found;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
const newNode = findNewNode(newRoot);
|
||||
ok(newNode, "Should find the node in the new tree again");
|
||||
ok(newNode !== oldNode, "We did not mutate the old node in place, instead created a new node");
|
||||
ok(newNode.children, "And the new node should have the children attached");
|
||||
|
||||
heapWorker.destroy();
|
||||
yield front.detach();
|
||||
});
|
||||
146
devtools/client/memory/test/unit/test_dominator_trees_07.js
Normal file
146
devtools/client/memory/test/unit/test_dominator_trees_07.js
Normal file
|
|
@ -0,0 +1,146 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
// Test that we can incrementally fetch two subtrees in the same dominator tree
|
||||
// concurrently. This exercises the activeFetchRequestCount machinery.
|
||||
|
||||
const {
|
||||
snapshotState: states,
|
||||
dominatorTreeState,
|
||||
viewState,
|
||||
} = require("devtools/client/memory/constants");
|
||||
const {
|
||||
takeSnapshotAndCensus,
|
||||
selectSnapshotAndRefresh,
|
||||
fetchImmediatelyDominated,
|
||||
} = require("devtools/client/memory/actions/snapshot");
|
||||
const DominatorTreeLazyChildren
|
||||
= require("devtools/client/memory/dominator-tree-lazy-children");
|
||||
|
||||
const { changeView } = require("devtools/client/memory/actions/view");
|
||||
|
||||
function run_test() {
|
||||
run_next_test();
|
||||
}
|
||||
|
||||
add_task(function* () {
|
||||
let front = new StubbedMemoryFront();
|
||||
let heapWorker = new HeapAnalysesClient();
|
||||
yield front.attach();
|
||||
let store = Store();
|
||||
let { getState, dispatch } = store;
|
||||
|
||||
dispatch(changeView(viewState.DOMINATOR_TREE));
|
||||
dispatch(takeSnapshotAndCensus(front, heapWorker));
|
||||
|
||||
// Wait for the dominator tree to finish being fetched.
|
||||
yield waitUntilState(store, state =>
|
||||
state.snapshots[0] &&
|
||||
state.snapshots[0].dominatorTree &&
|
||||
state.snapshots[0].dominatorTree.state === dominatorTreeState.LOADED);
|
||||
ok(getState().snapshots[0].dominatorTree.root,
|
||||
"The dominator tree was fetched");
|
||||
|
||||
// Find a node that has more children.
|
||||
|
||||
function findNode(node) {
|
||||
if (node.moreChildrenAvailable && !node.children) {
|
||||
return node;
|
||||
}
|
||||
|
||||
if (node.children) {
|
||||
for (let child of node.children) {
|
||||
const found = findNode(child);
|
||||
if (found) {
|
||||
return found;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
const oldRoot = getState().snapshots[0].dominatorTree.root;
|
||||
const oldNode = findNode(oldRoot);
|
||||
ok(oldNode, "Should have found a node with more children.");
|
||||
|
||||
// Find another node that has more children.
|
||||
function findNodeRev(node) {
|
||||
if (node.moreChildrenAvailable && !node.children) {
|
||||
return node;
|
||||
}
|
||||
|
||||
if (node.children) {
|
||||
for (let child of node.children.slice().reverse()) {
|
||||
const found = findNodeRev(child);
|
||||
if (found) {
|
||||
return found;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
const oldNode2 = findNodeRev(oldRoot);
|
||||
ok(oldNode2, "Should have found another node with more children.");
|
||||
ok(oldNode !== oldNode2,
|
||||
"The second node should not be the same as the first one");
|
||||
|
||||
// Fetch both subtrees concurrently.
|
||||
dispatch(fetchImmediatelyDominated(heapWorker, getState().snapshots[0].id,
|
||||
new DominatorTreeLazyChildren(oldNode.nodeId, 0)));
|
||||
dispatch(fetchImmediatelyDominated(heapWorker, getState().snapshots[0].id,
|
||||
new DominatorTreeLazyChildren(oldNode2.nodeId, 0)));
|
||||
|
||||
equal(getState().snapshots[0].dominatorTree.state,
|
||||
dominatorTreeState.INCREMENTAL_FETCHING,
|
||||
"Fetching immediately dominated children should put us in the " +
|
||||
"INCREMENTAL_FETCHING state");
|
||||
|
||||
yield waitUntilState(store, state =>
|
||||
state.snapshots[0].dominatorTree.state === dominatorTreeState.LOADED);
|
||||
ok(true,
|
||||
"The dominator tree should go back to LOADED after the incremental " +
|
||||
"fetching is done.");
|
||||
|
||||
const newRoot = getState().snapshots[0].dominatorTree.root;
|
||||
ok(oldRoot !== newRoot,
|
||||
"When we insert new nodes, we get a new tree");
|
||||
|
||||
// Find the new node which has the children inserted.
|
||||
|
||||
function findNodeWithId(id, node) {
|
||||
if (node.nodeId === id) {
|
||||
return node;
|
||||
}
|
||||
|
||||
if (node.children) {
|
||||
for (let child of node.children) {
|
||||
const found = findNodeWithId(id, child);
|
||||
if (found) {
|
||||
return found;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
const newNode = findNodeWithId(oldNode.nodeId, newRoot);
|
||||
ok(newNode, "Should find the node in the new tree again");
|
||||
ok(newNode !== oldNode,
|
||||
"We did not mutate the old node in place, instead created a new node");
|
||||
ok(newNode.children.length,
|
||||
"And the new node should have the new children attached");
|
||||
|
||||
const newNode2 = findNodeWithId(oldNode2.nodeId, newRoot);
|
||||
ok(newNode2, "Should find the second node in the new tree again");
|
||||
ok(newNode2 !== oldNode2,
|
||||
"We did not mutate the second old node in place, instead created a new node");
|
||||
ok(newNode2.children,
|
||||
"And the new node should have the new children attached");
|
||||
|
||||
heapWorker.destroy();
|
||||
yield front.detach();
|
||||
});
|
||||
81
devtools/client/memory/test/unit/test_dominator_trees_08.js
Normal file
81
devtools/client/memory/test/unit/test_dominator_trees_08.js
Normal file
|
|
@ -0,0 +1,81 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
// Test that we can change the display with which we describe a dominator tree
|
||||
// and that the dominator tree is re-fetched.
|
||||
|
||||
const {
|
||||
snapshotState: states,
|
||||
dominatorTreeState,
|
||||
viewState,
|
||||
labelDisplays,
|
||||
treeMapState
|
||||
} = require("devtools/client/memory/constants");
|
||||
const {
|
||||
setLabelDisplayAndRefresh
|
||||
} = require("devtools/client/memory/actions/label-display");
|
||||
const {
|
||||
changeView,
|
||||
} = require("devtools/client/memory/actions/view");
|
||||
const {
|
||||
takeSnapshotAndCensus,
|
||||
computeAndFetchDominatorTree,
|
||||
} = require("devtools/client/memory/actions/snapshot");
|
||||
|
||||
function run_test() {
|
||||
run_next_test();
|
||||
}
|
||||
|
||||
add_task(function* () {
|
||||
let front = new StubbedMemoryFront();
|
||||
let heapWorker = new HeapAnalysesClient();
|
||||
yield front.attach();
|
||||
let store = Store();
|
||||
let { getState, dispatch } = store;
|
||||
|
||||
dispatch(changeView(viewState.DOMINATOR_TREE));
|
||||
|
||||
dispatch(takeSnapshotAndCensus(front, heapWorker));
|
||||
yield waitUntilCensusState(store, s => s.treeMap, [treeMapState.SAVED]);
|
||||
ok(!getState().snapshots[0].dominatorTree,
|
||||
"There shouldn't be a dominator tree model yet since it is not computed " +
|
||||
"until we switch to the dominators view.");
|
||||
|
||||
// Wait for the dominator tree to finish being fetched.
|
||||
yield waitUntilState(store, state =>
|
||||
state.snapshots[0] &&
|
||||
state.snapshots[0].dominatorTree &&
|
||||
state.snapshots[0].dominatorTree.state === dominatorTreeState.LOADED);
|
||||
|
||||
ok(getState().labelDisplay,
|
||||
"We have a default display for describing nodes in a dominator tree");
|
||||
equal(getState().labelDisplay,
|
||||
labelDisplays.coarseType,
|
||||
"and the default is coarse type");
|
||||
equal(getState().labelDisplay,
|
||||
getState().snapshots[0].dominatorTree.display,
|
||||
"and the newly computed dominator tree has that display");
|
||||
|
||||
// Switch to the allocationStack display.
|
||||
dispatch(setLabelDisplayAndRefresh(
|
||||
heapWorker,
|
||||
labelDisplays.allocationStack));
|
||||
|
||||
yield waitUntilState(store, state =>
|
||||
state.snapshots[0].dominatorTree.state === dominatorTreeState.FETCHING);
|
||||
ok(true,
|
||||
"switching display types caused the dominator tree to be fetched " +
|
||||
"again.");
|
||||
|
||||
yield waitUntilState(store, state =>
|
||||
state.snapshots[0].dominatorTree.state === dominatorTreeState.LOADED);
|
||||
equal(getState().snapshots[0].dominatorTree.display,
|
||||
labelDisplays.allocationStack,
|
||||
"The new dominator tree's display is allocationStack");
|
||||
equal(getState().labelDisplay,
|
||||
labelDisplays.allocationStack,
|
||||
"as is our requested dominator tree display");
|
||||
|
||||
heapWorker.destroy();
|
||||
yield front.detach();
|
||||
});
|
||||
78
devtools/client/memory/test/unit/test_dominator_trees_09.js
Normal file
78
devtools/client/memory/test/unit/test_dominator_trees_09.js
Normal file
|
|
@ -0,0 +1,78 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
// Test that we can change the display with which we describe a dominator tree
|
||||
// while the dominator tree is in the middle of being fetched.
|
||||
|
||||
const {
|
||||
snapshotState: states,
|
||||
dominatorTreeState,
|
||||
viewState,
|
||||
labelDisplays,
|
||||
treeMapState,
|
||||
} = require("devtools/client/memory/constants");
|
||||
const {
|
||||
setLabelDisplayAndRefresh
|
||||
} = require("devtools/client/memory/actions/label-display");
|
||||
const {
|
||||
changeView,
|
||||
} = require("devtools/client/memory/actions/view");
|
||||
const {
|
||||
takeSnapshotAndCensus,
|
||||
computeAndFetchDominatorTree,
|
||||
} = require("devtools/client/memory/actions/snapshot");
|
||||
|
||||
function run_test() {
|
||||
run_next_test();
|
||||
}
|
||||
|
||||
add_task(function* () {
|
||||
let front = new StubbedMemoryFront();
|
||||
let heapWorker = new HeapAnalysesClient();
|
||||
yield front.attach();
|
||||
let store = Store();
|
||||
let { getState, dispatch } = store;
|
||||
|
||||
dispatch(changeView(viewState.DOMINATOR_TREE));
|
||||
|
||||
dispatch(takeSnapshotAndCensus(front, heapWorker));
|
||||
yield waitUntilCensusState(store, s => s.treeMap, [treeMapState.SAVED]);
|
||||
ok(!getState().snapshots[0].dominatorTree,
|
||||
"There shouldn't be a dominator tree model yet since it is not computed " +
|
||||
"until we switch to the dominators view.");
|
||||
|
||||
// Wait for the dominator tree to start fetching.
|
||||
yield waitUntilState(store, state =>
|
||||
state.snapshots[0] &&
|
||||
state.snapshots[0].dominatorTree &&
|
||||
state.snapshots[0].dominatorTree.state === dominatorTreeState.FETCHING);
|
||||
|
||||
ok(getState().labelDisplay,
|
||||
"We have a default display for describing nodes in a dominator tree");
|
||||
equal(getState().labelDisplay,
|
||||
labelDisplays.coarseType,
|
||||
"and the default is coarse type");
|
||||
equal(getState().labelDisplay,
|
||||
getState().snapshots[0].dominatorTree.display,
|
||||
"and the newly computed dominator tree has that display");
|
||||
|
||||
// Switch to the allocationStack display while we are still fetching the
|
||||
// dominator tree.
|
||||
dispatch(setLabelDisplayAndRefresh(
|
||||
heapWorker,
|
||||
labelDisplays.allocationStack));
|
||||
|
||||
// Wait for the dominator tree to finish being fetched.
|
||||
yield waitUntilState(store, state =>
|
||||
state.snapshots[0].dominatorTree.state === dominatorTreeState.LOADED);
|
||||
|
||||
equal(getState().snapshots[0].dominatorTree.display,
|
||||
labelDisplays.allocationStack,
|
||||
"The new dominator tree's display is allocationStack");
|
||||
equal(getState().labelDisplay,
|
||||
labelDisplays.allocationStack,
|
||||
"as is our requested dominator tree display");
|
||||
|
||||
heapWorker.destroy();
|
||||
yield front.detach();
|
||||
});
|
||||
74
devtools/client/memory/test/unit/test_dominator_trees_10.js
Normal file
74
devtools/client/memory/test/unit/test_dominator_trees_10.js
Normal file
|
|
@ -0,0 +1,74 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
// Test that we maintain focus of the selected dominator tree node across
|
||||
// changing breakdowns for labeling them.
|
||||
|
||||
let {
|
||||
snapshotState: states,
|
||||
dominatorTreeState,
|
||||
labelDisplays,
|
||||
viewState,
|
||||
} = require("devtools/client/memory/constants");
|
||||
let {
|
||||
takeSnapshotAndCensus,
|
||||
focusDominatorTreeNode,
|
||||
} = require("devtools/client/memory/actions/snapshot");
|
||||
const {
|
||||
changeView,
|
||||
} = require("devtools/client/memory/actions/view");
|
||||
const {
|
||||
setLabelDisplayAndRefresh,
|
||||
} = require("devtools/client/memory/actions/label-display");
|
||||
|
||||
function run_test() {
|
||||
run_next_test();
|
||||
}
|
||||
|
||||
add_task(function* () {
|
||||
let front = new StubbedMemoryFront();
|
||||
let heapWorker = new HeapAnalysesClient();
|
||||
yield front.attach();
|
||||
let store = Store();
|
||||
let { getState, dispatch } = store;
|
||||
|
||||
dispatch(changeView(viewState.DOMINATOR_TREE));
|
||||
dispatch(takeSnapshotAndCensus(front, heapWorker));
|
||||
|
||||
// Wait for the dominator tree to finish being fetched.
|
||||
yield waitUntilState(store, state =>
|
||||
state.snapshots[0] &&
|
||||
state.snapshots[0].dominatorTree &&
|
||||
state.snapshots[0].dominatorTree.state === dominatorTreeState.LOADED);
|
||||
ok(true, "The dominator tree was fetched");
|
||||
|
||||
const root = getState().snapshots[0].dominatorTree.root;
|
||||
ok(root, "When the dominator tree is loaded, we should have its root");
|
||||
|
||||
dispatch(focusDominatorTreeNode(getState().snapshots[0].id, root));
|
||||
equal(root, getState().snapshots[0].dominatorTree.focused,
|
||||
"The root should be focused.");
|
||||
|
||||
equal(getState().labelDisplay, labelDisplays.coarseType,
|
||||
"Using labelDisplays.coarseType by default");
|
||||
dispatch(setLabelDisplayAndRefresh(heapWorker,
|
||||
labelDisplays.allocationStack));
|
||||
equal(getState().labelDisplay, labelDisplays.allocationStack,
|
||||
"Using labelDisplays.allocationStack now");
|
||||
|
||||
yield waitUntilState(store, state =>
|
||||
state.snapshots[0].dominatorTree.state === dominatorTreeState.FETCHING);
|
||||
ok(true, "We started re-fetching the dominator tree");
|
||||
|
||||
yield waitUntilState(store, state =>
|
||||
state.snapshots[0].dominatorTree.state === dominatorTreeState.LOADED);
|
||||
ok(true, "The dominator tree was loaded again");
|
||||
|
||||
ok(getState().snapshots[0].dominatorTree.focused,
|
||||
"Still have a focused node");
|
||||
equal(getState().snapshots[0].dominatorTree.focused.nodeId, root.nodeId,
|
||||
"Focused node is the same as before");
|
||||
|
||||
heapWorker.destroy();
|
||||
yield front.detach();
|
||||
});
|
||||
76
devtools/client/memory/test/unit/test_individuals_01.js
Normal file
76
devtools/client/memory/test/unit/test_individuals_01.js
Normal file
|
|
@ -0,0 +1,76 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
"use strict";
|
||||
|
||||
// Basic test for switching to the individuals view.
|
||||
|
||||
const {
|
||||
censusState,
|
||||
viewState,
|
||||
individualsState,
|
||||
} = require("devtools/client/memory/constants");
|
||||
const {
|
||||
fetchIndividuals,
|
||||
takeSnapshotAndCensus,
|
||||
} = require("devtools/client/memory/actions/snapshot");
|
||||
const {
|
||||
changeView,
|
||||
} = require("devtools/client/memory/actions/view");
|
||||
|
||||
function run_test() {
|
||||
run_next_test();
|
||||
}
|
||||
|
||||
const EXPECTED_INDIVIDUAL_STATES = [
|
||||
individualsState.COMPUTING_DOMINATOR_TREE,
|
||||
individualsState.FETCHING,
|
||||
individualsState.FETCHED,
|
||||
];
|
||||
|
||||
add_task(function* () {
|
||||
let front = new StubbedMemoryFront();
|
||||
let heapWorker = new HeapAnalysesClient();
|
||||
yield front.attach();
|
||||
let store = Store();
|
||||
const { getState, dispatch } = store;
|
||||
|
||||
equal(getState().individuals, null,
|
||||
"no individuals state by default");
|
||||
|
||||
dispatch(changeView(viewState.CENSUS));
|
||||
dispatch(takeSnapshotAndCensus(front, heapWorker));
|
||||
yield waitUntilCensusState(store, s => s.census, [censusState.SAVED]);
|
||||
|
||||
const root = getState().snapshots[0].census.report;
|
||||
ok(root, "Should have a census");
|
||||
|
||||
const reportLeafIndex = findReportLeafIndex(root);
|
||||
ok(reportLeafIndex, "Should get a reportLeafIndex");
|
||||
|
||||
const snapshotId = getState().snapshots[0].id;
|
||||
ok(snapshotId, "Should have a snapshot id");
|
||||
|
||||
const breakdown = getState().snapshots[0].census.display.breakdown;
|
||||
ok(breakdown, "Should have a breakdown");
|
||||
|
||||
dispatch(fetchIndividuals(heapWorker, snapshotId, breakdown,
|
||||
reportLeafIndex));
|
||||
|
||||
// Wait for each expected state.
|
||||
for (let state of EXPECTED_INDIVIDUAL_STATES) {
|
||||
yield waitUntilState(store, s => {
|
||||
return s.view.state === viewState.INDIVIDUALS &&
|
||||
s.individuals &&
|
||||
s.individuals.state === state;
|
||||
});
|
||||
ok(true, `Reached state = ${state}`);
|
||||
}
|
||||
|
||||
ok(getState().individuals, "Should have individuals state");
|
||||
ok(getState().individuals.nodes, "Should have individuals nodes");
|
||||
ok(getState().individuals.nodes.length > 0,
|
||||
"Should have a positive number of nodes");
|
||||
|
||||
heapWorker.destroy();
|
||||
yield front.detach();
|
||||
});
|
||||
88
devtools/client/memory/test/unit/test_individuals_02.js
Normal file
88
devtools/client/memory/test/unit/test_individuals_02.js
Normal file
|
|
@ -0,0 +1,88 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
"use strict";
|
||||
|
||||
// Test switching to the individuals view when we are in the middle of computing
|
||||
// a dominator tree.
|
||||
|
||||
const {
|
||||
censusState,
|
||||
dominatorTreeState,
|
||||
viewState,
|
||||
individualsState,
|
||||
} = require("devtools/client/memory/constants");
|
||||
const {
|
||||
fetchIndividuals,
|
||||
takeSnapshotAndCensus,
|
||||
computeDominatorTree,
|
||||
} = require("devtools/client/memory/actions/snapshot");
|
||||
const {
|
||||
changeView,
|
||||
} = require("devtools/client/memory/actions/view");
|
||||
|
||||
function run_test() {
|
||||
run_next_test();
|
||||
}
|
||||
|
||||
const EXPECTED_INDIVIDUAL_STATES = [
|
||||
individualsState.COMPUTING_DOMINATOR_TREE,
|
||||
individualsState.FETCHING,
|
||||
individualsState.FETCHED,
|
||||
];
|
||||
|
||||
add_task(function* () {
|
||||
let front = new StubbedMemoryFront();
|
||||
let heapWorker = new HeapAnalysesClient();
|
||||
yield front.attach();
|
||||
let store = Store();
|
||||
const { getState, dispatch } = store;
|
||||
|
||||
equal(getState().individuals, null,
|
||||
"no individuals state by default");
|
||||
|
||||
dispatch(changeView(viewState.CENSUS));
|
||||
dispatch(takeSnapshotAndCensus(front, heapWorker));
|
||||
yield waitUntilCensusState(store, s => s.census, [censusState.SAVED]);
|
||||
|
||||
const root = getState().snapshots[0].census.report;
|
||||
ok(root, "Should have a census");
|
||||
|
||||
const reportLeafIndex = findReportLeafIndex(root);
|
||||
ok(reportLeafIndex, "Should get a reportLeafIndex");
|
||||
|
||||
const snapshotId = getState().snapshots[0].id;
|
||||
ok(snapshotId, "Should have a snapshot id");
|
||||
|
||||
const breakdown = getState().snapshots[0].census.display.breakdown;
|
||||
ok(breakdown, "Should have a breakdown");
|
||||
|
||||
// Start computing a dominator tree.
|
||||
|
||||
dispatch(computeDominatorTree(heapWorker, snapshotId));
|
||||
equal(getState().snapshots[0].dominatorTree.state,
|
||||
dominatorTreeState.COMPUTING,
|
||||
"Should be computing dominator tree");
|
||||
|
||||
// Fetch individuals in the middle of computing the dominator tree.
|
||||
|
||||
dispatch(fetchIndividuals(heapWorker, snapshotId, breakdown,
|
||||
reportLeafIndex));
|
||||
|
||||
// Wait for each expected state.
|
||||
for (let state of EXPECTED_INDIVIDUAL_STATES) {
|
||||
yield waitUntilState(store, s => {
|
||||
return s.view.state === viewState.INDIVIDUALS &&
|
||||
s.individuals &&
|
||||
s.individuals.state === state;
|
||||
});
|
||||
ok(true, `Reached state = ${state}`);
|
||||
}
|
||||
|
||||
ok(getState().individuals, "Should have individuals state");
|
||||
ok(getState().individuals.nodes, "Should have individuals nodes");
|
||||
ok(getState().individuals.nodes.length > 0,
|
||||
"Should have a positive number of nodes");
|
||||
|
||||
heapWorker.destroy();
|
||||
yield front.detach();
|
||||
});
|
||||
106
devtools/client/memory/test/unit/test_individuals_03.js
Normal file
106
devtools/client/memory/test/unit/test_individuals_03.js
Normal file
|
|
@ -0,0 +1,106 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
"use strict";
|
||||
|
||||
// Test switching to the individuals view when we are in the diffing view.
|
||||
|
||||
const {
|
||||
censusState,
|
||||
diffingState,
|
||||
viewState,
|
||||
individualsState,
|
||||
} = require("devtools/client/memory/constants");
|
||||
const {
|
||||
fetchIndividuals,
|
||||
takeSnapshotAndCensus,
|
||||
} = require("devtools/client/memory/actions/snapshot");
|
||||
const {
|
||||
changeView,
|
||||
popViewAndRefresh,
|
||||
} = require("devtools/client/memory/actions/view");
|
||||
const {
|
||||
selectSnapshotForDiffingAndRefresh,
|
||||
} = require("devtools/client/memory/actions/diffing");
|
||||
|
||||
function run_test() {
|
||||
run_next_test();
|
||||
}
|
||||
|
||||
const EXPECTED_INDIVIDUAL_STATES = [
|
||||
individualsState.COMPUTING_DOMINATOR_TREE,
|
||||
individualsState.FETCHING,
|
||||
individualsState.FETCHED,
|
||||
];
|
||||
|
||||
add_task(function* () {
|
||||
let front = new StubbedMemoryFront();
|
||||
let heapWorker = new HeapAnalysesClient();
|
||||
yield front.attach();
|
||||
let store = Store();
|
||||
const { getState, dispatch } = store;
|
||||
|
||||
dispatch(changeView(viewState.CENSUS));
|
||||
|
||||
// Take two snapshots and diff them from each other.
|
||||
|
||||
dispatch(takeSnapshotAndCensus(front, heapWorker));
|
||||
dispatch(takeSnapshotAndCensus(front, heapWorker));
|
||||
yield waitUntilCensusState(store, s => s.census, [censusState.SAVED,
|
||||
censusState.SAVED]);
|
||||
|
||||
dispatch(changeView(viewState.DIFFING));
|
||||
dispatch(selectSnapshotForDiffingAndRefresh(heapWorker, getState().snapshots[0]));
|
||||
dispatch(selectSnapshotForDiffingAndRefresh(heapWorker, getState().snapshots[1]));
|
||||
|
||||
yield waitUntilState(store, state => {
|
||||
return state.diffing &&
|
||||
state.diffing.state === diffingState.TOOK_DIFF;
|
||||
});
|
||||
ok(getState().diffing.census);
|
||||
|
||||
// Fetch individuals.
|
||||
|
||||
const root = getState().diffing.census.report;
|
||||
ok(root, "Should have a census");
|
||||
|
||||
const reportLeafIndex = findReportLeafIndex(root);
|
||||
ok(reportLeafIndex, "Should get a reportLeafIndex");
|
||||
|
||||
const snapshotId = getState().diffing.secondSnapshotId;
|
||||
ok(snapshotId, "Should have a snapshot id");
|
||||
|
||||
const breakdown = getState().censusDisplay.breakdown;
|
||||
ok(breakdown, "Should have a breakdown");
|
||||
|
||||
dispatch(fetchIndividuals(heapWorker, snapshotId, breakdown,
|
||||
reportLeafIndex));
|
||||
|
||||
for (let state of EXPECTED_INDIVIDUAL_STATES) {
|
||||
yield waitUntilState(store, s => {
|
||||
return s.view.state === viewState.INDIVIDUALS &&
|
||||
s.individuals &&
|
||||
s.individuals.state === state;
|
||||
});
|
||||
ok(true, `Reached state = ${state}`);
|
||||
}
|
||||
|
||||
ok(getState().individuals, "Should have individuals state");
|
||||
ok(getState().individuals.nodes, "Should have individuals nodes");
|
||||
ok(getState().individuals.nodes.length > 0,
|
||||
"Should have a positive number of nodes");
|
||||
|
||||
// Pop the view back to the diffing.
|
||||
|
||||
dispatch(popViewAndRefresh(heapWorker));
|
||||
|
||||
yield waitUntilState(store, state => {
|
||||
return state.diffing &&
|
||||
state.diffing.state === diffingState.TOOK_DIFF;
|
||||
});
|
||||
|
||||
ok(getState().diffing.census.report,
|
||||
"We have our census diff again after popping back to the last view");
|
||||
|
||||
heapWorker.destroy();
|
||||
yield front.detach();
|
||||
});
|
||||
89
devtools/client/memory/test/unit/test_individuals_04.js
Normal file
89
devtools/client/memory/test/unit/test_individuals_04.js
Normal file
|
|
@ -0,0 +1,89 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
"use strict";
|
||||
|
||||
// Test showing individual Array objects.
|
||||
|
||||
const {
|
||||
censusState,
|
||||
viewState,
|
||||
individualsState,
|
||||
} = require("devtools/client/memory/constants");
|
||||
const {
|
||||
fetchIndividuals,
|
||||
takeSnapshotAndCensus,
|
||||
} = require("devtools/client/memory/actions/snapshot");
|
||||
const {
|
||||
changeView,
|
||||
} = require("devtools/client/memory/actions/view");
|
||||
const {
|
||||
setFilterString,
|
||||
} = require("devtools/client/memory/actions/filter");
|
||||
|
||||
function run_test() {
|
||||
run_next_test();
|
||||
}
|
||||
|
||||
const EXPECTED_INDIVIDUAL_STATES = [
|
||||
individualsState.COMPUTING_DOMINATOR_TREE,
|
||||
individualsState.FETCHING,
|
||||
individualsState.FETCHED,
|
||||
];
|
||||
|
||||
add_task(function* () {
|
||||
let front = new StubbedMemoryFront();
|
||||
let heapWorker = new HeapAnalysesClient();
|
||||
yield front.attach();
|
||||
let store = Store();
|
||||
const { getState, dispatch } = store;
|
||||
|
||||
dispatch(changeView(viewState.CENSUS));
|
||||
dispatch(setFilterString("Array"));
|
||||
|
||||
// Take a snapshot and wait for the census to finish.
|
||||
|
||||
dispatch(takeSnapshotAndCensus(front, heapWorker));
|
||||
yield waitUntilCensusState(store, s => s.census, [censusState.SAVED]);
|
||||
|
||||
// Fetch individuals.
|
||||
|
||||
const root = getState().snapshots[0].census.report;
|
||||
ok(root, "Should have a census");
|
||||
|
||||
const reportLeafIndex = findReportLeafIndex(root, "Array");
|
||||
ok(reportLeafIndex, "Should get a reportLeafIndex for Array");
|
||||
|
||||
const snapshotId = getState().snapshots[0].id;
|
||||
ok(snapshotId, "Should have a snapshot id");
|
||||
|
||||
const breakdown = getState().censusDisplay.breakdown;
|
||||
ok(breakdown, "Should have a breakdown");
|
||||
|
||||
dispatch(fetchIndividuals(heapWorker, snapshotId, breakdown,
|
||||
reportLeafIndex));
|
||||
|
||||
for (let state of EXPECTED_INDIVIDUAL_STATES) {
|
||||
yield waitUntilState(store, s => {
|
||||
return s.view.state === viewState.INDIVIDUALS &&
|
||||
s.individuals &&
|
||||
s.individuals.state === state;
|
||||
});
|
||||
ok(true, `Reached state = ${state}`);
|
||||
}
|
||||
|
||||
ok(getState().individuals, "Should have individuals state");
|
||||
ok(getState().individuals.nodes, "Should have individuals nodes");
|
||||
ok(getState().individuals.nodes.length > 0,
|
||||
"Should have a positive number of nodes");
|
||||
|
||||
// Assert that all the individuals are `Array`s.
|
||||
|
||||
for (let node of getState().individuals.nodes) {
|
||||
dumpn("Checking node: " + node.label.join(" > "));
|
||||
ok(node.label.find(part => part === "Array"),
|
||||
"The node should be an Array node");
|
||||
}
|
||||
|
||||
heapWorker.destroy();
|
||||
yield front.detach();
|
||||
});
|
||||
82
devtools/client/memory/test/unit/test_individuals_05.js
Normal file
82
devtools/client/memory/test/unit/test_individuals_05.js
Normal file
|
|
@ -0,0 +1,82 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
"use strict";
|
||||
|
||||
// Test showing individual objects that do not have allocation stacks.
|
||||
|
||||
const {
|
||||
censusState,
|
||||
viewState,
|
||||
individualsState,
|
||||
censusDisplays,
|
||||
} = require("devtools/client/memory/constants");
|
||||
const {
|
||||
fetchIndividuals,
|
||||
takeSnapshotAndCensus,
|
||||
} = require("devtools/client/memory/actions/snapshot");
|
||||
const {
|
||||
changeView,
|
||||
} = require("devtools/client/memory/actions/view");
|
||||
const {
|
||||
setCensusDisplay,
|
||||
} = require("devtools/client/memory/actions/census-display");
|
||||
|
||||
function run_test() {
|
||||
run_next_test();
|
||||
}
|
||||
|
||||
const EXPECTED_INDIVIDUAL_STATES = [
|
||||
individualsState.COMPUTING_DOMINATOR_TREE,
|
||||
individualsState.FETCHING,
|
||||
individualsState.FETCHED,
|
||||
];
|
||||
|
||||
add_task(function* () {
|
||||
let front = new StubbedMemoryFront();
|
||||
let heapWorker = new HeapAnalysesClient();
|
||||
yield front.attach();
|
||||
let store = Store();
|
||||
const { getState, dispatch } = store;
|
||||
|
||||
dispatch(changeView(viewState.CENSUS));
|
||||
dispatch(setCensusDisplay(censusDisplays.invertedAllocationStack));
|
||||
|
||||
// Take a snapshot and wait for the census to finish.
|
||||
|
||||
dispatch(takeSnapshotAndCensus(front, heapWorker));
|
||||
yield waitUntilCensusState(store, s => s.census, [censusState.SAVED]);
|
||||
|
||||
// Fetch individuals.
|
||||
|
||||
const root = getState().snapshots[0].census.report;
|
||||
ok(root, "Should have a census");
|
||||
|
||||
const reportLeafIndex = findReportLeafIndex(root, "noStack");
|
||||
ok(reportLeafIndex, "Should get a reportLeafIndex for noStack");
|
||||
|
||||
const snapshotId = getState().snapshots[0].id;
|
||||
ok(snapshotId, "Should have a snapshot id");
|
||||
|
||||
const breakdown = getState().censusDisplay.breakdown;
|
||||
ok(breakdown, "Should have a breakdown");
|
||||
|
||||
dispatch(fetchIndividuals(heapWorker, snapshotId, breakdown,
|
||||
reportLeafIndex));
|
||||
|
||||
for (let state of EXPECTED_INDIVIDUAL_STATES) {
|
||||
yield waitUntilState(store, s => {
|
||||
return s.view.state === viewState.INDIVIDUALS &&
|
||||
s.individuals &&
|
||||
s.individuals.state === state;
|
||||
});
|
||||
ok(true, `Reached state = ${state}`);
|
||||
}
|
||||
|
||||
ok(getState().individuals, "Should have individuals state");
|
||||
ok(getState().individuals.nodes, "Should have individuals nodes");
|
||||
ok(getState().individuals.nodes.length > 0,
|
||||
"Should have a positive number of nodes");
|
||||
|
||||
heapWorker.destroy();
|
||||
yield front.detach();
|
||||
});
|
||||
84
devtools/client/memory/test/unit/test_individuals_06.js
Normal file
84
devtools/client/memory/test/unit/test_individuals_06.js
Normal file
|
|
@ -0,0 +1,84 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
"use strict";
|
||||
|
||||
// Test that clearing the current individuals' snapshot leaves the individuals
|
||||
// view.
|
||||
|
||||
const {
|
||||
censusState,
|
||||
viewState,
|
||||
individualsState,
|
||||
} = require("devtools/client/memory/constants");
|
||||
const {
|
||||
fetchIndividuals,
|
||||
takeSnapshotAndCensus,
|
||||
clearSnapshots,
|
||||
} = require("devtools/client/memory/actions/snapshot");
|
||||
const {
|
||||
changeView,
|
||||
} = require("devtools/client/memory/actions/view");
|
||||
|
||||
function run_test() {
|
||||
run_next_test();
|
||||
}
|
||||
|
||||
const EXPECTED_INDIVIDUAL_STATES = [
|
||||
individualsState.COMPUTING_DOMINATOR_TREE,
|
||||
individualsState.FETCHING,
|
||||
individualsState.FETCHED,
|
||||
];
|
||||
|
||||
add_task(function* () {
|
||||
let front = new StubbedMemoryFront();
|
||||
let heapWorker = new HeapAnalysesClient();
|
||||
yield front.attach();
|
||||
let store = Store();
|
||||
const { getState, dispatch } = store;
|
||||
|
||||
dispatch(changeView(viewState.CENSUS));
|
||||
|
||||
// Take a snapshot and wait for the census to finish.
|
||||
|
||||
dispatch(takeSnapshotAndCensus(front, heapWorker));
|
||||
yield waitUntilCensusState(store, s => s.census, [censusState.SAVED]);
|
||||
|
||||
// Fetch individuals.
|
||||
|
||||
const root = getState().snapshots[0].census.report;
|
||||
ok(root, "Should have a census");
|
||||
|
||||
const reportLeafIndex = findReportLeafIndex(root);
|
||||
ok(reportLeafIndex, "Should get a reportLeafIndex");
|
||||
|
||||
const snapshotId = getState().snapshots[0].id;
|
||||
ok(snapshotId, "Should have a snapshot id");
|
||||
|
||||
const breakdown = getState().censusDisplay.breakdown;
|
||||
ok(breakdown, "Should have a breakdown");
|
||||
|
||||
dispatch(fetchIndividuals(heapWorker, snapshotId, breakdown,
|
||||
reportLeafIndex));
|
||||
|
||||
for (let state of EXPECTED_INDIVIDUAL_STATES) {
|
||||
yield waitUntilState(store, s => {
|
||||
return s.view.state === viewState.INDIVIDUALS &&
|
||||
s.individuals &&
|
||||
s.individuals.state === state;
|
||||
});
|
||||
ok(true, `Reached state = ${state}`);
|
||||
}
|
||||
|
||||
ok(getState().individuals, "Should have individuals state");
|
||||
ok(getState().individuals.nodes, "Should have individuals nodes");
|
||||
ok(getState().individuals.nodes.length > 0,
|
||||
"Should have a positive number of nodes");
|
||||
|
||||
dispatch(clearSnapshots(heapWorker));
|
||||
|
||||
equal(getState().view.state, viewState.CENSUS,
|
||||
"Went back to census view");
|
||||
|
||||
heapWorker.destroy();
|
||||
yield front.detach();
|
||||
});
|
||||
81
devtools/client/memory/test/unit/test_pop_view_01.js
Normal file
81
devtools/client/memory/test/unit/test_pop_view_01.js
Normal file
|
|
@ -0,0 +1,81 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
"use strict";
|
||||
|
||||
// Test popping views from each intermediate individuals model state.
|
||||
|
||||
const {
|
||||
censusState,
|
||||
viewState,
|
||||
individualsState,
|
||||
} = require("devtools/client/memory/constants");
|
||||
const {
|
||||
fetchIndividuals,
|
||||
takeSnapshotAndCensus,
|
||||
} = require("devtools/client/memory/actions/snapshot");
|
||||
const {
|
||||
changeView,
|
||||
popViewAndRefresh,
|
||||
} = require("devtools/client/memory/actions/view");
|
||||
|
||||
function run_test() {
|
||||
run_next_test();
|
||||
}
|
||||
|
||||
const TEST_STATES = [
|
||||
individualsState.COMPUTING_DOMINATOR_TREE,
|
||||
individualsState.FETCHING,
|
||||
individualsState.FETCHED,
|
||||
];
|
||||
|
||||
add_task(function* () {
|
||||
let front = new StubbedMemoryFront();
|
||||
let heapWorker = new HeapAnalysesClient();
|
||||
yield front.attach();
|
||||
let store = Store();
|
||||
const { getState, dispatch } = store;
|
||||
|
||||
equal(getState().individuals, null,
|
||||
"no individuals state by default");
|
||||
|
||||
dispatch(changeView(viewState.CENSUS));
|
||||
dispatch(takeSnapshotAndCensus(front, heapWorker));
|
||||
yield waitUntilCensusState(store, s => s.census, [censusState.SAVED]);
|
||||
|
||||
const root = getState().snapshots[0].census.report;
|
||||
ok(root, "Should have a census");
|
||||
|
||||
const reportLeafIndex = findReportLeafIndex(root);
|
||||
ok(reportLeafIndex, "Should get a reportLeafIndex");
|
||||
|
||||
const snapshotId = getState().snapshots[0].id;
|
||||
ok(snapshotId, "Should have a snapshot id");
|
||||
|
||||
const breakdown = getState().snapshots[0].census.display.breakdown;
|
||||
ok(breakdown, "Should have a breakdown");
|
||||
|
||||
for (let state of TEST_STATES) {
|
||||
dumpn(`Testing popping back to the old view from state = ${state}`);
|
||||
|
||||
dispatch(fetchIndividuals(heapWorker, snapshotId, breakdown,
|
||||
reportLeafIndex));
|
||||
|
||||
// Wait for the expected test state.
|
||||
yield waitUntilState(store, s => {
|
||||
return s.view.state === viewState.INDIVIDUALS &&
|
||||
s.individuals &&
|
||||
s.individuals.state === state;
|
||||
});
|
||||
ok(true, `Reached state = ${state}`);
|
||||
|
||||
// Pop back to the CENSUS state.
|
||||
dispatch(popViewAndRefresh(heapWorker));
|
||||
yield waitUntilState(store, s => {
|
||||
return s.view.state === viewState.CENSUS;
|
||||
});
|
||||
ok(!getState().individuals, "Should no longer have individuals");
|
||||
}
|
||||
|
||||
heapWorker.destroy();
|
||||
yield front.detach();
|
||||
});
|
||||
57
devtools/client/memory/test/unit/test_tree-map-01.js
Normal file
57
devtools/client/memory/test/unit/test_tree-map-01.js
Normal file
|
|
@ -0,0 +1,57 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
const { drawBox } = require("devtools/client/memory/components/tree-map/draw");
|
||||
|
||||
function run_test() {
|
||||
run_next_test();
|
||||
}
|
||||
|
||||
add_task(function* () {
|
||||
let fillRectValues, strokeRectValues;
|
||||
let ctx = {
|
||||
fillRect: (...args) => fillRectValues = args,
|
||||
strokeRect: (...args) => strokeRectValues = args
|
||||
};
|
||||
let node = {
|
||||
x: 20,
|
||||
y: 30,
|
||||
dx: 50,
|
||||
dy: 70,
|
||||
type: "other",
|
||||
depth: 2
|
||||
};
|
||||
let padding = [10, 10];
|
||||
let borderWidth = () => 1;
|
||||
let dragZoom = {
|
||||
offsetX: 0,
|
||||
offsetY: 0,
|
||||
zoom: 0
|
||||
};
|
||||
drawBox(ctx, node, borderWidth, dragZoom, padding);
|
||||
ok(true, JSON.stringify([ctx, fillRectValues, strokeRectValues]));
|
||||
equal(ctx.fillStyle, "hsl(210,60%,70%)", "The fillStyle is set");
|
||||
equal(ctx.strokeStyle, "hsl(210,60%,35%)", "The strokeStyle is set");
|
||||
equal(ctx.lineWidth, 1, "The lineWidth is set");
|
||||
deepEqual(fillRectValues, [10.5, 20.5, 49, 69], "Draws a filled rectangle");
|
||||
deepEqual(strokeRectValues, [10.5, 20.5, 49, 69], "Draws a stroked rectangle");
|
||||
|
||||
|
||||
dragZoom.zoom = 0.5;
|
||||
|
||||
drawBox(ctx, node, borderWidth, dragZoom, padding);
|
||||
ok(true, JSON.stringify([ctx, fillRectValues, strokeRectValues]));
|
||||
deepEqual(fillRectValues, [15.5, 30.5, 74, 104],
|
||||
"Draws a zoomed filled rectangle");
|
||||
deepEqual(strokeRectValues, [15.5, 30.5, 74, 104],
|
||||
"Draws a zoomed stroked rectangle");
|
||||
|
||||
dragZoom.offsetX = 110;
|
||||
dragZoom.offsetY = 130;
|
||||
|
||||
drawBox(ctx, node, borderWidth, dragZoom, padding);
|
||||
deepEqual(fillRectValues, [-94.5, -99.5, 74, 104],
|
||||
"Draws a zoomed and offset filled rectangle");
|
||||
deepEqual(strokeRectValues, [-94.5, -99.5, 74, 104],
|
||||
"Draws a zoomed and offset stroked rectangle");
|
||||
});
|
||||
81
devtools/client/memory/test/unit/test_tree-map-02.js
Normal file
81
devtools/client/memory/test/unit/test_tree-map-02.js
Normal file
|
|
@ -0,0 +1,81 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
"use strict";
|
||||
|
||||
const { drawText } = require("devtools/client/memory/components/tree-map/draw");
|
||||
|
||||
function run_test() {
|
||||
run_next_test();
|
||||
}
|
||||
|
||||
add_task(function* () {
|
||||
// Mock out the Canvas2dContext
|
||||
let ctx = {
|
||||
fillText: (...args) => fillTextValues.push(args),
|
||||
measureText: (text) => {
|
||||
let width = text ? text.length * 10 : 0;
|
||||
return { width };
|
||||
}
|
||||
};
|
||||
let node = {
|
||||
x: 20,
|
||||
y: 30,
|
||||
dx: 500,
|
||||
dy: 70,
|
||||
name: "Example Node",
|
||||
totalBytes: 1200,
|
||||
totalCount: 100
|
||||
};
|
||||
let ratio = 0;
|
||||
let borderWidth = () => 1;
|
||||
let dragZoom = {
|
||||
offsetX: 0,
|
||||
offsetY: 0,
|
||||
zoom: 0
|
||||
};
|
||||
let fillTextValues = [];
|
||||
let padding = [10, 10];
|
||||
|
||||
drawText(ctx, node, borderWidth, ratio, dragZoom, padding);
|
||||
deepEqual(fillTextValues[0], ["Example Node", 11.5, 21.5],
|
||||
"Fills in the full node name");
|
||||
deepEqual(fillTextValues[1], ["1KiB 100 count", 141.5, 21.5],
|
||||
"Includes the full byte and count information");
|
||||
|
||||
fillTextValues = [];
|
||||
node.dx = 250;
|
||||
drawText(ctx, node, borderWidth, ratio, dragZoom, padding);
|
||||
|
||||
deepEqual(fillTextValues[0], ["Example Node", 11.5, 21.5],
|
||||
"Fills in the full node name");
|
||||
deepEqual(fillTextValues[1], undefined,
|
||||
"Drops off the byte and count information if not enough room");
|
||||
|
||||
fillTextValues = [];
|
||||
node.dx = 100;
|
||||
drawText(ctx, node, borderWidth, ratio, dragZoom, padding);
|
||||
|
||||
deepEqual(fillTextValues[0], ["Exampl...", 11.5, 21.5],
|
||||
"Cuts the name with ellipsis");
|
||||
deepEqual(fillTextValues[1], undefined,
|
||||
"Drops off the byte and count information if not enough room");
|
||||
|
||||
fillTextValues = [];
|
||||
node.dx = 40;
|
||||
drawText(ctx, node, borderWidth, ratio, dragZoom, padding);
|
||||
|
||||
deepEqual(fillTextValues[0], ["...", 11.5, 21.5],
|
||||
"Shows only ellipsis when smaller");
|
||||
deepEqual(fillTextValues[1], undefined,
|
||||
"Drops off the byte and count information if not enough room");
|
||||
|
||||
fillTextValues = [];
|
||||
node.dx = 20;
|
||||
drawText(ctx, node, borderWidth, ratio, dragZoom, padding);
|
||||
|
||||
deepEqual(fillTextValues[0], undefined,
|
||||
"Draw nothing when not enough room");
|
||||
deepEqual(fillTextValues[1], undefined,
|
||||
"Drops off the byte and count information if not enough room");
|
||||
});
|
||||
|
|
@ -0,0 +1,72 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
"use strict";
|
||||
|
||||
/**
|
||||
* Tests that we use the correct snapshot aggregate value
|
||||
* in `utils.getSnapshotTotals(snapshot)`
|
||||
*/
|
||||
|
||||
const { censusDisplays, snapshotState: states, viewState, censusState } = require("devtools/client/memory/constants");
|
||||
const { getSnapshotTotals } = require("devtools/client/memory/utils");
|
||||
const { takeSnapshotAndCensus } = require("devtools/client/memory/actions/snapshot");
|
||||
const { setCensusDisplayAndRefresh } = require("devtools/client/memory/actions/census-display");
|
||||
const { changeView } = require("devtools/client/memory/actions/view");
|
||||
|
||||
function run_test() {
|
||||
run_next_test();
|
||||
}
|
||||
|
||||
add_task(function* () {
|
||||
let front = new StubbedMemoryFront();
|
||||
let heapWorker = new HeapAnalysesClient();
|
||||
yield front.attach();
|
||||
let store = Store();
|
||||
let { getState, dispatch } = store;
|
||||
|
||||
dispatch(changeView(viewState.CENSUS));
|
||||
|
||||
yield dispatch(setCensusDisplayAndRefresh(heapWorker,
|
||||
censusDisplays.allocationStack));
|
||||
|
||||
dispatch(takeSnapshotAndCensus(front, heapWorker));
|
||||
yield waitUntilCensusState(store, s => s.census, [censusState.SAVED]);
|
||||
|
||||
ok(!getState().snapshots[0].census.display.inverted, "Snapshot is not inverted");
|
||||
|
||||
let census = getState().snapshots[0].census;
|
||||
let result = aggregate(census.report);
|
||||
let totalBytes = result.bytes;
|
||||
let totalCount = result.count;
|
||||
|
||||
ok(totalBytes > 0, "counted up bytes in the census");
|
||||
ok(totalCount > 0, "counted up count in the census");
|
||||
|
||||
result = getSnapshotTotals(getState().snapshots[0].census);
|
||||
equal(totalBytes, result.bytes, "getSnapshotTotals reuslted in correct bytes");
|
||||
equal(totalCount, result.count, "getSnapshotTotals reuslted in correct count");
|
||||
|
||||
dispatch(setCensusDisplayAndRefresh(heapWorker,
|
||||
censusDisplays.invertedAllocationStack));
|
||||
|
||||
yield waitUntilCensusState(store, s => s.census, [censusState.SAVING]);
|
||||
yield waitUntilCensusState(store, s => s.census, [censusState.SAVED]);
|
||||
ok(getState().snapshots[0].census.display.inverted, "Snapshot is inverted");
|
||||
|
||||
result = getSnapshotTotals(getState().snapshots[0].census);
|
||||
equal(totalBytes, result.bytes,
|
||||
"getSnapshotTotals reuslted in correct bytes when inverted");
|
||||
equal(totalCount, result.count,
|
||||
"getSnapshotTotals reuslted in correct count when inverted");
|
||||
});
|
||||
|
||||
function aggregate(report) {
|
||||
let totalBytes = report.bytes;
|
||||
let totalCount = report.count;
|
||||
for (let child of (report.children || [])) {
|
||||
let { bytes, count } = aggregate(child);
|
||||
totalBytes += bytes;
|
||||
totalCount += count;
|
||||
}
|
||||
return { bytes: totalBytes, count: totalCount };
|
||||
}
|
||||
70
devtools/client/memory/test/unit/test_utils.js
Normal file
70
devtools/client/memory/test/unit/test_utils.js
Normal file
|
|
@ -0,0 +1,70 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
"use strict";
|
||||
|
||||
/**
|
||||
* Tests the task creator `takeSnapshotAndCensus()` for the whole flow of
|
||||
* taking a snapshot, and its sub-actions. Tests the formatNumber and
|
||||
* formatPercent methods.
|
||||
*/
|
||||
|
||||
let utils = require("devtools/client/memory/utils");
|
||||
let { snapshotState: states, viewState } = require("devtools/client/memory/constants");
|
||||
let { Preferences } = require("resource://gre/modules/Preferences.jsm");
|
||||
|
||||
function run_test() {
|
||||
run_next_test();
|
||||
}
|
||||
|
||||
add_task(function* () {
|
||||
let s1 = utils.createSnapshot({ view: { state: viewState.CENSUS } });
|
||||
let s2 = utils.createSnapshot({ view: { state: viewState.CENSUS } });
|
||||
equal(s1.state, states.SAVING, "utils.createSnapshot() creates snapshot in saving state");
|
||||
ok(s1.id !== s2.id, "utils.createSnapshot() creates snapshot with unique ids");
|
||||
|
||||
let custom = { by: "internalType", then: { by: "count", bytes: true }};
|
||||
Preferences.set("devtools.memory.custom-census-displays", JSON.stringify({ "My Display": custom }));
|
||||
|
||||
equal(utils.getCustomCensusDisplays()["My Display"].by, custom.by,
|
||||
"utils.getCustomCensusDisplays() returns custom displays");
|
||||
|
||||
ok(true, "test formatNumber util functions");
|
||||
equal(utils.formatNumber(12), "12", "formatNumber returns 12 for 12");
|
||||
|
||||
equal(utils.formatNumber(0), "0", "formatNumber returns 0 for 0");
|
||||
equal(utils.formatNumber(-0), "0", "formatNumber returns 0 for -0");
|
||||
equal(utils.formatNumber(+0), "0", "formatNumber returns 0 for +0");
|
||||
|
||||
equal(utils.formatNumber(1234567), "1 234 567",
|
||||
"formatNumber adds a space every 3rd digit");
|
||||
equal(utils.formatNumber(12345678), "12 345 678",
|
||||
"formatNumber adds a space every 3rd digit");
|
||||
equal(utils.formatNumber(123456789), "123 456 789",
|
||||
"formatNumber adds a space every 3rd digit");
|
||||
|
||||
equal(utils.formatNumber(12, true), "+12",
|
||||
"formatNumber can display number sign");
|
||||
equal(utils.formatNumber(-12, true), "-12",
|
||||
"formatNumber can display number sign (negative)");
|
||||
|
||||
ok(true, "test formatPercent util functions");
|
||||
equal(utils.formatPercent(12), "12%", "formatPercent returns 12% for 12");
|
||||
equal(utils.formatPercent(12345), "12 345%",
|
||||
"formatPercent returns 12 345% for 12345");
|
||||
|
||||
equal(utils.formatAbbreviatedBytes(12), "12B", "Formats bytes");
|
||||
equal(utils.formatAbbreviatedBytes(12345), "12KiB", "Formats kilobytes");
|
||||
equal(utils.formatAbbreviatedBytes(12345678), "11MiB", "Formats megabytes");
|
||||
equal(utils.formatAbbreviatedBytes(12345678912), "11GiB", "Formats gigabytes");
|
||||
|
||||
equal(utils.hslToStyle(0.5, 0.6, 0.7),
|
||||
"hsl(180,60%,70%)", "hslToStyle converts an array to a style string");
|
||||
equal(utils.hslToStyle(0, 0, 0),
|
||||
"hsl(0,0%,0%)", "hslToStyle converts an array to a style string");
|
||||
equal(utils.hslToStyle(1, 1, 1),
|
||||
"hsl(360,100%,100%)", "hslToStyle converts an array to a style string");
|
||||
|
||||
equal(utils.lerp(5, 7, 0), 5, "lerp return first number for 0");
|
||||
equal(utils.lerp(5, 7, 1), 7, "lerp return second number for 1");
|
||||
equal(utils.lerp(5, 7, 0.5), 6, "lerp interpolates the numbers for 0.5");
|
||||
});
|
||||
56
devtools/client/memory/test/unit/xpcshell.ini
Normal file
56
devtools/client/memory/test/unit/xpcshell.ini
Normal file
|
|
@ -0,0 +1,56 @@
|
|||
[DEFAULT]
|
||||
tags = devtools devtools-memory
|
||||
head = head.js ../../../framework/test/shared-redux-head.js
|
||||
tail =
|
||||
firefox-appdir = browser
|
||||
skip-if = toolkit == 'android'
|
||||
|
||||
[test_action_diffing_01.js]
|
||||
[test_action_diffing_02.js]
|
||||
[test_action_diffing_03.js]
|
||||
[test_action_diffing_04.js]
|
||||
[test_action_diffing_05.js]
|
||||
[test_action-clear-snapshots_01.js]
|
||||
[test_action-clear-snapshots_02.js]
|
||||
[test_action-clear-snapshots_03.js]
|
||||
[test_action-clear-snapshots_04.js]
|
||||
[test_action-clear-snapshots_05.js]
|
||||
[test_action-clear-snapshots_06.js]
|
||||
[test_action-export-snapshot.js]
|
||||
[test_action-filter-01.js]
|
||||
[test_action-filter-02.js]
|
||||
[test_action-filter-03.js]
|
||||
[test_action-import-snapshot-and-census.js]
|
||||
[test_action-import-snapshot-dominator-tree.js]
|
||||
[test_action-select-snapshot.js]
|
||||
[test_action-set-display.js]
|
||||
[test_action-set-display-and-refresh-01.js]
|
||||
[test_action-set-display-and-refresh-02.js]
|
||||
[test_action-take-census.js]
|
||||
[test_action-take-snapshot.js]
|
||||
[test_action-take-snapshot-and-census.js]
|
||||
[test_action-toggle-inverted.js]
|
||||
[test_action-toggle-inverted-and-refresh-01.js]
|
||||
[test_action-toggle-inverted-and-refresh-02.js]
|
||||
[test_action-toggle-recording-allocations.js]
|
||||
[test_dominator_trees_01.js]
|
||||
[test_dominator_trees_02.js]
|
||||
[test_dominator_trees_03.js]
|
||||
[test_dominator_trees_04.js]
|
||||
[test_dominator_trees_05.js]
|
||||
[test_dominator_trees_06.js]
|
||||
[test_dominator_trees_07.js]
|
||||
[test_dominator_trees_08.js]
|
||||
[test_dominator_trees_09.js]
|
||||
[test_dominator_trees_10.js]
|
||||
[test_individuals_01.js]
|
||||
[test_individuals_02.js]
|
||||
[test_individuals_03.js]
|
||||
[test_individuals_04.js]
|
||||
[test_individuals_05.js]
|
||||
[test_individuals_06.js]
|
||||
[test_pop_view_01.js]
|
||||
[test_tree-map-01.js]
|
||||
[test_tree-map-02.js]
|
||||
[test_utils.js]
|
||||
[test_utils-get-snapshot-totals.js]
|
||||
Loading…
Add table
Add a link
Reference in a new issue