Backed out changeset 295b75fac0c8 (bug 940408) for test failures in B2g Desktop Linux & B2G ICS Emulator Opt

This commit is contained in:
Carsten "Tomcat" Book 2013-12-03 12:53:27 +01:00
parent ed69377fa1
commit 325e1c28d4
10 changed files with 293 additions and 719 deletions

View File

@ -2549,7 +2549,7 @@ OverflowableToolbar.prototype = {
this._lazyResizeHandler = new DeferredTask(this._onLazyResize.bind(this),
LAZY_RESIZE_INTERVAL_MS);
}
this._lazyResizeHandler.arm();
this._lazyResizeHandler.start();
},
_moveItemsBackToTheirOrigin: function(shouldMoveAllItems) {
@ -2608,7 +2608,7 @@ OverflowableToolbar.prototype = {
this._enabled = false;
this._moveItemsBackToTheirOrigin(true);
if (this._lazyResizeHandler) {
this._lazyResizeHandler.disarm();
this._lazyResizeHandler.cancel();
}
},

View File

@ -25,8 +25,6 @@ const Cr = Components.results;
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "DeferredTask",
"resource://gre/modules/DeferredTask.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "Downloads",
"resource://gre/modules/Downloads.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "DownloadStore",
@ -1100,7 +1098,6 @@ this.DownloadAutoSaveView = function (aList, aStore)
this._list = aList;
this._store = aStore;
this._downloadsMap = new Map();
this._writer = new DeferredTask(() => this._store.save(), kSaveDelayMs);
}
this.DownloadAutoSaveView.prototype = {
@ -1141,9 +1138,45 @@ this.DownloadAutoSaveView.prototype = {
_downloadsMap: null,
/**
* DeferredTask for the save operation.
* This is set to true when the save operation should be triggered. This is
* required so that a new operation can be scheduled while the current one is
* in progress, without re-entering the save method.
*/
_writer: null,
_shouldSave: false,
/**
* nsITimer used for triggering the save operation after a delay, or null if
* saving has finished and there is no operation scheduled for execution.
*
* The logic here is different from the DeferredTask module in that multiple
* requests will never delay the operation for longer than the expected time
* (no grace delay), and the operation is never re-entered during execution.
*/
_timer: null,
/**
* Timer callback used to serialize the list of downloads.
*/
_save: function ()
{
Task.spawn(function () {
// Any save request received during execution will be handled later.
this._shouldSave = false;
// Execute the asynchronous save operation.
try {
yield this._store.save();
} catch (ex) {
Cu.reportError(ex);
}
// Handle requests received during the operation.
this._timer = null;
if (this._shouldSave) {
this.saveSoon();
}
}.bind(this)).then(null, Cu.reportError);
},
/**
* Called when the list of downloads changed, this triggers the asynchronous
@ -1151,7 +1184,11 @@ this.DownloadAutoSaveView.prototype = {
*/
saveSoon: function ()
{
this._writer.arm();
this._shouldSave = true;
if (!this._timer) {
this._timer = new Timer(this._save.bind(this), kSaveDelayMs,
Ci.nsITimer.TYPE_ONE_SHOT);
}
},
//////////////////////////////////////////////////////////////////////////////

View File

@ -11,8 +11,6 @@ Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
Components.utils.import("resource://gre/modules/Services.jsm");
Components.utils.import("resource://gre/modules/commonjs/sdk/core/promise.js");
XPCOMUtils.defineLazyModuleGetter(this, "AsyncShutdown",
"resource://gre/modules/AsyncShutdown.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "DeferredTask",
"resource://gre/modules/DeferredTask.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "OS",
@ -2657,7 +2655,7 @@ Engine.prototype = {
url.addParam(aName, aValue);
// Serialize the changes to file lazily
this.lazySerializeTask.arm();
this.lazySerializeTask.start();
},
#ifdef ANDROID
@ -3873,8 +3871,7 @@ SearchService.prototype = {
engine._initFromMetadata(aName, aIconURL, aAlias, aDescription,
aMethod, aTemplate);
this._addEngineToStore(engine);
this.batchTask.disarm();
this.batchTask.arm();
this.batchTask.start();
},
addEngine: function SRCH_SVC_addEngine(aEngineURL, aDataType, aIconURL,
@ -3937,10 +3934,9 @@ SearchService.prototype = {
engineToRemove.hidden = true;
engineToRemove.alias = null;
} else {
// Cancel the serialized task if it's pending. Since the task is a
// synchronous function, we don't need to wait on the "finalize" method.
// Cancel the serialized task if it's running
if (engineToRemove._lazySerializeTask) {
engineToRemove._lazySerializeTask.disarm();
engineToRemove._lazySerializeTask.cancel();
engineToRemove._lazySerializeTask = null;
}
@ -4139,19 +4135,22 @@ SearchService.prototype = {
LOG("nsSearchService::observe: setting current");
this.currentEngine = aEngine;
}
this.batchTask.disarm();
this.batchTask.arm();
this.batchTask.start();
break;
case SEARCH_ENGINE_CHANGED:
case SEARCH_ENGINE_REMOVED:
this.batchTask.disarm();
this.batchTask.arm();
this.batchTask.start();
break;
}
break;
case QUIT_APPLICATION_TOPIC:
this._removeObservers();
if (this._batchTask) {
// Flush to disk immediately
this._batchTask.flush();
}
engineMetadataService.flush();
break;
case "nsPref:changed":
@ -4210,16 +4209,6 @@ SearchService.prototype = {
Services.obs.addObserver(this, QUIT_APPLICATION_TOPIC, false);
Services.prefs.addObserver(BROWSER_SEARCH_PREF + "defaultenginename", this, false);
Services.prefs.addObserver(BROWSER_SEARCH_PREF + "selectedEngine", this, false);
AsyncShutdown.profileBeforeChange.addBlocker(
"Search service: shutting down",
() => Task.spawn(function () {
if (this._batchTask) {
yield this._batchTask.finalize().then(null, Cu.reportError);
}
yield engineMetadataService.finalize();
}.bind(this))
);
},
_removeObservers: function SRCH_SVC_removeObservers() {
@ -4439,8 +4428,11 @@ var engineMetadataService = {
/**
* Flush any waiting write.
*/
finalize: function () this._lazyWriter ? this._lazyWriter.finalize()
: Promise.resolve(),
flush: function epsFlush() {
if (this._lazyWriter) {
this._lazyWriter.flush();
}
},
/**
* Commit changes to disk, asynchronously.
@ -4477,14 +4469,12 @@ var engineMetadataService = {
LOG("metadata writeCommit: done");
}
);
// Use our error logging instead of the default one.
return TaskUtils.captureErrors(promise).then(null, () => {});
TaskUtils.captureErrors(promise);
}
this._lazyWriter = new DeferredTask(writeCommit, LAZY_SERIALIZE_DELAY);
}
LOG("metadata _commit: (re)setting timer");
this._lazyWriter.disarm();
this._lazyWriter.arm();
this._lazyWriter.start();
},
_lazyWriter: null
};

View File

@ -187,17 +187,16 @@ let ActiveProviders = {
add: function (origin) {
this._providers[origin] = 1;
this._deferredTask.arm();
this._deferredTask.start();
},
delete: function (origin) {
delete this._providers[origin];
this._deferredTask.arm();
this._deferredTask.start();
},
flush: function () {
this._deferredTask.disarm();
this._persist();
this._deferredTask.flush();
},
get _deferredTask() {

View File

@ -1,299 +1,85 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=2 et sw=2 tw=80 filetype=javascript: */
/* 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 = ["DeferredTask"];
this.EXPORTED_SYMBOLS = [
"DeferredTask",
];
const Cu = Components.utils;
const Cc = Components.classes;
const Ci = Components.interfaces;
/**
* Sets up a function or an asynchronous task whose execution can be triggered
* after a defined delay. Multiple attempts to run the task before the delay
* has passed are coalesced. The task cannot be re-entered while running, but
* can be executed again after a previous run finished.
* A task that will run after a delay. Multiple attempts to run the same task
* before the delay will be coalesced
*
* A common use case occurs when a data structure should be saved into a file
* every time the data changes, using asynchronous calls, and multiple changes
* to the data may happen within a short time:
* Use this for instance if you write data to a file and you expect that you
* may have to rewrite data very soon afterwards. With |DeferredTask|, the
* task is delayed by a few milliseconds and, should a new change to the data
* occur during this period,
* 1/ only the final version of the data is actually written;
* 2/ a further grace delay is added to take into account other changes.
*
* let saveDeferredTask = new DeferredTask(function* () {
* yield OS.File.writeAtomic(...);
* // Any uncaught exception will be reported.
* }, 2000);
*
* // The task is ready, but will not be executed until requested.
*
* The "arm" method can be used to start the internal timer that will result in
* the eventual execution of the task. Multiple attempts to arm the timer don't
* introduce further delays:
*
* saveDeferredTask.arm();
*
* // The task will be executed in 2 seconds from now.
*
* yield waitOneSecond();
* saveDeferredTask.arm();
*
* // The task will be executed in 1 second from now.
*
* The timer can be disarmed to reset the delay, or just to cancel execution:
*
* saveDeferredTask.disarm();
* saveDeferredTask.arm();
*
* // The task will be executed in 2 seconds from now.
*
* When the internal timer fires and the execution of the task starts, the task
* cannot be canceled anymore. It is however possible to arm the timer again
* during the execution of the task, in which case the task will need to finish
* before the timer is started again, thus guaranteeing a time of inactivity
* between executions that is at least equal to the provided delay.
*
* The "finalize" method can be used to ensure that the task terminates
* properly. The promise it returns is resolved only after the last execution
* of the task is finished. To guarantee that the task is executed for the
* last time, the method prevents any attempt to arm the timer again.
*
* If the timer is already armed when the "finalize" method is called, then the
* task is executed immediately. If the task was already running at this point,
* then one last execution from start to finish will happen again, immediately
* after the current execution terminates. If the timer is not armed, the
* "finalize" method only ensures that any running task terminates.
*
* For example, during shutdown, you may want to ensure that any pending write
* is processed, using the latest version of the data if the timer is armed:
*
* AsyncShutdown.profileBeforeChange.addBlocker(
* "Example service: shutting down",
* () => saveDeferredTask.finalize()
* );
*
* Instead, if you are going to delete the saved data from disk anyways, you
* might as well prevent any pending write from starting, while still ensuring
* that any write that is currently in progress terminates, so that the file is
* not in use anymore:
*
* saveDeferredTask.disarm();
* saveDeferredTask.finalize().then(() => OS.File.remove(...))
* .then(null, Components.utils.reportError);
* Constructor
* @param aDelay The delay time in milliseconds.
* @param aCallback The code to execute after the delay.
*/
////////////////////////////////////////////////////////////////////////////////
//// Globals
const { classes: Cc, interfaces: Ci, utils: Cu, results: Cr } = Components;
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "Promise",
"resource://gre/modules/Promise.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "Task",
"resource://gre/modules/Task.jsm");
const Timer = Components.Constructor("@mozilla.org/timer;1", "nsITimer",
"initWithCallback");
////////////////////////////////////////////////////////////////////////////////
//// DeferredTask
/**
* Sets up a task whose execution can be triggered after a delay.
*
* @param aTaskFn
* Function or generator function to execute. This argument is passed to
* the "Task.spawn" method every time the task should be executed. This
* task is never re-entered while running.
* @param aDelayMs
* Time between executions, in milliseconds. Multiple attempts to run
* the task before the delay has passed are coalesced. This time of
* inactivity is guaranteed to pass between multiple executions of the
* task, except on finalization, when the task may restart immediately
* after the previous execution finished.
*/
this.DeferredTask = function (aTaskFn, aDelayMs) {
this._taskFn = aTaskFn;
this._delayMs = aDelayMs;
this.DeferredTask = function DeferredTask(aCallback, aDelay) {
this._callback = function onCallback() {
this._timer = null;
try {
aCallback();
} catch(e) {
Cu.reportError(e);
}
}.bind(this);
this._delay = aDelay;
}
this.DeferredTask.prototype = {
/**
* Function or generator function to execute.
*/
_taskFn: null,
/**
* Time between executions, in milliseconds.
*/
_delayMs: null,
/**
* Indicates whether the task is currently requested to start again later,
* regardless of whether it is currently running.
*/
get isArmed() this._armed,
_armed: false,
/**
* Indicates whether the task is currently running. This is always true when
* read from code inside the task function, but can also be true when read
* from external code, in case the task is an asynchronous generator function.
*/
get isRunning() !!this._runningPromise,
/**
* Promise resolved when the current execution of the task terminates, or null
* if the task is not currently running.
*/
_runningPromise: null,
/**
* nsITimer used for triggering the task after a delay, or null in case the
* task is running or there is no task scheduled for execution.
*/
DeferredTask.prototype = {
/* Callback */
_callback: null,
/* Delay */
_delay: null,
/* Timer */
_timer: null,
/**
* Actually starts the timer with the delay specified on construction.
* Check the current task state.
* @returns true if pending, false otherwise.
*/
_startTimer: function ()
{
this._timer = new Timer(this._timerCallback.bind(this), this._delayMs,
Ci.nsITimer.TYPE_ONE_SHOT);
get isPending() {
return (this._timer != null);
},
/**
* Requests the execution of the task after the delay specified on
* construction. Multiple calls don't introduce further delays. If the task
* is running, the delay will start when the current execution finishes.
*
* The task will always be executed on a different tick of the event loop,
* even if the delay specified on construction is zero. Multiple "arm" calls
* within the same tick of the event loop are guaranteed to result in a single
* execution of the task.
*
* @note By design, this method doesn't provide a way for the caller to detect
* when the next execution terminates, or collect a result. In fact,
* doing that would often result in duplicate processing or logging. If
* a special operation or error logging is needed on completion, it can
* be better handled from within the task itself, for example using a
* try/catch/finally clause in the task. The "finalize" method can be
* used in the common case of waiting for completion on shutdown.
* Start (or postpone) task.
*/
arm: function ()
{
if (this._finalized) {
throw new Error("Unable to arm timer, the object has been finalized.");
}
this._armed = true;
// In case the timer callback is running, do not create the timer now,
// because this will be handled by the timer callback itself. Also, the
// timer is not restarted in case it is already running.
if (!this._runningPromise && !this._timer) {
this._startTimer();
}
},
/**
* Cancels any request for a delayed the execution of the task, though the
* task itself cannot be canceled in case it is already running.
*
* This method stops any currently running timer, thus the delay will restart
* from its original value in case the "arm" method is called again.
*/
disarm: function () {
this._armed = false;
start: function DeferredTask_start() {
if (this._timer) {
this._timer.cancel();
}
this._timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
this._timer.initWithCallback(
this._callback, this._delay, Ci.nsITimer.TYPE_ONE_SHOT);
},
/**
* Perform any postponed task immediately.
*/
flush: function DeferredTask_flush() {
if (this._timer) {
this.cancel();
this._callback();
}
},
/**
* Cancel any pending task.
*/
cancel: function DeferredTask_cancel() {
if (this._timer) {
// Calling the "cancel" method and discarding the timer reference makes
// sure that the timer callback will not be called later, even if the
// timer thread has already posted the timer event on the main thread.
this._timer.cancel();
this._timer = null;
}
},
/**
* Ensures that any pending task is executed from start to finish, while
* preventing any attempt to arm the timer again.
*
* - If the task is running and the timer is armed, then one last execution
* from start to finish will happen again, immediately after the current
* execution terminates, then the returned promise will be resolved.
* - If the task is running and the timer is not armed, the returned promise
* will be resolved when the current execution terminates.
* - If the task is not running and the timer is armed, then the task is
* started immediately, and the returned promise resolves when the new
* execution terminates.
* - If the task is not running and the timer is not armed, the method returns
* a resolved promise.
*
* @return {Promise}
* @resolves After the last execution of the task is finished.
* @rejects Never.
*/
finalize: function () {
if (this._finalized) {
throw new Error("The object has been already finalized.");
}
this._finalized = true;
// If the timer is armed, it means that the task is not running but it is
// scheduled for execution. Cancel the timer and run the task immediately.
if (this._timer) {
this.disarm();
this._timerCallback();
}
// Wait for the operation to be completed, or resolve immediately.
if (this._runningPromise) {
return this._runningPromise;
}
return Promise.resolve();
},
_finalized: false,
/**
* Timer callback used to run the delayed task.
*/
_timerCallback: function ()
{
let runningDeferred = Promise.defer();
// All these state changes must occur at the same time directly inside the
// timer callback, to prevent race conditions and to ensure that all the
// methods behave consistently even if called from inside the task. This
// means that the assignment of "this._runningPromise" must complete before
// the task gets a chance to start.
this._timer = null;
this._armed = false;
this._runningPromise = runningDeferred.promise;
runningDeferred.resolve(Task.spawn(function () {
// Execute the provided function asynchronously.
yield Task.spawn(this._taskFn).then(null, Cu.reportError);
// Now that the task has finished, we check the state of the object to
// determine if we should restart the task again.
if (this._armed) {
if (!this._finalized) {
this._startTimer();
} else {
// Execute the task again immediately, for the last time. The isArmed
// property should return false while the task is running, and should
// remain false after the last execution terminates.
this._armed = false;
yield Task.spawn(this._taskFn).then(null, Cu.reportError);
}
}
// Indicate that the execution of the task has finished. This happens
// synchronously with the previous state changes in the function.
this._runningPromise = null;
}.bind(this)).then(null, Cu.reportError));
},
}
};

View File

@ -1,5 +1,6 @@
[DEFAULT]
[browser_DeferredTask.js]
[browser_Deprecated.js]
[browser_Finder.js]
[browser_Geometry.js]

View File

@ -0,0 +1,166 @@
/* 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 DeferredTask =
Components.utils.import("resource://gre/modules/DeferredTask.jsm", {}).DeferredTask;
function test() {
waitForExplicitFinish();
nextTest();
}
function nextTest() {
let test = tests.shift();
if (test) {
info("Starting " + test.name);
executeSoon(test.bind(null, nextTest));
} else {
executeSoon(finish);
}
}
let tests = [
function checkRanOnce(next) {
// Test that a deferred task runs only once.
let deferredTaskCounter = 0;
let deferredTask = new DeferredTask(function checkDeferred() {
is(++deferredTaskCounter, 1, "Deferred task ran once");
if (deferredTaskCounter > 1)
return; // shouldn't happen!
next();
}, 100);
deferredTask.start();
deferredTask.start();
deferredTask.start();
},
function testOrdering(next) {
// Test the deferred task running order.
let ARan, BRan;
let deferredTaskA = new DeferredTask(function () {
info("Deferred task A running");
ARan = true;
ok(!BRan, "Deferred task B shouldn't have run yet");
}, 10);
let deferredTaskB = new DeferredTask(function () {
info("Deferred task B running");
BRan = true;
ok(ARan, "Deferred task A should have run already");
executeSoon(next);
}, 100);
deferredTaskA.start();
deferredTaskB.start();
},
function testFlush(next) {
// Test the flush method.
let deferedTaskCounter = 0;
let deferredTask = new DeferredTask(function () {
is(++deferedTaskCounter, 1, "Deferred task should only run once");
}, 10);
is(deferedTaskCounter, 0, "Deferred task shouldn't run right away");
deferredTask.flush();
is(deferedTaskCounter, 0, "Deferred task shouldn't run after flush() if it wasn't started");
deferredTask.start();
is(deferedTaskCounter, 0, "Deferred task shouldn't run immediately after start");
deferredTask.flush();
is(deferedTaskCounter, 1, "Deferred task should run after flush() after being started");
next();
},
function testCancel(next) {
// Test the cancel method.
let deferredTask1, deferredTask2, deferredTask3;
let deferredTask2Ran = false;
deferredTask1 = new DeferredTask(function () {
info("Deferred task 1 running");
deferredTask2.cancel();
info("Deferred task 2 canceled");
}, 10);
deferredTask2 = new DeferredTask(function () {
info("Deferred task 2 running");
deferredTask2Ran = true;
}, 100);
deferredTask3 = new DeferredTask(function () {
info("Deferred task 3 running");
is(deferredTask2Ran, false, "Deferred task 2 shouldn't run");
next();
}, 200);
deferredTask1.start();
deferredTask2.start();
deferredTask3.start();
},
function testIsPending(next) {
// Test the pending property.
let deferredTask1, deferredTask2;
let deferredTask1 = new DeferredTask(function () {
info("Deferred task 1 running");
is(deferredTask1.isPending, false, "Deferred task 1 shouldn't be pending");
is(deferredTask2.isPending, true, "Deferred task 2 should be pending");
}, 100);
let deferredTask2 = new DeferredTask(function () {
info("Deferred task 2 running");
is(deferredTask1.isPending, false, "Deferred task 1 shouldn't be pending");
is(deferredTask2.isPending, false, "Deferred task 2 shouldn't be pending");
next();
}, 200);
is(deferredTask1.isPending, false, "Deferred task 1 shouldn't be pending");
is(deferredTask2.isPending, false, "Deferred task 2 shouldn't be pending");
deferredTask1.start();
deferredTask2.start();
is(deferredTask1.isPending, true, "Deferred task 1 should be pending");
is(deferredTask2.isPending, true, "Deferred task 2 should be pending");
},
function testRestartingTask(next) {
// Test restarting a task from other task.
let deferredTask1, deferredTask2, deferredTask3;
let deferredTask1 = new DeferredTask(function () {
info("Deferred task 1 running");
is(deferredTask1.isPending, false, "Deferred task 1 shouldn't be pending");
is(deferredTask2.isPending, false, "Deferred task 2 shouldn't be pending");
is(deferredTask3.isPending, false, "Deferred task 3 shouldn't be pending");
next();
}, 100);
let deferredTask2 = new DeferredTask(function () {
info("Deferred task 2 running");
deferredTask1.start();
deferredTask3.start();
}, 50);
let deferredTask3 = new DeferredTask(function () {
info("Deferred task 3 running");
is(deferredTask1.isPending, true, "Deferred task 1 should be pending");
}, 75);
deferredTask1.start();
deferredTask2.start();
},
function testRestartItselfTask(next) {
// Test restarting a task from the same task callback.
let deferredTask;
let taskReRun = false;
let deferredTask = new DeferredTask(function () {
info("Deferred task running");
is(deferredTask.isPending, false, "Deferred task shouldn't be pending");
if (!taskReRun) {
taskReRun = true;
info("Deferred task restart");
deferredTask.start();
is(deferredTask.isPending, true, "Deferred task should be pending");
} else {
next();
}
}, 100);
deferredTask.start();
}
];

View File

@ -1,392 +0,0 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
/**
* This file tests the DeferredTask.jsm module.
*/
////////////////////////////////////////////////////////////////////////////////
/// Globals
const { classes: Cc, interfaces: Ci, utils: Cu, results: Cr } = Components;
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "DeferredTask",
"resource://gre/modules/DeferredTask.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "Promise",
"resource://gre/modules/Promise.jsm");
/**
* Due to the nature of this module, most of the tests are time-dependent. All
* the timeouts are designed to occur at multiples of this granularity value,
* in milliseconds, that should be high enough to prevent intermittent failures,
* but low enough to prevent an excessive overall test execution time.
*/
const T = 100;
/**
* Waits for the specified timeout before resolving the returned promise.
*/
function promiseTimeout(aTimeoutMs)
{
let deferred = Promise.defer();
do_timeout(aTimeoutMs, deferred.resolve);
return deferred.promise;
}
function run_test()
{
run_next_test();
}
////////////////////////////////////////////////////////////////////////////////
//// Tests
/**
* Creates a simple DeferredTask and executes it once.
*/
add_test(function test_arm_simple()
{
new DeferredTask(run_next_test, 10).arm();
});
/**
* Checks that the delay set for the task is respected.
*/
add_test(function test_arm_delay_respected()
{
let executed1 = false;
let executed2 = false;
new DeferredTask(function () {
executed1 = true;
do_check_false(executed2);
}, 1*T).arm();
new DeferredTask(function () {
executed2 = true;
do_check_true(executed1);
run_next_test();
}, 2*T).arm();
});
/**
* Checks that calling "arm" again does not introduce further delay.
*/
add_test(function test_arm_delay_notrestarted()
{
let executed = false;
// Create a task that will run later.
let deferredTask = new DeferredTask(() => { executed = true; }, 4*T);
deferredTask.arm();
// Before the task starts, call "arm" again.
do_timeout(2*T, () => deferredTask.arm());
// The "arm" call should not have introduced further delays.
do_timeout(5*T, function () {
do_check_true(executed);
run_next_test();
});
});
/**
* Checks that a task runs only once when armed multiple times synchronously.
*/
add_test(function test_arm_coalesced()
{
let executed = false;
let deferredTask = new DeferredTask(function () {
do_check_false(executed);
executed = true;
run_next_test();
}, 50);
deferredTask.arm();
deferredTask.arm();
});
/**
* Checks that a task runs only once when armed multiple times synchronously,
* even when it has been created with a delay of zero milliseconds.
*/
add_test(function test_arm_coalesced_nodelay()
{
let executed = false;
let deferredTask = new DeferredTask(function () {
do_check_false(executed);
executed = true;
run_next_test();
}, 0);
deferredTask.arm();
deferredTask.arm();
});
/**
* Checks that a task can be armed again while running.
*/
add_test(function test_arm_recursive()
{
let executed = false;
let deferredTask = new DeferredTask(function () {
if (!executed) {
executed = true;
deferredTask.arm();
} else {
run_next_test();
}
}, 50);
deferredTask.arm();
});
/**
* Checks that calling "arm" while an asynchronous task is running waits until
* the task is finished before restarting the delay.
*/
add_test(function test_arm_async()
{
let finishedExecution = false;
let finishedExecutionAgain = false;
// Create a task that will run later.
let deferredTask = new DeferredTask(function () {
yield promiseTimeout(4*T);
if (!finishedExecution) {
finishedExecution = true;
} else if (!finishedExecutionAgain) {
finishedExecutionAgain = true;
}
}, 2*T);
deferredTask.arm();
// While the task is running, call "arm" again. This will result in a wait
// of 2*T until the task finishes, then another 2*T for the normal task delay
// specified on construction.
do_timeout(4*T, function () {
do_check_true(deferredTask.isRunning);
do_check_false(finishedExecution);
deferredTask.arm();
});
// This will fail in case the task was started without waiting 2*T after it
// has finished.
do_timeout(7*T, function () {
do_check_false(deferredTask.isRunning);
do_check_true(finishedExecution);
});
// This is in the middle of the second execution.
do_timeout(10*T, function () {
do_check_true(deferredTask.isRunning);
do_check_false(finishedExecutionAgain);
});
// Wait enough time to verify that the task was executed as expected.
do_timeout(13*T, function () {
do_check_false(deferredTask.isRunning);
do_check_true(finishedExecutionAgain);
run_next_test();
});
});
/**
* Checks that an armed task can be disarmed.
*/
add_test(function test_disarm()
{
// Create a task that will run later.
let deferredTask = new DeferredTask(function () {
do_throw("This task should not run.");
}, 2*T);
deferredTask.arm();
// Disable execution later, but before the task starts.
do_timeout(1*T, () => deferredTask.disarm());
// Wait enough time to verify that the task did not run.
do_timeout(3*T, run_next_test);
});
/**
* Checks that calling "disarm" allows the delay to be restarted.
*/
add_test(function test_disarm_delay_restarted()
{
let executed = false;
let deferredTask = new DeferredTask(() => { executed = true; }, 4*T);
deferredTask.arm();
do_timeout(2*T, function () {
deferredTask.disarm();
deferredTask.arm();
});
do_timeout(5*T, function () {
do_check_false(executed);
});
do_timeout(7*T, function () {
do_check_true(executed);
run_next_test();
});
});
/**
* Checks that calling "disarm" while an asynchronous task is running does not
* prevent the task to finish.
*/
add_test(function test_disarm_async()
{
let finishedExecution = false;
let deferredTask = new DeferredTask(function () {
deferredTask.arm();
yield promiseTimeout(2*T);
finishedExecution = true;
}, 1*T);
deferredTask.arm();
do_timeout(2*T, function () {
do_check_true(deferredTask.isRunning);
do_check_true(deferredTask.isArmed);
do_check_false(finishedExecution);
deferredTask.disarm();
});
do_timeout(4*T, function () {
do_check_false(deferredTask.isRunning);
do_check_false(deferredTask.isArmed);
do_check_true(finishedExecution);
run_next_test();
});
});
/**
* Checks that calling "arm" immediately followed by "disarm" while an
* asynchronous task is running does not cause it to run again.
*/
add_test(function test_disarm_immediate_async()
{
let executed = false;
let deferredTask = new DeferredTask(function () {
do_check_false(executed);
executed = true;
yield promiseTimeout(2*T);
}, 1*T);
deferredTask.arm();
do_timeout(2*T, function () {
do_check_true(deferredTask.isRunning);
do_check_false(deferredTask.isArmed);
deferredTask.arm();
deferredTask.disarm();
});
do_timeout(4*T, function () {
do_check_true(executed);
do_check_false(deferredTask.isRunning);
do_check_false(deferredTask.isArmed);
run_next_test();
});
});
/**
* Checks the isArmed and isRunning properties with a synchronous task.
*/
add_test(function test_isArmed_isRunning()
{
let deferredTask = new DeferredTask(function () {
do_check_true(deferredTask.isRunning);
do_check_false(deferredTask.isArmed);
deferredTask.arm();
do_check_true(deferredTask.isArmed);
deferredTask.disarm();
do_check_false(deferredTask.isArmed);
run_next_test();
}, 50);
do_check_false(deferredTask.isArmed);
deferredTask.arm();
do_check_true(deferredTask.isArmed);
do_check_false(deferredTask.isRunning);
});
/**
* Checks that the "finalize" method executes a synchronous task.
*/
add_test(function test_finalize()
{
let executed = false;
let timePassed = false;
let deferredTask = new DeferredTask(function () {
do_check_false(timePassed);
executed = true;
}, 2*T);
deferredTask.arm();
do_timeout(1*T, () => { timePassed = true; });
// This should trigger the immediate execution of the task.
deferredTask.finalize().then(function () {
do_check_true(executed);
run_next_test();
});
});
/**
* Checks that the "finalize" method executes the task again from start to
* finish in case it is already running.
*/
add_test(function test_finalize_executes_entirely()
{
let executed = false;
let executedAgain = false;
let timePassed = false;
let deferredTask = new DeferredTask(function () {
// The first time, we arm the timer again and set up the finalization.
if (!executed) {
deferredTask.arm();
do_check_true(deferredTask.isArmed);
do_check_true(deferredTask.isRunning);
deferredTask.finalize().then(function () {
// When we reach this point, the task must be finished.
do_check_true(executedAgain);
do_check_false(timePassed);
do_check_false(deferredTask.isArmed);
do_check_false(deferredTask.isRunning);
run_next_test();
});
// The second execution triggered by the finalization waits 1*T for the
// current task to finish (see the timeout below), but then it must not
// wait for the 2*T specified on construction as normal task delay. The
// second execution will finish after the timeout below has passed again,
// for a total of 2*T of wait time.
do_timeout(3*T, () => { timePassed = true; });
}
yield promiseTimeout(1*T);
// Just before finishing, indicate if we completed the second execution.
if (executed) {
do_check_true(deferredTask.isRunning);
executedAgain = true;
} else {
executed = true;
}
}, 2*T);
deferredTask.arm();
});

View File

@ -118,18 +118,6 @@ add_test(function test_spawn_function()
});
});
add_test(function test_spawn_function_returning_promise()
{
Task.spawn(function () {
return promiseResolvedLater("Resolution value.");
}).then(function (result) {
do_check_eq("Resolution value.", result);
run_next_test();
}, function (ex) {
do_throw("Unexpected error: " + ex);
});
});
add_test(function test_spawn_function_exceptions()
{
Task.spawn(function () {

View File

@ -7,7 +7,6 @@ support-files =
chromeappsstore.sqlite
[test_AsyncShutdown.js]
[test_DeferredTask.js]
[test_dict.js]
[test_FileUtils.js]
[test_Http.js]