import FIREFOX_52_6_0esr_RELEASE from mozilla-esr52 hg repo

This commit is contained in:
Roy Tam 2018-01-19 03:59:58 +08:00
commit dcd9973243
150858 changed files with 23884658 additions and 0 deletions

View file

@ -0,0 +1,6 @@
"use strict";
module.exports = {
// Extend from the shared list of defined globals for mochitests.
"extends": "../../../../.eslintrc.xpcshell.js"
};

View 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;
}

View file

@ -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();
});

View file

@ -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();
});

View file

@ -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();
});

View file

@ -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();
});

View file

@ -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();
});

View file

@ -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();
});

View file

@ -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();
});

View 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");
});

View 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();
});

View 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();
});

View file

@ -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;
}
});

View file

@ -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;
}

View file

@ -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; }

View file

@ -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();
});

View file

@ -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");
});

View 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");
});

View 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");
});

View file

@ -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");
});

View file

@ -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();
});

View file

@ -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();
});

View file

@ -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();
});

View file

@ -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");
});

View file

@ -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();
});

View 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();
});

View 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();
});

View 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();
});

View 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();
});

View 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();
});

View 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();
});

View 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();
});

View 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();
});

View 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();
});

View 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();
});

View 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();
});

View 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();
});

View 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();
});

View 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();
});

View 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();
});

View 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();
});

View 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();
});

View 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();
});

View 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();
});

View 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();
});

View 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();
});

View 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();
});

View 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");
});

View 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");
});

View file

@ -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 };
}

View 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");
});

View 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]