// Copyright 2012 The Closure Library Authors. All Rights Reserved. // Use of this source code is governed by the Apache License, Version 2.0. goog.provide('goog.result.SimpleResultTest'); goog.setTestOnly('goog.result.SimpleResultTest'); 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'); var result, mockClock, resultCallback; var resultCallback1; var resultCallback2; 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)); }