jetpack-addon-harness.js 7.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236
  1. /* -*- js-indent-level: 2; tab-width: 2; indent-tabs-mode: nil -*- */
  2. var gConfig;
  3. if (Cc === undefined) {
  4. var Cc = Components.classes;
  5. var Ci = Components.interfaces;
  6. var Cu = Components.utils;
  7. }
  8. Cu.import("resource://gre/modules/XPCOMUtils.jsm");
  9. Cu.import("resource://gre/modules/Task.jsm");
  10. XPCOMUtils.defineLazyModuleGetter(this, "Services",
  11. "resource://gre/modules/Services.jsm");
  12. XPCOMUtils.defineLazyModuleGetter(this, "AddonManager",
  13. "resource://gre/modules/AddonManager.jsm");
  14. // How long to wait for an add-on to uninstall before aborting
  15. const MAX_UNINSTALL_TIME = 10000;
  16. setTimeout(testInit, 0);
  17. var sdkpath = null;
  18. // Strip off the chrome prefix to get the actual path of the test directory
  19. function realPath(chrome) {
  20. return chrome.substring("chrome://mochitests/content/jetpack-addon/".length)
  21. .replace(".xpi", "");
  22. }
  23. const chromeRegistry = Cc["@mozilla.org/chrome/chrome-registry;1"]
  24. .getService(Ci.nsIChromeRegistry);
  25. // Installs a single add-on returning a promise for when install is completed
  26. function installAddon(url) {
  27. let chromeURL = Services.io.newURI(url, null, null);
  28. let file = chromeRegistry.convertChromeURL(chromeURL)
  29. .QueryInterface(Ci.nsIFileURL).file;
  30. let addon;
  31. const listener = {
  32. onInstalling(_addon) {
  33. addon = _addon;
  34. // Set add-on's test options
  35. const options = {
  36. test: {
  37. iterations: 1,
  38. stop: false,
  39. keepOpen: true,
  40. },
  41. profile: {
  42. memory: false,
  43. leaks: false,
  44. },
  45. output: {
  46. logLevel: "verbose",
  47. format: "tbpl",
  48. },
  49. console: {
  50. logLevel: "info",
  51. },
  52. }
  53. setPrefs("extensions." + addon.id + ".sdk", options);
  54. // If necessary override the add-ons module paths to point somewhere
  55. // else
  56. if (sdkpath) {
  57. let paths = {}
  58. for (let path of ["dev", "diffpatcher", "framescript", "method", "node", "sdk", "toolkit"]) {
  59. paths[path] = sdkpath + path;
  60. }
  61. setPrefs("extensions.modules." + addon.id + ".path", paths);
  62. }
  63. },
  64. };
  65. AddonManager.addAddonListener(listener);
  66. return AddonManager.installTemporaryAddon(file)
  67. .then(() => {
  68. AddonManager.removeAddonListener(listener);
  69. return addon;
  70. });
  71. }
  72. // Uninstalls an add-on returning a promise for when it is gone
  73. function uninstallAddon(oldAddon) {
  74. return new Promise(function(resolve, reject) {
  75. AddonManager.addAddonListener({
  76. onUninstalled: function(addon) {
  77. if (addon.id != oldAddon.id)
  78. return;
  79. AddonManager.removeAddonListener(this);
  80. dump("TEST-INFO | jetpack-addon-harness.js | Uninstalled test add-on " + addon.id + "\n");
  81. // Some add-ons do async work on uninstall, we must wait for that to
  82. // complete
  83. setTimeout(resolve, 500);
  84. }
  85. });
  86. oldAddon.uninstall();
  87. // The uninstall should happen quickly, if not throw an exception
  88. setTimeout(() => {
  89. reject(new Error(`Addon ${oldAddon.id} failed to uninstall in a timely fashion.`));
  90. }, MAX_UNINSTALL_TIME);
  91. });
  92. }
  93. // Waits for a test add-on to signal it has completed its tests
  94. function waitForResults() {
  95. return new Promise(function(resolve, reject) {
  96. Services.obs.addObserver(function(subject, topic, data) {
  97. Services.obs.removeObserver(arguments.callee, "sdk:test:results");
  98. resolve(JSON.parse(data));
  99. }, "sdk:test:results", false);
  100. });
  101. }
  102. // Runs tests for the add-on available at URL.
  103. var testAddon = Task.async(function*({ url }) {
  104. dump("TEST-INFO | jetpack-addon-harness.js | Installing test add-on " + realPath(url) + "\n");
  105. let addon = yield installAddon(url);
  106. let results = yield waitForResults();
  107. dump("TEST-INFO | jetpack-addon-harness.js | Uninstalling test add-on " + addon.id + "\n");
  108. yield uninstallAddon(addon);
  109. dump("TEST-INFO | jetpack-addon-harness.js | Testing add-on " + realPath(url) + " is complete\n");
  110. return results;
  111. });
  112. // Sets a set of prefs for test add-ons
  113. function setPrefs(root, options) {
  114. Object.keys(options).forEach(id => {
  115. const key = root + "." + id;
  116. const value = options[id]
  117. const type = typeof(value);
  118. value === null ? void(0) :
  119. value === undefined ? void(0) :
  120. type === "boolean" ? Services.prefs.setBoolPref(key, value) :
  121. type === "string" ? Services.prefs.setCharPref(key, value) :
  122. type === "number" ? Services.prefs.setIntPref(key, parseInt(value)) :
  123. type === "object" ? setPrefs(key, value) :
  124. void(0);
  125. });
  126. }
  127. function testInit() {
  128. // Make sure to run the test harness for the first opened window only
  129. if (Services.prefs.prefHasUserValue("testing.jetpackTestHarness.running"))
  130. return;
  131. Services.prefs.setBoolPref("testing.jetpackTestHarness.running", true);
  132. // Get the list of tests to run
  133. let config = readConfig();
  134. getTestList(config, function(links) {
  135. try {
  136. let fileNames = [];
  137. let fileNameRegexp = /.+\.xpi$/;
  138. arrayOfTestFiles(links, fileNames, fileNameRegexp);
  139. if (config.startAt || config.endAt) {
  140. fileNames = skipTests(fileNames, config.startAt, config.endAt);
  141. }
  142. // Override the SDK modules if necessary
  143. try {
  144. let sdklibs = Services.prefs.getCharPref("extensions.sdk.path");
  145. // sdkpath is a file path, make it a URI
  146. let sdkfile = Cc["@mozilla.org/file/local;1"].
  147. createInstance(Ci.nsIFile);
  148. sdkfile.initWithPath(sdklibs);
  149. sdkpath = Services.io.newFileURI(sdkfile).spec;
  150. }
  151. catch (e) {
  152. // Stick with the built-in modules
  153. }
  154. let passed = 0;
  155. let failed = 0;
  156. function finish() {
  157. if (passed + failed == 0) {
  158. dump("TEST-UNEXPECTED-FAIL | jetpack-addon-harness.js | " +
  159. "No tests to run. Did you pass invalid test_paths?\n");
  160. }
  161. else {
  162. dump("Jetpack Addon Test Summary\n");
  163. dump("\tPassed: " + passed + "\n" +
  164. "\tFailed: " + failed + "\n" +
  165. "\tTodo: 0\n");
  166. }
  167. if (config.closeWhenDone) {
  168. dump("TEST-INFO | jetpack-addon-harness.js | Shutting down.\n");
  169. const appStartup = Cc['@mozilla.org/toolkit/app-startup;1'].
  170. getService(Ci.nsIAppStartup);
  171. appStartup.quit(appStartup.eAttemptQuit);
  172. }
  173. }
  174. function testNextAddon() {
  175. if (fileNames.length == 0)
  176. return finish();
  177. let filename = fileNames.shift();
  178. dump("TEST-INFO | jetpack-addon-harness.js | Starting test add-on " + realPath(filename.url) + "\n");
  179. testAddon(filename).then(results => {
  180. passed += results.passed;
  181. failed += results.failed;
  182. }).then(testNextAddon, error => {
  183. // If something went wrong during the test then a previous test add-on
  184. // may still be installed, this leaves us in an unexpected state so
  185. // probably best to just abandon testing at this point
  186. failed++;
  187. dump("TEST-UNEXPECTED-FAIL | jetpack-addon-harness.js | Error testing " + realPath(filename.url) + ": " + error + "\n");
  188. finish();
  189. });
  190. }
  191. testNextAddon();
  192. }
  193. catch (e) {
  194. dump("TEST-UNEXPECTED-FAIL | jetpack-addon-harness.js | error starting test harness (" + e + ")\n");
  195. dump(e.stack);
  196. }
  197. });
  198. }