michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: "use strict"; michael@0: michael@0: var unload = require("sdk/system/unload"); michael@0: var { Loader, LoaderWithHookedConsole } = require("sdk/test/loader"); michael@0: michael@0: exports.testUnloading = function(assert) { michael@0: let { loader, messages } = LoaderWithHookedConsole(module); michael@0: var ul = loader.require("sdk/system/unload"); michael@0: var unloadCalled = 0; michael@0: function unload() { michael@0: unloadCalled++; michael@0: throw new Error("error"); michael@0: } michael@0: ul.when(unload); michael@0: michael@0: // This should be ignored, as we already registered it michael@0: ul.when(unload); michael@0: michael@0: function unload2() { unloadCalled++; } michael@0: ul.when(unload2); michael@0: loader.unload(); michael@0: assert.equal(unloadCalled, 2, michael@0: "Unloader functions are called on unload."); michael@0: assert.equal(messages.length, 1, michael@0: "One unload handler threw exception 1/2"); michael@0: assert.equal(messages[0].type, "exception", michael@0: "One unload handler threw exception 2/2"); michael@0: }; michael@0: michael@0: exports.testEnsure = function(assert) { michael@0: assert.throws(function() { unload.ensure({}); }, michael@0: /object has no 'unload' property/, michael@0: "passing obj with no unload prop should fail"); michael@0: assert.throws(function() { unload.ensure({}, "destroy"); }, michael@0: /object has no 'destroy' property/, michael@0: "passing obj with no custom unload prop should fail"); michael@0: michael@0: var called = 0; michael@0: var obj = {unload: function() { called++; }}; michael@0: michael@0: unload.ensure(obj); michael@0: obj.unload(); michael@0: assert.equal(called, 1, michael@0: "unload() should be called"); michael@0: obj.unload(); michael@0: assert.equal(called, 1, michael@0: "unload() should be called only once"); michael@0: }; michael@0: michael@0: /** michael@0: * Check that destructors are called only once with Traits. michael@0: * - check that public API is calling the destructor and unregister it, michael@0: * - check that composed traits with multiple ensure calls, leads to only michael@0: * one destructor call. michael@0: */ michael@0: exports.testEnsureWithTraits = function(assert) { michael@0: let { Trait } = require("sdk/deprecated/traits"); michael@0: let loader = Loader(module); michael@0: let ul = loader.require("sdk/system/unload"); michael@0: michael@0: let called = 0; michael@0: let composedCalled = 0; michael@0: let composedTrait = Trait.compose({ michael@0: constructor: function () { michael@0: // We have to give "public interface" of this trait, as we want to michael@0: // call public `unload` method and ensure that we call it only once, michael@0: // either when we call this public function manually or on add-on unload michael@0: ul.ensure(this._public); michael@0: }, michael@0: unload: function unload() { michael@0: composedCalled++; michael@0: } michael@0: }); michael@0: let obj = Trait.compose( michael@0: composedTrait.resolve({ michael@0: constructor: "_constructor", michael@0: unload : "_unload" michael@0: }), { michael@0: constructor: function constructor() { michael@0: // Same thing applies here, we need to pass public interface michael@0: ul.ensure(this._public); michael@0: this._constructor(); michael@0: }, michael@0: unload: function unload() { michael@0: called++; michael@0: this._unload(); michael@0: } michael@0: })(); michael@0: michael@0: obj.unload(); michael@0: assert.equal(called, 1, michael@0: "unload() should be called"); michael@0: michael@0: assert.equal(composedCalled, 1, michael@0: "composed object unload() should be called"); michael@0: michael@0: obj.unload(); michael@0: assert.equal(called, 1, michael@0: "unload() should be called only once"); michael@0: assert.equal(composedCalled, 1, michael@0: "composed object unload() should be called only once"); michael@0: michael@0: loader.unload(); michael@0: assert.equal(called, 1, michael@0: "unload() should be called only once, after addon unload"); michael@0: assert.equal(composedCalled, 1, michael@0: "composed object unload() should be called only once, " + michael@0: "after addon unload"); michael@0: }; michael@0: michael@0: exports.testEnsureWithTraitsPrivate = function(assert) { michael@0: let { Trait } = require("sdk/deprecated/traits"); michael@0: let loader = Loader(module); michael@0: let ul = loader.require("sdk/system/unload"); michael@0: michael@0: let called = 0; michael@0: let privateObj = null; michael@0: let obj = Trait.compose({ michael@0: constructor: function constructor() { michael@0: // This time wa don't have to give public interface, michael@0: // as we want to call a private method: michael@0: ul.ensure(this, "_unload"); michael@0: privateObj = this; michael@0: }, michael@0: _unload: function unload() { michael@0: called++; michael@0: this._unload(); michael@0: } michael@0: })(); michael@0: michael@0: loader.unload(); michael@0: assert.equal(called, 1, michael@0: "unload() should be called"); michael@0: michael@0: privateObj._unload(); michael@0: assert.equal(called, 1, michael@0: "_unload() should be called only once, after addon unload"); michael@0: }; michael@0: michael@0: exports.testReason = function (assert) { michael@0: var reason = "Reason doesn't actually have to be anything in particular."; michael@0: var loader = Loader(module); michael@0: var ul = loader.require("sdk/system/unload"); michael@0: ul.when(function (rsn) { michael@0: assert.equal(rsn, reason, michael@0: "when() reason should be reason given to loader"); michael@0: }); michael@0: var obj = { michael@0: unload: function (rsn) { michael@0: assert.equal(rsn, reason, michael@0: "ensure() reason should be reason given to loader"); michael@0: } michael@0: }; michael@0: ul.ensure(obj); michael@0: loader.unload(reason); michael@0: }; michael@0: michael@0: require("sdk/test").run(exports);