1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/addon-sdk/source/test/test-disposable.js Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,365 @@ 1.4 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.5 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.6 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.7 +"use strict"; 1.8 + 1.9 +const { Loader } = require("sdk/test/loader"); 1.10 +const { Class } = require("sdk/core/heritage"); 1.11 +const { Disposable } = require("sdk/core/disposable"); 1.12 +const { Cc, Ci, Cu } = require("chrome"); 1.13 +const { setTimeout } = require("sdk/timers"); 1.14 + 1.15 +exports["test destroy reasons"] = assert => { 1.16 + const Foo = Class({ 1.17 + extends: Disposable, 1.18 + dispose: function() { 1.19 + disposals = disposals + 1; 1.20 + } 1.21 + }); 1.22 + 1.23 + let disposals = 0; 1.24 + const f1 = new Foo(); 1.25 + 1.26 + f1.destroy(); 1.27 + assert.equal(disposals, 1, "disposed on destroy"); 1.28 + f1.destroy(); 1.29 + assert.equal(disposals, 1, "second destroy is ignored"); 1.30 + 1.31 + disposals = 0; 1.32 + const f2 = new Foo(); 1.33 + 1.34 + f2.destroy("uninstall"); 1.35 + assert.equal(disposals, 1, "uninstall invokes disposal"); 1.36 + f2.destroy("uninstall") 1.37 + f2.destroy(); 1.38 + assert.equal(disposals, 1, "disposal happens just once"); 1.39 + 1.40 + disposals = 0; 1.41 + const f3 = new Foo(); 1.42 + 1.43 + f3.destroy("shutdown"); 1.44 + assert.equal(disposals, 0, "shutdown doesn't invoke disposal"); 1.45 + f3.destroy(); 1.46 + assert.equal(disposals, 0, "shutdown already skipped disposal"); 1.47 + 1.48 + disposals = 0; 1.49 + const f4 = new Foo(); 1.50 + 1.51 + f4.destroy("disable"); 1.52 + assert.equal(disposals, 1, "disable invokes disposal"); 1.53 + f4.destroy("disable") 1.54 + f4.destroy(); 1.55 + assert.equal(disposals, 1, "destroy happens just once"); 1.56 + 1.57 + disposals = 0; 1.58 + const f5 = new Foo(); 1.59 + 1.60 + f5.destroy("disable"); 1.61 + assert.equal(disposals, 1, "disable invokes disposal"); 1.62 + f5.destroy("disable") 1.63 + f5.destroy(); 1.64 + assert.equal(disposals, 1, "destroy happens just once"); 1.65 + 1.66 + disposals = 0; 1.67 + const f6 = new Foo(); 1.68 + 1.69 + f6.destroy("upgrade"); 1.70 + assert.equal(disposals, 1, "upgrade invokes disposal"); 1.71 + f6.destroy("upgrade") 1.72 + f6.destroy(); 1.73 + assert.equal(disposals, 1, "destroy happens just once"); 1.74 + 1.75 + disposals = 0; 1.76 + const f7 = new Foo(); 1.77 + 1.78 + f7.destroy("downgrade"); 1.79 + assert.equal(disposals, 1, "downgrade invokes disposal"); 1.80 + f7.destroy("downgrade") 1.81 + f7.destroy(); 1.82 + assert.equal(disposals, 1, "destroy happens just once"); 1.83 + 1.84 + 1.85 + disposals = 0; 1.86 + const f8 = new Foo(); 1.87 + 1.88 + f8.destroy("whatever"); 1.89 + assert.equal(disposals, 1, "unrecognized reason invokes disposal"); 1.90 + f8.destroy("meh") 1.91 + f8.destroy(); 1.92 + assert.equal(disposals, 1, "destroy happens just once"); 1.93 +}; 1.94 + 1.95 +exports["test different unload hooks"] = assert => { 1.96 + const { uninstall, shutdown, disable, upgrade, 1.97 + downgrade, dispose } = require("sdk/core/disposable"); 1.98 + const UberUnload = Class({ 1.99 + extends: Disposable, 1.100 + setup: function() { 1.101 + this.log = []; 1.102 + } 1.103 + }); 1.104 + 1.105 + uninstall.define(UberUnload, x => x.log.push("uninstall")); 1.106 + shutdown.define(UberUnload, x => x.log.push("shutdown")); 1.107 + disable.define(UberUnload, x => x.log.push("disable")); 1.108 + upgrade.define(UberUnload, x => x.log.push("upgrade")); 1.109 + downgrade.define(UberUnload, x => x.log.push("downgrade")); 1.110 + dispose.define(UberUnload, x => x.log.push("dispose")); 1.111 + 1.112 + const u1 = new UberUnload(); 1.113 + u1.destroy("uninstall"); 1.114 + u1.destroy(); 1.115 + u1.destroy("shutdown"); 1.116 + assert.deepEqual(u1.log, ["uninstall"], "uninstall hook invoked"); 1.117 + 1.118 + const u2 = new UberUnload(); 1.119 + u2.destroy("shutdown"); 1.120 + u2.destroy(); 1.121 + u2.destroy("uninstall"); 1.122 + assert.deepEqual(u2.log, ["shutdown"], "shutdown hook invoked"); 1.123 + 1.124 + const u3 = new UberUnload(); 1.125 + u3.destroy("disable"); 1.126 + u3.destroy(); 1.127 + u3.destroy("uninstall"); 1.128 + assert.deepEqual(u3.log, ["disable"], "disable hook invoked"); 1.129 + 1.130 + const u4 = new UberUnload(); 1.131 + u4.destroy("upgrade"); 1.132 + u4.destroy(); 1.133 + u4.destroy("uninstall"); 1.134 + assert.deepEqual(u4.log, ["upgrade"], "upgrade hook invoked"); 1.135 + 1.136 + const u5 = new UberUnload(); 1.137 + u5.destroy("downgrade"); 1.138 + u5.destroy(); 1.139 + u5.destroy("uninstall"); 1.140 + assert.deepEqual(u5.log, ["downgrade"], "downgrade hook invoked"); 1.141 + 1.142 + const u6 = new UberUnload(); 1.143 + u6.destroy(); 1.144 + u6.destroy(); 1.145 + u6.destroy("uninstall"); 1.146 + assert.deepEqual(u6.log, ["dispose"], "dispose hook invoked"); 1.147 + 1.148 + const u7 = new UberUnload(); 1.149 + u7.destroy("whatever"); 1.150 + u7.destroy(); 1.151 + u7.destroy("uninstall"); 1.152 + assert.deepEqual(u7.log, ["dispose"], "dispose hook invoked"); 1.153 +}; 1.154 + 1.155 +exports["test disposables are desposed on unload"] = function(assert) { 1.156 + let loader = Loader(module); 1.157 + let { Disposable } = loader.require("sdk/core/disposable"); 1.158 + 1.159 + let arg1 = {} 1.160 + let arg2 = 2 1.161 + let disposals = 0 1.162 + 1.163 + let Foo = Class({ 1.164 + extends: Disposable, 1.165 + setup: function setup(a, b) { 1.166 + assert.equal(a, arg1, 1.167 + "arguments passed to constructur is passed to setup"); 1.168 + assert.equal(b, arg2, 1.169 + "second argument is also passed to a setup"); 1.170 + assert.ok(this instanceof Foo, 1.171 + "this is an instance in the scope of the setup method"); 1.172 + 1.173 + this.fooed = true 1.174 + }, 1.175 + dispose: function dispose() { 1.176 + assert.equal(this.fooed, true, "attribute was set") 1.177 + this.fooed = false 1.178 + disposals = disposals + 1 1.179 + } 1.180 + }) 1.181 + 1.182 + let foo1 = Foo(arg1, arg2) 1.183 + let foo2 = Foo(arg1, arg2) 1.184 + 1.185 + loader.unload(); 1.186 + 1.187 + assert.equal(disposals, 2, "both instances were disposed") 1.188 +} 1.189 + 1.190 +exports["test destroyed windows dispose before unload"] = function(assert) { 1.191 + let loader = Loader(module); 1.192 + let { Disposable } = loader.require("sdk/core/disposable"); 1.193 + 1.194 + let arg1 = {} 1.195 + let arg2 = 2 1.196 + let disposals = 0 1.197 + 1.198 + let Foo = Class({ 1.199 + extends: Disposable, 1.200 + setup: function setup(a, b) { 1.201 + assert.equal(a, arg1, 1.202 + "arguments passed to constructur is passed to setup"); 1.203 + assert.equal(b, arg2, 1.204 + "second argument is also passed to a setup"); 1.205 + assert.ok(this instanceof Foo, 1.206 + "this is an instance in the scope of the setup method"); 1.207 + 1.208 + this.fooed = true 1.209 + }, 1.210 + dispose: function dispose() { 1.211 + assert.equal(this.fooed, true, "attribute was set") 1.212 + this.fooed = false 1.213 + disposals = disposals + 1 1.214 + } 1.215 + }) 1.216 + 1.217 + let foo1 = Foo(arg1, arg2) 1.218 + let foo2 = Foo(arg1, arg2) 1.219 + 1.220 + foo1.destroy(); 1.221 + assert.equal(disposals, 1, "destroy disposes instance") 1.222 + 1.223 + loader.unload(); 1.224 + 1.225 + assert.equal(disposals, 2, "unload disposes alive instances") 1.226 +} 1.227 + 1.228 +exports["test disposables are GC-able"] = function(assert, done) { 1.229 + let loader = Loader(module); 1.230 + let { Disposable } = loader.require("sdk/core/disposable"); 1.231 + let { WeakReference } = loader.require("sdk/core/reference"); 1.232 + 1.233 + let arg1 = {} 1.234 + let arg2 = 2 1.235 + let disposals = 0 1.236 + 1.237 + let Foo = Class({ 1.238 + extends: Disposable, 1.239 + implements: [WeakReference], 1.240 + setup: function setup(a, b) { 1.241 + assert.equal(a, arg1, 1.242 + "arguments passed to constructur is passed to setup"); 1.243 + assert.equal(b, arg2, 1.244 + "second argument is also passed to a setup"); 1.245 + assert.ok(this instanceof Foo, 1.246 + "this is an instance in the scope of the setup method"); 1.247 + 1.248 + this.fooed = true 1.249 + }, 1.250 + dispose: function dispose() { 1.251 + assert.equal(this.fooed, true, "attribute was set") 1.252 + this.fooed = false 1.253 + disposals = disposals + 1 1.254 + } 1.255 + }); 1.256 + 1.257 + let foo1 = Foo(arg1, arg2) 1.258 + let foo2 = Foo(arg1, arg2) 1.259 + 1.260 + let foo1 = null 1.261 + let foo2 = null 1.262 + 1.263 + Cu.schedulePreciseGC(function() { 1.264 + loader.unload(); 1.265 + assert.equal(disposals, 0, "GC removed dispose listeners"); 1.266 + done(); 1.267 + }); 1.268 +} 1.269 + 1.270 + 1.271 +exports["test loader unloads do not affect other loaders"] = function(assert) { 1.272 + let loader1 = Loader(module); 1.273 + let loader2 = Loader(module); 1.274 + let { Disposable: Disposable1 } = loader1.require("sdk/core/disposable"); 1.275 + let { Disposable: Disposable2 } = loader2.require("sdk/core/disposable"); 1.276 + 1.277 + let arg1 = {} 1.278 + let arg2 = 2 1.279 + let disposals = 0 1.280 + 1.281 + let Foo1 = Class({ 1.282 + extends: Disposable1, 1.283 + dispose: function dispose() { 1.284 + disposals = disposals + 1; 1.285 + } 1.286 + }); 1.287 + 1.288 + let Foo2 = Class({ 1.289 + extends: Disposable2, 1.290 + dispose: function dispose() { 1.291 + disposals = disposals + 1; 1.292 + } 1.293 + }); 1.294 + 1.295 + let foo1 = Foo1(arg1, arg2); 1.296 + let foo2 = Foo2(arg1, arg2); 1.297 + 1.298 + assert.equal(disposals, 0, "no destroy calls"); 1.299 + 1.300 + loader1.unload(); 1.301 + 1.302 + assert.equal(disposals, 1, "1 destroy calls"); 1.303 + 1.304 + loader2.unload(); 1.305 + 1.306 + assert.equal(disposals, 2, "2 destroy calls"); 1.307 +} 1.308 + 1.309 +exports["test disposables that throw"] = function(assert) { 1.310 + let loader = Loader(module); 1.311 + let { Disposable } = loader.require("sdk/core/disposable"); 1.312 + 1.313 + let disposals = 0 1.314 + 1.315 + let Foo = Class({ 1.316 + extends: Disposable, 1.317 + setup: function setup(a, b) { 1.318 + throw Error("Boom!") 1.319 + }, 1.320 + dispose: function dispose() { 1.321 + disposals = disposals + 1 1.322 + } 1.323 + }) 1.324 + 1.325 + assert.throws(function() { 1.326 + let foo1 = Foo() 1.327 + }, /Boom/, "disposable constructors may throw"); 1.328 + 1.329 + loader.unload(); 1.330 + 1.331 + assert.equal(disposals, 0, "no disposal if constructor threw"); 1.332 +} 1.333 + 1.334 +exports["test multiple destroy"] = function(assert) { 1.335 + let loader = Loader(module); 1.336 + let { Disposable } = loader.require("sdk/core/disposable"); 1.337 + 1.338 + let disposals = 0 1.339 + 1.340 + let Foo = Class({ 1.341 + extends: Disposable, 1.342 + dispose: function dispose() { 1.343 + disposals = disposals + 1 1.344 + } 1.345 + }) 1.346 + 1.347 + let foo1 = Foo(); 1.348 + let foo2 = Foo(); 1.349 + let foo3 = Foo(); 1.350 + 1.351 + assert.equal(disposals, 0, "no disposals yet"); 1.352 + 1.353 + foo1.destroy(); 1.354 + assert.equal(disposals, 1, "disposed properly"); 1.355 + foo1.destroy(); 1.356 + assert.equal(disposals, 1, "didn't attempt to dispose twice"); 1.357 + 1.358 + foo2.destroy(); 1.359 + assert.equal(disposals, 2, "other instances still dispose fine"); 1.360 + foo2.destroy(); 1.361 + assert.equal(disposals, 2, "but not twice"); 1.362 + 1.363 + loader.unload(); 1.364 + 1.365 + assert.equal(disposals, 3, "unload only disposed the remaining instance"); 1.366 +} 1.367 + 1.368 +require('test').run(exports);