browser_dbg-backgroundtask-debugging.js (5997B)
1 /* This Source Code Form is subject to the terms of the Mozilla Public 2 * License, v. 2.0. If a copy of the MPL was not distributed with this 3 * file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */ 4 5 /** 6 * Tests that `--backgroundtask` debugging works. 7 * 8 * This test is subtle. We launch a `--backgroundtask` with `--jsdebugger` and 9 * `--wait-for-jsdebugger` within the test. The background task infrastructure 10 * launches a browser toolbox, and the test connects to that browser toolbox 11 * instance. The test drives the instance, verifying that the automatically 12 * placed breakpoint paused execution. It then closes the browser toolbox, 13 * which resumes the execution and the task exits. 14 * 15 * In the future, it would be nice to change the task's running environment, for 16 * example by redefining a failing exit code to exit code 0. Attempts to do 17 * this have so far not been robust in automation. 18 */ 19 20 "use strict"; 21 22 requestLongerTimeout(4); 23 24 Services.scriptloader.loadSubScript( 25 "chrome://mochitests/content/browser/devtools/client/framework/browser-toolbox/test/helpers-browser-toolbox.js", 26 this 27 ); 28 29 const { BackgroundTasksTestUtils } = ChromeUtils.importESModule( 30 "resource://testing-common/BackgroundTasksTestUtils.sys.mjs" 31 ); 32 BackgroundTasksTestUtils.init(this); 33 const do_backgroundtask = BackgroundTasksTestUtils.do_backgroundtask.bind( 34 BackgroundTasksTestUtils 35 ); 36 37 add_task(async function test_backgroundtask_debugger() { 38 // In this test, the background task infrastructure launches the browser 39 // toolbox. The browser toolbox profile prefs are taken from the default 40 // profile (which is the standard place for background tasks to look for 41 // task-specific configuration). The current test profile will be 42 // considered the default profile by the background task apparatus, so this 43 // is how we configure the browser toolbox profile prefs. 44 // 45 // These prefs are not set for the background task under test directly: the 46 // relevant prefs are set in the background task defaults. 47 await pushPref("devtools.chrome.enabled", true); 48 await pushPref("devtools.debugger.remote-enabled", true); 49 await pushPref("devtools.browsertoolbox.enable-test-server", true); 50 await pushPref("devtools.debugger.prompt-connection", false); 51 52 // Before we start the background task, the preference file must be flushed to disk. 53 Services.prefs.savePrefFile(null); 54 55 // This invokes the test-only background task `BackgroundTask_jsdebugger.jsm`. 56 const p = do_backgroundtask("jsdebugger", { 57 extraArgs: [`--jsdebugger`, "--wait-for-jsdebugger"], 58 extraEnv: { 59 // Force the current test profile to be considered the default profile. 60 MOZ_BACKGROUNDTASKS_DEFAULT_PROFILE_PATH: Services.dirsvc.get( 61 "ProfD", 62 Ci.nsIFile 63 ).path, 64 }, 65 }); 66 67 ok(true, "Launched background task"); 68 69 const existingProcessClose = async () => { 70 const exitCode = await p; 71 return { exitCode }; 72 }; 73 const ToolboxTask = await initBrowserToolboxTask({ existingProcessClose }); 74 75 await ToolboxTask.spawn(selectors, () => { 76 const { 77 LocalizationHelper, 78 } = require("resource://devtools/shared/l10n.js"); 79 // We have to expose this symbol as global for waitForSelectedSource 80 this.DEBUGGER_L10N = new LocalizationHelper( 81 "devtools/client/locales/debugger.properties" 82 ); 83 }); 84 85 await ToolboxTask.importFunctions({ 86 checkEvaluateInTopFrame, 87 evaluateInTopFrame, 88 createDebuggerContext, 89 expandAllScopes, 90 findElement, 91 findElementWithSelector, 92 getSelector, 93 getVisibleSelectedFrameLine, 94 isPaused, 95 resume, 96 stepOver, 97 toggleObjectInspectorNode, 98 toggleScopeNode, 99 waitForElement, 100 waitForLoadedScopes, 101 waitForPaused, 102 waitForResumed, 103 waitForSelectedSource, 104 //waitForElementWithSelector, 105 waitForInlinePreviews, 106 waitForState, 107 waitUntil, 108 createLocation, 109 getEditorContent, 110 getCMEditor, 111 log: (msg, data) => 112 console.log(`${msg} ${!data ? "" : JSON.stringify(data)}`), 113 info: (msg, data) => 114 console.info(`${msg} ${!data ? "" : JSON.stringify(data)}`), 115 }); 116 117 // ToolboxTask.spawn passes input arguments by stringify-ing them via string 118 // concatenation. But functions do not survive this process, so we manually 119 // recreate (in the toolbox process) the single function the `expandAllScopes` 120 // invocation in this test needs. 121 await ToolboxTask.spawn(selectors, async _selectors => { 122 this.selectors = _selectors; 123 this.selectors.scopeNode = i => 124 `.scopes-list .tree-node:nth-child(${i}) .object-label`; 125 }); 126 127 // The debugger should automatically be selected. 128 await ToolboxTask.spawn(null, async () => { 129 await waitUntil(() => gToolbox.currentToolId == "jsdebugger"); 130 }); 131 ok(true, "Debugger selected"); 132 133 // The debugger should automatically pause. 134 await ToolboxTask.spawn(null, async () => { 135 try { 136 /* global gToolbox */ 137 // Wait for the debugger to finish loading. 138 await gToolbox.getPanelWhenReady("jsdebugger"); 139 140 const dbg = createDebuggerContext(gToolbox); 141 142 // Scopes are supposed to be automatically expanded, but with 143 // `setBreakpointOnLoad` that doesn't seem to be happening. Explicitly 144 // expand scopes so that they are expanded for `waitForPaused`. 145 await expandAllScopes(dbg); 146 147 await waitForPaused(dbg); 148 149 if (!gToolbox.isHighlighted("jsdebugger")) { 150 throw new Error("Debugger not highlighted"); 151 } 152 } catch (e) { 153 console.log("Caught exception in spawn", e); 154 throw e; 155 } 156 }); 157 ok(true, "Paused in backgroundtask script"); 158 159 // If we `resume`, then the task completes and the Browser Toolbox exits, 160 // which isn't handled cleanly by `spawn`, resulting in a test time out. This 161 // closes the toolbox "from the inside", which continues execution. The test 162 // then waits for the background task to terminate with exit code 0. 163 await ToolboxTask.destroy(); 164 });