head.js (7198B)
1 /* Any copyright is dedicated to the Public Domain. 2 http://creativecommons.org/publicdomain/zero/1.0/ */ 3 4 "use strict"; 5 6 // Load the shared test helpers into this compartment. 7 Services.scriptloader.loadSubScript( 8 "chrome://mochitests/content/browser/devtools/client/shared/test/shared-head.js", 9 this 10 ); 11 12 var { 13 censusDisplays, 14 censusState, 15 snapshotState: states, 16 } = require("resource://devtools/client/memory/constants.js"); 17 var { L10N } = require("resource://devtools/client/memory/utils.js"); 18 19 Services.prefs.setBoolPref("devtools.memory.enabled", true); 20 21 /** 22 * Open the memory panel for the given tab. 23 */ 24 this.openMemoryPanel = async function (tab) { 25 info("Opening memory panel."); 26 const toolbox = await gDevTools.showToolboxForTab(tab, { toolId: "memory" }); 27 info("Memory panel shown successfully."); 28 const panel = toolbox.getCurrentPanel(); 29 return { tab, panel }; 30 }; 31 32 /** 33 * Close the memory panel for the given tab. 34 */ 35 this.closeMemoryPanel = async function (tab) { 36 info("Closing memory panel."); 37 const toolbox = gDevTools.getToolboxForTab(tab); 38 await toolbox.destroy(); 39 info("Closed memory panel successfully."); 40 }; 41 42 /** 43 * Return a test function that adds a tab with the given url, opens the memory 44 * panel, runs the given generator, closes the memory panel, removes the tab, 45 * and finishes. 46 * 47 * Example usage: 48 * 49 * this.test = makeMemoryTest(TEST_URL, async function ({ tab, panel }) { 50 * // Your tests go here... 51 * }); 52 */ 53 function makeMemoryTest(url, generator) { 54 return async function () { 55 waitForExplicitFinish(); 56 57 // It can take a long time to save a snapshot to disk, read the snapshots 58 // back from disk, and finally perform analyses on them. 59 requestLongerTimeout(2); 60 61 const tab = await addTab(url); 62 const results = await openMemoryPanel(tab); 63 64 try { 65 await generator(results); 66 } catch (err) { 67 ok(false, "Got an error: " + DevToolsUtils.safeErrorString(err)); 68 } 69 70 await closeMemoryPanel(tab); 71 await removeTab(tab); 72 73 finish(); 74 }; 75 } 76 77 function dumpn(msg) { 78 dump(`MEMORY-TEST: ${msg}\n`); 79 } 80 81 /** 82 * Returns a promise that will resolve when the provided store matches 83 * the expected array. expectedStates is an array of dominatorTree states. 84 * Expectations : 85 * - store.getState().snapshots.length == expected.length 86 * - snapshots[i].dominatorTree.state == expected[i] 87 * 88 * @param {Store} store 89 * @param {Array<string>} expectedStates [description] 90 * @return {Promise} 91 */ 92 function waitUntilDominatorTreeState(store, expected) { 93 const predicate = () => { 94 const snapshots = store.getState().snapshots; 95 return ( 96 snapshots.length === expected.length && 97 expected.every((state, i) => { 98 return ( 99 snapshots[i].dominatorTree && 100 snapshots[i].dominatorTree.state === state 101 ); 102 }) 103 ); 104 }; 105 info(`Waiting for dominator trees to be of state: ${expected}`); 106 return waitUntilState(store, predicate); 107 } 108 109 function takeSnapshot(window) { 110 const { gStore, document } = window; 111 const snapshotCount = gStore.getState().snapshots.length; 112 info("Taking snapshot..."); 113 document.querySelector(".devtools-toolbar .take-snapshot").click(); 114 return waitUntilState( 115 gStore, 116 () => gStore.getState().snapshots.length === snapshotCount + 1 117 ); 118 } 119 120 function clearSnapshots(window) { 121 const { gStore, document } = window; 122 document.querySelector(".devtools-toolbar .clear-snapshots").click(); 123 return waitUntilState(gStore, () => 124 gStore 125 .getState() 126 .snapshots.every(snapshot => snapshot.state !== states.READ) 127 ); 128 } 129 130 /** 131 * Sets the current requested display and waits for the selected snapshot to use 132 * it and complete the new census that entails. 133 */ 134 function setCensusDisplay(window, display) { 135 info(`Setting census display to ${display}...`); 136 const { gStore, gHeapAnalysesClient } = window; 137 // XXX: Should handle this via clicking the DOM, but React doesn't 138 // fire the onChange event, so just change it in the store. 139 // window.document.querySelector(`.select-display`).value = type; 140 gStore.dispatch( 141 require("resource://devtools/client/memory/actions/census-display.js").setCensusDisplayAndRefresh( 142 gHeapAnalysesClient, 143 display 144 ) 145 ); 146 147 return waitUntilState(window.gStore, () => { 148 const selected = window.gStore.getState().snapshots.find(s => s.selected); 149 return ( 150 selected.state === states.READ && 151 selected.census && 152 selected.census.state === censusState.SAVED && 153 selected.census.display === display 154 ); 155 }); 156 } 157 158 /** 159 * Get the snapshot tatus text currently displayed, or null if none is 160 * displayed. 161 * 162 * @param {Document} document 163 */ 164 function getDisplayedSnapshotStatus(document) { 165 const status = document.querySelector(".snapshot-status"); 166 return status ? status.textContent.trim() : null; 167 } 168 169 /** 170 * Get the index of the currently selected snapshot. 171 * 172 * @return {number} 173 */ 174 function getSelectedSnapshotIndex(store) { 175 const snapshots = store.getState().snapshots; 176 const selectedSnapshot = snapshots.find(s => s.selected); 177 return snapshots.indexOf(selectedSnapshot); 178 } 179 180 /** 181 * Returns a promise that will resolve when the snapshot with provided index 182 * becomes selected. 183 * 184 * @return {Promise} 185 */ 186 function waitUntilSnapshotSelected(store, snapshotIndex) { 187 return waitUntilState( 188 store, 189 state => 190 state.snapshots[snapshotIndex] && 191 state.snapshots[snapshotIndex].selected === true 192 ); 193 } 194 195 /** 196 * Wait until the state has censuses in a certain state. 197 * 198 * @return {Promise} 199 */ 200 function waitUntilCensusState(store, getCensus, expected) { 201 const predicate = () => { 202 const snapshots = store.getState().snapshots; 203 204 info( 205 "Current census state:" + 206 snapshots.map(x => (getCensus(x) ? getCensus(x).state : null)) 207 ); 208 209 return ( 210 snapshots.length === expected.length && 211 expected.every((state, i) => { 212 const census = getCensus(snapshots[i]); 213 return ( 214 state === "*" || 215 (!census && !state) || 216 (census && census.state === state) 217 ); 218 }) 219 ); 220 }; 221 info(`Waiting for snapshot censuses to be of state: ${expected}`); 222 return waitUntilState(store, predicate); 223 } 224 225 /** 226 * Mock out the requestAnimationFrame. 227 * 228 * @return {object} 229 * @function nextFrame 230 * Call the last queued function 231 * @function raf 232 * The mocked raf function 233 * @function timesCalled 234 * How many times the RAF has been called 235 */ 236 function createRAFMock() { 237 let queuedFns = []; 238 const mock = { timesCalled: 0 }; 239 240 mock.nextFrame = function () { 241 const thisQueue = queuedFns; 242 queuedFns = []; 243 for (let i = 0; i < thisQueue.length; i++) { 244 thisQueue[i](); 245 } 246 }; 247 248 mock.raf = function (fn) { 249 mock.timesCalled++; 250 queuedFns.push(fn); 251 }; 252 return mock; 253 } 254 255 /** 256 * Test to see if two floats are equivalent. 257 * 258 * @param {Float} a 259 * @param {Float} b 260 * @return {boolean} 261 */ 262 function floatEquality(a, b) { 263 const EPSILON = 0.00000000001; 264 const equals = Math.abs(a - b) < EPSILON; 265 if (!equals) { 266 info(`${a} not equal to ${b}`); 267 } 268 return equals; 269 }