toolkit/devtools/server/tests/unit/head_dbg.js

branch
TOR_BUG_3246
changeset 7
129ffea94266
equal deleted inserted replaced
-1:000000000000 0:48d98eba5778
1 /* Any copyright is dedicated to the Public Domain.
2 http://creativecommons.org/publicdomain/zero/1.0/ */
3
4 "use strict";
5 const Cc = Components.classes;
6 const Ci = Components.interfaces;
7 const Cu = Components.utils;
8 const Cr = Components.results;
9
10 const { devtools } = Cu.import("resource://gre/modules/devtools/Loader.jsm", {});
11 const Services = devtools.require("Services");
12 const { ActorPool, createExtraActors, appendExtraActors } = devtools.require("devtools/server/actors/common");
13 const DevToolsUtils = devtools.require("devtools/toolkit/DevToolsUtils.js");
14 const {Promise: promise} = Cu.import("resource://gre/modules/Promise.jsm", {});
15
16 // Always log packets when running tests. runxpcshelltests.py will throw
17 // the output away anyway, unless you give it the --verbose flag.
18 Services.prefs.setBoolPref("devtools.debugger.log", true);
19 // Enable remote debugging for the relevant tests.
20 Services.prefs.setBoolPref("devtools.debugger.remote-enabled", true);
21
22 function tryImport(url) {
23 try {
24 Cu.import(url);
25 } catch (e) {
26 dump("Error importing " + url + "\n");
27 dump(DevToolsUtils.safeErrorString(e) + "\n");
28 throw e;
29 }
30 }
31
32 tryImport("resource://gre/modules/devtools/dbg-server.jsm");
33 tryImport("resource://gre/modules/devtools/dbg-client.jsm");
34 tryImport("resource://gre/modules/devtools/Loader.jsm");
35 tryImport("resource://gre/modules/devtools/Console.jsm");
36
37 function testExceptionHook(ex) {
38 try {
39 do_report_unexpected_exception(ex);
40 } catch(ex) {
41 return {throw: ex}
42 }
43 return undefined;
44 }
45
46 // Convert an nsIScriptError 'aFlags' value into an appropriate string.
47 function scriptErrorFlagsToKind(aFlags) {
48 var kind;
49 if (aFlags & Ci.nsIScriptError.warningFlag)
50 kind = "warning";
51 if (aFlags & Ci.nsIScriptError.exceptionFlag)
52 kind = "exception";
53 else
54 kind = "error";
55
56 if (aFlags & Ci.nsIScriptError.strictFlag)
57 kind = "strict " + kind;
58
59 return kind;
60 }
61
62 // Redeclare dbg_assert with a fatal behavior.
63 function dbg_assert(cond, e) {
64 if (!cond) {
65 throw e;
66 }
67 }
68
69 // Register a console listener, so console messages don't just disappear
70 // into the ether.
71 let errorCount = 0;
72 let listener = {
73 observe: function (aMessage) {
74 errorCount++;
75 try {
76 // If we've been given an nsIScriptError, then we can print out
77 // something nicely formatted, for tools like Emacs to pick up.
78 var scriptError = aMessage.QueryInterface(Ci.nsIScriptError);
79 dump(aMessage.sourceName + ":" + aMessage.lineNumber + ": " +
80 scriptErrorFlagsToKind(aMessage.flags) + ": " +
81 aMessage.errorMessage + "\n");
82 var string = aMessage.errorMessage;
83 } catch (x) {
84 // Be a little paranoid with message, as the whole goal here is to lose
85 // no information.
86 try {
87 var string = "" + aMessage.message;
88 } catch (x) {
89 var string = "<error converting error message to string>";
90 }
91 }
92
93 // Make sure we exit all nested event loops so that the test can finish.
94 while (DebuggerServer.xpcInspector.eventLoopNestLevel > 0) {
95 DebuggerServer.xpcInspector.exitNestedEventLoop();
96 }
97 do_throw("head_dbg.js got console message: " + string + "\n");
98 }
99 };
100
101 let consoleService = Cc["@mozilla.org/consoleservice;1"].getService(Ci.nsIConsoleService);
102 consoleService.registerListener(listener);
103
104 function check_except(func)
105 {
106 try {
107 func();
108 } catch (e) {
109 do_check_true(true);
110 return;
111 }
112 dump("Should have thrown an exception: " + func.toString());
113 do_check_true(false);
114 }
115
116 function testGlobal(aName) {
117 let systemPrincipal = Cc["@mozilla.org/systemprincipal;1"]
118 .createInstance(Ci.nsIPrincipal);
119
120 let sandbox = Cu.Sandbox(systemPrincipal);
121 sandbox.__name = aName;
122 return sandbox;
123 }
124
125 function addTestGlobal(aName)
126 {
127 let global = testGlobal(aName);
128 DebuggerServer.addTestGlobal(global);
129 return global;
130 }
131
132 // List the DebuggerClient |aClient|'s tabs, look for one whose title is
133 // |aTitle|, and apply |aCallback| to the packet's entry for that tab.
134 function getTestTab(aClient, aTitle, aCallback) {
135 aClient.listTabs(function (aResponse) {
136 for (let tab of aResponse.tabs) {
137 if (tab.title === aTitle) {
138 aCallback(tab);
139 return;
140 }
141 }
142 aCallback(null);
143 });
144 }
145
146 // Attach to |aClient|'s tab whose title is |aTitle|; pass |aCallback| the
147 // response packet and a TabClient instance referring to that tab.
148 function attachTestTab(aClient, aTitle, aCallback) {
149 getTestTab(aClient, aTitle, function (aTab) {
150 aClient.attachTab(aTab.actor, aCallback);
151 });
152 }
153
154 // Attach to |aClient|'s tab whose title is |aTitle|, and then attach to
155 // that tab's thread. Pass |aCallback| the thread attach response packet, a
156 // TabClient referring to the tab, and a ThreadClient referring to the
157 // thread.
158 function attachTestThread(aClient, aTitle, aCallback) {
159 attachTestTab(aClient, aTitle, function (aResponse, aTabClient) {
160 function onAttach(aResponse, aThreadClient) {
161 aCallback(aResponse, aTabClient, aThreadClient);
162 }
163 aTabClient.attachThread({ useSourceMaps: true }, onAttach);
164 });
165 }
166
167 // Attach to |aClient|'s tab whose title is |aTitle|, attach to the tab's
168 // thread, and then resume it. Pass |aCallback| the thread's response to
169 // the 'resume' packet, a TabClient for the tab, and a ThreadClient for the
170 // thread.
171 function attachTestTabAndResume(aClient, aTitle, aCallback) {
172 attachTestThread(aClient, aTitle, function(aResponse, aTabClient, aThreadClient) {
173 aThreadClient.resume(function (aResponse) {
174 aCallback(aResponse, aTabClient, aThreadClient);
175 });
176 });
177 }
178
179 /**
180 * Initialize the testing debugger server.
181 */
182 function initTestDebuggerServer()
183 {
184 DebuggerServer.addActors("resource://gre/modules/devtools/server/actors/root.js");
185 DebuggerServer.addActors("resource://gre/modules/devtools/server/actors/script.js");
186 DebuggerServer.addActors("resource://test/testactors.js");
187 // Allow incoming connections.
188 DebuggerServer.init(function () { return true; });
189 }
190
191 function initTestTracerServer()
192 {
193 DebuggerServer.addActors("resource://gre/modules/devtools/server/actors/root.js");
194 DebuggerServer.addActors("resource://gre/modules/devtools/server/actors/script.js");
195 DebuggerServer.addActors("resource://test/testactors.js");
196 DebuggerServer.registerModule("devtools/server/actors/tracer");
197 // Allow incoming connections.
198 DebuggerServer.init(function () { return true; });
199 }
200
201 function finishClient(aClient)
202 {
203 aClient.close(function() {
204 do_test_finished();
205 });
206 }
207
208 /**
209 * Takes a relative file path and returns the absolute file url for it.
210 */
211 function getFileUrl(aName, aAllowMissing=false) {
212 let file = do_get_file(aName, aAllowMissing);
213 return Services.io.newFileURI(file).spec;
214 }
215
216 /**
217 * Returns the full path of the file with the specified name in a
218 * platform-independent and URL-like form.
219 */
220 function getFilePath(aName, aAllowMissing=false)
221 {
222 let file = do_get_file(aName, aAllowMissing);
223 let path = Services.io.newFileURI(file).spec;
224 let filePrePath = "file://";
225 if ("nsILocalFileWin" in Ci &&
226 file instanceof Ci.nsILocalFileWin) {
227 filePrePath += "/";
228 }
229 return path.slice(filePrePath.length);
230 }
231
232 Cu.import("resource://gre/modules/NetUtil.jsm");
233
234 /**
235 * Returns the full text contents of the given file.
236 */
237 function readFile(aFileName) {
238 let f = do_get_file(aFileName);
239 let s = Cc["@mozilla.org/network/file-input-stream;1"]
240 .createInstance(Ci.nsIFileInputStream);
241 s.init(f, -1, -1, false);
242 try {
243 return NetUtil.readInputStreamToString(s, s.available());
244 } finally {
245 s.close();
246 }
247 }
248
249 function writeFile(aFileName, aContent) {
250 let file = do_get_file(aFileName, true);
251 let stream = Cc["@mozilla.org/network/file-output-stream;1"]
252 .createInstance(Ci.nsIFileOutputStream);
253 stream.init(file, -1, -1, 0);
254 try {
255 do {
256 let numWritten = stream.write(aContent, aContent.length);
257 aContent = aContent.slice(numWritten);
258 } while (aContent.length > 0);
259 } finally {
260 stream.close();
261 }
262 }
263
264 function connectPipeTracing() {
265 return new TracingTransport(DebuggerServer.connectPipe());
266 }
267
268 function TracingTransport(childTransport) {
269 this.hooks = null;
270 this.child = childTransport;
271 this.child.hooks = this;
272
273 this.expectations = [];
274 this.packets = [];
275 this.checkIndex = 0;
276 }
277
278 function deepEqual(a, b) {
279 if (a === b)
280 return true;
281 if (typeof a != "object" || typeof b != "object")
282 return false;
283 if (a === null || b === null)
284 return false;
285 if (Object.keys(a).length != Object.keys(b).length)
286 return false;
287 for (let k in a) {
288 if (!deepEqual(a[k], b[k]))
289 return false;
290 }
291 return true;
292 }
293
294 TracingTransport.prototype = {
295 // Remove actor names
296 normalize: function(packet) {
297 return JSON.parse(JSON.stringify(packet, (key, value) => {
298 if (key === "to" || key === "from" || key === "actor") {
299 return "<actorid>";
300 }
301 return value;
302 }));
303 },
304 send: function(packet) {
305 this.packets.push({
306 type: "sent",
307 packet: this.normalize(packet)
308 });
309 return this.child.send(packet);
310 },
311 close: function() {
312 return this.child.close();
313 },
314 ready: function() {
315 return this.child.ready();
316 },
317 onPacket: function(packet) {
318 this.packets.push({
319 type: "received",
320 packet: this.normalize(packet)
321 });
322 this.hooks.onPacket(packet);
323 },
324 onClosed: function() {
325 this.hooks.onClosed();
326 },
327
328 expectSend: function(expected) {
329 let packet = this.packets[this.checkIndex++];
330 do_check_eq(packet.type, "sent");
331 do_check_true(deepEqual(packet.packet, this.normalize(expected)));
332 },
333
334 expectReceive: function(expected) {
335 let packet = this.packets[this.checkIndex++];
336 do_check_eq(packet.type, "received");
337 do_check_true(deepEqual(packet.packet, this.normalize(expected)));
338 },
339
340 // Write your tests, call dumpLog at the end, inspect the output,
341 // then sprinkle the calls through the right places in your test.
342 dumpLog: function() {
343 for (let entry of this.packets) {
344 if (entry.type === "sent") {
345 dump("trace.expectSend(" + entry.packet + ");\n");
346 } else {
347 dump("trace.expectReceive(" + entry.packet + ");\n");
348 }
349 }
350 }
351 };
352
353 function StubTransport() { }
354 StubTransport.prototype.ready = function () {};
355 StubTransport.prototype.send = function () {};
356 StubTransport.prototype.close = function () {};
357
358 function executeSoon(aFunc) {
359 Services.tm.mainThread.dispatch({
360 run: DevToolsUtils.makeInfallible(aFunc)
361 }, Ci.nsIThread.DISPATCH_NORMAL);
362 }

mercurial