Adding mapbox-gl branch

This commit is contained in:
Andreas Hocevar
2015-03-16 18:50:27 +01:00
parent 7985f030fa
commit 57ee7f52fd
3109 changed files with 943365 additions and 0 deletions
@@ -0,0 +1,236 @@
<!DOCTYPE html>
<html>
<!--
Copyright 2012 The Closure Library Authors. All Rights Reserved.
Use of this source code is governed by the Apache License, Version 2.0.
See the COPYING file for details.
-->
<head>
<title>Closure Unit Tests - goog.result.chain</title>
<script src="../base.js"></script>
<script>
goog.require('goog.Timer');
goog.require('goog.result');
goog.require('goog.testing.MockClock');
goog.require('goog.testing.jsunit');
goog.require('goog.testing.recordFunction');
</script>
</head>
<body>
<script>
var givenResult, dependentResult, counter, actionCallback;
var mockClock;
function setUpPage() {
mockClock = new goog.testing.MockClock();
mockClock.install();
}
function setUp() {
mockClock.reset();
givenResult = new goog.result.SimpleResult();
dependentResult = new goog.result.SimpleResult();
counter = new goog.testing.recordFunction();
actionCallback = goog.testing.recordFunction(function(result) {
return dependentResult;
});
}
function tearDown() {
givenResult = dependentResult = counter = null;
}
function tearDownPage() {
mockClock.uninstall();
}
// SYNCHRONOUS TESTS:
function testChainWhenBothResultsSuccess() {
var finalResult = goog.result.chain(givenResult, actionCallback);
goog.result.wait(finalResult, counter);
givenResult.setValue(1);
dependentResult.setValue(2);
assertSuccess(actionCallback, givenResult, 1);
assertSuccess(counter, finalResult, 2);
}
function testChainWhenFirstResultError() {
var finalResult = goog.result.chain(givenResult, actionCallback);
goog.result.wait(finalResult, counter);
givenResult.setError(4);
assertNoCall(actionCallback);
assertError(counter, finalResult, 4);
}
function testChainWhenSecondResultError() {
var finalResult = goog.result.chain(givenResult, actionCallback);
goog.result.wait(finalResult, counter);
givenResult.setValue(1);
dependentResult.setError(5);
assertSuccess(actionCallback, givenResult, 1);
assertError(counter, finalResult, 5);
}
function testChainCancelFirstResult() {
var finalResult = goog.result.chain(givenResult, actionCallback);
goog.result.wait(finalResult, counter);
goog.result.cancelParentResults(finalResult);
assertNoCall(actionCallback);
assertTrue(givenResult.isCanceled());
assertTrue(finalResult.isCanceled());
}
function testChainCancelSecondResult() {
var finalResult = goog.result.chain(givenResult, actionCallback);
goog.result.wait(finalResult, counter);
givenResult.setValue(1);
goog.result.cancelParentResults(finalResult);
assertSuccess(actionCallback, givenResult, 1);
assertTrue(dependentResult.isCanceled());
assertTrue(finalResult.isCanceled());
}
function testDoubleChainCancel() {
var intermediateResult = goog.result.chain(givenResult, actionCallback);
var finalResult = goog.result.chain(intermediateResult, actionCallback);
assertTrue(goog.result.cancelParentResults(finalResult));
assertTrue(finalResult.isCanceled());
assertTrue(intermediateResult.isCanceled());
assertFalse(givenResult.isCanceled());
assertFalse(goog.result.cancelParentResults(finalResult));
}
function testCustomScope() {
var scope = {};
var finalResult = goog.result.chain(givenResult, actionCallback, scope);
goog.result.wait(finalResult, counter);
givenResult.setValue(1);
dependentResult.setValue(2);
assertEquals(scope, actionCallback.popLastCall().getThis());
}
// ASYNCHRONOUS TESTS:
function testChainAsyncWhenBothResultsSuccess() {
var finalResult = goog.result.chain(givenResult, actionCallback);
goog.result.wait(finalResult, counter);
goog.Timer.callOnce(function() { givenResult.setValue(1); });
mockClock.tick();
assertSuccess(actionCallback, givenResult, 1);
goog.Timer.callOnce(function() { dependentResult.setValue(2); });
mockClock.tick();
assertSuccess(counter, finalResult, 2);
}
function testChainAsyncWhenFirstResultError() {
var finalResult = goog.result.chain(givenResult, actionCallback);
goog.result.wait(finalResult, counter);
goog.Timer.callOnce(function() { givenResult.setError(6); });
mockClock.tick();
assertNoCall(actionCallback);
assertError(counter, finalResult, 6);
}
function testChainAsyncWhenSecondResultError() {
var finalResult = goog.result.chain(givenResult, actionCallback);
goog.result.wait(finalResult, counter);
goog.Timer.callOnce(function() { givenResult.setValue(1); });
mockClock.tick();
assertSuccess(actionCallback, givenResult, 1);
goog.Timer.callOnce(function() { dependentResult.setError(7); });
mockClock.tick();
assertError(counter, finalResult, 7);
}
function testChainAsyncCancelFirstResult() {
var finalResult = goog.result.chain(givenResult, actionCallback);
goog.result.wait(finalResult, counter);
goog.Timer.callOnce(function() {
goog.result.cancelParentResults(finalResult);
});
mockClock.tick();
assertNoCall(actionCallback);
assertTrue(givenResult.isCanceled());
assertTrue(finalResult.isCanceled());
}
function testChainAsyncCancelSecondResult() {
var finalResult = goog.result.chain(givenResult, actionCallback);
goog.result.wait(finalResult, counter);
goog.Timer.callOnce(function() { givenResult.setValue(1); });
mockClock.tick();
assertSuccess(actionCallback, givenResult, 1);
goog.Timer.callOnce(function() {
goog.result.cancelParentResults(finalResult);
});
mockClock.tick();
assertTrue(dependentResult.isCanceled());
assertTrue(finalResult.isCanceled());
}
// HELPER FUNCTIONS:
// Assert that the recordFunction was called once with an argument of
// 'result' (the second argument) which has a state of SUCCESS and
// a value of 'value' (the third argument).
function assertSuccess(recordFunction, result, value) {
assertEquals(1, recordFunction.getCallCount());
var res = recordFunction.popLastCall().getArgument(0);
assertEquals(result, res);
assertEquals(goog.result.Result.State.SUCCESS, res.getState());
assertEquals(value, res.getValue());
}
// Assert that the recordFunction was called once with an argument of
// 'result' (the second argument) which has a state of ERROR.
function assertError(recordFunction, result, value) {
assertEquals(1, recordFunction.getCallCount());
var res = recordFunction.popLastCall().getArgument(0);
assertEquals(result, res);
assertEquals(goog.result.Result.State.ERROR, res.getState());
assertEquals(value, res.getError());
}
// Assert that the recordFunction wasn't called
function assertNoCall(recordFunction) {
assertEquals(0, recordFunction.getCallCount());
}
</script>
</body>
</html>
@@ -0,0 +1,229 @@
<!DOCTYPE html>
<html>
<!--
Copyright 2012 The Closure Library Authors. All Rights Reserved.
Use of this source code is governed by the Apache License, Version 2.0.
See the COPYING file for details.
-->
<head>
<title>Closure Unit Tests - goog.result.combine</title>
<script src="../base.js"></script>
<script>
goog.require('goog.Timer');
goog.require('goog.array');
goog.require('goog.result');
goog.require('goog.testing.MockClock');
goog.require('goog.testing.jsunit');
goog.require('goog.testing.recordFunction');
</script>
</head>
<body>
<script>
var result1, result2, result3, result4, resultCallback;
var combinedResult, successCombinedResult, mockClock;
function setUpPage() {
mockClock = new goog.testing.MockClock();
mockClock.install();
}
function tearDownPage() {
goog.dispose(mockClock);
}
function setUp() {
mockClock.reset();
result1 = new goog.result.SimpleResult();
result2 = new goog.result.SimpleResult();
result3 = new goog.result.SimpleResult();
result4 = new goog.result.SimpleResult();
combinedResult = goog.result.combine(result1, result2, result3, result4);
successCombinedResult =
goog.result.combineOnSuccess(result1, result2, result3, result4);
resultCallback = goog.testing.recordFunction();
}
function tearDown() {
result1 = result2 = result3 = result4 = resultCallback = null;
combinedResult = successCombinedResult = null;
}
function testSynchronousCombine() {
resolveAllGivenResultsToSuccess();
newCombinedResult = goog.result.combine(result1, result2, result3, result4);
goog.result.wait(newCombinedResult, resultCallback);
assertSuccessCall(newCombinedResult, resultCallback);
}
function testCombineWhenAllResultsSuccess() {
goog.result.wait(combinedResult, resultCallback);
resolveAllGivenResultsToSuccess();
assertSuccessCall(combinedResult, resultCallback);
}
function testAsyncCombineWhenAllResultsSuccess() {
goog.result.wait(combinedResult, resultCallback);
goog.Timer.callOnce(function() { resolveAllGivenResultsToSuccess(); });
mockClock.tick();
assertSuccessCall(combinedResult, resultCallback);
}
function testCombineWhenAllResultsFail() {
goog.result.wait(combinedResult, resultCallback);
resolveAllGivenResultsToError();
assertSuccessCall(combinedResult, resultCallback);
}
function testAsyncCombineWhenAllResultsFail() {
goog.result.wait(combinedResult, resultCallback);
goog.Timer.callOnce(function() { resolveAllGivenResultsToError(); });
mockClock.tick();
assertSuccessCall(combinedResult, resultCallback);
}
function testCombineWhenSomeResultsSuccess() {
goog.result.wait(combinedResult, resultCallback);
resolveSomeGivenResultsToSuccess();
assertSuccessCall(combinedResult, resultCallback);
}
function testAsyncCombineWhenSomeResultsSuccess() {
goog.result.wait(combinedResult, resultCallback);
goog.Timer.callOnce(function() { resolveSomeGivenResultsToSuccess(); });
mockClock.tick();
assertSuccessCall(combinedResult, resultCallback);
}
function testCombineOnSuccessWhenAllResultsSuccess() {
goog.result.wait(successCombinedResult, resultCallback);
resolveAllGivenResultsToSuccess();
assertSuccessCall(successCombinedResult, resultCallback);
}
function testAsyncCombineOnSuccessWhenAllResultsSuccess() {
goog.result.wait(successCombinedResult, resultCallback);
goog.Timer.callOnce(function() { resolveAllGivenResultsToSuccess(); });
mockClock.tick();
assertSuccessCall(successCombinedResult, resultCallback);
}
function testCombineOnSuccessWhenAllResultsFail() {
goog.result.wait(successCombinedResult, resultCallback);
resolveAllGivenResultsToError();
assertErrorCall(successCombinedResult, resultCallback);
}
function testAsyncCombineOnSuccessWhenAllResultsFail() {
goog.result.wait(successCombinedResult, resultCallback);
goog.Timer.callOnce(function() { resolveAllGivenResultsToError(); });
mockClock.tick();
assertErrorCall(successCombinedResult, resultCallback);
}
function testCombineOnSuccessWhenSomeResultsSuccess() {
goog.result.wait(successCombinedResult, resultCallback);
resolveSomeGivenResultsToSuccess();
assertErrorCall(successCombinedResult, resultCallback);
}
function testAsyncCombineOnSuccessWhenSomeResultsSuccess() {
goog.result.wait(successCombinedResult, resultCallback);
goog.Timer.callOnce(function() { resolveSomeGivenResultsToSuccess(); });
mockClock.tick();
assertErrorCall(successCombinedResult, resultCallback);
}
function testCancelParentResults() {
goog.result.wait(combinedResult, resultCallback);
goog.result.cancelParentResults(combinedResult);
assertArgumentContainsGivenResults(combinedResult.getValue())
goog.array.forEach([result1, result2, result3, result4],
function(result) {
assertTrue(result.isCanceled());
});
}
function assertSuccessCall(combinedResult, resultCallback) {
assertEquals(goog.result.Result.State.SUCCESS, combinedResult.getState());
assertEquals(1, resultCallback.getCallCount());
var result = resultCallback.popLastCall().getArgument(0);
assertEquals(combinedResult, result);
assertArgumentContainsGivenResults(result.getValue());
}
function assertErrorCall(combinedResult, resultCallback) {
assertEquals(goog.result.Result.State.ERROR,
combinedResult.getState());
assertEquals(1, resultCallback.getCallCount());
var result = resultCallback.popLastCall().getArgument(0);
assertEquals(combinedResult, result);
assertArgumentContainsGivenResults(combinedResult.getError());
}
function assertArgumentContainsGivenResults(resultsArray) {
assertEquals(4, resultsArray.length);
goog.array.forEach([result1, result2, result3, result4], function(res) {
assertTrue(goog.array.contains(resultsArray, res));
});
}
function resolveAllGivenResultsToSuccess() {
goog.array.forEach([result1, result2, result3, result4], function(res) {
res.setValue(1);
});
}
function resolveAllGivenResultsToError() {
goog.array.forEach([result1, result2, result3, result4], function(res) {
res.setError();
});
}
function resolveSomeGivenResultsToSuccess() {
goog.array.forEach([result2, result3, result4], function(res) {
res.setValue(1);
});
result1.setError();
}
</script>
</body>
</html>
@@ -0,0 +1,59 @@
// Copyright 2012 The Closure Library Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS-IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
/**
* @fileoverview An adaptor from a Result to a Deferred.
*
* TODO (vbhasin): cancel() support.
* TODO (vbhasin): See if we can make this a static.
* TODO (gboyer, vbhasin): Rename to "Adapter" once this graduates; this is the
* proper programmer spelling.
*/
goog.provide('goog.result.DeferredAdaptor');
goog.require('goog.async.Deferred');
goog.require('goog.result');
goog.require('goog.result.Result');
/**
* An adaptor from Result to a Deferred, for use with existing Deferred chains.
*
* @param {!goog.result.Result} result A result.
* @constructor
* @extends {goog.async.Deferred}
* @final
* @deprecated Use {@link goog.Promise} instead - http://go/promisemigration
*/
goog.result.DeferredAdaptor = function(result) {
goog.result.DeferredAdaptor.base(this, 'constructor');
goog.result.wait(result, function(result) {
if (this.hasFired()) {
return;
}
if (result.getState() == goog.result.Result.State.SUCCESS) {
this.callback(result.getValue());
} else if (result.getState() == goog.result.Result.State.ERROR) {
if (result.getError() instanceof goog.result.Result.CancelError) {
this.cancel();
} else {
this.errback(result.getError());
}
}
}, this);
};
goog.inherits(goog.result.DeferredAdaptor, goog.async.Deferred);
@@ -0,0 +1,86 @@
<!DOCTYPE html>
<html>
<!--
Copyright 2012 The Closure Library Authors. All Rights Reserved.
Use of this source code is governed by the Apache License, Version 2.0.
See the COPYING file for details.
-->
<head>
<title>Closure Unit Tests - goog.result.DeferredAdaptor</title>
<script src="../base.js"></script>
<script>
goog.require('goog.result');
goog.require('goog.result.DeferredAdaptor');
goog.require('goog.result.SimpleResult');
goog.require('goog.testing.jsunit');
goog.require('goog.testing.recordFunction');
</script>
</head>
<body>
<script>
var result, deferred, record;
function setUp() {
result = new goog.result.SimpleResult();
deferred = new goog.result.DeferredAdaptor(result);
record = new goog.testing.recordFunction();
}
function tearDown() {
result = deferred = record = null;
}
function testResultSuccesfulResolution() {
deferred.addCallback(record);
result.setValue(1);
assertEquals(1, record.getCallCount());
var call = record.popLastCall();
assertEquals(1, call.getArgument(0));
}
function testResultErrorResolution() {
deferred.addErrback(record);
result.setError(2);
assertEquals(1, record.getCallCount());
var call = record.popLastCall();
assertEquals(2, call.getArgument(0));
}
function testResultCancelResolution() {
deferred.addCallback(record);
var cancelCallback = new goog.testing.recordFunction();
deferred.addErrback(cancelCallback);
result.cancel();
assertEquals(0, record.getCallCount());
assertEquals(1, cancelCallback.getCallCount());
var call = cancelCallback.popLastCall();
assertTrue(call.getArgument(0) instanceof
goog.async.Deferred.CanceledError);
}
function testAddCallbackOnResolvedResult() {
result.setValue(1);
assertEquals(1, result.getValue());
deferred.addCallback(record);
// callback should be called immediately when result is already resolved.
assertEquals(1, record.getCallCount());
assertEquals(1, record.popLastCall().getArgument(0));
}
function testAddErrbackOnErroredResult() {
result.setError(1);
assertEquals(1, result.getError());
// errback should be called immediately when result already errored.
deferred.addErrback(record);
assertEquals(1, record.getCallCount());
assertEquals(1, record.popLastCall().getArgument(0));
}
</script>
</body>
</html>
@@ -0,0 +1,45 @@
// Copyright 2012 The Closure Library Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS-IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
/**
* @fileoverview An interface for Results whose eventual value depends on the
* value of one or more other Results.
*/
goog.provide('goog.result.DependentResult');
goog.require('goog.result.Result');
/**
* A DependentResult represents a Result whose eventual value depends on the
* value of one or more other Results. For example, the Result returned by
* @see goog.result.chain or @see goog.result.combine is dependent on the
* Results given as arguments.
* @interface
* @extends {goog.result.Result}
* @deprecated Use {@link goog.Promise} instead - http://go/promisemigration
*/
goog.result.DependentResult = function() {};
/**
*
* @return {!Array<!goog.result.Result>} A list of Results which will affect
* the eventual value of this Result. The returned Results may themselves
* have parent results, which would be grandparents of this Result;
* grandparents (and any other ancestors) are not included in this list.
*/
goog.result.DependentResult.prototype.getParentResults = function() {};
@@ -0,0 +1,119 @@
// Copyright 2012 The Closure Library Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS-IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
/**
* @fileoverview Defines an interface that represents a Result.
*
* NOTE: goog.result is soft deprecated - we expect to replace this and
* {@link goog.async.Deferred} with {@link goog.Promise}.
*/
goog.provide('goog.result.Result');
goog.require('goog.Thenable');
/**
* A Result object represents a value returned by an asynchronous
* operation at some point in the future (e.g. a network fetch). This is akin
* to a 'Promise' or a 'Future' in other languages and frameworks.
*
* @interface
* @extends {goog.Thenable}
* @deprecated Use {@link goog.Promise} instead - http://go/promisemigration
*/
goog.result.Result = function() {};
/**
* Attaches handlers to be called when the value of this Result is available.
* Handlers are called in the order they were added by wait.
*
* @param {!function(this:T, !goog.result.Result)} handler The function called
* when the value is available. The function is passed the Result object as
* the only argument.
* @param {T=} opt_scope Optional scope for the handler.
* @template T
*/
goog.result.Result.prototype.wait = function(handler, opt_scope) {};
/**
* The States this object can be in.
*
* @enum {string}
* @deprecated Use {@link goog.Promise} instead - http://go/promisemigration
*/
goog.result.Result.State = {
/** The operation was a success and the value is available. */
SUCCESS: 'success',
/** The operation resulted in an error. */
ERROR: 'error',
/** The operation is incomplete and the value is not yet available. */
PENDING: 'pending'
};
/**
* @return {!goog.result.Result.State} The state of this Result.
*/
goog.result.Result.prototype.getState = function() {};
/**
* @return {*} The value of this Result. Will return undefined if the Result is
* pending or was an error.
*/
goog.result.Result.prototype.getValue = function() {};
/**
* @return {*} The error slug for this Result. Will return undefined if the
* Result was a success, the error slug was not set, or if the Result is
* pending.
*/
goog.result.Result.prototype.getError = function() {};
/**
* Cancels the current Result, invoking the canceler function, if set.
*
* @return {boolean} Whether the Result was canceled.
*/
goog.result.Result.prototype.cancel = function() {};
/**
* @return {boolean} Whether this Result was canceled.
*/
goog.result.Result.prototype.isCanceled = function() {};
/**
* The value to be passed to the error handlers invoked upon cancellation.
* @constructor
* @extends {Error}
* @final
* @deprecated Use {@link goog.Promise} instead - http://go/promisemigration
*/
goog.result.Result.CancelError = function() {
// Note that this does not derive from goog.debug.Error in order to prevent
// stack trace capture and reduce the amount of garbage generated during a
// cancel() operation.
};
goog.inherits(goog.result.Result.CancelError, Error);
@@ -0,0 +1,556 @@
// Copyright 2012 The Closure Library Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS-IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
/**
* @fileoverview This file provides primitives and tools (wait, transform,
* chain, combine) that make it easier to work with Results. This section
* gives an overview of their functionality along with some examples and the
* actual definitions have detailed descriptions next to them.
*
*
* NOTE: goog.result is soft deprecated - we expect to replace this and
* goog.async.Deferred with a wrapper around W3C Promises:
* http://dom.spec.whatwg.org/#promises.
*/
goog.provide('goog.result');
goog.require('goog.array');
goog.require('goog.result.DependentResult');
goog.require('goog.result.Result');
goog.require('goog.result.SimpleResult');
/**
* Returns a successful result containing the provided value.
*
* Example:
* <pre>
*
* var value = 'some-value';
* var result = goog.result.immediateResult(value);
* assertEquals(goog.result.Result.State.SUCCESS, result.getState());
* assertEquals(value, result.getValue());
*
* </pre>
*
* @param {*} value The value of the result.
* @return {!goog.result.Result} A Result object that has already been resolved
* to the supplied value.
*/
goog.result.successfulResult = function(value) {
var result = new goog.result.SimpleResult();
result.setValue(value);
return result;
};
/**
* Returns a failed result with the optional error slug set.
*
* Example:
* <pre>
*
* var error = new Error('something-failed');
* var result = goog.result.failedResult(error);
* assertEquals(goog.result.Result.State.ERROR, result.getState());
* assertEquals(error, result.getError());
*
* </pre>
*
* @param {*=} opt_error The error to which the result should resolve.
* @return {!goog.result.Result} A Result object that has already been resolved
* to the supplied Error.
*/
goog.result.failedResult = function(opt_error) {
var result = new goog.result.SimpleResult();
result.setError(opt_error);
return result;
};
/**
* Returns a canceled result.
* The result will be resolved to an error of type CancelError.
*
* Example:
* <pre>
*
* var result = goog.result.canceledResult();
* assertEquals(goog.result.Result.State.ERROR, result.getState());
* var error = result.getError();
* assertTrue(error instanceof goog.result.Result.CancelError);
*
* </pre>
*
* @return {!goog.result.Result} A canceled Result.
*/
goog.result.canceledResult = function() {
var result = new goog.result.SimpleResult();
result.cancel();
return result;
};
/**
* Calls the handler on resolution of the result (success or failure).
* The handler is passed the result object as the only parameter. The call will
* be immediate if the result is no longer pending.
*
* Example:
* <pre>
*
* var result = xhr.get('testdata/xhr_test_text.data');
*
* // Wait for the result to be resolved and alert it's state.
* goog.result.wait(result, function(result) {
* alert('State: ' + result.getState());
* });
* </pre>
*
* @param {!goog.result.Result} result The result to install the handlers.
* @param {function(this:T, !goog.result.Result)} handler The handler to be
* called. The handler is passed the result object as the only parameter.
* @param {T=} opt_scope Optional scope for the handler.
* @template T
*/
goog.result.wait = function(result, handler, opt_scope) {
result.wait(handler, opt_scope);
};
/**
* Calls the handler if the result succeeds. The result object is the only
* parameter passed to the handler. The call will be immediate if the result
* has already succeeded.
*
* Example:
* <pre>
*
* var result = xhr.get('testdata/xhr_test_text.data');
*
* // attach a success handler.
* goog.result.waitOnSuccess(result, function(resultValue, result) {
* var datavalue = result.getvalue();
* alert('value: ' + datavalue + ' == ' + resultValue);
* });
* </pre>
*
* @param {!goog.result.Result} result The result to install the handlers.
* @param {function(this:T, ?, !goog.result.Result)} handler The handler to be
* called. The handler is passed the result value and the result as
* parameters.
* @param {T=} opt_scope Optional scope for the handler.
* @template T
*/
goog.result.waitOnSuccess = function(result, handler, opt_scope) {
goog.result.wait(result, function(res) {
if (res.getState() == goog.result.Result.State.SUCCESS) {
// 'this' refers to opt_scope
handler.call(this, res.getValue(), res);
}
}, opt_scope);
};
/**
* Calls the handler if the result action errors. The result object is passed as
* the only parameter to the handler. The call will be immediate if the result
* object has already resolved to an error.
*
* Example:
*
* <pre>
*
* var result = xhr.get('testdata/xhr_test_text.data');
*
* // Attach a failure handler.
* goog.result.waitOnError(result, function(error) {
* // Failed asynchronous call!
* });
* </pre>
*
* @param {!goog.result.Result} result The result to install the handlers.
* @param {function(this:T, ?, !goog.result.Result)} handler The handler to be
* called. The handler is passed the error and the result object as
* parameters.
* @param {T=} opt_scope Optional scope for the handler.
* @template T
*/
goog.result.waitOnError = function(result, handler, opt_scope) {
goog.result.wait(result, function(res) {
if (res.getState() == goog.result.Result.State.ERROR) {
// 'this' refers to opt_scope
handler.call(this, res.getError(), res);
}
}, opt_scope);
};
/**
* Given a result and a transform function, returns a new result whose value,
* on success, will be the value of the given result after having been passed
* through the transform function.
*
* If the given result is an error, the returned result is also an error and the
* transform will not be called.
*
* Example:
* <pre>
*
* var result = xhr.getJson('testdata/xhr_test_json.data');
*
* // Transform contents of returned data using 'processJson' and create a
* // transformed result to use returned JSON.
* var transformedResult = goog.result.transform(result, processJson);
*
* // Attach success and failure handlers to the tranformed result.
* goog.result.waitOnSuccess(transformedResult, function(resultValue, result) {
* var jsonData = resultValue;
* assertEquals('ok', jsonData['stat']);
* });
*
* goog.result.waitOnError(transformedResult, function(error) {
* // Failed getJson call
* });
* </pre>
*
* @param {!goog.result.Result} result The result whose value will be
* transformed.
* @param {function(?):?} transformer The transformer
* function. The return value of this function will become the value of the
* returned result.
*
* @return {!goog.result.DependentResult} A new Result whose eventual value will
* be the returned value of the transformer function.
*/
goog.result.transform = function(result, transformer) {
var returnedResult = new goog.result.DependentResultImpl_([result]);
goog.result.wait(result, function(res) {
if (res.getState() == goog.result.Result.State.SUCCESS) {
returnedResult.setValue(transformer(res.getValue()));
} else {
returnedResult.setError(res.getError());
}
});
return returnedResult;
};
/**
* The chain function aids in chaining of asynchronous Results. This provides a
* convenience for use cases where asynchronous operations must happen serially
* i.e. subsequent asynchronous operations are dependent on data returned by
* prior asynchronous operations.
*
* It accepts a result and an action callback as arguments and returns a
* result. The action callback is called when the first result succeeds and is
* supposed to return a second result. The returned result is resolved when one
* of both of the results resolve (depending on their success or failure.) The
* state and value of the returned result in the various cases is documented
* below:
* <pre>
*
* First Result State: Second Result State: Returned Result State:
* SUCCESS SUCCESS SUCCESS
* SUCCESS ERROR ERROR
* ERROR Not created ERROR
* </pre>
*
* The value of the returned result, in the case both results succeed, is the
* value of the second result (the result returned by the action callback.)
*
* Example:
* <pre>
*
* var testDataResult = xhr.get('testdata/xhr_test_text.data');
*
* // Chain this result to perform another asynchronous operation when this
* // Result is resolved.
* var chainedResult = goog.result.chain(testDataResult,
* function(testDataResult) {
*
* // The result value of testDataResult is the URL for JSON data.
* var jsonDataUrl = testDataResult.getValue();
*
* // Create a new Result object when the original result is resolved.
* var jsonResult = xhr.getJson(jsonDataUrl);
*
* // Return the newly created Result.
* return jsonResult;
* });
*
* // The chained result resolves to success when both results resolve to
* // success.
* goog.result.waitOnSuccess(chainedResult, function(resultValue, result) {
*
* // At this point, both results have succeeded and we can use the JSON
* // data returned by the second asynchronous call.
* var jsonData = resultValue;
* assertEquals('ok', jsonData['stat']);
* });
*
* // Attach the error handler to be called when either Result fails.
* goog.result.waitOnError(chainedResult, function(result) {
* alert('chained result failed!');
* });
* </pre>
*
* @param {!goog.result.Result} result The result to chain.
* @param {function(this:T, !goog.result.Result):!goog.result.Result}
* actionCallback The callback called when the result is resolved. This
* callback must return a Result.
* @param {T=} opt_scope Optional scope for the action callback.
* @return {!goog.result.DependentResult} A result that is resolved when both
* the given Result and the Result returned by the actionCallback have
* resolved.
* @template T
*/
goog.result.chain = function(result, actionCallback, opt_scope) {
var dependentResult = new goog.result.DependentResultImpl_([result]);
// Wait for the first action.
goog.result.wait(result, function(result) {
if (result.getState() == goog.result.Result.State.SUCCESS) {
// The first action succeeded. Chain the contingent action.
var contingentResult = actionCallback.call(opt_scope, result);
dependentResult.addParentResult(contingentResult);
goog.result.wait(contingentResult, function(contingentResult) {
// The contingent action completed. Set the dependent result based on
// the contingent action's outcome.
if (contingentResult.getState() ==
goog.result.Result.State.SUCCESS) {
dependentResult.setValue(contingentResult.getValue());
} else {
dependentResult.setError(contingentResult.getError());
}
});
} else {
// First action failed, the dependent result should also fail.
dependentResult.setError(result.getError());
}
});
return dependentResult;
};
/**
* Returns a result that waits on all given results to resolve. Once all have
* resolved, the returned result will succeed (and never error).
*
* Example:
* <pre>
*
* var result1 = xhr.get('testdata/xhr_test_text.data');
*
* // Get a second independent Result.
* var result2 = xhr.getJson('testdata/xhr_test_json.data');
*
* // Create a Result that resolves when both prior results resolve.
* var combinedResult = goog.result.combine(result1, result2);
*
* // Process data after resolution of both results.
* goog.result.waitOnSuccess(combinedResult, function(results) {
* goog.array.forEach(results, function(result) {
* alert(result.getState());
* });
* });
* </pre>
*
* @param {...!goog.result.Result} var_args The results to wait on.
*
* @return {!goog.result.DependentResult} A new Result whose eventual value will
* be the resolved given Result objects.
*/
goog.result.combine = function(var_args) {
/** @type {!Array<!goog.result.Result>} */
var results = goog.array.clone(arguments);
var combinedResult = new goog.result.DependentResultImpl_(results);
var isResolved = function(res) {
return res.getState() != goog.result.Result.State.PENDING;
};
var checkResults = function() {
if (combinedResult.getState() == goog.result.Result.State.PENDING &&
goog.array.every(results, isResolved)) {
combinedResult.setValue(results);
}
};
goog.array.forEach(results, function(result) {
goog.result.wait(result, checkResults);
});
return combinedResult;
};
/**
* Returns a result that waits on all given results to resolve. Once all have
* resolved, the returned result will succeed if and only if all given results
* succeeded. Otherwise it will error.
*
* Example:
* <pre>
*
* var result1 = xhr.get('testdata/xhr_test_text.data');
*
* // Get a second independent Result.
* var result2 = xhr.getJson('testdata/xhr_test_json.data');
*
* // Create a Result that resolves when both prior results resolve.
* var combinedResult = goog.result.combineOnSuccess(result1, result2);
*
* // Process data after successful resolution of both results.
* goog.result.waitOnSuccess(combinedResult, function(results) {
* var textData = results[0].getValue();
* var jsonData = results[1].getValue();
* assertEquals('Just some data.', textData);
* assertEquals('ok', jsonData['stat']);
* });
*
* // Handle errors when either or both results failed.
* goog.result.waitOnError(combinedResult, function(combined) {
* var results = combined.getError();
*
* if (results[0].getState() == goog.result.Result.State.ERROR) {
* alert('result1 failed');
* }
*
* if (results[1].getState() == goog.result.Result.State.ERROR) {
* alert('result2 failed');
* }
* });
* </pre>
*
* @param {...!goog.result.Result} var_args The results to wait on.
*
* @return {!goog.result.DependentResult} A new Result whose eventual value will
* be an array of values of the given Result objects.
*/
goog.result.combineOnSuccess = function(var_args) {
var results = goog.array.clone(arguments);
var combinedResult = new goog.result.DependentResultImpl_(results);
var resolvedSuccessfully = function(res) {
return res.getState() == goog.result.Result.State.SUCCESS;
};
goog.result.wait(
goog.result.combine.apply(goog.result.combine, results),
// The combined result never ERRORs
function(res) {
var results = /** @type {Array<!goog.result.Result>} */ (
res.getValue());
if (goog.array.every(results, resolvedSuccessfully)) {
combinedResult.setValue(results);
} else {
combinedResult.setError(results);
}
});
return combinedResult;
};
/**
* Given a DependentResult, cancels the Results it depends on (that is, the
* results returned by getParentResults). This function does not recurse,
* so e.g. parents of parents are not canceled; only the immediate parents of
* the given Result are canceled.
*
* Example using @see goog.result.combine:
* <pre>
* var result1 = xhr.get('testdata/xhr_test_text.data');
*
* // Get a second independent Result.
* var result2 = xhr.getJson('testdata/xhr_test_json.data');
*
* // Create a Result that resolves when both prior results resolve.
* var combinedResult = goog.result.combineOnSuccess(result1, result2);
*
* combinedResult.wait(function() {
* if (combinedResult.isCanceled()) {
* goog.result.cancelParentResults(combinedResult);
* }
* });
*
* // Now, canceling combinedResult will cancel both result1 and result2.
* combinedResult.cancel();
* </pre>
* @param {!goog.result.DependentResult} dependentResult A Result that is
* dependent on the values of other Results (for example the Result of a
* goog.result.combine, goog.result.chain, or goog.result.transform call).
* @return {boolean} True if any results were successfully canceled; otherwise
* false.
* TODO(user): Implement a recursive version of this that cancels all
* ancestor results.
*/
goog.result.cancelParentResults = function(dependentResult) {
var anyCanceled = false;
var results = dependentResult.getParentResults();
for (var n = 0; n < results.length; n++) {
anyCanceled |= results[n].cancel();
}
return !!anyCanceled;
};
/**
* A DependentResult represents a Result whose eventual value depends on the
* value of one or more other Results. For example, the Result returned by
* @see goog.result.chain or @see goog.result.combine is dependent on the
* Results given as arguments.
*
* @param {!Array<!goog.result.Result>} parentResults A list of Results that
* will affect the eventual value of this Result.
* @constructor
* @implements {goog.result.DependentResult}
* @extends {goog.result.SimpleResult}
* @private
*/
goog.result.DependentResultImpl_ = function(parentResults) {
goog.result.DependentResultImpl_.base(this, 'constructor');
/**
* A list of Results that will affect the eventual value of this Result.
* @type {!Array<!goog.result.Result>}
* @private
*/
this.parentResults_ = parentResults;
};
goog.inherits(goog.result.DependentResultImpl_, goog.result.SimpleResult);
/**
* Adds a Result to the list of Results that affect this one.
* @param {!goog.result.Result} parentResult A result whose value affects the
* value of this Result.
*/
goog.result.DependentResultImpl_.prototype.addParentResult = function(
parentResult) {
this.parentResults_.push(parentResult);
};
/** @override */
goog.result.DependentResultImpl_.prototype.getParentResults = function() {
return this.parentResults_;
};
@@ -0,0 +1,46 @@
<!DOCTYPE html>
<html>
<!--
Copyright 2012 The Closure Library Authors. All Rights Reserved.
Use of this source code is governed by the Apache License, Version 2.0.
See the COPYING file for details.
-->
<head>
<title>Closure Unit Tests - goog.result.*</title>
<script src="../base.js"></script>
<script>
goog.require('goog.result');
goog.require('goog.testing.jsunit');
</script>
</head>
<body>
<script>
function testSuccessfulResult() {
var value = 'some-value';
var result = goog.result.successfulResult(value);
assertEquals(goog.result.Result.State.SUCCESS, result.getState());
assertEquals(value, result.getValue());
}
function testFailedResult() {
var error = new Error('something-failed');
var result = goog.result.failedResult(error);
assertEquals(goog.result.Result.State.ERROR, result.getState());
assertEquals(error, result.getError());
}
function testCanceledResult() {
var result = goog.result.canceledResult();
assertEquals(goog.result.Result.State.ERROR, result.getState());
var error = result.getError();
assertTrue(error instanceof goog.result.Result.CancelError);
}
</script>
</body>
</html>
@@ -0,0 +1,260 @@
// Copyright 2012 The Closure Library Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS-IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
/**
* @fileoverview A SimpleResult object that implements goog.result.Result.
* See below for a more detailed description.
*/
goog.provide('goog.result.SimpleResult');
goog.provide('goog.result.SimpleResult.StateError');
goog.require('goog.Promise');
goog.require('goog.Thenable');
goog.require('goog.debug.Error');
goog.require('goog.result.Result');
/**
* A SimpleResult object is a basic implementation of the
* goog.result.Result interface. This could be subclassed(e.g. XHRResult)
* or instantiated and returned by another class as a form of result. The caller
* receiving the result could then attach handlers to be called when the result
* is resolved(success or error).
*
* @constructor
* @implements {goog.result.Result}
* @deprecated Use {@link goog.Promise} instead - http://go/promisemigration
*/
goog.result.SimpleResult = function() {
/**
* The current state of this Result.
* @type {goog.result.Result.State}
* @private
*/
this.state_ = goog.result.Result.State.PENDING;
/**
* The list of handlers to call when this Result is resolved.
* @type {!Array<!goog.result.SimpleResult.HandlerEntry_>}
* @private
*/
this.handlers_ = [];
// The value_ and error_ properties are initialized in the constructor to
// ensure that all SimpleResult instances share the same hidden class in
// modern JavaScript engines.
/**
* The 'value' of this Result.
* @type {*}
* @private
*/
this.value_ = undefined;
/**
* The error slug for this Result.
* @type {*}
* @private
*/
this.error_ = undefined;
};
goog.Thenable.addImplementation(goog.result.SimpleResult);
/**
* A waiting handler entry.
* @typedef {{
* callback: !function(goog.result.SimpleResult),
* scope: Object
* }}
* @private
*/
goog.result.SimpleResult.HandlerEntry_;
/**
* Error thrown if there is an attempt to set the value or error for this result
* more than once.
*
* @constructor
* @extends {goog.debug.Error}
* @final
* @deprecated Use {@link goog.Promise} instead - http://go/promisemigration
*/
goog.result.SimpleResult.StateError = function() {
goog.result.SimpleResult.StateError.base(this, 'constructor',
'Multiple attempts to set the state of this Result');
};
goog.inherits(goog.result.SimpleResult.StateError, goog.debug.Error);
/** @override */
goog.result.SimpleResult.prototype.getState = function() {
return this.state_;
};
/** @override */
goog.result.SimpleResult.prototype.getValue = function() {
return this.value_;
};
/** @override */
goog.result.SimpleResult.prototype.getError = function() {
return this.error_;
};
/**
* Attaches handlers to be called when the value of this Result is available.
*
* @param {!function(this:T, !goog.result.SimpleResult)} handler The function
* called when the value is available. The function is passed the Result
* object as the only argument.
* @param {T=} opt_scope Optional scope for the handler.
* @template T
* @override
*/
goog.result.SimpleResult.prototype.wait = function(handler, opt_scope) {
if (this.isPending_()) {
this.handlers_.push({
callback: handler,
scope: opt_scope || null
});
} else {
handler.call(opt_scope, this);
}
};
/**
* Sets the value of this Result, changing the state.
*
* @param {*} value The value to set for this Result.
*/
goog.result.SimpleResult.prototype.setValue = function(value) {
if (this.isPending_()) {
this.value_ = value;
this.state_ = goog.result.Result.State.SUCCESS;
this.callHandlers_();
} else if (!this.isCanceled()) {
// setValue is a no-op if this Result has been canceled.
throw new goog.result.SimpleResult.StateError();
}
};
/**
* Sets the Result to be an error Result.
*
* @param {*=} opt_error Optional error slug to set for this Result.
*/
goog.result.SimpleResult.prototype.setError = function(opt_error) {
if (this.isPending_()) {
this.error_ = opt_error;
this.state_ = goog.result.Result.State.ERROR;
this.callHandlers_();
} else if (!this.isCanceled()) {
// setError is a no-op if this Result has been canceled.
throw new goog.result.SimpleResult.StateError();
}
};
/**
* Calls the handlers registered for this Result.
*
* @private
*/
goog.result.SimpleResult.prototype.callHandlers_ = function() {
var handlers = this.handlers_;
this.handlers_ = [];
for (var n = 0; n < handlers.length; n++) {
var handlerEntry = handlers[n];
handlerEntry.callback.call(handlerEntry.scope, this);
}
};
/**
* @return {boolean} Whether the Result is pending.
* @private
*/
goog.result.SimpleResult.prototype.isPending_ = function() {
return this.state_ == goog.result.Result.State.PENDING;
};
/**
* Cancels the Result.
*
* @return {boolean} Whether the result was canceled. It will not be canceled if
* the result was already canceled or has already resolved.
* @override
*/
goog.result.SimpleResult.prototype.cancel = function() {
// cancel is a no-op if the result has been resolved.
if (this.isPending_()) {
this.setError(new goog.result.Result.CancelError());
return true;
}
return false;
};
/** @override */
goog.result.SimpleResult.prototype.isCanceled = function() {
return this.state_ == goog.result.Result.State.ERROR &&
this.error_ instanceof goog.result.Result.CancelError;
};
/** @override */
goog.result.SimpleResult.prototype.then = function(
opt_onFulfilled, opt_onRejected, opt_context) {
var resolve, reject;
// Copy the resolvers to outer scope, so that they are available
// when the callback to wait() fires (which may be synchronous).
var promise = new goog.Promise(function(res, rej) {
resolve = res;
reject = rej;
});
this.wait(function(result) {
if (result.isCanceled()) {
promise.cancel();
} else if (result.getState() == goog.result.Result.State.SUCCESS) {
resolve(result.getValue());
} else if (result.getState() == goog.result.Result.State.ERROR) {
reject(result.getError());
}
});
return promise.then(opt_onFulfilled, opt_onRejected, opt_context);
};
/**
* Creates a SimpleResult that fires when the given promise resolves.
* Use only during migration to Promises.
* @param {!goog.Promise<?>} promise
* @return {!goog.result.Result}
*/
goog.result.SimpleResult.fromPromise = function(promise) {
var result = new goog.result.SimpleResult();
promise.then(result.setValue, result.setError, result);
return result;
};
@@ -0,0 +1,375 @@
<!DOCTYPE html>
<html>
<!--
Copyright 2012 The Closure Library Authors. All Rights Reserved.
Use of this source code is governed by the Apache License, Version 2.0.
See the COPYING file for details.
-->
<head>
<title>Closure Unit Tests - goog.result.SimpleResult</title>
<script src="../base.js"></script>
<script>
goog.require('goog.Timer');
goog.require('goog.Promise');
goog.require('goog.Thenable');
goog.require('goog.result');
goog.require('goog.testing.MockClock');
goog.require('goog.testing.recordFunction');
goog.require('goog.testing.jsunit');
</script>
</head>
<body>
<script>
var result, mockClock, resultCallback;
function setUpPage() {
mockClock = new goog.testing.MockClock();
mockClock.install();
}
function setUp() {
mockClock.reset();
resultCallback = new goog.testing.recordFunction();
resultCallback1 = new goog.testing.recordFunction();
resultCallback2 = new goog.testing.recordFunction();
result = new goog.result.SimpleResult();
}
function tearDown() {
resultCallback = resultCallback1 = resultCallback2 = result = null;
}
function tearDownPage() {
mockClock.uninstall();
goog.dispose(mockClock);
}
function testHandlersCalledOnSuccess() {
result.wait(resultCallback1);
result.wait(resultCallback2);
assertEquals(goog.result.Result.State.PENDING, result.getState());
assertEquals(0, resultCallback1.getCallCount());
assertEquals(0, resultCallback2.getCallCount());
result.setValue(2);
assertEquals(goog.result.Result.State.SUCCESS, result.getState());
assertEquals(2, result.getValue());
assertEquals(1, resultCallback1.getCallCount());
assertEquals(1, resultCallback2.getCallCount());
var res1 = resultCallback1.popLastCall().getArgument(0);
assertObjectEquals(result, res1);
var res2 = resultCallback2.popLastCall().getArgument(0);
assertObjectEquals(result, res2);
}
function testCustomHandlerScope() {
result.wait(resultCallback1);
var scope = {};
result.wait(resultCallback2, scope);
result.setValue(2);
assertEquals(1, resultCallback1.getCallCount());
assertEquals(1, resultCallback2.getCallCount());
var this1 = resultCallback1.popLastCall().getThis();
assertObjectEquals(goog.global, this1);
var this2 = resultCallback2.popLastCall().getThis();
assertObjectEquals(scope, this2);
}
function testHandlersCalledOnError() {
result.wait(resultCallback1);
result.wait(resultCallback2);
assertEquals(goog.result.Result.State.PENDING, result.getState());
var error = "Network Error";
result.setError(error);
assertEquals(goog.result.Result.State.ERROR, result.getState());
assertEquals(error, result.getError());
assertEquals(1, resultCallback1.getCallCount());
assertEquals(1, resultCallback2.getCallCount());
var res1 = resultCallback1.popLastCall().getArgument(0);
assertObjectEquals(result, res1);
var res2 = resultCallback2.popLastCall().getArgument(0);
assertObjectEquals(result, res2);
}
function testAttachingHandlerOnSuccessfulResult() {
result.setValue(2);
assertEquals(goog.result.Result.State.SUCCESS, result.getState());
assertEquals(2, result.getValue());
// resultCallback should be called immediately on a resolved Result
assertEquals(0, resultCallback.getCallCount());
result.wait(resultCallback);
assertEquals(1, resultCallback.getCallCount());
var res = resultCallback.popLastCall().getArgument(0);
assertEquals(result, res);
}
function testAttachingHandlerOnErrorResult() {
var error = { code: -1, errorString: "Invalid JSON" };
result.setError(error);
assertEquals(goog.result.Result.State.ERROR, result.getState());
assertEquals(error, result.getError());
// resultCallback should be called immediately on a resolved Result
assertEquals(0, resultCallback.getCallCount());
result.wait(resultCallback);
assertEquals(1, resultCallback.getCallCount());
var res = resultCallback.popLastCall().getArgument(0);
assertEquals(result, res);
}
function testExceptionThrownOnMultipleSuccessfulResolutionAttempts() {
result.setValue(1);
assertEquals(goog.result.Result.State.SUCCESS, result.getState());
assertEquals(1, result.getValue());
// Try to set the value again
var e = assertThrows(goog.bind(result.setValue, result, 3));
assertTrue(e instanceof goog.result.SimpleResult.StateError);
}
function testExceptionThrownOnMultipleErrorResolutionAttempts() {
assertEquals(goog.result.Result.State.PENDING, result.getState());
result.setError(5);
assertEquals(goog.result.Result.State.ERROR, result.getState());
assertEquals(5, result.getError());
// Try to set error again
var e = assertThrows(goog.bind(result.setError, result, 4));
assertTrue(e instanceof goog.result.SimpleResult.StateError);
}
function testExceptionThrownOnSuccessThenErrorResolutionAttempt() {
assertEquals(goog.result.Result.State.PENDING, result.getState());
result.setValue(1);
assertEquals(goog.result.Result.State.SUCCESS, result.getState());
assertEquals(1, result.getValue());
// Try to set error after setting value
var e = assertThrows(goog.bind(result.setError, result, 3));
assertTrue(e instanceof goog.result.SimpleResult.StateError);
}
function testExceptionThrownOnErrorThenSuccessResolutionAttempt() {
assertEquals(goog.result.Result.State.PENDING, result.getState());
var error = "fail";
result.setError(error);
assertEquals(goog.result.Result.State.ERROR, result.getState());
assertEquals(error, result.getError());
// Try to set value after setting error
var e = assertThrows(goog.bind(result.setValue, result, 1));
assertTrue(e instanceof goog.result.SimpleResult.StateError);
}
function testSuccessfulAsyncResolution() {
result.wait(resultCallback);
assertEquals(goog.result.Result.State.PENDING, result.getState());
goog.Timer.callOnce(function() {
result.setValue(1);
});
mockClock.tick();
assertEquals(1, resultCallback.getCallCount());
var res = resultCallback.popLastCall().getArgument(0);
assertEquals(goog.result.Result.State.SUCCESS, res.getState());
assertEquals(1, res.getValue());
}
function testErrorAsyncResolution() {
result.wait(resultCallback);
assertEquals(goog.result.Result.State.PENDING, result.getState());
var error = 'Network failure';
goog.Timer.callOnce(function() {
result.setError(error);
});
mockClock.tick();
assertEquals(1, resultCallback.getCallCount());
var res = resultCallback.popLastCall().getArgument(0);
assertEquals(goog.result.Result.State.ERROR, res.getState());
assertEquals(error, res.getError());
}
function testCancelStateAndReturn() {
assertFalse(result.isCanceled());
var canceled = result.cancel();
assertTrue(result.isCanceled());
assertEquals(goog.result.Result.State.ERROR, result.getState());
assertTrue(result.getError() instanceof goog.result.Result.CancelError);
assertTrue(canceled);
}
function testErrorHandlersFireOnCancel() {
result.wait(resultCallback);
result.cancel();
assertEquals(1, resultCallback.getCallCount());
var lastCall = resultCallback.popLastCall();
var res = lastCall.getArgument(0);
assertEquals(goog.result.Result.State.ERROR, res.getState());
assertTrue(res.getError() instanceof goog.result.Result.CancelError);
}
function testCancelAfterSetValue() {
// cancel after setValue/setError => no-op
result.wait(resultCallback);
result.setValue(1);
assertEquals(goog.result.Result.State.SUCCESS, result.getState());
assertEquals(1, result.getValue());
assertEquals(1, resultCallback.getCallCount());
result.cancel();
assertEquals(goog.result.Result.State.SUCCESS, result.getState());
assertEquals(1, result.getValue());
assertEquals(1, resultCallback.getCallCount());
}
function testSetValueAfterCancel() {
// setValue/setError after cancel => no-op
result.wait(resultCallback);
result.cancel();
assertTrue(result.isCanceled());
assertTrue(result.getError() instanceof goog.result.Result.CancelError);
result.setValue(1);
assertTrue(result.isCanceled());
assertTrue(result.getError() instanceof goog.result.Result.CancelError);
result.setError(3);
assertTrue(result.isCanceled());
assertTrue(result.getError() instanceof goog.result.Result.CancelError);
}
function testFromResolvedPromise() {
var promise = goog.Promise.resolve('resolved');
result = goog.result.SimpleResult.fromPromise(promise);
assertEquals(goog.result.Result.State.PENDING, result.getState());
mockClock.tick();
assertEquals(goog.result.Result.State.SUCCESS, result.getState());
assertEquals('resolved', result.getValue());
assertEquals(undefined, result.getError());
}
function testFromRejectedPromise() {
var promise = goog.Promise.reject('rejected');
result = goog.result.SimpleResult.fromPromise(promise);
assertEquals(goog.result.Result.State.PENDING, result.getState());
mockClock.tick();
assertEquals(goog.result.Result.State.ERROR, result.getState());
assertEquals(undefined, result.getValue());
assertEquals('rejected', result.getError());
}
function testThen() {
var value1, value2;
result.then(function(val1) {
return value1 = val1;
}).then(function(val2) {
value2 = val2;
});
result.setValue('done');
assertUndefined(value1);
assertUndefined(value2);
mockClock.tick();
assertEquals('done', value1);
assertEquals('done', value2);
}
function testThen_reject() {
var value, reason;
result.then(
function(v) { value = v; },
function(r) { reason = r; });
result.setError(new Error('oops'));
assertUndefined(value);
mockClock.tick();
assertUndefined(value);
assertEquals('oops', reason.message);
}
function testPromiseAll() {
var promise = goog.Promise.resolve('promise');
goog.Promise.all([result, promise]).then(function(values) {
assertEquals(2, values.length);
assertEquals('result', values[0]);
assertEquals('promise', values[1]);
});
result.setValue('result');
mockClock.tick();
}
function testResolvingPromiseBlocksResult() {
var value;
goog.Promise.resolve('promise').then(function(value) {
result.setValue(value);
});
result.wait(function(r) {
value = r.getValue();
});
assertUndefined(value);
mockClock.tick();
assertEquals('promise', value);
}
function testRejectingPromiseBlocksResult() {
var err;
goog.Promise.reject(new Error('oops')).then(
undefined /* opt_onResolved */,
function(reason) {
result.setError(reason);
});
result.wait(function(r) {
err = r.getError();
});
assertUndefined(err);
mockClock.tick();
assertEquals('oops', err.message);
}
function testPromiseFromCanceledResult() {
var reason;
result.cancel();
result.then(
undefined /* opt_onResolved */,
function(r) {
reason = r;
});
mockClock.tick();
assertTrue(reason instanceof goog.Promise.CancellationError);
}
function testThenableInterface() {
assertTrue(goog.Thenable.isImplementedBy(result));
}
</script>
</body>
</html>
@@ -0,0 +1,153 @@
<!DOCTYPE html>
<html>
<!--
Copyright 2012 The Closure Library Authors. All Rights Reserved.
Use of this source code is governed by the Apache License, Version 2.0.
See the COPYING file for details.
-->
<head>
<title>Closure Unit Tests - goog.result.transform</title>
<script src="../base.js"></script>
<script>
goog.require('goog.Timer');
goog.require('goog.result.SimpleResult');
goog.require('goog.result');
goog.require('goog.testing.MockClock');
goog.require('goog.testing.jsunit');
goog.require('goog.testing.recordFunction');
</script>
</head>
<body>
<script>
var result, resultCallback, multiplyResult, mockClock;
function setUpPage() {
mockClock = new goog.testing.MockClock();
mockClock.install();
}
function setUp() {
mockClock.reset();
result = new goog.result.SimpleResult();
resultCallback = new goog.testing.recordFunction();
multiplyResult = goog.testing.recordFunction(function(value) {
return value * 2;
});
}
function tearDown() {
result = multiplyResult = null;
}
function tearDownPage() {
mockClock.uninstall();
goog.dispose(mockClock);
}
function testTransformWhenResultSuccess() {
var transformedResult = goog.result.transform(result, multiplyResult);
goog.result.wait(transformedResult, resultCallback);
assertEquals(goog.result.Result.State.PENDING, result.getState());
result.setValue(1);
assertTransformerCall(multiplyResult, 1);
assertSuccessCall(resultCallback, transformedResult, 2);
}
function testTransformWhenResultSuccessAsync() {
var transformedResult = goog.result.transform(result, multiplyResult);
goog.result.wait(transformedResult, resultCallback);
goog.Timer.callOnce(function() {
result.setValue(1);
});
assertEquals(goog.result.Result.State.PENDING, result.getState());
mockClock.tick();
assertTransformerCall(multiplyResult, 1);
assertSuccessCall(resultCallback, transformedResult, 2);
}
function testTransformWhenResultError() {
var transformedResult = goog.result.transform(result, multiplyResult);
goog.result.wait(transformedResult, resultCallback);
assertEquals(goog.result.Result.State.PENDING, result.getState());
result.setError(4);
assertNoCall(multiplyResult);
assertErrorCall(resultCallback, transformedResult, 4);
}
function testTransformWhenResultErrorAsync() {
var transformedResult = goog.result.transform(result, multiplyResult);
goog.result.wait(transformedResult, resultCallback);
goog.Timer.callOnce(function() {
result.setError(5);
});
assertEquals(goog.result.Result.State.PENDING, result.getState());
mockClock.tick();
assertNoCall(multiplyResult);
assertErrorCall(resultCallback, transformedResult, 5);
}
function testCancelParentResults() {
var transformedResult = goog.result.transform(result, multiplyResult);
goog.result.wait(transformedResult, resultCallback);
goog.result.cancelParentResults(transformedResult);
assertTrue(result.isCanceled());
result.setValue(1);
assertNoCall(multiplyResult);
}
function testDoubleTransformCancel() {
var step1Result = goog.result.transform(result, multiplyResult);
var step2Result = goog.result.transform(step1Result, multiplyResult);
goog.result.cancelParentResults(step2Result);
assertFalse(result.isCanceled());
assertTrue(step1Result.isCanceled());
assertTrue(step2Result.isCanceled());
}
function assertSuccessCall(recordFunction, result, value) {
assertEquals(1, recordFunction.getCallCount());
var res = recordFunction.popLastCall().getArgument(0);
assertEquals(result, res);
assertEquals(goog.result.Result.State.SUCCESS, res.getState());
assertEquals(value, res.getValue());
}
function assertErrorCall(recordFunction, result, value) {
assertEquals(1, recordFunction.getCallCount());
var res = recordFunction.popLastCall().getArgument(0);
assertEquals(result, res);
assertEquals(goog.result.Result.State.ERROR, res.getState());
assertEquals(value, res.getError());
}
function assertNoCall(recordFunction) {
assertEquals(0, recordFunction.getCallCount());
}
function assertTransformerCall(recordFunction, value) {
assertEquals(1, recordFunction.getCallCount());
var argValue = recordFunction.popLastCall().getArgument(0);
assertEquals(value, argValue);
}
</script>
</body>
</html>
@@ -0,0 +1,211 @@
<!DOCTYPE html>
<html>
<!--
Copyright 2012 The Closure Library Authors. All Rights Reserved.
Use of this source code is governed by the Apache License, Version 2.0.
See the COPYING file for details.
-->
<head>
<title>Closure Unit Tests - goog.result.wait</title>
<script src="../base.js"></script>
<script>
goog.require('goog.Timer');
goog.require('goog.result.SimpleResult');
goog.require('goog.result');
goog.require('goog.testing.MockClock');
goog.require('goog.testing.jsunit');
goog.require('goog.testing.recordFunction');
</script>
</head>
<body>
<script>
var result, waitCallback, waitOnSuccessCallback, waitOnErrorCallback;
var mockClock, propertyReplacer;
function setUpPage() {
mockClock = new goog.testing.MockClock();
mockClock.install();
}
function setUp() {
mockClock.reset();
result = new goog.result.SimpleResult();
propertyReplacer = new goog.testing.PropertyReplacer();
waitCallback = new goog.testing.recordFunction();
waitOnSuccessCallback = new goog.testing.recordFunction();
waitOnErrorCallback = new goog.testing.recordFunction();
}
function tearDown() {
result = waitCallback = waitOnSuccessCallback = waitOnErrorCallback = null;
propertyReplacer.reset();
}
function tearDownPage() {
mockClock.uninstall();
}
function testSynchronousSuccess() {
assertEquals(goog.result.Result.State.PENDING, result.getState());
assertUndefined(result.getValue());
goog.result.wait(result, waitCallback);
goog.result.waitOnSuccess(result, waitOnSuccessCallback);
goog.result.waitOnError(result, waitOnErrorCallback);
result.setValue(1);
assertEquals(goog.result.Result.State.SUCCESS, result.getState());
assertEquals(1, result.getValue());
assertWaitCall(waitCallback, result);
assertCall(waitOnSuccessCallback, 1, result);
assertNoCall(waitOnErrorCallback);
}
function testAsynchronousSuccess() {
goog.result.wait(result, waitCallback);
goog.result.waitOnSuccess(result, waitOnSuccessCallback);
goog.result.waitOnError(result, waitOnErrorCallback);
goog.Timer.callOnce(function() {
result.setValue(1);
});
assertUndefined(result.getValue());
assertEquals(goog.result.Result.State.PENDING, result.getState());
assertNoCall(waitCallback);
assertNoCall(waitOnSuccessCallback);
assertNoCall(waitOnErrorCallback);
mockClock.tick();
assertEquals(goog.result.Result.State.SUCCESS, result.getState());
assertEquals(1, result.getValue());
assertWaitCall(waitCallback, result);
assertCall(waitOnSuccessCallback, 1, result);
assertNoCall(waitOnErrorCallback);
}
function testSynchronousError() {
assertEquals(goog.result.Result.State.PENDING, result.getState());
assertUndefined(result.getValue());
goog.result.wait(result, waitCallback);
goog.result.waitOnSuccess(result, waitOnSuccessCallback);
goog.result.waitOnError(result, waitOnErrorCallback);
result.setError();
assertEquals(goog.result.Result.State.ERROR, result.getState());
assertUndefined(result.getValue());
assertWaitCall(waitCallback, result);
assertNoCall(waitOnSuccessCallback);
assertCall(waitOnErrorCallback, undefined, result);
}
function testAsynchronousError() {
goog.result.wait(result, waitCallback);
goog.result.waitOnSuccess(result, waitOnSuccessCallback);
goog.result.waitOnError(result, waitOnErrorCallback);
goog.Timer.callOnce(function() {
result.setError();
});
assertEquals(goog.result.Result.State.PENDING, result.getState());
assertUndefined(result.getValue());
assertNoCall(waitCallback);
assertNoCall(waitOnSuccessCallback);
assertNoCall(waitOnErrorCallback);
mockClock.tick();
assertEquals(goog.result.Result.State.ERROR, result.getState());
assertUndefined(result.getValue());
assertWaitCall(waitCallback, result);
assertNoCall(waitOnSuccessCallback);
assertCall(waitOnErrorCallback, undefined, result);
}
function testCustomScope() {
var scope = {};
goog.result.wait(result, waitCallback, scope);
result.setValue(1);
assertEquals(scope, waitCallback.popLastCall().getThis());
}
function testDefaultScope() {
goog.result.wait(result, waitCallback);
result.setValue(1);
assertEquals(goog.global, waitCallback.popLastCall().getThis());
}
function testOnSuccessScope() {
var scope = {};
goog.result.waitOnSuccess(result, waitOnSuccessCallback, scope);
result.setValue(1);
assertCall(waitOnSuccessCallback, 1, result, scope);
}
function testOnErrorScope() {
var scope = {};
goog.result.waitOnError(result, waitOnErrorCallback, scope);
result.setError();
assertCall(waitOnErrorCallback, undefined, result, scope);
}
/**
* Assert that a callback function stubbed out with goog.recordFunction was
* called with the expected arguments by goog.result.waitOnSuccess/Error.
* @param {Function} recordedFunction The callback function.
* @param {?} value The value stored in the result.
* @param {!goog.result.Result} result The result that was resolved to SUCCESS
* or ERROR.
* @param {Object=} opt_scope Optional scope that the test function should be
* called in. By default, it is goog.global.
*/
function assertCall(recordedFunction, value, result, opt_scope) {
var scope = opt_scope || goog.global;
assertEquals(1, recordedFunction.getCallCount());
var call = recordedFunction.popLastCall();
assertEquals(2, call.getArguments().length);
assertEquals(value, call.getArgument(0));
assertEquals(result, call.getArgument(1));
assertEquals(scope, call.getThis());
}
/**
* Assert that a callback function stubbed out with goog.recordFunction was
* called with the expected arguments by goog.result.wait.
* @param {Function} recordedFunction The callback function.
* @param {!goog.result.Result} result The result that was resolved to SUCCESS
* or ERROR.
* @param {Object=} opt_scope Optional scope that the test function should be
* called in. By default, it is goog.global.
*/
function assertWaitCall(recordedFunction, result, opt_scope) {
var scope = opt_scope || goog.global;
assertEquals(1, recordedFunction.getCallCount());
var call = recordedFunction.popLastCall();
assertEquals(1, call.getArguments().length);
assertEquals(result, call.getArgument(0));
assertEquals(scope, call.getThis());
}
function assertNoCall(recordedFunction) {
assertEquals(0, recordedFunction.getCallCount());
}
</script>
</body>
</html>