/* -*- js-indent-level: 2; tab-width: 2; indent-tabs-mode: nil -*- */ const TEST_PACKAGE = "chrome://mochitests/content/"; // Make sure to use the real add-on ID to get the e10s shims activated const TEST_ID = "mochikit@mozilla.org"; var gConfig; if (Cc === undefined) { var Cc = Components.classes; var Ci = Components.interfaces; var Cu = Components.utils; } Cu.import("resource://gre/modules/XPCOMUtils.jsm"); Cu.import("resource://gre/modules/Task.jsm"); XPCOMUtils.defineLazyModuleGetter(this, "Services", "resource://gre/modules/Services.jsm"); // Start the tests after the window has been displayed window.addEventListener("load", function testOnLoad() { window.removeEventListener("load", testOnLoad); window.addEventListener("MozAfterPaint", function testOnMozAfterPaint() { window.removeEventListener("MozAfterPaint", testOnMozAfterPaint); setTimeout(testInit, 0); }); }); // Tests a single module function testModule(require, { url, expected }) { return new Promise(resolve => { let path = url.substring(TEST_PACKAGE.length); const { stdout } = require("sdk/system"); const { runTests } = require("sdk/test/harness"); const loaderModule = require("toolkit/loader"); const options = require("sdk/test/options"); function findAndRunTests(loader, nextIteration) { const { TestRunner } = loaderModule.main(loader, "sdk/deprecated/unit-test"); const NOT_TESTS = ['setup', 'teardown']; var runner = new TestRunner(); let tests = []; let suiteModule; try { dump("TEST-INFO: " + path + " | Loading test module\n"); suiteModule = loaderModule.main(loader, "tests/" + path.substring(0, path.length - 3)); } catch (e) { // If `Unsupported Application` error thrown during test, // skip the test suite suiteModule = { 'test suite skipped': assert => assert.pass(e.message) }; } for (let name of Object.keys(suiteModule).sort()) { if (NOT_TESTS.indexOf(name) != -1) continue; tests.push({ setup: suiteModule.setup, teardown: suiteModule.teardown, testFunction: suiteModule[name], name: path + "." + name }); } runner.startMany({ tests: { getNext: () => Promise.resolve(tests.shift()) }, stopOnError: options.stopOnError, onDone: nextIteration }); } runTests({ findAndRunTests: findAndRunTests, iterations: options.iterations, filter: options.filter, profileMemory: options.profileMemory, stopOnError: options.stopOnError, verbose: options.verbose, parseable: options.parseable, print: stdout.write, onDone: resolve }); }); } // Sets the test prefs function setPrefs(root, options) { Object.keys(options).forEach(id => { const key = root + "." + id; const value = options[id] const type = typeof(value); value === null ? void(0) : value === undefined ? void(0) : type === "boolean" ? Services.prefs.setBoolPref(key, value) : type === "string" ? Services.prefs.setCharPref(key, value) : type === "number" ? Services.prefs.setIntPref(key, parseInt(value)) : type === "object" ? setPrefs(key, value) : void(0); }); } function testInit() { // Make sure to run the test harness for the first opened window only if (Services.prefs.prefHasUserValue("testing.jetpackTestHarness.running")) return; Services.prefs.setBoolPref("testing.jetpackTestHarness.running", true); // Get the list of tests to run let config = readConfig(); getTestList(config, function(links) { try { let fileNames = []; let fileNameRegexp = /test-.+\.js$/; arrayOfTestFiles(links, fileNames, fileNameRegexp); if (config.startAt || config.endAt) { fileNames = skipTests(fileNames, config.startAt, config.endAt); } if (config.totalChunks && config.thisChunk) { fileNames = chunkifyTests(fileNames, config.totalChunks, config.thisChunk, config.chunkByDir); } // The SDK assumes it is being run from resource URIs let chromeReg = Cc["@mozilla.org/chrome/chrome-registry;1"].getService(Ci.nsIChromeRegistry); let realPath = chromeReg.convertChromeURL(Services.io.newURI(TEST_PACKAGE, null, null)); let resProtocol = Cc["@mozilla.org/network/protocol;1?name=resource"].getService(Ci.nsIResProtocolHandler); resProtocol.setSubstitution("jetpack-package-tests", realPath); // Set the test options const options = { test: { iterations: config.runUntilFailure ? config.repeat : 1, stop: false, keepOpen: true, }, profile: { memory: false, leaks: false, }, output: { logLevel: "verbose", format: "tbpl", }, console: { logLevel: "info", }, } setPrefs("extensions." + TEST_ID + ".sdk", options); // Override the SDK modules if necessary let sdkpath = "resource://gre/modules/commonjs/"; try { let sdklibs = Services.prefs.getCharPref("extensions.sdk.path"); // sdkpath is a file path, make it a URI and map a resource URI to it let sdkfile = Cc["@mozilla.org/file/local;1"]. createInstance(Ci.nsIFile); sdkfile.initWithPath(sdklibs); let sdkuri = Services.io.newFileURI(sdkfile); resProtocol.setSubstitution("jetpack-modules", sdkuri); sdkpath = "resource://jetpack-modules/"; } catch (e) { // Stick with the built-in modules } const paths = { "": sdkpath, "tests/": "resource://jetpack-package-tests/", }; // Create the base module loader to load the test harness const loaderID = "toolkit/loader"; const loaderURI = paths[""] + loaderID + ".js"; const loaderModule = Cu.import(loaderURI, {}).Loader; const modules = {}; // Manually set the loader's module cache to include itself; // which otherwise fails due to lack of `Components`. modules[loaderID] = loaderModule; modules["@test/options"] = {}; let loader = loaderModule.Loader({ id: TEST_ID, name: "addon-sdk", version: "1.0", loadReason: "install", paths: paths, modules: modules, isNative: true, rootURI: paths["tests/"], prefixURI: paths["tests/"], metadata: {}, }); const module = loaderModule.Module(loaderID, loaderURI); const require = loaderModule.Require(loader, module); // Wait until the add-on window is ready require("sdk/addon/window").ready.then(() => { let passed = 0; let failed = 0; function finish() { if (passed + failed == 0) { dump("TEST-UNEXPECTED-FAIL | jetpack-package-harness.js | " + "No tests to run. Did you pass an invalid --test-path?\n"); } else { dump("Jetpack Package Test Summary\n"); dump("\tPassed: " + passed + "\n" + "\tFailed: " + failed + "\n" + "\tTodo: 0\n"); } if (config.closeWhenDone) { require("sdk/system").exit(failed == 0 ? 0 : 1); } } function testNextModule() { if (fileNames.length == 0) return finish(); let filename = fileNames.shift(); testModule(require, filename).then(tests => { passed += tests.passed; failed += tests.failed; }).then(testNextModule); } testNextModule(); }); } catch (e) { dump("TEST-UNEXPECTED-FAIL: jetpack-package-harness.js | error starting test harness (" + e + ")\n"); dump(e.stack); } }); }