addon-sdk/source/test/test-promise.js

Thu, 15 Jan 2015 15:59:08 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 15 Jan 2015 15:59:08 +0100
branch
TOR_BUG_9701
changeset 10
ac0c01689b40
permissions
-rw-r--r--

Implement a real Private Browsing Mode condition by changing the API/ABI;
This solves Tor bug #9701, complying with disk avoidance documented in
https://www.torproject.org/projects/torbrowser/design/#disk-avoidance.

     1 /* This Source Code Form is subject to the terms of the Mozilla Public
     2  * License, v. 2.0. If a copy of the MPL was not distributed with this
     3  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     5 'use strict';
     7 const { Cc, Cu, Ci } = require('chrome');
     8 const { setTimeout } = require('sdk/timers');
     9 const { prefixURI, name } = require('@loader/options');
    10 const addonPromiseURI = prefixURI + name + '/lib/sdk/core/promise.js';
    11 const builtPromiseURI = 'resource://gre/modules/commonjs/sdk/core/promise.js';
    12 let { Promise, defer, resolve, reject, all, promised } = require('sdk/core/promise');
    14 exports['test all observers are notified'] = function(assert, done) {
    15   let expected = 'Taram pam param!';
    16   let deferred = defer();
    17   let pending = 10, i = 0;
    19   function resolved(value) {
    20     assert.equal(value, expected, 'value resolved as expected: #' + pending);
    21     if (!--pending) done();
    22   }
    24   while (i++ < pending) deferred.promise.then(resolved);
    26   deferred.resolve(expected);
    27 };
    29 exports['test exceptions dont stop notifications'] = function(assert, done) {
    30   let threw = false, boom = Error('Boom!');
    31   let deferred = defer();
    33   let promise2 = deferred.promise.then(function() {
    34     threw = true;
    35     throw boom;
    36   });
    38   deferred.promise.then(function() {
    39     assert.ok(threw, 'observer is called even though previos one threw');
    40     promise2.then(function() {
    41       assert.fail('should not resolve');
    42     }, function(reason) {
    43       assert.equal(reason, boom, 'rejects to thrown error');
    44       done();
    45     });
    46   });
    48   deferred.resolve('go!');
    49 };
    51 exports['test subsequent resolves are ignored'] = function(assert, done) {
    52   let deferred = defer();
    53   deferred.resolve(1);
    54   deferred.resolve(2);
    55   deferred.reject(3);
    57   deferred.promise.then(function(actual) {
    58     assert.equal(actual, 1, 'resolves to first value');
    59   }, function() {
    60     assert.fail('must not reject');
    61   });
    62   deferred.promise.then(function(actual) {
    63     assert.equal(actual, 1, 'subsequent resolutions are ignored');
    64     done();
    65   }, function() {
    66     assert.fail('must not reject');
    67   });
    68 };
    70 exports['test subsequent rejections are ignored'] = function(assert, done) {
    71   let deferred = defer();
    72   deferred.reject(1);
    73   deferred.resolve(2);
    74   deferred.reject(3);
    76   deferred.promise.then(function(actual) {
    77     assert.fail('must not resolve');
    78   }, function(actual) {
    79     assert.equal(actual, 1, 'must reject to first');
    80   });
    81   deferred.promise.then(function(actual) {
    82     assert.fail('must not resolve');
    83   }, function(actual) {
    84     assert.equal(actual, 1, 'must reject to first');
    85     done();
    86   });
    87 };
    89 exports['test error recovery'] = function(assert, done) {
    90   let boom = Error('Boom!');
    91   let deferred = defer();
    93   deferred.promise.then(function() {
    94     assert.fail('rejected promise should not resolve');
    95   }, function(reason) {
    96     assert.equal(reason, boom, 'rejection reason delivered');
    97     return 'recovery';
    98   }).then(function(value) {
    99     assert.equal(value, 'recovery', 'error handled by a handler');
   100     done();
   101   });
   103   deferred.reject(boom);
   104 };
   106 exports['test error recovery with promise'] = function(assert, done) {
   107   let deferred = defer();
   109   deferred.promise.then(function() {
   110     assert.fail('must reject');
   111   }, function(actual) {
   112     assert.equal(actual, 'reason', 'rejected');
   113     let deferred = defer();
   114     deferred.resolve('recovery');
   115     return deferred.promise;
   116   }).then(function(actual) {
   117     assert.equal(actual, 'recovery', 'recorvered via promise');
   118     let deferred = defer();
   119     deferred.reject('error');
   120     return deferred.promise;
   121   }).then(null, function(actual) {
   122     assert.equal(actual, 'error', 'rejected via promise');
   123     let deferred = defer();
   124     deferred.reject('end');
   125     return deferred.promise;
   126   }).then(null, function(actual) {
   127     assert.equal(actual, 'end', 'rejeced via promise');
   128     done();
   129   });
   131   deferred.reject('reason');
   132 };
   134 exports['test propagation'] = function(assert, done) {
   135   let d1 = defer(), d2 = defer(), d3 = defer();
   137   d1.promise.then(function(actual) {
   138     assert.equal(actual, 'expected', 'resolves to expected value');
   139     done();
   140   });
   142   d1.resolve(d2.promise);
   143   d2.resolve(d3.promise);
   144   d3.resolve('expected');
   145 };
   147 exports['test chaining'] = function(assert, done) {
   148   let boom = Error('boom'), brax = Error('braxXXx');
   149   let deferred = defer();
   151   deferred.promise.then().then().then(function(actual) {
   152     assert.equal(actual, 2, 'value propagates unchanged');
   153     return actual + 2;
   154   }).then(null, function(reason) {
   155     assert.fail('should not reject');
   156   }).then(function(actual) {
   157     assert.equal(actual, 4, 'value propagates through if not handled');
   158     throw boom;
   159   }).then(function(actual) {
   160     assert.fail('exception must reject promise');
   161   }).then().then(null, function(actual) {
   162     assert.equal(actual, boom, 'reason propagates unchanged');
   163     throw brax;
   164   }).then().then(null, function(actual) {
   165     assert.equal(actual, brax, 'reason changed becase of exception');
   166     return 'recovery';
   167   }).then(function(actual) {
   168     assert.equal(actual, 'recovery', 'recovered from error');
   169     done();
   170   });
   172   deferred.resolve(2);
   173 };
   175 exports['test reject'] = function(assert, done) {
   176   let expected = Error('boom');
   178   reject(expected).then(function() {
   179     assert.fail('should reject');
   180   }, function(actual) {
   181     assert.equal(actual, expected, 'rejected with expected reason');
   182   }).then(done, assert.fail);
   183 };
   185 exports['test resolve to rejected'] = function(assert, done) {
   186   let expected = Error('boom');
   187   let deferred = defer();
   189   deferred.promise.then(function() {
   190     assert.fail('should reject');
   191   }, function(actual) {
   192     assert.equal(actual, expected, 'rejected with expected failure');
   193   }).then(done, assert.fail);
   195   deferred.resolve(reject(expected));
   196 };
   198 exports['test resolve'] = function(assert, done) {
   199   let expected = 'value';
   200   resolve(expected).then(function(actual) {
   201     assert.equal(actual, expected, 'resolved as expected');
   202   }).catch(assert.fail).then(done);
   203 };
   205 exports['test promised with normal args'] = function(assert, done) {
   206   let sum = promised((x, y) => x + y );
   208   sum(7, 8).then(function(actual) {
   209     assert.equal(actual, 7 + 8, 'resolves as expected');
   210   }).catch(assert.fail).then(done);
   211 };
   213 exports['test promised with promise args'] = function(assert, done) {
   214   let sum = promised((x, y) => x + y );
   215   let deferred = defer();
   217   sum(11, deferred.promise).then(function(actual) {
   218     assert.equal(actual, 11 + 24, 'resolved as expected');
   219   }).catch(assert.fail).then(done);
   221   deferred.resolve(24);
   222 };
   224 exports['test promised error handleing'] = function(assert, done) {
   225   let expected = Error('boom');
   226   let f = promised(function() {
   227     throw expected;
   228   });
   230   f().then(function() {
   231     assert.fail('should reject');
   232   }, function(actual) {
   233     assert.equal(actual, expected, 'rejected as expected');
   234   }).catch(assert.fail).then(done);
   235 };
   237 exports['test errors in promise resolution handlers are propagated'] = function(assert, done) {
   238   var expected = Error('Boom');
   239   var { promise, resolve } = defer();
   241   promise.then(function() {
   242     throw expected;
   243   }).then(function() {
   244     return undefined;
   245   }).then(null, function(actual) {
   246     assert.equal(actual, expected, 'rejected as expected');
   247   }).then(done, assert.fail);
   249   resolve({});
   250 };
   252 exports['test return promise form promised'] = function(assert, done) {
   253   let f = promised(function() {
   254     return resolve(17);
   255   });
   257   f().then(function(actual) {
   258     assert.equal(actual, 17, 'resolves to a promise resolution');
   259   }).catch(assert.fail).then(done);
   260 };
   262 exports['test promised returning failure'] = function(assert, done) {
   263   let expected = Error('boom');
   264   let f = promised(function() {
   265     return reject(expected);
   266   });
   268   f().then(function() {
   269     assert.fail('must reject');
   270   }, function(actual) {
   271     assert.equal(actual, expected, 'rejects with expected reason');
   272   }).catch(assert.fail).then(done);
   273 };
   275 /*
   276  * Changed for compliance in Bug 881047, promises are now always async
   277  */
   278 exports['test promises are always async'] = function (assert, done) {
   279   let runs = 0;
   280   resolve(1)
   281     .then(val => ++runs)
   282     .catch(assert.fail).then(done);
   283   assert.equal(runs, 0, 'resolutions are called in following tick');
   284 };
   286 /*
   287  * Changed for compliance in Bug 881047, promised's are now non greedy
   288  */
   289 exports['test promised are not greedy'] = function(assert, done) {
   290   let runs = 0;
   291   promised(() => ++runs)()
   292     .catch(assert.fail).then(done);
   293   assert.equal(runs, 0, 'promised does not run task right away');
   294 };
   296 exports['test arrays should not flatten'] = function(assert, done) {
   297   let a = defer();
   298   let b = defer();
   300   let combine = promised(function(str, arr) {
   301     assert.equal(str, 'Hello', 'Array was not flattened');
   302     assert.deepEqual(arr, [ 'my', 'friend' ]);
   303   });
   305   combine(a.promise, b.promise).catch(assert.fail).then(done);
   308   a.resolve('Hello');
   309   b.resolve([ 'my', 'friend' ]);
   310 };
   312 exports['test `all` for all promises'] = function (assert, done) {
   313   all([
   314     resolve(5), resolve(7), resolve(10)
   315   ]).then(function (val) {
   316     assert.equal(
   317       val[0] === 5 &&
   318       val[1] === 7 &&
   319       val[2] === 10
   320     , true, 'return value contains resolved promises values');
   321     done();
   322   }, function () {
   323     assert.fail('should not call reject function');
   324   });
   325 };
   327 exports['test `all` aborts upon first reject'] = function (assert, done) {
   328   all([
   329     resolve(5), reject('error'), delayedResolve()
   330   ]).then(function (val) {
   331     assert.fail('Successful resolve function should not be called');
   332   }, function (reason) {
   333     assert.equal(reason, 'error', 'should reject the `all` promise');
   334     done();
   335   });
   337   function delayedResolve () {
   338     let deferred = defer();
   339     setTimeout(deferred.resolve, 50);
   340     return deferred.promise;
   341   }
   342 };
   344 exports['test `all` with array containing non-promise'] = function (assert, done) {
   345   all([
   346     resolve(5), resolve(10), 925
   347   ]).then(function (val) {
   348     assert.equal(val[2], 925, 'non-promises should pass-through value');
   349     done();
   350   }, function () {
   351     assert.fail('should not be rejected');
   352   });
   353 };
   355 exports['test `all` should resolve with an empty array'] = function (assert, done) {
   356   all([]).then(function (val) {
   357     assert.equal(Array.isArray(val), true, 'should return array in resolved');
   358     assert.equal(val.length, 0, 'array should be empty in resolved');
   359     done();
   360   }, function () {
   361     assert.fail('should not be rejected');
   362   });
   363 };
   365 exports['test `all` with multiple rejected'] = function (assert, done) {
   366   all([
   367     reject('error1'), reject('error2'), reject('error3')
   368   ]).then(function (value) {
   369     assert.fail('should not be successful');
   370   }, function (reason) {
   371     assert.equal(reason, 'error1', 'should reject on first promise reject');
   372     done();
   373   });
   374 };
   376 exports['test Promise constructor resolve'] = function (assert, done) {
   377   var isAsync = true;
   378   new Promise(function (resolve, reject) {
   379     resolve(5);
   380   }).then(x => {
   381     isAsync = false;
   382     assert.equal(x, 5, 'Promise constructor resolves correctly');
   383   }).catch(assert.fail).then(done);
   384   assert.ok(isAsync, 'Promise constructor runs async');
   385 };
   387 exports['test Promise constructor reject'] = function (assert, done) {
   388   new Promise(function (resolve, reject) {
   389     reject(new Error('deferred4life'));
   390   }).then(assert.fail, (err) => {
   391     assert.equal(err.message, 'deferred4life', 'Promise constructor rejects correctly');
   392   }).catch(assert.fail).then(done);
   393 };
   395 exports['test JSM Load and API'] = function (assert, done) {
   396   // Use addon URL when loading from cfx/local:
   397   // resource://90111c90-c31e-4dc7-ac35-b65947434435-at-jetpack/addon-sdk/lib/sdk/core/promise.js
   398   // Use built URL when testing on try, etc.
   399   // resource://gre/modules/commonjs/sdk/core/promise.js
   400   try {
   401     var { Promise } = Cu.import(addonPromiseURI, {});
   402   } catch (e) {
   403     var { Promise } = Cu.import(builtPromiseURI, {});
   404   }
   405   testEnvironment(Promise, assert, done, 'JSM');
   406 };
   408 exports['test mozIJSSubScriptLoader exporting'] = function (assert, done) {
   409   let { Services } = Cu.import('resource://gre/modules/Services.jsm', {});
   410   let systemPrincipal = Services.scriptSecurityManager.getSystemPrincipal();
   411   let Promise = new Cu.Sandbox(systemPrincipal);
   412   let loader = Cc['@mozilla.org/moz/jssubscript-loader;1']
   413                  .getService(Ci.mozIJSSubScriptLoader);
   415   // Use addon URL when loading from cfx/local:
   416   // resource://90111c90-c31e-4dc7-ac35-b65947434435-at-jetpack/addon-sdk/lib/sdk/core/promise.js
   417   // Use built URL when testing on try, etc.
   418   // resource://gre/modules/commonjs/sdk/core/promise.js
   419   try {
   420     loader.loadSubScript(addonPromiseURI, Promise);
   421   } catch (e) {
   422     loader.loadSubScript(builtPromiseURI, Promise);
   423   }
   425   testEnvironment(Promise, assert, done, 'mozIJSSubScript');
   426 };
   428 function testEnvironment ({all, resolve, defer, reject, promised}, assert, done, type) {
   429   all([resolve(5), resolve(10), 925]).then(val => {
   430     assert.equal(val[0], 5, 'promise#all works ' + type);
   431     assert.equal(val[1], 10, 'promise#all works ' + type);
   432     assert.equal(val[2], 925, 'promise#all works ' + type);
   433     return resolve(1000);
   434   }).then(value => {
   435     assert.equal(value, 1000, 'promise#resolve works ' + type);
   436     return reject('testing reject');
   437   }).then(null, reason => {
   438     assert.equal(reason, 'testing reject', 'promise#reject works ' + type);
   439     let deferred = defer();
   440     setTimeout(() => deferred.resolve('\\m/'), 10);
   441     return deferred.promise;
   442   }).then(value => {
   443     assert.equal(value, '\\m/', 'promise#defer works ' + type);
   444     return promised(x => x * x)(5);
   445   }).then(value => {
   446     assert.equal(value, 25, 'promise#promised works ' + type);
   447   }).then(done, assert.fail);
   448 }
   450 require("sdk/test").run(exports);

mercurial