blob: 1945210c908eeee1784a55e7b0adaa1a32e68a41 [file] [log] [blame]
<!DOCTYPE html>
<html>
<!--
Copyright 2009 The Closure Library Authors. All Rights Reserved.
Author: arv@google.com (Erik Arvidsson)
-->
<head>
<title>Closure Unit Tests - goog.async.Deferred</title>
<script src="../../../../../closure/goog/base.js"></script>
<script>
goog.require('goog.Promise');
goog.require('goog.Thenable');
goog.require('goog.array');
goog.require('goog.async.Deferred');
goog.require('goog.string');
goog.require('goog.testing.MockClock');
goog.require('goog.testing.PropertyReplacer');
goog.require('goog.testing.jsunit');
</script>
</head>
<body>
<script>
var Deferred = goog.async.Deferred;
var AlreadyCalledError = Deferred.AlreadyCalledError;
var CanceledError = Deferred.CanceledError;
// Unhandled errors may be sent to the browser on a timeout.
var mockClock = new goog.testing.MockClock();
var stubs = new goog.testing.PropertyReplacer();
function setUp() {
mockClock.install();
}
function tearDown() {
// Advance the mockClock to fire any unhandled exception timeouts.
mockClock.tick();
mockClock.uninstall();
stubs.reset();
}
function assertEqualsCallback(msg, expected) {
return function(res) {
assertEquals(msg, expected, res);
// Since the assertion is an exception that will be caught inside the
// Deferred object, we must advance the clock to see if it has failed.
mockClock.tick();
return res;
};
}
function increment(res) {
return res + 1;
}
function throwStuff(res) {
throw res;
}
function catchStuff(res) {
return res;
}
function returnError(res) {
return Error(res);
}
function neverHappen(res) {
fail('This should not happen');
}
function testNormal() {
var d = new Deferred();
d.addCallback(assertEqualsCallback('pre-deferred callback', 1));
d.callback(1);
d.addCallback(increment);
d.addCallback(assertEqualsCallback('post-deferred callback', 2));
d.addCallback(throwStuff);
d.addCallback(neverHappen);
d.addErrback(catchStuff);
d.addCallback(assertEqualsCallback('throw -> err, catch -> success', 2));
d.addCallback(returnError);
d.addCallback(neverHappen);
d.addErrback(catchStuff);
d.addCallback(assertEqualsCallback('return -> err, catch -> succcess', 2));
}
function testCancel() {
var count = 0;
function canceled(d) {
count++;
}
function canceledError(res) {
assertTrue(res instanceof CanceledError);
}
var d = new Deferred(canceled);
d.addCallback(neverHappen);
d.addErrback(canceledError);
d.cancel();
assertEquals(1, count);
}
function testSucceedFail() {
var count = 0;
var d = Deferred.succeed(1).addCallback(assertEqualsCallback('succeed', 1));
// default error
d = Deferred.fail().addCallback(neverHappen);
d = d.addErrback(function(res) {
count++;
return res;
});
// default wrapped error
d = Deferred.fail('web taco').addCallback(neverHappen).addErrback(catchStuff);
d = d.addCallback(assertEqualsCallback('wrapped fail', 'web taco'));
// default unwrapped error
d = Deferred.fail(Error('ugh')).addCallback(neverHappen).addErrback(
catchStuff);
d = d.addCallback(assertEqualsCallback('unwrapped fail', 'ugh'));
assertEquals(1, count);
}
function testDeferredDependencies() {
function deferredIncrement(res) {
var rval = Deferred.succeed(res);
rval.addCallback(increment);
return rval;
}
var d = Deferred.succeed(1).addCallback(deferredIncrement);
d = d.addCallback(assertEqualsCallback('dependent deferred succeed', 2));
function deferredFailure(res) {
return Deferred.fail(res);
}
d = Deferred.succeed('ugh').addCallback(deferredFailure).addErrback(
catchStuff);
d = d.addCallback(assertEqualsCallback('dependent deferred fail', 'ugh'));
}
// Test double-calling, double-failing, etc.
function testDoubleCalling() {
var ex = assertThrows(function() {
Deferred.succeed(1).callback(2);
neverHappen();
});
assertTrue('double call', ex instanceof AlreadyCalledError);
}
function testDoubleCalling2() {
var ex = assertThrows(function() {
Deferred.fail(1).errback(2);
neverHappen();
});
assertTrue('double-fail', ex instanceof AlreadyCalledError);
}
function testDoubleCalling3() {
var ex = assertThrows(function() {
var d = Deferred.succeed(1);
d.cancel();
d = d.callback(2);
assertTrue('swallowed one callback, no canceler', true);
d.callback(3);
neverHappen();
});
assertTrue('swallow cancel', ex instanceof AlreadyCalledError);
}
function testDoubleCalling4() {
var count = 0;
function canceled(d) {
count++;
}
var ex = assertThrows(function() {
var d = new Deferred(canceled);
d.cancel();
d = d.callback(1);
});
assertTrue('non-swallowed cancel', ex instanceof AlreadyCalledError);
assertEquals(1, count);
}
// Test incorrect Deferred usage
function testIncorrectUsage() {
var d = new Deferred();
var ex = assertThrows(function() {
d.callback(new Deferred());
neverHappen();
});
assertTrue('deferred not allowed for callback', ex instanceof Error);
}
function testIncorrectUsage2() {
var d = new Deferred();
var ex = assertThrows(function() {
d.errback(new Deferred());
neverHappen();
});
assertTrue('deferred not allowed for errback', ex instanceof Error);
}
function testIncorrectUsage3() {
var d = new Deferred();
(new Deferred()).addCallback(function() {return d;}).callback(1);
var ex = assertThrows(function() {
d.addCallback(function() {});
neverHappen();
});
assertTrue('chained deferred not allowed to be re-used', ex instanceof Error);
}
function testCallbackScope1() {
var c1 = {}, c2 = {};
var callbackScope = null;
var errbackScope = null;
var d = new Deferred();
d.addCallback(function() {
callbackScope = this;
throw Error('Foo');
}, c1);
d.addErrback(function() {
errbackScope = this;
}, c2);
d.callback();
assertEquals('Incorrect callback scope', c1, callbackScope);
assertEquals('Incorrect errback scope', c2, errbackScope);
}
function testCallbackScope2() {
var callbackScope = null;
var errbackScope = null;
var d = new Deferred();
d.addCallback(function() {
callbackScope = this;
throw Error('Foo');
});
d.addErrback(function() {
errbackScope = this;
});
d.callback();
assertEquals('Incorrect callback scope', window, callbackScope);
assertEquals('Incorrect errback scope', window, errbackScope);
}
function testCallbackScope3() {
var c = {};
var callbackScope = null;
var errbackScope = null;
var d = new Deferred(null, c);
d.addCallback(function() {
callbackScope = this;
throw Error('Foo');
});
d.addErrback(function() {
errbackScope = this;
});
d.callback();
assertEquals('Incorrect callback scope', c, callbackScope);
assertEquals('Incorrect errback scope', c, errbackScope);
}
function testChainedDeferred1() {
var calls = [];
var d2 = new Deferred();
d2.addCallback(function() {calls.push('B1');});
d2.addCallback(function() {calls.push('B2');});
var d1 = new Deferred();
d1.addCallback(function() {calls.push('A1');});
d1.addCallback(function() {calls.push('A2');});
d1.chainDeferred(d2);
d1.addCallback(function() {calls.push('A3');});
d1.callback();
assertEquals('A1,A2,B1,B2,A3', calls.join(','));
}
function testChainedDeferred2() {
var calls = [];
var d2 = new Deferred();
d2.addCallback(function() {calls.push('B1');});
d2.addErrback(function(err) {calls.push('B2'); throw Error('x');});
var d1 = new Deferred();
d1.addCallback(function(err) {throw Error('foo');});
d1.chainDeferred(d2);
d1.addCallback(function() {calls.push('A1');});
d1.addErrback(function() {calls.push('A2');});
d1.callback();
assertEquals('B2,A2', calls.join(','));
var ex = assertThrows(function() {
mockClock.tick();
neverHappen();
});
assertTrue('Should catch unhandled throw from d2.', ex.message == 'x');
}
function testUndefinedResultAndCallbackSequence() {
var results = [];
var d = new Deferred();
d.addCallback(function(res) {return 'foo';});
d.addCallback(function(res) {results.push(res); return 'bar';});
d.addCallback(function(res) {results.push(res);});
d.addCallback(function(res) {results.push(res);});
d.callback();
assertEquals('foo,bar,bar', results.join(','));
}
function testUndefinedResultAndErrbackSequence() {
var results = [];
var d = new Deferred();
d.addCallback(function(res) {throw Error('uh oh');});
d.addErrback(function(res) {results.push('A');});
d.addCallback(function(res) {results.push('B');});
d.addErrback(function(res) {results.push('C');});
d.callback();
assertEquals('A,C', results.join(','));
}
function testHasFired() {
var d1 = new Deferred();
var d2 = new Deferred();
assertFalse(d1.hasFired());
assertFalse(d2.hasFired());
d1.callback();
d2.errback();
assertTrue(d1.hasFired());
assertTrue(d2.hasFired());
}
function testUnhandledErrors() {
var d = new Deferred();
d.addCallback(throwStuff);
var ex = assertThrows(function() {
d.callback(123);
mockClock.tick();
neverHappen();
});
assertEquals('Unhandled throws should hit the browser.', 123, ex);
assertNotThrows(
'Errbacks added after a failure should resume.',
function() {
d.addErrback(catchStuff);
mockClock.tick();
});
d.addCallback(assertEqualsCallback('Should recover after throw.', 1));
mockClock.tick();
}
function testStrictUnhandledErrors() {
stubs.replace(Deferred, 'STRICT_ERRORS', true);
var err = Error('never handled');
// The registered errback exists, but doesn't modify the error value.
var d = Deferred.succeed();
d.addCallback(function(res) { throw err; });
d.addErrback(function(unhandledErr) {});
var caught = assertThrows(
'The error should be rethrown at the next clock tick.',
function() { mockClock.tick(); });
assertEquals(err, caught);
}
function testStrictHandledErrors() {
stubs.replace(Deferred, 'STRICT_ERRORS', true);
// The registered errback returns a non-error value.
var d = Deferred.succeed();
d.addCallback(function(res) { throw Error('eventually handled'); });
d.addErrback(function(unhandledErr) { return true; });
assertNotThrows(
'The error was handled and should not be rethrown',
function() { mockClock.tick(); });
d.addCallback(function(res) { assertTrue(res); });
}
function testStrictBlockedErrors() {
stubs.replace(Deferred, 'STRICT_ERRORS', true);
var d1 = Deferred.fail(Error('blocked failure'));
var d2 = new Deferred();
d1.addBoth(function() { return d2; });
assertNotThrows(
'd1 should be blocked until d2 fires.',
function() { mockClock.tick(); });
d2.callback('unblocked');
d1.addCallback(assertEqualsCallback(
'd1 should receive the fired result from d2.', 'unblocked'));
}
function testStrictCanceledErrors() {
stubs.replace(Deferred, 'STRICT_ERRORS', true);
var d = Deferred.canceled();
assertNotThrows(
'CanceledErrors should not be rethrown to the global scope.',
function() { mockClock.tick(); });
}
function testSynchronousErrorCanceling() {
var d = new Deferred();
d.addCallback(throwStuff);
assertNotThrows(
'Adding an errback to the end of a failing Deferred should cancel the ' +
'unhandled error timeout.',
function() {
d.callback(1);
d.addErrback(catchStuff);
mockClock.tick();
});
d.addCallback(assertEqualsCallback('Callback should fire', 1));
}
function testThrowNonError() {
var results = [];
var d = new Deferred();
d.addCallback(function(res) {
throw res;
});
d.addErrback(function(res) {
results.push(res);
return 6;
});
d.addCallback(function(res) {
results.push(res);
});
d.callback(7);
assertArrayEquals(
'Errback should have been called with 7, followed by callback with 6.',
[7, 6], results);
}
function testThrownErrorWithNoErrbacks() {
var d = new Deferred();
d.addCallback(function() {
throw Error('foo');
});
d.addCallback(goog.nullFunction);
function assertCallback() {
d.callback(1);
mockClock.tick(); // Should cause error because throwing is delayed.
}
assertThrows('A thrown error should be rethrown if there is no ' +
'errback to catch it.', assertCallback);
}
function testThrownErrorCallbacksDoNotCancel() {
var d = new Deferred();
d.addCallback(function() {
throw Error('foo');
});
function assertCallback() {
d.callback(1);
// Add another callback after the fact. Note this is not an errback!
d.addCallback(neverHappen);
mockClock.tick(); // Should cause error because throwing is delayed.
}
assertThrows('A thrown error should be rethrown if there is no ' +
'errback to catch it.', assertCallback);
}
function testAwaitDeferred() {
var results = [];
function fn(x) {
return function() {
results.push(x);
};
}
var d2 = new Deferred();
d2.addCallback(fn('b'));
// d1 -> a -> (wait for d2) -> c
var d1 = new Deferred();
d1.addCallback(fn('a'));
d1.awaitDeferred(d2);
d1.addCallback(fn('c'));
// calls 'a' then yields for d2.
d1.callback(null);
// will get called after d2.
d1.addCallback(fn('d'));
assertEquals('a', results.join(''));
// d3 -> w -> (wait for d2) -> x
var d3 = new Deferred();
d3.addCallback(fn('w'));
d3.awaitDeferred(d2);
d3.addCallback(fn('x'));
// calls 'w', then yields for d2.
d3.callback();
// will get called after d2.
d3.addCallback(fn('y'));
assertEquals('aw', results.join(''));
// d1 calls 'd', d3 calls 'y'
d2.callback(null);
assertEquals('awbcdxy', results.join(''));
// d3 and d2 already called, so 'z' called immediately.
d3.addCallback(fn('z'));
assertEquals('awbcdxyz', results.join(''));
}
function testAwaitDeferred_withPromise() {
var results = [];
function fn(x) {
return function() {
results.push(x);
};
}
var resolver = new goog.Promise.withResolver();
resolver.promise.then(fn('b'));
// d1 -> a -> (wait for promise) -> c
var d1 = new Deferred();
d1.addCallback(fn('a'));
d1.awaitDeferred(resolver.promise);
d1.addCallback(fn('c'));
// calls 'a' then yields for promise.
d1.callback(1);
// will get called after promise.
d1.addCallback(fn('d'));
assertEquals('a', results.join(''));
// d3 -> w -> (wait for promise) -> x
var d3 = new Deferred();
d3.addCallback(fn('w'));
d3.awaitDeferred(resolver.promise);
d3.addCallback(fn('x'));
// calls 'w', then yields for promise.
d3.callback(2);
// will get called after promise.
d3.addCallback(fn('y'));
assertEquals('aw', results.join(''));
// d1 calls 'd', d3 calls 'y'
resolver.resolve();
mockClock.tick();
assertEquals('awbcdxy', results.join(''));
// d3 and promise already called, so 'z' called immediately.
d3.addCallback(fn('z'));
assertEquals('awbcdxyz', results.join(''));
}
function testAwaitDeferredWithErrors() {
var results = [];
function fn(x) {
return function(e) {
results.push(x);
};
}
var d2 = new Deferred();
d2.addErrback(fn('a'));
var d1 = new Deferred();
d1.awaitDeferred(d2);
d1.addCallback(fn('x'));
d1.addErrback(fn('b'));
d1.callback(null);
assertEquals('', results.join(''));
d2.addCallback(fn('z'));
d2.addErrback(fn('c'));
d2.errback(null);
// First errback added to d2 prints 'a'.
// Next 'd' was chained, so execute its err backs, printing 'b'.
// Finally 'c' was added last by d2's errback.
assertEquals('abc', results.join(''));
}
function testNonErrorErrback() {
var results = [];
function fn(x) {
return function(e) {
results.push(x);
};
}
var d = new Deferred();
d.addCallback(fn('a'));
d.addErrback(fn('b'));
d.addCallback(fn('c'));
d.addErrback(fn('d'));
d.errback('foo');
assertEquals('bd', results.join(''));
}
function testUnequalReturnValueForErrback() {
var results = [];
function fn(x) {
return function(e) {
results.push(x);
};
}
var d = new Deferred();
d.addCallback(fn('a'));
d.addErrback(function() {
results.push('b');
return 'bar';
});
d.addCallback(fn('c'));
d.addErrback(fn('d'));
d.errback('foo');
assertEquals('bc', results.join(''));
}
function testBranch() {
function fn(x) {
return function(arr) {
return arr.concat(x);
};
}
var d = new Deferred();
d.addCallback(fn(1));
d.addCallback(fn(2));
var d2 = d.branch();
d.addCallback(fn(3));
d2.addCallback(fn(4));
d.callback([]);
assertTrue('both deferreds should have fired', d.hasFired());
assertTrue('both deferreds should have fired', d2.hasFired());
d.addCallback(function(arr) { assertArrayEquals([1, 2, 3], arr); });
d2.addCallback(function(arr) { assertArrayEquals([1, 2, 4], arr); });
}
function testDiamondBranch() {
function fn(x) {
return function(arr) {
return arr.concat(x);
};
}
var d = new Deferred();
d.addCallback(fn(1));
var d2 = d.branch();
d2.addCallback(fn(2));
// Chain the branch back to the original. There is no good reason to do this
// cever.
d.addCallback(function(ret) {return d2;});
d.callback([]);
// But no reason it shouldn't work!
d.addCallback(function(arr) { assertArrayEquals([1, 2], arr); });
}
function testRepeatedBranch() {
var d = new Deferred().addCallback(increment);
d.branch().
addCallback(assertEqualsCallback('branch should be after increment', 2)).
addCallback(function(res) {return d.branch();}).
addCallback(assertEqualsCallback('second branch should be the same', 2));
d.callback(1);
}
function testCancelThroughBranch() {
var wasCanceled = false;
var d = new Deferred(function() { wasCanceled = true; });
var branch1 = d.branch(true);
var branch2 = d.branch(true);
branch1.cancel();
assertFalse(wasCanceled);
branch2.cancel();
assertTrue(wasCanceled);
}
function testCancelThroughSeveralBranches() {
var wasCanceled = false;
var d = new Deferred(function() { wasCanceled = true; });
var branch = d.branch(true).branch(true).branch(true);
branch.cancel();
assertTrue(wasCanceled);
}
function testBranchCancelThenCallback() {
var wasCanceled = false;
var d = new Deferred(function() { wasCanceled = true; });
var wasCalled = false;
d.addCallback(function() { wasCalled = true; });
var branch1 = d.branch();
var branch2 = d.branch();
var branch1WasCalled = false;
var branch2WasCalled = false;
branch1.addCallback(function() { branch1WasCalled = true; });
branch2.addCallback(function() { branch2WasCalled = true; });
var branch1HadErrback = false;
var branch2HadErrback = false;
branch1.addErrback(function() { branch1HadErrback = true; });
branch2.addErrback(function() { branch2HadErrback = true; });
branch1.cancel();
assertFalse(wasCanceled);
assertTrue(branch1HadErrback);
assertFalse(branch2HadErrback);
d.callback();
assertTrue(wasCalled);
assertFalse(branch1WasCalled);
assertTrue(branch2WasCalled);
}
function testDeepCancelOnBranch() {
var wasCanceled = false;
var d = new Deferred(function() { wasCanceled = true; });
var branch1 = d.branch(true);
var branch2 = d.branch(true).branch(true).branch(true);
var branch1HadErrback = false;
var branch2HadErrback = false;
branch1.addErrback(function() { branch1HadErrback = true; });
branch2.addErrback(function() { branch2HadErrback = true; });
branch2.cancel(true /* opt_deepCancel */);
assertTrue(wasCanceled);
assertTrue(branch1HadErrback);
assertTrue(branch2HadErrback);
}
function testCancelOnRoot() {
var wasCanceled = false;
var d = new Deferred(function() { wasCanceled = true; });
var branch = d.branch(true).branch(true).branch(true);
d.cancel();
assertTrue(wasCanceled);
}
function testCancelOnLeafBranch() {
var wasCanceled = false;
var branchWasCanceled = false;
var d = new Deferred(function() { wasCanceled = true; });
var branch = d.branch(true).branch(true).branch(true);
branch.addErrback(function() { branchWasCanceled = true; });
branch.cancel();
assertTrue(wasCanceled);
assertTrue(branchWasCanceled);
}
function testCancelOnIntermediateBranch() {
var rootWasCanceled = false;
var d = new Deferred(function() { rootWasCanceled = true; });
var branch = d.branch(true).branch(true).branch(true);
var deepBranch1 = branch.branch(true);
var deepBranch2 = branch.branch(true);
branch.cancel();
assertTrue(rootWasCanceled);
assertTrue(deepBranch1.hasFired());
assertTrue(deepBranch2.hasFired());
}
function testCancelWithSomeCompletedBranches() {
var d = new Deferred();
var branch1 = d.branch(true);
var branch1HadCallback = false;
var branch1HadErrback = false;
branch1.
addCallback(function() { branch1HadCallback = true; }).
addErrback(function() { branch1HadErrback = true; });
d.callback(true);
assertTrue(branch1HadCallback);
assertFalse(branch1HadErrback);
var rootHadCallback = false;
var rootHadErrback = false;
// Block the root on a new Deferred indefinitely.
d.
addCallback(function() { rootHadCallback = true; }).
addCallback(function() { return new Deferred(); }).
addErrback(function() { rootHadErrback = true; });
var branch2 = d.branch(true);
assertTrue(rootHadCallback);
assertFalse(rootHadErrback);
branch2.cancel();
assertFalse(branch1HadErrback);
assertTrue('Canceling the last active branch should cancel the parent.',
rootHadErrback);
}
function testStaticCanceled() {
var callbackCalled = false;
var errbackResult = null;
var d = goog.async.Deferred.canceled();
d.addCallback(function() { callbackCalled = true;} );
d.addErrback(function(err) { errbackResult = err;} );
assertTrue('Errback should have been called with a canceled error',
errbackResult instanceof goog.async.Deferred.CanceledError);
assertFalse('Callback should not have been called', callbackCalled);
}
function testWhenWithValues() {
var called = false;
Deferred.when(4, function(obj) {
called = true;
assertEquals(4, obj);
});
assertTrue('Fn should have been called', called);
}
function testWhenWithDeferred() {
var called = false;
var d = new Deferred();
Deferred.when(d, function(obj) {
called = true;
assertEquals(6, obj);
});
assertFalse('Fn should not have been called yet', called);
d.callback(6);
assertTrue('Fn should have been called', called);
}
function testWhenDoesntAlterOriginalChain() {
var calls = 0;
var d1 = new Deferred();
var d2 = Deferred.when(d1, function(obj) {
calls++;
return obj * 2;
});
d1.addCallback(function(obj) {
assertEquals('Original chain should get original value', 5, obj);
calls++;
});
d2.addCallback(function(obj) {
assertEquals('Branched chain should get modified value', 10, obj);
calls++;
});
d1.callback(5);
assertEquals('There should have been 3 callbacks', 3, calls);
}
function testAssertNoErrors() {
var d = new Deferred();
d.addCallback(function() {
throw new Error('Foo');
});
d.callback(1);
var ex = assertThrows(function() {
Deferred.assertNoErrors();
neverHappen();
});
assertEquals('Expected to get thrown error', 'Foo', ex.message);
assertNotThrows(
'Calling Deferred.assertNoErrors() a second time with only one ' +
'scheduled error should pass.',
function() {
Deferred.assertNoErrors();
});
}
function testThen() {
var result;
var result2;
var d = new Deferred();
assertEquals(d.then, d['then']);
d.then(function(r) {
return result = r;
}).then(function (r2) {
result2 = r2;
});
d.callback('done');
assertUndefined(result);
mockClock.tick();
assertEquals('done', result);
assertEquals('done', result2);
}
function testThen_reject() {
var result, error, error2;
var d = new Deferred();
assertEquals(d.then, d['then']);
d.then(function(r) {
result = r;
}, function(e) {
error = e;
});
d.errback(new Error('boom'));
assertUndefined(result);
mockClock.tick();
assertUndefined(result);
assertEquals('boom', error.message);
}
function testPromiseAll() {
var d = new Deferred();
var p = new goog.Promise(function(resolve) {
resolve('promise');
});
goog.Promise.all([d, p]).then(function(values) {
assertEquals(2, values.length);
assertEquals('deferred', values[0]);
assertEquals('promise', values[1]);
});
d.callback('deferred');
mockClock.tick();
}
function testPromiseBlocksDeferred() {
var result;
var d = new Deferred();
var p = new goog.Promise(function(resolve) {
resolve('promise');
});
d.callback();
d.addCallback(function() {
return p;
});
d.addCallback(function(r) {
result = r;
});
assertUndefined(result);
mockClock.tick();
assertEquals('promise', result);
}
function testFromPromise() {
var result;
var p = new goog.Promise(function(resolve) {
resolve('promise');
});
var d = Deferred.fromPromise(p);
d.addCallback(function(value) {
result = value;
});
assertUndefined(result);
mockClock.tick();
assertEquals('promise', result);
}
function testPromiseBlocksDeferredAndRejects() {
var result;
var d = new Deferred();
var p = new goog.Promise(function(resolve, reject) {
reject(new Error('error'));
});
d.callback();
d.addCallback(function(r) {
return p;
});
d.addErrback(function(r) {
result = r;
});
assertUndefined(result);
mockClock.tick();
assertEquals('error', result.message);
}
function testPromiseFromCanceledDeferred() {
var result;
var d = new Deferred();
d.cancel();
var p = d.then(neverHappen, function(reason) {
result = reason;
});
mockClock.tick();
assertTrue(result instanceof goog.Promise.CancellationError);
}
function testThenableInterface() {
var d = new Deferred();
assertTrue(goog.Thenable.isImplementedBy(d));
}
function testAddBothPropagatesToErrback() {
var log = [];
var deferred = new goog.async.Deferred();
deferred.addBoth(goog.nullFunction);
deferred.addErrback(function () {
log.push('errback');
});
deferred.errback(new Error('my error'));
mockClock.tick(1);
assertArrayEquals(['errback'], log);
}
function testAddBothDoesNotPropagateUncaughtExceptions() {
var deferred = new goog.async.Deferred();
deferred.addBoth(goog.nullFunction);
deferred.errback(new Error('my error'));
mockClock.tick(1);
}
function testAddFinally() {
var deferred = new goog.async.Deferred();
deferred.addFinally(goog.nullFunction);
deferred.errback(new Error('my error'));
try {
mockClock.tick(1);
} catch (e) {
assertEquals('my error', e.message);
}
}
</script>
</body>
</html>