Merge m-c to m-i
authorPhil Ringnalda <philringnalda@gmail.com>
Fri, 04 Oct 2013 22:17:30 -0700
changeset 149986 e40163b985c006f6533c1887127ed606f18bb953
parent 149985 97bdee4bd25998ff4085b19ebfa1043c62704d23 (current diff)
parent 149962 b5d24ef1eb37468d75acf77cfed7fca12b84d948 (diff)
child 149987 4a0abb47b3e67ca4d85972910fd1bb06aa485884
push id25412
push userphilringnalda@gmail.com
push dateSun, 06 Oct 2013 00:39:45 +0000
treeherdermozilla-central@f191c70fcbfb [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
milestone27.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge m-c to m-i
--- a/addon-sdk/source/bin/activate
+++ b/addon-sdk/source/bin/activate
@@ -20,21 +20,19 @@ deactivate () {
     fi
 
     if [ -n "$_OLD_VIRTUAL_PS1" ] ; then
         PS1="$_OLD_VIRTUAL_PS1"
         export PS1
         unset _OLD_VIRTUAL_PS1
     fi
 
-    if [ -n "$_OLD_PYTHONPATH" ] ; then
-        PYTHONPATH="$_OLD_PYTHONPATH"
-        export PYTHONPATH
-        unset _OLD_PYTHONPATH
-    fi
+    PYTHONPATH="$_OLD_PYTHONPATH"
+    export PYTHONPATH
+    unset _OLD_PYTHONPATH
 
     unset CUDDLEFISH_ROOT
 
     unset VIRTUAL_ENV
     if [ ! "$1" = "nondestructive" ] ; then
     # Self destruct!
         unset deactivate
     fi
--- a/addon-sdk/source/doc/module-source/sdk/test/utils.md
+++ b/addon-sdk/source/doc/module-source/sdk/test/utils.md
@@ -83,8 +83,29 @@ finished:
    * the first argument is the test's name as a `String`.
    * the second argument is the `assert` object for the test.
    * the third, optional, argument is a callback. If the callback is
     defined, then the `afterFn` is considered asynchronous, and the
     callback must be invoked before the next test runs.
 
 </api>
 
+<api name="waitUntil">
+@function
+  `waitUntil` returns a promise that resolves upon the
+  `predicate` returning a truthy value, which is called every
+  `interval` milliseconds.
+
+ @param predicate {Function}
+    A function that gets called every `interval` milliseconds
+    to determine if the promise should be resolved.
+
+ @param [interval] {Number}
+    The frequency in milliseconds to execute `predicate`.
+    Defaults to `10`.
+
+ @returns {Promise}
+    `waitUntil` returns a promise that becomes resolved once
+    the `predicate` returns a truthy value. The promise cannot
+    be rejected.
+
+</api>
+
new file mode 100644
--- /dev/null
+++ b/addon-sdk/source/doc/module-source/sdk/ui/id.md
@@ -0,0 +1,42 @@
+<!-- This Source Code Form is subject to the terms of the Mozilla Public
+   - License, v. 2.0. If a copy of the MPL was not distributed with this
+   - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
+
+The `ui/id` module provides the `identify` helper method for creating
+and defining UI component IDs.
+
+<api name="identify">
+@function
+Makes and/or gets a unique ID for the input.
+
+Making an ID
+
+    const { identify } = require('sdk/ui/id');
+
+    const Thingy = Class({
+      initialize: function(details) {
+        let id = identify(this);
+      }
+    });
+
+Getting an ID
+
+    const { identify } = require('sdk/ui/id');
+    const { Thingy } = require('./thingy');
+
+    let thing = Thingy(/* ... */);
+    let thingID = identify(thing);
+
+Defining ID generator
+
+    const { identify } = require('sdk/ui/id');
+
+    const Thingy = Class(/* ... */);
+
+    identify.define(Thingy, thing => thing.guid);
+
+@param object {Object}
+  Object to create an ID for.
+@returns {String}
+  Returns a UUID by default (or domain specific ID based on a provided definition).
+</api>
--- a/addon-sdk/source/doc/module-source/toolkit/loader.md
+++ b/addon-sdk/source/doc/module-source/toolkit/loader.md
@@ -52,16 +52,18 @@ to instantiate new loader instances:
 `Loader()` may be provided with a set of configuration options:
 
 * [`paths`](modules/toolkit/loader.html#paths): describes where the loader should
 find the modules it is asked to load. **Mandatory**.
 * [`modules`](modules/toolkit/loader.html#modules): provides a set of module exports. **Optional**.
 * [`globals`](modules/toolkit/loader.html#globals): provides a set of globals shared across modules loaded
 via this loader. **Optional**.
 * [`resolve`](modules/toolkit/loader.html#resolve): provide customized module resolution logic. **Optional**.
+* [`id`](modules/toolkit/loader.html#id): provide add-on id to attach to loaded
+modules. **Optional**.
 
 ### paths
 
 The loader needs to be provided with a set of locations to indicate
 where to find modules loaded using `require()`. This is provided by
 a mandatory `options.paths` hash that represents the mapping between
 module ID prefixes and locations. There are lots of
 different possibilities, but the most common setup looks like this:
@@ -227,16 +229,22 @@ exception:
       }
     });
 
 Thrown exceptions will propagate to the caller of `require()`. If
 the function assigned to `resolve`
 does not return a string value, an exception will still be thrown as
 the loader will be unable to resolve the required module's location.
 
+### id
+
+Add-on debugging requires knowing which objects belong to which add-on.
+When created with this option, Loader will transparently mark all new global
+objects with the provided value.
+
 ### All Together
 
 All of these options can be combined to configure the loader for
 a specific use case. Don't get too excited about configuration options, keep
 in mind that modules are more useful if they can be used across
 loader instances. Unless you have specific needs it's best to stick to an
 SDK-compatible configuration, like this:
 
--- a/addon-sdk/source/lib/sdk/addon/installer.js
+++ b/addon-sdk/source/lib/sdk/addon/installer.js
@@ -94,16 +94,23 @@ exports.uninstall = function uninstall(a
 
 exports.disable = function disable(addonId) {
   return getAddon(addonId).then(addon => {
     addon.userDisabled = true;
     return addonId;
   });
 };
 
+exports.enable = function enabled(addonId) {
+  return getAddon(addonId).then(addon => {
+    addon.userDisabled = false;
+    return addonId;
+  });
+};
+
 exports.isActive = function isActive(addonId) {
   return getAddon(addonId).then(addon => addon.isActive && !addon.appDisabled);
 };
 
 function getAddon (id) {
   let { promise, resolve, reject } = defer();
   AddonManager.getAddonByID(id, addon => addon ? resolve(addon) : reject());
   return promise;
--- a/addon-sdk/source/lib/sdk/addon/runner.js
+++ b/addon-sdk/source/lib/sdk/addon/runner.js
@@ -107,16 +107,17 @@ function startup(reason, options) {
     then(function onLocalizationReady(data) {
       // Exports data to a pseudo module so that api-utils/l10n/core
       // can get access to it
       definePseudo(options.loader, '@l10n/data', data ? data : null);
       return ready;
     }).then(function() {
       run(options);
     }).then(null, console.exception);
+    return void 0; // otherwise we raise a warning, see bug 910304
 }
 
 function run(options) {
   try {
     // Try initializing HTML localization before running main module. Just print
     // an exception in case of error, instead of preventing addon to be run.
     try {
       // Do not enable HTML localization while running test as it is hard to
--- a/addon-sdk/source/lib/sdk/content/worker.js
+++ b/addon-sdk/source/lib/sdk/content/worker.js
@@ -145,17 +145,18 @@ const WorkerSandbox = EventEmitter.compo
     apiSandbox.console = console;
 
     // Create the sandbox and bind it to window in order for content scripts to
     // have access to all standard globals (window, document, ...)
     let content = this._sandbox = sandbox(principals, {
       sandboxPrototype: proto,
       wantXrays: true,
       wantGlobalProperties: wantGlobalProperties,
-      sameZoneAs: window
+      sameZoneAs: window,
+      metadata: { SDKContentScript: true }
     });
     // We have to ensure that window.top and window.parent are the exact same
     // object than window object, i.e. the sandbox global object. But not
     // always, in case of iframes, top and parent are another window object.
     let top = window.top === window ? content : content.top;
     let parent = window.parent === window ? content : content.parent;
     merge(content, {
       // We need "this === window === top" to be true in toplevel scope:
@@ -549,16 +550,17 @@ const Worker = EventEmitter.compose({
   },
 
   /**
    * Tells content worker to unload itself and
    * removes all the references from itself.
    */
   destroy: function destroy() {
     this._workerCleanup();
+    this._inited = true;
     this._removeAllListeners();
   },
 
   /**
    * Remove all internal references to the attached document
    * Tells _port to unload itself and removes all the references from itself.
    */
   _workerCleanup: function _workerCleanup() {
@@ -575,16 +577,17 @@ const Worker = EventEmitter.compose({
     // This method may be called multiple times,
     // avoid dispatching `detach` event more than once
     if (this._windowID) {
       this._windowID = null;
       observers.remove("inner-window-destroyed", this._documentUnload);
       this._earlyEvents.length = 0;
       this._emit("detach");
     }
+    this._inited = false;
   },
 
   /**
    * Receive an event from the content script that need to be sent to
    * worker.port. Provide a way for composed object to catch all events.
    */
   _onContentScriptEvent: function _onContentScriptEvent() {
     this._port._emit.apply(this._port, arguments);
--- a/addon-sdk/source/lib/sdk/io/fs.js
+++ b/addon-sdk/source/lib/sdk/io/fs.js
@@ -103,18 +103,24 @@ function remove(path, recursive) {
   if (fd.exists()) {
     fd.remove(recursive || false);
   }
   else {
     throw FSError("remove", "ENOENT", 34, path);
   }
 }
 
+/**
+ * Utility function to convert either an octal number or string
+ * into an octal number
+ * 0777 => 0777
+ * "0644" => 0644
+ */
 function Mode(mode, fallback) {
-  return isString(mode) ? parseInt(mode) : mode || fallback;
+  return isString(mode) ? parseInt(mode, 8) : mode || fallback;
 }
 function Flags(flag) {
   return !isString(flag) ? flag :
          FLAGS[flag] || Error("Unknown file open flag: " + flag);
 }
 
 
 function FSError(op, code, errno, path, file, line) {
@@ -389,18 +395,25 @@ exports.lchownSync = chownSync;
 
 let lchown = Async(lchown);
 exports.lchown = lchown;
 
 /**
  * Synchronous chmod(2).
  */
 function chmodSync (path, mode) {
-  throw Error("Not implemented yet!!");
-};
+  let file;
+  try {
+    file = new nsILocalFile(path);
+  } catch(e) {
+    throw FSError("chmod", "ENOENT", 34, path);
+  }
+
+  file.permissions = Mode(mode);
+}
 exports.chmodSync = chmodSync;
 /**
  * Asynchronous chmod(2). No arguments other than a possible exception are
  * given to the completion callback.
  */
 let chmod = Async(chmodSync);
 exports.chmod = chmod;
 
@@ -411,17 +424,17 @@ function fchmodSync(fd, mode) {
   throw Error("Not implemented yet!!");
 };
 exports.fchmodSync = fchmodSync;
 /**
  * Asynchronous chmod(2). No arguments other than a possible exception are
  * given to the completion callback.
  */
 let fchmod = Async(fchmodSync);
-exports.chmod = fchmod;
+exports.fchmod = fchmod;
 
 
 /**
  * Synchronous stat(2). Returns an instance of `fs.Stats`
  */
 function statSync(path) {
   return new Stats(path);
 };
@@ -839,16 +852,19 @@ function readFileSync(path, encoding) {
 };
 exports.readFileSync = readFileSync;
 
 /**
  * Asynchronously writes data to a file, replacing the file if it already
  * exists. data can be a string or a buffer.
  */
 function writeFile(path, content, encoding, callback) {
+  if (!isString(path))
+    throw new TypeError('path must be a string');
+
   try {
     if (isFunction(encoding)) {
       callback = encoding
       encoding = null
     }
     if (isString(content))
       content = new Buffer(content, encoding);
 
--- a/addon-sdk/source/lib/sdk/loader/sandbox.js
+++ b/addon-sdk/source/lib/sdk/loader/sandbox.js
@@ -6,25 +6,31 @@
 module.metadata = {
   "stability": "experimental"
 };
 
 const { Cc, Ci, CC, Cu } = require('chrome');
 const systemPrincipal = CC('@mozilla.org/systemprincipal;1', 'nsIPrincipal')();
 const scriptLoader = Cc['@mozilla.org/moz/jssubscript-loader;1'].
                      getService(Ci.mozIJSSubScriptLoader);
+const self = require('sdk/self');
 
 /**
  * Make a new sandbox that inherits given `source`'s principals. Source can be
  * URI string, DOMWindow or `null` for system principals.
  */
 function sandbox(target, options) {
-  return Cu.Sandbox(target || systemPrincipal, options || {});
+  options = options || {};
+  options.metadata = options.metadata ? options.metadata : {};
+  options.metadata.addonID = options.metadata.addonID ?
+    options.metadata.addonID : self.id;
+
+  return Cu.Sandbox(target || systemPrincipal, options);
 }
-exports.sandbox = sandbox
+exports.sandbox = sandbox;
 
 /**
  * Evaluates given `source` in a given `sandbox` and returns result.
  */
 function evaluate(sandbox, code, uri, line, version) {
   return Cu.evalInSandbox(code, sandbox, version || '1.8', uri || '', line || 1);
 }
 exports.evaluate = evaluate;
--- a/addon-sdk/source/lib/sdk/page-worker.js
+++ b/addon-sdk/source/lib/sdk/page-worker.js
@@ -5,17 +5,17 @@
 
 module.metadata = {
   "stability": "stable"
 };
 
 const { Class } = require('./core/heritage');
 const { on, emit, off, setListeners } = require('./event/core');
 const { filter, pipe, map, merge: streamMerge } = require('./event/utils');
-const { WorkerHost, Worker, detach, attach } = require('./worker/utils');
+const { WorkerHost, Worker, detach, attach, destroy } = require('./worker/utils');
 const { Disposable } = require('./core/disposable');
 const { EventTarget } = require('./event/target');
 const { unload } = require('./system/unload');
 const { events, streamEventsFrom } = require('./content/events');
 const { getAttachEventType } = require('./content/utils');
 const { window } = require('./addon/window');
 const { getParentWindow } = require('./window/utils');
 const { create: makeFrame, getDocShell } = require('./frame/utils');
@@ -126,17 +126,17 @@ const Page = Class({
     let contentURL = pageContract({ contentURL: value }).contentURL;
     view.setAttribute('src', contentURL);
   },
   dispose: function () {
     if (isDisposed(this)) return;
     let view = viewFor(this);
     if (view.parentNode) view.parentNode.removeChild(view);
     views.delete(this);
-    detach(workers.get(this));
+    destroy(workers.get(this));
   },
   toString: function () '[object Page]'
 });
 
 exports.Page = Page;
 
 let pageEvents = streamMerge([events, streamEventsFrom(window)]);
 let readyEvents = filter(pageEvents, isReadyEvent);
--- a/addon-sdk/source/lib/sdk/panel.js
+++ b/addon-sdk/source/lib/sdk/panel.js
@@ -14,17 +14,17 @@ module.metadata = {
 
 const { Ci } = require("chrome");
 const { validateOptions: valid } = require('./deprecated/api-utils');
 const { setTimeout } = require('./timers');
 const { isPrivateBrowsingSupported } = require('./self');
 const { isWindowPBSupported } = require('./private-browsing/utils');
 const { Class } = require("./core/heritage");
 const { merge } = require("./util/object");
-const { WorkerHost, Worker, detach, attach,
+const { WorkerHost, Worker, detach, attach, destroy,
         requiresAddonGlobal } = require("./worker/utils");
 const { Disposable } = require("./core/disposable");
 const { contract: loaderContract } = require("./content/loader");
 const { contract } = require("./util/contract");
 const { on, off, emit, setListeners } = require("./event/core");
 const { EventTarget } = require("./event/target");
 const domPanel = require("./panel/utils");
 const { events } = require("./panel/events");
@@ -135,17 +135,17 @@ const Panel = Class({
 
     // pipe events from worker to a panel.
     pipe(worker, this);
   },
   dispose: function dispose() {
     this.hide();
     off(this);
 
-    detach(workerFor(this));
+    destroy(workerFor(this));
 
     domPanel.dispose(viewFor(this));
 
     // Release circular reference between view and panel instance. This
     // way view will be GC-ed. And panel as well once all the other refs
     // will be removed from it.
     views.delete(this);
   },
@@ -162,16 +162,19 @@ const Panel = Class({
   /* Public API: Panel.position */
   get position() modelFor(this).position,
 
   get contentURL() modelFor(this).contentURL,
   set contentURL(value) {
     let model = modelFor(this);
     model.contentURL = panelContract({ contentURL: value }).contentURL;
     domPanel.setURL(viewFor(this), model.contentURL);
+    // Detach worker so that messages send will be queued until it's
+    // reatached once panel content is ready.
+    detach(workerFor(this));
   },
 
   /* Public API: Panel.isShowing */
   get isShowing() !isDisposed(this) && domPanel.isOpen(viewFor(this)),
 
   /* Public API: Panel.show */
   show: function show(options, anchor) {
     if (options instanceof Ci.nsIDOMElement) {
@@ -246,20 +249,16 @@ let hides = filter(panelEvents, function
 
 // Panel events emitted after content inside panel is ready. For different
 // panels ready may mean different state based on `contentScriptWhen` attribute.
 // Weather given event represents readyness is detected by `getAttachEventType`
 // helper function.
 let ready = filter(panelEvents, function({type, target})
   getAttachEventType(modelFor(panelFor(target))) === type);
 
-// Panel events emitted after content document in the panel has changed.
-let change = filter(panelEvents, function({type})
-  type === "document-element-inserted");
-
 // Forward panel show / hide events to panel's own event listeners.
 on(shows, "data", function({target}) emit(panelFor(target), "show"));
 on(hides, "data", function({target}) emit(panelFor(target), "hide"));
 
 on(ready, "data", function({target}) {
   let worker = workerFor(panelFor(target));
   attach(worker, domPanel.getContentDocument(target).defaultView);
 });
--- a/addon-sdk/source/lib/sdk/panel/utils.js
+++ b/addon-sdk/source/lib/sdk/panel/utils.js
@@ -76,16 +76,20 @@ function open(panel, options, anchor) {
 }
 exports.open = open;
 
 function isOpen(panel) {
   return panel.state === "open"
 }
 exports.isOpen = isOpen;
 
+function isOpening(panel) {
+  return panel.state === "showing"
+}
+exports.isOpening = isOpening
 
 function close(panel) {
   // Sometimes "TypeError: panel.hidePopup is not a function" is thrown
   // when quitting the host application while a panel is visible.  To suppress
   // these errors, check for "hidePopup" in panel before calling it.
   // It's not clear if there's an issue or it's expected behavior.
 
   return panel.hidePopup && panel.hidePopup();
@@ -358,17 +362,19 @@ function style(panel) {
   }
   catch (error) {
     console.error("Unable to apply panel style");
     console.exception(error);
   }
 }
 exports.style = style;
 
-function getContentFrame(panel) isOpen(panel) ? panel.firstChild :
-                                                panel.backgroundFrame
+let getContentFrame = panel =>
+    (isOpen(panel) || isOpening(panel)) ?
+    panel.firstChild :
+    panel.backgroundFrame
 exports.getContentFrame = getContentFrame;
 
 function getContentDocument(panel) getContentFrame(panel).contentDocument
 exports.getContentDocument = getContentDocument;
 
 function setURL(panel, url) getContentFrame(panel).setAttribute("src", url)
 exports.setURL = setURL;
--- a/addon-sdk/source/lib/sdk/test/utils.js
+++ b/addon-sdk/source/lib/sdk/test/utils.js
@@ -3,16 +3,19 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 'use strict';
 
 module.metadata = {
   'stability': 'unstable'
 };
 
+const { defer } = require('../core/promise');
+const { setInterval, clearInterval } = require('../timers');
+
 function getTestNames (exports)
   Object.keys(exports).filter(name => /^test/.test(name))
 
 function isTestAsync (fn) fn.length > 1
 function isHelperAsync (fn) fn.length > 2
 
 /*
  * Takes an `exports` object of a test file and a function `beforeFn`
@@ -88,8 +91,19 @@ function after (exports, afterFn) {
         testFn(assert, () => {
           afterFn(name, assert, done);
         });
       };
     }
   });
 }
 exports.after = after;
+
+function waitUntil (predicate, delay) {
+  let { promise, resolve } = defer();
+  let interval = setInterval(() => {
+    if (!predicate()) return;
+    clearInterval(interval);
+    resolve();
+  }, delay || 10);
+  return promise;
+}
+exports.waitUntil = waitUntil;
new file mode 100644
--- /dev/null
+++ b/addon-sdk/source/lib/sdk/ui/id.js
@@ -0,0 +1,27 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+'use strict';
+
+module.metadata = {
+  'stability': 'experimental'
+};
+
+const method = require('method/core');
+const { uuid } = require('../util/uuid');
+
+// NOTE: use lang/functional memoize when it is updated to use WeakMap
+function memoize(f) {
+  const memo = new WeakMap();
+
+  return function memoizer(o) {
+    let key = o;
+    if (!memo.has(key))
+      memo.set(key, f.apply(this, arguments));
+    return memo.get(key);
+  };
+}
+
+let identify = method('identify');
+identify.define(Object, memoize(function() { return uuid(); }));
+exports.identify = identify;
--- a/addon-sdk/source/lib/sdk/worker/utils.js
+++ b/addon-sdk/source/lib/sdk/worker/utils.js
@@ -93,8 +93,14 @@ function detach(worker) {
 exports.detach = detach;
 
 function attach(worker, window) {
   let trait = traitFor(worker);
   // Cleanup the worker before injecting the content script into a new document.
   trait.attach(window);
 }
 exports.attach = attach;
+
+function destroy(worker) {
+  let trait = traitFor(worker);
+  if (trait) trait.destroy();
+}
+exports.destroy = destroy;
--- a/addon-sdk/source/lib/toolkit/loader.js
+++ b/addon-sdk/source/lib/toolkit/loader.js
@@ -165,31 +165,34 @@ exports.serializeStack = serializeStack
 //    system principal.
 // - `prototype`: Ancestor for the sandbox that will be created. Defaults to
 //    `{}`.
 // - `wantXrays`: A Boolean value indicating whether code outside the sandbox
 //    wants X-ray vision with respect to objects inside the sandbox. Defaults
 //    to `true`.
 // - `sandbox`: A sandbox to share JS compartment with. If omitted new
 //    compartment will be created.
+// - `metadata`: A metadata object associated with the sandbox. It should
+//    be JSON-serializable.
 // For more details see:
 // https://developer.mozilla.org/en/Components.utils.Sandbox
 const Sandbox = iced(function Sandbox(options) {
   // Normalize options and rename to match `Cu.Sandbox` expectations.
   options = {
     // Do not expose `Components` if you really need them (bad idea!) you
     // still can expose via prototype.
     wantComponents: false,
     sandboxName: options.name,
     principal: 'principal' in options ? options.principal : systemPrincipal,
     wantXrays: 'wantXrays' in options ? options.wantXrays : true,
     wantGlobalProperties: 'wantGlobalProperties' in options ?
                           options.wantGlobalProperties : [],
     sandboxPrototype: 'prototype' in options ? options.prototype : {},
-    sameGroupAs: 'sandbox' in options ? options.sandbox : null
+    sameGroupAs: 'sandbox' in options ? options.sandbox : null,
+    metadata: 'metadata' in options ? options.metadata : {}
   };
 
   // Make `options.sameGroupAs` only if `sandbox` property is passed,
   // otherwise `Cu.Sandbox` will throw.
   if (!options.sameGroupAs)
     delete options.sameGroupAs;
 
   let sandbox = Cu.Sandbox(options.principal, options);
@@ -248,17 +251,21 @@ const load = iced(function load(loader, 
 
   let sandbox = sandboxes[module.uri] = Sandbox({
     name: module.uri,
     // Get an existing module sandbox, if any, so we can reuse its compartment
     // when creating the new one to reduce memory consumption.
     sandbox: sandboxes[keys(sandboxes).shift()],
     prototype: create(globals, descriptors),
     wantXrays: false,
-    wantGlobalProperties: module.id == "sdk/indexed-db" ? ["indexedDB"] : []
+    wantGlobalProperties: module.id == "sdk/indexed-db" ? ["indexedDB"] : [],
+    metadata: {
+      addonID: loader.id,
+      URI: module.uri
+    }
   });
 
   try {
     evaluate(sandbox, module.uri);
   } catch (error) {
     let { message, fileName, lineNumber } = error;
     let stack = error.stack || Error().stack;
     let frames = parseStack(stack).filter(isntLoaderFrame);
@@ -328,16 +335,17 @@ exports.resolve = resolve;
 
 const resolveURI = iced(function resolveURI(id, mapping) {
   let count = mapping.length, index = 0;
   while (index < count) {
     let [ path, uri ] = mapping[index ++];
     if (id.indexOf(path) === 0)
       return normalize(id.replace(path, uri));
   }
+  return void 0; // otherwise we raise a warning, see bug 910304
 });
 exports.resolveURI = resolveURI;
 
 // Creates version of `require` that will be exposed to the given `module`
 // in the context of the given `loader`. Each module gets own limited copy
 // of `require` that is allowed to load only a modules that are associated
 // with it during link time.
 const Require = iced(function Require(loader, requirer) {
@@ -473,16 +481,18 @@ const Loader = iced(function Loader(opti
     destructor: { enumerable: false, value: destructor },
     globals: { enumerable: false, value: globals },
     mapping: { enumerable: false, value: mapping },
     // Map of module objects indexed by module URIs.
     modules: { enumerable: false, value: modules },
     // Map of module sandboxes indexed by module URIs.
     sandboxes: { enumerable: false, value: {} },
     resolve: { enumerable: false, value: resolve },
+    // ID of the addon, if provided.
+    id: { enumerable: false, value: options.id },
     load: { enumerable: false, value: options.load || load },
     // Main (entry point) module, it can be set only once, since loader
     // instance can have only one main module.
     main: new function() {
       let main;
       return {
         enumerable: false,
         get: function() { return main; },
--- a/addon-sdk/source/test/addons/private-browsing-supported/test-panel.js
+++ b/addon-sdk/source/test/addons/private-browsing-supported/test-panel.js
@@ -1,26 +1,32 @@
 'use strict';
 
 const { open, focus, close } = require('sdk/window/helpers');
 const { isPrivate } = require('sdk/private-browsing');
 const { defer } = require('sdk/core/promise');
+const { browserWindows: windows } = require('sdk/windows');
 
 const BROWSER = 'chrome://browser/content/browser.xul';
 
 exports.testRequirePanel = function(assert) {
   require('sdk/panel');
   assert.ok('the panel module should not throw an error');
 };
 
 exports.testShowPanelInPrivateWindow = function(assert, done) {
   let panel = require('sdk/panel').Panel({
     contentURL: "data:text/html;charset=utf-8,"
   });
 
+  assert.ok(windows.length > 0, 'there is at least one open window');
+  for (let window of windows) {
+    assert.equal(isPrivate(window), false, 'open window is private');
+  }
+
   testShowPanel(assert, panel).
     then(makeEmptyPrivateBrowserWindow).
     then(focus).
     then(function(window) {
       assert.equal(isPrivate(window), true, 'opened window is private');
       assert.pass('private window was focused');
       return window;
     }).
@@ -58,29 +64,34 @@ function makeEmptyPrivateBrowserWindow(o
       toolbar: true,
       private: true
     }
   });
 }
 
 function testShowPanel(assert, panel) {
   let { promise, resolve } = defer();
+  let shown = false;
 
   assert.ok(!panel.isShowing, 'the panel is not showing [1]');
 
+  panel.once('hide', function() {
+    assert.ok(!panel.isShowing, 'the panel is not showing [2]');
+    assert.ok(shown, 'the panel was shown')
+
+    resolve(null);
+  });
+
   panel.once('show', function() {
+    shown = true;
+
     assert.ok(panel.isShowing, 'the panel is showing');
 
-    panel.once('hide', function() {
-      assert.ok(!panel.isShowing, 'the panel is not showing [2]');
+    panel.hide();
+  });
 
-      resolve(null);
-    });
-
-    panel.hide();
-  })
   panel.show();
 
   return promise;
 }
 
 //Test disabled because of bug 911071
 module.exports = {}
--- a/addon-sdk/source/test/pagemod-test-helpers.js
+++ b/addon-sdk/source/test/pagemod-test-helpers.js
@@ -9,38 +9,33 @@ const timer = require("sdk/timers");
 const xulApp = require("sdk/system/xul-app");
 const { Loader } = require("sdk/test/loader");
 const { openTab, getBrowserForTab, closeTab } = require("sdk/tabs/utils");
 
 /**
  * A helper function that creates a PageMod, then opens the specified URL
  * and checks the effect of the page mod on 'onload' event via testCallback.
  */
-exports.testPageMod = function testPageMod(test, testURL, pageModOptions,
+exports.testPageMod = function testPageMod(assert, done, testURL, pageModOptions,
                                            testCallback, timeout) {
   if (!xulApp.versionInRange(xulApp.platformVersion, "1.9.3a3", "*") &&
       !xulApp.versionInRange(xulApp.platformVersion, "1.9.2.7", "1.9.2.*")) {
-    test.pass("Note: not testing PageMod, as it doesn't work on this platform version");
+    assert.pass("Note: not testing PageMod, as it doesn't work on this platform version");
     return null;
   }
 
   var wm = Cc['@mozilla.org/appshell/window-mediator;1']
            .getService(Ci.nsIWindowMediator);
   var browserWindow = wm.getMostRecentWindow("navigator:browser");
   if (!browserWindow) {
-    test.pass("page-mod tests: could not find the browser window, so " +
+    assert.pass("page-mod tests: could not find the browser window, so " +
               "will not run. Use -a firefox to run the pagemod tests.")
     return null;
   }
 
-  if (timeout !== undefined)
-    test.waitUntilDone(timeout);
-  else
-    test.waitUntilDone();
-
   let loader = Loader(module);
   let pageMod = loader.require("sdk/page-mod");
 
   var pageMods = [new pageMod.PageMod(opts) for each(opts in pageModOptions)];
 
   let newTab = openTab(browserWindow, testURL, {
     inBackground: false
   });
@@ -49,21 +44,21 @@ exports.testPageMod = function testPageM
   function onPageLoad() {
     b.removeEventListener("load", onPageLoad, true);
     // Delay callback execute as page-mod content scripts may be executed on
     // load event. So page-mod actions may not be already done.
     // If we delay even more contentScriptWhen:'end', we may want to modify
     // this code again.
     timer.setTimeout(testCallback, 0,
       b.contentWindow.wrappedJSObject, 
-      function done() {
+      function () {
         pageMods.forEach(function(mod) mod.destroy());
         // XXX leaks reported if we don't close the tab?
         closeTab(newTab);
         loader.unload();
-        test.done();
+        done();
       }
     );
   }
   b.addEventListener("load", onPageLoad, true);
 
   return pageMods;
 }
--- a/addon-sdk/source/test/private-browsing/global.js
+++ b/addon-sdk/source/test/private-browsing/global.js
@@ -5,214 +5,203 @@
 
 const timer = require("sdk/timers");
 const { LoaderWithHookedConsole, deactivate, pb, pbUtils } = require("./helper");
 const tabs = require("sdk/tabs");
 const { getMostRecentBrowserWindow, isWindowPrivate } = require('sdk/window/utils');
 const { set: setPref } = require("sdk/preferences/service");
 const DEPRECATE_PREF = "devtools.errorconsole.deprecation_warnings";
 
-exports["test activate private mode via handler"] = function(test) {
-  test.waitUntilDone();
-
+exports["test activate private mode via handler"] = function(assert, done) {
   function onReady(tab) {
     if (tab.url == "about:robots")
       tab.close(function() pb.activate());
   }
   function cleanup(tab) {
     if (tab.url == "about:") {
       tabs.removeListener("ready", cleanup);
       tab.close(function onClose() {
-        test.done();
+        done();
       });
     }
   }
 
   tabs.on("ready", onReady);
   pb.once("start", function onStart() {
-    test.pass("private mode was activated");
+    assert.pass("private mode was activated");
     pb.deactivate();
   });
   pb.once("stop", function onStop() {
-    test.pass("private mode was deactivated");
+    assert.pass("private mode was deactivated");
     tabs.removeListener("ready", onReady);
     tabs.on("ready", cleanup);
   });
   tabs.once("open", function onOpen() {
     tabs.open("about:robots");
   });
   tabs.open("about:");
 };
 
 // tests that isActive has the same value as the private browsing service
 // expects
-exports.testGetIsActive = function (test) {
-  test.waitUntilDone();
-
-  test.assertEqual(pb.isActive, false,
+exports.testGetIsActive = function (assert) {
+  assert.equal(pb.isActive, false,
                    "private-browsing.isActive is correct without modifying PB service");
-  test.assertEqual(pb.isPrivate(), false,
+  assert.equal(pb.isPrivate(), false,
                    "private-browsing.sPrivate() is correct without modifying PB service");
 
   pb.once("start", function() {
-    test.assert(pb.isActive,
+    assert.ok(pb.isActive,
                   "private-browsing.isActive is correct after modifying PB service");
-    test.assert(pb.isPrivate(),
+    assert.ok(pb.isPrivate(),
                   "private-browsing.sPrivate() is correct after modifying PB service");
     // Switch back to normal mode.
     pb.deactivate();
   });
   pb.activate();
 
   pb.once("stop", function() {
-    test.assert(!pb.isActive,
+    assert.ok(!pb.isActive,
                 "private-browsing.isActive is correct after modifying PB service");
-    test.assert(!pb.isPrivate(),
+    assert.ok(!pb.isPrivate(),
                 "private-browsing.sPrivate() is correct after modifying PB service");
     test.done();
   });
 };
 
-exports.testStart = function(test) {
-  test.waitUntilDone();
-
+exports.testStart = function(assert, done) {
   pb.on("start", function onStart() {
-    test.assertEqual(this, pb, "`this` should be private-browsing module");
-    test.assert(pbUtils.getMode(),
+    assert.equal(this, pb, "`this` should be private-browsing module");
+    assert.ok(pbUtils.getMode(),
                 'private mode is active when "start" event is emitted');
-    test.assert(pb.isActive,
+    assert.ok(pb.isActive,
                 '`isActive` is `true` when "start" event is emitted');
-    test.assert(pb.isPrivate(),
+    assert.ok(pb.isPrivate(),
                 '`isPrivate` is `true` when "start" event is emitted');
     pb.removeListener("start", onStart);
-    deactivate(function() test.done());
+    deactivate(done);
   });
   pb.activate();
 };
 
-exports.testStop = function(test) {
-  test.waitUntilDone();
+exports.testStop = function(assert, done) {
   pb.once("stop", function onStop() {
-    test.assertEqual(this, pb, "`this` should be private-browsing module");
-    test.assertEqual(pbUtils.getMode(), false,
+    assert.equal(this, pb, "`this` should be private-browsing module");
+    assert.equal(pbUtils.getMode(), false,
                      "private mode is disabled when stop event is emitted");
-    test.assertEqual(pb.isActive, false,
+    assert.equal(pb.isActive, false,
                      "`isActive` is `false` when stop event is emitted");
-    test.assertEqual(pb.isPrivate(), false,
+    assert.equal(pb.isPrivate(), false,
                      "`isPrivate()` is `false` when stop event is emitted");
-    test.done();
+    done();
   });
   pb.activate();
   pb.once("start", function() {
     pb.deactivate();
   });
 };
 
-exports.testBothListeners = function(test) {
-  test.waitUntilDone();
+exports.testBothListeners = function(assert, done) {
   let stop = false;
   let start = false;
 
   function onStop() {
-    test.assertEqual(stop, false,
+    assert.equal(stop, false,
                      "stop callback must be called only once");
-    test.assertEqual(pbUtils.getMode(), false,
+    assert.equal(pbUtils.getMode(), false,
                      "private mode is disabled when stop event is emitted");
-    test.assertEqual(pb.isActive, false,
+    assert.equal(pb.isActive, false,
                      "`isActive` is `false` when stop event is emitted");
-    test.assertEqual(pb.isPrivate(), false,
+    assert.equal(pb.isPrivate(), false,
                      "`isPrivate()` is `false` when stop event is emitted");
 
     pb.on("start", finish);
     pb.removeListener("start", onStart);
     pb.removeListener("start", onStart2);
     pb.activate();
     stop = true;
   }
 
   function onStart() {
-    test.assertEqual(false, start,
+    assert.equal(false, start,
                      "stop callback must be called only once");
-    test.assert(pbUtils.getMode(),
+    assert.ok(pbUtils.getMode(),
                 "private mode is active when start event is emitted");
-    test.assert(pb.isActive,
+    assert.ok(pb.isActive,
                 "`isActive` is `true` when start event is emitted");
-    test.assert(pb.isPrivate(),
+    assert.ok(pb.isPrivate(),
                 "`isPrivate()` is `true` when start event is emitted");
 
     pb.on("stop", onStop);
     pb.deactivate();
     start = true;
   }
 
   function onStart2() {
-    test.assert(start, "start listener must be called already");
-    test.assertEqual(false, stop, "stop callback must not be called yet");
+    assert.ok(start, "start listener must be called already");
+    assert.equal(false, stop, "stop callback must not be called yet");
   }
 
   function finish() {
-    test.assert(pbUtils.getMode(), true,
+    assert.ok(pbUtils.getMode(), true,
                 "private mode is active when start event is emitted");
-    test.assert(pb.isActive,
+    assert.ok(pb.isActive,
                 "`isActive` is `true` when start event is emitted");
-    test.assert(pb.isPrivate(),
+    assert.ok(pb.isPrivate(),
                 "`isPrivate()` is `true` when start event is emitted");
 
     pb.removeListener("start", finish);
     pb.removeListener("stop", onStop);
 
     pb.deactivate();
     pb.once("stop", function () {
-      test.assertEqual(pbUtils.getMode(), false);
-      test.assertEqual(pb.isActive, false);
-      test.assertEqual(pb.isPrivate(), false);
+      assert.equal(pbUtils.getMode(), false);
+      assert.equal(pb.isActive, false);
+      assert.equal(pb.isPrivate(), false);
 
-      test.done();
+      done();
     });
   }
 
   pb.on("start", onStart);
   pb.on("start", onStart2);
   pb.activate();
 };
 
-exports.testAutomaticUnload = function(test) {
-  test.waitUntilDone();
+exports.testAutomaticUnload = function(assert, done) {
   setPref(DEPRECATE_PREF, true);
 
   // Create another private browsing instance and unload it
   let { loader, errors } = LoaderWithHookedConsole(module);
   let pb2 = loader.require("sdk/private-browsing");
   let called = false;
   pb2.on("start", function onStart() {
     called = true;
-    test.fail("should not be called:x");
+    assert.fail("should not be called:x");
   });
   loader.unload();
 
   // Then switch to private mode in order to check that the previous instance
   // is correctly destroyed
   pb.once("start", function onStart() {
     timer.setTimeout(function () {
-      test.assert(!called, 
+      assert.ok(!called, 
         "First private browsing instance is destroyed and inactive");
       // Must reset to normal mode, so that next test starts with it.
       deactivate(function() {
-        test.assert(errors.length, 0, "should have been 1 deprecation error");
-        test.done();
+        assert.ok(errors.length, 0, "should have been 1 deprecation error");
+        done();
       });
     }, 0);
   });
 
   pb.activate();
 };
 
-exports.testUnloadWhileActive = function(test) {
-  test.waitUntilDone();
-
+exports.testUnloadWhileActive = function(assert, done) {
   let called = false;
   let { loader, errors } = LoaderWithHookedConsole(module);
   let pb2 = loader.require("sdk/private-browsing");
   let ul = loader.require("sdk/system/unload");
 
   let unloadHappened = false;
   ul.when(function() {
     unloadHappened = true;
@@ -220,34 +209,31 @@ exports.testUnloadWhileActive = function
       pb.deactivate();
     });
   });
   pb2.once("start", function() {
     loader.unload();
   });
   pb2.once("stop", function() {
     called = true;
-    test.assert(unloadHappened, "the unload event should have already occurred.");
-    test.fail("stop should not have been fired");
+    assert.ok(unloadHappened, "the unload event should have already occurred.");
+    assert.fail("stop should not have been fired");
   });
   pb.once("stop", function() {
-    test.assert(!called, "stop was not called on unload");
-    test.assert(errors.length, 2, "should have been 2 deprecation errors");
-    test.done();
+    assert.ok(!called, "stop was not called on unload");
+    assert.ok(errors.length, 2, "should have been 2 deprecation errors");
+    done();
   });
 
   pb.activate();
 };
 
-exports.testIgnoreWindow = function(test) {
-  test.waitUntilDone();
+exports.testIgnoreWindow = function(assert, done) {
   let window = getMostRecentBrowserWindow();
 
   pb.once('start', function() {
-    test.assert(isWindowPrivate(window), 'window is private');
-    test.assert(!pbUtils.ignoreWindow(window), 'window is not ignored');
-    pb.once('stop', function() {
-      test.done();
-    });
+    assert.ok(isWindowPrivate(window), 'window is private');
+    assert.ok(!pbUtils.ignoreWindow(window), 'window is not ignored');
+    pb.once('stop', done);
     pb.deactivate();
   });
   pb.activate();
 };
--- a/addon-sdk/source/test/private-browsing/tabs.js
+++ b/addon-sdk/source/test/private-browsing/tabs.js
@@ -1,27 +1,27 @@
 'use strict';
 
 const { Ci } = require('chrome');
 const { openTab, closeTab } = require('sdk/tabs/utils');
 const { browserWindows } = require('sdk/windows');
 const { getOwnerWindow } = require('sdk/private-browsing/window/utils');
 const { isPrivate } = require('sdk/private-browsing');
 
-exports.testIsPrivateOnTab = function(test) {
+exports.testIsPrivateOnTab = function(assert) {
   let window = browserWindows.activeWindow;
 
   let chromeWindow = getOwnerWindow(window);
 
-  test.assert(chromeWindow instanceof Ci.nsIDOMWindow, 'associated window is found');
-  test.assert(!isPrivate(chromeWindow), 'the top level window is not private');
+  assert.ok(chromeWindow instanceof Ci.nsIDOMWindow, 'associated window is found');
+  assert.ok(!isPrivate(chromeWindow), 'the top level window is not private');
 
   let rawTab = openTab(chromeWindow, 'data:text/html,<h1>Hi!</h1>', {
     isPrivate: true
   });
 
   // test that the tab is private
-  test.assert(rawTab.browser.docShell.QueryInterface(Ci.nsILoadContext).usePrivateBrowsing);
-  test.assert(isPrivate(rawTab.browser.contentWindow));
-  test.assert(isPrivate(rawTab.browser));
+  assert.ok(rawTab.browser.docShell.QueryInterface(Ci.nsILoadContext).usePrivateBrowsing);
+  assert.ok(isPrivate(rawTab.browser.contentWindow));
+  assert.ok(isPrivate(rawTab.browser));
 
   closeTab(rawTab);
-}
+};
--- a/addon-sdk/source/test/private-browsing/windows.js
+++ b/addon-sdk/source/test/private-browsing/windows.js
@@ -132,10 +132,8 @@ exports.testIsPrivateOnWindowOff = funct
   windows.open({
     onOpen: function(window) {
       assert.equal(isPrivate(window), false, 'isPrivate for a window is false when it should be');
       assert.equal(isPrivate(window.tabs[0]), false, 'isPrivate for a tab is false when it should be');
       window.close(done);
     }
   })
 }
-
-require("test").run(exports);
--- a/addon-sdk/source/test/test-addon-installer.js
+++ b/addon-sdk/source/test/test-addon-installer.js
@@ -135,34 +135,47 @@ exports["test Update"] = function (asser
 
 exports['test Uninstall failure'] = function (assert, done) {
   AddonInstaller.uninstall('invalid-addon-path').then(
     () => assert.fail('Addon uninstall should not resolve successfully'),
     () => assert.pass('Addon correctly rejected invalid uninstall')
   ).then(done, assert.fail);
 };
 
-exports['test Addon Disable'] = function (assert, done) {
+exports['test Addon Disable and Enable'] = function (assert, done) {
   let ensureActive = (addonId) => AddonInstaller.isActive(addonId).then(state => {
     assert.equal(state, true, 'Addon should be enabled by default');
     return addonId;
   });
   let ensureInactive = (addonId) => AddonInstaller.isActive(addonId).then(state => {
     assert.equal(state, false, 'Addon should be disabled after disabling');
     return addonId;
   });
 
   AddonInstaller.install(ADDON_PATH)
     .then(ensureActive)
+    .then(AddonInstaller.enable) // should do nothing, yet not fail
+    .then(ensureActive)
     .then(AddonInstaller.disable)
     .then(ensureInactive)
+    .then(AddonInstaller.disable) // should do nothing, yet not fail
+    .then(ensureInactive)
+    .then(AddonInstaller.enable)
+    .then(ensureActive)
     .then(AddonInstaller.uninstall)
     .then(done, assert.fail);
 };
 
 exports['test Disable failure'] = function (assert, done) {
   AddonInstaller.disable('not-an-id').then(
     () => assert.fail('Addon disable should not resolve successfully'),
     () => assert.pass('Addon correctly rejected invalid disable')
   ).then(done, assert.fail);
 };
 
+exports['test Enable failure'] = function (assert, done) {
+  AddonInstaller.enable('not-an-id').then(
+    () => assert.fail('Addon enable should not resolve successfully'),
+    () => assert.pass('Addon correctly rejected invalid enable')
+  ).then(done, assert.fail);
+};
+
 require("test").run(exports);
--- a/addon-sdk/source/test/test-app-strings.js
+++ b/addon-sdk/source/test/test-app-strings.js
@@ -1,62 +1,68 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-const { Cc,Ci } = require("chrome");
+"use strict";
 
-let StringBundle = require("sdk/deprecated/app-strings").StringBundle;
-exports.testStringBundle = function(test) {
+const { Cc, Ci } = require("chrome");
+const { StringBundle } = require("sdk/deprecated/app-strings");
+
+exports.testStringBundle = function(assert) {
   let url = "chrome://global/locale/security/caps.properties";
 
   let strings = StringBundle(url);
 
-  test.assertEqual(strings.url, url,
+  assert.equal(strings.url, url,
                    "'url' property contains correct URL of string bundle");
 
   let appLocale = Cc["@mozilla.org/intl/nslocaleservice;1"].
                   getService(Ci.nsILocaleService).
                   getApplicationLocale();
 
   let stringBundle = Cc["@mozilla.org/intl/stringbundle;1"].
                      getService(Ci.nsIStringBundleService).
                      createBundle(url, appLocale);
 
   let (name = "CheckMessage") {
-    test.assertEqual(strings.get(name), stringBundle.GetStringFromName(name),
-                     "getting a string returns the string");
+    assert.equal(strings.get(name), stringBundle.GetStringFromName(name),
+                 "getting a string returns the string");
   }
 
   let (name = "CreateWrapperDenied", args = ["foo"]) {
-    test.assertEqual(strings.get(name, args),
-                     stringBundle.formatStringFromName(name, args, args.length),
-                     "getting a formatted string returns the formatted string");
+    assert.equal(strings.get(name, args),
+                 stringBundle.formatStringFromName(name, args, args.length),
+                 "getting a formatted string returns the formatted string");
   }
 
-  test.assertRaises(function () strings.get("nonexistentString"),
-                    "String 'nonexistentString' could not be retrieved from " +
-                    "the bundle due to an unknown error (it doesn't exist?).",
-                    "retrieving a nonexistent string throws an exception");
+  assert.throws(function () strings.get("nonexistentString"),
+                RegExp("String 'nonexistentString' could not be retrieved from " +
+                       "the bundle due to an unknown error \\(it doesn't exist\\?\\)\\."),
+                "retrieving a nonexistent string throws an exception");
 
   let a = [], b = [];
   let enumerator = stringBundle.getSimpleEnumeration();
   while (enumerator.hasMoreElements()) {
     let elem = enumerator.getNext().QueryInterface(Ci.nsIPropertyElement);
     a.push([elem.key, elem.value]);
   }
-  for (let key in strings)
+
+  for (let key in strings) {
     b.push([ key, strings.get(key) ]);
+  }
 
   // Sort the arrays, because we don't assume enumeration has a set order.
   // Sort compares [key, val] as string "key,val", which sorts the way we want
   // it to, so there is no need to provide a custom compare function.
   a.sort();
   b.sort();
 
-  test.assertEqual(a.length, b.length,
-                   "the iterator returns the correct number of items");
+  assert.equal(a.length, b.length,
+               "the iterator returns the correct number of items");
+
   for (let i = 0; i < a.length; i++) {
-    test.assertEqual(a[i][0], b[i][0], "the iterated string's name is correct");
-    test.assertEqual(a[i][1], b[i][1],
+    assert.equal(a[i][0], b[i][0], "the iterated string's name is correct");
+    assert.equal(a[i][1], b[i][1],
                      "the iterated string's value is correct");
   }
 };
+
+require("sdk/test").run(exports);
new file mode 100644
--- /dev/null
+++ b/addon-sdk/source/test/test-buffer.js
@@ -0,0 +1,90 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+const { Buffer, TextEncoder, TextDecoder } = require('sdk/io/buffer');
+
+exports.testIsEncoding = function (assert) {
+  Object.keys(ENCODINGS).forEach(encoding => {
+    assert.ok(Buffer.isEncoding(encoding),
+      'Buffer.isEncoding ' + encoding + ' truthy');
+  });
+  ['not-encoding', undefined, null, 100, {}].forEach(encoding => {
+    assert.ok(!Buffer.isEncoding(encoding),
+      'Buffer.isEncoding ' + encoding + ' falsy');
+  });
+};
+
+exports.testIsBuffer = function (assert) {
+  let buffer = new Buffer('content', 'utf8');
+  assert.ok(Buffer.isBuffer(buffer), 'isBuffer truthy on buffers');
+  assert.ok(!Buffer.isBuffer({}), 'isBuffer falsy on objects');
+  assert.ok(!Buffer.isBuffer(new Uint8Array()),
+    'isBuffer falsy on Uint8Array');
+};
+
+exports.testBufferByteLength = function (assert) {
+  let str = '\u00bd + \u00bc = \u00be';
+  assert.equal(Buffer.byteLength(str), 12,
+    'correct byteLength of string');
+};
+
+exports.testTextEncoderDecoder = function (assert) {
+  assert.ok(TextEncoder, 'TextEncoder exists');
+  assert.ok(TextDecoder, 'TextDecoder exists');
+};
+
+// List of supported encodings taken from:
+// http://mxr.mozilla.org/mozilla-central/source/dom/encoding/labelsencodings.properties
+const ENCODINGS = { "unicode-1-1-utf-8": 1, "utf-8": 1, "utf8": 1,
+  "866": 1, "cp866": 1, "csibm866": 1, "ibm866": 1, "csisolatin2": 1,
+  "iso-8859-2": 1, "iso-ir-101": 1, "iso8859-2": 1, "iso88592": 1,
+  "iso_8859-2": 1, "iso_8859-2:1987": 1, "l2": 1, "latin2": 1, "csisolatin3": 1,
+  "iso-8859-3": 1, "iso-ir-109": 1, "iso8859-3": 1, "iso88593": 1,
+  "iso_8859-3": 1, "iso_8859-3:1988": 1, "l3": 1, "latin3": 1, "csisolatin4": 1,
+  "iso-8859-4": 1, "iso-ir-110": 1, "iso8859-4": 1, "iso88594": 1,
+  "iso_8859-4": 1, "iso_8859-4:1988": 1, "l4": 1, "latin4": 1,
+  "csisolatincyrillic": 1, "cyrillic": 1, "iso-8859-5": 1, "iso-ir-144": 1,
+  "iso8859-5": 1, "iso88595": 1, "iso_8859-5": 1, "iso_8859-5:1988": 1,
+  "arabic": 1, "asmo-708": 1, "csiso88596e": 1, "csiso88596i": 1,
+  "csisolatinarabic": 1, "ecma-114": 1, "iso-8859-6": 1, "iso-8859-6-e": 1,
+  "iso-8859-6-i": 1, "iso-ir-127": 1, "iso8859-6": 1, "iso88596": 1,
+  "iso_8859-6": 1, "iso_8859-6:1987": 1, "csisolatingreek": 1, "ecma-118": 1,
+  "elot_928": 1, "greek": 1, "greek8": 1, "iso-8859-7": 1, "iso-ir-126": 1,
+  "iso8859-7": 1, "iso88597": 1, "iso_8859-7": 1, "iso_8859-7:1987": 1,
+  "sun_eu_greek": 1, "csiso88598e": 1, "csisolatinhebrew": 1, "hebrew": 1,
+  "iso-8859-8": 1, "iso-8859-8-e": 1, "iso-ir-138": 1, "iso8859-8": 1,
+  "iso88598": 1, "iso_8859-8": 1, "iso_8859-8:1988": 1, "visual": 1,
+  "csiso88598i": 1, "iso-8859-8-i": 1, "logical": 1, "csisolatin6": 1,
+  "iso-8859-10": 1, "iso-ir-157": 1, "iso8859-10": 1, "iso885910": 1,
+  "l6": 1, "latin6": 1, "iso-8859-13": 1, "iso8859-13": 1, "iso885913": 1,
+  "iso-8859-14": 1, "iso8859-14": 1, "iso885914": 1, "csisolatin9": 1,
+  "iso-8859-15": 1, "iso8859-15": 1, "iso885915": 1, "iso_8859-15": 1,
+  "l9": 1, "iso-8859-16": 1, "cskoi8r": 1, "koi": 1, "koi8": 1, "koi8-r": 1,
+  "koi8_r": 1, "koi8-u": 1, "csmacintosh": 1, "mac": 1, "macintosh": 1,
+  "x-mac-roman": 1, "dos-874": 1, "iso-8859-11": 1, "iso8859-11": 1,
+  "iso885911": 1, "tis-620": 1, "windows-874": 1, "cp1250": 1,
+  "windows-1250": 1, "x-cp1250": 1, "cp1251": 1, "windows-1251": 1,
+  "x-cp1251": 1, "ansi_x3.4-1968": 1, "ascii": 1, "cp1252": 1, "cp819": 1,
+  "csisolatin1": 1, "ibm819": 1, "iso-8859-1": 1, "iso-ir-100": 1,
+  "iso8859-1": 1, "iso88591": 1, "iso_8859-1": 1, "iso_8859-1:1987": 1,
+  "l1": 1, "latin1": 1, "us-ascii": 1, "windows-1252": 1, "x-cp1252": 1,
+  "cp1253": 1, "windows-1253": 1, "x-cp1253": 1, "cp1254": 1, "csisolatin5": 1,
+  "iso-8859-9": 1, "iso-ir-148": 1, "iso8859-9": 1, "iso88599": 1,
+  "iso_8859-9": 1, "iso_8859-9:1989": 1, "l5": 1, "latin5": 1,
+  "windows-1254": 1, "x-cp1254": 1, "cp1255": 1, "windows-1255": 1,
+  "x-cp1255": 1, "cp1256": 1, "windows-1256": 1, "x-cp1256": 1, "cp1257": 1,
+  "windows-1257": 1, "x-cp1257": 1, "cp1258": 1, "windows-1258": 1,
+  "x-cp1258": 1, "x-mac-cyrillic": 1, "x-mac-ukrainian": 1, "chinese": 1,
+  "csgb2312": 1, "csiso58gb231280": 1, "gb2312": 1, "gb_2312": 1,
+  "gb_2312-80": 1, "gbk": 1, "iso-ir-58": 1, "x-gbk": 1, "gb18030": 1,
+  "hz-gb-2312": 1, "big5": 1, "big5-hkscs": 1, "cn-big5": 1, "csbig5": 1,
+  "x-x-big5": 1, "cseucpkdfmtjapanese": 1, "euc-jp": 1, "x-euc-jp": 1,
+  "csiso2022jp": 1, "iso-2022-jp": 1, "csshiftjis": 1, "ms_kanji": 1,
+  "shift-jis": 1, "shift_jis": 1, "sjis": 1, "windows-31j": 1, "x-sjis": 1,
+  "cseuckr": 1, "csksc56011987": 1, "euc-kr": 1, "iso-ir-149": 1, "korean": 1,
+  "ks_c_5601-1987": 1, "ks_c_5601-1989": 1, "ksc5601": 1, "ksc_5601": 1,
+  "windows-949": 1, "csiso2022kr": 1, "iso-2022-kr": 1, "utf-16": 1,
+  "utf-16le": 1, "utf-16be": 1, "x-user-defined": 1 };
+
+require('sdk/test').run(exports);
--- a/addon-sdk/source/test/test-byte-streams.js
+++ b/addon-sdk/source/test/test-byte-streams.js
@@ -2,166 +2,168 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 const byteStreams = require("sdk/io/byte-streams");
 const file = require("sdk/io/file");
 const { pathFor } = require("sdk/system");
 const { Loader } = require("sdk/test/loader");
 
-const STREAM_CLOSED_ERROR = "The stream is closed and cannot be used.";
+const STREAM_CLOSED_ERROR = new RegExp("The stream is closed and cannot be used.");
 
 // This should match the constant of the same name in byte-streams.js.
 const BUFFER_BYTE_LEN = 0x8000;
 
-exports.testWriteRead = function (test) {
+exports.testWriteRead = function (assert) {
   let fname = dataFileFilename();
 
   // Write a small string less than the stream's buffer size...
   let str = "exports.testWriteRead data!";
-  let stream = open(test, fname, true);
-  test.assert(!stream.closed, "stream.closed after open should be false");
+  let stream = open(assert, fname, true);
+  assert.ok(!stream.closed, "stream.closed after open should be false");
   stream.write(str);
   stream.close();
-  test.assert(stream.closed, "Stream should be closed after stream.close");
-  test.assertRaises(function () stream.write("This shouldn't be written!"),
-                    STREAM_CLOSED_ERROR,
-                    "stream.write after close should raise error");
+  assert.ok(stream.closed, "Stream should be closed after stream.close");
+  assert.throws(function () stream.write("This shouldn't be written!"),
+    STREAM_CLOSED_ERROR,
+    "stream.write after close should raise error");
 
   // ... and read it.
-  stream = open(test, fname);
-  test.assertEqual(stream.read(), str,
-                   "stream.read should return string written");
-  test.assertEqual(stream.read(), "",
-                   "stream.read at EOS should return empty string");
+  stream = open(assert, fname);
+  assert.equal(stream.read(), str,
+    "stream.read should return string written");
+  assert.equal(stream.read(), "",
+    "stream.read at EOS should return empty string");
   stream.close();
-  test.assert(stream.closed, "Stream should be closed after stream.close");
-  test.assertRaises(function () stream.read(),
-                    STREAM_CLOSED_ERROR,
-                    "stream.read after close should raise error");
+  assert.ok(stream.closed, "Stream should be closed after stream.close");
+  assert.throws(function () stream.read(),
+    STREAM_CLOSED_ERROR,
+    "stream.read after close should raise error");
 
   file.remove(fname);
 };
 
 // Write a big string many times the size of the stream's buffer and read it.
-exports.testWriteReadBig = function (test) {
+exports.testWriteReadBig = function (assert) {
   let str = "";
   let bufLen = BUFFER_BYTE_LEN;
   let fileSize = bufLen * 10;
   for (let i = 0; i < fileSize; i++)
     str += i % 10;
   let fname = dataFileFilename();
-  let stream = open(test, fname, true);
+  let stream = open(assert, fname, true);
   stream.write(str);
   stream.close();
-  stream = open(test, fname);
-  test.assertEqual(stream.read(), str,
-                   "stream.read should return string written");
+  stream = open(assert, fname);
+  assert.equal(stream.read(), str,
+    "stream.read should return string written");
   stream.close();
   file.remove(fname);
 };
 
 // The same, but write and read in chunks.
-exports.testWriteReadChunks = function (test) {
+exports.testWriteReadChunks = function (assert) {
   let str = "";
   let bufLen = BUFFER_BYTE_LEN;
   let fileSize = bufLen * 10;
   for (let i = 0; i < fileSize; i++)
     str += i % 10;
   let fname = dataFileFilename();
-  let stream = open(test, fname, true);
+  let stream = open(assert, fname, true);
   let i = 0;
   while (i < str.length) {
     // Use a chunk length that spans buffers.
     let chunk = str.substr(i, bufLen + 1);
     stream.write(chunk);
     i += bufLen + 1;
   }
   stream.close();
-  stream = open(test, fname);
+  stream = open(assert, fname);
   let readStr = "";
   bufLen = BUFFER_BYTE_LEN;
   let readLen = bufLen + 1;
   do {
     var frag = stream.read(readLen);
     readStr += frag;
   } while (frag);
   stream.close();
-  test.assertEqual(readStr, str,
-                   "stream.write and read in chunks should work as expected");
+  assert.equal(readStr, str,
+    "stream.write and read in chunks should work as expected");
   file.remove(fname);
 };
 
-exports.testReadLengths = function (test) {
+exports.testReadLengths = function (assert) {
   let fname = dataFileFilename();
   let str = "exports.testReadLengths data!";
-  let stream = open(test, fname, true);
+  let stream = open(assert, fname, true);
   stream.write(str);
   stream.close();
 
-  stream = open(test, fname);
-  test.assertEqual(stream.read(str.length * 1000), str,
-                   "stream.read with big byte length should return string " +
-                   "written");
+  stream = open(assert, fname);
+  assert.equal(stream.read(str.length * 1000), str,
+    "stream.read with big byte length should return string " +
+    "written");
   stream.close();
 
-  stream = open(test, fname);
-  test.assertEqual(stream.read(0), "",
-                   "string.read with zero byte length should return empty " +
-                   "string");
+  stream = open(assert, fname);
+  assert.equal(stream.read(0), "",
+    "string.read with zero byte length should return empty " +
+    "string");
   stream.close();
 
-  stream = open(test, fname);
-  test.assertEqual(stream.read(-1), "",
-                   "string.read with negative byte length should return " +
-                   "empty string");
+  stream = open(assert, fname);
+  assert.equal(stream.read(-1), "",
+    "string.read with negative byte length should return " +
+    "empty string");
   stream.close();
 
   file.remove(fname);
 };
 
-exports.testTruncate = function (test) {
+exports.testTruncate = function (assert) {
   let fname = dataFileFilename();
   let str = "exports.testReadLengths data!";
-  let stream = open(test, fname, true);
+  let stream = open(assert, fname, true);
   stream.write(str);
   stream.close();
 
-  stream = open(test, fname);
-  test.assertEqual(stream.read(), str,
-                   "stream.read should return string written");
+  stream = open(assert, fname);
+  assert.equal(stream.read(), str,
+    "stream.read should return string written");
   stream.close();
 
-  stream = open(test, fname, true);
+  stream = open(assert, fname, true);
   stream.close();
 
-  stream = open(test, fname);
-  test.assertEqual(stream.read(), "",
-                   "stream.read after truncate should be empty");
+  stream = open(assert, fname);
+  assert.equal(stream.read(), "",
+    "stream.read after truncate should be empty");
   stream.close();
 
   file.remove(fname);
 };
 
-exports.testUnload = function (test) {
+exports.testUnload = function (assert) {
   let loader = Loader(module);
   let file = loader.require("sdk/io/file");
 
   let filename = dataFileFilename("temp-b");
   let stream = file.open(filename, "wb");
 
   loader.unload();
-  test.assert(stream.closed, "Stream should be closed after module unload");
+  assert.ok(stream.closed, "Stream should be closed after module unload");
 };
 
 // Returns the name of a file that should be used to test writing and reading.
 function dataFileFilename() {
   return file.join(pathFor("ProfD"), "test-byte-streams-data");
 }
 
 // Opens and returns the given file and ensures it's of the correct class.
-function open(test, filename, forWriting) {
+function open(assert, filename, forWriting) {
   let stream = file.open(filename, forWriting ? "wb" : "b");
   let klass = forWriting ? "ByteWriter" : "ByteReader";
-  test.assert(stream instanceof byteStreams[klass],
-              "Opened stream should be a " + klass);
+  assert.ok(stream instanceof byteStreams[klass],
+           "Opened stream should be a " + klass);
   return stream;
 }
+
+require('sdk/test').run(exports);
--- a/addon-sdk/source/test/test-content-loader.js
+++ b/addon-sdk/source/test/test-content-loader.js
@@ -1,216 +1,218 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 const { Loader } = require('sdk/content/loader');
 const self = require("sdk/self");
 
-exports['test:contentURL'] = function(test) {
+exports['test:contentURL'] = function(assert) {
   let loader = Loader(),
       value, emitted = 0, changes = 0;
 
-  test.assertRaises(
+  assert.throws(
     function() loader.contentURL = 4,
-    'The `contentURL` option must be a valid URL.',
+    /The `contentURL` option must be a valid URL./,
     'Must throw an exception if `contentURL` is not URL.'
   );
- test.assertRaises(
+ assert.throws(
     function() loader.contentURL = { toString: function() 'Oops' },
-    'The `contentURL` option must be a valid URL.',
+    /The `contentURL` option must be a valid URL./,
     'Must throw an exception if `contentURL` is not URL.'
   );
 
   function listener(e) {
     emitted ++;
-    test.assert(
+    assert.ok(
       'contentURL' in e,
       'emitted event must contain "content" property'
     );
-    test.assert(
+    assert.ok(
       value,
       '' + e.contentURL,
       'content property of an event must match value'
     );
   }
   loader.on('propertyChange', listener);
 
-  test.assertEqual(
+  assert.equal(
     null,
     loader.contentURL,
     'default value is `null`'
   );
   loader.contentURL = value = 'data:text/html,<html><body>Hi</body><html>';
-  test.assertEqual(
+  assert.equal(
     value,
     '' + loader.contentURL,
     'data uri is ok'
   );
-  test.assertEqual(
+  assert.equal(
     ++changes,
     emitted,
     'had to emit `propertyChange`'
   );
   loader.contentURL = value;
-  test.assertEqual(
+  assert.equal(
     changes,
     emitted,
     'must not emit `propertyChange` if same value is set'
   );
 
   loader.contentURL = value = 'http://google.com/';
-  test.assertEqual(
+  assert.equal(
     value,
     '' + loader.contentURL,
     'value must be set'
   );
-  test.assertEqual(
+  assert.equal(
     ++ changes,
     emitted,
     'had to emit `propertyChange`'
   );
   loader.contentURL = value;
-  test.assertEqual(
+  assert.equal(
     changes,
     emitted,
     'must not emit `propertyChange` if same value is set'
   );
 
   loader.removeListener('propertyChange', listener);
   loader.contentURL = value = 'about:blank';
-  test.assertEqual(
+  assert.equal(
     value,
     '' + loader.contentURL,
     'contentURL must be an actual value'
   );
-  test.assertEqual(
+  assert.equal(
     changes,
     emitted,
     'listener had to be romeved'
   );
 };
 
-exports['test:contentScriptWhen'] = function(test) {
+exports['test:contentScriptWhen'] = function(assert) {
   let loader = Loader();
-  test.assertEqual(
+  assert.equal(
     'end',
     loader.contentScriptWhen,
     '`contentScriptWhen` defaults to "end"'
   );
   loader.contentScriptWhen = "end";
-  test.assertEqual(
+  assert.equal(
     "end",
     loader.contentScriptWhen
   );
   try {
     loader.contentScriptWhen = 'boom';
     test.fail('must throw when wrong value is set');
   } catch(e) {
-    test.assertEqual(
+    assert.equal(
       'The `contentScriptWhen` option must be either "start", "ready" or "end".',
       e.message
     );
   }
   loader.contentScriptWhen = null;
-  test.assertEqual(
+  assert.equal(
     'end',
     loader.contentScriptWhen,
     '`contentScriptWhen` defaults to "end"'
   );
   loader.contentScriptWhen = "ready";
-  test.assertEqual(
+  assert.equal(
     "ready",
     loader.contentScriptWhen
   );
   loader.contentScriptWhen = "start";
-  test.assertEqual(
+  assert.equal(
     'start',
     loader.contentScriptWhen
   );
 };
 
-exports['test:contentScript'] = function(test) {
+exports['test:contentScript'] = function(assert) {
   let loader = Loader(), value;
-  test.assertEqual(
+  assert.equal(
     null,
     loader.contentScript,
     '`contentScript` defaults to `null`'
   );
   loader.contentScript = value = 'let test = {};';
-  test.assertEqual(
+  assert.equal(
     value,
     loader.contentScript
   );
   try {
     loader.contentScript = { 1: value }
     test.fail('must throw when wrong value is set');
   } catch(e) {
-    test.assertEqual(
+    assert.equal(
       'The `contentScript` option must be a string or an array of strings.',
       e.message
     );
   }
   try {
     loader.contentScript = ['oue', 2]
     test.fail('must throw when wrong value is set');
   } catch(e) {
-    test.assertEqual(
+    assert.equal(
       'The `contentScript` option must be a string or an array of strings.',
       e.message
     );
   }
   loader.contentScript = undefined;
-  test.assertEqual(
+  assert.equal(
     null,
     loader.contentScript
   );
   loader.contentScript = value = ["1;", "2;"];
-  test.assertEqual(
+  assert.equal(
     value,
     loader.contentScript
   );
 };
 
-exports['test:contentScriptFile'] = function(test) {
+exports['test:contentScriptFile'] = function(assert) {
   let loader = Loader(), value, uri = self.data.url("test-content-loader.js");
-  test.assertEqual(
+  assert.equal(
     null,
     loader.contentScriptFile,
     '`contentScriptFile` defaults to `null`'
   );
   loader.contentScriptFile = value = uri;
-  test.assertEqual(
+  assert.equal(
     value,
     loader.contentScriptFile
   );
   try {
     loader.contentScriptFile = { 1: uri }
     test.fail('must throw when wrong value is set');
   } catch(e) {
-    test.assertEqual(
+    assert.equal(
       'The `contentScriptFile` option must be a local URL or an array of URLs.',
       e.message
     );
   }
 
   try {
     loader.contentScriptFile = [ 'oue', uri ]
     test.fail('must throw when wrong value is set');
   } catch(e) {
-    test.assertEqual(
+    assert.equal(
       'The `contentScriptFile` option must be a local URL or an array of URLs.',
       e.message
     );
   }
 
   loader.contentScriptFile = undefined;
-  test.assertEqual(
+  assert.equal(
     null,
     loader.contentScriptFile
   );
   loader.contentScriptFile = value = [uri];
-  test.assertEqual(
+  assert.equal(
     value,
     loader.contentScriptFile
   );
 };
+
+require('sdk/test').run(exports);
--- a/addon-sdk/source/test/test-context-menu.js
+++ b/addon-sdk/source/test/test-context-menu.js
@@ -19,45 +19,45 @@ const OVERFLOW_THRESH_PREF =
   "extensions.addon-sdk.context-menu.overflowThreshold";
 const OVERFLOW_MENU_CLASS = "addon-content-menu-overflow-menu";
 const OVERFLOW_POPUP_CLASS = "addon-content-menu-overflow-popup";
 
 const TEST_DOC_URL = module.uri.replace(/\.js$/, ".html");
 
 // Tests that when present the separator is placed before the separator from
 // the old context-menu module
-exports.testSeparatorPosition = function (test) {
-  test = new TestHelper(test);
+exports.testSeparatorPosition = function (assert, done) {
+  let test = new TestHelper(assert, done);
   let loader = test.newLoader();
 
   // Create the old separator
   let oldSeparator = test.contextMenuPopup.ownerDocument.createElement("menuseparator");
   oldSeparator.id = "jetpack-context-menu-separator";
   test.contextMenuPopup.appendChild(oldSeparator);
 
   // Create an item.
   let item = new loader.cm.Item({ label: "item" });
 
   test.showMenu(null, function (popup) {
-    test.assertEqual(test.contextMenuSeparator.nextSibling.nextSibling, oldSeparator,
+    assert.equal(test.contextMenuSeparator.nextSibling.nextSibling, oldSeparator,
                      "New separator should appear before the old one");
     test.contextMenuPopup.removeChild(oldSeparator);
     test.done();
   });
 };
 
 // Destroying items that were previously created should cause them to be absent
 // from the menu.
-exports.testConstructDestroy = function (test) {
-  test = new TestHelper(test);
+exports.testConstructDestroy = function (assert, done) {
+  let test = new TestHelper(assert, done);
   let loader = test.newLoader();
 
   // Create an item.
   let item = new loader.cm.Item({ label: "item" });
-  test.assertEqual(item.parentMenu, loader.cm.contentContextMenu,
+  assert.equal(item.parentMenu, loader.cm.contentContextMenu,
                    "item's parent menu should be correct");
 
   test.showMenu(null, function (popup) {
 
     // It should be present when the menu is shown.
     test.checkMenu([item], [], []);
     popup.hidePopup();
 
@@ -70,33 +70,33 @@ exports.testConstructDestroy = function 
       test.checkMenu([item], [], [item]);
       test.done();
     });
   });
 };
 
 
 // Destroying an item twice should not cause an error.
-exports.testDestroyTwice = function (test) {
-  test = new TestHelper(test);
+exports.testDestroyTwice = function (assert, done) {
+  let test = new TestHelper(assert, done);
   let loader = test.newLoader();
 
   let item = new loader.cm.Item({ label: "item" });
   item.destroy();
   item.destroy();
 
   test.pass("Destroying an item twice should not cause an error.");
   test.done();
 };
 
 
 // CSS selector contexts should cause their items to be present in the menu
 // when the menu is invoked on nodes that match the selectors.
-exports.testSelectorContextMatch = function (test) {
-  test = new TestHelper(test);
+exports.testSelectorContextMatch = function (assert, done) {
+  let test = new TestHelper(assert, done);
   let loader = test.newLoader();
 
   let item = new loader.cm.Item({
     label: "item",
     data: "item",
     context: loader.cm.SelectorContext("img")
   });
 
@@ -107,18 +107,18 @@ exports.testSelectorContextMatch = funct
     });
   });
 };
 
 
 // CSS selector contexts should cause their items to be present in the menu
 // when the menu is invoked on nodes that have ancestors that match the
 // selectors.
-exports.testSelectorAncestorContextMatch = function (test) {
-  test = new TestHelper(test);
+exports.testSelectorAncestorContextMatch = function (assert, done) {
+  let test = new TestHelper(assert, done);
   let loader = test.newLoader();
 
   let item = new loader.cm.Item({
     label: "item",
     data: "item",
     context: loader.cm.SelectorContext("a[href]")
   });
 
@@ -129,18 +129,18 @@ exports.testSelectorAncestorContextMatch
     });
   });
 };
 
 
 // CSS selector contexts should cause their items to be absent from the menu
 // when the menu is not invoked on nodes that match or have ancestors that
 // match the selectors.
-exports.testSelectorContextNoMatch = function (test) {
-  test = new TestHelper(test);
+exports.testSelectorContextNoMatch = function (assert, done) {
+  let test = new TestHelper(assert, done);
   let loader = test.newLoader();
 
   let item = new loader.cm.Item({
     label: "item",
     data: "item",
     context: loader.cm.SelectorContext("img")
   });
 
@@ -148,18 +148,18 @@ exports.testSelectorContextNoMatch = fun
     test.checkMenu([item], [item], []);
     test.done();
   });
 };
 
 
 // Page contexts should cause their items to be present in the menu when the
 // menu is not invoked on an active element.
-exports.testPageContextMatch = function (test) {
-  test = new TestHelper(test);
+exports.testPageContextMatch = function (assert, done) {
+  let test = new TestHelper(assert, done);
   let loader = test.newLoader();
 
   let items = [
     new loader.cm.Item({
       label: "item 0"
     }),
     new loader.cm.Item({
       label: "item 1",
@@ -179,18 +179,18 @@ exports.testPageContextMatch = function 
     test.checkMenu(items, [], []);
     test.done();
   });
 };
 
 
 // Page contexts should cause their items to be absent from the menu when the
 // menu is invoked on an active element.
-exports.testPageContextNoMatch = function (test) {
-  test = new TestHelper(test);
+exports.testPageContextNoMatch = function (assert, done) {
+  let test = new TestHelper(assert, done);
   let loader = test.newLoader();
 
   let items = [
     new loader.cm.Item({
       label: "item 0"
     }),
     new loader.cm.Item({
       label: "item 1",
@@ -211,18 +211,18 @@ exports.testPageContextNoMatch = functio
       test.checkMenu(items, items, []);
       test.done();
     });
   });
 };
 
 
 // Selection contexts should cause items to appear when a selection exists.
-exports.testSelectionContextMatch = function (test) {
-  test = new TestHelper(test);
+exports.testSelectionContextMatch = function (assert, done) {
+  let test = new TestHelper(assert, done);
   let loader = test.newLoader();
 
   let item = loader.cm.Item({
     label: "item",
     context: loader.cm.SelectionContext()
   });
 
   test.withTestDoc(function (window, doc) {
@@ -232,18 +232,18 @@ exports.testSelectionContextMatch = func
       test.done();
     });
   });
 };
 
 
 // Selection contexts should cause items to appear when a selection exists in
 // a text field.
-exports.testSelectionContextMatchInTextField = function (test) {
-  test = new TestHelper(test);
+exports.testSelectionContextMatchInTextField = function (assert, done) {
+  let test = new TestHelper(assert, done);
   let loader = test.newLoader();
 
   let item = loader.cm.Item({
     label: "item",
     context: loader.cm.SelectionContext()
   });
 
   test.withTestDoc(function (window, doc) {
@@ -254,18 +254,18 @@ exports.testSelectionContextMatchInTextF
       test.done();
     });
   });
 };
 
 
 // Selection contexts should not cause items to appear when a selection does
 // not exist in a text field.
-exports.testSelectionContextNoMatchInTextField = function (test) {
-  test = new TestHelper(test);
+exports.testSelectionContextNoMatchInTextField = function (assert, done) {
+  let test = new TestHelper(assert, done);
   let loader = test.newLoader();
 
   let item = loader.cm.Item({
     label: "item",
     context: loader.cm.SelectionContext()
   });
 
   test.withTestDoc(function (window, doc) {
@@ -276,36 +276,36 @@ exports.testSelectionContextNoMatchInTex
       test.done();
     });
   });
 };
 
 
 // Selection contexts should not cause items to appear when a selection does
 // not exist.
-exports.testSelectionContextNoMatch = function (test) {
-  test = new TestHelper(test);
+exports.testSelectionContextNoMatch = function (assert, done) {
+  let test = new TestHelper(assert, done);
   let loader = test.newLoader();
 
   let item = loader.cm.Item({
     label: "item",
     context: loader.cm.SelectionContext()
   });
 
   test.showMenu(null, function (popup) {
     test.checkMenu([item], [item], []);
     test.done();
   });
 };
 
 
 // Selection contexts should cause items to appear when a selection exists even
 // for newly opened pages
-exports.testSelectionContextInNewTab = function (test) {
-  test = new TestHelper(test);
+exports.testSelectionContextInNewTab = function (assert, done) {
+  let test = new TestHelper(assert, done);
   let loader = test.newLoader();
 
   let item = loader.cm.Item({
     label: "item",
     context: loader.cm.SelectionContext()
   });
 
   test.withTestDoc(function (window, doc) {
@@ -331,18 +331,18 @@ exports.testSelectionContextInNewTab = f
         });
       });
     }, true);
   });
 };
 
 
 // Selection contexts should work when right clicking a form button
-exports.testSelectionContextButtonMatch = function (test) {
-  test = new TestHelper(test);
+exports.testSelectionContextButtonMatch = function (assert, done) {
+  let test = new TestHelper(assert, done);
   let loader = test.newLoader();
 
   let item = loader.cm.Item({
     label: "item",
     context: loader.cm.SelectionContext()
   });
 
   test.withTestDoc(function (window, doc) {
@@ -352,18 +352,18 @@ exports.testSelectionContextButtonMatch 
       test.checkMenu([item], [], []);
       test.done();
     });
   });
 };
 
 
 //Selection contexts should work when right clicking a form button
-exports.testSelectionContextButtonNoMatch = function (test) {
-  test = new TestHelper(test);
+exports.testSelectionContextButtonNoMatch = function (assert, done) {
+  let test = new TestHelper(assert, done);
   let loader = test.newLoader();
 
   let item = loader.cm.Item({
     label: "item",
     context: loader.cm.SelectionContext()
   });
 
   test.withTestDoc(function (window, doc) {
@@ -372,18 +372,18 @@ exports.testSelectionContextButtonNoMatc
       test.checkMenu([item], [item], []);
       test.done();
     });
   });
 };
 
 
 // URL contexts should cause items to appear on pages that match.
-exports.testURLContextMatch = function (test) {
-  test = new TestHelper(test);
+exports.testURLContextMatch = function (assert, done) {
+  let test = new TestHelper(assert, done);
   let loader = test.newLoader();
 
   let items = [
     loader.cm.Item({
       label: "item 0",
       context: loader.cm.URLContext(TEST_DOC_URL)
     }),
     loader.cm.Item({
@@ -401,18 +401,18 @@ exports.testURLContextMatch = function (
       test.checkMenu(items, [], []);
       test.done();
     });
   });
 };
 
 
 // URL contexts should not cause items to appear on pages that do not match.
-exports.testURLContextNoMatch = function (test) {
-  test = new TestHelper(test);
+exports.testURLContextNoMatch = function (assert, done) {
+  let test = new TestHelper(assert, done);
   let loader = test.newLoader();
 
   let items = [
     loader.cm.Item({
       label: "item 0",
       context: loader.cm.URLContext("*.bogus.com")
     }),
     loader.cm.Item({
@@ -432,47 +432,47 @@ exports.testURLContextNoMatch = function
     });
   });
 };
 
 
 // Removing a non-matching URL context after its item is created and the page is
 // loaded should cause the item's content script to be evaluated when the
 // context menu is next opened.
-exports.testURLContextRemove = function (test) {
-  test = new TestHelper(test);
+exports.testURLContextRemove = function (assert, done) {
+  let test = new TestHelper(assert, done);
   let loader = test.newLoader();
 
   let shouldBeEvaled = false;
   let context = loader.cm.URLContext("*.bogus.com");
   let item = loader.cm.Item({
     label: "item",
     context: context,
     contentScript: 'self.postMessage("ok"); self.on("context", function () true);',
     onMessage: function (msg) {
-      test.assert(shouldBeEvaled,
+      assert.ok(shouldBeEvaled,
                   "content script should be evaluated when expected");
-      test.assertEqual(msg, "ok", "Should have received the right message");
+      assert.equal(msg, "ok", "Should have received the right message");
       shouldBeEvaled = false;
     }
   });
 
   test.withTestDoc(function (window, doc) {
     test.showMenu(null, function (popup) {
       test.checkMenu([item], [item], []);
 
       item.context.remove(context);
 
       shouldBeEvaled = true;
 
       test.hideMenu(function () {
         test.showMenu(null, function (popup) {
           test.checkMenu([item], [], []);
 
-          test.assert(!shouldBeEvaled,
+          assert.ok(!shouldBeEvaled,
                       "content script should have been evaluated");
 
           test.hideMenu(function () {
             // Shouldn't get evaluated again
             test.showMenu(null, function (popup) {
               test.checkMenu([item], [], []);
               test.done();
             });
@@ -480,18 +480,18 @@ exports.testURLContextRemove = function 
         });
       });
     });
   });
 };
 
 // Loading a new page in the same tab should correctly start a new worker for
 // any content scripts
-exports.testPageReload = function (test) {
-  test = new TestHelper(test);
+exports.testPageReload = function (assert, done) {
+  let test = new TestHelper(assert, done);
   let loader = test.newLoader();
 
   let item = loader.cm.Item({
     label: "Item",
     contentScript: "var doc = document; self.on('context', function(node) doc.body.getAttribute('showItem') == 'true');"
   });
 
   test.withTestDoc(function (window, doc) {
@@ -527,129 +527,129 @@ exports.testPageReload = function (test)
         browser.loadURI("about:blank", null, null);
       });
     });
   });
 };
 
 // Closing a page after it's been used with a worker should cause the worker
 // to be destroyed
-/*exports.testWorkerDestroy = function (test) {
-  test = new TestHelper(test);
+/*exports.testWorkerDestroy = function (assert, done) {
+  let test = new TestHelper(assert, done);
   let loader = test.newLoader();
 
   let loadExpected = false;
 
   let item = loader.cm.Item({
     label: "item",
     contentScript: 'self.postMessage("loaded"); self.on("detach", function () { console.log("saw detach"); self.postMessage("detach") });',
     onMessage: function (msg) {
       console.log("Saw " + msg)
       switch (msg) {
       case "loaded":
-        test.assert(loadExpected, "Should have seen the load event at the right time");
+        assert.ok(loadExpected, "Should have seen the load event at the right time");
         loadExpected = false;
         break;
       case "detach":
         test.done();
         break;
       }
     }
   });
 
   test.withTestDoc(function (window, doc) {
     loadExpected = true;
     test.showMenu(null, function (popup) {
-      test.assert(!loadExpected, "Should have seen a message");
+      assert.ok(!loadExpected, "Should have seen a message");
 
       test.checkMenu([item], [], []);
 
       test.closeTab();
     });
   });
 };*/
 
 
 // Content contexts that return true should cause their items to be present
 // in the menu.
-exports.testContentContextMatch = function (test) {
-  test = new TestHelper(test);
+exports.testContentContextMatch = function (assert, done) {
+  let test = new TestHelper(assert, done);
   let loader = test.newLoader();
 
   let item = new loader.cm.Item({
     label: "item",
     contentScript: 'self.on("context", function () true);'
   });
 
   test.showMenu(null, function (popup) {
     test.checkMenu([item], [], []);
     test.done();
   });
 };
 
 
 // Content contexts that return false should cause their items to be absent
 // from the menu.
-exports.testContentContextNoMatch = function (test) {
-  test = new TestHelper(test);
+exports.testContentContextNoMatch = function (assert, done) {
+  let test = new TestHelper(assert, done);
   let loader = test.newLoader();
 
   let item = new loader.cm.Item({
     label: "item",
     contentScript: 'self.on("context", function () false);'
   });
 
   test.showMenu(null, function (popup) {
     test.checkMenu([item], [item], []);
     test.done();
   });
 };
 
 
 // Content contexts that return undefined should cause their items to be absent
 // from the menu.
-exports.testContentContextUndefined = function (test) {
-  test = new TestHelper(test);
+exports.testContentContextUndefined = function (assert, done) {
+  let test = new TestHelper(assert, done);
   let loader = test.newLoader();
 
   let item = new loader.cm.Item({
     label: "item",
     contentScript: 'self.on("context", function () {});'
   });
 
   test.showMenu(null, function (popup) {
     test.checkMenu([item], [item], []);
     test.done();
   });
 };
 
 
 // Content contexts that return an empty string should cause their items to be
 // absent from the menu and shouldn't wipe the label
-exports.testContentContextEmptyString = function (test) {
-  test = new TestHelper(test);
+exports.testContentContextEmptyString = function (assert, done) {
+  let test = new TestHelper(assert, done);
   let loader = test.newLoader();
 
   let item = new loader.cm.Item({
     label: "item",
     contentScript: 'self.on("context", function () "");'
   });
 
   test.showMenu(null, function (popup) {
     test.checkMenu([item], [item], []);
-    test.assertEqual(item.label, "item", "Label should still be correct");
+    assert.equal(item.label, "item", "Label should still be correct");
     test.done();
   });
 };
 
 
 // If any content contexts returns true then their items should be present in
 // the menu.
-exports.testMultipleContentContextMatch1 = function (test) {
-  test = new TestHelper(test);
+exports.testMultipleContentContextMatch1 = function (assert, done) {
+  let test = new TestHelper(assert, done);
   let loader = test.newLoader();
 
   let item = new loader.cm.Item({
     label: "item",
     contentScript: 'self.on("context", function () true); ' +
                    'self.on("context", function () false);',
     onMessage: function() {
       test.fail("Should not have called the second context listener");
@@ -660,18 +660,18 @@ exports.testMultipleContentContextMatch1
     test.checkMenu([item], [], []);
     test.done();
   });
 };
 
 
 // If any content contexts returns true then their items should be present in
 // the menu.
-exports.testMultipleContentContextMatch2 = function (test) {
-  test = new TestHelper(test);
+exports.testMultipleContentContextMatch2 = function (assert, done) {
+  let test = new TestHelper(assert, done);
   let loader = test.newLoader();
 
   let item = new loader.cm.Item({
     label: "item",
     contentScript: 'self.on("context", function () false); ' +
                    'self.on("context", function () true);'
   });
 
@@ -679,77 +679,77 @@ exports.testMultipleContentContextMatch2
     test.checkMenu([item], [], []);
     test.done();
   });
 };
 
 
 // If any content contexts returns a string then their items should be present
 // in the menu.
-exports.testMultipleContentContextString1 = function (test) {
-  test = new TestHelper(test);
+exports.testMultipleContentContextString1 = function (assert, done) {
+  let test = new TestHelper(assert, done);
   let loader = test.newLoader();
 
   let item = new loader.cm.Item({
     label: "item",
     contentScript: 'self.on("context", function () "new label"); ' +
                    'self.on("context", function () false);'
   });
 
   test.showMenu(null, function (popup) {
     test.checkMenu([item], [], []);
-    test.assertEqual(item.label, "new label", "Label should have changed");
+    assert.equal(item.label, "new label", "Label should have changed");
     test.done();
   });
 };
 
 
 // If any content contexts returns a string then their items should be present
 // in the menu.
-exports.testMultipleContentContextString2 = function (test) {
-  test = new TestHelper(test);
+exports.testMultipleContentContextString2 = function (assert, done) {
+  let test = new TestHelper(assert, done);
   let loader = test.newLoader();
 
   let item = new loader.cm.Item({
     label: "item",
     contentScript: 'self.on("context", function () false); ' +
                    'self.on("context", function () "new label");'
   });
 
   test.showMenu(null, function (popup) {
     test.checkMenu([item], [], []);
-    test.assertEqual(item.label, "new label", "Label should have changed");
+    assert.equal(item.label, "new label", "Label should have changed");
     test.done();
   });
 };
 
 
 // If many content contexts returns a string then the first should take effect
-exports.testMultipleContentContextString3 = function (test) {
-  test = new TestHelper(test);
+exports.testMultipleContentContextString3 = function (assert, done) {
+  let test = new TestHelper(assert, done);
   let loader = test.newLoader();
 
   let item = new loader.cm.Item({
     label: "item",
     contentScript: 'self.on("context", function () "new label 1"); ' +
                    'self.on("context", function () "new label 2");'
   });
 
   test.showMenu(null, function (popup) {
     test.checkMenu([item], [], []);
-    test.assertEqual(item.label, "new label 1", "Label should have changed");
+    assert.equal(item.label, "new label 1", "Label should have changed");
     test.done();
   });
 };
 
 
 // Content contexts that return true should cause their items to be present
 // in the menu when context clicking an active element.
-exports.testContentContextMatchActiveElement = function (test) {
-  test = new TestHelper(test);
+exports.testContentContextMatchActiveElement = function (assert, done) {
+  let test = new TestHelper(assert, done);
   let loader = test.newLoader();
 
   let items = [
     new loader.cm.Item({
       label: "item 1",
       contentScript: 'self.on("context", function () true);'
     }),
     new loader.cm.Item({
@@ -776,18 +776,18 @@ exports.testContentContextMatchActiveEle
       test.done();
     });
   });
 };
 
 
 // Content contexts that return false should cause their items to be absent
 // from the menu when context clicking an active element.
-exports.testContentContextNoMatchActiveElement = function (test) {
-  test = new TestHelper(test);
+exports.testContentContextNoMatchActiveElement = function (assert, done) {
+  let test = new TestHelper(assert, done);
   let loader = test.newLoader();
 
   let items = [
     new loader.cm.Item({
       label: "item 1",
       contentScript: 'self.on("context", function () false);'
     }),
     new loader.cm.Item({
@@ -814,18 +814,18 @@ exports.testContentContextNoMatchActiveE
       test.done();
     });
   });
 };
 
 
 // Content contexts that return undefined should cause their items to be absent
 // from the menu when context clicking an active element.
-exports.testContentContextNoMatchActiveElement = function (test) {
-  test = new TestHelper(test);
+exports.testContentContextNoMatchActiveElement = function (assert, done) {
+  let test = new TestHelper(assert, done);
   let loader = test.newLoader();
 
   let items = [
     new loader.cm.Item({
       label: "item 1",
       contentScript: 'self.on("context", function () {});'
     }),
     new loader.cm.Item({
@@ -852,90 +852,90 @@ exports.testContentContextNoMatchActiveE
       test.done();
     });
   });
 };
 
 
 // Content contexts that return a string should cause their items to be present
 // in the menu and the items' labels to be updated.
-exports.testContentContextMatchString = function (test) {
-  test = new TestHelper(test);
+exports.testContentContextMatchString = function (assert, done) {
+  let test = new TestHelper(assert, done);
   let loader = test.newLoader();
 
   let item = new loader.cm.Item({
     label: "first label",
     contentScript: 'self.on("context", function () "second label");'
   });
 
   test.showMenu(null, function (popup) {
     test.checkMenu([item], [], []);
-    test.assertEqual(item.label, "second label",
+    assert.equal(item.label, "second label",
                      "item's label should be updated");
     test.done();
   });
 };
 
 
 // Ensure that contentScriptFile is working correctly
-exports.testContentScriptFile = function (test) {
-  test = new TestHelper(test);
+exports.testContentScriptFile = function (assert, done) {
+  let test = new TestHelper(assert, done);
   let loader = test.newLoader();
 
   // Reject remote files
-  test.assertRaises(function() {
+  assert.throws(function() {
       new loader.cm.Item({
         label: "item",
         contentScriptFile: "http://mozilla.com/context-menu.js"
       });
     },
-    "The 'contentScriptFile' option must be a local file URL " +
-    "or an array of local file URLs.",
+    new RegExp("The 'contentScriptFile' option must be a local file URL " +
+    "or an array of local file URLs."),
     "Item throws when contentScriptFile is a remote URL");
 
   // But accept files from data folder
   let item = new loader.cm.Item({
     label: "item",
     contentScriptFile: require("sdk/self").data.url("test-context-menu.js")
   });
 
   test.showMenu(null, function (popup) {
     test.checkMenu([item], [], []);
     test.done();
   });
 };
 
 
 // The args passed to context listeners should be correct.
-exports.testContentContextArgs = function (test) {
-  test = new TestHelper(test);
+exports.testContentContextArgs = function (assert, done) {
+  let test = new TestHelper(assert, done);
   let loader = test.newLoader();
   let callbacks = 0;
 
   let item = new loader.cm.Item({
     label: "item",
     contentScript: 'self.on("context", function (node) {' +
                    '  self.postMessage(node.tagName);' +
                    '  return false;' +
                    '});',
     onMessage: function (tagName) {
-      test.assertEqual(tagName, "HTML", "node should be an HTML element");
+      assert.equal(tagName, "HTML", "node should be an HTML element");
       if (++callbacks == 2) test.done();
     }
   });
 
   test.showMenu(null, function () {
     if (++callbacks == 2) test.done();
   });
 };
 
 // Multiple contexts imply intersection, not union, and content context
 // listeners should not be called if all declarative contexts are not current.
-exports.testMultipleContexts = function (test) {
-  test = new TestHelper(test);
+exports.testMultipleContexts = function (assert, done) {
+  let test = new TestHelper(assert, done);
   let loader = test.newLoader();
 
   let item = new loader.cm.Item({
     label: "item",
     context: [loader.cm.SelectorContext("a[href]"), loader.cm.PageContext()],
     contentScript: 'self.on("context", function () self.postMessage());',
     onMessage: function () {
       test.fail("Context listener should not be called");
@@ -946,18 +946,18 @@ exports.testMultipleContexts = function 
     test.showMenu(doc.getElementById("span-link"), function (popup) {
       test.checkMenu([item], [item], []);
       test.done();
     });
   });
 };
 
 // Once a context is removed, it should no longer cause its item to appear.
-exports.testRemoveContext = function (test) {
-  test = new TestHelper(test);
+exports.testRemoveContext = function (assert, done) {
+  let test = new TestHelper(assert, done);
   let loader = test.newLoader();
 
   let ctxt = loader.cm.SelectorContext("img");
   let item = new loader.cm.Item({
     label: "item",
     context: ctxt
   });
 
@@ -975,36 +975,36 @@ exports.testRemoveContext = function (te
         test.done();
       });
     });
   });
 };
 
 
 // Lots of items should overflow into the overflow submenu.
-exports.testOverflow = function (test) {
-  test = new TestHelper(test);
+exports.testOverflow = function (assert, done) {
+  let test = new TestHelper(assert, done);
   let loader = test.newLoader();
 
   let items = [];
   for (let i = 0; i < OVERFLOW_THRESH_DEFAULT + 1; i++) {
     let item = new loader.cm.Item({ label: "item " + i });
     items.push(item);
   }
 
   test.showMenu(null, function (popup) {
     test.checkMenu(items, [], []);
     test.done();
   });
 };
 
 
 // Module unload should cause all items to be removed.
-exports.testUnload = function (test) {
-  test = new TestHelper(test);
+exports.testUnload = function (assert, done) {
+  let test = new TestHelper(assert, done);
   let loader = test.newLoader();
 
   let item = new loader.cm.Item({ label: "item" });
 
   test.showMenu(null, function (popup) {
 
     // The menu should contain the item.
     test.checkMenu([item], [], []);
@@ -1019,18 +1019,18 @@ exports.testUnload = function (test) {
       test.done();
     });
   });
 };
 
 
 // Using multiple module instances to add items without causing overflow should
 // work OK.  Assumes OVERFLOW_THRESH_DEFAULT >= 2.
-exports.testMultipleModulesAdd = function (test) {
-  test = new TestHelper(test);
+exports.testMultipleModulesAdd = function (assert, done) {
+  let test = new TestHelper(assert, done);
   let loader0 = test.newLoader();
   let loader1 = test.newLoader();
 
   // Use each module to add an item, then unload each module in turn.
   let item0 = new loader0.cm.Item({ label: "item 0" });
   let item1 = new loader1.cm.Item({ label: "item 1" });
 
   test.showMenu(null, function (popup) {
@@ -1056,18 +1056,18 @@ exports.testMultipleModulesAdd = functio
         test.done();
       });
     });
   });
 };
 
 
 // Using multiple module instances to add items causing overflow should work OK.
-exports.testMultipleModulesAddOverflow = function (test) {
-  test = new TestHelper(test);
+exports.testMultipleModulesAddOverflow = function (assert, done) {
+  let test = new TestHelper(assert, done);
   let loader0 = test.newLoader();
   let loader1 = test.newLoader();
 
   // Use module 0 to add OVERFLOW_THRESH_DEFAULT items.
   let items0 = [];
   for (let i = 0; i < OVERFLOW_THRESH_DEFAULT; i++) {
     let item = new loader0.cm.Item({ label: "item 0 " + i });
     items0.push(item);
@@ -1105,18 +1105,18 @@ exports.testMultipleModulesAddOverflow =
   });
 };
 
 
 // Using multiple module instances to modify the menu without causing overflow
 // should work OK.  This test creates two loaders and:
 // loader0 create item -> loader1 create item -> loader0.unload ->
 // loader1.unload
-exports.testMultipleModulesDiffContexts1 = function (test) {
-  test = new TestHelper(test);
+exports.testMultipleModulesDiffContexts1 = function (assert, done) {
+  let test = new TestHelper(assert, done);
   let loader0 = test.newLoader();
   let loader1 = test.newLoader();
 
   let item0 = new loader0.cm.Item({
     label: "item 0",
     context: loader0.cm.SelectorContext("img")
   });
 
@@ -1148,18 +1148,18 @@ exports.testMultipleModulesDiffContexts1
   });
 };
 
 
 // Using multiple module instances to modify the menu without causing overflow
 // should work OK.  This test creates two loaders and:
 // loader1 create item -> loader0 create item -> loader0.unload ->
 // loader1.unload
-exports.testMultipleModulesDiffContexts2 = function (test) {
-  test = new TestHelper(test);
+exports.testMultipleModulesDiffContexts2 = function (assert, done) {
+  let test = new TestHelper(assert, done);
   let loader0 = test.newLoader();
   let loader1 = test.newLoader();
 
   let item1 = new loader1.cm.Item({ label: "item 1" });
 
   let item0 = new loader0.cm.Item({
     label: "item 0",
     context: loader0.cm.SelectorContext("img")
@@ -1191,18 +1191,18 @@ exports.testMultipleModulesDiffContexts2
   });
 };
 
 
 // Using multiple module instances to modify the menu without causing overflow
 // should work OK.  This test creates two loaders and:
 // loader0 create item -> loader1 create item -> loader1.unload ->
 // loader0.unload
-exports.testMultipleModulesDiffContexts3 = function (test) {
-  test = new TestHelper(test);
+exports.testMultipleModulesDiffContexts3 = function (assert, done) {
+  let test = new TestHelper(assert, done);
   let loader0 = test.newLoader();
   let loader1 = test.newLoader();
 
   let item0 = new loader0.cm.Item({
     label: "item 0",
     context: loader0.cm.SelectorContext("img")
   });
 
@@ -1234,18 +1234,18 @@ exports.testMultipleModulesDiffContexts3
   });
 };
 
 
 // Using multiple module instances to modify the menu without causing overflow
 // should work OK.  This test creates two loaders and:
 // loader1 create item -> loader0 create item -> loader1.unload ->
 // loader0.unload
-exports.testMultipleModulesDiffContexts4 = function (test) {
-  test = new TestHelper(test);
+exports.testMultipleModulesDiffContexts4 = function (assert, done) {
+  let test = new TestHelper(assert, done);
   let loader0 = test.newLoader();
   let loader1 = test.newLoader();
 
   let item1 = new loader1.cm.Item({ label: "item 1" });
 
   let item0 = new loader0.cm.Item({
     label: "item 0",
     context: loader0.cm.SelectorContext("img")
@@ -1275,18 +1275,18 @@ exports.testMultipleModulesDiffContexts4
       });
     });
   });
 };
 
 
 // Test interactions between a loaded module, unloading another module, and the
 // menu separator and overflow submenu.
-exports.testMultipleModulesAddRemove = function (test) {
-  test = new TestHelper(test);
+exports.testMultipleModulesAddRemove = function (assert, done) {
+  let test = new TestHelper(assert, done);
   let loader0 = test.newLoader();
   let loader1 = test.newLoader();
 
   let item = new loader0.cm.Item({ label: "item" });
 
   test.showMenu(null, function (popup) {
 
     // The menu should contain the item.
@@ -1312,18 +1312,18 @@ exports.testMultipleModulesAddRemove = f
       });
     });
   });
 };
 
 
 // Checks that the order of menu items is correct when adding/removing across
 // multiple modules. All items from a single module should remain in a group
-exports.testMultipleModulesOrder = function (test) {
-  test = new TestHelper(test);
+exports.testMultipleModulesOrder = function (assert, done) {
+  let test = new TestHelper(assert, done);
   let loader0 = test.newLoader();
   let loader1 = test.newLoader();
 
   // Use each module to add an item, then unload each module in turn.
   let item0 = new loader0.cm.Item({ label: "item 0" });
   let item1 = new loader1.cm.Item({ label: "item 1" });
 
   test.showMenu(null, function (popup) {
@@ -1351,18 +1351,18 @@ exports.testMultipleModulesOrder = funct
     });
   });
 };
 
 
 // Checks that the order of menu items is correct when adding/removing across
 // multiple modules when overflowing. All items from a single module should
 // remain in a group
-exports.testMultipleModulesOrderOverflow = function (test) {
-  test = new TestHelper(test);
+exports.testMultipleModulesOrderOverflow = function (assert, done) {
+  let test = new TestHelper(assert, done);
   let loader0 = test.newLoader();
   let loader1 = test.newLoader();
 
   let prefs = loader0.loader.require("sdk/preferences/service");
   prefs.set(OVERFLOW_THRESH_PREF, 0);
 
   // Use each module to add an item, then unload each module in turn.
   let item0 = new loader0.cm.Item({ label: "item 0" });
@@ -1392,18 +1392,18 @@ exports.testMultipleModulesOrderOverflow
       });
     });
   });
 };
 
 
 // Checks that if a module's items are all hidden then the overflow menu doesn't
 // get hidden
-exports.testMultipleModulesOverflowHidden = function (test) {
-  test = new TestHelper(test);
+exports.testMultipleModulesOverflowHidden = function (assert, done) {
+  let test = new TestHelper(assert, done);
   let loader0 = test.newLoader();
   let loader1 = test.newLoader();
 
   let prefs = loader0.loader.require("sdk/preferences/service");
   prefs.set(OVERFLOW_THRESH_PREF, 0);
 
   // Use each module to add an item, then unload each module in turn.
   let item0 = new loader0.cm.Item({ label: "item 0" });
@@ -1417,18 +1417,18 @@ exports.testMultipleModulesOverflowHidde
     test.checkMenu([item0, item1], [item1], []);
     test.done();
   });
 };
 
 
 // Checks that if a module's items are all hidden then the overflow menu doesn't
 // get hidden (reverse order to above)
-exports.testMultipleModulesOverflowHidden2 = function (test) {
-  test = new TestHelper(test);
+exports.testMultipleModulesOverflowHidden2 = function (assert, done) {
+  let test = new TestHelper(assert, done);
   let loader0 = test.newLoader();
   let loader1 = test.newLoader();
 
   let prefs = loader0.loader.require("sdk/preferences/service");
   prefs.set(OVERFLOW_THRESH_PREF, 0);
 
   // Use each module to add an item, then unload each module in turn.
   let item0 = new loader0.cm.Item({
@@ -1442,18 +1442,18 @@ exports.testMultipleModulesOverflowHidde
     test.checkMenu([item0, item1], [item0], []);
     test.done();
   });
 };
 
 
 // Checks that we don't overflow if there are more items than the overflow
 // threshold but not all of them are visible
-exports.testOverflowIgnoresHidden = function (test) {
-  test = new TestHelper(test);
+exports.testOverflowIgnoresHidden = function (assert, done) {
+  let test = new TestHelper(assert, done);
   let loader = test.newLoader();
 
   let prefs = loader.loader.require("sdk/preferences/service");
   prefs.set(OVERFLOW_THRESH_PREF, 2);
 
   let allItems = [
     new loader.cm.Item({
       label: "item 0"
@@ -1472,18 +1472,18 @@ exports.testOverflowIgnoresHidden = func
     test.checkMenu(allItems, [allItems[2]], []);
     test.done();
   });
 };
 
 
 // Checks that we don't overflow if there are more items than the overflow
 // threshold but not all of them are visible
-exports.testOverflowIgnoresHiddenMultipleModules1 = function (test) {
-  test = new TestHelper(test);
+exports.testOverflowIgnoresHiddenMultipleModules1 = function (assert, done) {
+  let test = new TestHelper(assert, done);
   let loader0 = test.newLoader();
   let loader1 = test.newLoader();
 
   let prefs = loader0.loader.require("sdk/preferences/service");
   prefs.set(OVERFLOW_THRESH_PREF, 2);
 
   let allItems = [
     new loader0.cm.Item({
@@ -1507,18 +1507,18 @@ exports.testOverflowIgnoresHiddenMultipl
     test.checkMenu(allItems, [allItems[2], allItems[3]], []);
     test.done();
   });
 };
 
 
 // Checks that we don't overflow if there are more items than the overflow
 // threshold but not all of them are visible
-exports.testOverflowIgnoresHiddenMultipleModules2 = function (test) {
-  test = new TestHelper(test);
+exports.testOverflowIgnoresHiddenMultipleModules2 = function (assert, done) {
+  let test = new TestHelper(assert, done);
   let loader0 = test.newLoader();
   let loader1 = test.newLoader();
 
   let prefs = loader0.loader.require("sdk/preferences/service");
   prefs.set(OVERFLOW_THRESH_PREF, 2);
 
   let allItems = [
     new loader0.cm.Item({
@@ -1542,18 +1542,18 @@ exports.testOverflowIgnoresHiddenMultipl
     test.checkMenu(allItems, [allItems[1], allItems[3]], []);
     test.done();
   });
 };
 
 
 // Checks that we don't overflow if there are more items than the overflow
 // threshold but not all of them are visible
-exports.testOverflowIgnoresHiddenMultipleModules3 = function (test) {
-  test = new TestHelper(test);
+exports.testOverflowIgnoresHiddenMultipleModules3 = function (assert, done) {
+  let test = new TestHelper(assert, done);
   let loader0 = test.newLoader();
   let loader1 = test.newLoader();
 
   let prefs = loader0.loader.require("sdk/preferences/service");
   prefs.set(OVERFLOW_THRESH_PREF, 2);
 
   let allItems = [
     new loader0.cm.Item({
@@ -1577,18 +1577,18 @@ exports.testOverflowIgnoresHiddenMultipl
     test.checkMenu(allItems, [allItems[0], allItems[1]], []);
     test.done();
   });
 };
 
 
 // Tests that we transition between overflowing to non-overflowing to no items
 // and back again
-exports.testOverflowTransition = function (test) {
-  test = new TestHelper(test);
+exports.testOverflowTransition = function (assert, done) {
+  let test = new TestHelper(assert, done);
   let loader = test.newLoader();
 
   let prefs = loader.loader.require("sdk/preferences/service");
   prefs.set(OVERFLOW_THRESH_PREF, 2);
 
   let pItems = [
     new loader.cm.Item({
       label: "item 0",
@@ -1655,33 +1655,33 @@ exports.testOverflowTransition = functio
         });
       });
     });
   });
 };
 
 
 // An item's command listener should work.
-exports.testItemCommand = function (test) {
-  test = new TestHelper(test);
+exports.testItemCommand = function (assert, done) {
+  let test = new TestHelper(assert, done);
   let loader = test.newLoader();
 
   let item = new loader.cm.Item({
     label: "item",
     data: "item data",
     contentScript: 'self.on("click", function (node, data) {' +
                    '  self.postMessage({' +
                    '    tagName: node.tagName,' +
                    '    data: data' +
                    '  });' +
                    '});',
     onMessage: function (data) {
-      test.assertEqual(this, item, "`this` inside onMessage should be item");
-      test.assertEqual(data.tagName, "HTML", "node should be an HTML element");
-      test.assertEqual(data.data, item.data, "data should be item data");
+      assert.equal(this, item, "`this` inside onMessage should be item");
+      assert.equal(data.tagName, "HTML", "node should be an HTML element");
+      assert.equal(data.data, item.data, "data should be item data");
       test.done();
     }
   });
 
   test.showMenu(null, function (popup) {
     test.checkMenu([item], [], []);
     let elt = test.getItemElt(popup, item);
 
@@ -1692,21 +1692,21 @@ exports.testItemCommand = function (test
   });
 };
 
 
 // A menu's click listener should work and receive bubbling 'command' events from
 // sub-items appropriately.  This also tests menus and ensures that when a CSS
 // selector context matches the clicked node's ancestor, the matching ancestor
 // is passed to listeners as the clicked node.
-exports.testMenuCommand = function (test) {
+exports.testMenuCommand = function (assert, done) {
   // Create a top-level menu, submenu, and item, like this:
   // topMenu -> submenu -> item
   // Click the item and make sure the click bubbles.
-  test = new TestHelper(test);
+  let test = new TestHelper(assert, done);
   let loader = test.newLoader();
 
   let item = new loader.cm.Item({
     label: "submenu item",
     data: "submenu item data",
     context: loader.cm.SelectorContext("a"),
   });
 
@@ -1720,19 +1720,19 @@ exports.testMenuCommand = function (test
     label: "top menu",
     contentScript: 'self.on("click", function (node, data) {' +
                    '  self.postMessage({' +
                    '    tagName: node.tagName,' +
                    '    data: data' +
                    '  });' +
                    '});',
     onMessage: function (data) {
-      test.assertEqual(this, topMenu, "`this` inside top menu should be menu");
-      test.assertEqual(data.tagName, "A", "Clicked node should be anchor");
-      test.assertEqual(data.data, item.data,
+      assert.equal(this, topMenu, "`this` inside top menu should be menu");
+      assert.equal(data.tagName, "A", "Clicked node should be anchor");
+      assert.equal(data.data, item.data,
                        "Clicked item data should be correct");
       test.done();
     },
     items: [submenu],
     context: loader.cm.SelectorContext("a")
   });
 
   test.withTestDoc(function (window, doc) {
@@ -1749,18 +1749,18 @@ exports.testMenuCommand = function (test
       evt.initEvent('command', true, true);
       itemElt.dispatchEvent(evt);
     });
   });
 };
 
 
 // Click listeners should work when multiple modules are loaded.
-exports.testItemCommandMultipleModules = function (test) {
-  test = new TestHelper(test);
+exports.testItemCommandMultipleModules = function (assert, done) {
+  let test = new TestHelper(assert, done);
   let loader0 = test.newLoader();
   let loader1 = test.newLoader();
 
   let item0 = loader0.cm.Item({
     label: "loader 0 item",
     contentScript: 'self.on("click", self.postMessage);',
     onMessage: function () {
       test.fail("loader 0 item should not emit click event");
@@ -1785,54 +1785,54 @@ exports.testItemCommandMultipleModules =
     item1Elt.dispatchEvent(evt);
   });
 };
 
 
 
 
 // An item's click listener should work.
-exports.testItemClick = function (test) {
-  test = new TestHelper(test);
+exports.testItemClick = function (assert, done) {
+  let test = new TestHelper(assert, done);
   let loader = test.newLoader();
 
   let item = new loader.cm.Item({
     label: "item",
     data: "item data",
     contentScript: 'self.on("click", function (node, data) {' +
                    '  self.postMessage({' +
                    '    tagName: node.tagName,' +
                    '    data: data' +
                    '  });' +
                    '});',
     onMessage: function (data) {
-      test.assertEqual(this, item, "`this` inside onMessage should be item");
-      test.assertEqual(data.tagName, "HTML", "node should be an HTML element");
-      test.assertEqual(data.data, item.data, "data should be item data");
+      assert.equal(this, item, "`this` inside onMessage should be item");
+      assert.equal(data.tagName, "HTML", "node should be an HTML element");
+      assert.equal(data.data, item.data, "data should be item data");
       test.done();
     }
   });
 
   test.showMenu(null, function (popup) {
     test.checkMenu([item], [], []);
     let elt = test.getItemElt(popup, item);
     elt.click();
   });
 };
 
 
 // A menu's click listener should work and receive bubbling clicks from
 // sub-items appropriately.  This also tests menus and ensures that when a CSS
 // selector context matches the clicked node's ancestor, the matching ancestor
 // is passed to listeners as the clicked node.
-exports.testMenuClick = function (test) {
+exports.testMenuClick = function (assert, done) {
   // Create a top-level menu, submenu, and item, like this:
   // topMenu -> submenu -> item
   // Click the item and make sure the click bubbles.
-  test = new TestHelper(test);
+  let test = new TestHelper(assert, done);
   let loader = test.newLoader();
 
   let item = new loader.cm.Item({
     label: "submenu item",
     data: "submenu item data",
     context: loader.cm.SelectorContext("a"),
   });
 
@@ -1846,19 +1846,19 @@ exports.testMenuClick = function (test) 
     label: "top menu",
     contentScript: 'self.on("click", function (node, data) {' +
                    '  self.postMessage({' +
                    '    tagName: node.tagName,' +
                    '    data: data' +
                    '  });' +
                    '});',
     onMessage: function (data) {
-      test.assertEqual(this, topMenu, "`this` inside top menu should be menu");
-      test.assertEqual(data.tagName, "A", "Clicked node should be anchor");
-      test.assertEqual(data.data, item.data,
+      assert.equal(this, topMenu, "`this` inside top menu should be menu");
+      assert.equal(data.tagName, "A", "Clicked node should be anchor");
+      assert.equal(data.data, item.data,
                        "Clicked item data should be correct");
       test.done();
     },
     items: [submenu],
     context: loader.cm.SelectorContext("a")
   });
 
   test.withTestDoc(function (window, doc) {
@@ -1870,18 +1870,18 @@ exports.testMenuClick = function (test) 
       let submenuPopup = submenuElt.firstChild;
       let itemElt = test.getItemElt(submenuPopup, item);
       itemElt.click();
     });
   });
 };
 
 // Click listeners should work when multiple modules are loaded.
-exports.testItemClickMultipleModules = function (test) {
-  test = new TestHelper(test);
+exports.testItemClickMultipleModules = function (assert, done) {
+  let test = new TestHelper(assert, done);
   let loader0 = test.newLoader();
   let loader1 = test.newLoader();
 
   let item0 = loader0.cm.Item({
     label: "loader 0 item",
     contentScript: 'self.on("click", self.postMessage);',
     onMessage: function () {
       test.fail("loader 0 item should not emit click event");
@@ -1900,77 +1900,77 @@ exports.testItemClickMultipleModules = f
     test.checkMenu([item0, item1], [], []);
     let item1Elt = test.getItemElt(popup, item1);
     item1Elt.click();
   });
 };
 
 
 // Adding a separator to a submenu should work OK.
-exports.testSeparator = function (test) {
-  test = new TestHelper(test);
+exports.testSeparator = function (assert, done) {
+  let test = new TestHelper(assert, done);
   let loader = test.newLoader();
 
   let menu = new loader.cm.Menu({
     label: "submenu",
     items: [new loader.cm.Separator()]
   });
 
   test.showMenu(null, function (popup) {
     test.checkMenu([menu], [], []);
     test.done();
   });
 };
 
 
 // The parentMenu option should work
-exports.testParentMenu = function (test) {
-  test = new TestHelper(test);
+exports.testParentMenu = function (assert, done) {
+  let test = new TestHelper(assert, done);
   let loader = test.newLoader();
 
   let menu = new loader.cm.Menu({
     label: "submenu",
     items: [loader.cm.Item({ label: "item 1" })],
     parentMenu: loader.cm.contentContextMenu
   });
 
   let item = loader.cm.Item({
     label: "item 2",
     parentMenu: menu,
   });
 
-  test.assertEqual(menu.items[1], item, "Item should be in the sub menu");
+  assert.equal(menu.items[1], item, "Item should be in the sub menu");
 
   test.showMenu(null, function (popup) {
     test.checkMenu([menu], [], []);
     test.done();
   });
 };
 
 
 // Existing context menu modifications should apply to new windows.
-exports.testNewWindow = function (test) {
-  test = new TestHelper(test);
+exports.testNewWindow = function (assert, done) {
+  let test = new TestHelper(assert, done);
   let loader = test.newLoader();
 
   let item = new loader.cm.Item({ label: "item" });
 
   test.withNewWindow(function () {
     test.showMenu(null, function (popup) {
       test.checkMenu([item], [], []);
       test.done();
     });
   });
 };
 
 
 // When a new window is opened, items added by an unloaded module should not
 // be present in the menu.
-exports.testNewWindowMultipleModules = function (test) {
-  test = new TestHelper(test);
+exports.testNewWindowMultipleModules = function (assert, done) {
+  let test = new TestHelper(assert, done);
   let loader = test.newLoader();
   let item = new loader.cm.Item({ label: "item" });
 
   test.showMenu(null, function (popup) {
     test.checkMenu([item], [], []);
     popup.hidePopup();
     loader.unload();
     test.withNewWindow(function () {
@@ -1979,18 +1979,18 @@ exports.testNewWindowMultipleModules = f
         test.done();
       });
     });
   });
 };
 
 
 // Existing context menu modifications should not apply to new private windows.
-exports.testNewPrivateWindow = function (test) {
-  test = new TestHelper(test);
+exports.testNewPrivateWindow = function (assert, done) {
+  let test = new TestHelper(assert, done);
   let loader = test.newLoader();
 
   let item = new loader.cm.Item({ label: "item" });
 
   test.showMenu(null, function (popup) {
     test.checkMenu([item], [], []);
     popup.hidePopup();
 
@@ -2001,18 +2001,18 @@ exports.testNewPrivateWindow = function 
       });
     });
   });
 };
 
 
 // Existing context menu modifications should apply to new private windows when
 // private browsing support is enabled.
-exports.testNewPrivateEnabledWindow = function (test) {
-  test = new TestHelper(test);
+exports.testNewPrivateEnabledWindow = function (assert, done) {
+  let test = new TestHelper(assert, done);
   let loader = test.newPrivateLoader();
 
   let item = new loader.cm.Item({ label: "item" });
 
   test.showMenu(null, function (popup) {
     test.checkMenu([item], [], []);
     popup.hidePopup();
 
@@ -2023,18 +2023,18 @@ exports.testNewPrivateEnabledWindow = fu
       });
     });
   });
 };
 
 
 // Existing context menu modifications should apply to new private windows when
 // private browsing support is enabled unless unloaded.
-exports.testNewPrivateEnabledWindowUnloaded = function (test) {
-  test = new TestHelper(test);
+exports.testNewPrivateEnabledWindowUnloaded = function (assert, done) {
+  let test = new TestHelper(assert, done);
   let loader = test.newPrivateLoader();
 
   let item = new loader.cm.Item({ label: "item" });
 
   test.showMenu(null, function (popup) {
     test.checkMenu([item], [], []);
     popup.hidePopup();
 
@@ -2046,18 +2046,18 @@ exports.testNewPrivateEnabledWindowUnloa
         test.done();
       });
     });
   });
 };
 
 
 // Items in the context menu should be sorted according to locale.
-exports.testSorting = function (test) {
-  test = new TestHelper(test);
+exports.testSorting = function (assert, done) {
+  let test = new TestHelper(assert, done);
   let loader = test.newLoader();
 
   // Make an unsorted items list.  It'll look like this:
   //   item 1, item 0, item 3, item 2, item 5, item 4, ...
   let items = [];
   for (let i = 0; i < OVERFLOW_THRESH_DEFAULT; i += 2) {
     items.push(new loader.cm.Item({ label: "item " + (i + 1) }));
     items.push(new loader.cm.Item({ label: "item " + i }));
@@ -2066,18 +2066,18 @@ exports.testSorting = function (test) {
   test.showMenu(null, function (popup) {
     test.checkMenu(items, [], []);
     test.done();
   });
 };
 
 
 // Items in the overflow menu should be sorted according to locale.
-exports.testSortingOverflow = function (test) {
-  test = new TestHelper(test);
+exports.testSortingOverflow = function (assert, done) {
+  let test = new TestHelper(assert, done);
   let loader = test.newLoader();
 
   // Make an unsorted items list.  It'll look like this:
   //   item 1, item 0, item 3, item 2, item 5, item 4, ...
   let items = [];
   for (let i = 0; i < OVERFLOW_THRESH_DEFAULT * 2; i += 2) {
     items.push(new loader.cm.Item({ label: "item " + (i + 1) }));
     items.push(new loader.cm.Item({ label: "item " + i }));
@@ -2086,18 +2086,18 @@ exports.testSortingOverflow = function (
   test.showMenu(null, function (popup) {
     test.checkMenu(items, [], []);
     test.done();
   });
 };
 
 
 // Multiple modules shouldn't interfere with sorting.
-exports.testSortingMultipleModules = function (test) {
-  test = new TestHelper(test);
+exports.testSortingMultipleModules = function (assert, done) {
+  let test = new TestHelper(assert, done);
   let loader0 = test.newLoader();
   let loader1 = test.newLoader();
 
   let items0 = [];
   let items1 = [];
   for (let i = 0; i < OVERFLOW_THRESH_DEFAULT; i++) {
     if (i % 2) {
       let item = new loader0.cm.Item({ label: "item " + i });
@@ -2124,50 +2124,50 @@ exports.testSortingMultipleModules = fun
       test.done();
     });
   });
 };
 
 
 // Content click handlers and context handlers should be able to communicate,
 // i.e., they're eval'ed in the same worker and sandbox.
-exports.testContentCommunication = function (test) {
-  test = new TestHelper(test);
+exports.testContentCommunication = function (assert, done) {
+  let test = new TestHelper(assert, done);
   let loader = test.newLoader();
 
   let item = new loader.cm.Item({
     label: "item",
     contentScript: 'var potato;' +
                    'self.on("context", function () {' +
                    '  potato = "potato";' +
                    '  return true;' +
                    '});' +
                    'self.on("click", function () {' +
                    '  self.postMessage(potato);' +
                    '});',
   });
 
   item.on("message", function (data) {
-    test.assertEqual(data, "potato", "That's a lot of potatoes!");
+    assert.equal(data, "potato", "That's a lot of potatoes!");
     test.done();
   });
 
   test.showMenu(null, function (popup) {
     test.checkMenu([item], [], []);
     let elt = test.getItemElt(popup, item);
     elt.click();
   });
 };
 
 
 // When the context menu is invoked on a tab that was already open when the
 // module was loaded, it should contain the expected items and content workers
 // should function as expected.
-exports.testLoadWithOpenTab = function (test) {
-  test = new TestHelper(test);
+exports.testLoadWithOpenTab = function (assert, done) {
+  let test = new TestHelper(assert, done);
   test.withTestDoc(function (window, doc) {
     let loader = test.newLoader();
     let item = new loader.cm.Item({
       label: "item",
       contentScript:
         'self.on("click", function () self.postMessage("click"));',
       onMessage: function (msg) {
         if (msg === "click")
@@ -2178,18 +2178,18 @@ exports.testLoadWithOpenTab = function (
       test.checkMenu([item], [], []);
       test.getItemElt(popup, item).click();
     });
   });
 };
 
 // Bug 732716: Ensure that the node given in `click` event works fine
 // (i.e. is correctly wrapped)
-exports.testDrawImageOnClickNode = function (test) {
-  test = new TestHelper(test);
+exports.testDrawImageOnClickNode = function (assert, done) {
+  let test = new TestHelper(assert, done);
   test.withTestDoc(function (window, doc) {
     let loader = test.newLoader();
     let item = new loader.cm.Item({
       label: "item",
       context: loader.cm.SelectorContext("img"),
       contentScript: "new " + function() {
         self.on("click", function (img, data) {
           let ctx = document.createElement("canvas").getContext("2d");
@@ -2207,398 +2207,398 @@ exports.testDrawImageOnClickNode = funct
       test.getItemElt(popup, item).click();
     });
   });
 };
 
 
 // Setting an item's label before the menu is ever shown should correctly change
 // its label.
-exports.testSetLabelBeforeShow = function (test) {
-  test = new TestHelper(test);
+exports.testSetLabelBeforeShow = function (assert, done) {
+  let test = new TestHelper(assert, done);
   let loader = test.newLoader();
 
   let items = [
     new loader.cm.Item({ label: "a" }),
     new loader.cm.Item({ label: "b" })
   ]
   items[0].label = "z";
-  test.assertEqual(items[0].label, "z");
+  assert.equal(items[0].label, "z");
 
   test.showMenu(null, function (popup) {
     test.checkMenu(items, [], []);
     test.done();
   });
 };
 
 
 // Setting an item's label after the menu is shown should correctly change its
 // label.
-exports.testSetLabelAfterShow = function (test) {
-  test = new TestHelper(test);
+exports.testSetLabelAfterShow = function (assert, done) {
+  let test = new TestHelper(assert, done);
   let loader = test.newLoader();
 
   let items = [
     new loader.cm.Item({ label: "a" }),
     new loader.cm.Item({ label: "b" })
   ];
 
   test.showMenu(null, function (popup) {
     test.checkMenu(items, [], []);
     popup.hidePopup();
 
     items[0].label = "z";
-    test.assertEqual(items[0].label, "z");
+    assert.equal(items[0].label, "z");
     test.showMenu(null, function (popup) {
       test.checkMenu(items, [], []);
       test.done();
     });
   });
 };
 
 
 // Setting an item's label before the menu is ever shown should correctly change
 // its label.
-exports.testSetLabelBeforeShowOverflow = function (test) {
-  test = new TestHelper(test);
+exports.testSetLabelBeforeShowOverflow = function (assert, done) {
+  let test = new TestHelper(assert, done);
   let loader = test.newLoader();
 
   let prefs = loader.loader.require("sdk/preferences/service");
   prefs.set(OVERFLOW_THRESH_PREF, 0);
 
   let items = [
     new loader.cm.Item({ label: "a" }),
     new loader.cm.Item({ label: "b" })
   ]
   items[0].label = "z";
-  test.assertEqual(items[0].label, "z");
+  assert.equal(items[0].label, "z");
 
   test.showMenu(null, function (popup) {
     test.checkMenu(items, [], []);
     test.done();
   });
 };
 
 
 // Setting an item's label after the menu is shown should correctly change its
 // label.
-exports.testSetLabelAfterShowOverflow = function (test) {
-  test = new TestHelper(test);
+exports.testSetLabelAfterShowOverflow = function (assert, done) {
+  let test = new TestHelper(assert, done);
   let loader = test.newLoader();
 
   let prefs = loader.loader.require("sdk/preferences/service");
   prefs.set(OVERFLOW_THRESH_PREF, 0);
 
   let items = [
     new loader.cm.Item({ label: "a" }),
     new loader.cm.Item({ label: "b" })
   ];
 
   test.showMenu(null, function (popup) {
     test.checkMenu(items, [], []);
     popup.hidePopup();
 
     items[0].label = "z";
-    test.assertEqual(items[0].label, "z");
+    assert.equal(items[0].label, "z");
     test.showMenu(null, function (popup) {
       test.checkMenu(items, [], []);
       test.done();
     });
   });
 };
 
 
 // Setting the label of an item in a Menu should work.
-exports.testSetLabelMenuItem = function (test) {
-  test = new TestHelper(test);
+exports.testSetLabelMenuItem = function (assert, done) {
+  let test = new TestHelper(assert, done);
   let loader = test.newLoader();
 
   let menu = loader.cm.Menu({
     label: "menu",
     items: [loader.cm.Item({ label: "a" })]
   });
   menu.items[0].label = "z";
 
-  test.assertEqual(menu.items[0].label, "z");
+  assert.equal(menu.items[0].label, "z");
 
   test.showMenu(null, function (popup) {
     test.checkMenu([menu], [], []);
     test.done();
   });
 };
 
 
 // Menu.addItem() should work.
-exports.testMenuAddItem = function (test) {
-  test = new TestHelper(test);
+exports.testMenuAddItem = function (assert, done) {
+  let test = new TestHelper(assert, done);
   let loader = test.newLoader();
 
   let menu = loader.cm.Menu({
     label: "menu",
     items: [
       loader.cm.Item({ label: "item 0" })
     ]
   });
   menu.addItem(loader.cm.Item({ label: "item 1" }));
   menu.addItem(loader.cm.Item({ label: "item 2" }));
 
-  test.assertEqual(menu.items.length, 3,
+  assert.equal(menu.items.length, 3,
                    "menu should have correct number of items");
   for (let i = 0; i < 3; i++) {
-    test.assertEqual(menu.items[i].label, "item " + i,
+    assert.equal(menu.items[i].label, "item " + i,
                      "item label should be correct");
-    test.assertEqual(menu.items[i].parentMenu, menu,
+    assert.equal(menu.items[i].parentMenu, menu,
                      "item's parent menu should be correct");
   }
 
   test.showMenu(null, function (popup) {
     test.checkMenu([menu], [], []);
     test.done();
   });
 };
 
 
 // Adding the same item twice to a menu should work as expected.
-exports.testMenuAddItemTwice = function (test) {
-  test = new TestHelper(test);
+exports.testMenuAddItemTwice = function (assert, done) {
+  let test = new TestHelper(assert, done);
   let loader = test.newLoader();
 
   let menu = loader.cm.Menu({
     label: "menu",
     items: []
   });
   let subitem = loader.cm.Item({ label: "item 1" })
   menu.addItem(subitem);
   menu.addItem(loader.cm.Item({ label: "item 0" }));
   menu.addItem(subitem);
 
-  test.assertEqual(menu.items.length, 2,
+  assert.equal(menu.items.length, 2,
                    "menu should have correct number of items");
   for (let i = 0; i < 2; i++) {
-    test.assertEqual(menu.items[i].label, "item " + i,
+    assert.equal(menu.items[i].label, "item " + i,
                      "item label should be correct");
   }
 
   test.showMenu(null, function (popup) {
     test.checkMenu([menu], [], []);
     test.done();
   });
 };
 
 
 // Menu.removeItem() should work.
-exports.testMenuRemoveItem = function (test) {
-  test = new TestHelper(test);
+exports.testMenuRemoveItem = function (assert, done) {
+  let test = new TestHelper(assert, done);
   let loader = test.newLoader();
 
   let subitem = loader.cm.Item({ label: "item 1" });
   let menu = loader.cm.Menu({
     label: "menu",
     items: [
       loader.cm.Item({ label: "item 0" }),
       subitem,
       loader.cm.Item({ label: "item 2" })
     ]
   });
 
   // Removing twice should be harmless.
   menu.removeItem(subitem);
   menu.removeItem(subitem);
 
-  test.assertEqual(subitem.parentMenu, null,
+  assert.equal(subitem.parentMenu, null,
                    "item's parent menu should be correct");
 
-  test.assertEqual(menu.items.length, 2,
+  assert.equal(menu.items.length, 2,
                    "menu should have correct number of items");
-  test.assertEqual(menu.items[0].label, "item 0",
+  assert.equal(menu.items[0].label, "item 0",
                    "item label should be correct");
-  test.assertEqual(menu.items[1].label, "item 2",
+  assert.equal(menu.items[1].label, "item 2",
                    "item label should be correct");
 
   test.showMenu(null, function (popup) {
     test.checkMenu([menu], [], []);
     test.done();
   });
 };
 
 
 // Adding an item currently contained in one menu to another menu should work.
-exports.testMenuItemSwap = function (test) {
-  test = new TestHelper(test);
+exports.testMenuItemSwap = function (assert, done) {
+  let test = new TestHelper(assert, done);
   let loader = test.newLoader();
 
   let subitem = loader.cm.Item({ label: "item" });
   let menu0 = loader.cm.Menu({
     label: "menu 0",
     items: [subitem]
   });
   let menu1 = loader.cm.Menu({
     label: "menu 1",
     items: []
   });
   menu1.addItem(subitem);
 
-  test.assertEqual(menu0.items.length, 0,
+  assert.equal(menu0.items.length, 0,
                    "menu should have correct number of items");
 
-  test.assertEqual(menu1.items.length, 1,
+  assert.equal(menu1.items.length, 1,
                    "menu should have correct number of items");
-  test.assertEqual(menu1.items[0].label, "item",
+  assert.equal(menu1.items[0].label, "item",
                    "item label should be correct");
 
-  test.assertEqual(subitem.parentMenu, menu1,
+  assert.equal(subitem.parentMenu, menu1,
                    "item's parent menu should be correct");
 
   test.showMenu(null, function (popup) {
     test.checkMenu([menu0, menu1], [menu0], []);
     test.done();
   });
 };
 
 
 // Destroying an item should remove it from its parent menu.
-exports.testMenuItemDestroy = function (test) {
-  test = new TestHelper(test);
+exports.testMenuItemDestroy = function (assert, done) {
+  let test = new TestHelper(assert, done);
   let loader = test.newLoader();
 
   let subitem = loader.cm.Item({ label: "item" });
   let menu = loader.cm.Menu({
     label: "menu",
     items: [subitem]
   });
   subitem.destroy();
 
-  test.assertEqual(menu.items.length, 0,
+  assert.equal(menu.items.length, 0,
                    "menu should have correct number of items");
-  test.assertEqual(subitem.parentMenu, null,
+  assert.equal(subitem.parentMenu, null,
                    "item's parent menu should be correct");
 
   test.showMenu(null, function (popup) {
     test.checkMenu([menu], [menu], []);
     test.done();
   });
 };
 
 
 // Setting Menu.items should work.
-exports.testMenuItemsSetter = function (test) {
-  test = new TestHelper(test);
+exports.testMenuItemsSetter = function (assert, done) {
+  let test = new TestHelper(assert, done);
   let loader = test.newLoader();
 
   let menu = loader.cm.Menu({
     label: "menu",
     items: [
       loader.cm.Item({ label: "old item 0" }),
       loader.cm.Item({ label: "old item 1" })
     ]
   });
   menu.items = [
     loader.cm.Item({ label: "new item 0" }),
     loader.cm.Item({ label: "new item 1" }),
     loader.cm.Item({ label: "new item 2" })
   ];
 
-  test.assertEqual(menu.items.length, 3,
+  assert.equal(menu.items.length, 3,
                    "menu should have correct number of items");
   for (let i = 0; i < 3; i++) {
-    test.assertEqual(menu.items[i].label, "new item " + i,
+    assert.equal(menu.items[i].label, "new item " + i,
                      "item label should be correct");
-    test.assertEqual(menu.items[i].parentMenu, menu,
+    assert.equal(menu.items[i].parentMenu, menu,
                      "item's parent menu should be correct");
   }
 
   test.showMenu(null, function (popup) {
     test.checkMenu([menu], [], []);
     test.done();
   });
 };
 
 
 // Setting Item.data should work.
-exports.testItemDataSetter = function (test) {
-  test = new TestHelper(test);
+exports.testItemDataSetter = function (assert, done) {
+  let test = new TestHelper(assert, done);
   let loader = test.newLoader();
 
   let item = loader.cm.Item({ label: "old item 0", data: "old" });
   item.data = "new";
 
-  test.assertEqual(item.data, "new", "item should have correct data");
+  assert.equal(item.data, "new", "item should have correct data");
 
   test.showMenu(null, function (popup) {
     test.checkMenu([item], [], []);
     test.done();
   });
 };
 
 
 // Open the test doc, load the module, make sure items appear when context-
 // clicking the iframe.
-exports.testAlreadyOpenIframe = function (test) {
-  test = new TestHelper(test);
+exports.testAlreadyOpenIframe = function (assert, done) {
+  let test = new TestHelper(assert, done);
   test.withTestDoc(function (window, doc) {
     let loader = test.newLoader();
     let item = new loader.cm.Item({
       label: "item"
     });
     test.showMenu(doc.getElementById("iframe"), function (popup) {
       test.checkMenu([item], [], []);
       test.done();
     });
   });
 };
 
 
 // Tests that a missing label throws an exception
-exports.testItemNoLabel = function (test) {
-  test = new TestHelper(test);
+exports.testItemNoLabel = function (assert, done) {
+  let test = new TestHelper(assert, done);
   let loader = test.newLoader();
 
   try {
     new loader.cm.Item({});
-    test.assert(false, "Should have seen exception");
+    assert.ok(false, "Should have seen exception");
   }
   catch (e) {
-    test.assert(true, "Should have seen exception");
+    assert.ok(true, "Should have seen exception");
   }
 
   try {
     new loader.cm.Item({ label: null });
-    test.assert(false, "Should have seen exception");
+    assert.ok(false, "Should have seen exception");
   }
   catch (e) {
-    test.assert(true, "Should have seen exception");
+    assert.ok(true, "Should have seen exception");
   }
 
   try {
     new loader.cm.Item({ label: undefined });
-    test.assert(false, "Should have seen exception");
+    assert.ok(false, "Should have seen exception");
   }
   catch (e) {
-    test.assert(true, "Should have seen exception");
+    assert.ok(true, "Should have seen exception");
   }
 
   try {
     new loader.cm.Item({ label: "" });
-    test.assert(false, "Should have seen exception");
+    assert.ok(false, "Should have seen exception");
   }
   catch (e) {
-    test.assert(true, "Should have seen exception");
+    assert.ok(true, "Should have seen exception");
   }
 
   test.done();
 }
 
 
 // Tests that items can have an empty data property
-exports.testItemNoData = function (test) {
-  test = new TestHelper(test);
+exports.testItemNoData = function (assert, done) {
+  let test = new TestHelper(assert, done);
   let loader = test.newLoader();
 
   function checkData(data) {
-    test.assertEqual(data, undefined, "Data should be undefined");
+    assert.equal(data, undefined, "Data should be undefined");
   }
 
   let item1 = new loader.cm.Item({
     label: "item 1",
     contentScript: 'self.on("click", function(node, data) self.postMessage(data))',
     onMessage: checkData
   });
   let item2 = new loader.cm.Item({
@@ -2609,19 +2609,19 @@ exports.testItemNoData = function (test)
   });
   let item3 = new loader.cm.Item({
     label: "item 3",
     data: undefined,
     contentScript: 'self.on("click", function(node, data) self.postMessage(data))',
     onMessage: checkData
   });
 
-  test.assertEqual(item1.data, undefined, "Should be no defined data");
-  test.assertEqual(item2.data, null, "Should be no defined data");
-  test.assertEqual(item3.data, undefined, "Should be no defined data");
+  assert.equal(item1.data, undefined, "Should be no defined data");
+  assert.equal(item2.data, null, "Should be no defined data");
+  assert.equal(item3.data, undefined, "Should be no defined data");
 
   test.showMenu(null, function (popup) {
     test.checkMenu([item1, item2, item3], [], []);
 
     let itemElt = test.getItemElt(popup, item1);
     itemElt.click();
 
     test.hideMenu(function() {
@@ -2639,114 +2639,114 @@ exports.testItemNoData = function (test)
         });
       });
     });
   });
 }
 
 
 // Tests that items without an image don't attempt to show one
-exports.testItemNoImage = function (test) {
-  test = new TestHelper(test);
+exports.testItemNoImage = function (assert, done) {
+  let test = new TestHelper(assert, done);
   let loader = test.newLoader();
 
   let item1 = new loader.cm.Item({ label: "item 1" });
   let item2 = new loader.cm.Item({ label: "item 2", image: null });
   let item3 = new loader.cm.Item({ label: "item 3", image: undefined });
 
-  test.assertEqual(item1.image, undefined, "Should be no defined image");
-  test.assertEqual(item2.image, null, "Should be no defined image");
-  test.assertEqual(item3.image, undefined, "Should be no defined image");
+  assert.equal(item1.image, undefined, "Should be no defined image");
+  assert.equal(item2.image, null, "Should be no defined image");
+  assert.equal(item3.image, undefined, "Should be no defined image");
 
   test.showMenu(null, function (popup) {
     test.checkMenu([item1, item2, item3], [], []);
 
     test.done();
   });
 }
 
 
 // Test image support.
-exports.testItemImage = function (test) {
-  test = new TestHelper(test);
+exports.testItemImage = function (assert, done) {
+  let test = new TestHelper(assert, done);
   let loader = test.newLoader();
 
   let imageURL = require("sdk/self").data.url("moz_favicon.ico");
   let item = new loader.cm.Item({ label: "item", image: imageURL });
   let menu = new loader.cm.Menu({ label: "menu", image: imageURL, items: [
     loader.cm.Item({ label: "subitem" })
   ]});
-  test.assertEqual(item.image, imageURL, "Should have set the image correctly");
-  test.assertEqual(menu.image, imageURL, "Should have set the image correctly");
+  assert.equal(item.image, imageURL, "Should have set the image correctly");
+  assert.equal(menu.image, imageURL, "Should have set the image correctly");
 
   test.showMenu(null, function (popup) {
     test.checkMenu([item, menu], [], []);
 
     let imageURL2 = require("sdk/self").data.url("dummy.ico");
     item.image = imageURL2;
     menu.image = imageURL2;
-    test.assertEqual(item.image, imageURL2, "Should have set the image correctly");
-    test.assertEqual(menu.image, imageURL2, "Should have set the image correctly");
+    assert.equal(item.image, imageURL2, "Should have set the image correctly");
+    assert.equal(menu.image, imageURL2, "Should have set the image correctly");
     test.checkMenu([item, menu], [], []);
 
     item.image = null;
     menu.image = null;
-    test.assertEqual(item.image, null, "Should have set the image correctly");
-    test.assertEqual(menu.image, null, "Should have set the image correctly");
+    assert.equal(item.image, null, "Should have set the image correctly");
+    assert.equal(menu.image, null, "Should have set the image correctly");
     test.checkMenu([item, menu], [], []);
 
     test.done();
   });
 };
 
 // Test image URL validation.
-exports.testItemImageValidURL = function (test) {
-  test = new TestHelper(test);
+exports.testItemImageValidURL = function (assert, done) {
+  let test = new TestHelper(assert, done);
   let loader = test.newLoader();
  
-  test.assertRaises(function(){
+  assert.throws(function(){
       new loader.cm.Item({
         label: "item 1",
         image: "foo"
       })
-    }, "Image URL validation failed"
+    }, /Image URL validation failed/
   );
 
-  test.assertRaises(function(){
+  assert.throws(function(){
       new loader.cm.Item({
         label: "item 2",
         image: false
       })
-    }, "Image URL validation failed"
+    }, /Image URL validation failed/
   );
 
-  test.assertRaises(function(){
+  assert.throws(function(){
       new loader.cm.Item({
         label: "item 3",
         image: 0
       })
-    }, "Image URL validation failed"
+    }, /Image URL validation failed/
   );
    
   let imageURL = require("sdk/self").data.url("moz_favicon.ico");
   let item4 = new loader.cm.Item({ label: "item 4", image: imageURL });
   let item5 = new loader.cm.Item({ label: "item 5", image: null });
   let item6 = new loader.cm.Item({ label: "item 6", image: undefined });
 
-  test.assertEqual(item4.image, imageURL, "Should be proper image URL");
-  test.assertEqual(item5.image, null, "Should be null image");
-  test.assertEqual(item6.image, undefined, "Should be undefined image");
+  assert.equal(item4.image, imageURL, "Should be proper image URL");
+  assert.equal(item5.image, null, "Should be null image");
+  assert.equal(item6.image, undefined, "Should be undefined image");
 
   test.done();
-}
+};
 
 
 // Menu.destroy should destroy the item tree rooted at that menu.
-exports.testMenuDestroy = function (test) {
-  test = new TestHelper(test);
+exports.testMenuDestroy = function (assert, done) {
+  let test = new TestHelper(assert, done);
   let loader = test.newLoader();
 
   let menu = loader.cm.Menu({
     label: "menu",
     items: [
       loader.cm.Item({ label: "item 0" }),
       loader.cm.Menu({
         label: "item 1",
@@ -2761,28 +2761,28 @@ exports.testMenuDestroy = function (test
   });
   menu.destroy();
 
   /*let numRegistryEntries = 0;
   loader.globalScope.browserManager.browserWins.forEach(function (bwin) {
     for (let itemID in bwin.items)
       numRegistryEntries++;
   });
-  test.assertEqual(numRegistryEntries, 0, "All items should be unregistered.");*/
+  assert.equal(numRegistryEntries, 0, "All items should be unregistered.");*/
 
   test.showMenu(null, function (popup) {
     test.checkMenu([menu], [], [menu]);
     test.done();
   });
 };
 
 // Checks that if a menu contains sub items that are hidden then the menu is
 // hidden too. Also checks that content scripts and contexts work for sub items.
-exports.testSubItemContextNoMatchHideMenu = function (test) {
-  test = new TestHelper(test);
+exports.testSubItemContextNoMatchHideMenu = function (assert, done) {
+  let test = new TestHelper(assert, done);
   let loader = test.newLoader();
 
   let items = [
     loader.cm.Menu({
       label: "menu 1",
       items: [
         loader.cm.Item({
           label: "subitem 1",
@@ -2818,18 +2818,18 @@ exports.testSubItemContextNoMatchHideMen
     test.checkMenu(items, items, []);
     test.done();
   });
 };
 
 
 // Checks that if a menu contains a combination of hidden and visible sub items
 // then the menu is still visible too.
-exports.testSubItemContextMatch = function (test) {
-  test = new TestHelper(test);
+exports.testSubItemContextMatch = function (assert, done) {
+  let test = new TestHelper(assert, done);
   let loader = test.newLoader();
 
   let hiddenItems = [
     loader.cm.Item({
       label: "subitem 3",
       context: loader.cm.SelectorContext(".foo")
     }),
     loader.cm.Item({
@@ -2897,18 +2897,18 @@ exports.testSubItemContextMatch = functi
       test.checkMenu(items, hiddenItems, []);
       test.done();
     });
   });
 };
 
 
 // Child items should default to visible, not to PageContext
-exports.testSubItemDefaultVisible = function (test) {
-  test = new TestHelper(test);
+exports.testSubItemDefaultVisible = function (assert, done) {
+  let test = new TestHelper(assert, done);
   let loader = test.newLoader();
 
   let items = [
     loader.cm.Menu({
       label: "menu 1",
       context: loader.cm.SelectorContext("img"),
       items: [
         loader.cm.Item({
@@ -2934,18 +2934,18 @@ exports.testSubItemDefaultVisible = func
       test.checkMenu(items, hiddenItems, []);
       test.done();
     });
   });
 };
 
 // Tests that the click event on sub menuitem
 // tiggers the click event for the sub menuitem and the parent menu
-exports.testSubItemClick = function (test) {
-  test = new TestHelper(test);
+exports.testSubItemClick = function (assert, done) {
+  let test = new TestHelper(assert, done);
   let loader = test.newLoader();
 
   let state = 0;
 
   let items = [
     loader.cm.Menu({
       label: "menu 1",
       items: [
@@ -2954,33 +2954,33 @@ exports.testSubItemClick = function (tes
           data: "foobar",
           contentScript: 'self.on("click", function (node, data) {' +
                          '  self.postMessage({' +
                          '    tagName: node.tagName,' +
                          '    data: data' +
                          '  });' +
                          '});',
           onMessage: function(msg) {
-            test.assertEqual(msg.tagName, "HTML", "should have seen the right node");
-            test.assertEqual(msg.data, "foobar", "should have seen the right data");
-            test.assertEqual(state, 0, "should have seen the event at the right time");
+            assert.equal(msg.tagName, "HTML", "should have seen the right node");
+            assert.equal(msg.data, "foobar", "should have seen the right data");
+            assert.equal(state, 0, "should have seen the event at the right time");
             state++;
           }
         })
       ],
       contentScript: 'self.on("click", function (node, data) {' +
                      '  self.postMessage({' +
                      '    tagName: node.tagName,' +
                      '    data: data' +
                      '  });' +
                      '});',
       onMessage: function(msg) {
-        test.assertEqual(msg.tagName, "HTML", "should have seen the right node");
-        test.assertEqual(msg.data, "foobar", "should have seen the right data");
-        test.assertEqual(state, 1, "should have seen the event at the right time");
+        assert.equal(msg.tagName, "HTML", "should have seen the right node");
+        assert.equal(msg.data, "foobar", "should have seen the right data");
+        assert.equal(state, 1, "should have seen the event at the right time");
 
         test.done();
       }
     })
   ];
 
   test.withTestDoc(function (window, doc) {
     test.showMenu(null, function (popup) {
@@ -2991,18 +2991,18 @@ exports.testSubItemClick = function (tes
       let itemElt = test.getItemElt(topMenuPopup, items[0].items[0]);
       itemElt.click();
     });
   });
 };
 
 // Tests that the command event on sub menuitem
 // tiggers the click event for the sub menuitem and the parent menu
-exports.testSubItemCommand = function (test) {
-  test = new TestHelper(test);
+exports.testSubItemCommand = function (assert, done) {
+  let test = new TestHelper(assert, done);
   let loader = test.newLoader();
 
   let state = 0;
 
   let items = [
     loader.cm.Menu({
       label: "menu 1",
       items: [
@@ -3011,33 +3011,33 @@ exports.testSubItemCommand = function (t
           data: "foobar",
           contentScript: 'self.on("click", function (node, data) {' +
                          '  self.postMessage({' +
                          '    tagName: node.tagName,' +
                          '    data: data' +
                          '  });' +
                          '});',
           onMessage: function(msg) {
-            test.assertEqual(msg.tagName, "HTML", "should have seen the right node");
-            test.assertEqual(msg.data, "foobar", "should have seen the right data");
-            test.assertEqual(state, 0, "should have seen the event at the right time");
+            assert.equal(msg.tagName, "HTML", "should have seen the right node");
+            assert.equal(msg.data, "foobar", "should have seen the right data");
+            assert.equal(state, 0, "should have seen the event at the right time");
             state++;
           }
         })
       ],
       contentScript: 'self.on("click", function (node, data) {' +
                      '  self.postMessage({' +
                      '    tagName: node.tagName,' +
                      '    data: data' +
                      '  });' +
                      '});',
       onMessage: function(msg) {
-        test.assertEqual(msg.tagName, "HTML", "should have seen the right node");
-        test.assertEqual(msg.data, "foobar", "should have seen the right data");
-        test.assertEqual(state, 1, "should have seen the event at the right time");
+        assert.equal(msg.tagName, "HTML", "should have seen the right node");
+        assert.equal(msg.data, "foobar", "should have seen the right data");
+        assert.equal(state, 1, "should have seen the event at the right time");
         state++
 
         test.done();
       }
     })
   ];
 
   test.withTestDoc(function (window, doc) {
@@ -3053,18 +3053,18 @@ exports.testSubItemCommand = function (t
       evt.initEvent('command', true, true);
       itemElt.dispatchEvent(evt);
     });
   });
 };
 
 // Tests that opening a context menu for an outer frame when an inner frame
 // has a selection doesn't activate the SelectionContext
-exports.testSelectionInInnerFrameNoMatch = function (test) {
-  test = new TestHelper(test);
+exports.testSelectionInInnerFrameNoMatch = function (assert, done) {
+  let test = new TestHelper(assert, done);
   let loader = test.newLoader();
 
   let state = 0;
 
   let items = [
     loader.cm.Item({
       label: "test item",
       context: loader.cm.SelectionContext()
@@ -3079,18 +3079,18 @@ exports.testSelectionInInnerFrameNoMatch
       test.checkMenu(items, items, []);
       test.done();
     });
   });
 };
 
 // Tests that opening a context menu for an inner frame when the inner frame
 // has a selection does activate the SelectionContext
-exports.testSelectionInInnerFrameMatch = function (test) {
-  test = new TestHelper(test);
+exports.testSelectionInInnerFrameMatch = function (assert, done) {
+  let test = new TestHelper(assert, done);
   let loader = test.newLoader();
 
   let state = 0;
 
   let items = [
     loader.cm.Item({
       label: "test item",
       context: loader.cm.SelectionContext()
@@ -3105,18 +3105,18 @@ exports.testSelectionInInnerFrameMatch =
       test.checkMenu(items, [], []);
       test.done();
     });
   });
 };
 
 // Tests that opening a context menu for an inner frame when the outer frame
 // has a selection doesn't activate the SelectionContext
-exports.testSelectionInOuterFrameNoMatch = function (test) {
-  test = new TestHelper(test);
+exports.testSelectionInOuterFrameNoMatch = function (assert, done) {
+  let test = new TestHelper(assert, done);
   let loader = test.newLoader();
 
   let state = 0;
 
   let items = [
     loader.cm.Item({
       label: "test item",
       context: loader.cm.SelectionContext()
@@ -3136,19 +3136,19 @@ exports.testSelectionInOuterFrameNoMatch
 
 // NO TESTS BELOW THIS LINE! ///////////////////////////////////////////////////
 
 // This makes it easier to run tests by handling things like opening the menu,
 // opening new windows, making assertions, etc.  Methods on |test| can be called
 // on instances of this class.  Don't forget to call done() to end the test!
 // WARNING: This looks up items in popups by comparing labels, so don't give two
 // items the same label.
-function TestHelper(test) {
-  test.waitUntilDone();
-  this.test = test;
+function TestHelper(assert, done) {
+  this.assert = assert;
+  this.end = done;
   this.loaders = [];
   this.browserWindow = Cc["@mozilla.org/appshell/window-mediator;1"].
                        getService(Ci.nsIWindowMediator).
                        getMostRecentWindow("navigator:browser");
   this.overflowThreshValue = require("sdk/preferences/service").
                              get(OVERFLOW_THRESH_PREF, OVERFLOW_THRESH_DEFAULT);
 }
 
@@ -3170,63 +3170,63 @@ TestHelper.prototype = {
   },
 
   get tabBrowser() {
     return this.browserWindow.gBrowser;
   },
 
   // Methods on the wrapped test can be called on this object.
   __noSuchMethod__: function (methodName, args) {
-    this.test[methodName].apply(this.test, args);
+    this.assert[methodName].apply(this.assert, args);
   },
 
   // Asserts that elt, a DOM element representing item, looks OK.
   checkItemElt: function (elt, item) {
     let itemType = this.getItemType(item);
 
     switch (itemType) {
     case "Item":
-      this.test.assertEqual(elt.localName, "menuitem",
+      this.assert.equal(elt.localName, "menuitem",
                             "Item DOM element should be a xul:menuitem");
       if (typeof(item.data) === "string") {
-        this.test.assertEqual(elt.getAttribute("value"), item.data,
+        this.assert.equal(elt.getAttribute("value"), item.data,
                               "Item should have correct data");
       }
       break
     case "Menu":
-      this.test.assertEqual(elt.localName, "menu",
+      this.assert.equal(elt.localName, "menu",
                             "Menu DOM element should be a xul:menu");
       let subPopup = elt.firstChild;
-      this.test.assert(subPopup, "xul:menu should have a child");
-      this.test.assertEqual(subPopup.localName, "menupopup",
+      this.assert.ok(subPopup, "xul:menu should have a child");
+      this.assert.equal(subPopup.localName, "menupopup",
                             "xul:menu's first child should be a menupopup");
       break;
     case "Separator":
-      this.test.assertEqual(elt.localName, "menuseparator",
+      this.assert.equal(elt.localName, "menuseparator",
                          "Separator DOM element should be a xul:menuseparator");
       break;
     }
 
     if (itemType === "Item" || itemType === "Menu") {
-      this.test.assertEqual(elt.getAttribute("label"), item.label,
+      this.assert.equal(elt.getAttribute("label"), item.label,
                             "Item should have correct title");
       if (typeof(item.image) === "string") {
-        this.test.assertEqual(elt.getAttribute("image"), item.image,
+        this.assert.equal(elt.getAttribute("image"), item.image,
                               "Item should have correct image");
         if (itemType === "Menu")
-          this.test.assert(elt.classList.contains("menu-iconic"),
+          this.assert.ok(elt.classList.contains("menu-iconic"),
                            "Menus with images should have the correct class")
         else
-          this.test.assert(elt.classList.contains("menuitem-iconic"),
+          this.assert.ok(elt.classList.contains("menuitem-iconic"),
                            "Items with images should have the correct class")
       }
       else {
-        this.test.assert(!elt.getAttribute("image"),
+        this.assert.ok(!elt.getAttribute("image"),
                          "Item should not have image");
-        this.test.assert(!elt.classList.contains("menu-iconic") && !elt.classList.contains("menuitem-iconic"),
+        this.assert.ok(!elt.classList.contains("menu-iconic") && !elt.classList.contains("menuitem-iconic"),
                          "The iconic classes should not be present")
       }
     }
   },
 
   // Asserts that the context menu looks OK given the arguments.  presentItems
   // are items that have been added to the menu.  absentItems are items that 
   // shouldn't match the current context.  removedItems are items that have been
@@ -3236,43 +3236,43 @@ TestHelper.prototype = {
     let total = 0;
     for (let item of presentItems) {
       if (absentItems.indexOf(item) < 0 && removedItems.indexOf(item) < 0)
         total++;
     }
 
     let separator = this.contextMenuSeparator;
     if (total == 0) {
-      this.test.assert(!separator || separator.hidden,
+      this.assert.ok(!separator || separator.hidden,
                        "separator should not be present");
     }
     else {
-      this.test.assert(separator && !separator.hidden,
+      this.assert.ok(separator && !separator.hidden,
                        "separator should be present");
     }
 
     let mainNodes = this.browserWindow.document.querySelectorAll("#contentAreaContextMenu > ." + ITEM_CLASS);
     let overflowNodes = this.browserWindow.document.querySelectorAll("." + OVERFLOW_POPUP_CLASS + " > ." + ITEM_CLASS);
 
-    this.test.assert(mainNodes.length == 0 || overflowNodes.length == 0,
+    this.assert.ok(mainNodes.length == 0 || overflowNodes.length == 0,
                      "Should only see nodes at the top level or in overflow");
 
     let overflow = this.overflowSubmenu;
     if (this.shouldOverflow(total)) {
-      this.test.assert(overflow && !overflow.hidden,
+      this.assert.ok(overflow && !overflow.hidden,
                        "overflow menu should be present");
-      this.test.assertEqual(mainNodes.length, 0,
+      this.assert.equal(mainNodes.length, 0,
                             "should be no items in the main context menu");
     }
     else {
-      this.test.assert(!overflow || overflow.hidden,
+      this.assert.ok(!overflow || overflow.hidden,
                        "overflow menu should not be present");
       // When visible nodes == 0 they could be in overflow or top level
       if (total > 0) {
-        this.test.assertEqual(overflowNodes.length, 0,
+        this.assert.equal(overflowNodes.length, 0,
                               "should be no items in the overflow context menu");
       }
     }
 
     // Iterate over wherever the nodes have ended up
     let nodes = mainNodes.length ? mainNodes : overflowNodes;
     this.checkNodes(nodes, presentItems, absentItems, removedItems)
     let pos = 0;
@@ -3285,40 +3285,40 @@ TestHelper.prototype = {
   checkNodes: function (nodes, presentItems, absentItems, removedItems) {
     let pos = 0;
     for (let item of presentItems) {
       // Removed items shouldn't be in the list
       if (removedItems.indexOf(item) >= 0)
         continue;
 
       if (nodes.length <= pos) {
-        this.test.assert(false, "Not enough nodes");
+        this.assert.ok(false, "Not enough nodes");
         return;
       }
 
       let hidden = absentItems.indexOf(item) >= 0;
 
       this.checkItemElt(nodes[pos], item);
-      this.test.assertEqual(nodes[pos].hidden, hidden,
+      this.assert.equal(nodes[pos].hidden, hidden,
                             "hidden should be set correctly");
 
       // The contents of hidden menus doesn't matter so much
       if (!hidden && this.getItemType(item) == "Menu") {
-        this.test.assertEqual(nodes[pos].firstChild.localName, "menupopup",
+        this.assert.equal(nodes[pos].firstChild.localName, "menupopup",
                               "menu XUL should contain a menupopup");
         this.checkNodes(nodes[pos].firstChild.childNodes, item.items, absentItems, removedItems);
       }
 
       if (pos > 0)
-        this.test.assertEqual(nodes[pos].previousSibling, nodes[pos - 1],
+        this.assert.equal(nodes[pos].previousSibling, nodes[pos - 1],
                               "nodes should all be in the same group");
       pos++;
     }
 
-    this.test.assertEqual(nodes.length, pos,
+    this.assert.equal(nodes.length, pos,
                           "should have checked all the XUL nodes");
   },
 
   // Attaches an event listener to node.  The listener is automatically removed
   // when it's fired (so it's assumed it will fire), and callback is called
   // after a short delay.  Since the module we're testing relies on the same
   // event listeners to do its work, this is to give them a little breathing
   // room before callback runs.  Inside callback |this| is this object.
@@ -3330,18 +3330,18 @@ TestHelper.prototype = {
       if (isValid && !isValid(evt))
         return;
       node.removeEventListener(event, handler, useCapture);
       timer.setTimeout(function () {
         try {
           callback.call(self, evt);
         }
         catch (err) {
-          self.test.exception(err);
-          self.test.done();
+          self.assert.fail(err);
+          self.end();
         }
       }, 20);
     }, useCapture);
   },
 
   // Call to finish the test.
   done: function () {
     const self = this;
@@ -3349,17 +3349,17 @@ TestHelper.prototype = {
       this.closeTab();
 
       while (this.loaders.length) {
         this.loaders[0].unload();
       }
 
       require("sdk/preferences/service").set(OVERFLOW_THRESH_PREF, self.overflowThreshValue);
 
-      this.test.done();
+      this.end();
     }
 
     function closeBrowserWindow() {
       if (this.oldBrowserWindow) {
         this.delayedEventListener(this.browserWindow, "unload", commonDone,
                                   false);
         this.browserWindow.close();
         this.browserWindow = this.oldBrowserWindow;
@@ -3553,8 +3553,10 @@ TestHelper.prototype = {
     this.delayedEventListener(browser, "load", function () {
       this.tabBrowser.selectedTab = this.tab;
       onloadCallback.call(this, browser.contentWindow, browser.contentDocument);
     }, true, function(evt) {
       return evt.target.location == TEST_DOC_URL;
     });
   }
 };
+
+require('sdk/test').run(exports);
--- a/addon-sdk/source/test/test-file.js
+++ b/addon-sdk/source/test/test-file.js
@@ -19,255 +19,257 @@ const ERRORS = {
 
 // Use profile directory to list / read / write files.
 const profilePath = pathFor('ProfD');
 const fileNameInProfile = 'compatibility.ini';
 const dirNameInProfile = 'extensions';
 const filePathInProfile = file.join(profilePath, fileNameInProfile);
 const dirPathInProfile = file.join(profilePath, dirNameInProfile);
 
-exports.testDirName = function(test) {
-  test.assertEqual(file.dirname(dirPathInProfile), profilePath,
+exports.testDirName = function(assert) {
+  assert.equal(file.dirname(dirPathInProfile), profilePath,
                    "file.dirname() of dir should return parent dir");
 
-  test.assertEqual(file.dirname(filePathInProfile), profilePath,
+  assert.equal(file.dirname(filePathInProfile), profilePath,
                    "file.dirname() of file should return its dir");
 
   let dir = profilePath;
   while (dir)
     dir = file.dirname(dir);
 
-  test.assertEqual(dir, "",
+  assert.equal(dir, "",
                    "dirname should return empty string when dir has no parent");
 };
 
-exports.testBasename = function(test) {
+exports.testBasename = function(assert) {
   // Get the top-most path -- the path with no basename.  E.g., on Unix-like
   // systems this will be /.  We'll use it below to build up some test paths.
   // We have to go to this trouble because file.join() needs a legal path as a
   // base case; join("foo", "bar") doesn't work unfortunately.
   let topPath = profilePath;
   let parentPath = file.dirname(topPath);
   while (parentPath) {
     topPath = parentPath;
     parentPath = file.dirname(topPath);
   }
 
   let path = topPath;
-  test.assertEqual(file.basename(path), "",
+  assert.equal(file.basename(path), "",
                    "basename should work on paths with no components");
 
   path = file.join(path, "foo");
-  test.assertEqual(file.basename(path), "foo",
+  assert.equal(file.basename(path), "foo",
                    "basename should work on paths with a single component");
 
   path = file.join(path, "bar");
-  test.assertEqual(file.basename(path), "bar",
+  assert.equal(file.basename(path), "bar",
                    "basename should work on paths with multiple components");
 };
 
-exports.testList = function(test) {
+exports.testList = function(assert) {
   let list = file.list(profilePath);
   let found = [ true for each (name in list)
                     if (name === fileNameInProfile) ];
 
   if (found.length > 1)
-    test.fail("a dir can't contain two files of the same name!");
-  test.assertEqual(found[0], true, "file.list() should work");
+    assert.fail("a dir can't contain two files of the same name!");
+  assert.equal(found[0], true, "file.list() should work");
 
-  test.assertRaises(function() {
+  assert.throws(function() {
     file.list(filePathInProfile);
   }, ERRORS.NOT_A_DIRECTORY, "file.list() on non-dir should raise error");
 
-  test.assertRaises(function() {
+  assert.throws(function() {
     file.list(file.join(dirPathInProfile, "does-not-exist"));
   }, ERRORS.FILE_NOT_FOUND, "file.list() on nonexistent should raise error");
 };
 
-exports.testRead = function(test) {
+exports.testRead = function(assert) {
   let contents = file.read(filePathInProfile);
-  test.assertMatches(contents, /Compatibility/,
+  assert.ok(/Compatibility/.test(contents),
                      "file.read() should work");
 
-  test.assertRaises(function() {
+  assert.throws(function() {
     file.read(file.join(dirPathInProfile, "does-not-exists"));
   }, ERRORS.FILE_NOT_FOUND, "file.read() on nonexistent file should throw");
 
-  test.assertRaises(function() {
+  assert.throws(function() {
     file.read(dirPathInProfile);
   }, ERRORS.NOT_A_FILE, "file.read() on dir should throw");
 };
 
-exports.testJoin = function(test) {
+exports.testJoin = function(assert) {
   let baseDir = file.dirname(filePathInProfile);
 
-  test.assertEqual(file.join(baseDir, fileNameInProfile),
+  assert.equal(file.join(baseDir, fileNameInProfile),
                    filePathInProfile, "file.join() should work");
 };
 
-exports.testOpenNonexistentForRead = function (test) {
+exports.testOpenNonexistentForRead = function (assert) {
   var filename = file.join(profilePath, 'does-not-exists');
-  test.assertRaises(function() {
+  assert.throws(function() {
     file.open(filename);
   }, ERRORS.FILE_NOT_FOUND, "file.open() on nonexistent file should throw");
 
-  test.assertRaises(function() {
+  assert.throws(function() {
     file.open(filename, "r");
   }, ERRORS.FILE_NOT_FOUND, "file.open('r') on nonexistent file should throw");
 
-  test.assertRaises(function() {
+  assert.throws(function() {
     file.open(filename, "zz");
   }, ERRORS.FILE_NOT_FOUND, "file.open('zz') on nonexistent file should throw");
 };
 
-exports.testOpenNonexistentForWrite = function (test) {
+exports.testOpenNonexistentForWrite = function (assert) {
   let filename = file.join(profilePath, 'open.txt');
 
   let stream = file.open(filename, "w");
   stream.close();
 
-  test.assert(file.exists(filename),
+  assert.ok(file.exists(filename),
               "file.exists() should return true after file.open('w')");
   file.remove(filename);
-  test.assert(!file.exists(filename),
+  assert.ok(!file.exists(filename),
               "file.exists() should return false after file.remove()");
 
   stream = file.open(filename, "rw");
   stream.close();
 
-  test.assert(file.exists(filename),
+  assert.ok(file.exists(filename),
               "file.exists() should return true after file.open('rw')");
   file.remove(filename);
-  test.assert(!file.exists(filename),
+  assert.ok(!file.exists(filename),
               "file.exists() should return false after file.remove()");
 };
 
-exports.testOpenDirectory = function (test) {
+exports.testOpenDirectory = function (assert) {
   let dir = dirPathInProfile;
-  test.assertRaises(function() {
+  assert.throws(function() {
     file.open(dir);
   }, ERRORS.NOT_A_FILE, "file.open() on directory should throw");
 
-  test.assertRaises(function() {
+  assert.throws(function() {
     file.open(dir, "w");
   }, ERRORS.NOT_A_FILE, "file.open('w') on directory should throw");
 };
 
-exports.testOpenTypes = function (test) {
+exports.testOpenTypes = function (assert) {
   let filename = file.join(profilePath, 'open-types.txt');
 
 
   // Do the opens first to create the data file.
   var stream = file.open(filename, "w");
-  test.assert(stream instanceof textStreams.TextWriter,
+  assert.ok(stream instanceof textStreams.TextWriter,
               "open(w) should return a TextWriter");
   stream.close();
 
   stream = file.open(filename, "wb");
-  test.assert(stream instanceof byteStreams.ByteWriter,
+  assert.ok(stream instanceof byteStreams.ByteWriter,
               "open(wb) should return a ByteWriter");
   stream.close();
 
   stream = file.open(filename);
-  test.assert(stream instanceof textStreams.TextReader,
+  assert.ok(stream instanceof textStreams.TextReader,
               "open() should return a TextReader");
   stream.close();
 
   stream = file.open(filename, "r");
-  test.assert(stream instanceof textStreams.TextReader,
+  assert.ok(stream instanceof textStreams.TextReader,
               "open(r) should return a TextReader");
   stream.close();
 
   stream = file.open(filename, "b");
-  test.assert(stream instanceof byteStreams.ByteReader,
+  assert.ok(stream instanceof byteStreams.ByteReader,
               "open(b) should return a ByteReader");
   stream.close();
 
   stream = file.open(filename, "rb");
-  test.assert(stream instanceof byteStreams.ByteReader,
+  assert.ok(stream instanceof byteStreams.ByteReader,
               "open(rb) should return a ByteReader");
   stream.close();
 
   file.remove(filename);
 };
 
-exports.testMkpathRmdir = function (test) {
+exports.testMkpathRmdir = function (assert) {
   let basePath = profilePath;
   let dirs = [];
   for (let i = 0; i < 3; i++)
     dirs.push("test-file-dir");
 
   let paths = [];
   for (let i = 0; i < dirs.length; i++) {
     let args = [basePath].concat(dirs.slice(0, i + 1));
     paths.unshift(file.join.apply(null, args));
   }
 
   for (let i = 0; i < paths.length; i++) {
-    test.assert(!file.exists(paths[i]),
+    assert.ok(!file.exists(paths[i]),
                 "Sanity check: path should not exist: " + paths[i]);
   }
 
   file.mkpath(paths[0]);
-  test.assert(file.exists(paths[0]), "mkpath should create path: " + paths[0]);
+  assert.ok(file.exists(paths[0]), "mkpath should create path: " + paths[0]);
 
   for (let i = 0; i < paths.length; i++) {
     file.rmdir(paths[i]);
-    test.assert(!file.exists(paths[i]),
+    assert.ok(!file.exists(paths[i]),
                 "rmdir should remove path: " + paths[i]);
   }
 };
 
-exports.testMkpathTwice = function (test) {
+exports.testMkpathTwice = function (assert) {
   let dir = profilePath;
   let path = file.join(dir, "test-file-dir");
-  test.assert(!file.exists(path),
+  assert.ok(!file.exists(path),
               "Sanity check: path should not exist: " + path);
   file.mkpath(path);
-  test.assert(file.exists(path), "mkpath should create path: " + path);
+  assert.ok(file.exists(path), "mkpath should create path: " + path);
   file.mkpath(path);
-  test.assert(file.exists(path),
+  assert.ok(file.exists(path),
               "After second mkpath, path should still exist: " + path);
   file.rmdir(path);
-  test.assert(!file.exists(path), "rmdir should remove path: " + path);
+  assert.ok(!file.exists(path), "rmdir should remove path: " + path);
 };
 
-exports.testMkpathExistingNondirectory = function (test) {
+exports.testMkpathExistingNondirectory = function (assert) {
   var fname = file.join(profilePath, 'conflict.txt');
   file.open(fname, "w").close();
-  test.assert(file.exists(fname), "File should exist");
-  test.assertRaises(function() file.mkpath(fname),
+  assert.ok(file.exists(fname), "File should exist");
+  assert.throws(function() file.mkpath(fname),
                     /^The path already exists and is not a directory: .+$/,
                     "mkpath on file should raise error");
   file.remove(fname);
 };
 
-exports.testRmdirNondirectory = function (test) {
+exports.testRmdirNondirectory = function (assert) {
   var fname = file.join(profilePath, 'not-a-dir')
   file.open(fname, "w").close();
-  test.assert(file.exists(fname), "File should exist");
-  test.assertRaises(function() {
+  assert.ok(file.exists(fname), "File should exist");
+  assert.throws(function() {
     file.rmdir(fname);
   }, ERRORS.NOT_A_DIRECTORY, "rmdir on file should raise error");
   file.remove(fname);
-  test.assert(!file.exists(fname), "File should not exist");
-  test.assertRaises(function () file.rmdir(fname),
+  assert.ok(!file.exists(fname), "File should not exist");
+  assert.throws(function () file.rmdir(fname),
                     ERRORS.FILE_NOT_FOUND,
                     "rmdir on non-existing file should raise error");
 };
 
-exports.testRmdirNonempty = function (test) {
+exports.testRmdirNonempty = function (assert) {
   let dir = profilePath;
   let path = file.join(dir, "test-file-dir");
-  test.assert(!file.exists(path),
+  assert.ok(!file.exists(path),
               "Sanity check: path should not exist: " + path);
   file.mkpath(path);
   let filePath = file.join(path, "file");
   file.open(filePath, "w").close();
-  test.assert(file.exists(filePath),
+  assert.ok(file.exists(filePath),
               "Sanity check: path should exist: " + filePath);
-  test.assertRaises(function () file.rmdir(path),
+  assert.throws(function () file.rmdir(path),
                     /^The directory is not empty: .+$/,
                     "rmdir on non-empty directory should raise error");
   file.remove(filePath);
   file.rmdir(path);
-  test.assert(!file.exists(path), "Path should not exist");
+  assert.ok(!file.exists(path), "Path should not exist");
 };
+
+require('sdk/test').run(exports);
--- a/addon-sdk/source/test/test-fs.js
+++ b/addon-sdk/source/test/test-fs.js
@@ -1,42 +1,46 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 
-const { pathFor } = require("sdk/system");
+const { pathFor, platform } = require("sdk/system");
 const fs = require("sdk/io/fs");
 const url = require("sdk/url");
 const path = require("sdk/fs/path");
+const { defer } = require("sdk/core/promise");
 const { Buffer } = require("sdk/io/buffer");
 const { is } = require("sdk/system/xul-app");
 
 // Use profile directory to list / read / write files.
 const profilePath = pathFor("ProfD");
 const fileNameInProfile = "compatibility.ini";
 const dirNameInProfile = "extensions";
 const filePathInProfile = path.join(profilePath, fileNameInProfile);
 const dirPathInProfile = path.join(profilePath, dirNameInProfile);
 const mkdirPath = path.join(profilePath, "sdk-fixture-mkdir");
 const writePath = path.join(profilePath, "sdk-fixture-writeFile");
 const unlinkPath = path.join(profilePath, "sdk-fixture-unlink");
 const truncatePath = path.join(profilePath, "sdk-fixture-truncate");
 const renameFromPath = path.join(profilePath, "sdk-fixture-rename-from");
 const renameToPath = path.join(profilePath, "sdk-fixture-rename-to");
+const chmodPath = path.join(profilePath, "sdk-fixture-chmod");
 
 const profileEntries = [
   "compatibility.ini",
   "extensions",
   "prefs.js"
-  // There are likely to be a lot more files but we can't really
+  // There are likely to be a lot more files but we can"t really
   // on consistent list so we limit to this.
 ];
 
+const isWindows = platform.indexOf('win') === 0;
+
 exports["test readdir"] = function(assert, end) {
   var async = false;
   fs.readdir(profilePath, function(error, entries) {
     assert.ok(async, "readdir is async");
     assert.ok(!error, "there is no error when reading directory");
     assert.ok(profileEntries.length <= entries.length,
               "got at least number of entries we expect");
     assert.ok(profileEntries.every(function(entry) {
@@ -478,9 +482,140 @@ exports["test fs.writeFile (with large f
     fs.unlinkSync(path);
     assert.ok(!fs.exists(path), "file was removed");
 
     end();
   });
   async = true;
 };
 
+exports["test fs.writeFile error"] = function (assert, done) {
+  try {
+    fs.writeFile({}, 'content', function (err) {
+      assert.fail('Error thrown from TypeError should not be caught');
+    });
+  } catch (e) {
+    assert.ok(e,
+      'writeFile with a non-string error should not be caught');
+    assert.equal(e.name, 'TypeError', 'error should be TypeError');
+  }
+  fs.writeFile('not/a/valid/path', 'content', function (err) {
+    assert.ok(err, 'error caught and handled in callback');
+    done();
+  });
+};
+
+exports["test fs.chmod"] = function (assert, done) {
+  let content = ["hej från sverige"];
+
+  fs.writeFile(chmodPath, content, function (err) {
+    testPerm("0755")()
+      .then(testPerm("0777"))
+      .then(testPerm("0666"))
+      .then(testPerm(parseInt("0511", 8)))
+      .then(testPerm(parseInt("0200", 8)))
+      .then(testPerm("0040"))
+      .then(testPerm("0000"))
+      .then(testPermSync(parseInt("0777", 8)))
+      .then(testPermSync(parseInt("0666", 8)))
+      .then(testPermSync("0511"))
+      .then(testPermSync("0200"))
+      .then(testPermSync("0040"))
+      .then(testPermSync("0000"))
+      .then(() => {
+        assert.pass("Successful chmod passes");
+      }, assert.fail)
+      // Test invalid paths
+      .then(() => chmod("not-a-valid-file", parseInt("0755", 8)))
+      .then(assert.fail, (err) => {
+        checkPermError(err, "not-a-valid-file");
+      })
+      .then(() => chmod("not-a-valid-file", parseInt("0755", 8), "sync"))
+      .then(assert.fail, (err) => {
+        checkPermError(err, "not-a-valid-file");
+      })
+      // Test invalid files
+      .then(() => chmod("resource://not-a-real-file", parseInt("0755", 8)))
+      .then(assert.fail, (err) => {
+        checkPermError(err, "resource://not-a-real-file");
+      })
+      .then(() => chmod("resource://not-a-real-file", parseInt("0755", 8), 'sync'))
+      .then(assert.fail, (err) => {
+        checkPermError(err, "resource://not-a-real-file");
+      })
+      .then(done, assert.fail);
+  });
+
+  function checkPermError (err, path) {
+    assert.equal(err.message, "ENOENT, chmod " + path);
+    assert.equal(err.code, "ENOENT", "error has a code");
+    assert.equal(err.path, path, "error has a path");
+    assert.equal(err.errno, 34, "error has a errno");
+  }
+
+  function chmod (path, mode, sync) {
+    let { promise, resolve, reject } = defer();
+    if (!sync) {
+      fs.chmod(path, mode, (err) => {
+        if (err) reject(err);
+        else resolve();
+      });
+    } else {
+      fs.chmodSync(path, mode);
+      resolve();
+    }
+    return promise;
+  }
+
+  function testPerm (mode, sync) {
+    return function () {
+      return chmod(chmodPath, mode, sync)
+        .then(() => getPerm(chmodPath))
+        .then(perm => {
+          let nMode = normalizeMode(mode);
+          if (isWindows)
+            assert.equal(perm, nMode,
+              "mode correctly set to " + mode + " (" + nMode + " on windows)");
+          else
+            assert.equal(perm, nMode, "mode correctly set to " + nMode);
+        });
+    };
+  }
+
+  function testPermSync (mode) {
+    return testPerm(mode, true);
+  }
+
+  function getPerm (path) {
+    let { promise, resolve, reject } = defer();
+    fs.stat(path, function (err, stat) {
+      if (err) reject(err);
+      else resolve(stat.mode);
+    });
+    return promise;
+  }
+
+  /*
+   * Converts a unix mode `0755` into a Windows version of unix permissions
+   */
+  function normalizeMode (mode) {
+    if (typeof mode === "string")
+      mode = parseInt(mode, 8);
+
+    if (!isWindows)
+      return mode;
+
+    var ANY_READ = parseInt("0444", 8);
+    var ANY_WRITE = parseInt("0222", 8);
+    var winMode = 0;
+
+    // On Windows, if WRITE is true, then READ is also true
+    if (mode & ANY_WRITE)
+      winMode |= ANY_WRITE | ANY_READ;
+    // Minimum permissions are READ for Windows
+    else
+      winMode |= ANY_READ;
+
+    return winMode;
+  }
+};
+
 require("test").run(exports);
--- a/addon-sdk/source/test/test-functional.js
+++ b/addon-sdk/source/test/test-functional.js
@@ -175,16 +175,31 @@ exports['test once'] = function(assert) 
   let target = { state: 0, update: once(function() this.state ++ ) };
 
   target.update();
   target.update();
 
   assert.equal(target.state, 1, 'this was passed in and called only once');
 };
 
+exports['test once with argument'] = function(assert) {
+  let n = 0;
+  let increment = once(function(a) n++);
+
+  increment();
+  increment('foo');
+
+  assert.equal(n, 1, 'only incremented once');
+
+  increment();
+  increment('foo');
+
+  assert.equal(n, 1, 'only incremented once');
+};
+
 exports['test chain'] = function (assert) {
   let Player = function () { this.volume = 5; };
   Player.prototype = {
     setBand: chain(function (band) this.band = band),
     incVolume: chain(function () this.volume++)
   };
   let player = new Player();
   player
--- a/addon-sdk/source/test/test-httpd.js
+++ b/addon-sdk/source/test/test-httpd.js
@@ -8,85 +8,66 @@ const { pathFor } = require("sdk/system"
 const { Loader } = require("sdk/test/loader");
 const options = require("@test/options");
 
 const loader = Loader(module);
 const httpd = loader.require("sdk/test/httpd");
 if (options.parseable || options.verbose)
   loader.sandbox("sdk/test/httpd").DEBUG = true;
 
-exports.testBasicHTTPServer = function(test) {
+exports.testBasicHTTPServer = function(assert, done) {
   // Use the profile directory for the temporary file as that will be deleted
   // when tests are complete
   let basePath = pathFor("ProfD");
   let filePath = file.join(basePath, 'test-httpd.txt');
   let content = "This is the HTTPD test file.\n";
   let fileStream = file.open(filePath, 'w');
   fileStream.write(content);
   fileStream.close();
 
   let srv = httpd.startServerAsync(port, basePath);
 
-  test.waitUntilDone();
-
   // Request this very file.
   let Request = require('sdk/request').Request;
   Request({
     url: "http://localhost:" + port + "/test-httpd.txt",
     onComplete: function (response) {
-      test.assertEqual(response.text, content);
-      done();
+      assert.equal(response.text, content);
+      srv.stop(done);
     }
   }).get();
-
-  function done() {
-    srv.stop(function() {
-      test.done();
-    });
-  }
 };
 
-exports.testDynamicServer = function (test) {
+exports.testDynamicServer = function (assert, done) {
   let content = "This is the HTTPD test file.\n";
 
   let srv = httpd.startServerAsync(port);
 
   // See documentation here:
   //http://doxygen.db48x.net/mozilla/html/interfacensIHttpServer.html#a81fc7e7e29d82aac5ce7d56d0bedfb3a
   //http://doxygen.db48x.net/mozilla/html/interfacensIHttpRequestHandler.html
   srv.registerPathHandler("/test-httpd.txt", function handle(request, response) {
     // Add text content type, only to avoid error in `Request` API
     response.setHeader("Content-Type", "text/plain", false);
     response.write(content);
   });
 
-  test.waitUntilDone();
-
   // Request this very file.
   let Request = require('sdk/request').Request;
   Request({
     url: "http://localhost:" + port + "/test-httpd.txt",
     onComplete: function (response) {
-      test.assertEqual(response.text, content);
-      done();
+      assert.equal(response.text, content);
+      srv.stop(done);
     }
   }).get();
+};
 
-  function done() {
-    srv.stop(function() {
-      test.done();
-    });
-  }
-
-}
-
-exports.testAutomaticPortSelection = function (test) {
+exports.testAutomaticPortSelection = function (assert, done) {
   const srv = httpd.startServerAsync(-1);
 
-  test.waitUntilDone();
-
   const port = srv.identity.primaryPort;
-  test.assert(0 <= port && port <= 65535);
+  assert.ok(0 <= port && port <= 65535);
 
-  srv.stop(function() {
-    test.done();
-  });
-}
+  srv.stop(done);
+};
+
+require('sdk/test').run(exports);
--- a/addon-sdk/source/test/test-l10n-locale.js
+++ b/addon-sdk/source/test/test-l10n-locale.js
@@ -7,53 +7,53 @@ const prefs = require("sdk/preferences/s
 const { Cc, Ci, Cu } = require("chrome");
 const { Services } = Cu.import("resource://gre/modules/Services.jsm");
 const BundleService = Cc["@mozilla.org/intl/stringbundle;1"].getService(Ci.nsIStringBundleService);
 
 const PREF_MATCH_OS_LOCALE  = "intl.locale.matchOS";
 const PREF_SELECTED_LOCALE  = "general.useragent.locale";
 const PREF_ACCEPT_LANGUAGES = "intl.accept_languages";
 
-function assertPrefered(test, expected, msg) {
-  test.assertEqual(JSON.stringify(getPreferedLocales()), JSON.stringify(expected),
+function assertPrefered(assert, expected, msg) {
+  assert.equal(JSON.stringify(getPreferedLocales()), JSON.stringify(expected),
                    msg);
 }
 
-exports.testGetPreferedLocales = function(test) {
+exports.testGetPreferedLocales = function(assert) {
   prefs.set(PREF_MATCH_OS_LOCALE, false);
   prefs.set(PREF_SELECTED_LOCALE, "");
   prefs.set(PREF_ACCEPT_LANGUAGES, "");
-  assertPrefered(test, ["en-us"],
+  assertPrefered(assert, ["en-us"],
                  "When all preferences are empty, we only have en-us");
 
   prefs.set(PREF_SELECTED_LOCALE, "fr");
   prefs.set(PREF_ACCEPT_LANGUAGES, "jp");
-  assertPrefered(test, ["fr", "jp", "en-us"],
+  assertPrefered(assert, ["fr", "jp", "en-us"],
                  "We first have useragent locale, then web one and finally en-US");
 
   prefs.set(PREF_SELECTED_LOCALE, "en-US");
   prefs.set(PREF_ACCEPT_LANGUAGES, "en-US");
-  assertPrefered(test, ["en-us"],
+  assertPrefered(assert, ["en-us"],
                  "We do not have duplicates");
 
   prefs.set(PREF_SELECTED_LOCALE, "en-US");
   prefs.set(PREF_ACCEPT_LANGUAGES, "fr");
-  assertPrefered(test, ["en-us", "fr"],
+  assertPrefered(assert, ["en-us", "fr"],
                  "en-US can be first if specified by higher priority preference");
 
   // Reset what we changed
   prefs.reset(PREF_MATCH_OS_LOCALE);
   prefs.reset(PREF_SELECTED_LOCALE);
   prefs.reset(PREF_ACCEPT_LANGUAGES);
 }
 
 // In some cases, mainly on Fennec and on Linux version,
 // `general.useragent.locale` is a special 'localized' value, like:
 // "chrome://global/locale/intl.properties"
-exports.testPreferedLocalizedLocale = function(test) {
+exports.testPreferedLocalizedLocale = function(assert) {
   prefs.set(PREF_MATCH_OS_LOCALE, false);
   let bundleURL = "chrome://global/locale/intl.properties";
   prefs.setLocalized(PREF_SELECTED_LOCALE, bundleURL);
   let contentLocale = "ja";
   prefs.set(PREF_ACCEPT_LANGUAGES, contentLocale);
 
   // Read manually the expected locale value from the property file
   let expectedLocale = BundleService.createBundle(bundleURL).
@@ -66,76 +66,78 @@ exports.testPreferedLocalizedLocale = fu
   // Then the content locale
   if (expectedLocaleList.indexOf(contentLocale) == -1)
     expectedLocaleList.push(contentLocale);
 
   // Add default "en-us" fallback if the main language is not already en-us
   if (expectedLocaleList.indexOf("en-us") == -1)
     expectedLocaleList.push("en-us");
 
-  assertPrefered(test, expectedLocaleList, "test localized pref value");
+  assertPrefered(assert, expectedLocaleList, "test localized pref value");
 
   // Reset what we have changed
   prefs.reset(PREF_MATCH_OS_LOCALE);
   prefs.reset(PREF_SELECTED_LOCALE);
   prefs.reset(PREF_ACCEPT_LANGUAGES);
 }
 
-exports.testPreferedOsLocale = function(test) {
+exports.testPreferedOsLocale = function(assert) {
   prefs.set(PREF_MATCH_OS_LOCALE, true);
   prefs.set(PREF_SELECTED_LOCALE, "");
   prefs.set(PREF_ACCEPT_LANGUAGES, "");
 
   let expectedLocale = Services.locale.getLocaleComponentForUserAgent().
     toLowerCase();
   let expectedLocaleList = [expectedLocale];
 
   // Add default "en-us" fallback if the main language is not already en-us
   if (expectedLocale != "en-us")
     expectedLocaleList.push("en-us");
 
-  assertPrefered(test, expectedLocaleList, "Ensure that we select OS locale when related preference is set");
+  assertPrefered(assert, expectedLocaleList, "Ensure that we select OS locale when related preference is set");
 
   // Reset what we have changed
   prefs.reset(PREF_MATCH_OS_LOCALE);
   prefs.reset(PREF_SELECTED_LOCALE);
   prefs.reset(PREF_ACCEPT_LANGUAGES);
 }
 
-exports.testFindClosestLocale = function(test) {
+exports.testFindClosestLocale = function(assert) {
   // Second param of findClosestLocale (aMatchLocales) have to be in lowercase
-  test.assertEqual(findClosestLocale([], []), null,
+  assert.equal(findClosestLocale([], []), null,
                    "When everything is empty we get null");
 
-  test.assertEqual(findClosestLocale(["en", "en-US"], ["en"]),
+  assert.equal(findClosestLocale(["en", "en-US"], ["en"]),
                    "en", "We always accept exact match first 1/5");
-  test.assertEqual(findClosestLocale(["en-US", "en"], ["en"]),
+  assert.equal(findClosestLocale(["en-US", "en"], ["en"]),
                    "en", "We always accept exact match first 2/5");
-  test.assertEqual(findClosestLocale(["en", "en-US"], ["en-us"]),
+  assert.equal(findClosestLocale(["en", "en-US"], ["en-us"]),
                    "en-US", "We always accept exact match first 3/5");
-  test.assertEqual(findClosestLocale(["ja-JP-mac", "ja", "ja-JP"], ["ja-jp"]),
+  assert.equal(findClosestLocale(["ja-JP-mac", "ja", "ja-JP"], ["ja-jp"]),
                    "ja-JP", "We always accept exact match first 4/5");
-  test.assertEqual(findClosestLocale(["ja-JP-mac", "ja", "ja-JP"], ["ja-jp-mac"]),
+  assert.equal(findClosestLocale(["ja-JP-mac", "ja", "ja-JP"], ["ja-jp-mac"]),
                    "ja-JP-mac", "We always accept exact match first 5/5");
 
-  test.assertEqual(findClosestLocale(["en", "en-GB"], ["en-us"]),
+  assert.equal(findClosestLocale(["en", "en-GB"], ["en-us"]),
                    "en", "We accept more generic locale, when there is no exact match 1/2");
-  test.assertEqual(findClosestLocale(["en-ZA", "en"], ["en-gb"]),
+  assert.equal(findClosestLocale(["en-ZA", "en"], ["en-gb"]),
                    "en", "We accept more generic locale, when there is no exact match 2/2");
 
-  test.assertEqual(findClosestLocale(["ja-JP"], ["ja"]),
+  assert.equal(findClosestLocale(["ja-JP"], ["ja"]),
                    "ja-JP", "We accept more specialized locale, when there is no exact match 1/2");
   // Better to select "ja" in this case but behave same as current AddonManager
-  test.assertEqual(findClosestLocale(["ja-JP-mac", "ja"], ["ja-jp"]),
+  assert.equal(findClosestLocale(["ja-JP-mac", "ja"], ["ja-jp"]),
                    "ja-JP-mac", "We accept more specialized locale, when there is no exact match 2/2");
 
-  test.assertEqual(findClosestLocale(["en-US"], ["en-us"]),
+  assert.equal(findClosestLocale(["en-US"], ["en-us"]),
                    "en-US", "We keep the original one as result 1/2");
-  test.assertEqual(findClosestLocale(["en-us"], ["en-us"]),
+  assert.equal(findClosestLocale(["en-us"], ["en-us"]),
                    "en-us", "We keep the original one as result 2/2");
 
-  test.assertEqual(findClosestLocale(["ja-JP-mac"], ["ja-jp-mac"]),
+  assert.equal(findClosestLocale(["ja-JP-mac"], ["ja-jp-mac"]),
                    "ja-JP-mac", "We accept locale with 3 parts");
-  test.assertEqual(findClosestLocale(["ja-JP"], ["ja-jp-mac"]),
+  assert.equal(findClosestLocale(["ja-JP"], ["ja-jp-mac"]),
                    "ja-JP", "We accept locale with 2 parts from locale with 3 parts");
-  test.assertEqual(findClosestLocale(["ja"], ["ja-jp-mac"]),
+  assert.equal(findClosestLocale(["ja"], ["ja-jp-mac"]),
                    "ja", "We accept locale with 1 part from locale with 3 parts");
-}
+};
+
+require('sdk/test').run(exports);
--- a/addon-sdk/source/test/test-l10n-plural-rules.js
+++ b/addon-sdk/source/test/test-l10n-plural-rules.js
@@ -2,81 +2,83 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 const { getRulesForLocale } = require("sdk/l10n/plural-rules");
 
 // For more information, please visit unicode website:
 // http://unicode.org/repos/cldr-tmp/trunk/diff/supplemental/language_plural_rules.html
 
-function map(test, f, n, form) {
-  test.assertEqual(f(n), form, n + " maps to '" + form + "'");
+function map(assert, f, n, form) {
+  assert.equal(f(n), form, n + " maps to '" + form + "'");
 }
 
-exports.testFrench = function(test) {
+exports.testFrench = function(assert) {
   let f = getRulesForLocale("fr");
-  map(test, f, -1, "other");
-  map(test, f, 0, "one");
-  map(test, f, 1, "one");
-  map(test, f, 1.5, "one");
-  map(test, f, 2, "other");
-  map(test, f, 100, "other");
+  map(assert, f, -1, "other");
+  map(assert, f, 0, "one");
+  map(assert, f, 1, "one");
+  map(assert, f, 1.5, "one");
+  map(assert, f, 2, "other");
+  map(assert, f, 100, "other");
 }
 
-exports.testEnglish = function(test) {
+exports.testEnglish = function(assert) {
   let f = getRulesForLocale("en");
-  map(test, f, -1, "other");
-  map(test, f, 0, "other");
-  map(test, f, 1, "one");
-  map(test, f, 1.5, "other");
-  map(test, f, 2, "other");
-  map(test, f, 100, "other");
+  map(assert, f, -1, "other");
+  map(assert, f, 0, "other");
+  map(assert, f, 1, "one");
+  map(assert, f, 1.5, "other");
+  map(assert, f, 2, "other");
+  map(assert, f, 100, "other");
 }
 
-exports.testArabic = function(test) {
+exports.testArabic = function(assert) {
   let f = getRulesForLocale("ar");
-  map(test, f, -1, "other");
-  map(test, f, 0, "zero");
-  map(test, f, 0.5, "other");
+  map(assert, f, -1, "other");
+  map(assert, f, 0, "zero");
+  map(assert, f, 0.5, "other");
 
-  map(test, f, 1, "one");
-  map(test, f, 1.5, "other");
+  map(assert, f, 1, "one");
+  map(assert, f, 1.5, "other");
 
-  map(test, f, 2, "two");
-  map(test, f, 2.5, "other");
+  map(assert, f, 2, "two");
+  map(assert, f, 2.5, "other");
 
-  map(test, f, 3, "few");
-  map(test, f, 3.5, "few"); // I'd expect it to be 'other', but the unicode.org
+  map(assert, f, 3, "few");
+  map(assert, f, 3.5, "few"); // I'd expect it to be 'other', but the unicode.org
                             // algorithm computes 'few'.
-  map(test, f, 5, "few");
-  map(test, f, 10, "few");
-  map(test, f, 103, "few");
-  map(test, f, 105, "few");
-  map(test, f, 110, "few");
-  map(test, f, 203, "few");
-  map(test, f, 205, "few");
-  map(test, f, 210, "few");
+  map(assert, f, 5, "few");
+  map(assert, f, 10, "few");
+  map(assert, f, 103, "few");
+  map(assert, f, 105, "few");
+  map(assert, f, 110, "few");
+  map(assert, f, 203, "few");
+  map(assert, f, 205, "few");
+  map(assert, f, 210, "few");
 
-  map(test, f, 11, "many");
-  map(test, f, 50, "many");
-  map(test, f, 99, "many");
-  map(test, f, 111, "many");
-  map(test, f, 150, "many");
-  map(test, f, 199, "many");
+  map(assert, f, 11, "many");
+  map(assert, f, 50, "many");
+  map(assert, f, 99, "many");
+  map(assert, f, 111, "many");
+  map(assert, f, 150, "many");
+  map(assert, f, 199, "many");
 
-  map(test, f, 100, "other");
-  map(test, f, 101, "other");
-  map(test, f, 102, "other");
-  map(test, f, 200, "other");
-  map(test, f, 201, "other");
-  map(test, f, 202, "other");
+  map(assert, f, 100, "other");
+  map(assert, f, 101, "other");
+  map(assert, f, 102, "other");
+  map(assert, f, 200, "other");
+  map(assert, f, 201, "other");
+  map(assert, f, 202, "other");
 }
 
-exports.testJapanese = function(test) {
+exports.testJapanese = function(assert) {
   // Japanese doesn't have plural forms.
   let f = getRulesForLocale("ja");
-  map(test, f, -1, "other");
-  map(test, f, 0, "other");
-  map(test, f, 1, "other");
-  map(test, f, 1.5, "other");
-  map(test, f, 2, "other");
-  map(test, f, 100, "other");
+  map(assert, f, -1, "other");
+  map(assert, f, 0, "other");
+  map(assert, f, 1, "other");
+  map(assert, f, 1.5, "other");
+  map(assert, f, 2, "other");
+  map(assert, f, 100, "other");
 }
+
+require('sdk/test').run(exports);
--- a/addon-sdk/source/test/test-loader.js
+++ b/addon-sdk/source/test/test-loader.js
@@ -3,16 +3,21 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 'use strict';
 
 let { Loader, main, unload, parseStack } = require('toolkit/loader');
 
 let root = module.uri.substr(0, module.uri.lastIndexOf('/'))
 
+// The following adds Debugger constructor to the global namespace.
+const { Cu } = require('chrome');
+const { addDebuggerToGlobal } = Cu.import('resource://gre/modules/jsdebugger.jsm', {});
+addDebuggerToGlobal(this);
+
 exports['test dependency cycles'] = function(assert) {
   let uri = root + '/fixtures/loader/cycles/';
   let loader = Loader({ paths: { '': uri } });
 
   let program = main(loader, 'main');
 
   assert.equal(program.a.b, program.b, 'module `a` gets correct `b`');
   assert.equal(program.b.a, program.a, 'module `b` gets correct `a`');
@@ -139,9 +144,27 @@ exports['test early errors in module'] =
 
     assert.equal(stack.pop().fileName, module.uri,
                  "this test module is next in the stack");
   } finally {
     unload(loader);
   }
 }
 
+exports['test setting metadata for newly created sandboxes'] = function(assert) {
+  let addonID = 'random-addon-id';
+  let uri = root + '/fixtures/loader/cycles/';
+  let loader = Loader({ paths: { '': uri }, id: addonID });
+
+  let dbg = new Debugger();
+  dbg.onNewGlobalObject = function(global) {
+    dbg.onNewGlobalObject = undefined;
+
+    let metadata = Cu.getSandboxMetadata(global.unsafeDereference());
+    assert.ok(metadata, 'this global has attached metadata');
+    assert.equal(metadata.URI, uri + 'main.js', 'URI is set properly');
+    assert.equal(metadata.addonID, addonID, 'addon ID is set');
+  }
+
+  let program = main(loader, 'main');
+}
+
 require('test').run(exports);
--- a/addon-sdk/source/test/test-match-pattern.js
+++ b/addon-sdk/source/test/test-match-pattern.js
@@ -1,19 +1,19 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 "use strict";
 
 const { MatchPattern } = require("sdk/util/match-pattern");
 
-exports.testMatchPatternTestTrue = function(test) {
+exports.testMatchPatternTestTrue = function(assert) {
   function ok(pattern, url) {
     let mp = new MatchPattern(pattern);
-    test.assert(mp.test(url), pattern + " should match " + url);
+    assert.ok(mp.test(url), pattern + " should match " + url);
   }
 
   ok("*", "http://example.com");
   ok("*", "https://example.com");
   ok("*", "ftp://example.com");
 
   ok("*.example.com", "http://example.com");
   ok("*.example.com", "http://hamburger.example.com");
@@ -32,20 +32,20 @@ exports.testMatchPatternTestTrue = funct
   ok(/.*zilla.*/, "https://bugzilla.redhat.com/show_bug.cgi?id=569753");
   ok(/https:.*zilla.*/, "https://bugzilla.redhat.com/show_bug.cgi?id=569753");
   ok('*.sample.com', 'http://ex.sample.com/foo.html');
   ok('*.amp.le.com', 'http://ex.amp.le.com');
 
   ok('data:*', 'data:text/html;charset=utf-8,');
 };
 
-exports.testMatchPatternTestFalse = function(test) {
+exports.testMatchPatternTestFalse = function(assert) {
   function ok(pattern, url) {
     let mp = new MatchPattern(pattern);
-    test.assert(!mp.test(url), pattern + " should not match " + url);
+    assert.ok(!mp.test(url), pattern + " should not match " + url);
   }
 
   ok("*", null);
   ok("*", "");
   ok("*", "bogus");
   ok("*", "chrome://browser/content/browser.xul");
   ok("*", "nttp://example.com");
 
@@ -77,63 +77,65 @@ exports.testMatchPatternTestFalse = func
   ok('*.ign.com', 'http://www.design.com');
   ok('*.ign.com', 'http://design.com');
   ok('*.zilla.com', 'http://bugzilla.mozilla.com');
   ok('*.zilla.com', 'http://mo-zilla.com');
   ok('*.amp.le.com', 'http://amp-le.com');
   ok('*.amp.le.com', 'http://examp.le.com');
 };
 
-exports.testMatchPatternErrors = function(test) {
-  test.assertRaises(
+exports.testMatchPatternErrors = function(assert) {
+  assert.throws(
     function() new MatchPattern("*.google.com/*"),
     /There can be at most one/,
     "MatchPattern throws when supplied multiple '*'"
   );
 
-  test.assertRaises(
+  assert.throws(
     function() new MatchPattern("google.com"),
     /expected to be either an exact URL/,
     "MatchPattern throws when the wildcard doesn't use '*' and doesn't " +
     "look like a URL"
   );
 
-  test.assertRaises(
+  assert.throws(
     function() new MatchPattern("http://google*.com"),
     /expected to be the first or the last/,
     "MatchPattern throws when a '*' is in the middle of the wildcard"
   );
 
-  test.assertRaises(
+  assert.throws(
     function() new MatchPattern(/ /g),
     /^A RegExp match pattern cannot be set to `global` \(i\.e\. \/\/g\)\.$/,
     "MatchPattern throws on a RegExp set to `global` (i.e. //g)."
   );
 
-  test.assertRaises(
+  assert.throws(
     function() new MatchPattern(/ /i),
     /^A RegExp match pattern cannot be set to `ignoreCase` \(i\.e\. \/\/i\)\.$/,
     "MatchPattern throws on a RegExp set to `ignoreCase` (i.e. //i)."
   );
 
-  test.assertRaises(
+  assert.throws(
     function() new MatchPattern( / /m ),
     /^A RegExp match pattern cannot be set to `multiline` \(i\.e\. \/\/m\)\.$/,
     "MatchPattern throws on a RegExp set to `multiline` (i.e. //m)."
   );
 };
 
-exports.testMatchPatternInternals = function(test) {
-  test.assertEqual(
+exports.testMatchPatternInternals = function(assert) {
+  assert.equal(
     new MatchPattern("http://google.com/test").exactURL,
     "http://google.com/test"
   );
 
-  test.assertEqual(
+  assert.equal(
     new MatchPattern("http://google.com/test/*").urlPrefix,
     "http://google.com/test/"
   );
 
-  test.assertEqual(
+  assert.equal(
     new MatchPattern("*.example.com").domain,
     "example.com"
   );
 };
+
+require('sdk/test').run(exports);
--- a/addon-sdk/source/test/test-memory.js
+++ b/addon-sdk/source/test/test-memory.js
@@ -1,19 +1,21 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 var memory = require("sdk/deprecated/memory");
 
-exports.testMemory = function(test) {
-  test.pass("Skipping this test until Gecko memory debugging issues " +
+exports.testMemory = function(assert) {
+  assert.pass("Skipping this test until Gecko memory debugging issues " +
             "are resolved (see bug 592774).");
   return;
 
   var obj = {};
   memory.track(obj, "testMemory.testObj");
   var objs = memory.getObjects("testMemory.testObj");
-  test.assertEqual(objs[0].weakref.get(), obj);
+  assert.equal(objs[0].weakref.get(), obj);
   obj = null;
   memory.gc();
-  test.assertEqual(objs[0].weakref.get(), null);
+  assert.equal(objs[0].weakref.get(), null);
 };
+
+require('sdk/test').run(exports);
--- a/addon-sdk/source/test/test-modules.js
+++ b/addon-sdk/source/test/test-modules.js
@@ -1,148 +1,150 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
-exports.testDefine = function(test) {
+exports.testDefine = function(assert) {
   let tiger = require('./modules/tiger');
-  test.assertEqual(tiger.name, 'tiger', 'name proprety was exported properly');
-  test.assertEqual(tiger.type, 'cat', 'property form other module exported');
+  assert.equal(tiger.name, 'tiger', 'name proprety was exported properly');
+  assert.equal(tiger.type, 'cat', 'property form other module exported');
 };
 
-exports.testDefineInoresNonFactory = function(test) {
+exports.testDefineInoresNonFactory = function(assert) {
   let mod = require('./modules/async2');
-  test.assertEqual(mod.name, 'async2', 'name proprety was exported properly');
-  test.assertNotEqual(mod.traditional2Name, 'traditional2', '1st is ignored');
+  assert.equal(mod.name, 'async2', 'name proprety was exported properly');
+  assert.ok(mod.traditional2Name !== 'traditional2', '1st is ignored');
 };
 /* Disable test that require AMD specific functionality:
 
 // define() that exports a function as the module value,
 // specifying a module name.
-exports.testDefExport = function(test) {
+exports.testDefExport = function(assert) {
   var add = require('modules/add');
-  test.assertEqual(add(1, 1), 2, 'Named define() exporting a function');
+  assert.equal(add(1, 1), 2, 'Named define() exporting a function');
 };
 
 // define() that exports function as a value, but is anonymous
-exports.testAnonDefExport = function (test) {
+exports.testAnonDefExport = function (assert) {
   var subtract = require('modules/subtract');
-  test.assertEqual(subtract(4, 2), 2,
+  assert.equal(subtract(4, 2), 2,
                    'Anonymous define() exporting a function');
 }
 
 // using require([], function () {}) to load modules.
-exports.testSimpleRequire = function (test) {
+exports.testSimpleRequire = function (assert) {
   require(['modules/blue', 'modules/orange'], function (blue, orange) {
-    test.assertEqual(blue.name, 'blue', 'Simple require for blue');
-    test.assertEqual(orange.name, 'orange', 'Simple require for orange');
-    test.assertEqual(orange.parentType, 'color',
+    assert.equal(blue.name, 'blue', 'Simple require for blue');
+    assert.equal(orange.name, 'orange', 'Simple require for orange');
+    assert.equal(orange.parentType, 'color',
                      'Simple require dependency check for orange');
   });
 }
 
 // using nested require([]) calls.
-exports.testSimpleRequireNested = function (test) {
+exports.testSimpleRequireNested = function (assert) {
   require(['modules/blue', 'modules/orange', 'modules/green'],
   function (blue, orange, green) {
 
     require(['modules/orange', 'modules/red'], function (orange, red) {
-      test.assertEqual(red.name, 'red', 'Simple require for red');
-      test.assertEqual(red.parentType, 'color',
+      assert.equal(red.name, 'red', 'Simple require for red');
+      assert.equal(red.parentType, 'color',
                        'Simple require dependency check for red');
-      test.assertEqual(blue.name, 'blue', 'Simple require for blue');
-      test.assertEqual(orange.name, 'orange', 'Simple require for orange');
-      test.assertEqual(orange.parentType, 'color',
+      assert.equal(blue.name, 'blue', 'Simple require for blue');
+      assert.equal(orange.name, 'orange', 'Simple require for orange');
+      assert.equal(orange.parentType, 'color',
                        'Simple require dependency check for orange');
-      test.assertEqual(green.name, 'green', 'Simple require for green');
-      test.assertEqual(green.parentType, 'color',
+      assert.equal(green.name, 'green', 'Simple require for green');
+      assert.equal(green.parentType, 'color',
                        'Simple require dependency check for green');
     });
 
   });
 }
 
 // requiring a traditional module, that uses async, that use traditional and
 // async, with a circular reference
-exports.testMixedCircular = function (test) {
+exports.testMixedCircular = function (assert) {
   var t = require('modules/traditional1');
-  test.assertEqual(t.name, 'traditional1', 'Testing name');
-  test.assertEqual(t.traditional2Name, 'traditional2',
+  assert.equal(t.name, 'traditional1', 'Testing name');
+  assert.equal(t.traditional2Name, 'traditional2',
                    'Testing dependent name');
-  test.assertEqual(t.traditional1Name, 'traditional1', 'Testing circular name');
-  test.assertEqual(t.async2Name, 'async2', 'Testing async2 name');
-  test.assertEqual(t.async2Traditional2Name, 'traditional2',
+  assert.equal(t.traditional1Name, 'traditional1', 'Testing circular name');
+  assert.equal(t.async2Name, 'async2', 'Testing async2 name');
+  assert.equal(t.async2Traditional2Name, 'traditional2',
                    'Testing nested traditional2 name');
 }
 
 // Testing define()(function(require) {}) with some that use exports,
 // some that use return.
-exports.testAnonExportsReturn = function (test) {
+exports.testAnonExportsReturn = function (assert) {
   var lion = require('modules/lion');
   require(['modules/tiger', 'modules/cheetah'], function (tiger, cheetah) {
-    test.assertEqual('lion', lion, 'Check lion name');
-    test.assertEqual('tiger', tiger.name, 'Check tiger name');
-    test.assertEqual('cat', tiger.type, 'Check tiger type');
-    test.assertEqual('cheetah', cheetah(), 'Check cheetah name');
+    assert.equal('lion', lion, 'Check lion name');
+    assert.equal('tiger', tiger.name, 'Check tiger name');
+    assert.equal('cat', tiger.type, 'Check tiger type');
+    assert.equal('cheetah', cheetah(), 'Check cheetah name');
   });
 }
 
 // circular dependency
-exports.testCircular = function (test) {
+exports.testCircular = function (assert) {
   var pollux = require('modules/pollux'),
       castor = require('modules/castor');
 
-  test.assertEqual(pollux.name, 'pollux', 'Pollux\'s name');
-  test.assertEqual(pollux.getCastorName(),
+  assert.equal(pollux.name, 'pollux', 'Pollux\'s name');
+  assert.equal(pollux.getCastorName(),
                    'castor', 'Castor\'s name from Pollux.');
-  test.assertEqual(castor.name, 'castor', 'Castor\'s name');
-  test.assertEqual(castor.getPolluxName(), 'pollux',
+  assert.equal(castor.name, 'castor', 'Castor\'s name');
+  assert.equal(castor.getPolluxName(), 'pollux',
                    'Pollux\'s name from Castor.');
 }
 
 // test a bad module that asks for exports but also does a define() return
-exports.testBadExportAndReturn = function (test) {
+exports.testBadExportAndReturn = function (assert) {
   var passed = false;
   try {
     var bad = require('modules/badExportAndReturn');
   } catch(e) {
     passed = /cannot use exports and also return/.test(e.toString());
   }
-  test.assertEqual(passed, true, 'Make sure exports and return fail');
+  assert.equal(passed, true, 'Make sure exports and return fail');
 }
 
 // test a bad circular dependency, where an exported value is needed, but
 // the return value happens too late, a module already asked for the exported
 // value.
-exports.testBadExportAndReturnCircular = function (test) {
+exports.testBadExportAndReturnCircular = function (assert) {
   var passed = false;
   try {
     var bad = require('modules/badFirst');
   } catch(e) {
     passed = /after another module has referenced its exported value/
              .test(e.toString());
   }
-  test.assertEqual(passed, true, 'Make sure return after an exported ' +
+  assert.equal(passed, true, 'Make sure return after an exported ' +
                                  'value is grabbed by another module fails.');
 }
 
 // only allow one define call per file.
-exports.testOneDefine = function (test) {
+exports.testOneDefine = function (assert) {
   var passed = false;
   try {
     var dupe = require('modules/dupe');
   } catch(e) {
     passed = /Only one call to define/.test(e.toString());
   }
-  test.assertEqual(passed, true, 'Only allow one define call per module');
+  assert.equal(passed, true, 'Only allow one define call per module');
 }
 
 // only allow one define call per file, testing a bad nested define call.
-exports.testOneDefineNested = function (test) {
+exports.testOneDefineNested = function (assert) {
   var passed = false;
   try {
     var dupe = require('modules/dupeNested');
   } catch(e) {
     passed = /Only one call to define/.test(e.toString());
   }
-  test.assertEqual(passed, true, 'Only allow one define call per module');
+  assert.equal(passed, true, 'Only allow one define call per module');
 }
 */
+
+require('sdk/test').run(exports);
--- a/addon-sdk/source/test/test-notifications.js
+++ b/addon-sdk/source/test/test-notifications.js
@@ -1,44 +1,46 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 const { Loader } = require('sdk/test/loader');
 
-exports.testOnClick = function (test) {
+exports.testOnClick = function (assert) {
   let [loader, mockAlertServ] = makeLoader(module);
   let notifs = loader.require("sdk/notifications");
   let data = "test data";
   let opts = {
     onClick: function (clickedData) {
-      test.assertEqual(this, notifs, "|this| should be notifications module");
-      test.assertEqual(clickedData, data,
+      assert.equal(this, notifs, "|this| should be notifications module");
+      assert.equal(clickedData, data,
                        "data passed to onClick should be correct");
     },
     data: data,
     title: "test title",
     text: "test text",
     iconURL: "test icon URL"
   };
   notifs.notify(opts);
   mockAlertServ.click();
   loader.unload();
 };
 
 // Returns [loader, mockAlertService].
-function makeLoader(test) {
+function makeLoader(module) {
   let loader = Loader(module);
   let mockAlertServ = {
     showAlertNotification: function (imageUrl, title, text, textClickable,
                                      cookie, alertListener, name) {
       this._cookie = cookie;
       this._alertListener = alertListener;
     },
     click: function () {
       this._alertListener.observe(null, "alertclickcallback", this._cookie);
     }
   };
   loader.require("sdk/notifications");
   let scope = loader.sandbox("sdk/notifications");
   scope.notify = mockAlertServ.showAlertNotification.bind(mockAlertServ);
   return [loader, mockAlertServ];
-};
+}
+
+require('sdk/test').run(exports);
--- a/addon-sdk/source/test/test-object.js
+++ b/addon-sdk/source/test/test-object.js
@@ -4,32 +4,33 @@
 'use strict';
 
 const { merge, extend, has, each } = require('sdk/util/object');
 
 let o = {
   'paper': 0,
   'rock': 1,
   'scissors': 2
-}
-
-//exports.testMerge = function(test) {}
-//exports.testExtend = function(test) {}
-
-exports.testHas = function(test) {
-  test.assertEqual(has(o, 'paper'), true, 'has correctly finds key');
-  test.assertEqual(has(o, 'rock'), true, 'has correctly finds key');
-  test.assertEqual(has(o, 'scissors'), true, 'has correctly finds key');
-  test.assertEqual(has(o, 'nope'), false, 'has correctly does not find key');
-  test.assertEqual(has(o, '__proto__'), false, 'has correctly does not find key');
-  test.assertEqual(has(o, 'isPrototypeOf'), false, 'has correctly does not find key');
 };
 
-exports.testEach = function(test) {
-  var count = 0;
+//exports.testMerge = function(assert) {}
+//exports.testExtend = function(assert) {}
+
+exports.testHas = function(assert) {
+  assert.equal(has(o, 'paper'), true, 'has correctly finds key');
+  assert.equal(has(o, 'rock'), true, 'has correctly finds key');
+  assert.equal(has(o, 'scissors'), true, 'has correctly finds key');
+  assert.equal(has(o, 'nope'), false, 'has correctly does not find key');
+  assert.equal(has(o, '__proto__'), false, 'has correctly does not find key');
+  assert.equal(has(o, 'isPrototypeOf'), false, 'has correctly does not find key');
+};
+
+exports.testEach = function(assert) {
   var keys = new Set();
   each(o, function (value, key, object) {
     keys.add(key);
-    test.assertEqual(o[key], value, 'Key and value pairs passed in');
-    test.assertEqual(o, object, 'Object passed in');
+    assert.equal(o[key], value, 'Key and value pairs passed in');
+    assert.equal(o, object, 'Object passed in');
   });
-  test.assertEqual(keys.size, 3, 'All keys have been iterated upon');
-}
+  assert.equal(keys.size, 3, 'All keys have been iterated upon');
+};
+
+require('sdk/test').run(exports);
--- a/addon-sdk/source/test/test-observer-service.js
+++ b/addon-sdk/source/test/test-observer-service.js
@@ -1,46 +1,46 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 const observers = require("sdk/deprecated/observer-service");
 const { Cc, Ci } = require("chrome");
 const { LoaderWithHookedConsole2 } = require("sdk/test/loader");
 
-exports.testUnloadAndErrorLogging = function(test) {
+exports.testUnloadAndErrorLogging = function(assert) {
   let { loader, messages } = LoaderWithHookedConsole2(module);
   var sbobsvc = loader.require("sdk/deprecated/observer-service");
 
   var timesCalled = 0;
   var cb = function(subject, data) {
     timesCalled++;
   };
   var badCb = function(subject, data) {
     throw new Error("foo");
   };
   sbobsvc.add("blarg", cb);
   observers.notify("blarg", "yo yo");
-  test.assertEqual(timesCalled, 1);
+  assert.equal(timesCalled, 1);
   sbobsvc.add("narg", badCb);
   observers.notify("narg", "yo yo");
 
-  test.assertEqual(messages[0], "console.error: " + require("sdk/self").name + ": \n");
+  assert.equal(messages[0], "console.error: " + require("sdk/self").name + ": \n");
   var lines = messages[1].split("\n");
-  test.assertEqual(lines[0], "  Message: Error: foo");
-  test.assertEqual(lines[1], "  Stack:");
+  assert.equal(lines[0], "  Message: Error: foo");
+  assert.equal(lines[1], "  Stack:");
   // Keep in mind to update "18" to the line of "throw new Error("foo")"
-  test.assert(lines[2].indexOf(module.uri + ":18") != -1);
+  assert.ok(lines[2].indexOf(module.uri + ":18") != -1);
 
   loader.unload();
   observers.notify("blarg", "yo yo");
-  test.assertEqual(timesCalled, 1);
+  assert.equal(timesCalled, 1);
 };
 
-exports.testObserverService = function(test) {
+exports.testObserverService = function(assert) {
   var ios = Cc['@mozilla.org/network/io-service;1']
             .getService(Ci.nsIIOService);
   var service = Cc["@mozilla.org/observer-service;1"].
                 getService(Ci.nsIObserverService);
   var uri = ios.newURI("http://www.foo.com", null, null);
   var timesCalled = 0;
   var lastSubject = null;
   var lastData = null;
@@ -48,30 +48,32 @@ exports.testObserverService = function(t
   var cb = function(subject, data) {
     timesCalled++;
     lastSubject = subject;
     lastData = data;
   };
 
   observers.add("blarg", cb);
   service.notifyObservers(uri, "blarg", "some data");
-  test.assertEqual(timesCalled, 1,
+  assert.equal(timesCalled, 1,
                    "observer-service.add() should call callback");
-  test.assertEqual(lastSubject, uri,
+  assert.equal(lastSubject, uri,
                    "observer-service.add() should pass subject");
-  test.assertEqual(lastData, "some data",
+  assert.equal(lastData, "some data",
                    "observer-service.add() should pass data");
 
   function customSubject() {}
   function customData() {}
   observers.notify("blarg", customSubject, customData);
-  test.assertEqual(timesCalled, 2,
+  assert.equal(timesCalled, 2,
                    "observer-service.notify() should work");
-  test.assertEqual(lastSubject, customSubject,
+  assert.equal(lastSubject, customSubject,
                    "observer-service.notify() should pass+wrap subject");
-  test.assertEqual(lastData, customData,
+  assert.equal(lastData, customData,
                    "observer-service.notify() should pass data");
 
   observers.remove("blarg", cb);
   service.notifyObservers(null, "blarg", "some data");
-  test.assertEqual(timesCalled, 2,
+  assert.equal(timesCalled, 2,
                    "observer-service.remove() should work");
 };
+
+require('sdk/test').run(exports);
--- a/addon-sdk/source/test/test-packaging.js
+++ b/addon-sdk/source/test/test-packaging.js
@@ -1,44 +1,46 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
  "use strict";
 
 var options = require("@loader/options");
 
-exports.testPackaging = function(test) {
-  test.assertEqual(options.metadata.description,
+exports.testPackaging = function(assert) {
+  assert.equal(options.metadata.description,
                    "Add-on development made easy.",
                    "packaging metadata should be available");
   try {
     options.metadata.description = 'new description';
-    test.fail('should not have been able to set options.metadata property');
+    assert.fail('should not have been able to set options.metadata property');
   }
   catch (e) {}
 
-  test.assertEqual(options.metadata.description,
+  assert.equal(options.metadata.description,
                    "Add-on development made easy.",
                    "packaging metadata should be frozen");
 
-  test.assertEqual(options.metadata.permissions['private-browsing'], undefined,
+  assert.equal(options.metadata.permissions['private-browsing'], undefined,
                    "private browsing metadata should be undefined");
-  test.assertEqual(options.metadata['private-browsing'], undefined,
+  assert.equal(options.metadata['private-browsing'], undefined,
                    "private browsing metadata should be be frozen");
-  test.assertEqual(options['private-browsing'], undefined,
+  assert.equal(options['private-browsing'], undefined,
                    "private browsing metadata should be be frozen");
 
   try {
     options.metadata['private-browsing'] = true;
-    test.fail('should not have been able to set options.metadata property');
+    assert.fail('should not have been able to set options.metadata property');
   }
   catch(e) {}
-  test.assertEqual(options.metadata['private-browsing'], undefined,
+  assert.equal(options.metadata['private-browsing'], undefined,
                    "private browsing metadata should be be frozen");
 
   try {
     options.metadata.permissions['private-browsing'] = true;
-    test.fail('should not have been able to set options.metadata.permissions property');
+    assert.fail('should not have been able to set options.metadata.permissions property');
   }
   catch (e) {}
-  test.assertEqual(options.metadata.permissions['private-browsing'], undefined,
+  assert.equal(options.metadata.permissions['private-browsing'], undefined,
                    "private browsing metadata should be be frozen");
 };
+
+require('sdk/test').run(exports);
--- a/addon-sdk/source/test/test-page-mod.js
+++ b/addon-sdk/source/test/test-page-mod.js
@@ -3,73 +3,66 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 "use strict";
 
 const { PageMod } = require("sdk/page-mod");
 const testPageMod = require("./pagemod-test-helpers").testPageMod;
 const { Loader } = require('sdk/test/loader');
 const tabs = require("sdk/tabs");
 const timer = require("sdk/timers");
-const { Cc, Ci } = require("chrome");
+const { Cc, Ci, Cu } = require("chrome");
 const { open, getFrames, getMostRecentBrowserWindow } = require('sdk/window/utils');
 const windowUtils = require('sdk/deprecated/window-utils');
 const { getTabContentWindow, getActiveTab, setTabURL, openTab, closeTab } = require('sdk/tabs/utils');
 const xulApp = require("sdk/system/xul-app");
 const { data, isPrivateBrowsingSupported } = require('sdk/self');
 const { isPrivate } = require('sdk/private-browsing');
 const { openWebpage } = require('./private-browsing/helper');
 const { isTabPBSupported, isWindowPBSupported, isGlobalPBSupported } = require('sdk/private-browsing/utils');
 const promise = require("sdk/core/promise");
 const { pb } = require('./private-browsing/helper');
 const { URL } = require("sdk/url");
 const testPageURI = require("sdk/self").data.url("test.html");
+const { waitUntil } = require("sdk/test/utils");
 
-/* XXX This can be used to delay closing the test Firefox instance for interactive
- * testing or visual inspection. This test is registered first so that it runs
- * the last. */
-exports.delay = function(test) {
-  if (false) {
-    test.waitUntilDone(60000);
-    timer.setTimeout(function() {test.done();}, 4000);
-  }
-  else {
-    test.pass();
-  }
-}
+// The following adds Debugger constructor to the global namespace.
+const { addDebuggerToGlobal } =
+  Cu.import('resource://gre/modules/jsdebugger.jsm', {});
+addDebuggerToGlobal(this);
 
 function Isolate(worker) {
   return "(" + worker + ")()";
 }
 
 /* Tests for the PageMod APIs */
 
-exports.testPageMod1 = function(test) {
-  let mods = testPageMod(test, "about:", [{
+exports.testPageMod1 = function(assert, done) {
+  let mods = testPageMod(assert, done, "about:", [{
       include: /about:/,
       contentScriptWhen: 'end',
       contentScript: 'new ' + function WorkerScope() {
         window.document.body.setAttribute("JEP-107", "worked");
       },
       onAttach: function() {
-        test.assertEqual(this, mods[0], "The 'this' object is the page mod.");
+        assert.equal(this, mods[0], "The 'this' object is the page mod.");
       }
     }],
     function(win, done) {
-      test.assertEqual(
+      assert.equal(
         win.document.body.getAttribute("JEP-107"),
         "worked",
         "PageMod.onReady test"
       );
       done();
     }
   );
 };
 
-exports.testPageMod2 = function(test) {
-  testPageMod(test, "about:", [{
+exports.testPageMod2 = function(assert, done) {
+  testPageMod(assert, done, "about:", [{
       include: "about:*",
       contentScript: [
         'new ' + function contentScript() {
           window.AUQLUE = function() { return 42; }
           try {
             window.AUQLUE()
           }
           catch(e) {
@@ -77,35 +70,35 @@ exports.testPageMod2 = function(test) {
           }
           document.documentElement.setAttribute("first", "true");
         },
         'new ' + function contentScript() {
           document.documentElement.setAttribute("second", "true");
         }
       ]
     }], function(win, done) {
-      test.assertEqual(win.document.documentElement.getAttribute("first"),
+      assert.equal(win.document.documentElement.getAttribute("first"),
                        "true",
                        "PageMod test #2: first script has run");
-      test.assertEqual(win.document.documentElement.getAttribute("second"),
+      assert.equal(win.document.documentElement.getAttribute("second"),
                        "true",
                        "PageMod test #2: second script has run");
-      test.assertEqual("AUQLUE" in win, false,
+      assert.equal("AUQLUE" in win, false,
                        "PageMod test #2: scripts get a wrapped window");
       done();
     });
 };
 
-exports.testPageModIncludes = function(test) {
+exports.testPageModIncludes = function(assert, done) {
   var asserts = [];
   function createPageModTest(include, expectedMatch) {
     // Create an 'onload' test function...
     asserts.push(function(test, win) {
       var matches = include in win.localStorage;
-      test.assert(expectedMatch ? matches : !matches,
+      assert.ok(expectedMatch ? matches : !matches,
                   "'" + include + "' match test, expected: " + expectedMatch);
     });
     // ...and corresponding PageMod options
     return {
       include: include,
       contentScript: 'new ' + function() {
         self.on("message", function(msg) {
           window.localStorage[msg] = true;
@@ -116,154 +109,154 @@ exports.testPageModIncludes = function(t
       // so we attach it on 'start'.
       contentScriptWhen: 'start',
       onAttach: function(worker) {
         worker.postMessage(this.include[0]);
       }
     };
   }
 
-  testPageMod(test, testPageURI, [
+  testPageMod(assert, done, testPageURI, [
       createPageModTest("*", false),
       createPageModTest("*.google.com", false),
       createPageModTest("resource:*", true),
       createPageModTest("resource:", false),
       createPageModTest(testPageURI, true)
     ],
     function (win, done) {
-      test.waitUntil(function () win.localStorage[testPageURI],
+      waitUntil(function () win.localStorage[testPageURI],
                      testPageURI + " page-mod to be executed")
           .then(function () {
             asserts.forEach(function(fn) {
-              fn(test, win);
+              fn(assert, win);
             });
             done();
           });
     }
     );
 };
 
-exports.testPageModErrorHandling = function(test) {
-  test.assertRaises(function() {
+exports.testPageModErrorHandling = function(assert) {
+  assert.throws(function() {
       new PageMod();
     },
-    'The `include` option must always contain atleast one rule',
+    /The `include` option must always contain atleast one rule/,
     "PageMod() throws when 'include' option is not specified.");
 };
 
 /* Tests for internal functions. */
-exports.testCommunication1 = function(test) {
+exports.testCommunication1 = function(assert, done) {
   let workerDone = false,
       callbackDone = null;
 
-  testPageMod(test, "about:", [{
+  testPageMod(assert, done, "about:", [{
       include: "about:*",
       contentScriptWhen: 'end',
       contentScript: 'new ' + function WorkerScope() {
         self.on('message', function(msg) {
           document.body.setAttribute('JEP-107', 'worked');
           self.postMessage(document.body.getAttribute('JEP-107'));
         })
       },
       onAttach: function(worker) {
         worker.on('error', function(e) {
-          test.fail('Errors where reported');
+          assert.fail('Errors where reported');
         });
         worker.on('message', function(value) {
-          test.assertEqual(
+          assert.equal(
             "worked",
             value,
             "test comunication"
           );
           workerDone = true;
           if (callbackDone)
             callbackDone();
         });
         worker.postMessage('do it!')
       }
     }],
     function(win, done) {
       (callbackDone = function() {
         if (workerDone) {
-          test.assertEqual(
+          assert.equal(
             'worked',
             win.document.body.getAttribute('JEP-107'),
             'attribute should be modified'
           );
           done();
         }
       })();
     }
   );
 };
 
-exports.testCommunication2 = function(test) {
+exports.testCommunication2 = function(assert, done) {
   let callbackDone = null,
       window;
 
-  testPageMod(test, "about:license", [{
+  testPageMod(assert, done, "about:license", [{
       include: "about:*",
       contentScriptWhen: 'start',
       contentScript: 'new ' + function WorkerScope() {
         document.documentElement.setAttribute('AUQLUE', 42);
         window.addEventListener('load', function listener() {
           self.postMessage('onload');
         }, false);
         self.on("message", function() {
           self.postMessage(document.documentElement.getAttribute("test"))
         });
       },
       onAttach: function(worker) {
         worker.on('error', function(e) {
-          test.fail('Errors where reported');
+          assert.fail('Errors where reported');
         });
         worker.on('message', function(msg) {
           if ('onload' == msg) {
-            test.assertEqual(
+            assert.equal(
               '42',
               window.document.documentElement.getAttribute('AUQLUE'),
               'PageMod scripts executed in order'
             );
             window.document.documentElement.setAttribute('test', 'changes in window');
             worker.postMessage('get window.test')
           } else {
-            test.assertEqual(
+            assert.equal(
               'changes in window',
               msg,
               'PageMod test #2: second script has run'
             )
             callbackDone();
           }
         });
       }
     }],
     function(win, done) {
       window = win;
       callbackDone = done;
     }
   );
 };
 
-exports.testEventEmitter = function(test) {
+exports.testEventEmitter = function(assert, done) {
   let workerDone = false,
       callbackDone = null;
 
-  testPageMod(test, "about:", [{
+  testPageMod(assert, done, "about:", [{
       include: "about:*",
       contentScript: 'new ' + function WorkerScope() {
         self.port.on('addon-to-content', function(data) {
           self.port.emit('content-to-addon', data);
         });
       },
       onAttach: function(worker) {
         worker.on('error', function(e) {
-          test.fail('Errors were reported : '+e);
+          assert.fail('Errors were reported : '+e);
         });
         worker.port.on('content-to-addon', function(value) {
-          test.assertEqual(
+          assert.equal(
             "worked",
             value,
             "EventEmitter API works!"
           );
           if (callbackDone)
             callbackDone();
           else
             workerDone = true;
@@ -277,181 +270,169 @@ exports.testEventEmitter = function(test
       else
         callbackDone = done;
     }
   );
 };
 
 // Execute two concurrent page mods on same document to ensure that their
 // JS contexts are different
-exports.testMixedContext = function(test) {
+exports.testMixedContext = function(assert, done) {
   let doneCallback = null;
   let messages = 0;
   let modObject = {
     include: "data:text/html;charset=utf-8,",
     contentScript: 'new ' + function WorkerScope() {
       // Both scripts will execute this,
       // context is shared if one script see the other one modification.
       let isContextShared = "sharedAttribute" in document;
       self.postMessage(isContextShared);
       document.sharedAttribute = true;
     },
     onAttach: function(w) {
       w.on("message", function (isContextShared) {
         if (isContextShared) {
-          test.fail("Page mod contexts are mixed.");
+          assert.fail("Page mod contexts are mixed.");
           doneCallback();
         }
         else if (++messages == 2) {
-          test.pass("Page mod contexts are different.");
+          assert.pass("Page mod contexts are different.");
           doneCallback();
         }
       });
     }
   };
-  testPageMod(test, "data:text/html;charset=utf-8,", [modObject, modObject],
+  testPageMod(assert, done, "data:text/html;charset=utf-8,", [modObject, modObject],
     function(win, done) {
       doneCallback = done;
     }
   );
 };
 
-exports.testHistory = function(test) {
+exports.testHistory = function(assert, done) {
   // We need a valid url in order to have a working History API.
   // (i.e do not work on data: or about: pages)
   // Test bug 679054.
   let url = require("sdk/self").data.url("test-page-mod.html");
   let callbackDone = null;
-  testPageMod(test, url, [{
+  testPageMod(assert, done, url, [{
       include: url,
       contentScriptWhen: 'end',
       contentScript: 'new ' + function WorkerScope() {
         history.pushState({}, "", "#");
         history.replaceState({foo: "bar"}, "", "#");
         self.postMessage(history.state);
       },
       onAttach: function(worker) {
         worker.on('message', function (data) {
-          test.assertEqual(JSON.stringify(data), JSON.stringify({foo: "bar"}),
+          assert.equal(JSON.stringify(data), JSON.stringify({foo: "bar"}),
                            "History API works!");
           callbackDone();
         });
       }
     }],
     function(win, done) {
       callbackDone = done;
     }
   );
 };
 
-exports.testRelatedTab = function(test) {
-  test.waitUntilDone();
-
+exports.testRelatedTab = function(assert, done) {
   let tab;
   let pageMod = new PageMod({
     include: "about:*",
     onAttach: function(worker) {
-      test.assert(!!worker.tab, "Worker.tab exists");
-      test.assertEqual(tab, worker.tab, "Worker.tab is valid");
+      assert.ok(!!worker.tab, "Worker.tab exists");
+      assert.equal(tab, worker.tab, "Worker.tab is valid");
       pageMod.destroy();
-      tab.close(function() {
-        test.done();
-      });
+      tab.close(done);
     }
   });
 
   tabs.open({
     url: "about:",
     onOpen: function onOpen(t) {
       tab = t;
     }
   });
 };
 
-exports.testRelatedTabNoRequireTab = function(test) {
-  test.waitUntilDone();
-
+exports.testRelatedTabNoRequireTab = function(assert, done) {
   let loader = Loader(module);
   let tab;
   let url = "data:text/html;charset=utf-8," + encodeURI("Test related worker tab 2");
   let { PageMod } = loader.require("sdk/page-mod");
   let pageMod = new PageMod({
     include: url,
     onAttach: function(worker) {
-      test.assertEqual(worker.tab.url, url, "Worker.tab.url is valid");
+      assert.equal(worker.tab.url, url, "Worker.tab.url is valid");
       worker.tab.close(function() {
         pageMod.destroy();
         loader.unload();
-        test.done();
+        done();
       });
     }
   });
 
   tabs.open(url);
 };
 
-exports.testRelatedTabNoOtherReqs = function(test) {
-  test.waitUntilDone();
-
+exports.testRelatedTabNoOtherReqs = function(assert, done) {
   let loader = Loader(module);
   let { PageMod } = loader.require("sdk/page-mod");
   let pageMod = new PageMod({
     include: "about:blank?testRelatedTabNoOtherReqs",
     onAttach: function(worker) {
-      test.assert(!!worker.tab, "Worker.tab exists");
+      assert.ok(!!worker.tab, "Worker.tab exists");
       pageMod.destroy();
       worker.tab.close(function() {
         worker.destroy();
         loader.unload();
-        test.done();
+        done();
       });
     }
   });
 
   tabs.open({
     url: "about:blank?testRelatedTabNoOtherReqs"
   });
 };
 
-exports.testWorksWithExistingTabs = function(test) {
-  test.waitUntilDone();
-
+exports.testWorksWithExistingTabs = function(assert, done) {
   let url = "data:text/html;charset=utf-8," + encodeURI("Test unique document");
   let { PageMod } = require("sdk/page-mod");
   tabs.open({
     url: url,
     onReady: function onReady(tab) {
       let pageModOnExisting = new PageMod({
         include: url,
         attachTo: ["existing", "top", "frame"],
         onAttach: function(worker) {
-          test.assert(!!worker.tab, "Worker.tab exists");
-          test.assertEqual(tab, worker.tab, "A worker has been created on this existing tab");
+          assert.ok(!!worker.tab, "Worker.tab exists");
+          assert.equal(tab, worker.tab, "A worker has been created on this existing tab");
 
           timer.setTimeout(function() {
             pageModOnExisting.destroy();
             pageModOffExisting.destroy();
-            tab.close(test.done.bind(test));
+            tab.close(done);
           }, 0);
         }
       });
 
       let pageModOffExisting = new PageMod({
         include: url,
         onAttach: function(worker) {
-          test.fail("pageModOffExisting page-mod should not have attached to anything");
+          assert.fail("pageModOffExisting page-mod should not have attached to anything");
         }
       });
     }
   });
 };
 
-exports.testTabWorkerOnMessage = function(test) {
-  test.waitUntilDone();
-
+exports.testTabWorkerOnMessage = function(assert, done) {
   let { browserWindows } = require("sdk/windows");
   let tabs = require("sdk/tabs");
   let { PageMod } = require("sdk/page-mod");
 
   let url1 = "data:text/html;charset=utf-8,<title>tab1</title><h1>worker1.tab</h1>";
   let url2 = "data:text/html;charset=utf-8,<title>tab2</title><h1>worker2.tab</h1>";
   let worker1 = null;
 
@@ -460,91 +441,87 @@ exports.testTabWorkerOnMessage = functio
     contentScriptWhen: "ready",
     contentScript: "self.postMessage('#1');",
     onAttach: function onAttach(worker) {
       worker.on("message", function onMessage() {
         this.tab.attach({
           contentScriptWhen: "ready",
           contentScript: "self.postMessage({ url: window.location.href, title: document.title });",
           onMessage: function onMessage(data) {
-            test.assertEqual(this.tab.url, data.url, "location is correct");
-            test.assertEqual(this.tab.title, data.title, "title is correct");
+            assert.equal(this.tab.url, data.url, "location is correct");
+            assert.equal(this.tab.title, data.title, "title is correct");
             if (this.tab.url === url1) {
               worker1 = this;
               tabs.open({ url: url2, inBackground: true });
             }
             else if (this.tab.url === url2) {
               mod.destroy();
               worker1.tab.close(function() {
                 worker1.destroy();
                 worker.tab.close(function() {
                   worker.destroy();
-                  test.done();
+                  done();
                 });
               });
             }
           }
         });
       });
     }
   });
 
   tabs.open(url1);
 };
 
-exports.testAutomaticDestroy = function(test) {
-  test.waitUntilDone();
+exports.testAutomaticDestroy = function(assert, done) {
   let loader = Loader(module);
 
   let pageMod = loader.require("sdk/page-mod").PageMod({
     include: "about:*",
     contentScriptWhen: "start",
     onAttach: function(w) {
-      test.fail("Page-mod should have been detroyed during module unload");
+      assert.fail("Page-mod should have been detroyed during module unload");
     }
   });
 
   // Unload the page-mod module so that our page mod is destroyed
   loader.unload();
 
   // Then create a second tab to ensure that it is correctly destroyed
   let tabs = require("sdk/tabs");
   tabs.open({
     url: "about:",
     onReady: function onReady(tab) {
-      test.pass("check automatic destroy");
-      tab.close(test.done.bind(test));
+      assert.pass("check automatic destroy");
+      tab.close(done);
     }
   });
-}
+};
 
-exports.testAttachToTabsOnly = function(test) {
-  test.waitUntilDone();
+exports.testAttachToTabsOnly = function(assert, done) {
 
   let { PageMod } = require('sdk/page-mod');
   let openedTab = null; // Tab opened in openTabWithIframe()
   let workerCount = 0;
 
   let mod = PageMod({
     include: 'data:text/html*',
     contentScriptWhen: 'start',
     contentScript: '',
     onAttach: function onAttach(worker) {
       if (worker.tab === openedTab) {
         if (++workerCount == 3) {
-          test.pass('Succesfully applied to tab documents and its iframe');
+          assert.pass('Succesfully applied to tab documents and its iframe');
           worker.destroy();
           mod.destroy();
-          openedTab.close(function() {
-            test.done();
-          });
+          openedTab.close(done);
         }
       }
       else {
-        test.fail('page-mod attached to a non-tab document');
+        assert.fail('page-mod attached to a non-tab document');
       }
     }
   });
 
   function openHiddenFrame() {
     console.info('Open iframe in hidden window');
     let hiddenFrames = require('sdk/frame/hidden-frame');
     let hiddenFrame = hiddenFrames.add(hiddenFrames.HiddenFrame({
@@ -603,19 +580,17 @@ exports.testAttachToTabsOnly = function(
         openedTab = tab;
       }
     });
   }
 
   openHiddenFrame();
 };
 
-exports['test111 attachTo [top]'] = function(test) {
-  test.waitUntilDone();
-
+exports['test111 attachTo [top]'] = function(assert, done) {
   let { PageMod } = require('sdk/page-mod');
 
   let subContent = '<iframe src="data:text/html;charset=utf-8,sub frame" />'
   let content = '<iframe src="data:text/html;charset=utf-8,' +
                 encodeURIComponent(subContent) + '" />';
   let topDocumentURL = 'data:text/html;charset=utf-8,' + encodeURIComponent(content)
 
   let workerCount = 0;
@@ -623,134 +598,126 @@ exports['test111 attachTo [top]'] = func
   let mod = PageMod({
     include: 'data:text/html*',
     contentScriptWhen: 'start',
     contentScript: 'self.postMessage(document.location.href);',
     attachTo: ['top'],
     onAttach: function onAttach(worker) {
       if (++workerCount == 1) {
         worker.on('message', function (href) {
-          test.assertEqual(href, topDocumentURL,
+          assert.equal(href, topDocumentURL,
                            "worker on top level document only");
           let tab = worker.tab;
           worker.destroy();
           mod.destroy();
-          tab.close(function() {
-            test.done();
-          });
+          tab.close(done);
         });
       }
       else {
-        test.fail('page-mod attached to a non-top document');
+        assert.fail('page-mod attached to a non-top document');
       }
     }
   });
 
   require('sdk/tabs').open(topDocumentURL);
 };
 
-exports['test111 attachTo [frame]'] = function(test) {
-  test.waitUntilDone();
-
+exports['test111 attachTo [frame]'] = function(assert, done) {
   let { PageMod } = require('sdk/page-mod');
 
   let subFrameURL = 'data:text/html;charset=utf-8,subframe';
   let subContent = '<iframe src="' + subFrameURL + '" />';
   let frameURL = 'data:text/html;charset=utf-8,' + encodeURIComponent(subContent);
   let content = '<iframe src="' + frameURL + '" />';
   let topDocumentURL = 'data:text/html;charset=utf-8,' + encodeURIComponent(content)
 
   let workerCount = 0, messageCount = 0;
 
   function onMessage(href) {
     if (href == frameURL)
-      test.pass("worker on first frame");
+      assert.pass("worker on first frame");
     else if (href == subFrameURL)
-      test.pass("worker on second frame");
+      assert.pass("worker on second frame");
     else
-      test.fail("worker on unexpected document: " + href);
+      assert.fail("worker on unexpected document: " + href);
     this.destroy();
     if (++messageCount == 2) {
       mod.destroy();
-      require('sdk/tabs').activeTab.close(function() {
-        test.done();
-      });
+      require('sdk/tabs').activeTab.close(done);
     }
   }
   let mod = PageMod({
     include: 'data:text/html*',
     contentScriptWhen: 'start',
     contentScript: 'self.postMessage(document.location.href);',
     attachTo: ['frame'],
     onAttach: function onAttach(worker) {
       if (++workerCount <= 2) {
         worker.on('message', onMessage);
       }
       else {
-        test.fail('page-mod attached to a non-frame document');
+        assert.fail('page-mod attached to a non-frame document');
       }
     }
   });
 
   require('sdk/tabs').open(topDocumentURL);
 };
 
-exports.testContentScriptOptionsOption = function(test) {
-  test.waitUntilDone();
-
+exports.testContentScriptOptionsOption = function(assert, done) {
   let callbackDone = null;
-  testPageMod(test, "about:", [{
+  testPageMod(assert, done, "about:", [{
       include: "about:*",
       contentScript: "self.postMessage( [typeof self.options.d, self.options] );",
       contentScriptWhen: "end",
       contentScriptOptions: {a: true, b: [1,2,3], c: "string", d: function(){ return 'test'}},
       onAttach: function(worker) {
         worker.on('message', function(msg) {
-          test.assertEqual( msg[0], 'undefined', 'functions are stripped from contentScriptOptions' );
-          test.assertEqual( typeof msg[1], 'object', 'object as contentScriptOptions' );
-          test.assertEqual( msg[1].a, true, 'boolean in contentScriptOptions' );
-          test.assertEqual( msg[1].b.join(), '1,2,3', 'array and numbers in contentScriptOptions' );
-          test.assertEqual( msg[1].c, 'string', 'string in contentScriptOptions' );
+          assert.equal( msg[0], 'undefined', 'functions are stripped from contentScriptOptions' );
+          assert.equal( typeof msg[1], 'object', 'object as contentScriptOptions' );
+          assert.equal( msg[1].a, true, 'boolean in contentScriptOptions' );
+          assert.equal( msg[1].b.join(), '1,2,3', 'array and numbers in contentScriptOptions' );
+          assert.equal( msg[1].c, 'string', 'string in contentScriptOptions' );
           callbackDone();
         });
       }
     }],
     function(win, done) {
       callbackDone = done;
     }
   );
 };
 
-exports.testPageModCss = function(test) {
-  let [pageMod] = testPageMod(test,
+exports.testPageModCss = function(assert, done) {
+  let [pageMod] = testPageMod(assert, done,
     'data:text/html;charset=utf-8,<div style="background: silver">css test</div>', [{
       include: ["*", "data:*"],
       contentStyle: "div { height: 100px; }",
       contentStyleFile:
         require("sdk/self").data.url("pagemod-css-include-file.css")
     }],
     function(win, done) {
       let div = win.document.querySelector("div");
-      test.assertEqual(
+      assert.equal(
         div.clientHeight,
         100,
         "PageMod contentStyle worked"
       );
-      test.assertEqual(
+      assert.equal(
        div.offsetHeight,
         120,
         "PageMod contentStyleFile worked"
       );
       done();
     }
   );
 };
 
-exports.testPageModCssList = function(test) {
-  let [pageMod] = testPageMod(test,
+exports.testPageModCssList = function(assert, done) {
+  let [pageMod] = testPageMod(assert, done,
     'data:text/html;charset=utf-8,<div style="width:320px; max-width: 480px!important">css test</div>', [{
       include: "data:*",
       contentStyleFile: [
         // Highlight evaluation order in this list
         "data:text/css;charset=utf-8,div { border: 1px solid black; }",
         "data:text/css;charset=utf-8,div { border: 10px solid black; }",
         // Highlight evaluation order between contentStylesheet & contentStylesheetFile
         "data:text/css;charset=utf-8s,div { height: 1000px; }",
@@ -761,77 +728,76 @@ exports.testPageModCssList = function(te
         "div { height: 10px; }",
         "div { height: 100px; }"
       ]
     }],
     function(win, done) {
       let div = win.document.querySelector("div"),
           style = win.getComputedStyle(div);
 
-      test.assertEqual(
+      assert.equal(
        div.clientHeight,
         100,
         "PageMod contentStyle list works and is evaluated after contentStyleFile"
       );
 
-      test.assertEqual(
+      assert.equal(
         div.offsetHeight,
         120,
         "PageMod contentStyleFile list works"
       );
 
-      test.assertEqual(
+      assert.equal(
         style.width,
         "320px",
         "PageMod add-on author/page author style sheet precedence works"
       );
 
-      test.assertEqual(
+      assert.equal(
         style.maxWidth,
         "480px",
         "PageMod add-on author/page author style sheet precedence with !important works"
       );
 
       done();
     }
   );
 };
 
-exports.testPageModCssDestroy = function(test) {
-  let [pageMod] = testPageMod(test,
+exports.testPageModCssDestroy = function(assert, done) {
+  let [pageMod] = testPageMod(assert, done,
     'data:text/html;charset=utf-8,<div style="width:200px">css test</div>', [{
       include: "data:*",
       contentStyle: "div { width: 100px!important; }"
     }],
 
     function(win, done) {
       let div = win.document.querySelector("div"),
           style = win.getComputedStyle(div);
 
-      test.assertEqual(
+      assert.equal(
         style.width,
         "100px",
         "PageMod contentStyle worked"
       );
 
       pageMod.destroy();
-      test.assertEqual(
+      assert.equal(
         style.width,
         "200px",
         "PageMod contentStyle is removed after destroy"
       );
 
       done();
 
     }
   );
 };
 
-exports.testPageModCssAutomaticDestroy = function(test) {
-  test.waitUntilDone();
+exports.testPageModCssAutomaticDestroy = function(assert, done) {
   let loader = Loader(module);
 
   let pageMod = loader.require("sdk/page-mod").PageMod({
     include: "data:*",
     contentStyle: "div { width: 100px!important; }"
   });
 
   tabs.open({
@@ -839,74 +805,72 @@ exports.testPageModCssAutomaticDestroy =
 
     onReady: function onReady(tab) {
       let browserWindow = windowUtils.activeBrowserWindow;
       let win = getTabContentWindow(getActiveTab(browserWindow));
 
       let div = win.document.querySelector("div");
       let style = win.getComputedStyle(div);
 
-      test.assertEqual(
+      assert.equal(
         style.width,
         "100px",
         "PageMod contentStyle worked"
       );
 
       loader.unload();
 
-      test.assertEqual(
+      assert.equal(
         style.width,
         "200px",
         "PageMod contentStyle is removed after loader's unload"
       );
 
-      tab.close(test.done.bind(test));
+      tab.close(done);
     }
   });
 };
 
 
-exports.testPageModTimeout = function(test) {
-  test.waitUntilDone();
+exports.testPageModTimeout = function(assert, done) {
   let tab = null
   let loader = Loader(module);
   let { PageMod } = loader.require("sdk/page-mod");
 
   let mod = PageMod({
     include: "data:*",
     contentScript: Isolate(function() {
       var id = setTimeout(function() {
         self.port.emit("fired", id)
       }, 10)
       self.port.emit("scheduled", id);
     }),
     onAttach: function(worker) {
       worker.port.on("scheduled", function(id) {
-        test.pass("timer was scheduled")
+        assert.pass("timer was scheduled")
         worker.port.on("fired", function(data) {
-          test.assertEqual(id, data, "timer was fired")
+          assert.equal(id, data, "timer was fired")
           tab.close(function() {
             worker.destroy()
             loader.unload()
-            test.done()
+            done()
           });
         })
       })
     }
   });
 
   tabs.open({
     url: "data:text/html;charset=utf-8,timeout",
     onReady: function($) { tab = $ }
   })
 }
 
 
-exports.testPageModcancelTimeout = function(test) {
-  test.waitUntilDone();
+exports.testPageModcancelTimeout = function(assert, done) {
   let tab = null
   let loader = Loader(module);
   let { PageMod } = loader.require("sdk/page-mod");
 
   let mod = PageMod({
     include: "data:*",
     contentScript: Isolate(function() {
       var id1 = setTimeout(function() {
@@ -914,39 +878,37 @@ exports.testPageModcancelTimeout = funct
       }, 10)
       var id2 = setTimeout(function() {
         self.port.emit("timeout")
       }, 100)
       clearTimeout(id1)
     }),
     onAttach: function(worker) {
       worker.port.on("failed", function() {
-        test.fail("cancelled timeout fired")
+        assert.fail("cancelled timeout fired")
       })
       worker.port.on("timeout", function(id) {
-        test.pass("timer was scheduled")
+        assert.pass("timer was scheduled")
         tab.close(function() {
           worker.destroy();
           mod.destroy();
           loader.unload();
-          test.done();
+          done();
         });
       })
     }
   });
 
   tabs.open({
     url: "data:text/html;charset=utf-8,cancell timeout",
     onReady: function($) { tab = $ }
   })
 }
 
-exports.testExistingOnFrames = function(test) {
-  test.waitUntilDone();
-
+exports.testExistingOnFrames = function(assert, done) {
   let subFrameURL = 'data:text/html;charset=utf-8,testExistingOnFrames-sub-frame';
   let subIFrame = '<iframe src="' + subFrameURL + '" />'
   let iFrameURL = 'data:text/html;charset=utf-8,' + encodeURIComponent(subIFrame)
   let iFrame = '<iframe src="' + iFrameURL + '" />';
   let url = 'data:text/html;charset=utf-8,' + encodeURIComponent(iFrame);
 
   // we want all urls related to the test here, and not just the iframe urls
   // because we need to fail if the test is applied to the top window url.
@@ -967,201 +929,228 @@ exports.testExistingOnFrames = function(
       attachTo: ["existing", "frame"],
       contentScriptWhen: 'ready',
       onAttach: function(worker) {
         // need to ignore urls that are not part of the test, because other
         // tests are not closing their tabs when they complete..
         if (urls.indexOf(worker.url) == -1)
           return;
 
-        test.assertNotEqual(url,
+        assert.notEqual(url,
                             worker.url,
                             'worker should not be attached to the top window');
 
         if (++counter < 2) {
           // we can rely on this order in this case because we are sure that
           // the frames being tested have completely loaded
-          test.assertEqual(iFrameURL, worker.url, '1st attach is for top frame');
+          assert.equal(iFrameURL, worker.url, '1st attach is for top frame');
         }
         else if (counter > 2) {
-          test.fail('applied page mod too many times');
+          assert.fail('applied page mod too many times');
         }
         else {
-          test.assertEqual(subFrameURL, worker.url, '2nd attach is for sub frame');
+          assert.equal(subFrameURL, worker.url, '2nd attach is for sub frame');
           // need timeout because onAttach is called before the constructor returns
           timer.setTimeout(function() {
             pagemodOnExisting.destroy();
             pagemodOffExisting.destroy();
             closeTab(tab);
-            test.done();
+            done();
           }, 0);
         }
       }
     });
 
     let pagemodOffExisting = PageMod({
       include: ["*", "data:*"],
       attachTo: ["frame"],
       contentScriptWhen: 'ready',
       onAttach: function(mod) {
-        test.fail('pagemodOffExisting page-mod should not have been attached');
+        assert.fail('pagemodOffExisting page-mod should not have been attached');
       }
     });
   }
 
   window.addEventListener("load", wait4Iframes, false);
 };
 
-exports.testIFramePostMessage = function(test) {
-  test.waitUntilDone();
+exports.testIFramePostMessage = function(assert, done) {
   let count = 0;
 
   tabs.open({
     url: data.url("test-iframe.html"),
     onReady: function(tab) {
       var worker = tab.attach({
         contentScriptFile: data.url('test-iframe.js'),
         contentScript: 'var iframePath = \'' + data.url('test-iframe-postmessage.html') + '\'',
         onMessage: function(msg) {
-          test.assertEqual(++count, 1);
-          test.assertEqual(msg.first, 'a string');
-          test.assert(msg.second[1], "array");
-          test.assertEqual(typeof msg.third, 'object');
+          assert.equal(++count, 1);
+          assert.equal(msg.first, 'a string');
+          assert.ok(msg.second[1], "array");
+          assert.equal(typeof msg.third, 'object');
 
           worker.destroy();
-          tab.close(function() test.done());
+          tab.close(done);
         }
       });
     }
   });
 };
 
-exports.testEvents = function(test) {
+exports.testEvents = function(assert, done) {
   let content = "<script>\n new " + function DocumentScope() {
     window.addEventListener("ContentScriptEvent", function () {
       window.receivedEvent = true;
     }, false);
   } + "\n</script>";
   let url = "data:text/html;charset=utf-8," + encodeURIComponent(content);
-  testPageMod(test, url, [{
+  testPageMod(assert, done, url, [{
       include: "data:*",
       contentScript: 'new ' + function WorkerScope() {
         let evt = document.createEvent("Event");
         evt.initEvent("ContentScriptEvent", true, true);
         document.body.dispatchEvent(evt);
       }
     }],
     function(win, done) {
-      test.assert(
+      assert.ok(
         win.receivedEvent,
         "Content script sent an event and document received it"
       );
       done();
     }
   );
 };
 
-exports["test page-mod on private tab"] = function (test) {
-  test.waitUntilDone();
-  let fail = test.fail.bind(test);
+exports["test page-mod on private tab"] = function (assert, done) {
+  let fail = assert.fail.bind(assert);
 
   let privateUri = "data:text/html;charset=utf-8," +
                    "<iframe src=\"data:text/html;charset=utf-8,frame\" />";
   let nonPrivateUri = "data:text/html;charset=utf-8,non-private";
 
   let pageMod = new PageMod({
     include: "data:*",
     onAttach: function(worker) {
       if (isTabPBSupported || isWindowPBSupported) {
         // When PB isn't supported, the page-mod will apply to all document
         // as all of them will be non-private
-        test.assertEqual(worker.tab.url,
+        assert.equal(worker.tab.url,
                          nonPrivateUri,
                          "page-mod should only attach to the non-private tab");
       }
 
-      test.assert(!isPrivate(worker),
+      assert.ok(!isPrivate(worker),
                   "The worker is really non-private");
-      test.assert(!isPrivate(worker.tab),
+      assert.ok(!isPrivate(worker.tab),
                   "The document is really non-private");
       pageMod.destroy();
 
       page1.close().
         then(page2.close).
-        then(test.done.bind(test), fail);
+        then(done, fail);
     }
   });
 
   let page1, page2;
   page1 = openWebpage(privateUri, true);
   page1.ready.then(function() {
     page2 = openWebpage(nonPrivateUri, false);
   }, fail);
 }
 
-exports["test page-mod on private tab in global pb"] = function (test) {
-  test.waitUntilDone();
+exports["test page-mod on private tab in global pb"] = function (assert, done) {
   if (!isGlobalPBSupported) {
-    test.pass();
-    return test.done();
+    assert.pass();
+    return done();
   }
 
   let privateUri = "data:text/html;charset=utf-8," +
                    "<iframe%20src=\"data:text/html;charset=utf-8,frame\"/>";
 
   let pageMod = new PageMod({
     include: privateUri,
     onAttach: function(worker) {
-      test.assertEqual(worker.tab.url,
+      assert.equal(worker.tab.url,
                        privateUri,
                        "page-mod should attach");
-      test.assertEqual(isPrivateBrowsingSupported,
+      assert.equal(isPrivateBrowsingSupported,
                        false,
                        "private browsing is not supported");
-      test.assert(isPrivate(worker),
+      assert.ok(isPrivate(worker),
                   "The worker is really non-private");
-      test.assert(isPrivate(worker.tab),
+      assert.ok(isPrivate(worker.tab),
                   "The document is really non-private");
       pageMod.destroy();
 
       worker.tab.close(function() {
         pb.once('stop', function() {
-          test.pass('global pb stop');
-          test.done();
+          assert.pass('global pb stop');
+          done();
         });
         pb.deactivate();
       });
     }
   });
 
   let page1;
   pb.once('start', function() {
-    test.pass('global pb start');
+    assert.pass('global pb start');
     tabs.open({ url: privateUri });
   });
   pb.activate();
 }
 
 // Bug 699450: Calling worker.tab.close() should not lead to exception
-exports.testWorkerTabClose = function(test) {
+exports.testWorkerTabClose = function(assert, done) {
   let callbackDone;
-  testPageMod(test, "about:", [{
+  testPageMod(assert, done, "about:", [{
       include: "about:",
       contentScript: '',
       onAttach: function(worker) {
         console.log("call close");
         worker.tab.close(function () {
           // On Fennec, tab is completely destroyed right after close event is
           // dispatch, so we need to wait for the next event loop cycle to
           // check for tab nulliness.
           timer.setTimeout(function () {
-            test.assert(!worker.tab,
+            assert.ok(!worker.tab,
                         "worker.tab should be null right after tab.close()");
             callbackDone();
           }, 0);
         });
       }
     }],
     function(win, done) {
       callbackDone = done;
     }
   );
 };
+
+exports.testDebugMetadata = function(assert, done) {
+  let dbg = new Debugger;
+  let globalDebuggees = [];
+  dbg.onNewGlobalObject = function(global) {
+    globalDebuggees.push(global);
+  }
+
+  let mods = testPageMod(assert, done, "about:", [{
+      include: "about:",
+      contentScriptWhen: "start",
+      contentScript: "null;",
+    }],
+    function(win, done) {
+      assert.ok(globalDebuggees.some(function(global) {
+        try {
+          let metadata = Cu.getSandboxMetadata(global.unsafeDereference());
+          return metadata && metadata.addonID && metadata.SDKContentScript;
+        } catch(e) {
+          // Some of the globals might not be Sandbox instances and thus
+          // will cause getSandboxMetadata to fail.
+          return false;
+        }
+      }), "one of the globals is a content script");
+      done();
+    }
+  );
+};
+
+require('sdk/test').run(exports);
--- a/addon-sdk/source/test/test-panel.js
+++ b/addon-sdk/source/test/test-panel.js
@@ -555,21 +555,19 @@ exports["test Automatic Destroy"] = func
   let panel = loader.require("sdk/panel").Panel({
     contentURL: "about:buildconfig",
     contentScript:
       "self.port.on('event', function() self.port.emit('event-back'));"
   });
 
   loader.unload();
 
-  panel.port.on("event-back", function () {
-    assert.fail("Panel should have been destroyed on module unload");
-  });
-  panel.port.emit("event");
-  assert.pass("check automatic destroy");
+  assert.throws(() => {
+    panel.port.emit("event");
+  }, /already have been unloaded/, "check automatic destroy");
 };
 
 exports["test Show Then Destroy"] = makeEventOrderTest({
   test: function(assert, done, expect, panel) {
     panel.show();
     expect('show', function() { panel.destroy(); }).
       then('hide', function() { done(); });
   }
@@ -937,16 +935,40 @@ exports['test nested popups'] = function
         done();
       });
     }
   });
 
   panel.show();
 };
 
+exports['test emits on url changes'] = function (assert, done) {
+  let loader = Loader(module);
+  let { Panel } = loader.require('sdk/panel');
+  let uriA = 'data:text/html;charset=utf-8,A';
+  let uriB = 'data:text/html;charset=utf-8,B';
+
+  let panel = Panel({
+    contentURL: uriA,
+    contentScript: 'new ' + function() {
+      self.port.on('hi', function() {
+        self.port.emit('bye', document.URL);
+      });
+    }
+  });
+
+  panel.contentURL = uriB;
+  panel.port.emit('hi', 'hi')
+  panel.port.on('bye', function(uri) {
+    assert.equal(uri, uriB, 'message was delivered to new uri');
+    loader.unload();
+    done();
+  });
+};
+
 if (isWindowPBSupported) {
   exports.testGetWindow = function(assert, done) {
     let activeWindow = getMostRecentBrowserWindow();
     open(null, { features: {
       toolbar: true,
       chrome: true,
       private: true
     } }).then(function(window) {
--- a/addon-sdk/source/test/test-preferences-service.js
+++ b/addon-sdk/source/test/test-preferences-service.js
@@ -5,139 +5,141 @@
 
 const prefs = require("sdk/preferences/service");
 const Branch = prefs.Branch;
 const { Cc, Ci, Cu } = require("chrome");
 const BundleService = Cc["@mozilla.org/intl/stringbundle;1"].getService(Ci.nsIStringBundleService);
 
 const specialChars = "!@#$%^&*()_-=+[]{}~`\'\"<>,./?;:";
 
-exports.testReset = function(test) {
+exports.testReset = function(assert) {
   prefs.reset("test_reset_pref");
-  test.assertEqual(prefs.has("test_reset_pref"), false);
-  test.assertEqual(prefs.isSet("test_reset_pref"), false);
+  assert.equal(prefs.has("test_reset_pref"), false);
+  assert.equal(prefs.isSet("test_reset_pref"), false);
   prefs.set("test_reset_pref", 5);
-  test.assertEqual(prefs.has("test_reset_pref"), true);
-  test.assertEqual(prefs.isSet("test_reset_pref"), true);
-  test.assertEqual(prefs.keys("test_reset_pref").toString(), "test_reset_pref");
+  assert.equal(prefs.has("test_reset_pref"), true);
+  assert.equal(prefs.isSet("test_reset_pref"), true);
+  assert.equal(prefs.keys("test_reset_pref").toString(), "test_reset_pref");
 };
 
-exports.testGetAndSet = function(test) {
+exports.testGetAndSet = function(assert) {
   let svc = Cc["@mozilla.org/preferences-service;1"].
             getService(Ci.nsIPrefService).
             getBranch(null);
   svc.setCharPref("test_set_get_pref", "a normal string");
-  test.assertEqual(prefs.get("test_set_get_pref"), "a normal string",
+  assert.equal(prefs.get("test_set_get_pref"), "a normal string",
                    "preferences-service should read from " +
                    "application-wide preferences service");
 
   prefs.set("test_set_get_pref.integer", 1);
-  test.assertEqual(prefs.get("test_set_get_pref.integer"), 1,
+  assert.equal(prefs.get("test_set_get_pref.integer"), 1,
                    "set/get integer preference should work");
 
-  test.assertEqual(
+  assert.equal(
       prefs.keys("test_set_get_pref").sort().toString(),
       ["test_set_get_pref.integer","test_set_get_pref"].sort().toString());
 
   prefs.set("test_set_get_number_pref", 42);
-  test.assertRaises(
+  assert.throws(
     function() { prefs.set("test_set_get_number_pref", 3.14159); },
-    "cannot store non-integer number: 3.14159",
+    /cannot store non-integer number: 3.14159/,
     "setting a float preference should raise an error"
   );
-  test.assertEqual(prefs.get("test_set_get_number_pref"), 42,
+  assert.equal(prefs.get("test_set_get_number_pref"), 42,
                    "bad-type write attempt should not overwrite");
 
   // 0x80000000 (no), 0x7fffffff (yes), -0x80000000 (yes), -0x80000001 (no)
-  test.assertRaises(
+  assert.throws(
     function() { prefs.set("test_set_get_number_pref", Math.pow(2, 31)); },
-    ("you cannot set the test_set_get_number_pref pref to the number " +
-     "2147483648, as number pref values must be in the signed 32-bit " +
-     "integer range -(2^31) to 2^31-1.  To store numbers outside that " +
+    new RegExp("you cannot set the test_set_get_number_pref pref to the number " +
+     "2147483648, as number pref values must be in the signed 32\\-bit " +
+     "integer range \\-\\(2\\^31\\) to 2\\^31\\-1.  To store numbers outside that " +
      "range, store them as strings."),
     "setting an int pref outside the range -(2^31) to 2^31-1 shouldn't work"
   );
-  test.assertEqual(prefs.get("test_set_get_number_pref"), 42,
+  assert.equal(prefs.get("test_set_get_number_pref"), 42,
                    "out-of-range write attempt should not overwrite 1");
   prefs.set("test_set_get_number_pref", Math.pow(2, 31)-1);
-  test.assertEqual(prefs.get("test_set_get_number_pref"), 0x7fffffff,
+  assert.equal(prefs.get("test_set_get_number_pref"), 0x7fffffff,
                    "in-range write attempt should work 1");
   prefs.set("test_set_get_number_pref", -Math.pow(2, 31));
-  test.assertEqual(prefs.get("test_set_get_number_pref"), -0x80000000,
+  assert.equal(prefs.get("test_set_get_number_pref"), -0x80000000,
                    "in-range write attempt should work 2");
-  test.assertRaises(
+  assert.throws(
     function() { prefs.set("test_set_get_number_pref", -0x80000001); },
-    ("you cannot set the test_set_get_number_pref pref to the number " +
-     "-2147483649, as number pref values must be in the signed 32-bit " +
-     "integer range -(2^31) to 2^31-1.  To store numbers outside that " +
+    new RegExp("you cannot set the test_set_get_number_pref pref to the number " +
+     "\\-2147483649, as number pref values must be in the signed 32-bit " +
+     "integer range \\-\\(2\\^31\\) to 2\\^31\\-1.  To store numbers outside that " +
      "range, store them as strings."),
     "setting an int pref outside the range -(2^31) to 2^31-1 shouldn't work"
   );
-  test.assertEqual(prefs.get("test_set_get_number_pref"), -0x80000000,
+  assert.equal(prefs.get("test_set_get_number_pref"), -0x80000000,
                    "out-of-range write attempt should not overwrite 2");
 
 
   prefs.set("test_set_get_pref.string", "foo");
-  test.assertEqual(prefs.get("test_set_get_pref.string"), "foo",
+  assert.equal(prefs.get("test_set_get_pref.string"), "foo",
                    "set/get string preference should work");
 
   prefs.set("test_set_get_pref.boolean", true);
-  test.assertEqual(prefs.get("test_set_get_pref.boolean"), true,
+  assert.equal(prefs.get("test_set_get_pref.boolean"), true,
                    "set/get boolean preference should work");
 
   prefs.set("test_set_get_unicode_pref", String.fromCharCode(960));
-  test.assertEqual(prefs.get("test_set_get_unicode_pref"),
+  assert.equal(prefs.get("test_set_get_unicode_pref"),
                    String.fromCharCode(960),
                    "set/get unicode preference should work");
 
   var unsupportedValues = [null, [], undefined];
   unsupportedValues.forEach(
     function(value) {
-      test.assertRaises(
+      assert.throws(
         function() { prefs.set("test_set_pref", value); },
-        ("can't set pref test_set_pref to value '" + value + "'; " +
+        new RegExp("can't set pref test_set_pref to value '" + value + "'; " +
          "it isn't a string, integer, or boolean"),
         "Setting a pref to " + uneval(value) + " should raise error"
       );
     });
 };
 
-exports.testPrefClass = function(test) {
+exports.testPrefClass = function(assert) {
   var branch = Branch("test_foo");
 
-  test.assertEqual(branch.test, undefined, "test_foo.test is undefined");
+  assert.equal(branch.test, undefined, "test_foo.test is undefined");
   branch.test = true;
-  test.assertEqual(branch.test, true, "test_foo.test is true");
+  assert.equal(branch.test, true, "test_foo.test is true");
   delete branch.test;
-  test.assertEqual(branch.test, undefined, "test_foo.test is undefined");
+  assert.equal(branch.test, undefined, "test_foo.test is undefined");
 };
 
-exports.testGetSetLocalized = function(test) {
+exports.testGetSetLocalized = function(assert) {
   let prefName = "general.useragent.locale";
 
   // Ensure that "general.useragent.locale" is a 'localized' pref
   let bundleURL = "chrome://global/locale/intl.properties";
   prefs.setLocalized(prefName, bundleURL);
 
   // Fetch the expected value directly from the property file
   let expectedValue = BundleService.createBundle(bundleURL).
     GetStringFromName(prefName).
     toLowerCase();
 
-  test.assertEqual(prefs.getLocalized(prefName).toLowerCase(),
+  assert.equal(prefs.getLocalized(prefName).toLowerCase(),
                    expectedValue,
                    "get localized preference");
 
   // Undo our modification
   prefs.reset(prefName);
 }
 
 // TEST: setting and getting preferences with special characters work
-exports.testSpecialChars = function(test) {
+exports.testSpecialChars = function(assert) {
   let chars = specialChars.split('');
   const ROOT = "test.";
 
   chars.forEach(function(char) {
     let rand = Math.random() + "";
     prefs.set(ROOT+char, rand);
-    test.assertEqual(prefs.get(ROOT+char), rand, "setting pref with a name that is a special char, " + char + ", worked!");
+    assert.equal(prefs.get(ROOT+char), rand, "setting pref with a name that is a special char, " + char + ", worked!");
   });
 };
+
+require('sdk/test').run(exports);
--- a/addon-sdk/source/test/test-preferences-target.js
+++ b/addon-sdk/source/test/test-preferences-target.js
@@ -5,38 +5,38 @@
 
 const { PrefsTarget } = require('sdk/preferences/event-target');
 const { get, set, reset } = require('sdk/preferences/service');
 const { Loader } = require('sdk/test/loader');
 const { setTimeout } = require('sdk/timers');
 
 const root = PrefsTarget();
 
-exports.testPrefsTarget = function(test) {
-  test.waitUntilDone();
-
+exports.testPrefsTarget = function(assert, done) {
   let loader = Loader(module);
   let pt = loader.require('sdk/preferences/event-target').PrefsTarget({});
   let name = 'test';
 
-  test.assertEqual(get(name, ''), '', 'test pref is blank');
+  assert.equal(get(name, ''), '', 'test pref is blank');
 
   pt.once(name, function() {
-    test.assertEqual(pt.prefs[name], 2, 'test pref is 2');
+    assert.equal(pt.prefs[name], 2, 'test pref is 2');
 
     pt.once(name, function() {
-      test.fail('should not have heard a pref change');
+      assert.fail('should not have heard a pref change');
     });
     loader.unload();
     root.once(name, function() {
-      test.pass('test pref was changed');
+      assert.pass('test pref was changed');
       reset(name);
 
       // NOTE: using setTimeout to make sure that the other listener had
       //       a chance to fail
       // end test
-      setTimeout(function() test.done());
+      setTimeout(done);
     });
     set(name, 3);
   });
 
   pt.prefs[name] = 2;
 };
+
+require('sdk/test').run(exports);
--- a/addon-sdk/source/test/test-private-browsing.js
+++ b/addon-sdk/source/test/test-private-browsing.js
@@ -1,15 +1,15 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 'use strict';
 
 const { Ci } = require('chrome');
-const { merge } = require('sdk/util/object');
+const { safeMerge } = require('sdk/util/object');
 const windows = require('sdk/windows').browserWindows;
 const tabs = require('sdk/tabs');
 const winUtils = require('sdk/window/utils');
 const { isWindowPrivate } = winUtils;
 const { isPrivateBrowsingSupported } = require('sdk/self');
 const { is } = require('sdk/system/xul-app');
 const { isPrivate } = require('sdk/private-browsing');
 const { getOwnerWindow } = require('sdk/private-browsing/window/utils');
@@ -20,103 +20,103 @@ const { pb } = require('./private-browsi
 const prefs = require('sdk/preferences/service');
 const { set: setPref } = require("sdk/preferences/service");
 const DEPRECATE_PREF = "devtools.errorconsole.deprecation_warnings";
 
 const kAutoStartPref = "browser.privatebrowsing.autostart";
 
 // is global pb is enabled?
 if (isGlobalPBSupported) {
-  merge(module.exports, require('./private-browsing/global'));
+  safeMerge(module.exports, require('./private-browsing/global'));
 
-  exports.testGlobalOnlyOnFirefox = function(test) {
-    test.assert(is("Firefox"), "isGlobalPBSupported is only true on Firefox");
+  exports.testGlobalOnlyOnFirefox = function(assert) {
+    assert.ok(is("Firefox"), "isGlobalPBSupported is only true on Firefox");
   }
 }
 else if (isWindowPBSupported) {
-  merge(module.exports, require('./private-browsing/windows'));
+  safeMerge(module.exports, require('./private-browsing/windows'));
 
-  exports.testPWOnlyOnFirefox = function(test) {
-    test.assert(is("Firefox"), "isWindowPBSupported is only true on Firefox");
+  exports.testPWOnlyOnFirefox = function(assert) {
+    assert.ok(is("Firefox"), "isWindowPBSupported is only true on Firefox");
   }
 }
 // only on Fennec
 else if (isTabPBSupported) {
-  merge(module.exports, require('./private-browsing/tabs'));
+  safeMerge(module.exports, require('./private-browsing/tabs'));
 
-  exports.testPTOnlyOnFennec = function(test) {
-    test.assert(is("Fennec"), "isTabPBSupported is only true on Fennec");
+  exports.testPTOnlyOnFennec = function(assert) {
+    assert.ok(is("Fennec"), "isTabPBSupported is only true on Fennec");
   }
 }
 
-exports.testIsPrivateDefaults = function(test) {
-  test.assertEqual(isPrivate(), false, 'undefined is not private');
-  test.assertEqual(isPrivate('test'), false, 'strings are not private');
-  test.assertEqual(isPrivate({}), false, 'random objects are not private');
-  test.assertEqual(isPrivate(4), false, 'numbers are not private');
-  test.assertEqual(isPrivate(/abc/), false, 'regex are not private');
-  test.assertEqual(isPrivate(function() {}), false, 'functions are not private');
+exports.testIsPrivateDefaults = function(assert) {
+  assert.equal(isPrivate(), false, 'undefined is not private');
+  assert.equal(isPrivate('test'), false, 'strings are not private');
+  assert.equal(isPrivate({}), false, 'random objects are not private');
+  assert.equal(isPrivate(4), false, 'numbers are not private');
+  assert.equal(isPrivate(/abc/), false, 'regex are not private');
+  assert.equal(isPrivate(function() {}), false, 'functions are not private');
 };
 
-exports.testWindowDefaults = function(test) {
+exports.testWindowDefaults = function(assert) {
   setPref(DEPRECATE_PREF, true);
   // Ensure that browserWindow still works while being deprecated
   let { loader, messages } = LoaderWithHookedConsole(module);
   let windows = loader.require("sdk/windows").browserWindows;
-  test.assertEqual(windows.activeWindow.isPrivateBrowsing, false,
+  assert.equal(windows.activeWindow.isPrivateBrowsing, false,
                    'window is not private browsing by default');
-  test.assertMatches(messages[0].msg, /DEPRECATED.+isPrivateBrowsing/,
+  assert.ok(/DEPRECATED.+isPrivateBrowsing/.test(messages[0].msg),
                      'isPrivateBrowsing is deprecated');
 
   let chromeWin = winUtils.getMostRecentBrowserWindow();
-  test.assertEqual(getMode(chromeWin), false);
-  test.assertEqual(isWindowPrivate(chromeWin), false);
-}
+  assert.equal(getMode(chromeWin), false);
+  assert.equal(isWindowPrivate(chromeWin), false);
+};
 
 // tests for the case where private browsing doesn't exist
-exports.testIsActiveDefault = function(test) {
-  test.assertEqual(pb.isActive, false,
+exports.testIsActiveDefault = function(assert) {
+  assert.equal(pb.isActive, false,
                    'pb.isActive returns false when private browsing isn\'t supported');
 };
 
-exports.testIsPrivateBrowsingFalseDefault = function(test) {
-  test.assertEqual(isPrivateBrowsingSupported, false,
+exports.testIsPrivateBrowsingFalseDefault = function(assert) {
+  assert.equal(isPrivateBrowsingSupported, false,
   	               'isPrivateBrowsingSupported property is false by default');
 };
 
-exports.testGetOwnerWindow = function(test) {
-  test.waitUntilDone();
-
+exports.testGetOwnerWindow = function(assert, done) {
   let window = windows.activeWindow;
   let chromeWindow = getOwnerWindow(window);
-  test.assert(chromeWindow instanceof Ci.nsIDOMWindow, 'associated window is found');
+  assert.ok(chromeWindow instanceof Ci.nsIDOMWindow, 'associated window is found');
 
   tabs.open({
     url: 'about:blank',
     isPrivate: true,
     onOpen: function(tab) {
       // test that getOwnerWindow works as expected
       if (is('Fennec')) {
-        test.assertNotStrictEqual(chromeWindow, getOwnerWindow(tab)); 
-        test.assert(getOwnerWindow(tab) instanceof Ci.nsIDOMWindow); 
+        assert.notStrictEqual(chromeWindow, getOwnerWindow(tab)); 
+        assert.ok(getOwnerWindow(tab) instanceof Ci.nsIDOMWindow); 
       }
       else {
-        test.assertStrictEqual(chromeWindow, getOwnerWindow(tab), 'associated window is the same for window and window\'s tab');
+        assert.strictEqual(chromeWindow, getOwnerWindow(tab), 'associated window is the same for window and window\'s tab');
       }
 
       // test that the tab is not private
       // private flag should be ignored by default
-      test.assert(!isPrivate(tab));
-      test.assert(!isPrivate(getOwnerWindow(tab)));
+      assert.ok(!isPrivate(tab));
+      assert.ok(!isPrivate(getOwnerWindow(tab)));
 
-      tab.close(function() test.done());
+      tab.close(done);
     }
   });
 };
 
-exports.testNewGlobalPBService = function(test) {
-  test.assertEqual(isPrivate(), false, 'isPrivate() is false by default');
+exports.testNewGlobalPBService = function(assert) {
+  assert.equal(isPrivate(), false, 'isPrivate() is false by default');
   prefs.set(kAutoStartPref, true);
-  test.assertEqual(prefs.get(kAutoStartPref, false), true, kAutoStartPref + ' is true now');
-  test.assertEqual(isPrivate(), true, 'isPrivate() is true now');
+  assert.equal(prefs.get(kAutoStartPref, false), true, kAutoStartPref + ' is true now');
+  assert.equal(isPrivate(), true, 'isPrivate() is true now');
   prefs.set(kAutoStartPref, false);
-  test.assertEqual(isPrivate(), false, 'isPrivate() is false again');
-}
+  assert.equal(isPrivate(), false, 'isPrivate() is false again');
+};
+
+require('sdk/test').run(exports);
--- a/addon-sdk/source/test/test-registry.js
+++ b/addon-sdk/source/test/test-registry.js
@@ -1,80 +1,82 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 'use strict';
 
-exports['test:add'] = function(test) {
+exports['test:add'] = function(assert) {
   function Class() {}
   let fixture = require('sdk/util/registry').Registry(Class);
   let isAddEmitted = false;
   fixture.on('add', function(item) {
-    test.assert(
+    assert.ok(
       item instanceof Class,
       'if object added is not an instance should construct instance from it'
     );
-    test.assert(
+    assert.ok(
       fixture.has(item),
       'callback is called after instance is added'
     );
-    test.assert(
+    assert.ok(
       !isAddEmitted,
       'callback should be called for the same item only once'
     );
     isAddEmitted = true;
   });
 
   let object = fixture.add({});
   fixture.add(object);
 };
 
-exports['test:remove'] = function(test) {
+exports['test:remove'] = function(assert) {
   function Class() {}
   let fixture = require('sdk/util/registry').Registry(Class);
   fixture.on('remove', function(item) {
-     test.assert(
+     assert.ok(
       item instanceof Class,
       'if object removed can be only instance of Class'
     );
-    test.assert(
+    assert.ok(
       fixture.has(item),
       'callback is called before instance is removed'
     );
-    test.assert(
+    assert.ok(
       !isRemoveEmitted,
       'callback should be called for the same item only once'
     );
     isRemoveEmitted = true;
   });
 
   fixture.remove({});
   let object = fixture.add({});
   fixture.remove(object);
   fixture.remove(object);
 };
 
-exports['test:items'] = function(test) {
+exports['test:items'] = function(assert) {
   function Class() {}
   let fixture = require('sdk/util/registry').Registry(Class),
       actual,
       times = 0;
 
   function testItem(item) {
     times ++;
-    test.assertEqual(
+    assert.equal(
       actual,
       item,
       'item should match actual item being added/removed'
     );
   }
   
   actual = fixture.add({});
 
   fixture.on('add', testItem);
   fixture.on('remove', testItem);
   
   fixture.remove(actual);
   fixture.remove(fixture.add(actual = new Class()));
-  test.assertEqual(3, times, 'should notify listeners on each call');
-}
+  assert.equal(3, times, 'should notify listeners on each call');
+};
 
+require('sdk/test').run(exports);
+
--- a/addon-sdk/source/test/test-request.js
+++ b/addon-sdk/source/test/test-request.js
@@ -16,137 +16,132 @@ if (options.parseable || options.verbose
   loader.sandbox("sdk/test/httpd").DEBUG = true;
 const { startServerAsync } = httpd;
 
 const { Cc, Ci, Cu } = require("chrome");
 const { Services } = Cu.import("resource://gre/modules/Services.jsm");
 
 // Use the profile directory for the temporary files as that will be deleted
 // when tests are complete
-const basePath = pathFor("ProfD")
+const basePath = pathFor("ProfD");
 const port = 8099;
 
 
-exports.testOptionsValidator = function(test) {
+exports.testOptionsValidator = function(assert) {
   // First, a simple test to make sure we didn't break normal functionality.
-  test.assertRaises(function () {
+  assert.throws(function () {
     Request({
       url: null
     });
-  }, 'The option "url" is invalid.');
+  }, /The option "url" is invalid./);
 
   // Next we'll have a Request that doesn't throw from c'tor, but from a setter.
   let req = Request({
     url: "http://playground.zpao.com/jetpack/request/text.php",
     onComplete: function () {}
   });
-  test.assertRaises(function () {
+  assert.throws(function () {
     req.url = 'www.mozilla.org';
-  }, 'The option "url" is invalid.');
+  }, /The option "url" is invalid/);
   // The url shouldn't have changed, so check that
-  test.assertEqual(req.url, "http://playground.zpao.com/jetpack/request/text.php");
-}
+  assert.equal(req.url, "http://playground.zpao.com/jetpack/request/text.php");
+};
 
-exports.testContentValidator = function(test) {
-  test.waitUntilDone();
-  runMultipleURLs(null, test, {
+exports.testContentValidator = function(assert, done) {
+  runMultipleURLs(null, assert, done, {
     url: "data:text/html;charset=utf-8,response",
     content: { 'key1' : null, 'key2' : 'some value' },
     onComplete: function(response) {
-      test.assertEqual(response.text, "response?key1=null&key2=some+value");
+      assert.equal(response.text, "response?key1=null&key2=some+value");
     }
   });
 };
 
 // This is a request to a file that exists.
-exports.testStatus200 = function (test) {
+exports.testStatus200 = function (assert, done) {
   let srv = startServerAsync(port, basePath);
   let content = "Look ma, no hands!\n";
   let basename = "test-request.txt"
   prepareFile(basename, content);
 
-  test.waitUntilDone();
   var req = Request({
     url: "http://localhost:" + port + "/" + basename,
     onComplete: function (response) {
-      test.assertEqual(this, req, "`this` should be request");
-      test.assertEqual(response.status, 200);
-      test.assertEqual(response.statusText, "OK");
-      test.assertEqual(response.headers["Content-Type"], "text/plain");
-      test.assertEqual(response.text, content);
-      srv.stop(function() test.done());
+      assert.equal(this, req, "`this` should be request");
+      assert.equal(response.status, 200);
+      assert.equal(response.statusText, "OK");
+      assert.equal(response.headers["Content-Type"], "text/plain");
+      assert.equal(response.text, content);
+      srv.stop(done);
     }
   }).get();
-}
+};
 
 // This tries to get a file that doesn't exist
-exports.testStatus404 = function (test) {
+exports.testStatus404 = function (assert, done) {
   var srv = startServerAsync(port, basePath);
 
-  test.waitUntilDone();
-  runMultipleURLs(srv, test, {
+  runMultipleURLs(srv, assert, done, {
     // the following URL doesn't exist
     url: "http://localhost:" + port + "/test-request-404.txt",
     onComplete: function (response) {
-      test.assertEqual(response.status, 404);
-      test.assertEqual(response.statusText, "Not Found");
+      assert.equal(response.status, 404);
+      assert.equal(response.statusText, "Not Found");
     }
   });
-}
+};
 
 // a simple file with a known header
-exports.testKnownHeader = function (test) {
+exports.testKnownHeader = function (assert, done) {
   var srv = startServerAsync(port, basePath);
 
  // Create the file that will be requested with the associated headers file
   let content = "This tests adding headers to the server's response.\n";
   let basename = "test-request-headers.txt";
   let headerContent = "x-jetpack-header: Jamba Juice\n";
   let headerBasename = "test-request-headers.txt^headers^";
   prepareFile(basename, content);
   prepareFile(headerBasename, headerContent);
 
-  test.waitUntilDone();
-  runMultipleURLs(srv, test, {
+  runMultipleURLs(srv, assert, done, {
     url: "http://localhost:" + port + "/test-request-headers.txt",
     onComplete: function (response) {
-      test.assertEqual(response.headers["x-jetpack-header"], "Jamba Juice");
+      assert.equal(response.headers["x-jetpack-header"], "Jamba Juice");
     }
   });
-}
+};
 
 // complex headers
-exports.testComplexHeader = function (test) {
+exports.testComplexHeader = function (assert, done) {
   let srv = startServerAsync(port, basePath);
 
   let basename = "test-request-complex-headers.sjs";
   let content = handleRequest.toString();
   prepareFile(basename, content);
 
   let headers = {
     "x-jetpack-header": "Jamba Juice is: delicious",
     "x-jetpack-header-2": "foo,bar",
     "x-jetpack-header-3": "sup dawg, i heard you like x, so we put a x in " +
       "yo x so you can y while you y",
     "Set-Cookie": "foo=bar\nbaz=foo"
-  }
+  };
 
-  test.waitUntilDone();
-  runMultipleURLs(srv, test, {
+  runMultipleURLs(srv, assert, done, {
     url: "http://localhost:" + port + "/test-request-complex-headers.sjs",
     onComplete: function (response) {
       for (k in headers) {
-        test.assertEqual(response.headers[k], headers[k]);
+        assert.equal(response.headers[k], headers[k]);
       }
     }
   });
-}
+};
 
 // Force Allow Third Party cookies
-exports.test3rdPartyCookies = function (test) {
+exports.test3rdPartyCookies = function (assert, done) {
   let srv = startServerAsync(port, basePath);
 
   let basename = "test-request-3rd-party-cookies.sjs";
 
   // Function to handle the requests in the server
   let content = function handleRequest(request, response) {
     var cookiePresent = request.hasHeader("Cookie");
     // If no cookie, set it
@@ -154,299 +149,253 @@ exports.test3rdPartyCookies = function (
       response.setHeader("Set-Cookie", "cookie=monster;", "true");
       response.setHeader("x-jetpack-3rd-party", "false", "true");
     } else {
       // We got the cookie, say so
       response.setHeader("x-jetpack-3rd-party", "true", "true");
     }
 
     response.write("<html><body>This tests 3rd party cookies.</body></html>");
-  }.toString()
+  }.toString();
 
   prepareFile(basename, content);
 
   // Disable the 3rd party cookies
   Services.prefs.setIntPref("network.cookie.cookieBehavior", 1);
 
-  test.waitUntilDone();
   Request({
     url: "http://localhost:" + port + "/test-request-3rd-party-cookies.sjs",
     onComplete: function (response) {
       // Check that the server created the cookie
-      test.assertEqual(response.headers['Set-Cookie'], 'cookie=monster;');
+      assert.equal(response.headers['Set-Cookie'], 'cookie=monster;');
 
       // Check it wasn't there before
-      test.assertEqual(response.headers['x-jetpack-3rd-party'], 'false');
+      assert.equal(response.headers['x-jetpack-3rd-party'], 'false');
 
       // Make a second request, and check that the server this time
       // got the cookie
       Request({
         url: "http://localhost:" + port + "/test-request-3rd-party-cookies.sjs",
         onComplete: function (response) {
-          test.assertEqual(response.headers['x-jetpack-3rd-party'], 'true');
-          srv.stop(function() test.done());
+          assert.equal(response.headers['x-jetpack-3rd-party'], 'true');
+          srv.stop(done);
         }
       }).get();
     }
   }).get();
-}
+};
 
-exports.testSimpleJSON = function (test) {
+exports.testSimpleJSON = function (assert, done) {
   let srv = startServerAsync(port, basePath);
   let json = { foo: "bar" };
   let basename = "test-request.json";
   prepareFile(basename, JSON.stringify(json));
 
-  test.waitUntilDone();
-  runMultipleURLs(srv, test, {
+  runMultipleURLs(srv, assert, done, {
     url: "http://localhost:" + port + "/" + basename,
     onComplete: function (response) {
-      assertDeepEqual(test, response.json, json);
+      assert.deepEqual(response.json, json);
     }
   });
-}
+};
 
-exports.testInvalidJSON = function (test) {
+exports.testInvalidJSON = function (assert, done) {
   let srv = startServerAsync(port, basePath);
   let basename = "test-request-invalid.json";
   prepareFile(basename, '"this": "isn\'t JSON"');
 
-  test.waitUntilDone();
-  runMultipleURLs(srv, test, {
+  runMultipleURLs(srv, assert, done, {
     url: "http://localhost:" + port + "/" + basename,
     onComplete: function (response) {
-      test.assertEqual(response.json, null);
+      assert.equal(response.json, null);
     }
   });
-}
+};
 
-exports.testHead = function (test) {
+exports.testHead = function (assert, done) {
   let srv = startServerAsync(port, basePath);
 
   srv.registerPathHandler("/test-head",
       function handle(request, response) {
     response.setHeader("Content-Type", "text/plain", false);
   });
 
-  test.waitUntilDone();
   Request({
     url: "http://localhost:" + port + "/test-head",
     onComplete: function (response) {
-      test.assertEqual(response.text, "");
-      test.assertEqual(response.statusText, "OK");
-      test.assertEqual(response.headers["Content-Type"], "text/plain");
-      srv.stop(function() test.done());
+      assert.equal(response.text, "");
+      assert.equal(response.statusText, "OK");
+      assert.equal(response.headers["Content-Type"], "text/plain");
+      srv.stop(done);
     }
   }).head();
-}
+};
 
-function runMultipleURLs (srv, test, options) {
+function runMultipleURLs (srv, assert, done, options) {
   let urls = [options.url, URL(options.url)];
   let cb = options.onComplete;
   let ran = 0;
   let onComplete = function (res) {
     cb(res);
     if (++ran === urls.length)
-      srv ? srv.stop(function () test.done()) : test.done();
-  }
+      srv ? srv.stop(done) : done();
+  };
   urls.forEach(function (url) {
     Request(extend(options, { url: url, onComplete: onComplete })).get();
   });
 }
 
 // All tests below here require a network connection. They will be commented out
 // when checked in. If you'd like to run them, simply uncomment them.
 //
 // When we have the means, these tests will be converted so that they don't
 // require an external server nor a network connection.
 
 /*
-exports.testGetWithParamsNotContent = function (test) {
-  test.waitUntilDone();
+exports.testGetWithParamsNotContent = function (assert, done) {
   Request({
     url: "http://playground.zpao.com/jetpack/request/getpost.php?foo=bar",
     onComplete: function (response) {
       let expected = {
         "POST": [],
         "GET" : { foo: "bar" }
       };
-      assertDeepEqual(test, response.json, expected);
-      test.done();
+      assert.deepEqual(response.json, expected);
+      done();
     }
   }).get();
 }
 
-exports.testGetWithContent = function (test) {
-  test.waitUntilDone();
+exports.testGetWithContent = function (assert, done) {
   Request({
     url: "http://playground.zpao.com/jetpack/request/getpost.php",
     content: { foo: "bar" },
     onComplete: function (response) {
       let expected = {
         "POST": [],
         "GET" : { foo: "bar" }
       };
-      assertDeepEqual(test, response.json, expected);
-      test.done();
+      assert.deepEqual(response.json, expected);
+      done();
     }
   }).get();
 }
 
-exports.testGetWithParamsAndContent = function (test) {
-  test.waitUntilDone();
+exports.testGetWithParamsAndContent = function (assert, done) {
   Request({
     url: "http://playground.zpao.com/jetpack/request/getpost.php?foo=bar",
     content: { baz: "foo" },
     onComplete: function (response) {
       let expected = {
         "POST": [],
         "GET" : { foo: "bar", baz: "foo" }
       };
-      assertDeepEqual(test, response.json, expected);
-      test.done();
+      assert.deepEqual(response.json, expected);
+      done();
     }
   }).get();
 }
 
-exports.testSimplePost = function (test) {
-  test.waitUntilDone();
+exports.testSimplePost = function (assert, done) {
   Request({
     url: "http://playground.zpao.com/jetpack/request/getpost.php",
     content: { foo: "bar" },
     onComplete: function (response) {
       let expected = {
         "POST": { foo: "bar" },
         "GET" : []
       };
-      assertDeepEqual(test, response.json, expected);
-      test.done();
+      assert.deepEqual(response.json, expected);
+      done();
     }
   }).post();
 }
 
-exports.testEncodedContent = function (test) {
-  test.waitUntilDone();
+exports.testEncodedContent = function (assert, done) {
   Request({
     url: "http://playground.zpao.com/jetpack/request/getpost.php",
     content: "foo=bar&baz=foo",
     onComplete: function (response) {
       let expected = {
         "POST": [],
         "GET" : { foo: "bar", baz: "foo" }
       };
-      assertDeepEqual(test, response.json, expected);
-      test.done();
+      assert.deepEqual(response.json, expected);
+      done();
     }
   }).get();
 }
 
-exports.testEncodedContentWithSpaces = function (test) {
-  test.waitUntilDone();
+exports.testEncodedContentWithSpaces = function (assert, done) {
   Request({
     url: "http://playground.zpao.com/jetpack/request/getpost.php",
     content: "foo=bar+hop!&baz=foo",
     onComplete: function (response) {
       let expected = {
         "POST": [],
         "GET" : { foo: "bar hop!", baz: "foo" }
       };
-      assertDeepEqual(test, response.json, expected);
-      test.done();
+      assert.deepEqual(response.json, expected);
+      done();
     }
   }).get();
 }
 
-exports.testGetWithArray = function (test) {
-  test.waitUntilDone();
+exports.testGetWithArray = function (assert, done) {
   Request({
     url: "http://playground.zpao.com/jetpack/request/getpost.php",
     content: { foo: [1, 2], baz: "foo" },
     onComplete: function (response) {
       let expected = {
         "POST": [],
         "GET" : { foo: [1, 2], baz: "foo" }
       };
-      assertDeepEqual(test, response.json, expected);
-      test.done();
+      assert.deepEqual(response.json, expected);
+      done();
     }
   }).get();
 }
 
-exports.testGetWithNestedArray = function (test) {
-  test.waitUntilDone();
+exports.testGetWithNestedArray = function (assert, done) {
   Request({
     url: "http://playground.zpao.com/jetpack/request/getpost.php",
     content: { foo: [1, 2, [3, 4]], bar: "baz" },
     onComplete: function (response) {
       let expected = {
         "POST": [],
         "GET" : this.content
       };
-      assertDeepEqual(test, response.json, expected);
-      test.done();
+      assert.deepEqual(response.json, expected);
+      done();
     }
   }).get();
 }
 
-exports.testGetWithNestedArray = function (test) {
-  test.waitUntilDone();
+exports.testGetWithNestedArray = function (assert, done) {
   let request = Request({
     url: "http://playground.zpao.com/jetpack/request/getpost.php",
     content: {
       foo: [1, 2, {
         omg: "bbq",
         "all your base!": "are belong to us"
       }],
       bar: "baz"
     },
     onComplete: function (response) {
       let expected = {
         "POST": [],
         "GET" : request.content
       };
-      assertDeepEqual(test, response.json, expected);
-      test.done();
+      assert.deepEqual(response.json, expected);
+      done();
     }
   }).get();
 }
 */
 
-// This is not a proper testing for deep equal, but it's good enough for my uses
-// here. It will do type coercion to check equality, but that's good here. Data
-// coming from the server will be stringified and so "0" should be equal to 0.
-function assertDeepEqual(test, obj1, obj2, msg) {
-  function equal(o1, o2) {
-    // cover our non-object cases well enough
-    if (o1 == o2)
-      return true;
-    if (typeof(o1) != typeof(o2))
-      return false;
-    if (typeof(o1) != "object")
-      return o1 == o2;
-
-    let e = true;
-    for (let key in o1) {
-      let val = o1[key];
-      e = e && key in o2 && equal(o2[key], val);
-      if (!e)
-        break;
-    }
-    for (let key in o2) {
-      let val = o2[key]
-      e = e && key in o1 && equal(o1[key], val);
-      if (!e)
-        break;
-    }
-    return e;
-  }
-  msg = msg || "objects not equal - " + JSON.stringify(obj1) + " != " +
-               JSON.stringify(obj2);
-  test.assert(equal(obj1, obj2), msg);
-}
-
 function prepareFile(basename, content) {
   let filePath = file.join(basePath, basename);
   let fileStream = file.open(filePath, 'w');
   fileStream.write(content);
   fileStream.close();
 }
 
 // Helper function for testComplexHeaders
@@ -464,8 +413,9 @@ function handleRequest(request, response
 
   // Test that multiple cookies work
   response.setHeader("Set-Cookie", "foo=bar", "true");
   response.setHeader("Set-Cookie", "baz=foo", "true");
 
   response.write("<html><body>This file tests more complex headers.</body></html>");
 }
 
+require('sdk/test').run(exports);
--- a/addon-sdk/source/test/test-sandbox.js
+++ b/addon-sdk/source/test/test-sandbox.js
@@ -1,16 +1,21 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 const { sandbox, load, evaluate } = require('sdk/loader/sandbox');
 const xulApp = require("sdk/system/xul-app");
 const fixturesURI = module.uri.split('test-sandbox.js')[0] + 'fixtures/';
 
+// The following adds Debugger constructor to the global namespace.
+const { Cu } = require('chrome');
+const { addDebuggerToGlobal } =
+  Cu.import('resource://gre/modules/jsdebugger.jsm', {});
+addDebuggerToGlobal(this);
 
 exports['test basics'] = function(assert) {
   let fixture = sandbox('http://example.com');
   assert.equal(evaluate(fixture, 'var a = 1;'), undefined,
                'returns expression value');
   assert.equal(evaluate(fixture, 'b = 2;'), 2,
                'returns expression value');
   assert.equal(fixture.b, 2, 'global is defined as property');
@@ -113,9 +118,23 @@ exports['test load script with complex c
 exports['test load script with data: URL and complex char'] = function(assert) {
   let code = "var chars = 'გამარჯობა';";
   let fixture = sandbox();
   load(fixture, "data:," + encodeURIComponent(code));
 
   assert.equal(fixture.chars, 'გამარჯობა', 'complex chars were loaded correctly');
 };
 
+exports['test metadata']  = function(assert) {
+  let dbg = new Debugger();
+  dbg.onNewGlobalObject = function(global) {
+    let metadata = Cu.getSandboxMetadata(global.unsafeDereference());
+    assert.ok(metadata, 'this global has attached metadata');
+    assert.equal(metadata.addonID, self.id, 'addon ID is set');
+
+    dbg.onNewGlobalObject = undefined;
+  }
+
+  let fixture = sandbox();
+  let self = require('sdk/self');
+}
+
 require('test').run(exports);
--- a/addon-sdk/source/test/test-set-exports.js
+++ b/addon-sdk/source/test/test-set-exports.js
@@ -1,35 +1,37 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 let four = require("./modules/exportsEquals");
-exports.testExportsEquals = function(test) {
-  test.assertEqual(four, 4);
-}
+exports.testExportsEquals = function(assert) {
+  assert.equal(four, 4);
+};
 
 /* TODO: Discuss idea of dropping support for this feature that was alternative
          to `module.exports = ..` that failed.
 let five = require("./modules/setExports");
-exports.testSetExports = function(test) {
-  test.assertEqual(five, 5);
+exports.testSetExports = function(assert) {
+  assert.equal(five, 5);
 }
 
-exports.testDupeSetExports = function(test) {
+exports.testDupeSetExports = function(assert) {
   var passed = false;
   try {
     var dupe = require('./modules/dupeSetExports');
   } catch(e) {
     passed = /define\(\) was used, so module\.exports= and module\.setExports\(\) may not be used/.test(e.toString());
   }
-  test.assertEqual(passed, true, 'define() or setExports(), not both');
+  assert.equal(passed, true, 'define() or setExports(), not both');
 }
 */
 
-exports.testModule = function(test) {
+exports.testModule = function(assert) {
   // module.id is not cast in stone yet. In the future, it may include the
   // package name, or may possibly be a/ URL of some sort. For now, it's a
   // URL that starts with resource: and ends with this module name, but the
   // part in between varies depending upon how the test is run.
   var found = /test-set-exports$/.test(module.id);
-  test.assertEqual(found, true, module.id+" ends with test-set-exports.js");
-}
+  assert.equal(found, true, module.id+" ends with test-set-exports.js");
+};
+
+require('sdk/test').run(exports);
--- a/addon-sdk/source/test/test-simple-prefs.js
+++ b/addon-sdk/source/test/test-simple-prefs.js
@@ -7,100 +7,98 @@ const { Loader } = require("sdk/test/loa
 const { setTimeout } = require("sdk/timers");
 const { notify } = require("sdk/deprecated/observer-service");
 const { id } = require("sdk/self");
 const simplePrefs = require("sdk/simple-prefs");
 const { prefs: sp } = simplePrefs;
 
 const specialChars = "!@#$%^&*()_-=+[]{}~`\'\"<>,./?;:";
 
-exports.testIterations = function(test) {
+exports.testIterations = function(assert) {
   sp["test"] = true;
   sp["test.test"] = true;
   let prefAry = [];
   for (var name in sp ) {
     prefAry.push(name);
   }
-  test.assert("test" in sp);
-  test.assert(!sp.getPropertyDescriptor);
-  test.assert(Object.prototype.hasOwnProperty.call(sp, "test"));
-  test.assertEqual(["test", "test.test"].toString(), prefAry.sort().toString(), "for (x in y) part 1/2 works");
-  test.assertEqual(["test", "test.test"].toString(), Object.keys(sp).sort().toString(), "Object.keys works");
+  assert.ok("test" in sp);
+  assert.ok(!sp.getPropertyDescriptor);
+  assert.ok(Object.prototype.hasOwnProperty.call(sp, "test"));
+  assert.equal(["test", "test.test"].toString(), prefAry.sort().toString(), "for (x in y) part 1/2 works");
+  assert.equal(["test", "test.test"].toString(), Object.keys(sp).sort().toString(), "Object.keys works");
 
   delete sp["test"];
   delete sp["test.test"];
   let prefAry = [];
   for (var name in sp ) {
     prefAry.push(name);
   }
-  test.assertEqual([].toString(), prefAry.toString(), "for (x in y) part 2/2 works");
+  assert.equal([].toString(), prefAry.toString(), "for (x in y) part 2/2 works");
 }
 
-exports.testSetGetBool = function(test) {
-  test.assertEqual(sp.test, undefined, "Value should not exist");
+exports.testSetGetBool = function(assert) {
+  assert.equal(sp.test, undefined, "Value should not exist");
   sp.test = true;
-  test.assert(sp.test, "Value read should be the value previously set");
+  assert.ok(sp.test, "Value read should be the value previously set");
 };
 
 // TEST: setting and getting preferences with special characters work
-exports.testSpecialChars = function(test) {
+exports.testSpecialChars = function(assert) {
   let chars = specialChars.split("");
   let len = chars.length;
 
   let count = 0;
   chars.forEach(function(char) {
     let rand = Math.random() + "";
     simplePrefs.on(char, function onPrefChanged() {
       simplePrefs.removeListener(char, onPrefChanged);
-      test.assertEqual(sp[char], rand, "setting pref with a name that is a special char, " + char + ", worked!");
+      assert.equal(sp[char], rand, "setting pref with a name that is a special char, " + char + ", worked!");
 
       // end test
       if (++count == len)
         test.done();
     })
     sp[char] = rand;
   });
 };
 
-exports.testSetGetInt = function(test) {
-  test.assertEqual(sp["test-int"], undefined, "Value should not exist");
+exports.testSetGetInt = function(assert) {
+  assert.equal(sp["test-int"], undefined, "Value should not exist");
   sp["test-int"] = 1;
-  test.assertEqual(sp["test-int"], 1, "Value read should be the value previously set");
+  assert.equal(sp["test-int"], 1, "Value read should be the value previously set");
 };
 
-exports.testSetComplex = function(test) {
+exports.testSetComplex = function(assert) {
   try {
     sp["test-complex"] = {test: true};
-    test.fail("Complex values are not allowed");
+    assert.fail("Complex values are not allowed");
   }
   catch (e) {
-    test.pass("Complex values are not allowed");
+    assert.pass("Complex values are not allowed");
   }
 };
 
-exports.testSetGetString = function(test) {
-  test.assertEqual(sp["test-string"], undefined, "Value should not exist");
+exports.testSetGetString = function(assert) {
+  assert.equal(sp["test-string"], undefined, "Value should not exist");
   sp["test-string"] = "test";
-  test.assertEqual(sp["test-string"], "test", "Value read should be the value previously set");
+  assert.equal(sp["test-string"], "test", "Value read should be the value previously set");
 };
 
-exports.testHasAndRemove = function(test) {
+exports.testHasAndRemove = function(assert) {
   sp.test = true;
-  test.assert(("test" in sp), "Value exists");
+  assert.ok(("test" in sp), "Value exists");
   delete sp.test;
-  test.assertEqual(sp.test, undefined, "Value should be undefined");
+  assert.equal(sp.test, undefined, "Value should be undefined");
 };
 
-exports.testPrefListener = function(test) {
-  test.waitUntilDone();
-
+exports.testPrefListener = function(assert, done) {
   let listener = function(prefName) {
     simplePrefs.removeListener('test-listener', listener);
-    test.assertEqual(prefName, "test-listen", "The prefs listener heard the right event");
-    test.done();
+    assert.equal(prefName, "test-listen", "The prefs listener heard the right event");
+    done();
   };
 
   simplePrefs.on("test-listen", listener);
 
   sp["test-listen"] = true;
 
   // Wildcard listen
   let toSet = ['wildcard1','wildcard.pref2','wildcard.even.longer.test'];
@@ -111,121 +109,116 @@ exports.testPrefListener = function(test
   };
 
   simplePrefs.on('',wildlistener);
 
   toSet.forEach(function(pref) {
     sp[pref] = true;
   });
 
-  test.assert((observed.length == 3 && toSet.length == 3),
+  assert.ok((observed.length == 3 && toSet.length == 3),
       "Wildcard lengths inconsistent" + JSON.stringify([observed.length, toSet.length]));
 
   toSet.forEach(function(pref,ii) {
-    test.assertEqual(observed[ii], pref, "Wildcard observed " + pref);
+    assert.equal(observed[ii], pref, "Wildcard observed " + pref);
   });
 
   simplePrefs.removeListener('',wildlistener);
 
 };
 
-exports.testBtnListener = function(test) {
-  test.waitUntilDone();
-
+exports.testBtnListener = function(assert, done) {
   let name = "test-btn-listen";
   simplePrefs.on(name, function listener() {
     simplePrefs.removeListener(name, listener);
-    test.pass("Button press event was heard");
-    test.done();
+    assert.pass("Button press event was heard");
+    done();
   });
   notify((id + "-cmdPressed"), "", name);
 };
 
-exports.testPrefRemoveListener = function(test) {
-  test.waitUntilDone();
-
+exports.testPrefRemoveListener = function(assert, done) {
   let counter = 0;
 
   let listener = function() {
-    test.pass("The prefs listener was not removed yet");
+    assert.pass("The prefs listener was not removed yet");
 
     if (++counter > 1)
-      test.fail("The prefs listener was not removed");
+      assert.fail("The prefs listener was not removed");
 
     simplePrefs.removeListener("test-listen2", listener);
 
     sp["test-listen2"] = false;
 
     setTimeout(function() {
-      test.pass("The prefs listener was removed");
-      test.done();
+      assert.pass("The prefs listener was removed");
+      done();
     }, 250);
   };
 
   simplePrefs.on("test-listen2", listener);
 
   // emit change
   sp["test-listen2"] = true;
 };
 
 // Bug 710117: Test that simple-pref listeners are removed on unload
-exports.testPrefUnloadListener = function(test) {
-  test.waitUntilDone();
-
+exports.testPrefUnloadListener = function(assert, done) {
   let loader = Loader(module);
   let sp = loader.require("sdk/simple-prefs");
   let counter = 0;
 
   let listener = function() {
-    test.assertEqual(++counter, 1, "This listener should only be called once");
+    assert.equal(++counter, 1, "This listener should only be called once");
 
     loader.unload();
 
     // this may not execute after unload, but definitely shouldn't fire listener
     sp.prefs["test-listen3"] = false;
     // this should execute, but also definitely shouldn't fire listener
     require("sdk/simple-prefs").prefs["test-listen3"] = false;
 
-    test.done();
+    done();
   };
 
   sp.on("test-listen3", listener);
 
   // emit change
   sp.prefs["test-listen3"] = true;
 };
 
 
 // Bug 710117: Test that simple-pref listeners are removed on unload
-exports.testPrefUnloadWildcardListener = function(test) {
-  test.waitUntilDone();
+exports.testPrefUnloadWildcardListener = function(assert, done) {
   let testpref = "test-wildcard-unload-listener";
   let loader = Loader(module);
   let sp = loader.require("sdk/simple-prefs");
   let counter = 0;
 
   let listener = function() {
-    test.assertEqual(++counter, 1, "This listener should only be called once");
+    assert.equal(++counter, 1, "This listener should only be called once");
 
     loader.unload();
 
     // this may not execute after unload, but definitely shouldn't fire listener
     sp.prefs[testpref] = false;
     // this should execute, but also definitely shouldn't fire listener
     require("sdk/simple-prefs").prefs[testpref] = false;
 
-    test.done();
+    done();
   };
 
   sp.on("", listener);
   // emit change
   sp.prefs[testpref] = true;
 };
 
 
 // Bug 732919 - JSON.stringify() fails on simple-prefs.prefs
-exports.testPrefJSONStringification = function(test) {
+exports.testPrefJSONStringification = function(assert) {
   var sp = require("sdk/simple-prefs").prefs;
-  test.assertEqual(
+  assert.equal(
       Object.keys(sp).join(),
       Object.keys(JSON.parse(JSON.stringify(sp))).join(),
       "JSON stringification should work.");
 };
+
+require('sdk/test').run(exports);
--- a/addon-sdk/source/test/test-simple-storage.js
+++ b/addon-sdk/source/test/test-simple-storage.js
@@ -18,292 +18,284 @@ let storeFile = Cc["@mozilla.org/file/di
 storeFile.append("jetpack");
 storeFile.append(id);
 storeFile.append("simple-storage");
 storeFile.append("store.json");
 let storeFilename = storeFile.path;
 
 function manager(loader) loader.sandbox("sdk/simple-storage").manager;
 
-exports.testSetGet = function (test) {
-  test.waitUntilDone();
-
+exports.testSetGet = function (assert, done) {
   // Load the module once, set a value.
   let loader = Loader(module);
   let ss = loader.require("sdk/simple-storage");
   manager(loader).jsonStore.onWrite = function (storage) {
-    test.assert(file.exists(storeFilename), "Store file should exist");
+    assert.ok(file.exists(storeFilename), "Store file should exist");
 
     // Load the module again and make sure the value stuck.
     loader = Loader(module);
     ss = loader.require("sdk/simple-storage");
     manager(loader).jsonStore.onWrite = function (storage) {
       file.remove(storeFilename);
-      test.done();
+      done();
     };
-    test.assertEqual(ss.storage.foo, val, "Value should persist");
+    assert.equal(ss.storage.foo, val, "Value should persist");
     loader.unload();
   };
   let val = "foo";
   ss.storage.foo = val;
-  test.assertEqual(ss.storage.foo, val, "Value read should be value set");
+  assert.equal(ss.storage.foo, val, "Value read should be value set");
   loader.unload();
 };
 
-exports.testSetGetRootArray = function (test) {
-  setGetRoot(test, [1, 2, 3], function (arr1, arr2) {
+exports.testSetGetRootArray = function (assert, done) {
+  setGetRoot(assert, done, [1, 2, 3], function (arr1, arr2) {
     if (arr1.length !== arr2.length)
       return false;
     for (let i = 0; i < arr1.length; i++) {
       if (arr1[i] !== arr2[i])
         return false;
     }
     return true;
   });
 };
 
-exports.testSetGetRootBool = function (test) {
-  setGetRoot(test, true);
+exports.testSetGetRootBool = function (assert, done) {
+  setGetRoot(assert, done, true);
 };
 
-exports.testSetGetRootFunction = function (test) {
-  setGetRootError(test, function () {},
+exports.testSetGetRootFunction = function (assert, done) {
+  setGetRootError(assert, done, function () {},
                   "Setting storage to a function should fail");
 };
 
-exports.testSetGetRootNull = function (test) {
-  setGetRoot(test, null);
+exports.testSetGetRootNull = function (assert, done) {
+  setGetRoot(assert, done, null);
 };
 
-exports.testSetGetRootNumber = function (test) {
-  setGetRoot(test, 3.14);
+exports.testSetGetRootNumber = function (assert, done) {
+  setGetRoot(assert, done, 3.14);
 };
 
-exports.testSetGetRootObject = function (test) {
-  setGetRoot(test, { foo: 1, bar: 2 }, function (obj1, obj2) {
+exports.testSetGetRootObject = function (assert, done) {
+  setGetRoot(assert, done, { foo: 1, bar: 2 }, function (obj1, obj2) {
     for (let prop in obj1) {
       if (!(prop in obj2) || obj2[prop] !== obj1[prop])
         return false;
     }
     for (let prop in obj2) {
       if (!(prop in obj1) || obj1[prop] !== obj2[prop])
         return false;
     }
     return true;
   });
 };
 
-exports.testSetGetRootString = function (test) {
-  setGetRoot(test, "sho' 'nuff");
+exports.testSetGetRootString = function (assert, done) {
+  setGetRoot(assert, done, "sho' 'nuff");
 };
 
-exports.testSetGetRootUndefined = function (test) {
-  setGetRootError(test, undefined, "Setting storage to undefined should fail");
+exports.testSetGetRootUndefined = function (assert, done) {
+  setGetRootError(assert, done, undefined, "Setting storage to undefined should fail");
 };
 
-exports.testEmpty = function (test) {
+exports.testEmpty = function (assert) {
   let loader = Loader(module);
   let ss = loader.require("sdk/simple-storage");
   loader.unload();
-  test.assert(!file.exists(storeFilename), "Store file should not exist");
+  assert.ok(!file.exists(storeFilename), "Store file should not exist");
 };
 
-exports.testMalformed = function (test) {
+exports.testMalformed = function (assert) {
   let stream = file.open(storeFilename, "w");
   stream.write("i'm not json");
   stream.close();
   let loader = Loader(module);
   let ss = loader.require("sdk/simple-storage");
   let empty = true;
   for (let key in ss.storage) {
     empty = false;
     break;
   }
-  test.assert(empty, "Malformed storage should cause root to be empty");
+  assert.ok(empty, "Malformed storage should cause root to be empty");
   loader.unload();
 };
 
 // Go over quota and handle it by listener.
-exports.testQuotaExceededHandle = function (test) {
-  test.waitUntilDone();
+exports.testQuotaExceededHandle = function (assert, done) {
   prefs.set(QUOTA_PREF, 18);
 
   let loader = Loader(module);
   let ss = loader.require("sdk/simple-storage");
   ss.on("OverQuota", function () {
-    test.pass("OverQuota was emitted as expected");
-    test.assertEqual(this, ss, "`this` should be simple storage");
+    assert.pass("OverQuota was emitted as expected");
+    assert.equal(this, ss, "`this` should be simple storage");
     ss.storage = { x: 4, y: 5 };
 
     manager(loader).jsonStore.onWrite = function () {
       loader = Loader(module);
       ss = loader.require("sdk/simple-storage");
       let numProps = 0;
       for (let prop in ss.storage)
         numProps++;
-      test.assert(numProps, 2,
+      assert.ok(numProps, 2,
                   "Store should contain 2 values: " + ss.storage.toSource());
-      test.assertEqual(ss.storage.x, 4, "x value should be correct");
-      test.assertEqual(ss.storage.y, 5, "y value should be correct");
+      assert.equal(ss.storage.x, 4, "x value should be correct");
+      assert.equal(ss.storage.y, 5, "y value should be correct");
       manager(loader).jsonStore.onWrite = function (storage) {
         prefs.reset(QUOTA_PREF);
-        test.done();
+        done();
       };
       loader.unload();
     };
     loader.unload();
   });
   // This will be JSON.stringify()ed to: {"a":1,"b":2,"c":3} (19 bytes)
   ss.storage = { a: 1, b: 2, c: 3 };
   manager(loader).jsonStore.write();
 };
 
 // Go over quota but don't handle it.  The last good state should still persist.
-exports.testQuotaExceededNoHandle = function (test) {
-  test.waitUntilDone();
+exports.testQuotaExceededNoHandle = function (assert, done) {
   prefs.set(QUOTA_PREF, 5);
 
   let loader = Loader(module);
   let ss = loader.require("sdk/simple-storage");
 
   manager(loader).jsonStore.onWrite = function (storage) {
     loader = Loader(module);
     ss = loader.require("sdk/simple-storage");
-    test.assertEqual(ss.storage, val,
+    assert.equal(ss.storage, val,
                      "Value should have persisted: " + ss.storage);
     ss.storage = "some very long string that is very long";
     ss.on("OverQuota", function () {
-      test.pass("OverQuota emitted as expected");
+      assert.pass("OverQuota emitted as expected");
       manager(loader).jsonStore.onWrite = function () {
-        test.fail("Over-quota value should not have been written");
+        assert.fail("Over-quota value should not have been written");
       };
       loader.unload();
 
       loader = Loader(module);
       ss = loader.require("sdk/simple-storage");
-      test.assertEqual(ss.storage, val,
+      assert.equal(ss.storage, val,
                        "Over-quota value should not have been written, " +
                        "old value should have persisted: " + ss.storage);
       loader.unload();
       prefs.reset(QUOTA_PREF);
-      test.done();
+      done();
     });
     manager(loader).jsonStore.write();
   };
 
   let val = "foo";
   ss.storage = val;
   loader.unload();
 };
 
-exports.testQuotaUsage = function (test) {
-  test.waitUntilDone();
-
+exports.testQuotaUsage = function (assert, done) {
   let quota = 21;
   prefs.set(QUOTA_PREF, quota);
 
   let loader = Loader(module);
   let ss = loader.require("sdk/simple-storage");
 
   // {"a":1} (7 bytes)
   ss.storage = { a: 1 };
-  test.assertEqual(ss.quotaUsage, 7 / quota, "quotaUsage should be correct");
+  assert.equal(ss.quotaUsage, 7 / quota, "quotaUsage should be correct");
 
   // {"a":1,"bb":2} (14 bytes)
   ss.storage = { a: 1, bb: 2 };
-  test.assertEqual(ss.quotaUsage, 14 / quota, "quotaUsage should be correct");
+  assert.equal(ss.quotaUsage, 14 / quota, "quotaUsage should be correct");
 
   // {"a":1,"bb":2,"cc":3} (21 bytes)
   ss.storage = { a: 1, bb: 2, cc: 3 };
-  test.assertEqual(ss.quotaUsage, 21 / quota, "quotaUsage should be correct");
+  assert.equal(ss.quotaUsage, 21 / quota, "quotaUsage should be correct");
 
   manager(loader).jsonStore.onWrite = function () {
     prefs.reset(QUOTA_PREF);
-    test.done();
+    done();
   };
   loader.unload();
 };
 
-exports.testUninstall = function (test) {
-  test.waitUntilDone();
+exports.testUninstall = function (assert, done) {
   let loader = Loader(module);
   let ss = loader.require("sdk/simple-storage");
   manager(loader).jsonStore.onWrite = function () {
-    test.assert(file.exists(storeFilename), "Store file should exist");
+    assert.ok(file.exists(storeFilename), "Store file should exist");
 
     loader = Loader(module);
     ss = loader.require("sdk/simple-storage");
     loader.unload("uninstall");
-    test.assert(!file.exists(storeFilename), "Store file should be removed");
-    test.done();
+    assert.ok(!file.exists(storeFilename), "Store file should be removed");
+    done();
   };
   ss.storage.foo = "foo";
   loader.unload();
 };
 
-exports.testSetNoSetRead = function (test) {
-  test.waitUntilDone();
-
+exports.testSetNoSetRead = function (assert, done) {
   // Load the module, set a value.
   let loader = Loader(module);
   let ss = loader.require("sdk/simple-storage");
   manager(loader).jsonStore.onWrite = function (storage) {
-    test.assert(file.exists(storeFilename), "Store file should exist");
+    assert.ok(file.exists(storeFilename), "Store file should exist");
 
     // Load the module again but don't access ss.storage.
     loader = Loader(module);
     ss = loader.require("sdk/simple-storage");
     manager(loader).jsonStore.onWrite = function (storage) {
-      test.fail("Nothing should be written since `storage` was not accessed.");
+      assert.fail("Nothing should be written since `storage` was not accessed.");
     };
     loader.unload();
 
     // Load the module a third time and make sure the value stuck.
     loader = Loader(module);
     ss = loader.require("sdk/simple-storage");
     manager(loader).jsonStore.onWrite = function (storage) {
       file.remove(storeFilename);
-      test.done();
+      done();
     };
-    test.assertEqual(ss.storage.foo, val, "Value should persist");
+    assert.equal(ss.storage.foo, val, "Value should persist");
     loader.unload();
   };
   let val = "foo";
   ss.storage.foo = val;
-  test.assertEqual(ss.storage.foo, val, "Value read should be value set");
+  assert.equal(ss.storage.foo, val, "Value read should be value set");
   loader.unload();
 };
 
 
-function setGetRoot(test, val, compare) {
-  test.waitUntilDone();
-
+function setGetRoot(assert, done, val, compare) {
   compare = compare || function (a, b) a === b;
 
   // Load the module once, set a value.
   let loader = Loader(module);
   let ss = loader.require("sdk/simple-storage");
   manager(loader).jsonStore.onWrite = function () {
-    test.assert(file.exists(storeFilename), "Store file should exist");
+    assert.ok(file.exists(storeFilename), "Store file should exist");
 
     // Load the module again and make sure the value stuck.
     loader = Loader(module);
     ss = loader.require("sdk/simple-storage");
     manager(loader).jsonStore.onWrite = function () {
       file.remove(storeFilename);
-      test.done();
+      done();
     };
-    test.assert(compare(ss.storage, val), "Value should persist");
+    assert.ok(compare(ss.storage, val), "Value should persist");
     loader.unload();
   };
   ss.storage = val;
-  test.assert(compare(ss.storage, val), "Value read should be value set");
+  assert.ok(compare(ss.storage, val), "Value read should be value set");
   loader.unload();
 }
 
-function setGetRootError(test, val, msg) {
-  let pred = "storage must be one of the following types: " +
-             "array, boolean, null, number, object, string";
+function setGetRootError(assert, done, val, msg) {
+  let pred = new RegExp("storage must be one of the following types: " +
+             "array, boolean, null, number, object, string");
   let loader = Loader(module);
   let ss = loader.require("sdk/simple-storage");
-  test.assertRaises(function () ss.storage = val, pred, msg);
+  assert.throws(function () ss.storage = val, pred, msg);
+  done();
   loader.unload();
 }
+
+require('sdk/test').run(exports);
--- a/addon-sdk/source/test/test-tab-browser.js
+++ b/addon-sdk/source/test/test-tab-browser.js
@@ -60,18 +60,17 @@ function openTwoWindows(callback) {
 
 // Helper for closing two windows at once
 function closeTwoWindows(window1, window2, callback) {
   closeBrowserWindow(window1, function() {
     closeBrowserWindow(window2, callback);
   });
 }
 
-exports.testAddTab = function(test) {
-  test.waitUntilDone();
+exports.testAddTab = function(assert, done) {
   openBrowserWindow(function(window, browser) {
     const tabBrowser = require("sdk/deprecated/tab-browser");
 
     let cache = [];
     let windowUtils = require("sdk/deprecated/window-utils");
     new windowUtils.WindowTracker({
       onTrack: function(win) {
         cache.push(win);
@@ -82,134 +81,124 @@ exports.testAddTab = function(test) {
     });
     let startWindowCount = cache.length;
 
     // Test 1: add a tab
     let firstUrl = "data:text/html;charset=utf-8,one";
     tabBrowser.addTab(firstUrl, {
       onLoad: function(e) {
         let win1 = cache[startWindowCount - 1];
-        test.assertEqual(win1.content.location, firstUrl, "URL of new tab in first window matches");
+        assert.equal(win1.content.location, firstUrl, "URL of new tab in first window matches");
 
         // Test 2: add a tab in a new window
         let secondUrl = "data:text/html;charset=utf-8,two";
         tabBrowser.addTab(secondUrl, {
           inNewWindow: true,
           onLoad: function(e) {
-            test.assertEqual(cache.length, startWindowCount + 1, "a new window was opened");
+            assert.equal(cache.length, startWindowCount + 1, "a new window was opened");
             let win2 = cache[startWindowCount];
             let gBrowser = win2.gBrowser;
             gBrowser.addEventListener("DOMContentLoaded", function onLoad(e) {
               gBrowser.removeEventListener("DOMContentLoaded", onLoad, false);
-              test.assertEqual(win2.content.location, secondUrl, "URL of new tab in the new window matches");
+              assert.equal(win2.content.location, secondUrl, "URL of new tab in the new window matches");
 
               closeBrowserWindow(win2, function() {
-                closeBrowserWindow(win1, function() {
-                  test.done();
-                });
+                closeBrowserWindow(win1, done);
               });
             }, false);
           }
         });
       }
     });
   });
 };
 
-exports.testTrackerWithDelegate = function(test) {
-  test.waitUntilDone();
+exports.testTrackerWithDelegate = function(assert, done) {
   const tabBrowser = require("sdk/deprecated/tab-browser");
 
   var delegate = {
     state: "initializing",
     onTrack: function onTrack(browser) {
       if (this.state == "initializing") {
         this.state = "waiting for browser window to open";
       }
       else if (this.state == "waiting for browser window to open") {
         this.state = "waiting for browser window to close";
         timer.setTimeout(function() {
           closeBrowserWindow(browser.ownerDocument.defaultView, function() {
-            test.assertEqual(delegate.state, "deinitializing");
+            assert.equal(delegate.state, "deinitializing");
             tb.unload();
-            test.done();
+            done();
           });
         }, 0);
       }
       else
-        test.fail("invalid state");
+        assert.fail("invalid state");
     },
     onUntrack: function onUntrack(browser) {
       if (this.state == "waiting for browser window to close") {
-        test.pass("proper state in onUntrack");
+        assert.pass("proper state in onUntrack");
         this.state = "deinitializing";
       }
       else if (this.state != "deinitializing")
-        test.fail("invalid state");
+        assert.fail("invalid state");
     }
   };
   var tb = new tabBrowser.Tracker(delegate);
 
   delegate.state = "waiting for browser window to open";
 
   openBrowserWindow();
 };
 
-exports.testWhenContentLoaded = function(test) {
-  test.waitUntilDone();
+exports.testWhenContentLoaded = function(assert, done) {
   const tabBrowser = require("sdk/deprecated/tab-browser");
 
   var tracker = tabBrowser.whenContentLoaded(
     function(window) {
       var item = window.document.getElementById("foo");
-      test.assertEqual(item.textContent, "bar",
+      assert.equal(item.textContent, "bar",
                        "whenContentLoaded() works.");
       tracker.unload();
-      closeBrowserWindow(activeWindow(), function() {
-        test.done();
-      });
+      closeBrowserWindow(activeWindow(), done);
     });
 
   openBrowserWindow(function(browserWindow, browser) {
     var html = '<div id="foo">bar</div>';
     browser.addTab("data:text/html;charset=utf-8," + html);
   });
 };
 
-exports.testTrackerWithoutDelegate = function(test) {
-  test.waitUntilDone();
+exports.testTrackerWithoutDelegate = function(assert, done) {
   const tabBrowser = require("sdk/deprecated/tab-browser");
 
   openBrowserWindow(function(browserWindow, browser) {
     var tb = new tabBrowser.Tracker();
 
     if (tb.length == 0)
       test.fail("expect at least one tab browser to exist.");
 
     for (var i = 0; i < tb.length; i++)
-      test.assertEqual(tb.get(i).nodeName, "tabbrowser",
+      assert.equal(tb.get(i).nodeName, "tabbrowser",
                        "get() method and length prop should work");
     for (var b in tb)
-      test.assertEqual(b.nodeName, "tabbrowser",
+      assert.equal(b.nodeName, "tabbrowser",
                        "iterator should work");
 
     var matches = [b for (b in tb)
                            if (b == browser)];
-    test.assertEqual(matches.length, 1,
+    assert.equal(matches.length, 1,
                      "New browser should be in tracker.");
     tb.unload();
 
-    closeBrowserWindow(browserWindow, function() {
-      test.done();
-    });
+    closeBrowserWindow(browserWindow, done);
   });
 };
 
-exports.testTabTracker = function(test) {
-  test.waitUntilDone();
+exports.testTabTracker = function(assert, done) {
   const tabBrowser = require("sdk/deprecated/tab-browser");
 
   openBrowserWindow(function(browserWindow, browser) {
     var delegate = {
       tracked: 0,
       onTrack: function(tab) {
         this.tracked++;
       },
@@ -231,114 +220,109 @@ exports.testTabTracker = function(test) 
       if (loadedURL == url1)
         tabCount++;
       else if (loadedURL == url2)
         tabCount++;
       else if (loadedURL == url3)
         tabCount++;
 
       if (tabCount == 3) {
-        test.assertEqual(delegate.tracked, tracked + 3, "delegate tracked tabs matched count");
+        assert.equal(delegate.tracked, tracked + 3, "delegate tracked tabs matched count");
         tabTracker.unload();
         closeBrowserWindow(browserWindow, function() {
-          timer.setTimeout(function() test.done(), 0);
+          timer.setTimeout(done, 0);
         });
       }
     }
 
     tabBrowser.addTab(url1, {
       onLoad: tabLoadListener
     });
     tabBrowser.addTab(url2, {
       onLoad: tabLoadListener
     });
     tabBrowser.addTab(url3, {
       onLoad: tabLoadListener
     });
   });
 };
 
-exports.testActiveTab = function(test) {
-  test.waitUntilDone();
+exports.testActiveTab = function(assert, done) {
   openBrowserWindow(function(browserWindow, browser) {
     const tabBrowser = require("sdk/deprecated/tab-browser");
     const TabModule = require("sdk/deprecated/tab-browser").TabModule;
     let tm = new TabModule(browserWindow);
-    test.assertEqual(tm.length, 1);
+    assert.equal(tm.length, 1);
     let url1 = "data:text/html;charset=utf-8,foo";
     let url2 = "data:text/html;charset=utf-8,bar";
 
     function tabURL(tab) tab.ownerDocument.defaultView.content.location.toString()
 
     tabBrowser.addTab(url1, {
       onLoad: function(e) {
         // make sure we're running in the right window.
-        test.assertEqual(tabBrowser.activeTab.ownerDocument.defaultView, browserWindow, "active window matches");
+        assert.equal(tabBrowser.activeTab.ownerDocument.defaultView, browserWindow, "active window matches");
         browserWindow.focus();
 
-        test.assertEqual(tabURL(tabBrowser.activeTab), url1, "url1 matches");
+        assert.equal(tabURL(tabBrowser.activeTab), url1, "url1 matches");
         let tabIndex = browser.getBrowserIndexForDocument(e.target);
         let tabAtIndex = browser.tabContainer.getItemAtIndex(tabIndex);
-        test.assertEqual(tabAtIndex, tabBrowser.activeTab, "activeTab element matches");
+        assert.equal(tabAtIndex, tabBrowser.activeTab, "activeTab element matches");
 
         tabBrowser.addTab(url2, {
           inBackground: true,
           onLoad: function() {
-            test.assertEqual(tabURL(tabBrowser.activeTab), url1, "url1 still matches");
+            assert.equal(tabURL(tabBrowser.activeTab), url1, "url1 still matches");
             let tabAtIndex = browser.tabContainer.getItemAtIndex(tabIndex);
-            test.assertEqual(tabAtIndex, tabBrowser.activeTab, "activeTab element matches");
-            closeBrowserWindow(browserWindow, function() {
-              test.done()
-            });
+            assert.equal(tabAtIndex, tabBrowser.activeTab, "activeTab element matches");
+            closeBrowserWindow(browserWindow, done);
           }
         });
       }
     });
   });
 };
 
 // TabModule tests
-exports.testEventsAndLengthStayInModule = function(test) {
-  test.waitUntilDone();
+exports.testEventsAndLengthStayInModule = function(assert, done) {
   let TabModule = require("sdk/deprecated/tab-browser").TabModule;
 
   openTwoWindows(function(window1, window2) {
     let tm1 = new TabModule(window1);
     let tm2 = new TabModule(window2);
 
     let counter1 = 0, counter2 = 0;
     let counterTabs = 0;
 
     function onOpenListener() {
       ++counterTabs;
       if (counterTabs < 5)
         return;
-      test.assertEqual(counter1, 2, "Correct number of events fired from window 1");
-      test.assertEqual(counter2, 3, "Correct number of events fired from window 2");
-      test.assertEqual(counterTabs, 5, "Correct number of events fired from all windows");
-      test.assertEqual(tm1.length, 3, "Correct number of tabs in window 1");
-      test.assertEqual(tm2.length, 4, "Correct number of tabs in window 2");
-      closeTwoWindows(window1, window2, function() test.done());
+      assert.equal(counter1, 2, "Correct number of events fired from window 1");
+      assert.equal(counter2, 3, "Correct number of events fired from window 2");
+      assert.equal(counterTabs, 5, "Correct number of events fired from all windows");
+      assert.equal(tm1.length, 3, "Correct number of tabs in window 1");
+      assert.equal(tm2.length, 4, "Correct number of tabs in window 2");
+      closeTwoWindows(window1, window2, done);
     }
 
     tm1.onOpen = function() ++counter1 && onOpenListener();
     tm2.onOpen = function() ++counter2 && onOpenListener();
 
     let url = "data:text/html;charset=utf-8,default";
     tm1.open(url);
     tm1.open(url);
 
     tm2.open(url);
     tm2.open(url);
     tm2.open(url);
   });
 }
 
-exports.testTabModuleActiveTab_getterAndSetter = function(test) {
-  test.waitUntilDone();
+exports.testTabModuleActiveTab_getterAndSetter = function(assert, done) {
   let TabModule = require("sdk/deprecated/tab-browser").TabModule;
 
   openTwoWindows(function(window1, window2) {
     let tm1 = new TabModule(window1);
     let tm2 = new TabModule(window2);
 
     // First open two tabs per window
     tm1.open({
@@ -361,141 +345,146 @@ exports.testTabModuleActiveTab_getterAnd
           }
         });
       }
     });
 
     // Then try to activate tabs, but wait for all of them to be activated after
     // being opened
     function onTabsOpened(tab1, tab2, tab3, tab4) {
-      test.assertEqual(tm1.activeTab.title, "window1,tab2",
+      assert.equal(tm1.activeTab.title, "window1,tab2",
                        "Correct active tab on window 1");
-      test.assertEqual(tm2.activeTab.title, "window2,tab2",
+      assert.equal(tm2.activeTab.title, "window2,tab2",
                        "Correct active tab on window 2");
 
       tm1.onActivate = function onActivate() {
         tm1.onActivate.remove(onActivate);
         timer.setTimeout(function() {
-          test.assertEqual(tm1.activeTab.title, "window1,tab1",
+          assert.equal(tm1.activeTab.title, "window1,tab1",
                            "activeTab setter works (window 1)");
-          test.assertEqual(tm2.activeTab.title, "window2,tab2",
+          assert.equal(tm2.activeTab.title, "window2,tab2",
                            "activeTab is ignored with tabs from another window");
-          closeTwoWindows(window1, window2, function() test.done());
+          closeTwoWindows(window1, window2, done);
         }, 1000);
       }
 
       tm1.activeTab = tab1;
       // Setting activeTab from another window should have no effect:
       tm1.activeTab = tab4;
     }
 
   });
 }
 
 // test tabs iterator
-exports.testTabModuleTabsIterator = function(test) {
-  test.waitUntilDone();
+exports.testTabModuleTabsIterator = function(assert, done) {
   let TabModule = require("sdk/deprecated/tab-browser").TabModule;
 
   openBrowserWindow(function(window) {
     let tm1 = new TabModule(window);
     let url = "data:text/html;charset=utf-8,default";
     tm1.open(url);
     tm1.open(url);
     tm1.open({
       url: url,
       onOpen: function(tab) {
         let count = 0;
         for each (let t in tm1) count++;
-        test.assertEqual(count, 4, "iterated tab count matches");
-        test.assertEqual(count, tm1.length, "length tab count matches");
-        closeBrowserWindow(window, function() test.done());
+        assert.equal(count, 4, "iterated tab count matches");
+        assert.equal(count, tm1.length, "length tab count matches");
+        closeBrowserWindow(window, done);
       }
     });
   });
 };
 
 // inNewWindow parameter is ignored on single-window modules
-exports.testTabModuleCantOpenInNewWindow = function(test) {
-  test.waitUntilDone();
+exports.testTabModuleCantOpenInNewWindow = function(assert, done) {
   let TabModule = require("sdk/deprecated/tab-browser").TabModule;
 
   openBrowserWindow(function(window) {
     let tm = new TabModule(window);
     let url = "data:text/html;charset=utf-8,default";
     tm.open({
       url: url,
       inNewWindow: true,
       onOpen: function() {
-        test.assertEqual(tm.length, 2, "Tab was open on same window");
-        closeBrowserWindow(window, function() test.done());
+        assert.equal(tm.length, 2, "Tab was open on same window");
+        closeBrowserWindow(window, done);
       }
     });
   });
 };
 
 // Test that having two modules attached to the same
 // window won't duplicate events fired on each module
-exports.testModuleListenersDontInteract = function(test) {
-  test.waitUntilDone();
+exports.testModuleListenersDontInteract = function(assert, done) {
   let TabModule = require("sdk/deprecated/tab-browser").TabModule;
-
+  let onOpenTab;
   openBrowserWindow(function(window) {
     let tm1 = new TabModule(window);
     let tm2 = new TabModule(window);
 
     let url = "data:text/html;charset=utf-8,foo";
     let eventCount = 0, eventModule1 = 0, eventModule2 = 0;
 
 
     let listener1 = function() {
       // this should be called twice: when tab is open and when
       // the url location is changed
       eventCount++;
       eventModule1++;
+      check();
     }
     tm1.onReady = listener1;
 
     tm2.open({
       url: "about:blank",
       onOpen: function(tab) {
+        onOpenTab = tab;
         // add listener via property assignment
-        let listener2 = function() {
-          eventCount++;
-          eventModule2++;
-        };
         tab.onReady = listener2;
 
         // add listener via collection add
-        let listener3 = function() {
-          eventCount++;
-          eventModule2++;
-        };
         tab.onReady.add(listener3);
 
         tab.location = url;
 
-        test.waitUntilEqual(function () eventCount, 4,
-                            "Correct global number of events")
-            .then(function () {
-              test.assertEqual(eventModule1, 2,
-                               "Correct number of events on module 1");
-              test.assertEqual(eventModule2, 2,
-                               "Correct number of events on module 2");
-
-              tm1.onReady.remove(listener1);
-              tab.onReady.remove(listener2);
-              tab.onReady.remove(listener3);
-              closeBrowserWindow(window, function() test.done());
-            });
       }
     });
+    
+    function listener2 () {
+      eventCount++;
+      eventModule2++;
+      check();
+    }
+    
+    function listener3 () {
+      eventCount++;
+      eventModule2++;
+      check();
+    }
+
+    function check () {
+      if (eventCount !== 4) return;
+      assert.equal(eventModule1, 2,
+        "Correct number of events on module 1");
+      assert.equal(eventModule2, 2,
+        "Correct number of events on module 2");
+
+      tm1.onReady.remove(listener1);
+      onOpenTab.onReady.remove(listener2);
+      onOpenTab.onReady.remove(listener3);
+      closeBrowserWindow(window, done);
+    }
   });
 };
 
 /******************* helpers *********************/
 
 // Helper for getting the active window
 function activeWindow() {
   return Cc["@mozilla.org/appshell/window-mediator;1"].
          getService(Ci.nsIWindowMediator).
          getMostRecentWindow("navigator:browser");
 }
+
+require('sdk/test').run(exports);
--- a/addon-sdk/source/test/test-tabs-common.js
+++ b/addon-sdk/source/test/test-tabs-common.js
@@ -12,174 +12,157 @@ const { isWindowPrivate } = require('sdk
 const { setTimeout } = require('sdk/timers');
 const { openWebpage } = require('./private-browsing/helper');
 const { isTabPBSupported, isWindowPBSupported } = require('sdk/private-browsing/utils');
 const app = require("sdk/system/xul-app");
 
 const URL = 'data:text/html;charset=utf-8,<html><head><title>#title#</title></head></html>';
 
 // TEST: tab count
-exports.testTabCounts = function(test) {
-  test.waitUntilDone();
-
+exports.testTabCounts = function(assert, done) {
   tabs.open({
     url: 'about:blank',
     onReady: function(tab) {
       let count1 = 0,
           count2 = 0;
       for each(let window in browserWindows) {
         count1 += window.tabs.length;
         for each(let tab in window.tabs) {
           count2 += 1;
         }
       }
 
-      test.assert(tabs.length > 1, 'tab count is > 1');
-      test.assertEqual(count1, tabs.length, 'tab count by length is correct');
-      test.assertEqual(count2, tabs.length, 'tab count by iteration is correct');
+      assert.ok(tabs.length > 1, 'tab count is > 1');
+      assert.equal(count1, tabs.length, 'tab count by length is correct');
+      assert.equal(count2, tabs.length, 'tab count by iteration is correct');
 
       // end test
-      tab.close(function() test.done());
+      tab.close(done);
     }
   });
 };
 
 
 // TEST: tabs.activeTab getter
-exports.testActiveTab_getter = function(test) {
-  test.waitUntilDone();
+exports.testActiveTab_getter = function(assert, done) {
   let evtCount = 0;
   let activeTab = null;
 
   function endTest(type, tab) {
     if (type == 'activate') {
-      test.assertStrictEqual(tabs.activeTab, tab, 'the active tab is the opened tab');
+      assert.strictEqual(tabs.activeTab, tab, 'the active tab is the opened tab');
       activeTab = tabs.activeTab;
     }
     else {
-      test.assertEqual(tab.url, url, 'the opened tab has the correct url');
+      assert.equal(tab.url, url, 'the opened tab has the correct url');
     }
 
     if (++evtCount != 2)
       return;
 
-    test.assertStrictEqual(activeTab, tab, 'the active tab is the ready tab');
-    test.assertStrictEqual(tabs.activeTab, tab, 'the active tab is the ready tab');
+    assert.strictEqual(activeTab, tab, 'the active tab is the ready tab');
+    assert.strictEqual(tabs.activeTab, tab, 'the active tab is the ready tab');
 
-    tab.close(function() {
-      // end test
-      test.done();
-    });
+    tab.close(done);
   }
 
   let url = URL.replace("#title#", "testActiveTab_getter");
   tabs.open({
     url: url,
     onReady: endTest.bind(null, 'ready'),
     onActivate: endTest.bind(null, 'activate')
   });
 };
 
 // TEST: tab.activate()
-exports.testActiveTab_setter = function(test) {
-  test.waitUntilDone();
-
+exports.testActiveTab_setter = function(assert, done) {
   let url = URL.replace("#title#", "testActiveTab_setter");
   let tab1URL = URL.replace("#title#", "tab1");
 
   tabs.open({
     url: tab1URL,
     onReady: function(activeTab) {
       let activeTabURL = tabs.activeTab.url;
 
       tabs.open({
         url: url,
         inBackground: true,
         onReady: function onReady(tab) {
-          test.assertEqual(tabs.activeTab.url, activeTabURL, "activeTab url has not changed");
-          test.assertEqual(tab.url, url, "url of new background tab matches");
+          assert.equal(tabs.activeTab.url, activeTabURL, "activeTab url has not changed");
+          assert.equal(tab.url, url, "url of new background tab matches");
 
           tab.once('activate', function onActivate(eventTab) {
-            test.assertEqual(tabs.activeTab.url, url, "url after activeTab setter matches");
-            test.assertEqual(eventTab, tab, "event argument is the activated tab");
-            test.assertEqual(eventTab, tabs.activeTab, "the tab is the active one");
+            assert.equal(tabs.activeTab.url, url, "url after activeTab setter matches");
+            assert.equal(eventTab, tab, "event argument is the activated tab");
+            assert.equal(eventTab, tabs.activeTab, "the tab is the active one");
 
             activeTab.close(function() {
-              tab.close(function() {
-                // end test
-                test.done();
-              });
+              tab.close(done);
             });
           });
 
           tab.activate();
         }
       });
     }
   });
 };
 
 // TEST: tab.close()
-exports.testTabClose_alt = function(test) {
-  test.waitUntilDone();
-
+exports.testTabClose_alt = function(assert, done) {
   let url = URL.replace('#title#', 'TabClose_alt');
   let tab1URL = URL.replace('#title#', 'tab1');
 
   tabs.open({
     url: tab1URL,
     onReady: function(tab1) {
       // make sure that our tab is not active first
-      test.assertNotEqual(tabs.activeTab.url, url, "tab is not the active tab");
+      assert.notEqual(tabs.activeTab.url, url, "tab is not the active tab");
 
       tabs.open({
         url: url,
         onReady: function(tab) {
-          test.assertEqual(tab.url, url, "tab is now the active tab");
-          test.assertEqual(tabs.activeTab.url, url, "tab is now the active tab");
+          assert.equal(tab.url, url, "tab is now the active tab");
+          assert.equal(tabs.activeTab.url, url, "tab is now the active tab");
 
           // another tab should be activated on close
           tabs.once('activate', function() {
-            test.assertNotEqual(tabs.activeTab.url, url, "tab is no longer the active tab");
+            assert.notEqual(tabs.activeTab.url, url, "tab is no longer the active tab");
 
             // end test
-            tab1.close(function() test.done());
+            tab1.close(done);
           });
 
           tab.close();
         }
       });
     }
   });
 };
 
-exports.testAttachOnOpen_alt = function (test) {
+exports.testAttachOnOpen_alt = function (assert, done) {
   // Take care that attach has to be called on tab ready and not on tab open.
-  test.waitUntilDone();
-
   tabs.open({
     url: "data:text/html;charset=utf-8,foobar",
     onOpen: function (tab) {
       let worker = tab.attach({
         contentScript: 'self.postMessage(document.location.href); ',
         onMessage: function (msg) {
-          test.assertEqual(msg, "about:blank",
+          assert.equal(msg, "about:blank",
             "Worker document url is about:blank on open");
           worker.destroy();
-          tab.close(function() test.done());
+          tab.close(done);
         }
       });
     }
   });
 };
 
-exports.testAttachOnMultipleDocuments_alt = function (test) {
+exports.testAttachOnMultipleDocuments_alt = function (assert, done) {
   // Example of attach that process multiple tab documents
-  test.waitUntilDone();
-
   let firstLocation = "data:text/html;charset=utf-8,foobar";
   let secondLocation = "data:text/html;charset=utf-8,bar";
   let thirdLocation = "data:text/html;charset=utf-8,fox";
   let onReadyCount = 0;
   let worker1 = null;
   let worker2 = null;
   let detachEventCount = 0;
 
@@ -188,47 +171,47 @@ exports.testAttachOnMultipleDocuments_al
     onReady: function (tab) {
       onReadyCount++;
       if (onReadyCount == 1) {
         worker1 = tab.attach({
           contentScript: 'self.on("message", ' +
                          '  function () self.postMessage(document.location.href)' +
                          ');',
           onMessage: function (msg) {
-            test.assertEqual(msg, firstLocation,
+            assert.equal(msg, firstLocation,
                              "Worker url is equal to the 1st document");
             tab.url = secondLocation;
           },
           onDetach: function () {
             detachEventCount++;
-            test.pass("Got worker1 detach event");
-            test.assertRaises(function () {
+            assert.pass("Got worker1 detach event");
+            assert.throws(function () {
                 worker1.postMessage("ex-1");
               },
               /Couldn't find the worker/,
               "postMessage throw because worker1 is destroyed");
             checkEnd();
           }
         });
         worker1.postMessage("new-doc-1");
       }
       else if (onReadyCount == 2) {
         worker2 = tab.attach({
           contentScript: 'self.on("message", ' +
                          '  function () self.postMessage(document.location.href)' +
                          ');',
           onMessage: function (msg) {
-            test.assertEqual(msg, secondLocation,
+            assert.equal(msg, secondLocation,
                              "Worker url is equal to the 2nd document");
             tab.url = thirdLocation;
           },
           onDetach: function () {
             detachEventCount++;
-            test.pass("Got worker2 detach event");
-            test.assertRaises(function () {
+            assert.pass("Got worker2 detach event");
+            assert.throws(function () {
                 worker2.postMessage("ex-2");
               },
               /Couldn't find the worker/,
               "postMessage throw because worker2 is destroyed");
             checkEnd(tab);
           }
         });
         worker2.postMessage("new-doc-2");
@@ -238,319 +221,301 @@ exports.testAttachOnMultipleDocuments_al
       }
     }
   });
 
   function checkEnd(tab) {
     if (detachEventCount != 2)
       return;
 
-    test.pass("Got all detach events");
+    assert.pass("Got all detach events");
 
-    // end test
-    test.done();
+    done();
   }
 };
 
-exports.testAttachWrappers_alt = function (test) {
+exports.testAttachWrappers_alt = function (assert, done) {
   // Check that content script has access to wrapped values by default
-  test.waitUntilDone();
 
   let document = "data:text/html;charset=utf-8,<script>var globalJSVar = true; " +
                  "                       document.getElementById = 3;</script>";
   let count = 0;
 
   tabs.open({
     url: document,
     onReady: function (tab) {
       let worker = tab.attach({
         contentScript: 'try {' +
                        '  self.postMessage(!("globalJSVar" in window));' +
                        '  self.postMessage(typeof window.globalJSVar == "undefined");' +
                        '} catch(e) {' +
                        '  self.postMessage(e.message);' +
                        '}',
         onMessage: function (msg) {
-          test.assertEqual(msg, true, "Worker has wrapped objects ("+count+")");
+          assert.equal(msg, true, "Worker has wrapped objects ("+count+")");
           if (count++ == 1)
-            tab.close(function() test.done());
+            tab.close(function() done());
         }
       });
     }
   });
 };
 
 // TEST: activeWindow getter and activeTab getter on tab 'activate' event
-exports.testActiveWindowActiveTabOnActivate_alt = function(test) {
-  test.waitUntilDone();
+exports.testActiveWindowActiveTabOnActivate_alt = function(assert, done) {
 
   let activateCount = 0;
   let newTabs = [];
   let tabs = browserWindows.activeWindow.tabs;
 
   tabs.on('activate', function onActivate(tab) {
-    test.assertEqual(tabs.activeTab, tab,
+    assert.equal(tabs.activeTab, tab,
                     "the active window's active tab is the tab provided");
 
     if (++activateCount == 2) {
       tabs.removeListener('activate', onActivate);
 
       newTabs.forEach(function(tab) {
         tab.close(function() {
           if (--activateCount == 0) {
-            // end test
-            test.done();
+            done();
           }
         });
       });
     }
     else if (activateCount > 2) {
-      test.fail("activateCount is greater than 2 for some reason..");
+      assert.fail("activateCount is greater than 2 for some reason..");
     }
   });
 
   tabs.open({
     url: URL.replace("#title#", "tabs.open1"),
     onOpen: function(tab) newTabs.push(tab)
   });
   tabs.open({
     url: URL.replace("#title#", "tabs.open2"),
     onOpen: function(tab) newTabs.push(tab)
   });
 };
 
 // TEST: tab properties
-exports.testTabContentTypeAndReload = function(test) {
-  test.waitUntilDone();
+exports.testTabContentTypeAndReload = function(assert, done) {
 
   let url = "data:text/html;charset=utf-8,<html><head><title>foo</title></head><body>foo</body></html>";
   let urlXML = "data:text/xml;charset=utf-8,<foo>bar</foo>";
   tabs.open({
     url: url,
     onReady: function(tab) {
       if (tab.url === url) {
-        test.assertEqual(tab.contentType, "text/html");
+        assert.equal(tab.contentType, "text/html");
         tab.url = urlXML;
       }
       else {
-        test.assertEqual(tab.contentType, "text/xml");
-        tab.close(function() {
-          test.done();
-        });
+        assert.equal(tab.contentType, "text/xml");
+        tab.close(done);
       }
     }
   });
 };
 
 // test that it isn't possible to open a private tab without the private permission
-exports.testTabOpenPrivate = function(test) {
-  test.waitUntilDone();
+exports.testTabOpenPrivate = function(assert, done) {
 
   let url = 'about:blank';
   tabs.open({
     url: url,
     isPrivate: true,
     onReady: function(tab) {
-      test.assertEqual(tab.url, url, 'opened correct tab');
-      test.assertEqual(isPrivate(tab), false, 'private tabs are not supported by default');
+      assert.equal(tab.url, url, 'opened correct tab');
+      assert.equal(isPrivate(tab), false, 'private tabs are not supported by default');
 
-      tab.close(function() {
-        test.done();
-      });
+      tab.close(done);
     }
   });
 }
 
 // We need permission flag in order to see private window's tabs
-exports.testPrivateAreNotListed = function (test) {
+exports.testPrivateAreNotListed = function (assert, done) {
   let originalTabCount = tabs.length;
 
   let page = openWebpage("about:blank", true);
   if (!page) {
-    test.pass("Private browsing isn't supported in this release");
+    assert.pass("Private browsing isn't supported in this release");
     return;
   }
 
-  test.waitUntilDone();
   page.ready.then(function (win) {
     if (isTabPBSupported || isWindowPBSupported) {
-      test.assert(isWindowPrivate(win), "the window is private");
-      test.assertEqual(tabs.length, originalTabCount,
+      assert.ok(isWindowPrivate(win), "the window is private");
+      assert.equal(tabs.length, originalTabCount,
                        'but the tab is *not* visible in tabs list');
     }
     else {
-      test.assert(!isWindowPrivate(win), "the window isn't private");
-      test.assertEqual(tabs.length, originalTabCount + 1,
+      assert.ok(!isWindowPrivate(win), "the window isn't private");
+      assert.equal(tabs.length, originalTabCount + 1,
                        'so that the tab is visible is tabs list');
     }
-    page.close().then(test.done.bind(test));
+    page.close().then(done);
   });
 }
 
 // If we close the tab while being in `onOpen` listener,
 // we end up synchronously consuming TabOpen, closing the tab and still
 // synchronously consuming the related TabClose event before the second
 // loader have a change to process the first TabOpen event!
-exports.testImmediateClosing = function (test) {
-  test.waitUntilDone();
-
+exports.testImmediateClosing = function (assert, done) {
   let tabURL = 'data:text/html,foo';
 
   let { loader, messages } = LoaderWithHookedConsole(module, onMessage);
   let concurrentTabs = loader.require("sdk/tabs");
   concurrentTabs.on("open", function (tab) {
     // On Firefox, It shouldn't receive such event as the other loader will just
     // open and destroy the tab without giving a chance to other loader to even
     // know about the existance of this tab.
     if (app.is("Firefox")) {
-      test.fail("Concurrent loader received a tabs `open` event");
+      assert.fail("Concurrent loader received a tabs `open` event");
     }
     else {
       // On mobile, we can still receive an open event,
       // but not the related ready event
       tab.on("ready", function () {
-        test.fail("Concurrent loader received a tabs `ready` event");
+        assert.fail("Concurrent loader received a tabs `ready` event");
       });
     }
   });
   function onMessage(type, msg) {
-    test.fail("Unexpected mesage on concurrent loader: " + msg);
+    assert.fail("Unexpected mesage on concurrent loader: " + msg);
   }
 
   tabs.open({
     url: tabURL,
     onOpen: function(tab) {
       tab.close(function () {
-        test.pass("Tab succesfully removed");
+        assert.pass("Tab succesfully removed");
         // Let a chance to the concurrent loader to receive a TabOpen event
         // on the next event loop turn
         setTimeout(function () {
           loader.unload();
-          test.done();
+          done();
         }, 0);
       });
     }
   });
 }
 
 // TEST: tab.reload()
-exports.testTabReload = function(test) {
-  test.waitUntilDone();
+exports.testTabReload = function(assert, done) {
 
   let url = "data:text/html;charset=utf-8,<!doctype%20html><title></title>";
 
   tabs.open({
     url: url,
     onReady: function onReady(tab) {
       tab.removeListener('ready', onReady);
 
       tab.once(
         'ready',
         function onReload() {
-          test.pass("the tab was loaded again");
-          test.assertEqual(tab.url, url, "the tab has the same URL");
+          assert.pass("the tab was loaded again");
+          assert.equal(tab.url, url, "the tab has the same URL");
 
-          // end test
-          tab.close(function() test.done());
+          tab.close(function() done());
         }
       );
 
       tab.reload();
     }
   });
 };
 
-exports.testOnPageShowEvent = function (test) {
-  test.waitUntilDone();
-
+exports.testOnPageShowEvent = function (assert, done) {
   let events = [];
   let firstUrl = 'data:text/html;charset=utf-8,First';
   let secondUrl = 'data:text/html;charset=utf-8,Second';
 
   let counter = 0;
   function onPageShow (tab, persisted) {
     events.push('pageshow');
     counter++;
     if (counter === 1) {
-      test.assertEqual(persisted, false, 'page should not be cached on initial load');
+      assert.equal(persisted, false, 'page should not be cached on initial load');
       tab.url = secondUrl;
     }
     else if (counter === 2) {
-      test.assertEqual(persisted, false, 'second test page should not be cached either');
+      assert.equal(persisted, false, 'second test page should not be cached either');
       tab.attach({
         contentScript: 'setTimeout(function () { window.history.back(); }, 0)'
       });
     }
     else {
-      test.assertEqual(persisted, true, 'when we get back to the fist page, it has to' +
+      assert.equal(persisted, true, 'when we get back to the fist page, it has to' +
                              'come from cache');
       tabs.removeListener('pageshow', onPageShow);
       tabs.removeListener('open', onOpen);
       tabs.removeListener('ready', onReady);
       tab.close(() => {
         ['open', 'ready', 'pageshow', 'ready',
             'pageshow', 'pageshow'].map((type, i) => {
-          test.assertEqual(type, events[i], 'correct ordering of events');
+          assert.equal(type, events[i], 'correct ordering of events');
         });
-        test.done()
+        done()
       });
     }
   }
 
   function onOpen () events.push('open');
   function onReady () events.push('ready');
 
   tabs.on('pageshow', onPageShow);
   tabs.on('open', onOpen);
   tabs.on('ready', onReady);
   tabs.open({
     url: firstUrl
   });
 };
 
-exports.testOnPageShowEventDeclarative = function (test) {
-  test.waitUntilDone();
-
+exports.testOnPageShowEventDeclarative = function (assert, done) {
   let events = [];
   let firstUrl = 'data:text/html;charset=utf-8,First';
   let secondUrl = 'data:text/html;charset=utf-8,Second';
 
   let counter = 0;
   function onPageShow (tab, persisted) {
     events.push('pageshow');
     counter++;
     if (counter === 1) {
-      test.assertEqual(persisted, false, 'page should not be cached on initial load');
+      assert.equal(persisted, false, 'page should not be cached on initial load');
       tab.url = secondUrl;
     }
     else if (counter === 2) {
-      test.assertEqual(persisted, false, 'second test page should not be cached either');
+      assert.equal(persisted, false, 'second test page should not be cached either');
       tab.attach({
         contentScript: 'setTimeout(function () { window.history.back(); }, 0)'
       });
     }
     else {
-      test.assertEqual(persisted, true, 'when we get back to the fist page, it has to' +
+      assert.equal(persisted, true, 'when we get back to the fist page, it has to' +
                              'come from cache');
       tabs.removeListener('pageshow', onPageShow);
       tabs.removeListener('open', onOpen);
       tabs.removeListener('ready', onReady);
       tab.close(() => {
         ['open', 'ready', 'pageshow', 'ready',
             'pageshow', 'pageshow'].map((type, i) => {
-          test.assertEqual(type, events[i], 'correct ordering of events');
+          assert.equal(type, events[i], 'correct ordering of events');
         });
-        test.done()
+        done()
       });
     }
   }
 
   function onOpen () events.push('open');
   function onReady () events.push('ready');
 
   tabs.open({
     url: firstUrl,
     onPageShow: onPageShow,
     onOpen: onOpen,
     onReady: onReady
   });
 };
 
+require('sdk/test').run(exports);
new file mode 100644
--- /dev/null
+++ b/addon-sdk/source/test/test-test-utils.js
@@ -0,0 +1,46 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+'use strict'
+
+const { setTimeout } = require('sdk/timers');
+const { waitUntil } = require('sdk/test/utils');
+
+exports.testWaitUntil = function (assert, done) {
+  let bool = false;
+  let finished = false;
+  waitUntil(() => {
+    if (finished)
+      assert.fail('interval should be cleared after predicate is truthy');
+    return bool;
+  }).then(function () {
+    assert.ok(bool,
+      'waitUntil shouldn\'t call until predicate is truthy');
+    finished = true;
+    done();
+  });
+  setTimeout(() => { bool = true; }, 20);
+};
+
+exports.testWaitUntilInterval = function (assert, done) {
+  let bool = false;
+  let finished = false;
+  let counter = 0;
+  waitUntil(() => {
+    if (finished)
+      assert.fail('interval should be cleared after predicate is truthy');
+    counter++;
+    return bool;
+  }, 50).then(function () {
+    assert.ok(bool,
+      'waitUntil shouldn\'t call until predicate is truthy');
+    assert.equal(counter, 1,
+      'predicate should only be called once with a higher interval');
+    finished = true;
+    done();
+  });
+  setTimeout(() => { bool = true; }, 10);
+};
+
+require('sdk/test').run(exports);
--- a/addon-sdk/source/test/test-text-streams.js
+++ b/addon-sdk/source/test/test-text-streams.js
@@ -1,67 +1,67 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 const file = require("sdk/io/file");
 const { pathFor } = require("sdk/system");
 const { Loader } = require("sdk/test/loader");
 
-const STREAM_CLOSED_ERROR = "The stream is closed and cannot be used.";
+const STREAM_CLOSED_ERROR = new RegExp("The stream is closed and cannot be used.");
 
 // This should match the constant of the same name in text-streams.js.
 const BUFFER_BYTE_LEN = 0x8000;
 
-exports.testWriteRead = function (test) {
+exports.testWriteRead = function (assert) {
   let fname = dataFileFilename();
 
   // Write a small string less than the stream's buffer size...
   let str = "exports.testWriteRead data!";
   let stream = file.open(fname, "w");
-  test.assert(!stream.closed, "stream.closed after open should be false");
+  assert.ok(!stream.closed, "stream.closed after open should be false");
   stream.write(str);
   stream.close();
-  test.assert(stream.closed, "stream.closed after close should be true");
-  test.assertRaises(function () stream.close(),
+  assert.ok(stream.closed, "stream.closed after close should be true");
+  assert.throws(function () stream.close(),
                     STREAM_CLOSED_ERROR,
                     "stream.close after already closed should raise error");
-  test.assertRaises(function () stream.write("This shouldn't be written!"),
+  assert.throws(function () stream.write("This shouldn't be written!"),
                     STREAM_CLOSED_ERROR,
                     "stream.write after close should raise error");
 
   // ... and read it.
   stream = file.open(fname);
-  test.assert(!stream.closed, "stream.closed after open should be false");
-  test.assertEqual(stream.read(), str,
+  assert.ok(!stream.closed, "stream.closed after open should be false");
+  assert.equal(stream.read(), str,
                    "stream.read should return string written");
-  test.assertEqual(stream.read(), "",
+  assert.equal(stream.read(), "",
                    "stream.read at EOS should return empty string");
   stream.close();
-  test.assert(stream.closed, "stream.closed after close should be true");
-  test.assertRaises(function () stream.close(),
+  assert.ok(stream.closed, "stream.closed after close should be true");
+  assert.throws(function () stream.close(),
                     STREAM_CLOSED_ERROR,
                     "stream.close after already closed should raise error");
-  test.assertRaises(function () stream.read(),
+  assert.throws(function () stream.read(),
                     STREAM_CLOSED_ERROR,
                     "stream.read after close should raise error");
 
   // Write a big string many times the size of the stream's buffer and read it.
   // Since it comes after the previous test, this also ensures that the file is
   // truncated when it's opened for writing.
   str = "";
   let bufLen = BUFFER_BYTE_LEN;
   let fileSize = bufLen * 10;
   for (let i = 0; i < fileSize; i++)
     str += i % 10;
   stream = file.open(fname, "w");
   stream.write(str);
   stream.close();
   stream = file.open(fname);
-  test.assertEqual(stream.read(), str,
+  assert.equal(stream.read(), str,
                    "stream.read should return string written");
   stream.close();
 
   // The same, but write and read in chunks.
   stream = file.open(fname, "w");
   let i = 0;
   while (i < str.length) {
     // Use a chunk length that spans buffers.
@@ -74,81 +74,81 @@ exports.testWriteRead = function (test) 
   let readStr = "";
   bufLen = BUFFER_BYTE_LEN;
   let readLen = bufLen + 1;
   do {
     var frag = stream.read(readLen);
     readStr += frag;
   } while (frag);
   stream.close();
-  test.assertEqual(readStr, str,
+  assert.equal(readStr, str,
                    "stream.write and read in chunks should work as expected");
 
   // Read the same file, passing in strange numbers of bytes to read.
   stream = file.open(fname);
-  test.assertEqual(stream.read(fileSize * 100), str,
+  assert.equal(stream.read(fileSize * 100), str,
                    "stream.read with big byte length should return string " +
                    "written");
   stream.close();
 
   stream = file.open(fname);
-  test.assertEqual(stream.read(0), "",
+  assert.equal(stream.read(0), "",
                    "string.read with zero byte length should return empty " +
                    "string");
   stream.close();
 
   stream = file.open(fname);
-  test.assertEqual(stream.read(-1), "",
+  assert.equal(stream.read(-1), "",
                    "string.read with negative byte length should return " +
                    "empty string");
   stream.close();
 
   file.remove(fname);
 };
 
-exports.testWriteAsync = function (test) {
-  test.waitUntilDone();
-
+exports.testWriteAsync = function (assert, done) {
   let fname = dataFileFilename();
   let str = "exports.testWriteAsync data!";
   let stream = file.open(fname, "w");
-  test.assert(!stream.closed, "stream.closed after open should be false");
+  assert.ok(!stream.closed, "stream.closed after open should be false");
 
   // Write.
   stream.writeAsync(str, function (err) {
-    test.assertEqual(this, stream, "|this| should be the stream object");
-    test.assertEqual(err, undefined,
+    assert.equal(this, stream, "|this| should be the stream object");
+    assert.equal(err, undefined,
                      "stream.writeAsync should not cause error");
-    test.assert(stream.closed, "stream.closed after write should be true");
-    test.assertRaises(function () stream.close(),
+    assert.ok(stream.closed, "stream.closed after write should be true");
+    assert.throws(function () stream.close(),
                       STREAM_CLOSED_ERROR,
                       "stream.close after already closed should raise error");
-    test.assertRaises(function () stream.writeAsync("This shouldn't work!"),
+    assert.throws(function () stream.writeAsync("This shouldn't work!"),
                       STREAM_CLOSED_ERROR,
                       "stream.writeAsync after close should raise error");
 
     // Read.
     stream = file.open(fname, "r");
-    test.assert(!stream.closed, "stream.closed after open should be false");
+    assert.ok(!stream.closed, "stream.closed after open should be false");
     let readStr = stream.read();
-    test.assertEqual(readStr, str,
+    assert.equal(readStr, str,
                      "string.read should yield string written");
     stream.close();
     file.remove(fname);
-    test.done();
+    done();
   });
 };
 
-exports.testUnload = function (test) {
+exports.testUnload = function (assert) {
   let loader = Loader(module);
   let file = loader.require("sdk/io/file");
 
   let filename = dataFileFilename("temp");
   let stream = file.open(filename, "w");
 
   loader.unload();
-  test.assert(stream.closed, "stream should be closed after module unload");
+  assert.ok(stream.closed, "stream should be closed after module unload");
 };
 
 // Returns the name of a file that should be used to test writing and reading.
 function dataFileFilename() {
   return file.join(pathFor("ProfD"), "test-text-streams-data");
 }
+
+require('sdk/test').run(exports);
--- a/addon-sdk/source/test/test-traceback.js
+++ b/addon-sdk/source/test/test-traceback.js
@@ -10,112 +10,114 @@ function throwNsIException() {
             .getService(Ci.nsIIOService);
   ios.newURI("i'm a malformed URI", null, null);
 }
 
 function throwError() {
   throw new Error("foob");
 }
 
-exports.testFormatDoesNotFetchRemoteFiles = function(test) {
+exports.testFormatDoesNotFetchRemoteFiles = function(assert) {
   var observers = require("sdk/deprecated/observer-service");
   ["http", "https"].forEach(
     function(scheme) {
       var httpRequests = 0;
       function onHttp() {
         httpRequests++;
       }
 
       observers.add("http-on-modify-request", onHttp);
 
       try {
         var tb = [{filename: scheme + "://www.mozilla.org/",
                    lineNumber: 1,
                    name: "blah"}];
         traceback.format(tb);
       } catch (e) {
-        test.exception(e);
+        assert.fail(e);
       }
 
       observers.remove("http-on-modify-request", onHttp);
 
-      test.assertEqual(httpRequests, 0,
+      assert.equal(httpRequests, 0,
                        "traceback.format() does not make " +
                        scheme + " request");
     });
 };
 
-exports.testFromExceptionWithString = function(test) {
+exports.testFromExceptionWithString = function(assert) {
   try {
     throw "foob";
-    test.fail("an exception should've been thrown");
+    assert.fail("an exception should've been thrown");
   } catch (e if e == "foob") {
     var tb = traceback.fromException(e);
-    test.assertEqual(tb.length, 0);
+    assert.equal(tb.length, 0);
   }
 };
 
-exports.testFormatWithString = function(test) {
+exports.testFormatWithString = function(assert) {
   // This can happen if e.g. a thrown exception was
   // a string instead of an Error instance.
-  test.assertEqual(traceback.format("blah"),
+  assert.equal(traceback.format("blah"),
 		   "Traceback (most recent call last):");
 };
 
-exports.testFromExceptionWithError = function(test) {
+exports.testFromExceptionWithError = function(assert) {
   try {
     throwError();
-    test.fail("an exception should've been thrown");
+    assert.fail("an exception should've been thrown");
   } catch (e if e instanceof Error) {
     var tb = traceback.fromException(e);
 
     var xulApp = require("sdk/system/xul-app");
-    test.assertEqual(tb.slice(-1)[0].name, "throwError");
+    assert.equal(tb.slice(-1)[0].name, "throwError");
   }
 };
 
-exports.testFromExceptionWithNsIException = function(test) {
+exports.testFromExceptionWithNsIException = function(assert) {
   try {
     throwNsIException();
-    test.fail("an exception should've been thrown");
+    assert.fail("an exception should've been thrown");
   } catch (e if e.result == Cr.NS_ERROR_MALFORMED_URI) {
     var tb = traceback.fromException(e);
-    test.assertEqual(tb[tb.length - 1].name, "throwNsIException");
+    assert.equal(tb[tb.length - 1].name, "throwNsIException");
   }
 };
 
-exports.testFormat = function(test) {
+exports.testFormat = function(assert) {
   function getTraceback() {
     return traceback.format();
   }
 
   var formatted = getTraceback();
-  test.assertEqual(typeof(formatted), "string");
+  assert.equal(typeof(formatted), "string");
   var lines = formatted.split("\n");
 
-  test.assertEqual(lines[lines.length - 2].indexOf("getTraceback") > 0,
+  assert.equal(lines[lines.length - 2].indexOf("getTraceback") > 0,
                    true,
                    "formatted traceback should include function name");
 
-  test.assertEqual(lines[lines.length - 1].trim(),
+  assert.equal(lines[lines.length - 1].trim(),
                    "return traceback.format();",
                    "formatted traceback should include source code");
 };
 
-exports.testExceptionsWithEmptyStacksAreLogged = function(test) {
+exports.testExceptionsWithEmptyStacksAreLogged = function(assert) {
   // Ensures that our fix to bug 550368 works.
   var sandbox = Cu.Sandbox("http://www.foo.com");
   var excRaised = false;
   try {
     Cu.evalInSandbox("returns 1 + 2;", sandbox, "1.8",
                      "blah.js", 25);
   } catch (e) {
     excRaised = true;
     var stack = traceback.fromException(e);
-    test.assertEqual(stack.length, 1, "stack should have one frame");
+    assert.equal(stack.length, 1, "stack should have one frame");
 
-    test.assert(stack[0].fileName, "blah.js", "frame should have filename");
-    test.assert(stack[0].lineNumber, 25, "frame should have line no");
-    test.assertEqual(stack[0].name, null, "frame should have null function name");
+    assert.ok(stack[0].fileName, "blah.js", "frame should have filename");