1
0
mirror of https://github.com/adobe/brackets.git synced 2024-11-20 18:02:54 +01:00
brackets/test/spec/Async-test.js

666 lines
25 KiB
JavaScript

/*
* Copyright (c) 2013 - present Adobe Systems Incorporated. All rights reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*
*/
/*global describe, beforeEach, it, runs, waitsFor, waitsForDone, waitsForFail, expect */
define(function (require, exports, module) {
"use strict";
var Async = require("utils/Async"),
PromiseQueue = Async.PromiseQueue;
describe("Async", function () {
describe("Chain", function () {
function zeroArgThatSucceeds() {
var d = new $.Deferred();
setTimeout(function () {
d.resolve();
}, 1);
return d.promise();
}
function zeroArgThatFails() {
var d = new $.Deferred();
setTimeout(function () {
d.reject();
}, 1);
return d.promise();
}
function oneArgThatSucceeds(x) {
var d = new $.Deferred();
setTimeout(function () {
d.resolveWith(null, [x]);
}, 1);
return d.promise();
}
function twoArgThatSucceeds(x, y) {
var d = new $.Deferred();
setTimeout(function () {
d.resolveWith(null, [x, y]);
}, 1);
return d.promise();
}
function twoArgThatFails(x, y) {
var d = new $.Deferred();
setTimeout(function () {
d.rejectWith(null, [x, y]);
}, 1);
return d.promise();
}
function syncAddTwoArg(x, y) {
return x + y;
}
function syncException() {
throw new Error("sync error");
}
function createArrayComparator(arrayOne) {
return function (arrayTwo) {
var i, l;
expect(arrayOne.length).toBe(arrayTwo.length);
l = Math.min(arrayOne.length, arrayTwo.length);
for (i = 0; i < l; i++) {
expect(arrayOne[i]).toBe(arrayTwo[i]);
}
};
}
function expectChainHelper(functions, args, shouldSucceed, responseComparator) {
var done = false;
var success = false;
var response = null;
runs(function () {
var result = Async.chain(
functions,
args
);
result.done(function () {
done = true;
success = true;
response = arguments;
});
result.fail(function () {
done = true;
success = false;
response = arguments;
});
});
waitsFor(function () {
return done;
}, "The chain should complete", 100);
runs(function () {
expect(success).toBe(shouldSucceed);
responseComparator(response);
});
}
describe("Zero-argument deferreds", function () {
it("[zero-arg] work with a null argument array", function () {
expectChainHelper(
[zeroArgThatSucceeds, zeroArgThatSucceeds, zeroArgThatSucceeds],
null,
true,
createArrayComparator([])
);
});
it("[zero-arg] call error callback when first deferred fails", function () {
expectChainHelper(
[zeroArgThatFails, zeroArgThatSucceeds, zeroArgThatSucceeds],
[],
false,
createArrayComparator([])
);
});
it("[zero-arg] call error callback when middle deferred fails", function () {
expectChainHelper(
[zeroArgThatSucceeds, zeroArgThatFails, zeroArgThatSucceeds],
[],
false,
createArrayComparator([])
);
});
it("[zero-arg] call error callback when last deferred fails", function () {
expectChainHelper(
[zeroArgThatSucceeds, zeroArgThatSucceeds, zeroArgThatFails],
[],
false,
createArrayComparator([])
);
});
it("[zero-arg] call success callback when all deferreds succeed", function () {
expectChainHelper(
[zeroArgThatSucceeds, zeroArgThatSucceeds, zeroArgThatSucceeds],
[],
true,
createArrayComparator([])
);
});
it("[zero-arg] call success callback immediately if there are no deferreds", function () {
expectChainHelper(
[],
[],
true,
createArrayComparator([])
);
});
});
describe("Nonzero-argument deferreds", function () {
it("[nonzero-arg] call error callback when first deferred fails", function () {
expectChainHelper(
[twoArgThatFails, twoArgThatSucceeds, twoArgThatSucceeds],
[1, 2],
false,
createArrayComparator([1, 2])
);
});
it("[nonzero-arg] call error callback when middle deferred fails", function () {
expectChainHelper(
[twoArgThatSucceeds, twoArgThatFails, twoArgThatSucceeds],
[1, 2],
false,
createArrayComparator([1, 2])
);
});
it("[nonzero-arg] call error callback when last deferred fails", function () {
expectChainHelper(
[twoArgThatSucceeds, twoArgThatSucceeds, twoArgThatFails],
[1, 2],
false,
createArrayComparator([1, 2])
);
});
it("[nonzero-arg] call success callback when all deferreds succeed", function () {
expectChainHelper(
[twoArgThatSucceeds, twoArgThatSucceeds, twoArgThatSucceeds],
[1, 2],
true,
createArrayComparator([1, 2])
);
});
it("[nonzero-arg] call success callback immediately if there are no deferreds", function () {
expectChainHelper(
[],
[1, 2],
true,
createArrayComparator([1, 2])
);
});
});
describe("With Timeout", function () {
function promiseThatSucceeds(duration) {
var d = new $.Deferred();
setTimeout(function () {
d.resolve();
}, duration);
return d.promise();
}
function promiseThatFails(duration) {
var d = new $.Deferred();
setTimeout(function () {
d.reject();
}, duration);
return d.promise();
}
it("should resolve promise before rejected with timeout", function () {
var promiseBase, promiseWrapper;
runs(function () {
promiseBase = promiseThatSucceeds(5);
promiseWrapper = Async.withTimeout(promiseBase, 10);
waitsForDone(promiseWrapper, "promise resolves before timeout");
});
runs(function () {
// base promise resolves wrapper promise
expect(promiseBase.state()).toBe("resolved");
expect(promiseWrapper.state()).toBe("resolved");
});
});
it("should reject promise before resolved with timeout", function () {
var promiseBase, promiseWrapper;
runs(function () {
promiseBase = promiseThatFails(5);
promiseWrapper = Async.withTimeout(promiseBase, 10, true);
waitsForFail(promiseWrapper, "promise rejected before timeout");
});
runs(function () {
// base promise rejects wrapper promise
expect(promiseBase.state()).toBe("rejected");
expect(promiseWrapper.state()).toBe("rejected");
});
});
it("should timeout with reject before promise resolves", function () {
var promiseBase, promiseWrapper;
runs(function () {
promiseBase = promiseThatSucceeds(10);
promiseWrapper = Async.withTimeout(promiseBase, 5);
waitsForFail(promiseWrapper, "times out before promise resolves");
});
runs(function () {
expect(promiseWrapper.state()).toBe("rejected");
waitsForDone(promiseBase, "promise resolves after timeout");
});
runs(function () {
expect(promiseBase.state()).toBe("resolved");
});
});
it("should timeout with resolve before promise rejected", function () {
var promiseBase, promiseWrapper;
runs(function () {
promiseBase = promiseThatFails(10);
promiseWrapper = Async.withTimeout(promiseBase, 5, true);
waitsForDone(promiseWrapper, "times out before promise is rejected");
});
runs(function () {
expect(promiseWrapper.state()).toBe("resolved");
waitsForFail(promiseBase, "promise is rejected after timeout");
});
runs(function () {
expect(promiseBase.state()).toBe("rejected");
});
});
});
describe("Async/sync mix", function () {
it("[async/sync] succeed with sync command at beginning", function () {
expectChainHelper(
[syncAddTwoArg, oneArgThatSucceeds, oneArgThatSucceeds],
[1, 2],
true,
createArrayComparator([3])
);
});
it("[async/sync] succeed with sync command at middle", function () {
expectChainHelper(
[twoArgThatSucceeds, syncAddTwoArg, oneArgThatSucceeds],
[1, 2],
true,
createArrayComparator([3])
);
});
it("[async/sync] succeed with sync command at end", function () {
expectChainHelper(
[twoArgThatSucceeds, twoArgThatSucceeds, syncAddTwoArg],
[1, 2],
true,
createArrayComparator([3])
);
});
it("[async/sync] call error callback if sync command fails", function () {
expectChainHelper(
[twoArgThatSucceeds, syncException, twoArgThatSucceeds],
[1, 2],
false,
function (response) {
expect(response.length).toBe(1);
expect(response[0] instanceof Error).toBe(true);
}
);
});
it("[async/sync] two sync commands in order are executed completely synchronously", function () {
var promise = null,
flag = true;
runs(function () {
function negate(b) {
return !b;
}
function setFlag(b) {
flag = b;
return flag;
}
promise = Async.chain([negate, setFlag], [flag]);
promise.done(function (b) {
expect(b).toBe(flag);
});
// note, we WANT to test this synchronously. This is not a bug
// in the unit test. A series of synchronous functions should
// execute synchronously.
expect(flag).toBe(false);
});
runs(function () {
// With (the current version) of jQuery promises, resolution and
// resolution handlers will get called synchronously. However, if we
// move to a different promise implementation (e.g. Q) then resolution
// handlers will get called asynchronously. So, we check completion
// of the promise on a separate pass.
waitsForDone(promise, "The chain to complete");
});
});
});
});
describe("promisify", function () {
var testObj = {
someVal: 5,
succeeder: function (input, cb) {
cb(null, input, this.someVal);
},
failer: function (input, cb) {
cb("this is an error");
}
};
it("should resolve its returned promise when the errback is called with null err", function () {
Async.promisify(testObj, "succeeder", "myInput")
.then(function (input, someVal) {
expect(input).toBe("myInput");
expect(someVal).toBe(testObj.someVal);
}, function (err) {
expect("should not have called fail callback").toBe(false);
});
});
it("should reject its returned promise when the errback is called with an err", function () {
Async.promisify(testObj, "failer", "myInput")
.then(function (input, someVal) {
expect("should not have called success callback").toBe(false);
}, function (err) {
expect(err).toBe("this is an error");
});
});
});
describe("Async PromiseQueue", function () {
var queue, calledFns;
beforeEach(function () {
queue = new PromiseQueue();
calledFns = {};
});
function makeFn(id, resolveNow, rejectNow) {
var result = new $.Deferred();
return {
fn: function () {
calledFns[id] = true;
if (rejectNow) {
result.reject();
} else if (resolveNow) {
result.resolve();
}
return result.promise();
},
deferred: result
};
}
it("should immediately execute a function when the queue is empty", function () {
var fnInfo = makeFn("one");
queue.add(fnInfo.fn);
expect(calledFns.one).toBe(true);
fnInfo.deferred.resolve();
expect(queue._queue.length).toBe(0);
expect(queue._curPromise).toBe(null);
});
it("should wait to execute the second added function", function () {
var fnInfo1 = makeFn("one"),
fnInfo2 = makeFn("two");
queue.add(fnInfo1.fn);
expect(calledFns.one).toBe(true);
queue.add(fnInfo2.fn);
expect(calledFns.two).toBeUndefined();
fnInfo1.deferred.resolve();
expect(calledFns.two).toBe(true);
fnInfo2.deferred.resolve();
expect(queue._queue.length).toBe(0);
expect(queue._curPromise).toBe(null);
});
it("should properly handle the case where the first function completes synchronously", function () {
var fnInfo1 = makeFn("one", true),
fnInfo2 = makeFn("two");
queue.add(fnInfo1.fn);
expect(calledFns.one).toBe(true);
queue.add(fnInfo2.fn);
expect(calledFns.two).toBe(true);
fnInfo2.deferred.resolve();
expect(queue._queue.length).toBe(0);
expect(queue._curPromise).toBe(null);
});
it("should sequentially execute a second and third function if they're both added while the first is executing", function () {
var fnInfo1 = makeFn("one"),
fnInfo2 = makeFn("two"),
fnInfo3 = makeFn("three");
queue.add(fnInfo1.fn);
expect(calledFns.one).toBe(true);
queue.add(fnInfo2.fn);
queue.add(fnInfo3.fn);
expect(calledFns.two).toBeUndefined();
expect(calledFns.three).toBeUndefined();
fnInfo1.deferred.resolve();
expect(calledFns.two).toBe(true);
expect(calledFns.three).toBeUndefined();
fnInfo2.deferred.resolve();
expect(calledFns.three).toBe(true);
fnInfo3.deferred.resolve();
expect(queue._queue.length).toBe(0);
expect(queue._curPromise).toBe(null);
});
it("should sequentially execute a second and third function if the third is added while the second is executing", function () {
var fnInfo1 = makeFn("one"),
fnInfo2 = makeFn("two"),
fnInfo3 = makeFn("three");
queue.add(fnInfo1.fn);
expect(calledFns.one).toBe(true);
queue.add(fnInfo2.fn);
expect(calledFns.two).toBeUndefined();
fnInfo1.deferred.resolve();
expect(calledFns.two).toBe(true);
queue.add(fnInfo3.fn);
expect(calledFns.three).toBeUndefined();
fnInfo2.deferred.resolve();
expect(calledFns.three).toBe(true);
fnInfo3.deferred.resolve();
expect(queue._queue.length).toBe(0);
expect(queue._curPromise).toBe(null);
});
it("should execute a second function when added to the empty queue after the first function has completed", function () {
var fnInfo1 = makeFn("one"),
fnInfo2 = makeFn("two");
queue.add(fnInfo1.fn);
expect(calledFns.one).toBe(true);
fnInfo1.deferred.resolve();
expect(queue._queue.length).toBe(0);
expect(queue._curPromise).toBe(null);
queue.add(fnInfo2.fn);
expect(calledFns.two).toBe(true);
fnInfo2.deferred.resolve();
expect(queue._queue.length).toBe(0);
expect(queue._curPromise).toBe(null);
});
it("should execute the second function if the first function is rejected", function () {
var fnInfo1 = makeFn("one"),
fnInfo2 = makeFn("two");
queue.add(fnInfo1.fn);
expect(calledFns.one).toBe(true);
queue.add(fnInfo2.fn);
expect(calledFns.two).toBeUndefined();
fnInfo1.deferred.reject();
expect(calledFns.two).toBe(true);
fnInfo2.deferred.resolve();
expect(queue._queue.length).toBe(0);
expect(queue._curPromise).toBe(null);
});
it("should execute the third function after first and second functions are rejected", function () {
var fnInfo1 = makeFn("one"),
fnInfo2 = makeFn("two"),
fnInfo3 = makeFn("three");
queue.add(fnInfo1.fn);
expect(calledFns.one).toBe(true);
queue.add(fnInfo2.fn);
queue.add(fnInfo3.fn);
expect(calledFns.two).toBeUndefined();
expect(calledFns.three).toBeUndefined();
fnInfo1.deferred.reject();
expect(calledFns.two).toBe(true);
fnInfo2.deferred.reject();
expect(calledFns.three).toBe(true);
fnInfo3.deferred.resolve();
expect(queue._queue.length).toBe(0);
expect(queue._curPromise).toBe(null);
});
it("should execute the second function after the already-rejected first function is added to the queue", function () {
var fnInfo1 = makeFn("one", false, true),
fnInfo2 = makeFn("two");
queue.add(fnInfo1.fn);
expect(calledFns.one).toBe(true);
queue.add(fnInfo2.fn);
expect(calledFns.two).toBe(true);
fnInfo2.deferred.resolve();
expect(queue._queue.length).toBe(0);
expect(queue._curPromise).toBe(null);
});
it("should be able to run two queues simultaneously without clashing", function () {
var queue2 = new PromiseQueue(),
q1FnInfo1 = makeFn("one"),
q1FnInfo2 = makeFn("two"),
q2FnInfo3 = makeFn("three"),
q2FnInfo4 = makeFn("four");
//queue one
queue.add(q1FnInfo1.fn);
queue.add(q1FnInfo2.fn);
expect(calledFns.one).toBe(true);
expect(calledFns.two).toBeUndefined();
//queue one should have one in _queue,
//queue two should have zero in _queue
expect(queue._queue.length).toBe(1);
expect(queue2._queue.length).toBe(0);
//queue two
queue2.add(q2FnInfo3.fn);
queue2.add(q2FnInfo4.fn);
expect(calledFns.three).toBe(true);
expect(calledFns.four).toBeUndefined();
//queue one and two should have one in _queue
expect(queue._queue.length).toBe(1);
expect(queue2._queue.length).toBe(1);
q1FnInfo1.deferred.resolve();
expect(calledFns.two).toBe(true);
expect(queue._queue.length).toBe(0);
q1FnInfo2.deferred.resolve();
expect(queue._queue.length).toBe(0);
expect(queue._curPromise).toBe(null);
q2FnInfo3.deferred.resolve();
expect(calledFns.three).toBe(true);
expect(queue2._queue.length).toBe(0);
q2FnInfo4.deferred.resolve();
expect(queue2._queue.length).toBe(0);
expect(queue2._curPromise).toBe(null);
});
});
});
});