1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
|
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
// via xpcshell.ini
/* import-globals-from ../../../shared/test/shared-head.js */
Services.prefs.setBoolPref("devtools.testing", true);
Services.prefs.setBoolPref("devtools.debugger.log", true);
registerCleanupFunction(() => {
Services.prefs.clearUserPref("devtools.testing");
Services.prefs.clearUserPref("devtools.debugger.log");
});
var { FileUtils } = ChromeUtils.importESModule(
"resource://gre/modules/FileUtils.sys.mjs"
);
var { expectState } = require("resource://devtools/server/actors/common.js");
var HeapSnapshotFileUtils = require("resource://devtools/shared/heapsnapshot/HeapSnapshotFileUtils.js");
var HeapAnalysesClient = require("resource://devtools/shared/heapsnapshot/HeapAnalysesClient.js");
var { addDebuggerToGlobal } = ChromeUtils.importESModule(
"resource://gre/modules/jsdebugger.sys.mjs"
);
var Store = require("resource://devtools/client/memory/store.js");
var { L10N } = require("resource://devtools/client/memory/utils.js");
var SYSTEM_PRINCIPAL = Cc["@mozilla.org/systemprincipal;1"].createInstance(
Ci.nsIPrincipal
);
var EXPECTED_DTU_ASSERT_FAILURE_COUNT = 0;
registerCleanupFunction(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() {
const global = new Cu.Sandbox(SYSTEM_PRINCIPAL, { freshZone: true });
addDebuggerToGlobal(global);
return new global.Debugger();
}
function StubbedMemoryFront() {
this.state = "detached";
this.dbg = initDebugger();
}
StubbedMemoryFront.prototype.attach = async function () {
this.state = "attached";
};
StubbedMemoryFront.prototype.detach = async function () {
this.state = "detached";
};
StubbedMemoryFront.prototype.saveHeapSnapshot = expectState(
"attached",
async function () {
return ChromeUtils.saveHeapSnapshot({ runtime: true });
},
"saveHeapSnapshot"
);
StubbedMemoryFront.prototype.startRecordingAllocations = expectState(
"attached",
async function () {}
);
StubbedMemoryFront.prototype.stopRecordingAllocations = expectState(
"attached",
async function () {}
);
function waitUntilSnapshotState(store, expected) {
const predicate = () => {
const snapshots = store.getState().snapshots;
info(snapshots.map(x => x.state));
return (
snapshots.length === expected.length &&
expected.every(
(state, i) => state === "*" || snapshots[i].state === state
)
);
};
info(`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 (const child of node.children) {
const found = findReportLeafIndex(child);
if (found) {
return found;
}
}
}
return null;
}
function waitUntilCensusState(store, getCensus, expected) {
const predicate = () => {
const snapshots = store.getState().snapshots;
info(
"Current census state:" +
snapshots.map(x => (getCensus(x) ? getCensus(x).state : null))
);
return (
snapshots.length === expected.length &&
expected.every((state, i) => {
const census = getCensus(snapshots[i]);
return (
state === "*" ||
(!census && !state) ||
(census && census.state === state)
);
})
);
};
info(`Waiting for snapshots' censuses to be of state: ${expected}`);
return waitUntilState(store, predicate);
}
async function createTempFile() {
const file = new FileUtils.File(
PathUtils.join(PathUtils.tempDir, "tmp.fxsnapshot")
);
file.createUnique(Ci.nsIFile.NORMAL_FILE_TYPE, FileUtils.PERMS_FILE);
const destPath = file.path;
const stat = await IOUtils.stat(destPath);
Assert.strictEqual(stat.size, 0, "new file is 0 bytes at start");
return destPath;
}
// This is a copy of the same method from shared-head.js as
// xpcshell test aren't using shared-head.js
/**
* Wait for a specific action type to be dispatched.
*
* If the action is async and defines a `status` property, this helper will wait
* for the status to reach either "error" or "done".
*
* @param {Object} store
* Redux store where the action should be dispatched.
* @param {String} actionType
* The actionType to wait for.
* @param {Number} repeat
* Optional, number of time the action is expected to be dispatched.
* Defaults to 1
* @return {Promise}
*/
function waitForDispatch(store, actionType, repeat = 1) {
let count = 0;
return new Promise(resolve => {
store.dispatch({
type: "@@service/waitUntil",
predicate: action => {
const isDone =
!action.status ||
action.status === "done" ||
action.status === "error";
if (action.type === actionType && isDone && ++count == repeat) {
return true;
}
return false;
},
run: (dispatch, getState, action) => {
resolve(action);
},
});
});
}
|