test-stepper.js (2871B)
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 "use strict"; 6 7 const { JSTracer } = ChromeUtils.importESModule( 8 "resource://devtools/server/tracer/tracer.sys.mjs", 9 { global: "contextual" } 10 ); 11 12 let testFileContent; 13 14 function traceFrame({ frame }) { 15 const { script } = frame; 16 const { lineNumber, columnNumber } = script.getOffsetMetadata(frame.offset); 17 18 const { url } = script.source; 19 const filename = url.substr(url.lastIndexOf("/") + 1); 20 const line = testFileContent[lineNumber - 1]; 21 // Grey out the beginning of the line, before frame's column, 22 // and display an arrow before displaying the rest of the line. 23 const code = 24 "\x1b[2m" + 25 line.substr(0, columnNumber - 1) + 26 "\x1b[0m" + 27 "\u21A6 " + 28 line.substr(columnNumber - 1); 29 30 const position = (lineNumber + ":" + columnNumber).padEnd(7); 31 logStep(`${filename} @ ${position} :: ${code}`); 32 33 // Disable builtin tracer logging 34 return false; 35 } 36 37 function logStep(message) { 38 dump(` \x1b[2m[STEP]\x1b[0m ${message}\n`); 39 } 40 41 const tracingListener = { 42 onTracingFrame: traceFrame, 43 onTracingFrameStep: traceFrame, 44 }; 45 46 exports.start = function (testGlobal, testUrl, pause) { 47 const tracerOptions = { 48 global: testGlobal, 49 // Ensure tracing each execution within functions (and not only function calls) 50 traceSteps: true, 51 // Only trace the running test and nothing else 52 filterFrameSourceUrl: testUrl, 53 }; 54 testFileContent = readURI(testUrl).split("\n"); 55 // Only pause on each step if the passed value is a number, 56 // otherwise we are only going to print each executed line in the test script. 57 if (!isNaN(pause)) { 58 // Delay each step by an amount of milliseconds 59 tracerOptions.pauseOnStep = Number(pause); 60 logStep(`Tracing all test script steps with ${pause}ms pause`); 61 logStep( 62 `/!\\ Be conscious about each pause releasing the event loop and breaking run-to-completion.` 63 ); 64 } else { 65 logStep(`Tracing all test script steps`); 66 } 67 logStep( 68 `'\u21A6 ' symbol highlights what precise instruction is being called` 69 ); 70 JSTracer.startTracing(tracerOptions); 71 JSTracer.addTracingListener(tracingListener); 72 }; 73 74 exports.stop = function () { 75 JSTracer.stopTracing(); 76 JSTracer.removeTracingListener(tracingListener); 77 }; 78 79 function readURI(uri) { 80 const { NetUtil } = ChromeUtils.importESModule( 81 "resource://gre/modules/NetUtil.sys.mjs", 82 { global: "contextual" } 83 ); 84 const stream = NetUtil.newChannel({ 85 uri: NetUtil.newURI(uri, "UTF-8"), 86 loadUsingSystemPrincipal: true, 87 }).open(); 88 const count = stream.available(); 89 const data = NetUtil.readInputStreamToString(stream, count, { 90 charset: "UTF-8", 91 }); 92 93 stream.close(); 94 return data; 95 }