1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/toolkit/devtools/server/tests/unit/head_dbg.js Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,362 @@ 1.4 +/* Any copyright is dedicated to the Public Domain. 1.5 + http://creativecommons.org/publicdomain/zero/1.0/ */ 1.6 + 1.7 +"use strict"; 1.8 +const Cc = Components.classes; 1.9 +const Ci = Components.interfaces; 1.10 +const Cu = Components.utils; 1.11 +const Cr = Components.results; 1.12 + 1.13 +const { devtools } = Cu.import("resource://gre/modules/devtools/Loader.jsm", {}); 1.14 +const Services = devtools.require("Services"); 1.15 +const { ActorPool, createExtraActors, appendExtraActors } = devtools.require("devtools/server/actors/common"); 1.16 +const DevToolsUtils = devtools.require("devtools/toolkit/DevToolsUtils.js"); 1.17 +const {Promise: promise} = Cu.import("resource://gre/modules/Promise.jsm", {}); 1.18 + 1.19 +// Always log packets when running tests. runxpcshelltests.py will throw 1.20 +// the output away anyway, unless you give it the --verbose flag. 1.21 +Services.prefs.setBoolPref("devtools.debugger.log", true); 1.22 +// Enable remote debugging for the relevant tests. 1.23 +Services.prefs.setBoolPref("devtools.debugger.remote-enabled", true); 1.24 + 1.25 +function tryImport(url) { 1.26 + try { 1.27 + Cu.import(url); 1.28 + } catch (e) { 1.29 + dump("Error importing " + url + "\n"); 1.30 + dump(DevToolsUtils.safeErrorString(e) + "\n"); 1.31 + throw e; 1.32 + } 1.33 +} 1.34 + 1.35 +tryImport("resource://gre/modules/devtools/dbg-server.jsm"); 1.36 +tryImport("resource://gre/modules/devtools/dbg-client.jsm"); 1.37 +tryImport("resource://gre/modules/devtools/Loader.jsm"); 1.38 +tryImport("resource://gre/modules/devtools/Console.jsm"); 1.39 + 1.40 +function testExceptionHook(ex) { 1.41 + try { 1.42 + do_report_unexpected_exception(ex); 1.43 + } catch(ex) { 1.44 + return {throw: ex} 1.45 + } 1.46 + return undefined; 1.47 +} 1.48 + 1.49 +// Convert an nsIScriptError 'aFlags' value into an appropriate string. 1.50 +function scriptErrorFlagsToKind(aFlags) { 1.51 + var kind; 1.52 + if (aFlags & Ci.nsIScriptError.warningFlag) 1.53 + kind = "warning"; 1.54 + if (aFlags & Ci.nsIScriptError.exceptionFlag) 1.55 + kind = "exception"; 1.56 + else 1.57 + kind = "error"; 1.58 + 1.59 + if (aFlags & Ci.nsIScriptError.strictFlag) 1.60 + kind = "strict " + kind; 1.61 + 1.62 + return kind; 1.63 +} 1.64 + 1.65 +// Redeclare dbg_assert with a fatal behavior. 1.66 +function dbg_assert(cond, e) { 1.67 + if (!cond) { 1.68 + throw e; 1.69 + } 1.70 +} 1.71 + 1.72 +// Register a console listener, so console messages don't just disappear 1.73 +// into the ether. 1.74 +let errorCount = 0; 1.75 +let listener = { 1.76 + observe: function (aMessage) { 1.77 + errorCount++; 1.78 + try { 1.79 + // If we've been given an nsIScriptError, then we can print out 1.80 + // something nicely formatted, for tools like Emacs to pick up. 1.81 + var scriptError = aMessage.QueryInterface(Ci.nsIScriptError); 1.82 + dump(aMessage.sourceName + ":" + aMessage.lineNumber + ": " + 1.83 + scriptErrorFlagsToKind(aMessage.flags) + ": " + 1.84 + aMessage.errorMessage + "\n"); 1.85 + var string = aMessage.errorMessage; 1.86 + } catch (x) { 1.87 + // Be a little paranoid with message, as the whole goal here is to lose 1.88 + // no information. 1.89 + try { 1.90 + var string = "" + aMessage.message; 1.91 + } catch (x) { 1.92 + var string = "<error converting error message to string>"; 1.93 + } 1.94 + } 1.95 + 1.96 + // Make sure we exit all nested event loops so that the test can finish. 1.97 + while (DebuggerServer.xpcInspector.eventLoopNestLevel > 0) { 1.98 + DebuggerServer.xpcInspector.exitNestedEventLoop(); 1.99 + } 1.100 + do_throw("head_dbg.js got console message: " + string + "\n"); 1.101 + } 1.102 +}; 1.103 + 1.104 +let consoleService = Cc["@mozilla.org/consoleservice;1"].getService(Ci.nsIConsoleService); 1.105 +consoleService.registerListener(listener); 1.106 + 1.107 +function check_except(func) 1.108 +{ 1.109 + try { 1.110 + func(); 1.111 + } catch (e) { 1.112 + do_check_true(true); 1.113 + return; 1.114 + } 1.115 + dump("Should have thrown an exception: " + func.toString()); 1.116 + do_check_true(false); 1.117 +} 1.118 + 1.119 +function testGlobal(aName) { 1.120 + let systemPrincipal = Cc["@mozilla.org/systemprincipal;1"] 1.121 + .createInstance(Ci.nsIPrincipal); 1.122 + 1.123 + let sandbox = Cu.Sandbox(systemPrincipal); 1.124 + sandbox.__name = aName; 1.125 + return sandbox; 1.126 +} 1.127 + 1.128 +function addTestGlobal(aName) 1.129 +{ 1.130 + let global = testGlobal(aName); 1.131 + DebuggerServer.addTestGlobal(global); 1.132 + return global; 1.133 +} 1.134 + 1.135 +// List the DebuggerClient |aClient|'s tabs, look for one whose title is 1.136 +// |aTitle|, and apply |aCallback| to the packet's entry for that tab. 1.137 +function getTestTab(aClient, aTitle, aCallback) { 1.138 + aClient.listTabs(function (aResponse) { 1.139 + for (let tab of aResponse.tabs) { 1.140 + if (tab.title === aTitle) { 1.141 + aCallback(tab); 1.142 + return; 1.143 + } 1.144 + } 1.145 + aCallback(null); 1.146 + }); 1.147 +} 1.148 + 1.149 +// Attach to |aClient|'s tab whose title is |aTitle|; pass |aCallback| the 1.150 +// response packet and a TabClient instance referring to that tab. 1.151 +function attachTestTab(aClient, aTitle, aCallback) { 1.152 + getTestTab(aClient, aTitle, function (aTab) { 1.153 + aClient.attachTab(aTab.actor, aCallback); 1.154 + }); 1.155 +} 1.156 + 1.157 +// Attach to |aClient|'s tab whose title is |aTitle|, and then attach to 1.158 +// that tab's thread. Pass |aCallback| the thread attach response packet, a 1.159 +// TabClient referring to the tab, and a ThreadClient referring to the 1.160 +// thread. 1.161 +function attachTestThread(aClient, aTitle, aCallback) { 1.162 + attachTestTab(aClient, aTitle, function (aResponse, aTabClient) { 1.163 + function onAttach(aResponse, aThreadClient) { 1.164 + aCallback(aResponse, aTabClient, aThreadClient); 1.165 + } 1.166 + aTabClient.attachThread({ useSourceMaps: true }, onAttach); 1.167 + }); 1.168 +} 1.169 + 1.170 +// Attach to |aClient|'s tab whose title is |aTitle|, attach to the tab's 1.171 +// thread, and then resume it. Pass |aCallback| the thread's response to 1.172 +// the 'resume' packet, a TabClient for the tab, and a ThreadClient for the 1.173 +// thread. 1.174 +function attachTestTabAndResume(aClient, aTitle, aCallback) { 1.175 + attachTestThread(aClient, aTitle, function(aResponse, aTabClient, aThreadClient) { 1.176 + aThreadClient.resume(function (aResponse) { 1.177 + aCallback(aResponse, aTabClient, aThreadClient); 1.178 + }); 1.179 + }); 1.180 +} 1.181 + 1.182 +/** 1.183 + * Initialize the testing debugger server. 1.184 + */ 1.185 +function initTestDebuggerServer() 1.186 +{ 1.187 + DebuggerServer.addActors("resource://gre/modules/devtools/server/actors/root.js"); 1.188 + DebuggerServer.addActors("resource://gre/modules/devtools/server/actors/script.js"); 1.189 + DebuggerServer.addActors("resource://test/testactors.js"); 1.190 + // Allow incoming connections. 1.191 + DebuggerServer.init(function () { return true; }); 1.192 +} 1.193 + 1.194 +function initTestTracerServer() 1.195 +{ 1.196 + DebuggerServer.addActors("resource://gre/modules/devtools/server/actors/root.js"); 1.197 + DebuggerServer.addActors("resource://gre/modules/devtools/server/actors/script.js"); 1.198 + DebuggerServer.addActors("resource://test/testactors.js"); 1.199 + DebuggerServer.registerModule("devtools/server/actors/tracer"); 1.200 + // Allow incoming connections. 1.201 + DebuggerServer.init(function () { return true; }); 1.202 +} 1.203 + 1.204 +function finishClient(aClient) 1.205 +{ 1.206 + aClient.close(function() { 1.207 + do_test_finished(); 1.208 + }); 1.209 +} 1.210 + 1.211 +/** 1.212 + * Takes a relative file path and returns the absolute file url for it. 1.213 + */ 1.214 +function getFileUrl(aName, aAllowMissing=false) { 1.215 + let file = do_get_file(aName, aAllowMissing); 1.216 + return Services.io.newFileURI(file).spec; 1.217 +} 1.218 + 1.219 +/** 1.220 + * Returns the full path of the file with the specified name in a 1.221 + * platform-independent and URL-like form. 1.222 + */ 1.223 +function getFilePath(aName, aAllowMissing=false) 1.224 +{ 1.225 + let file = do_get_file(aName, aAllowMissing); 1.226 + let path = Services.io.newFileURI(file).spec; 1.227 + let filePrePath = "file://"; 1.228 + if ("nsILocalFileWin" in Ci && 1.229 + file instanceof Ci.nsILocalFileWin) { 1.230 + filePrePath += "/"; 1.231 + } 1.232 + return path.slice(filePrePath.length); 1.233 +} 1.234 + 1.235 +Cu.import("resource://gre/modules/NetUtil.jsm"); 1.236 + 1.237 +/** 1.238 + * Returns the full text contents of the given file. 1.239 + */ 1.240 +function readFile(aFileName) { 1.241 + let f = do_get_file(aFileName); 1.242 + let s = Cc["@mozilla.org/network/file-input-stream;1"] 1.243 + .createInstance(Ci.nsIFileInputStream); 1.244 + s.init(f, -1, -1, false); 1.245 + try { 1.246 + return NetUtil.readInputStreamToString(s, s.available()); 1.247 + } finally { 1.248 + s.close(); 1.249 + } 1.250 +} 1.251 + 1.252 +function writeFile(aFileName, aContent) { 1.253 + let file = do_get_file(aFileName, true); 1.254 + let stream = Cc["@mozilla.org/network/file-output-stream;1"] 1.255 + .createInstance(Ci.nsIFileOutputStream); 1.256 + stream.init(file, -1, -1, 0); 1.257 + try { 1.258 + do { 1.259 + let numWritten = stream.write(aContent, aContent.length); 1.260 + aContent = aContent.slice(numWritten); 1.261 + } while (aContent.length > 0); 1.262 + } finally { 1.263 + stream.close(); 1.264 + } 1.265 +} 1.266 + 1.267 +function connectPipeTracing() { 1.268 + return new TracingTransport(DebuggerServer.connectPipe()); 1.269 +} 1.270 + 1.271 +function TracingTransport(childTransport) { 1.272 + this.hooks = null; 1.273 + this.child = childTransport; 1.274 + this.child.hooks = this; 1.275 + 1.276 + this.expectations = []; 1.277 + this.packets = []; 1.278 + this.checkIndex = 0; 1.279 +} 1.280 + 1.281 +function deepEqual(a, b) { 1.282 + if (a === b) 1.283 + return true; 1.284 + if (typeof a != "object" || typeof b != "object") 1.285 + return false; 1.286 + if (a === null || b === null) 1.287 + return false; 1.288 + if (Object.keys(a).length != Object.keys(b).length) 1.289 + return false; 1.290 + for (let k in a) { 1.291 + if (!deepEqual(a[k], b[k])) 1.292 + return false; 1.293 + } 1.294 + return true; 1.295 +} 1.296 + 1.297 +TracingTransport.prototype = { 1.298 + // Remove actor names 1.299 + normalize: function(packet) { 1.300 + return JSON.parse(JSON.stringify(packet, (key, value) => { 1.301 + if (key === "to" || key === "from" || key === "actor") { 1.302 + return "<actorid>"; 1.303 + } 1.304 + return value; 1.305 + })); 1.306 + }, 1.307 + send: function(packet) { 1.308 + this.packets.push({ 1.309 + type: "sent", 1.310 + packet: this.normalize(packet) 1.311 + }); 1.312 + return this.child.send(packet); 1.313 + }, 1.314 + close: function() { 1.315 + return this.child.close(); 1.316 + }, 1.317 + ready: function() { 1.318 + return this.child.ready(); 1.319 + }, 1.320 + onPacket: function(packet) { 1.321 + this.packets.push({ 1.322 + type: "received", 1.323 + packet: this.normalize(packet) 1.324 + }); 1.325 + this.hooks.onPacket(packet); 1.326 + }, 1.327 + onClosed: function() { 1.328 + this.hooks.onClosed(); 1.329 + }, 1.330 + 1.331 + expectSend: function(expected) { 1.332 + let packet = this.packets[this.checkIndex++]; 1.333 + do_check_eq(packet.type, "sent"); 1.334 + do_check_true(deepEqual(packet.packet, this.normalize(expected))); 1.335 + }, 1.336 + 1.337 + expectReceive: function(expected) { 1.338 + let packet = this.packets[this.checkIndex++]; 1.339 + do_check_eq(packet.type, "received"); 1.340 + do_check_true(deepEqual(packet.packet, this.normalize(expected))); 1.341 + }, 1.342 + 1.343 + // Write your tests, call dumpLog at the end, inspect the output, 1.344 + // then sprinkle the calls through the right places in your test. 1.345 + dumpLog: function() { 1.346 + for (let entry of this.packets) { 1.347 + if (entry.type === "sent") { 1.348 + dump("trace.expectSend(" + entry.packet + ");\n"); 1.349 + } else { 1.350 + dump("trace.expectReceive(" + entry.packet + ");\n"); 1.351 + } 1.352 + } 1.353 + } 1.354 +}; 1.355 + 1.356 +function StubTransport() { } 1.357 +StubTransport.prototype.ready = function () {}; 1.358 +StubTransport.prototype.send = function () {}; 1.359 +StubTransport.prototype.close = function () {}; 1.360 + 1.361 +function executeSoon(aFunc) { 1.362 + Services.tm.mainThread.dispatch({ 1.363 + run: DevToolsUtils.makeInfallible(aFunc) 1.364 + }, Ci.nsIThread.DISPATCH_NORMAL); 1.365 +}