/* * Copyright 2012 LinkedIn, Inc * * 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. */ package com.linkedin.parseq.promise; import org.testng.annotations.AfterMethod; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; import java.util.concurrent.CountDownLatch; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicReference; import static com.linkedin.parseq.TestUtil.withDisabledLogging; import static org.testng.AssertJUnit.*; /** * @author Chris Pettitt (cpettitt@linkedin.com) */ public class TestSettablePromise { private ScheduledExecutorService _scheduler; @BeforeMethod public void setUp() throws Exception { _scheduler = Executors.newSingleThreadScheduledExecutor(); } @AfterMethod public void tearDown() throws Exception { _scheduler.shutdownNow(); _scheduler = null; } @Test public void testPromiseInitialState() { assertFalse(Promises.settable().isDone()); assertFalse(Promises.settable().isFailed()); } @Test public void testSetAndGet() { final SettablePromise<String> promise = Promises.settable(); promise.done("done!"); assertEquals("done!", promise.get()); assertTrue(promise.isDone()); } @Test public void testGetWithoutSet() { try { Promises.settable().get(); fail("Should have thrown PromiseUnresolvedException"); } catch (PromiseUnresolvedException e) { // Expected case } } @Test public void testGetOrDefaultWithSet() { final SettablePromise<String> promise = Promises.settable(); promise.done("done!"); assertEquals("done!", promise.getOrDefault("default")); } @Test public void testGetOrDefaultWithSetError() { final SettablePromise<String> promise = Promises.settable(); promise.fail(new Exception()); assertEquals("default", promise.getOrDefault("default")); } @Test public void testSetTwice() { final SettablePromise<String> promise = Promises.settable(); promise.done("done!"); try { promise.done("no, really done!"); fail("Should have thrown an Exception"); } catch (PromiseResolvedException e) { // Expected case assertEquals("done!", promise.get()); } } @Test public void testSetErrorTwice() { final SettablePromise<String> promise = Promises.settable(); final Exception exception = new Exception(); promise.fail(exception); try { promise.fail(new Exception()); fail("Should have thrown an Exception"); } catch (PromiseResolvedException e) { // Expected case assertEquals(exception, promise.getError()); } } @Test public void testSetThenSetError() { final SettablePromise<String> promise = Promises.settable(); promise.done("done!"); try { promise.fail(new Exception()); fail("Should have thrown an Exception"); } catch (PromiseResolvedException e) { // Expected case assertEquals("done!", promise.get()); } } @Test public void testSetErrorAndGet() { final SettablePromise<String> promise = Promises.settable(); final Exception error = new Exception(); promise.fail(error); try { promise.get(); fail("Should have thrown an exception"); } catch (PromiseException e) { assertEquals(error, e.getCause()); } } @Test public void testSetErrorAndGetError() { final SettablePromise<String> promise = Promises.settable(); final Exception error = new Exception(); promise.fail(error); assertEquals(error, promise.getError()); assertTrue(promise.isDone()); assertTrue(promise.isFailed()); } @Test public void testSetAndGetError() { final SettablePromise<String> promise = Promises.settable(); promise.done("done!"); assertNull(promise.getError()); } @Test public void testGetErrorWithoutSet() { try { Promises.settable().getError(); fail("Should have thrown PromiseUnresolvedException"); } catch (PromiseUnresolvedException e) { // Expected case } } @Test public void testTimedAwaitWithoutSet() throws InterruptedException { assertFalse(Promises.settable().await(50, TimeUnit.MILLISECONDS)); } @Test public void testAwaitWithSet() throws InterruptedException { final SettablePromise<String> promise = Promises.settable(); _scheduler.schedule(new Runnable() { @Override public void run() { promise.done("done!"); } }, 10, TimeUnit.MILLISECONDS); assertTrue(promise.await(5, TimeUnit.SECONDS)); assertEquals("done!", promise.get()); } @Test public void testAwaitWithSetError() throws InterruptedException { final SettablePromise<String> promise = Promises.settable(); final Exception error = new Exception(); _scheduler.schedule(new Runnable() { @Override public void run() { promise.fail(error); } }, 10, TimeUnit.MILLISECONDS); assertTrue(promise.await(5, TimeUnit.SECONDS)); assertEquals(error, promise.getError()); } @Test public void testListenerWithSet() throws InterruptedException { final SettablePromise<String> promise = Promises.settable(); final AtomicReference<String> resultRef = new AtomicReference<String>(); final CountDownLatch latch = new CountDownLatch(1); promise.addListener(new PromiseListener<String>() { @Override public void onResolved(Promise<String> resolvedPromise) { resultRef.set(resolvedPromise.get()); latch.countDown(); } }); _scheduler.schedule(new Runnable() { @Override public void run() { promise.done("done!"); } }, 10, TimeUnit.MILLISECONDS); latch.await(); assertEquals("done!", resultRef.get()); } @Test public void testListenerWithSetError() throws InterruptedException { final SettablePromise<String> promise = Promises.settable(); final Exception error = new Exception(); final AtomicReference<Throwable> resultRef = new AtomicReference<Throwable>(); final CountDownLatch latch = new CountDownLatch(1); promise.addListener(new PromiseListener<String>() { @Override public void onResolved(Promise<String> resolvedPromise) { resultRef.set(resolvedPromise.getError()); latch.countDown(); } }); _scheduler.schedule(new Runnable() { @Override public void run() { promise.fail(error); } }, 10, TimeUnit.MILLISECONDS); latch.await(); assertEquals(error, resultRef.get()); } @Test public void testListenerAfterSet() throws InterruptedException { final SettablePromise<String> promise = Promises.settable(); promise.done("done!"); final AtomicReference<String> resultRef = new AtomicReference<String>(); final CountDownLatch latch = new CountDownLatch(1); promise.addListener(new PromiseListener<String>() { @Override public void onResolved(Promise<String> resolvedPromise) { resultRef.set(resolvedPromise.get()); latch.countDown(); } }); latch.await(); assertEquals("done!", resultRef.get()); } @Test public void testListenerThrowsException() throws InterruptedException { // This test ensures that we properly resolve the promise even when // one of its listeners throws an Error. final SettablePromise<String> promise = Promises.settable(); promise.addListener(new PromiseListener<String>() { @Override public void onResolved(Promise<String> promise) { throw new RuntimeException(); } }); withDisabledLogging(new Runnable() { @Override public void run() { promise.done("Done!"); } }); assertTrue(promise.await(5, TimeUnit.SECONDS)); assertEquals("Done!", promise.get()); } @Test public void testListenerThrowsError() throws InterruptedException { // This test ensures that we properly resolve the promise even when // one of its listeners throws an Error. final SettablePromise<String> promise = Promises.settable(); promise.addListener(new PromiseListener<String>() { @Override public void onResolved(Promise<String> promise) { throw new Error(); } }); withDisabledLogging(new Runnable() { @Override public void run() { promise.done("Done!"); } }); assertTrue(promise.await(5, TimeUnit.SECONDS)); assertEquals("Done!", promise.get()); } @Test public void testListenerThrowsErrorAfterPromiseDone() throws InterruptedException { // This test ensures that we catch and do not rethrow errors from listeners // even if they are added after the promise is done. final SettablePromise<String> promise = Promises.settable(); promise.done("Done!"); assertTrue(promise.await(5, TimeUnit.SECONDS)); withDisabledLogging(new Runnable() { @Override public void run() { // This should not throw promise.addListener(new PromiseListener<String>() { @Override public void onResolved(Promise<String> promise) { throw new Error(); } }); } }); } }