author | Akshendra Pratap <akshendra521994@gmail.com> |
Thu, 30 Oct 2014 08:21:00 -0400 | |
changeset 213965 | 26bc4268f868ccca19336a56bae4663153649aee |
parent 213964 | d64ba06f84721f85777a69aa612f94b0729f5745 |
child 213966 | 92073635b516f8e522ab0f494ffc9df3fcec6309 |
push id | 27769 |
push user | kwierso@gmail.com |
push date | Wed, 05 Nov 2014 03:53:35 +0000 |
treeherder | mozilla-central@62990ec7ad78 [default view] [failures only] |
perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
reviewers | yoric |
bugs | 1080466 |
milestone | 36.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
|
new file mode 100644 --- /dev/null +++ b/toolkit/modules/PromiseUtils.jsm @@ -0,0 +1,54 @@ +/* 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" + +this.EXPORTED_SYMBOLS = ["PromiseUtils"]; + +Components.utils.import("resource://gre/modules/Timer.jsm"); + +this.PromiseUtils = { + /* + * A simple timeout mechanism. + * + * Example: + * resolveOrTimeout(myModule.shutdown(), 1000, new Error("The module took too long to shutdown")); + * + * @param {Promise} promise The Promise that should resolve/reject quickly. + * @param {number} delay A delay after which to stop waiting for `promise`, in milliseconds. + * @param {function} rejection If `promise` hasn't resolved/rejected after `delay`, + * a value used to construct the rejection. + * + * @return {Promise} A promise that behaves as `promise`, if `promise` is + * resolved/rejected within `delay` ms, or rejects with `rejection()` otherwise. + */ + resolveOrTimeout : function(promise, delay, rejection) { + // throw a TypeError if <promise> is not a Promise object + if (!(promise instanceof Promise)) { + throw new TypeError("first argument <promise> must be a Promise object"); + } + + // throw a TypeError if <delay> is not a number + if (typeof delay != "number" || delay < 0) { + throw new TypeError("second argument <delay> must be a positive number"); + } + + // throws a TypeError if <rejection> is not a function + if (rejection && typeof rejection != "function") { + throw new TypeError("third optional argument <rejection> must be a function"); + } + + return new Promise((resolve, reject) => { + promise.then(resolve, reject); + let id = setTimeout(() => { + try { + rejection ? reject(rejection()) : reject(new Error("Promise Timeout")); + } catch(ex) { + reject(ex); + } + clearTimeout(id); + }, delay); + }); + } +} \ No newline at end of file
--- a/toolkit/modules/moz.build +++ b/toolkit/modules/moz.build @@ -31,16 +31,17 @@ EXTRA_JS_MODULES += [ 'NewTabUtils.jsm', 'PageMenu.jsm', 'PermissionsUtils.jsm', 'PopupNotifications.jsm', 'Preferences.jsm', 'PrivateBrowsingUtils.jsm', 'Promise-backend.js', 'Promise.jsm', + 'PromiseUtils.jsm', 'PropertyListUtils.jsm', 'RemoteController.jsm', 'RemoteFinder.jsm', 'RemoteSecurityUI.jsm', 'RemoteWebNavigation.jsm', 'RemoteWebProgress.jsm', 'SelectContentHelper.jsm', 'SelectParentHelper.jsm',
new file mode 100644 --- /dev/null +++ b/toolkit/modules/tests/xpcshell/test_PromiseUtils.js @@ -0,0 +1,78 @@ + /* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +Components.utils.import("resource://gre/modules/PromiseUtils.jsm"); +Components.utils.import("resource://gre/modules/Timer.jsm"); + +// Tests for PromiseUtils.jsm +function run_test() { + run_next_test(); +} + +/* Tests for the case when arguments to resolveOrTimeout + * are not of correct type */ +add_task(function* test_wrong_arguments() { + let p = new Promise((resolve, reject) => {}); + // for the first argument + Assert.throws(() => PromiseUtils.resolveOrTimeout("string", 200), /first argument <promise> must be a Promise object/, + "TypeError thrown because first argument is not a Promsie object"); + // for second argument + Assert.throws(() => PromiseUtils.resolveOrTimeout(p, "string"), /second argument <delay> must be a positive number/, + "TypeError thrown because second argument is not a positive number"); + // for the third argument + Assert.throws(() => PromiseUtils.resolveOrTimeout(p, 200, "string"), /third optional argument <rejection> must be a function/, + "TypeError thrown because thrird argument is not a function"); +}); + +/* Tests for the case when the optional third argument is not provided + * In that case the returned promise rejects with a default Error */ +add_task(function* test_optional_third_argument() { + let p = new Promise((resolve, reject) => {}); + yield Assert.rejects(PromiseUtils.resolveOrTimeout(p, 200), /Promise Timeout/, "Promise rejects with a default Error"); +}); + +/* Test for the case when the passed promise resolves quickly + * In that case the returned promise also resolves with the same value */ +add_task(function* test_resolve_quickly() { + let p = new Promise((resolve, reject) => setTimeout(() => resolve("Promise is resolved"), 20)); + let result = yield PromiseUtils.resolveOrTimeout(p, 200); + Assert.equal(result, "Promise is resolved", "Promise resolves quickly"); +}); + +/* Test for the case when the passed promise rejects quickly + * In that case the returned promise also rejects with the same value */ +add_task(function* test_reject_quickly() { + let p = new Promise((resolve, reject) => setTimeout(() => reject("Promise is rejected"), 20)); + yield Assert.rejects(PromiseUtils.resolveOrTimeout(p, 200), /Promise is rejected/, "Promise rejects quickly"); +}); + +/* Tests for the case when the passed promise doesn't settle + * and rejection returns string/object/undefined */ +add_task(function* test_rejection_function() { + let p = new Promise((resolve, reject) => {}); + // for rejection returning string + yield Assert.rejects(PromiseUtils.resolveOrTimeout(p, 200, () => { + return "Rejection returned a string"; + }), /Rejection returned a string/, "Rejection returned a string"); + + // for rejection returning object + yield Assert.rejects(PromiseUtils.resolveOrTimeout(p, 200, () => { + return {Name:"Promise"}; + }), Object, "Rejection returned an object"); + + // for rejection returning undefined + yield Assert.rejects(PromiseUtils.resolveOrTimeout(p, 200, () => { + return; + }), undefined, "Rejection returned undefined"); +}); + +/* Tests for the case when the passed promise doesn't settles + * and rejection throws an error */ +add_task(function* test_rejection_throw_error() { + let p = new Promise((resolve, reject) => {}); + yield Assert.rejects(PromiseUtils.resolveOrTimeout(p, 200, () => { + throw new Error("Rejection threw an Error"); + }), /Rejection threw an Error/, "Rejection threw an error"); +}); \ No newline at end of file
--- a/toolkit/modules/tests/xpcshell/xpcshell.ini +++ b/toolkit/modules/tests/xpcshell/xpcshell.ini @@ -16,16 +16,17 @@ support-files = # GMPInstallManager is not shipped on Android skip-if = os == 'android' [test_Http.js] [test_Log.js] [test_NewTabUtils.js] [test_PermissionsUtils.js] [test_Preferences.js] [test_Promise.js] +[test_PromiseUtils.js] [test_propertyListsUtils.js] [test_readCertPrefs.js] [test_Services.js] [test_sqlite.js] [test_sqlite_shutdown.js] [test_task.js] [test_TelemetryTimestamps.js] [test_timer.js]