aboutsummaryrefslogtreecommitdiff
path: root/tools/addon-sdk-1.3/packages/test-harness
diff options
context:
space:
mode:
Diffstat (limited to 'tools/addon-sdk-1.3/packages/test-harness')
-rw-r--r--tools/addon-sdk-1.3/packages/test-harness/README.md8
-rw-r--r--tools/addon-sdk-1.3/packages/test-harness/docs/harness.md2
-rw-r--r--tools/addon-sdk-1.3/packages/test-harness/docs/run-tests.md9
-rw-r--r--tools/addon-sdk-1.3/packages/test-harness/lib/harness.js365
-rw-r--r--tools/addon-sdk-1.3/packages/test-harness/lib/run-tests.js121
-rw-r--r--tools/addon-sdk-1.3/packages/test-harness/package.json9
-rw-r--r--tools/addon-sdk-1.3/packages/test-harness/tests/test-packaging.js30
7 files changed, 544 insertions, 0 deletions
diff --git a/tools/addon-sdk-1.3/packages/test-harness/README.md b/tools/addon-sdk-1.3/packages/test-harness/README.md
new file mode 100644
index 0000000..13e2b66
--- /dev/null
+++ b/tools/addon-sdk-1.3/packages/test-harness/README.md
@@ -0,0 +1,8 @@
+<span class="aside">
+For more information on testing in the Add-on SDK, see the
+[Reusable Modules](#guide/addon-development/implementing-reusable-module)
+tutorial.
+</span>
+
+This package contains a program that finds and runs tests. It is
+automatically used whenever the `cfx test` command is executed.
diff --git a/tools/addon-sdk-1.3/packages/test-harness/docs/harness.md b/tools/addon-sdk-1.3/packages/test-harness/docs/harness.md
new file mode 100644
index 0000000..9f6cd7b
--- /dev/null
+++ b/tools/addon-sdk-1.3/packages/test-harness/docs/harness.md
@@ -0,0 +1,2 @@
+This module contains the bulk of the test harness setup and execution
+implementation.
diff --git a/tools/addon-sdk-1.3/packages/test-harness/docs/run-tests.md b/tools/addon-sdk-1.3/packages/test-harness/docs/run-tests.md
new file mode 100644
index 0000000..ad64606
--- /dev/null
+++ b/tools/addon-sdk-1.3/packages/test-harness/docs/run-tests.md
@@ -0,0 +1,9 @@
+<span class="aside">
+For more information on testing in the Add-on SDK, see the
+[Reusable Modules](#guide/addon-development/implementing-reusable-module)
+tutorial.
+</span>
+
+This module contains the package's main program, which does a
+bit of high-level setup and then delegates test finding and running to
+the `harness` module.
diff --git a/tools/addon-sdk-1.3/packages/test-harness/lib/harness.js b/tools/addon-sdk-1.3/packages/test-harness/lib/harness.js
new file mode 100644
index 0000000..7f000b7
--- /dev/null
+++ b/tools/addon-sdk-1.3/packages/test-harness/lib/harness.js
@@ -0,0 +1,365 @@
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Jetpack.
+ *
+ * The Initial Developer of the Original Code is Mozilla.
+ * Portions created by the Initial Developer are Copyright (C) 2007
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Atul Varma <atul@mozilla.com>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+"use strict";
+
+var {Cc,Ci} = require("chrome");
+
+var cService = Cc['@mozilla.org/consoleservice;1'].getService()
+ .QueryInterface(Ci.nsIConsoleService);
+
+// Cuddlefish loader for the sandbox in which we load and
+// execute tests.
+var sandbox;
+
+// Function to call when we're done running tests.
+var onDone;
+
+// Function to print text to a console, w/o CR at the end.
+var print;
+
+// The directories to look for tests in.
+var dirs;
+
+// How many more times to run all tests.
+var iterationsLeft;
+
+// Only tests in files whose names match this regexp filter will be run.
+var filter;
+
+// Whether to report memory profiling information.
+var profileMemory;
+
+// Combined information from all test runs.
+var results = {
+ passed: 0,
+ failed: 0,
+ testRuns: []
+};
+
+// JSON serialization of last memory usage stats; we keep it stringified
+// so we don't actually change the memory usage stats (in terms of objects)
+// of the JSRuntime we're profiling.
+var lastMemoryUsage;
+
+function analyzeRawProfilingData(data) {
+ var graph = data.graph;
+ var shapes = {};
+
+ // Convert keys in the graph from strings to ints.
+ // TODO: Can we get rid of this ridiculousness?
+ var newGraph = {};
+ for (id in graph) {
+ newGraph[parseInt(id)] = graph[id];
+ }
+ graph = newGraph;
+
+ var modules = 0;
+ var moduleIds = [];
+ var moduleObjs = {UNKNOWN: 0};
+ for (let name in data.namedObjects) {
+ moduleObjs[name] = 0;
+ moduleIds[data.namedObjects[name]] = name;
+ modules++;
+ }
+
+ var count = 0;
+ for (id in graph) {
+ var parent = graph[id].parent;
+ while (parent) {
+ if (parent in moduleIds) {
+ var name = moduleIds[parent];
+ moduleObjs[name]++;
+ break;
+ }
+ if (!(parent in graph)) {
+ moduleObjs.UNKNOWN++;
+ break;
+ }
+ parent = graph[parent].parent;
+ }
+ count++;
+ }
+
+ print("\nobject count is " + count + " in " + modules + " modules" +
+ " (" + data.totalObjectCount + " across entire JS runtime)\n");
+ if (lastMemoryUsage) {
+ var last = JSON.parse(lastMemoryUsage);
+ var diff = {
+ moduleObjs: dictDiff(last.moduleObjs, moduleObjs),
+ totalObjectClasses: dictDiff(last.totalObjectClasses,
+ data.totalObjectClasses)
+ };
+
+ for (let name in diff.moduleObjs)
+ print(" " + diff.moduleObjs[name] + " in " + name + "\n");
+ for (let name in diff.totalObjectClasses)
+ print(" " + diff.totalObjectClasses[name] + " instances of " +
+ name + "\n");
+ }
+ lastMemoryUsage = JSON.stringify(
+ {moduleObjs: moduleObjs,
+ totalObjectClasses: data.totalObjectClasses}
+ );
+}
+
+function dictDiff(last, curr) {
+ var diff = {};
+
+ for (let name in last) {
+ var result = (curr[name] || 0) - last[name];
+ if (result)
+ diff[name] = (result > 0 ? "+" : "") + result;
+ }
+ for (let name in curr) {
+ var result = curr[name] - (last[name] || 0);
+ if (result)
+ diff[name] = (result > 0 ? "+" : "") + result;
+ }
+ return diff;
+}
+
+function reportMemoryUsage() {
+ memory.gc();
+ sandbox.memory.gc();
+
+ var mgr = Cc["@mozilla.org/memory-reporter-manager;1"]
+ .getService(Ci.nsIMemoryReporterManager);
+ var reporters = mgr.enumerateReporters();
+ if (reporters.hasMoreElements())
+ print("\n");
+ while (reporters.hasMoreElements()) {
+ var reporter = reporters.getNext();
+ reporter.QueryInterface(Ci.nsIMemoryReporter);
+ print(reporter.description + ": " + reporter.memoryUsed + "\n");
+ }
+
+ var weakrefs = [info.weakref.get()
+ for each (info in sandbox.memory.getObjects())];
+ weakrefs = [weakref for each (weakref in weakrefs) if (weakref)];
+ print("Tracked memory objects in testing sandbox: " +
+ weakrefs.length + "\n");
+}
+
+var gWeakrefInfo;
+
+function showResults() {
+ memory.gc();
+
+ if (gWeakrefInfo) {
+ gWeakrefInfo.forEach(
+ function(info) {
+ var ref = info.weakref.get();
+ if (ref !== null) {
+ var data = ref.__url__ ? ref.__url__ : ref;
+ var warning = data == "[object Object]"
+ ? "[object " + data.constructor.name + "(" +
+ [p for (p in data)].join(", ") + ")]"
+ : data;
+ console.warn("LEAK", warning, info.bin);
+ }
+ }
+ );
+ }
+
+ print("\n");
+ var total = results.passed + results.failed;
+ print(results.passed + " of " + total + " tests passed.\n");
+ onDone(results);
+}
+
+function cleanup() {
+ try {
+ for (let name in sandbox.sandboxes)
+ sandbox.memory.track(sandbox.sandboxes[name].globalScope,
+ "module global scope: " + name);
+ sandbox.memory.track(sandbox, "Cuddlefish Loader");
+
+ if (profileMemory) {
+ gWeakrefInfo = [{ weakref: info.weakref, bin: info.bin }
+ for each (info in sandbox.memory.getObjects())];
+ }
+
+ sandbox.unload();
+
+ if (sandbox.console.errorsLogged && !results.failed) {
+ results.failed++;
+ console.error("warnings and/or errors were logged.");
+ }
+
+ if (consoleListener.errorsLogged && !results.failed) {
+ console.warn(consoleListener.errorsLogged + " " +
+ "warnings or errors were logged to the " +
+ "platform's nsIConsoleService, which could " +
+ "be of no consequence; however, they could also " +
+ "be indicative of aberrant behavior.");
+ }
+
+ consoleListener.errorsLogged = 0;
+ sandbox = null;
+
+ memory.gc();
+ } catch (e) {
+ results.failed++;
+ console.error("unload.send() threw an exception.");
+ console.exception(e);
+ };
+
+ require("api-utils/timer").setTimeout(showResults, 1);
+}
+
+function nextIteration(tests) {
+ if (tests) {
+ results.passed += tests.passed;
+ results.failed += tests.failed;
+
+ if (profileMemory)
+ reportMemoryUsage();
+
+ let testRun = [];
+ for each (let test in tests.testRunSummary) {
+ let testCopy = {};
+ for (let info in test) {
+ testCopy[info] = test[info];
+ }
+ testRun.push(testCopy);
+ }
+
+ results.testRuns.push(testRun);
+ iterationsLeft--;
+ }
+ if (iterationsLeft)
+ sandbox.require("api-utils/unit-test").findAndRunTests({
+ testOutOfProcess: packaging.enableE10s,
+ testInProcess: true,
+ fs: sandbox.fs,
+ dirs: dirs,
+ filter: filter,
+ onDone: nextIteration
+ });
+ else
+ require("api-utils/timer").setTimeout(cleanup, 0);
+}
+
+var POINTLESS_ERRORS = [
+ "Invalid chrome URI:",
+ "OpenGL LayerManager Initialized Succesfully."
+];
+
+var consoleListener = {
+ errorsLogged: 0,
+ observe: function(object) {
+ if (!(object instanceof Ci.nsIScriptError))
+ return;
+ this.errorsLogged++;
+ var message = object.QueryInterface(Ci.nsIConsoleMessage).message;
+ var pointless = [err for each (err in POINTLESS_ERRORS)
+ if (message.indexOf(err) == 0)];
+ if (pointless.length == 0 && message)
+ print("console: " + message + "\n");
+ }
+};
+
+function TestRunnerConsole(base, options) {
+ this.__proto__ = {
+ errorsLogged: 0,
+ warn: function warn() {
+ this.errorsLogged++;
+ base.warn.apply(base, arguments);
+ },
+ error: function error() {
+ this.errorsLogged++;
+ base.error.apply(base, arguments);
+ },
+ info: function info(first) {
+ if (options.verbose)
+ base.info.apply(base, arguments);
+ else
+ if (first == "pass:")
+ print(".");
+ },
+ __proto__: base
+ };
+}
+
+var runTests = exports.runTests = function runTests(options) {
+ iterationsLeft = options.iterations;
+ filter = options.filter;
+ profileMemory = options.profileMemory;
+ onDone = options.onDone;
+ print = options.print;
+
+ try {
+ cService.registerListener(consoleListener);
+
+ var cuddlefish = require("api-utils/cuddlefish");
+ var ptc = require("api-utils/plain-text-console");
+ var url = require("api-utils/url");
+
+ dirs = [url.toFilename(path)
+ for each (path in options.rootPaths)];
+ var console = new TestRunnerConsole(new ptc.PlainTextConsole(print),
+ options);
+ var globals = {packaging: packaging};
+
+ var xulApp = require("api-utils/xul-app");
+ var xulRuntime = Cc["@mozilla.org/xre/app-info;1"]
+ .getService(Ci.nsIXULRuntime);
+
+ print("Running tests on " + xulApp.name + " " + xulApp.version +
+ "/Gecko " + xulApp.platformVersion + " (" +
+ xulApp.ID + ") under " +
+ xulRuntime.OS + "/" + xulRuntime.XPCOMABI + ".\n");
+
+ sandbox = new cuddlefish.Loader({console: console,
+ globals: globals,
+ metadata: packaging.options.metadata,
+ jetpackID: packaging.options.jetpackID,
+ uriPrefix: packaging.options.uriPrefix,
+ name: packaging.options.name,
+ packaging: packaging,
+ __proto__: options});
+ nextIteration();
+ } catch (e) {
+ print(require("api-utils/traceback").format(e) + "\n" + e + "\n");
+ onDone({passed: 0, failed: 1});
+ }
+};
+
+require("api-utils/unload").when(
+ function() {
+ cService.unregisterListener(consoleListener);
+ });
diff --git a/tools/addon-sdk-1.3/packages/test-harness/lib/run-tests.js b/tools/addon-sdk-1.3/packages/test-harness/lib/run-tests.js
new file mode 100644
index 0000000..bcf76e4
--- /dev/null
+++ b/tools/addon-sdk-1.3/packages/test-harness/lib/run-tests.js
@@ -0,0 +1,121 @@
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Jetpack.
+ *
+ * The Initial Developer of the Original Code is Mozilla.
+ * Portions created by the Initial Developer are Copyright (C) 2007
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Atul Varma <atul@mozilla.com>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+"use strict";
+var obsvc = require("api-utils/observer-service");
+var {Cc,Ci} = require("chrome");
+
+function runTests(iterations, filter, profileMemory, verbose, rootPaths, quit, print) {
+ var ww = Cc["@mozilla.org/embedcomp/window-watcher;1"]
+ .getService(Ci.nsIWindowWatcher);
+
+ var window = ww.openWindow(null, "data:text/plain,Running tests...",
+ "harness", "centerscreen", null);
+
+ var harness = require("./harness");
+
+ function onDone(tests) {
+ window.close();
+ if (tests.passed > 0 && tests.failed == 0) {
+ quit("OK");
+ } else {
+ if (tests.passed == 0) {
+ print("No tests were run\n");
+ } else {
+ printFailedTests(tests, verbose, print);
+ }
+ quit("FAIL");
+ }
+ };
+
+ harness.runTests({iterations: iterations,
+ filter: filter,
+ profileMemory: profileMemory,
+ verbose: verbose,
+ rootPaths: rootPaths,
+ print: print,
+ onDone: onDone});
+}
+
+function printFailedTests(tests, verbose, print) {
+ if (!verbose)
+ return;
+
+ let iterationNumber = 0;
+ let singleIteration = tests.testRuns.length == 1;
+ let padding = singleIteration ? "" : " ";
+
+ print("\nThe following tests failed:\n");
+
+ for each (let testRun in tests.testRuns) {
+ iterationNumber++;
+
+ if (!singleIteration)
+ print(" Iteration " + iterationNumber + ":\n");
+
+ for each (let test in testRun) {
+ if (test.failed > 0) {
+ print(padding + " " + test.name + ": " + test.errors +"\n");
+ }
+ }
+ print("\n");
+ }
+}
+
+exports.main = function main(options, callbacks) {
+ var testsStarted = false;
+
+ function doRunTests() {
+ if (!testsStarted) {
+ testsStarted = true;
+ runTests(options.iterations, options.filter,
+ options.profileMemory, options.verbose,
+ options.rootPaths, callbacks.quit,
+ callbacks.print);
+ }
+ }
+
+ // TODO: This is optional code that might be put in by
+ // something running this code to force it to just
+ // run tests immediately, rather than wait. We need
+ // to actually standardize on this, though.
+ if (options.runImmediately) {
+ doRunTests();
+ }
+ else {
+ obsvc.add(obsvc.topics.APPLICATION_READY, doRunTests);
+ }
+};
diff --git a/tools/addon-sdk-1.3/packages/test-harness/package.json b/tools/addon-sdk-1.3/packages/test-harness/package.json
new file mode 100644
index 0000000..9a81139
--- /dev/null
+++ b/tools/addon-sdk-1.3/packages/test-harness/package.json
@@ -0,0 +1,9 @@
+{
+ "name": "test-harness",
+ "description": "A harness for running Jetpack tests.",
+ "author": "Atul Varma (http://toolness.com/)",
+ "keywords": ["jetpack-low-level"],
+ "version": "1.3",
+ "license": "MPL 1.1/GPL 2.0/LGPL 2.1",
+ "dependencies": ["api-utils"]
+}
diff --git a/tools/addon-sdk-1.3/packages/test-harness/tests/test-packaging.js b/tools/addon-sdk-1.3/packages/test-harness/tests/test-packaging.js
new file mode 100644
index 0000000..fb1f616
--- /dev/null
+++ b/tools/addon-sdk-1.3/packages/test-harness/tests/test-packaging.js
@@ -0,0 +1,30 @@
+var url = require("url");
+var file = require("file");
+var {Cm,Ci} = require("chrome");
+
+exports.testPackaging = function(test) {
+ test.assertEqual(packaging.options.main,
+ 'test-harness/run-tests',
+ "main program should be the test harness");
+
+ var factory = Cm.getClassObjectByContractID(
+ packaging.options.bootstrap.contractID,
+ Ci.nsIFactory
+ );
+
+ var harness = factory.wrappedJSObject.singleton;
+
+ test.assertEqual(packaging.harnessService, harness);
+
+ test.assertNotEqual(harness.loader, undefined,
+ "bootstrap component should be available");
+
+ test.assertEqual(JSON.stringify(harness.options),
+ JSON.stringify(packaging.options),
+ ("bootstrap component options should be identical " +
+ "to packaging.options"));
+
+ test.assertEqual(packaging.options.metadata['test-harness'].author,
+ 'Atul Varma (http://toolness.com/)',
+ "packaging metadata should be available");
+};