head.js 52 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647
  1. /* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
  2. /* vim:set ts=2 sw=2 sts=2 et: */
  3. /* This Source Code Form is subject to the terms of the Mozilla Public
  4. * License, v. 2.0. If a copy of the MPL was not distributed with this
  5. * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
  6. /*
  7. * This file contains common code that is loaded before each test file(s).
  8. * See http://developer.mozilla.org/en/docs/Writing_xpcshell-based_unit_tests
  9. * for more information.
  10. */
  11. var _quit = false;
  12. var _passed = true;
  13. var _tests_pending = 0;
  14. var _cleanupFunctions = [];
  15. var _pendingTimers = [];
  16. var _profileInitialized = false;
  17. // Register the testing-common resource protocol early, to have access to its
  18. // modules.
  19. _register_modules_protocol_handler();
  20. var _Promise = Components.utils.import("resource://gre/modules/Promise.jsm", {}).Promise;
  21. var _PromiseTestUtils = Components.utils.import("resource://testing-common/PromiseTestUtils.jsm", {}).PromiseTestUtils;
  22. Components.utils.importGlobalProperties(["XMLHttpRequest"]);
  23. // Support a common assertion library, Assert.jsm.
  24. var AssertCls = Components.utils.import("resource://testing-common/Assert.jsm", null).Assert;
  25. // Pass a custom report function for xpcshell-test style reporting.
  26. var Assert = new AssertCls(function(err, message, stack) {
  27. if (err) {
  28. do_report_result(false, err.message, err.stack);
  29. } else {
  30. do_report_result(true, message, stack);
  31. }
  32. });
  33. var _add_params = function (params) {
  34. if (typeof _XPCSHELL_PROCESS != "undefined") {
  35. params.xpcshell_process = _XPCSHELL_PROCESS;
  36. }
  37. };
  38. var _dumpLog = function (raw_msg) {
  39. dump("\n" + JSON.stringify(raw_msg) + "\n");
  40. }
  41. var _LoggerClass = Components.utils.import("resource://testing-common/StructuredLog.jsm", null).StructuredLogger;
  42. var _testLogger = new _LoggerClass("xpcshell/head.js", _dumpLog, [_add_params]);
  43. // Disable automatic network detection, so tests work correctly when
  44. // not connected to a network.
  45. {
  46. let ios = Components.classes["@mozilla.org/network/io-service;1"]
  47. .getService(Components.interfaces.nsIIOService2);
  48. ios.manageOfflineStatus = false;
  49. ios.offline = false;
  50. }
  51. // Determine if we're running on parent or child
  52. var runningInParent = true;
  53. try {
  54. runningInParent = Components.classes["@mozilla.org/xre/runtime;1"].
  55. getService(Components.interfaces.nsIXULRuntime).processType
  56. == Components.interfaces.nsIXULRuntime.PROCESS_TYPE_DEFAULT;
  57. }
  58. catch (e) { }
  59. // Only if building of places is enabled.
  60. if (runningInParent &&
  61. "mozIAsyncHistory" in Components.interfaces) {
  62. // Ensure places history is enabled for xpcshell-tests as some non-FF
  63. // apps disable it.
  64. let prefs = Components.classes["@mozilla.org/preferences-service;1"]
  65. .getService(Components.interfaces.nsIPrefBranch);
  66. prefs.setBoolPref("places.history.enabled", true);
  67. }
  68. try {
  69. if (runningInParent) {
  70. let prefs = Components.classes["@mozilla.org/preferences-service;1"]
  71. .getService(Components.interfaces.nsIPrefBranch);
  72. // disable necko IPC security checks for xpcshell, as they lack the
  73. // docshells needed to pass them
  74. prefs.setBoolPref("network.disable.ipc.security", true);
  75. // Disable IPv6 lookups for 'localhost' on windows.
  76. if ("@mozilla.org/windows-registry-key;1" in Components.classes) {
  77. prefs.setCharPref("network.dns.ipv4OnlyDomains", "localhost");
  78. }
  79. }
  80. }
  81. catch (e) { }
  82. // Configure a console listener so messages sent to it are logged as part
  83. // of the test.
  84. try {
  85. let levelNames = {}
  86. for (let level of ["debug", "info", "warn", "error"]) {
  87. levelNames[Components.interfaces.nsIConsoleMessage[level]] = level;
  88. }
  89. let listener = {
  90. QueryInterface : function(iid) {
  91. if (!iid.equals(Components.interfaces.nsISupports) &&
  92. !iid.equals(Components.interfaces.nsIConsoleListener)) {
  93. throw Components.results.NS_NOINTERFACE;
  94. }
  95. return this;
  96. },
  97. observe : function (msg) {
  98. if (typeof do_print === "function")
  99. do_print("CONSOLE_MESSAGE: (" + levelNames[msg.logLevel] + ") " + msg.toString());
  100. }
  101. };
  102. Components.classes["@mozilla.org/consoleservice;1"]
  103. .getService(Components.interfaces.nsIConsoleService)
  104. .registerListener(listener);
  105. } catch (e) {}
  106. /**
  107. * Date.now() is not necessarily monotonically increasing (insert sob story
  108. * about times not being the right tool to use for measuring intervals of time,
  109. * robarnold can tell all), so be wary of error by erring by at least
  110. * _timerFuzz ms.
  111. */
  112. const _timerFuzz = 15;
  113. function _Timer(func, delay) {
  114. delay = Number(delay);
  115. if (delay < 0)
  116. do_throw("do_timeout() delay must be nonnegative");
  117. if (typeof func !== "function")
  118. do_throw("string callbacks no longer accepted; use a function!");
  119. this._func = func;
  120. this._start = Date.now();
  121. this._delay = delay;
  122. var timer = Components.classes["@mozilla.org/timer;1"]
  123. .createInstance(Components.interfaces.nsITimer);
  124. timer.initWithCallback(this, delay + _timerFuzz, timer.TYPE_ONE_SHOT);
  125. // Keep timer alive until it fires
  126. _pendingTimers.push(timer);
  127. }
  128. _Timer.prototype = {
  129. QueryInterface: function(iid) {
  130. if (iid.equals(Components.interfaces.nsITimerCallback) ||
  131. iid.equals(Components.interfaces.nsISupports))
  132. return this;
  133. throw Components.results.NS_ERROR_NO_INTERFACE;
  134. },
  135. notify: function(timer) {
  136. _pendingTimers.splice(_pendingTimers.indexOf(timer), 1);
  137. // The current nsITimer implementation can undershoot, but even if it
  138. // couldn't, paranoia is probably a virtue here given the potential for
  139. // random orange on tinderboxen.
  140. var end = Date.now();
  141. var elapsed = end - this._start;
  142. if (elapsed >= this._delay) {
  143. try {
  144. this._func.call(null);
  145. } catch (e) {
  146. do_throw("exception thrown from do_timeout callback: " + e);
  147. }
  148. return;
  149. }
  150. // Timer undershot, retry with a little overshoot to try to avoid more
  151. // undershoots.
  152. var newDelay = this._delay - elapsed;
  153. do_timeout(newDelay, this._func);
  154. }
  155. };
  156. function _do_main() {
  157. if (_quit)
  158. return;
  159. _testLogger.info("running event loop");
  160. var thr = Components.classes["@mozilla.org/thread-manager;1"]
  161. .getService().currentThread;
  162. while (!_quit)
  163. thr.processNextEvent(true);
  164. while (thr.hasPendingEvents())
  165. thr.processNextEvent(true);
  166. }
  167. function _do_quit() {
  168. _testLogger.info("exiting test");
  169. _quit = true;
  170. }
  171. /**
  172. * Overrides idleService with a mock. Idle is commonly used for maintenance
  173. * tasks, thus if a test uses a service that requires the idle service, it will
  174. * start handling them.
  175. * This behaviour would cause random failures and slowdown tests execution,
  176. * for example by running database vacuum or cleanups for each test.
  177. *
  178. * @note Idle service is overridden by default. If a test requires it, it will
  179. * have to call do_get_idle() function at least once before use.
  180. */
  181. var _fakeIdleService = {
  182. get registrar() {
  183. delete this.registrar;
  184. return this.registrar =
  185. Components.manager.QueryInterface(Components.interfaces.nsIComponentRegistrar);
  186. },
  187. contractID: "@mozilla.org/widget/idleservice;1",
  188. get CID() {
  189. return this.registrar.contractIDToCID(this.contractID);
  190. },
  191. activate: function FIS_activate()
  192. {
  193. if (!this.originalFactory) {
  194. // Save original factory.
  195. this.originalFactory =
  196. Components.manager.getClassObject(Components.classes[this.contractID],
  197. Components.interfaces.nsIFactory);
  198. // Unregister original factory.
  199. this.registrar.unregisterFactory(this.CID, this.originalFactory);
  200. // Replace with the mock.
  201. this.registrar.registerFactory(this.CID, "Fake Idle Service",
  202. this.contractID, this.factory
  203. );
  204. }
  205. },
  206. deactivate: function FIS_deactivate()
  207. {
  208. if (this.originalFactory) {
  209. // Unregister the mock.
  210. this.registrar.unregisterFactory(this.CID, this.factory);
  211. // Restore original factory.
  212. this.registrar.registerFactory(this.CID, "Idle Service",
  213. this.contractID, this.originalFactory);
  214. delete this.originalFactory;
  215. }
  216. },
  217. factory: {
  218. // nsIFactory
  219. createInstance: function (aOuter, aIID)
  220. {
  221. if (aOuter) {
  222. throw Components.results.NS_ERROR_NO_AGGREGATION;
  223. }
  224. return _fakeIdleService.QueryInterface(aIID);
  225. },
  226. lockFactory: function (aLock) {
  227. throw Components.results.NS_ERROR_NOT_IMPLEMENTED;
  228. },
  229. QueryInterface: function(aIID) {
  230. if (aIID.equals(Components.interfaces.nsIFactory) ||
  231. aIID.equals(Components.interfaces.nsISupports)) {
  232. return this;
  233. }
  234. throw Components.results.NS_ERROR_NO_INTERFACE;
  235. }
  236. },
  237. // nsIIdleService
  238. get idleTime() {
  239. return 0;
  240. },
  241. addIdleObserver: function () {},
  242. removeIdleObserver: function () {},
  243. QueryInterface: function(aIID) {
  244. // Useful for testing purposes, see test_get_idle.js.
  245. if (aIID.equals(Components.interfaces.nsIFactory)) {
  246. return this.factory;
  247. }
  248. if (aIID.equals(Components.interfaces.nsIIdleService) ||
  249. aIID.equals(Components.interfaces.nsISupports)) {
  250. return this;
  251. }
  252. throw Components.results.NS_ERROR_NO_INTERFACE;
  253. }
  254. }
  255. /**
  256. * Restores the idle service factory if needed and returns the service's handle.
  257. * @return A handle to the idle service.
  258. */
  259. function do_get_idle() {
  260. _fakeIdleService.deactivate();
  261. return Components.classes[_fakeIdleService.contractID]
  262. .getService(Components.interfaces.nsIIdleService);
  263. }
  264. // Map resource://test/ to current working directory and
  265. // resource://testing-common/ to the shared test modules directory.
  266. function _register_protocol_handlers() {
  267. let ios = Components.classes["@mozilla.org/network/io-service;1"]
  268. .getService(Components.interfaces.nsIIOService);
  269. let protocolHandler =
  270. ios.getProtocolHandler("resource")
  271. .QueryInterface(Components.interfaces.nsIResProtocolHandler);
  272. let curDirURI = ios.newFileURI(do_get_cwd());
  273. protocolHandler.setSubstitution("test", curDirURI);
  274. _register_modules_protocol_handler();
  275. }
  276. function _register_modules_protocol_handler() {
  277. if (!_TESTING_MODULES_DIR) {
  278. throw new Error("Please define a path where the testing modules can be " +
  279. "found in a variable called '_TESTING_MODULES_DIR' before " +
  280. "head.js is included.");
  281. }
  282. let ios = Components.classes["@mozilla.org/network/io-service;1"]
  283. .getService(Components.interfaces.nsIIOService);
  284. let protocolHandler =
  285. ios.getProtocolHandler("resource")
  286. .QueryInterface(Components.interfaces.nsIResProtocolHandler);
  287. let modulesFile = Components.classes["@mozilla.org/file/local;1"].
  288. createInstance(Components.interfaces.nsILocalFile);
  289. modulesFile.initWithPath(_TESTING_MODULES_DIR);
  290. if (!modulesFile.exists()) {
  291. throw new Error("Specified modules directory does not exist: " +
  292. _TESTING_MODULES_DIR);
  293. }
  294. if (!modulesFile.isDirectory()) {
  295. throw new Error("Specified modules directory is not a directory: " +
  296. _TESTING_MODULES_DIR);
  297. }
  298. let modulesURI = ios.newFileURI(modulesFile);
  299. protocolHandler.setSubstitution("testing-common", modulesURI);
  300. }
  301. /* Debugging support */
  302. // Used locally and by our self-tests.
  303. function _setupDebuggerServer(breakpointFiles, callback) {
  304. let prefs = Components.classes["@mozilla.org/preferences-service;1"]
  305. .getService(Components.interfaces.nsIPrefBranch);
  306. // Always allow remote debugging.
  307. prefs.setBoolPref("devtools.debugger.remote-enabled", true);
  308. // for debugging-the-debugging, let an env var cause log spew.
  309. let env = Components.classes["@mozilla.org/process/environment;1"]
  310. .getService(Components.interfaces.nsIEnvironment);
  311. if (env.get("DEVTOOLS_DEBUGGER_LOG")) {
  312. prefs.setBoolPref("devtools.debugger.log", true);
  313. }
  314. if (env.get("DEVTOOLS_DEBUGGER_LOG_VERBOSE")) {
  315. prefs.setBoolPref("devtools.debugger.log.verbose", true);
  316. }
  317. let require;
  318. try {
  319. ({ require } = Components.utils.import("resource://devtools/shared/Loader.jsm", {}));
  320. } catch (e) {
  321. throw new Error("resource://devtools appears to be inaccessible from the " +
  322. "xpcshell environment.\n" +
  323. "This can usually be resolved by adding:\n" +
  324. " firefox-appdir = browser\n" +
  325. "to the xpcshell.ini manifest.\n" +
  326. "It is possible for this to alter test behevior by " +
  327. "triggering additional browser code to run, so check " +
  328. "test behavior after making this change.\n" +
  329. "See also https://bugzil.la/1215378.")
  330. }
  331. let { DebuggerServer } = require("devtools/server/main");
  332. let { OriginalLocation } = require("devtools/server/actors/common");
  333. DebuggerServer.init();
  334. DebuggerServer.addBrowserActors();
  335. DebuggerServer.addActors("resource://testing-common/dbg-actors.js");
  336. DebuggerServer.allowChromeProcess = true;
  337. // An observer notification that tells us when we can "resume" script
  338. // execution.
  339. let obsSvc = Components.classes["@mozilla.org/observer-service;1"].
  340. getService(Components.interfaces.nsIObserverService);
  341. const TOPICS = ["devtools-thread-resumed", "xpcshell-test-devtools-shutdown"];
  342. let observe = function(subject, topic, data) {
  343. switch (topic) {
  344. case "devtools-thread-resumed":
  345. // Exceptions in here aren't reported and block the debugger from
  346. // resuming, so...
  347. try {
  348. // Add a breakpoint for the first line in our test files.
  349. let threadActor = subject.wrappedJSObject;
  350. for (let file of breakpointFiles) {
  351. // Pass an empty `source` object to workaround `source` function assertion
  352. let sourceActor = threadActor.sources.source({originalUrl: file, source: {}});
  353. sourceActor._getOrCreateBreakpointActor(new OriginalLocation(sourceActor, 1));
  354. }
  355. } catch (ex) {
  356. do_print("Failed to initialize breakpoints: " + ex + "\n" + ex.stack);
  357. }
  358. break;
  359. case "xpcshell-test-devtools-shutdown":
  360. // the debugger has shutdown before we got a resume event - nothing
  361. // special to do here.
  362. break;
  363. }
  364. for (let topicToRemove of TOPICS) {
  365. obsSvc.removeObserver(observe, topicToRemove);
  366. }
  367. callback();
  368. };
  369. for (let topic of TOPICS) {
  370. obsSvc.addObserver(observe, topic, false);
  371. }
  372. return DebuggerServer;
  373. }
  374. function _initDebugging(port) {
  375. let initialized = false;
  376. let DebuggerServer = _setupDebuggerServer(_TEST_FILE, () => {initialized = true;});
  377. do_print("");
  378. do_print("*******************************************************************");
  379. do_print("Waiting for the debugger to connect on port " + port)
  380. do_print("")
  381. do_print("To connect the debugger, open a Firefox instance, select 'Connect'");
  382. do_print("from the Developer menu and specify the port as " + port);
  383. do_print("*******************************************************************");
  384. do_print("")
  385. let AuthenticatorType = DebuggerServer.Authenticators.get("PROMPT");
  386. let authenticator = new AuthenticatorType.Server();
  387. authenticator.allowConnection = () => {
  388. return DebuggerServer.AuthenticationResult.ALLOW;
  389. };
  390. let listener = DebuggerServer.createListener();
  391. listener.portOrPath = port;
  392. listener.authenticator = authenticator;
  393. listener.open();
  394. // spin an event loop until the debugger connects.
  395. let thr = Components.classes["@mozilla.org/thread-manager;1"]
  396. .getService().currentThread;
  397. while (!initialized) {
  398. do_print("Still waiting for debugger to connect...");
  399. thr.processNextEvent(true);
  400. }
  401. // NOTE: if you want to debug the harness itself, you can now add a 'debugger'
  402. // statement anywhere and it will stop - but we've already added a breakpoint
  403. // for the first line of the test scripts, so we just continue...
  404. do_print("Debugger connected, starting test execution");
  405. }
  406. function _execute_test() {
  407. // _JSDEBUGGER_PORT is dynamically defined by <runxpcshelltests.py>.
  408. if (_JSDEBUGGER_PORT) {
  409. try {
  410. _initDebugging(_JSDEBUGGER_PORT);
  411. } catch (ex) {
  412. // Fail the test run immediately if debugging is requested but fails, so
  413. // that the failure state is more obvious.
  414. do_throw(`Failed to initialize debugging: ${ex}`, ex.stack);
  415. }
  416. }
  417. _register_protocol_handlers();
  418. // Override idle service by default.
  419. // Call do_get_idle() to restore the factory and get the service.
  420. _fakeIdleService.activate();
  421. _PromiseTestUtils.init();
  422. _PromiseTestUtils.Assert = Assert;
  423. let coverageCollector = null;
  424. if (typeof _JSCOV_DIR === 'string') {
  425. let _CoverageCollector = Components.utils.import("resource://testing-common/CoverageUtils.jsm", {}).CoverageCollector;
  426. coverageCollector = new _CoverageCollector(_JSCOV_DIR);
  427. }
  428. // _HEAD_FILES is dynamically defined by <runxpcshelltests.py>.
  429. _load_files(_HEAD_FILES);
  430. // _TEST_FILE is dynamically defined by <runxpcshelltests.py>.
  431. _load_files(_TEST_FILE);
  432. // Tack Assert.jsm methods to the current scope.
  433. this.Assert = Assert;
  434. for (let func in Assert) {
  435. this[func] = Assert[func].bind(Assert);
  436. }
  437. if (_gTestHasOnly) {
  438. _gTests = _gTests.filter(([props,]) => {
  439. return ("_only" in props) && props._only;
  440. });
  441. }
  442. try {
  443. do_test_pending("MAIN run_test");
  444. // Check if run_test() is defined. If defined, run it.
  445. // Else, call run_next_test() directly to invoke tests
  446. // added by add_test() and add_task().
  447. if (typeof run_test === "function") {
  448. run_test();
  449. } else {
  450. run_next_test();
  451. }
  452. if (coverageCollector != null) {
  453. coverageCollector.recordTestCoverage(_TEST_FILE[0]);
  454. }
  455. do_test_finished("MAIN run_test");
  456. _do_main();
  457. _PromiseTestUtils.assertNoUncaughtRejections();
  458. } catch (e) {
  459. _passed = false;
  460. // do_check failures are already logged and set _quit to true and throw
  461. // NS_ERROR_ABORT. If both of those are true it is likely this exception
  462. // has already been logged so there is no need to log it again. It's
  463. // possible that this will mask an NS_ERROR_ABORT that happens after a
  464. // do_check failure though.
  465. if (coverageCollector != null) {
  466. coverageCollector.recordTestCoverage(_TEST_FILE[0]);
  467. }
  468. if (!_quit || e != Components.results.NS_ERROR_ABORT) {
  469. let extra = {};
  470. if (e.fileName) {
  471. extra.source_file = e.fileName;
  472. if (e.lineNumber) {
  473. extra.line_number = e.lineNumber;
  474. }
  475. } else {
  476. extra.source_file = "xpcshell/head.js";
  477. }
  478. let message = _exception_message(e);
  479. if (e.stack) {
  480. extra.stack = _format_stack(e.stack);
  481. }
  482. _testLogger.error(message, extra);
  483. }
  484. }
  485. if (coverageCollector != null) {
  486. coverageCollector.finalize();
  487. }
  488. // _TAIL_FILES is dynamically defined by <runxpcshelltests.py>.
  489. _load_files(_TAIL_FILES);
  490. // Execute all of our cleanup functions.
  491. let reportCleanupError = function(ex) {
  492. let stack, filename;
  493. if (ex && typeof ex == "object" && "stack" in ex) {
  494. stack = ex.stack;
  495. } else {
  496. stack = Components.stack.caller;
  497. }
  498. if (stack instanceof Components.interfaces.nsIStackFrame) {
  499. filename = stack.filename;
  500. } else if (ex.fileName) {
  501. filename = ex.fileName;
  502. }
  503. _testLogger.error(_exception_message(ex),
  504. {
  505. stack: _format_stack(stack),
  506. source_file: filename
  507. });
  508. };
  509. let func;
  510. while ((func = _cleanupFunctions.pop())) {
  511. let result;
  512. try {
  513. result = func();
  514. } catch (ex) {
  515. reportCleanupError(ex);
  516. continue;
  517. }
  518. if (result && typeof result == "object"
  519. && "then" in result && typeof result.then == "function") {
  520. // This is a promise, wait until it is satisfied before proceeding
  521. let complete = false;
  522. let promise = result.then(null, reportCleanupError);
  523. promise = promise.then(() => complete = true);
  524. let thr = Components.classes["@mozilla.org/thread-manager;1"]
  525. .getService().currentThread;
  526. while (!complete) {
  527. thr.processNextEvent(true);
  528. }
  529. }
  530. }
  531. // Restore idle service to avoid leaks.
  532. _fakeIdleService.deactivate();
  533. if (_profileInitialized) {
  534. // Since we have a profile, we will notify profile shutdown topics at
  535. // the end of the current test, to ensure correct cleanup on shutdown.
  536. let obs = Components.classes["@mozilla.org/observer-service;1"]
  537. .getService(Components.interfaces.nsIObserverService);
  538. obs.notifyObservers(null, "profile-change-net-teardown", null);
  539. obs.notifyObservers(null, "profile-change-teardown", null);
  540. obs.notifyObservers(null, "profile-before-change", null);
  541. obs.notifyObservers(null, "profile-before-change-qm", null);
  542. _profileInitialized = false;
  543. }
  544. try {
  545. _PromiseTestUtils.ensureDOMPromiseRejectionsProcessed();
  546. _PromiseTestUtils.assertNoUncaughtRejections();
  547. _PromiseTestUtils.assertNoMoreExpectedRejections();
  548. } finally {
  549. // It's important to terminate the module to avoid crashes on shutdown.
  550. _PromiseTestUtils.uninit();
  551. }
  552. }
  553. /**
  554. * Loads files.
  555. *
  556. * @param aFiles Array of files to load.
  557. */
  558. function _load_files(aFiles) {
  559. function load_file(element, index, array) {
  560. try {
  561. load(element);
  562. } catch (e) {
  563. let extra = {
  564. source_file: element
  565. }
  566. if (e.stack) {
  567. extra.stack = _format_stack(e.stack);
  568. }
  569. _testLogger.error(_exception_message(e), extra);
  570. }
  571. }
  572. aFiles.forEach(load_file);
  573. }
  574. function _wrap_with_quotes_if_necessary(val) {
  575. return typeof val == "string" ? '"' + val + '"' : val;
  576. }
  577. /************** Functions to be used from the tests **************/
  578. /**
  579. * Prints a message to the output log.
  580. */
  581. function do_print(msg, data) {
  582. msg = _wrap_with_quotes_if_necessary(msg);
  583. data = data ? data : null;
  584. _testLogger.info(msg, data);
  585. }
  586. /**
  587. * Calls the given function at least the specified number of milliseconds later.
  588. * The callback will not undershoot the given time, but it might overshoot --
  589. * don't expect precision!
  590. *
  591. * @param delay : uint
  592. * the number of milliseconds to delay
  593. * @param callback : function() : void
  594. * the function to call
  595. */
  596. function do_timeout(delay, func) {
  597. new _Timer(func, Number(delay));
  598. }
  599. function do_execute_soon(callback, aName) {
  600. let funcName = (aName ? aName : callback.name);
  601. do_test_pending(funcName);
  602. var tm = Components.classes["@mozilla.org/thread-manager;1"]
  603. .getService(Components.interfaces.nsIThreadManager);
  604. tm.mainThread.dispatch({
  605. run: function() {
  606. try {
  607. callback();
  608. } catch (e) {
  609. // do_check failures are already logged and set _quit to true and throw
  610. // NS_ERROR_ABORT. If both of those are true it is likely this exception
  611. // has already been logged so there is no need to log it again. It's
  612. // possible that this will mask an NS_ERROR_ABORT that happens after a
  613. // do_check failure though.
  614. if (!_quit || e != Components.results.NS_ERROR_ABORT) {
  615. let stack = e.stack ? _format_stack(e.stack) : null;
  616. _testLogger.testStatus(_TEST_NAME,
  617. funcName,
  618. 'FAIL',
  619. 'PASS',
  620. _exception_message(e),
  621. stack);
  622. _do_quit();
  623. }
  624. }
  625. finally {
  626. do_test_finished(funcName);
  627. }
  628. }
  629. }, Components.interfaces.nsIThread.DISPATCH_NORMAL);
  630. }
  631. /**
  632. * Shows an error message and the current stack and aborts the test.
  633. *
  634. * @param error A message string or an Error object.
  635. * @param stack null or nsIStackFrame object or a string containing
  636. * \n separated stack lines (as in Error().stack).
  637. */
  638. function do_throw(error, stack) {
  639. let filename = "";
  640. // If we didn't get passed a stack, maybe the error has one
  641. // otherwise get it from our call context
  642. stack = stack || error.stack || Components.stack.caller;
  643. if (stack instanceof Components.interfaces.nsIStackFrame)
  644. filename = stack.filename;
  645. else if (error.fileName)
  646. filename = error.fileName;
  647. _testLogger.error(_exception_message(error),
  648. {
  649. source_file: filename,
  650. stack: _format_stack(stack)
  651. });
  652. _abort_failed_test();
  653. }
  654. function _abort_failed_test() {
  655. // Called to abort the test run after all failures are logged.
  656. _passed = false;
  657. _do_quit();
  658. throw Components.results.NS_ERROR_ABORT;
  659. }
  660. function _format_stack(stack) {
  661. let normalized;
  662. if (stack instanceof Components.interfaces.nsIStackFrame) {
  663. let frames = [];
  664. for (let frame = stack; frame; frame = frame.caller) {
  665. frames.push(frame.filename + ":" + frame.name + ":" + frame.lineNumber);
  666. }
  667. normalized = frames.join("\n");
  668. } else {
  669. normalized = "" + stack;
  670. }
  671. return _Task.Debugging.generateReadableStack(normalized, " ");
  672. }
  673. // Make a nice display string from an object that behaves
  674. // like Error
  675. function _exception_message(ex) {
  676. let message = "";
  677. if (ex.name) {
  678. message = ex.name + ": ";
  679. }
  680. if (ex.message) {
  681. message += ex.message;
  682. }
  683. if (ex.fileName) {
  684. message += (" at " + ex.fileName);
  685. if (ex.lineNumber) {
  686. message += (":" + ex.lineNumber);
  687. }
  688. }
  689. if (message !== "") {
  690. return message;
  691. }
  692. // Force ex to be stringified
  693. return "" + ex;
  694. }
  695. function do_report_unexpected_exception(ex, text) {
  696. let filename = Components.stack.caller.filename;
  697. text = text ? text + " - " : "";
  698. _passed = false;
  699. _testLogger.error(text + "Unexpected exception " + _exception_message(ex),
  700. {
  701. source_file: filename,
  702. stack: _format_stack(ex.stack)
  703. });
  704. _do_quit();
  705. throw Components.results.NS_ERROR_ABORT;
  706. }
  707. function do_note_exception(ex, text) {
  708. let filename = Components.stack.caller.filename;
  709. _testLogger.info(text + "Swallowed exception " + _exception_message(ex),
  710. {
  711. source_file: filename,
  712. stack: _format_stack(ex.stack)
  713. });
  714. }
  715. function _do_check_neq(left, right, stack, todo) {
  716. Assert.notEqual(left, right);
  717. }
  718. function do_check_neq(left, right, stack) {
  719. if (!stack)
  720. stack = Components.stack.caller;
  721. _do_check_neq(left, right, stack, false);
  722. }
  723. function todo_check_neq(left, right, stack) {
  724. if (!stack)
  725. stack = Components.stack.caller;
  726. _do_check_neq(left, right, stack, true);
  727. }
  728. function do_report_result(passed, text, stack, todo) {
  729. while (stack.filename.includes("head.js") && stack.caller) {
  730. stack = stack.caller;
  731. }
  732. let name = _gRunningTest ? _gRunningTest.name : stack.name;
  733. let message;
  734. if (name) {
  735. message = "[" + name + " : " + stack.lineNumber + "] " + text;
  736. } else {
  737. message = text;
  738. }
  739. if (passed) {
  740. if (todo) {
  741. _testLogger.testStatus(_TEST_NAME,
  742. name,
  743. "PASS",
  744. "FAIL",
  745. message,
  746. _format_stack(stack));
  747. _abort_failed_test();
  748. } else {
  749. _testLogger.testStatus(_TEST_NAME,
  750. name,
  751. "PASS",
  752. "PASS",
  753. message);
  754. }
  755. } else {
  756. if (todo) {
  757. _testLogger.testStatus(_TEST_NAME,
  758. name,
  759. "FAIL",
  760. "FAIL",
  761. message);
  762. } else {
  763. _testLogger.testStatus(_TEST_NAME,
  764. name,
  765. "FAIL",
  766. "PASS",
  767. message,
  768. _format_stack(stack));
  769. _abort_failed_test();
  770. }
  771. }
  772. }
  773. function _do_check_eq(left, right, stack, todo) {
  774. if (!stack)
  775. stack = Components.stack.caller;
  776. var text = _wrap_with_quotes_if_necessary(left) + " == " +
  777. _wrap_with_quotes_if_necessary(right);
  778. do_report_result(left == right, text, stack, todo);
  779. }
  780. function do_check_eq(left, right, stack) {
  781. Assert.equal(left, right);
  782. }
  783. function todo_check_eq(left, right, stack) {
  784. if (!stack)
  785. stack = Components.stack.caller;
  786. _do_check_eq(left, right, stack, true);
  787. }
  788. function do_check_true(condition, stack) {
  789. Assert.ok(condition, stack);
  790. }
  791. function todo_check_true(condition, stack) {
  792. if (!stack)
  793. stack = Components.stack.caller;
  794. todo_check_eq(condition, true, stack);
  795. }
  796. function do_check_false(condition, stack) {
  797. Assert.ok(!condition, stack);
  798. }
  799. function todo_check_false(condition, stack) {
  800. if (!stack)
  801. stack = Components.stack.caller;
  802. todo_check_eq(condition, false, stack);
  803. }
  804. function do_check_null(condition, stack) {
  805. Assert.equal(condition, null);
  806. }
  807. function todo_check_null(condition, stack=Components.stack.caller) {
  808. todo_check_eq(condition, null, stack);
  809. }
  810. function do_check_matches(pattern, value) {
  811. Assert.deepEqual(pattern, value);
  812. }
  813. // Check that |func| throws an nsIException that has
  814. // |Components.results[resultName]| as the value of its 'result' property.
  815. function do_check_throws_nsIException(func, resultName,
  816. stack=Components.stack.caller, todo=false)
  817. {
  818. let expected = Components.results[resultName];
  819. if (typeof expected !== 'number') {
  820. do_throw("do_check_throws_nsIException requires a Components.results" +
  821. " property name, not " + uneval(resultName), stack);
  822. }
  823. let msg = ("do_check_throws_nsIException: func should throw" +
  824. " an nsIException whose 'result' is Components.results." +
  825. resultName);
  826. try {
  827. func();
  828. } catch (ex) {
  829. if (!(ex instanceof Components.interfaces.nsIException) ||
  830. ex.result !== expected) {
  831. do_report_result(false, msg + ", threw " + legible_exception(ex) +
  832. " instead", stack, todo);
  833. }
  834. do_report_result(true, msg, stack, todo);
  835. return;
  836. }
  837. // Call this here, not in the 'try' clause, so do_report_result's own
  838. // throw doesn't get caught by our 'catch' clause.
  839. do_report_result(false, msg + ", but returned normally", stack, todo);
  840. }
  841. // Produce a human-readable form of |exception|. This looks up
  842. // Components.results values, tries toString methods, and so on.
  843. function legible_exception(exception)
  844. {
  845. switch (typeof exception) {
  846. case 'object':
  847. if (exception instanceof Components.interfaces.nsIException) {
  848. return "nsIException instance: " + uneval(exception.toString());
  849. }
  850. return exception.toString();
  851. case 'number':
  852. for (let name in Components.results) {
  853. if (exception === Components.results[name]) {
  854. return "Components.results." + name;
  855. }
  856. }
  857. // Fall through.
  858. default:
  859. return uneval(exception);
  860. }
  861. }
  862. function do_check_instanceof(value, constructor,
  863. stack=Components.stack.caller, todo=false) {
  864. do_report_result(value instanceof constructor,
  865. "value should be an instance of " + constructor.name,
  866. stack, todo);
  867. }
  868. function todo_check_instanceof(value, constructor,
  869. stack=Components.stack.caller) {
  870. do_check_instanceof(value, constructor, stack, true);
  871. }
  872. function do_test_pending(aName) {
  873. ++_tests_pending;
  874. _testLogger.info("(xpcshell/head.js) | test" +
  875. (aName ? " " + aName : "") +
  876. " pending (" + _tests_pending + ")");
  877. }
  878. function do_test_finished(aName) {
  879. _testLogger.info("(xpcshell/head.js) | test" +
  880. (aName ? " " + aName : "") +
  881. " finished (" + _tests_pending + ")");
  882. if (--_tests_pending == 0)
  883. _do_quit();
  884. }
  885. function do_get_file(path, allowNonexistent) {
  886. try {
  887. let lf = Components.classes["@mozilla.org/file/directory_service;1"]
  888. .getService(Components.interfaces.nsIProperties)
  889. .get("CurWorkD", Components.interfaces.nsILocalFile);
  890. let bits = path.split("/");
  891. for (let i = 0; i < bits.length; i++) {
  892. if (bits[i]) {
  893. if (bits[i] == "..")
  894. lf = lf.parent;
  895. else
  896. lf.append(bits[i]);
  897. }
  898. }
  899. if (!allowNonexistent && !lf.exists()) {
  900. // Not using do_throw(): caller will continue.
  901. _passed = false;
  902. var stack = Components.stack.caller;
  903. _testLogger.error("[" + stack.name + " : " + stack.lineNumber + "] " +
  904. lf.path + " does not exist");
  905. }
  906. return lf;
  907. }
  908. catch (ex) {
  909. do_throw(ex.toString(), Components.stack.caller);
  910. }
  911. return null;
  912. }
  913. // do_get_cwd() isn't exactly self-explanatory, so provide a helper
  914. function do_get_cwd() {
  915. return do_get_file("");
  916. }
  917. function do_load_manifest(path) {
  918. var lf = do_get_file(path);
  919. const nsIComponentRegistrar = Components.interfaces.nsIComponentRegistrar;
  920. do_check_true(Components.manager instanceof nsIComponentRegistrar);
  921. // Previous do_check_true() is not a test check.
  922. Components.manager.autoRegister(lf);
  923. }
  924. /**
  925. * Parse a DOM document.
  926. *
  927. * @param aPath File path to the document.
  928. * @param aType Content type to use in DOMParser.
  929. *
  930. * @return nsIDOMDocument from the file.
  931. */
  932. function do_parse_document(aPath, aType) {
  933. switch (aType) {
  934. case "application/xhtml+xml":
  935. case "application/xml":
  936. case "text/xml":
  937. break;
  938. default:
  939. do_throw("type: expected application/xhtml+xml, application/xml or text/xml," +
  940. " got '" + aType + "'",
  941. Components.stack.caller);
  942. }
  943. let file = do_get_file(aPath),
  944. ios = Components.classes['@mozilla.org/network/io-service;1']
  945. .getService(Components.interfaces.nsIIOService),
  946. url = ios.newFileURI(file).spec;
  947. file = null;
  948. return new Promise((resolve, reject) => {
  949. let xhr = new XMLHttpRequest();
  950. xhr.open("GET", url);
  951. xhr.responseType = "document";
  952. xhr.onerror = reject;
  953. xhr.onload = () => {
  954. resolve(xhr.response);
  955. };
  956. xhr.send();
  957. });
  958. }
  959. /**
  960. * Registers a function that will run when the test harness is done running all
  961. * tests.
  962. *
  963. * @param aFunction
  964. * The function to be called when the test harness has finished running.
  965. */
  966. function do_register_cleanup(aFunction)
  967. {
  968. _cleanupFunctions.push(aFunction);
  969. }
  970. /**
  971. * Returns the directory for a temp dir, which is created by the
  972. * test harness. Every test gets its own temp dir.
  973. *
  974. * @return nsILocalFile of the temporary directory
  975. */
  976. function do_get_tempdir() {
  977. let env = Components.classes["@mozilla.org/process/environment;1"]
  978. .getService(Components.interfaces.nsIEnvironment);
  979. // the python harness sets this in the environment for us
  980. let path = env.get("XPCSHELL_TEST_TEMP_DIR");
  981. let file = Components.classes["@mozilla.org/file/local;1"]
  982. .createInstance(Components.interfaces.nsILocalFile);
  983. file.initWithPath(path);
  984. return file;
  985. }
  986. /**
  987. * Returns the directory for crashreporter minidumps.
  988. *
  989. * @return nsILocalFile of the minidump directory
  990. */
  991. function do_get_minidumpdir() {
  992. let env = Components.classes["@mozilla.org/process/environment;1"]
  993. .getService(Components.interfaces.nsIEnvironment);
  994. // the python harness may set this in the environment for us
  995. let path = env.get("XPCSHELL_MINIDUMP_DIR");
  996. if (path) {
  997. let file = Components.classes["@mozilla.org/file/local;1"]
  998. .createInstance(Components.interfaces.nsILocalFile);
  999. file.initWithPath(path);
  1000. return file;
  1001. } else {
  1002. return do_get_tempdir();
  1003. }
  1004. }
  1005. /**
  1006. * Registers a directory with the profile service,
  1007. * and return the directory as an nsILocalFile.
  1008. *
  1009. * @param notifyProfileAfterChange Whether to notify for "profile-after-change".
  1010. * @return nsILocalFile of the profile directory.
  1011. */
  1012. function do_get_profile(notifyProfileAfterChange = false) {
  1013. if (!runningInParent) {
  1014. _testLogger.info("Ignoring profile creation from child process.");
  1015. return null;
  1016. }
  1017. let env = Components.classes["@mozilla.org/process/environment;1"]
  1018. .getService(Components.interfaces.nsIEnvironment);
  1019. // the python harness sets this in the environment for us
  1020. let profd = env.get("XPCSHELL_TEST_PROFILE_DIR");
  1021. let file = Components.classes["@mozilla.org/file/local;1"]
  1022. .createInstance(Components.interfaces.nsILocalFile);
  1023. file.initWithPath(profd);
  1024. let dirSvc = Components.classes["@mozilla.org/file/directory_service;1"]
  1025. .getService(Components.interfaces.nsIProperties);
  1026. let provider = {
  1027. getFile: function(prop, persistent) {
  1028. persistent.value = true;
  1029. if (prop == "ProfD" || prop == "ProfLD" || prop == "ProfDS" ||
  1030. prop == "ProfLDS" || prop == "TmpD") {
  1031. return file.clone();
  1032. }
  1033. return null;
  1034. },
  1035. QueryInterface: function(iid) {
  1036. if (iid.equals(Components.interfaces.nsIDirectoryServiceProvider) ||
  1037. iid.equals(Components.interfaces.nsISupports)) {
  1038. return this;
  1039. }
  1040. throw Components.results.NS_ERROR_NO_INTERFACE;
  1041. }
  1042. };
  1043. dirSvc.QueryInterface(Components.interfaces.nsIDirectoryService)
  1044. .registerProvider(provider);
  1045. let obsSvc = Components.classes["@mozilla.org/observer-service;1"].
  1046. getService(Components.interfaces.nsIObserverService);
  1047. // We need to update the crash events directory when the profile changes.
  1048. if (runningInParent &&
  1049. "@mozilla.org/toolkit/crash-reporter;1" in Components.classes) {
  1050. let crashReporter =
  1051. Components.classes["@mozilla.org/toolkit/crash-reporter;1"]
  1052. .getService(Components.interfaces.nsICrashReporter);
  1053. crashReporter.UpdateCrashEventsDir();
  1054. }
  1055. if (!_profileInitialized) {
  1056. obsSvc.notifyObservers(null, "profile-do-change", "xpcshell-do-get-profile");
  1057. _profileInitialized = true;
  1058. if (notifyProfileAfterChange) {
  1059. obsSvc.notifyObservers(null, "profile-after-change", "xpcshell-do-get-profile");
  1060. }
  1061. }
  1062. // The methods of 'provider' will retain this scope so null out everything
  1063. // to avoid spurious leak reports.
  1064. env = null;
  1065. profd = null;
  1066. dirSvc = null;
  1067. provider = null;
  1068. obsSvc = null;
  1069. return file.clone();
  1070. }
  1071. /**
  1072. * This function loads head.js (this file) in the child process, so that all
  1073. * functions defined in this file (do_throw, etc) are available to subsequent
  1074. * sendCommand calls. It also sets various constants used by these functions.
  1075. *
  1076. * (Note that you may use sendCommand without calling this function first; you
  1077. * simply won't have any of the functions in this file available.)
  1078. */
  1079. function do_load_child_test_harness()
  1080. {
  1081. // Make sure this isn't called from child process
  1082. if (!runningInParent) {
  1083. do_throw("run_test_in_child cannot be called from child!");
  1084. }
  1085. // Allow to be called multiple times, but only run once
  1086. if (typeof do_load_child_test_harness.alreadyRun != "undefined")
  1087. return;
  1088. do_load_child_test_harness.alreadyRun = 1;
  1089. _XPCSHELL_PROCESS = "parent";
  1090. let command =
  1091. "const _HEAD_JS_PATH=" + uneval(_HEAD_JS_PATH) + "; "
  1092. + "const _HEAD_FILES=" + uneval(_HEAD_FILES) + "; "
  1093. + "const _MOZINFO_JS_PATH=" + uneval(_MOZINFO_JS_PATH) + "; "
  1094. + "const _TAIL_FILES=" + uneval(_TAIL_FILES) + "; "
  1095. + "const _TEST_NAME=" + uneval(_TEST_NAME) + "; "
  1096. // We'll need more magic to get the debugger working in the child
  1097. + "const _JSDEBUGGER_PORT=0; "
  1098. + "const _XPCSHELL_PROCESS='child';";
  1099. if (typeof _JSCOV_DIR === 'string') {
  1100. command += " const _JSCOV_DIR=" + uneval(_JSCOV_DIR) + ";";
  1101. }
  1102. if (_TESTING_MODULES_DIR) {
  1103. command += " const _TESTING_MODULES_DIR=" + uneval(_TESTING_MODULES_DIR) + ";";
  1104. }
  1105. command += " load(_HEAD_JS_PATH);";
  1106. sendCommand(command);
  1107. }
  1108. /**
  1109. * Runs an entire xpcshell unit test in a child process (rather than in chrome,
  1110. * which is the default).
  1111. *
  1112. * This function returns immediately, before the test has completed.
  1113. *
  1114. * @param testFile
  1115. * The name of the script to run. Path format same as load().
  1116. * @param optionalCallback.
  1117. * Optional function to be called (in parent) when test on child is
  1118. * complete. If provided, the function must call do_test_finished();
  1119. * @return Promise Resolved when the test in the child is complete.
  1120. */
  1121. function run_test_in_child(testFile, optionalCallback)
  1122. {
  1123. return new Promise((resolve) => {
  1124. var callback = () => {
  1125. resolve();
  1126. if (typeof optionalCallback == 'undefined') {
  1127. do_test_finished();
  1128. } else {
  1129. optionalCallback();
  1130. }
  1131. };
  1132. do_load_child_test_harness();
  1133. var testPath = do_get_file(testFile).path.replace(/\\/g, "/");
  1134. do_test_pending("run in child");
  1135. sendCommand("_testLogger.info('CHILD-TEST-STARTED'); "
  1136. + "const _TEST_FILE=['" + testPath + "']; "
  1137. + "_execute_test(); "
  1138. + "_testLogger.info('CHILD-TEST-COMPLETED');",
  1139. callback);
  1140. });
  1141. }
  1142. /**
  1143. * Execute a given function as soon as a particular cross-process message is received.
  1144. * Must be paired with do_send_remote_message or equivalent ProcessMessageManager calls.
  1145. *
  1146. * @param optionalCallback
  1147. * Optional callback that is invoked when the message is received. If provided,
  1148. * the function must call do_test_finished().
  1149. * @return Promise Promise that is resolved when the message is received.
  1150. */
  1151. function do_await_remote_message(name, optionalCallback)
  1152. {
  1153. return new Promise((resolve) => {
  1154. var listener = {
  1155. receiveMessage: function(message) {
  1156. if (message.name == name) {
  1157. mm.removeMessageListener(name, listener);
  1158. resolve();
  1159. if (optionalCallback) {
  1160. optionalCallback();
  1161. } else {
  1162. do_test_finished();
  1163. }
  1164. }
  1165. }
  1166. };
  1167. var mm;
  1168. if (runningInParent) {
  1169. mm = Cc["@mozilla.org/parentprocessmessagemanager;1"].getService(Ci.nsIMessageBroadcaster);
  1170. } else {
  1171. mm = Cc["@mozilla.org/childprocessmessagemanager;1"].getService(Ci.nsISyncMessageSender);
  1172. }
  1173. do_test_pending();
  1174. mm.addMessageListener(name, listener);
  1175. });
  1176. }
  1177. /**
  1178. * Asynchronously send a message to all remote processes. Pairs with do_await_remote_message
  1179. * or equivalent ProcessMessageManager listeners.
  1180. */
  1181. function do_send_remote_message(name) {
  1182. var mm;
  1183. var sender;
  1184. if (runningInParent) {
  1185. mm = Cc["@mozilla.org/parentprocessmessagemanager;1"].getService(Ci.nsIMessageBroadcaster);
  1186. sender = 'broadcastAsyncMessage';
  1187. } else {
  1188. mm = Cc["@mozilla.org/childprocessmessagemanager;1"].getService(Ci.nsISyncMessageSender);
  1189. sender = 'sendAsyncMessage';
  1190. }
  1191. mm[sender](name);
  1192. }
  1193. /**
  1194. * Helper function to add the _only property to add_task/add_test function when
  1195. * running it as add_task.only(...).
  1196. *
  1197. * @param addFunc
  1198. * The parent function to call, e.g. add_task or add_test.
  1199. * @param funcOrProperties
  1200. * A function to be run or an object represents test properties.
  1201. * @param func
  1202. * A function to be run only if the funcOrProperies is not a function.
  1203. */
  1204. function _add_only(addFunc, funcOrProperties, func) {
  1205. _gTestHasOnly = true;
  1206. if (typeof funcOrProperties == "function") {
  1207. func = funcOrProperties;
  1208. funcOrProperties = {};
  1209. }
  1210. if (typeof funcOrProperties == "object") {
  1211. funcOrProperties._only = true;
  1212. }
  1213. return addFunc(funcOrProperties, func);
  1214. }
  1215. /**
  1216. * Helper function to skip the test using e.g. add_task.skip(...)
  1217. *
  1218. * @param addFunc
  1219. * The parent function to call, e.g. add_task or add_test.
  1220. * @param funcOrProperties
  1221. * A function to be run or an object represents test properties.
  1222. * @param func
  1223. * A function to be run only if the funcOrProperies is not a function.
  1224. */
  1225. function _add_skip(addFunc, funcOrProperties, func) {
  1226. if (typeof funcOrProperties == "function") {
  1227. func = funcOrProperties;
  1228. funcOrProperties = {};
  1229. }
  1230. if (typeof funcOrProperties == "object") {
  1231. funcOrProperties.skip_if = () => true;
  1232. }
  1233. return addFunc(funcOrProperties, func);
  1234. }
  1235. /**
  1236. * Add a test function to the list of tests that are to be run asynchronously.
  1237. *
  1238. * @param funcOrProperties
  1239. * A function to be run or an object represents test properties.
  1240. * Supported properties:
  1241. * skip_if : An arrow function which has an expression to be
  1242. * evaluated whether the test is skipped or not.
  1243. * @param func
  1244. * A function to be run only if the funcOrProperies is not a function.
  1245. *
  1246. * Each test function must call run_next_test() when it's done. Test files
  1247. * should call run_next_test() in their run_test function to execute all
  1248. * async tests.
  1249. *
  1250. * @return the test function that was passed in.
  1251. */
  1252. var _gTests = [];
  1253. function add_test(funcOrProperties, func) {
  1254. if (typeof funcOrProperties == "function") {
  1255. _gTests.push([{ _isTask: false }, funcOrProperties]);
  1256. } else if (typeof funcOrProperties == "object") {
  1257. funcOrProperties._isTask = false;
  1258. _gTests.push([funcOrProperties, func]);
  1259. } else {
  1260. do_throw("add_test() should take a function or an object and a function");
  1261. }
  1262. return func;
  1263. }
  1264. add_test.only = _add_only.bind(undefined, add_test);
  1265. add_test.skip = _add_skip.bind(undefined, add_test);
  1266. /**
  1267. * Add a test function which is a Task function.
  1268. *
  1269. * @param funcOrProperties
  1270. * A generator function to be run or an object represents test
  1271. * properties.
  1272. * Supported properties:
  1273. * skip_if : An arrow function which has an expression to be
  1274. * evaluated whether the test is skipped or not.
  1275. * @param func
  1276. * A generator function to be run only if the funcOrProperies is not a
  1277. * function.
  1278. *
  1279. * Task functions are functions fed into Task.jsm's Task.spawn(). They are
  1280. * generators that emit promises.
  1281. *
  1282. * If an exception is thrown, a do_check_* comparison fails, or if a rejected
  1283. * promise is yielded, the test function aborts immediately and the test is
  1284. * reported as a failure.
  1285. *
  1286. * Unlike add_test(), there is no need to call run_next_test(). The next test
  1287. * will run automatically as soon the task function is exhausted. To trigger
  1288. * premature (but successful) termination of the function, simply return or
  1289. * throw a Task.Result instance.
  1290. *
  1291. * Example usage:
  1292. *
  1293. * add_task(function* test() {
  1294. * let result = yield Promise.resolve(true);
  1295. *
  1296. * do_check_true(result);
  1297. *
  1298. * let secondary = yield someFunctionThatReturnsAPromise(result);
  1299. * do_check_eq(secondary, "expected value");
  1300. * });
  1301. *
  1302. * add_task(function* test_early_return() {
  1303. * let result = yield somethingThatReturnsAPromise();
  1304. *
  1305. * if (!result) {
  1306. * // Test is ended immediately, with success.
  1307. * return;
  1308. * }
  1309. *
  1310. * do_check_eq(result, "foo");
  1311. * });
  1312. *
  1313. * add_task({
  1314. * skip_if: () => !("@mozilla.org/telephony/volume-service;1" in Components.classes),
  1315. * }, function* test_volume_service() {
  1316. * let volumeService = Cc["@mozilla.org/telephony/volume-service;1"]
  1317. * .getService(Ci.nsIVolumeService);
  1318. * ...
  1319. * });
  1320. */
  1321. function add_task(funcOrProperties, func) {
  1322. if (typeof funcOrProperties == "function") {
  1323. _gTests.push([{ _isTask: true }, funcOrProperties]);
  1324. } else if (typeof funcOrProperties == "object") {
  1325. funcOrProperties._isTask = true;
  1326. _gTests.push([funcOrProperties, func]);
  1327. } else {
  1328. do_throw("add_task() should take a function or an object and a function");
  1329. }
  1330. }
  1331. add_task.only = _add_only.bind(undefined, add_task);
  1332. add_task.skip = _add_skip.bind(undefined, add_task);
  1333. var _Task = Components.utils.import("resource://gre/modules/Task.jsm", {}).Task;
  1334. _Task.Debugging.maintainStack = true;
  1335. /**
  1336. * Runs the next test function from the list of async tests.
  1337. */
  1338. var _gRunningTest = null;
  1339. var _gTestIndex = 0; // The index of the currently running test.
  1340. var _gTaskRunning = false;
  1341. var _gTestHasOnly = false;
  1342. function run_next_test()
  1343. {
  1344. if (_gTaskRunning) {
  1345. throw new Error("run_next_test() called from an add_task() test function. " +
  1346. "run_next_test() should not be called from inside add_task() " +
  1347. "under any circumstances!");
  1348. }
  1349. function _run_next_test()
  1350. {
  1351. if (_gTestIndex < _gTests.length) {
  1352. // Check for uncaught rejections as early and often as possible.
  1353. _PromiseTestUtils.assertNoUncaughtRejections();
  1354. let _properties;
  1355. [_properties, _gRunningTest,] = _gTests[_gTestIndex++];
  1356. if (typeof(_properties.skip_if) == "function" && _properties.skip_if()) {
  1357. let _condition = _properties.skip_if.toSource().replace(/\(\)\s*=>\s*/, "");
  1358. let _message = _gRunningTest.name
  1359. + " skipped because the following conditions were"
  1360. + " met: (" + _condition + ")";
  1361. _testLogger.testStatus(_TEST_NAME,
  1362. _gRunningTest.name,
  1363. "SKIP",
  1364. "SKIP",
  1365. _message);
  1366. do_execute_soon(run_next_test);
  1367. return;
  1368. }
  1369. _testLogger.info(_TEST_NAME + " | Starting " + _gRunningTest.name);
  1370. do_test_pending(_gRunningTest.name);
  1371. if (_properties._isTask) {
  1372. _gTaskRunning = true;
  1373. _Task.spawn(_gRunningTest).then(() => {
  1374. _gTaskRunning = false;
  1375. run_next_test();
  1376. }, ex => {
  1377. _gTaskRunning = false;
  1378. try {
  1379. do_report_unexpected_exception(ex);
  1380. } catch (ex) {
  1381. // The above throws NS_ERROR_ABORT and we don't want this to show up
  1382. // as an unhandled rejection later.
  1383. }
  1384. });
  1385. } else {
  1386. // Exceptions do not kill asynchronous tests, so they'll time out.
  1387. try {
  1388. _gRunningTest();
  1389. } catch (e) {
  1390. do_throw(e);
  1391. }
  1392. }
  1393. }
  1394. }
  1395. // For sane stacks during failures, we execute this code soon, but not now.
  1396. // We do this now, before we call do_test_finished(), to ensure the pending
  1397. // counter (_tests_pending) never reaches 0 while we still have tests to run
  1398. // (do_execute_soon bumps that counter).
  1399. do_execute_soon(_run_next_test, "run_next_test " + _gTestIndex);
  1400. if (_gRunningTest !== null) {
  1401. // Close the previous test do_test_pending call.
  1402. do_test_finished(_gRunningTest.name);
  1403. }
  1404. }
  1405. try {
  1406. if (runningInParent) {
  1407. // Always use network provider for geolocation tests
  1408. // so we bypass the OSX dialog raised by the corelocation provider
  1409. let prefs = Components.classes["@mozilla.org/preferences-service;1"]
  1410. .getService(Components.interfaces.nsIPrefBranch);
  1411. prefs.setBoolPref("geo.provider.testing", true);
  1412. }
  1413. } catch (e) { }
  1414. // We need to avoid hitting the network with certain components.
  1415. try {
  1416. if (runningInParent) {
  1417. let prefs = Components.classes["@mozilla.org/preferences-service;1"]
  1418. .getService(Components.interfaces.nsIPrefBranch);
  1419. prefs.setCharPref("media.gmp-manager.url.override", "http://%(server)s/dummy-gmp-manager.xml");
  1420. prefs.setCharPref("media.gmp-manager.updateEnabled", false);
  1421. prefs.setCharPref("extensions.systemAddon.update.url", "http://%(server)s/dummy-system-addons.xml");
  1422. prefs.setCharPref("browser.selfsupport.url", "https://%(server)s/selfsupport-dummy/");
  1423. prefs.setCharPref("toolkit.telemetry.server", "https://%(server)s/telemetry-dummy");
  1424. prefs.setCharPref("browser.search.geoip.url", "https://%(server)s/geoip-dummy");
  1425. }
  1426. } catch (e) { }
  1427. // Make tests run consistently on DevEdition (which has a lightweight theme
  1428. // selected by default).
  1429. try {
  1430. if (runningInParent) {
  1431. let prefs = Components.classes["@mozilla.org/preferences-service;1"]
  1432. .getService(Components.interfaces.nsIPrefBranch);
  1433. prefs.deleteBranch("lightweightThemes.selectedThemeID");
  1434. prefs.deleteBranch("browser.devedition.theme.enabled");
  1435. }
  1436. } catch (e) { }
  1437. function _load_mozinfo() {
  1438. let mozinfoFile = Components.classes["@mozilla.org/file/local;1"]
  1439. .createInstance(Components.interfaces.nsIFile);
  1440. mozinfoFile.initWithPath(_MOZINFO_JS_PATH);
  1441. let stream = Components.classes["@mozilla.org/network/file-input-stream;1"]
  1442. .createInstance(Components.interfaces.nsIFileInputStream);
  1443. stream.init(mozinfoFile, -1, 0, 0);
  1444. let json = Components.classes["@mozilla.org/dom/json;1"]
  1445. .createInstance(Components.interfaces.nsIJSON);
  1446. let mozinfo = json.decodeFromStream(stream, stream.available());
  1447. stream.close();
  1448. return mozinfo;
  1449. }
  1450. Object.defineProperty(this, "mozinfo", {
  1451. configurable: true,
  1452. get() {
  1453. let _mozinfo = _load_mozinfo();
  1454. Object.defineProperty(this, "mozinfo", {
  1455. configurable: false,
  1456. value: _mozinfo
  1457. });
  1458. return _mozinfo;
  1459. }
  1460. });