package org.limewire.concurrent; import java.awt.EventQueue; import java.util.concurrent.CountDownLatch; import java.util.concurrent.ExecutionException; import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicReference; import javax.swing.SwingUtilities; import junit.framework.Test; import org.limewire.listener.EventListener; import org.limewire.listener.SwingEDTEvent; import org.limewire.service.ErrorCallback; import org.limewire.service.ErrorCallbackStub; import org.limewire.service.ErrorService; import org.limewire.util.BaseTestCase; public class ListeningFutureTaskTest extends BaseTestCase { protected ListeningExecutorService q; protected ErrorCallback oldService; protected ErrorCallbackStub serviceStub; public ListeningFutureTaskTest(String name) { super(name); } public static Test suite() { return buildTestSuite(ListeningFutureTaskTest.class); } @Override protected void setUp() throws Exception { q = ExecutorsHelper.newProcessingQueue("PQ"); oldService = ErrorService.getErrorCallback(); serviceStub = new ErrorCallbackStub(); ErrorService.setErrorCallback(serviceStub); } @Override protected void tearDown() throws Exception { ErrorService.setErrorCallback(oldService); if(serviceStub.getExceptionCount() != 0) { if(serviceStub.getExceptionCount() == 1) { fail("Test Had Exceptions", serviceStub.getException(0)); } else { fail("Test had multiple exceptions, first one was: " + serviceStub.getException(0)); } } } public void testListensBeforeCompletes() throws Exception { RunWaiter waiter = new RunWaiter(); q.execute(waiter); Runner runner = new Runner(); Object result = new Object(); ListeningFuture<Object> task = q.submit(runner, result); Listener listener = new Listener(); task.addFutureListener(listener); waiter.latch.countDown(); assertTrue(listener.latch.await(1, TimeUnit.SECONDS)); assertNotNull(listener.event); assertSame(result, listener.event.getResult()); assertEquals(FutureEvent.Type.SUCCESS, listener.event.getType()); assertNull(listener.event.getException()); assertEquals(runner.thread, listener.thread); } public void testListensBeforeCompletesWithException() throws Exception { RunWaiter waiter = new RunWaiter(); q.execute(waiter); Runner runner = new Runner(true); Object result = new Object(); ListeningFuture<Object> task = q.submit(runner, result); Listener listener = new Listener(); task.addFutureListener(listener); waiter.latch.countDown(); assertTrue(listener.latch.await(1, TimeUnit.SECONDS)); assertNotNull(listener.event); assertNull(listener.event.getResult()); assertEquals(FutureEvent.Type.EXCEPTION, listener.event.getType()); ExecutionException ee = listener.event.getException(); assertNotNull(ee); assertInstanceof(RuntimeException.class, ee.getCause()); assertEquals("Boo!", ee.getCause().getMessage()); assertEquals(runner.thread, listener.thread); assertEquals(1, serviceStub.getExceptionCount()); assertEquals(ee.getCause(), serviceStub.getException(0)); serviceStub.clear(); } public void testListensBeforeCompletesCancelsWithoutRun() throws Exception { RunWaiter waiter = new RunWaiter(); q.execute(waiter); Runner runner = new Runner(); Object result = new Object(); ListeningFuture<Object> task = q.submit(runner, result); Listener listener = new Listener(); task.addFutureListener(listener); task.cancel(false); assertTrue(listener.latch.await(1, TimeUnit.SECONDS)); assertNotNull(listener.event); assertNull(listener.event.getResult()); assertEquals(FutureEvent.Type.CANCELLED, listener.event.getType()); assertNull(listener.event.getException()); waiter.latch.countDown(); assertEquals(Thread.currentThread(), listener.thread); // from cancel thread. } public void testListensBeforeCompletesCancelsDuringRun() throws Exception { RunWaiter runner = new RunWaiter(); Object result = new Object(); ListeningFuture<Object> task = q.submit(runner, result); Listener listener = new Listener(); task.addFutureListener(listener); runner.enter.await(1, TimeUnit.SECONDS); task.cancel(true); assertTrue(listener.latch.await(1, TimeUnit.SECONDS)); assertNotNull(listener.event); assertNull(listener.event.getResult()); assertEquals(FutureEvent.Type.CANCELLED, listener.event.getType()); assertNull(listener.event.getException()); runner.latch.countDown(); Thread.sleep(100); assertTrue(runner.interrupted); assertEquals(Thread.currentThread(), listener.thread); // from cancel thread. assertEquals(1, serviceStub.getExceptionCount()); assertEquals(RuntimeInterruptedException.class, serviceStub.getException(0).getClass()); serviceStub.clear(); } public void testListensBeforeCompletesCancelsDuringRunWithoutAllowedCausesCancelToo() throws Exception { RunWaiter runner = new RunWaiter(); Object result = new Object(); ListeningFuture<Object> task = q.submit(runner, result); Listener listener = new Listener(); task.addFutureListener(listener); runner.enter.await(1, TimeUnit.SECONDS); task.cancel(false); assertTrue(listener.latch.await(1, TimeUnit.SECONDS)); assertNotNull(listener.event); assertNull(listener.event.getResult()); assertEquals(FutureEvent.Type.CANCELLED, listener.event.getType()); assertNull(listener.event.getException()); runner.latch.countDown(); Thread.sleep(100); assertFalse(runner.interrupted); assertEquals(Thread.currentThread(), listener.thread); // from cancel thread. } public void testListensAfterCompletes() throws Exception { Runner runner = new Runner(); Object result = new Object(); ListeningFuture<Object> task = q.submit(runner, result); waitForFuture(task); Listener listener = new Listener(); task.addFutureListener(listener); assertNotNull(listener.event); assertSame(result, listener.event.getResult()); assertEquals(FutureEvent.Type.SUCCESS, listener.event.getType()); assertNull(listener.event.getException()); assertEquals(Thread.currentThread(), listener.thread); } public void testListensAfterCompletesWithException() throws Exception { Runner runner = new Runner(true); Object result = new Object(); ListeningFuture<Object> task = q.submit(runner, result); waitForFuture(task); Listener listener = new Listener(); task.addFutureListener(listener); assertNotNull(listener.event); assertNull(listener.event.getResult()); assertEquals(FutureEvent.Type.EXCEPTION, listener.event.getType()); ExecutionException ee = listener.event.getException(); assertNotNull(ee); assertInstanceof(RuntimeException.class, ee.getCause()); assertEquals("Boo!", ee.getCause().getMessage()); assertEquals(Thread.currentThread(), listener.thread); assertEquals(1, serviceStub.getExceptionCount()); assertEquals(ee.getCause(), serviceStub.getException(0)); serviceStub.clear(); } public void testListenAfterCompletesCancelsWithoutRun() throws Exception { RunWaiter waiter = new RunWaiter(); q.execute(waiter); Runner runner = new Runner(); Object result = new Object(); ListeningFuture<Object> task = q.submit(runner, result); task.cancel(false); waitForFuture(task); Listener listener = new Listener(); task.addFutureListener(listener); assertNotNull(listener.event); assertNull(listener.event.getResult()); assertEquals(FutureEvent.Type.CANCELLED, listener.event.getType()); assertNull(listener.event.getException()); waiter.latch.countDown(); assertEquals(Thread.currentThread(), listener.thread); } public void testListensAfterCompletesCancelsDuringRun() throws Exception { RunWaiter runner = new RunWaiter(); Object result = new Object(); ListeningFuture<Object> task = q.submit(runner, result); runner.enter.await(1, TimeUnit.SECONDS); task.cancel(true); waitForFuture(task); Listener listener = new Listener(); task.addFutureListener(listener); assertNotNull(listener.event); assertNull(listener.event.getResult()); assertEquals(FutureEvent.Type.CANCELLED, listener.event.getType()); assertNull(listener.event.getException()); Thread.sleep(100); assertTrue(runner.interrupted); assertEquals(Thread.currentThread(), listener.thread); assertEquals(1, serviceStub.getExceptionCount()); assertEquals(RuntimeInterruptedException.class, serviceStub.getException(0).getClass()); serviceStub.clear(); } public void testListensAfterCompletesCancelsDuringRunWithoutAllowedCausesCancelToo() throws Exception { RunWaiter runner = new RunWaiter(); Object result = new Object(); ListeningFuture<Object> task = q.submit(runner, result); runner.enter.await(1, TimeUnit.SECONDS); task.cancel(false); waitForFuture(task); Listener listener = new Listener(); task.addFutureListener(listener); assertNotNull(listener.event); assertNull(listener.event.getResult()); assertEquals(FutureEvent.Type.CANCELLED, listener.event.getType()); assertNull(listener.event.getException()); Thread.sleep(100); assertFalse(runner.interrupted); assertEquals(Thread.currentThread(), listener.thread); } private void spinUpSwing() throws Exception { SwingUtilities.invokeAndWait(new Runnable() { public void run() {} }); } public void testAnnotatedListensBeforeCompletes() throws Exception { spinUpSwing(); RunWaiter waiter = new RunWaiter(); q.execute(waiter); Runner runner = new Runner(); Object result = new Object(); ListeningFuture<Object> task = q.submit(runner, result); AnnotatedListener listener = new AnnotatedListener(); task.addFutureListener(listener); waiter.latch.countDown(); assertTrue(listener.latch.await(1, TimeUnit.SECONDS)); assertNotNull(listener.event); assertSame(result, listener.event.getResult()); assertEquals(FutureEvent.Type.SUCCESS, listener.event.getType()); assertNull(listener.event.getException()); assertEquals(dispatchThread(), listener.thread); } public void testAnnotatedListensBeforeCompletesWithException() throws Exception { spinUpSwing(); RunWaiter waiter = new RunWaiter(); q.execute(waiter); Runner runner = new Runner(true); Object result = new Object(); ListeningFuture<Object> task = q.submit(runner, result); AnnotatedListener listener = new AnnotatedListener(); task.addFutureListener(listener); waiter.latch.countDown(); assertTrue(listener.latch.await(1, TimeUnit.SECONDS)); assertNotNull(listener.event); assertNull(listener.event.getResult()); assertEquals(FutureEvent.Type.EXCEPTION, listener.event.getType()); ExecutionException ee = listener.event.getException(); assertNotNull(ee); assertInstanceof(RuntimeException.class, ee.getCause()); assertEquals("Boo!", ee.getCause().getMessage()); assertEquals(dispatchThread(), listener.thread); assertEquals(1, serviceStub.getExceptionCount()); assertEquals(ee.getCause(), serviceStub.getException(0)); serviceStub.clear(); } public void testAnnotatedListensBeforeCompletesCancelsWithoutRun() throws Exception { spinUpSwing(); RunWaiter waiter = new RunWaiter(); q.execute(waiter); Runner runner = new Runner(); Object result = new Object(); ListeningFuture<Object> task = q.submit(runner, result); AnnotatedListener listener = new AnnotatedListener(); task.addFutureListener(listener); task.cancel(false); assertTrue(listener.latch.await(1, TimeUnit.SECONDS)); assertNotNull(listener.event); assertNull(listener.event.getResult()); assertEquals(FutureEvent.Type.CANCELLED, listener.event.getType()); assertNull(listener.event.getException()); waiter.latch.countDown(); assertEquals(dispatchThread(), listener.thread); // from cancel thread. } public void testAnnotatedListensBeforeCompletesCancelsDuringRun() throws Exception { spinUpSwing(); RunWaiter runner = new RunWaiter(); Object result = new Object(); ListeningFuture<Object> task = q.submit(runner, result); AnnotatedListener listener = new AnnotatedListener(); task.addFutureListener(listener); runner.enter.await(1, TimeUnit.SECONDS); task.cancel(true); assertTrue(listener.latch.await(1, TimeUnit.SECONDS)); assertNotNull(listener.event); assertNull(listener.event.getResult()); assertEquals(FutureEvent.Type.CANCELLED, listener.event.getType()); assertNull(listener.event.getException()); runner.latch.countDown(); Thread.sleep(100); assertTrue(runner.interrupted); assertEquals(dispatchThread(), listener.thread); // from cancel thread. assertEquals(1, serviceStub.getExceptionCount()); assertEquals(RuntimeInterruptedException.class, serviceStub.getException(0).getClass()); serviceStub.clear(); } public void testAnnotatedListensBeforeCompletesCancelsDuringRunWithoutAllowedCausesCancelToo() throws Exception { spinUpSwing(); RunWaiter runner = new RunWaiter(); Object result = new Object(); ListeningFuture<Object> task = q.submit(runner, result); AnnotatedListener listener = new AnnotatedListener(); task.addFutureListener(listener); runner.enter.await(1, TimeUnit.SECONDS); task.cancel(false); assertTrue(listener.latch.await(1, TimeUnit.SECONDS)); assertNotNull(listener.event); assertNull(listener.event.getResult()); assertEquals(FutureEvent.Type.CANCELLED, listener.event.getType()); assertNull(listener.event.getException()); runner.latch.countDown(); Thread.sleep(100); assertFalse(runner.interrupted); assertEquals(dispatchThread(), listener.thread); // from cancel thread. } public void testAnnotatedListensAfterCompletes() throws Exception { spinUpSwing(); Runner runner = new Runner(); Object result = new Object(); ListeningFuture<Object> task = q.submit(runner, result); waitForFuture(task); AnnotatedListener listener = new AnnotatedListener(); task.addFutureListener(listener); assertTrue(listener.latch.await(1, TimeUnit.SECONDS)); assertNotNull(listener.event); assertSame(result, listener.event.getResult()); assertEquals(FutureEvent.Type.SUCCESS, listener.event.getType()); assertNull(listener.event.getException()); assertEquals(dispatchThread(), listener.thread); } public void testAnnotatedListensAfterCompletesWithException() throws Exception { spinUpSwing(); Runner runner = new Runner(true); Object result = new Object(); ListeningFuture<Object> task = q.submit(runner, result); waitForFuture(task); AnnotatedListener listener = new AnnotatedListener(); task.addFutureListener(listener); assertTrue(listener.latch.await(1, TimeUnit.SECONDS)); assertNotNull(listener.event); assertNull(listener.event.getResult()); assertEquals(FutureEvent.Type.EXCEPTION, listener.event.getType()); ExecutionException ee = listener.event.getException(); assertNotNull(ee); assertInstanceof(RuntimeException.class, ee.getCause()); assertEquals("Boo!", ee.getCause().getMessage()); assertEquals(dispatchThread(), listener.thread); assertEquals(1, serviceStub.getExceptionCount()); assertEquals(ee.getCause(), serviceStub.getException(0)); serviceStub.clear(); } public void testAnnotatedListenAfterCompletesCancelsWithoutRun() throws Exception { spinUpSwing(); RunWaiter waiter = new RunWaiter(); q.execute(waiter); Runner runner = new Runner(); Object result = new Object(); ListeningFuture<Object> task = q.submit(runner, result); task.cancel(false); waitForFuture(task); AnnotatedListener listener = new AnnotatedListener(); task.addFutureListener(listener); assertTrue(listener.latch.await(1, TimeUnit.SECONDS)); assertNotNull(listener.event); assertNull(listener.event.getResult()); assertEquals(FutureEvent.Type.CANCELLED, listener.event.getType()); assertNull(listener.event.getException()); waiter.latch.countDown(); assertEquals(dispatchThread(), listener.thread); } public void testAnnotatedListensAfterCompletesCancelsDuringRun() throws Exception { spinUpSwing(); RunWaiter runner = new RunWaiter(); Object result = new Object(); ListeningFuture<Object> task = q.submit(runner, result); runner.enter.await(1, TimeUnit.SECONDS); task.cancel(true); waitForFuture(task); AnnotatedListener listener = new AnnotatedListener(); task.addFutureListener(listener); assertTrue(listener.latch.await(1, TimeUnit.SECONDS)); assertNotNull(listener.event); assertNull(listener.event.getResult()); assertEquals(FutureEvent.Type.CANCELLED, listener.event.getType()); assertNull(listener.event.getException()); Thread.sleep(100); assertTrue(runner.interrupted); assertEquals(dispatchThread(), listener.thread); assertEquals(1, serviceStub.getExceptionCount()); assertEquals(RuntimeInterruptedException.class, serviceStub.getException(0).getClass()); serviceStub.clear(); } public void testAnnotatedListensAfterCompletesCancelsDuringRunWithoutAllowedCausesCancelToo() throws Exception { spinUpSwing(); RunWaiter runner = new RunWaiter(); Object result = new Object(); ListeningFuture<Object> task = q.submit(runner, result); runner.enter.await(1, TimeUnit.SECONDS); task.cancel(false); waitForFuture(task); AnnotatedListener listener = new AnnotatedListener(); task.addFutureListener(listener); assertTrue(listener.latch.await(1, TimeUnit.SECONDS)); assertNotNull(listener.event); assertNull(listener.event.getResult()); assertEquals(FutureEvent.Type.CANCELLED, listener.event.getType()); assertNull(listener.event.getException()); Thread.sleep(100); assertFalse(runner.interrupted); assertEquals(dispatchThread(), listener.thread); } protected Thread dispatchThread() throws Exception { final AtomicReference<Thread> threadRef = new AtomicReference<Thread>(); EventQueue.invokeAndWait(new Runnable() { @Override public void run() { threadRef.set(Thread.currentThread()); } }); assertNotNull(threadRef.get()); return threadRef.get(); } protected void waitForFuture(Future<?> future) { try { future.get(1, TimeUnit.SECONDS); } catch(Throwable ignored) {} } protected static class AnnotatedListener implements EventListener<FutureEvent<Object>> { protected final CountDownLatch latch = new CountDownLatch(1); protected volatile FutureEvent<Object> event; protected volatile Thread thread; @Override @SwingEDTEvent public void handleEvent(FutureEvent<Object> event) { assert this.event == null; this.thread = Thread.currentThread(); this.event = event; latch.countDown(); } } protected static class Listener implements EventListener<FutureEvent<Object>> { protected final CountDownLatch latch = new CountDownLatch(1); protected volatile FutureEvent<Object> event; protected volatile Thread thread; @Override public void handleEvent(FutureEvent<Object> event) { assert this.event == null; this.event = event; this.thread = Thread.currentThread(); latch.countDown(); } } protected static class RuntimeInterruptedException extends RuntimeException { public RuntimeInterruptedException(Throwable cause) { super(cause); } } protected static class RunWaiter implements Runnable { protected CountDownLatch enter = new CountDownLatch(1); protected CountDownLatch latch = new CountDownLatch(1); protected volatile boolean interrupted; @Override public void run() { enter.countDown(); try { latch.await(); } catch(InterruptedException ie) { interrupted = true; throw new RuntimeInterruptedException(ie); } } } protected static class Runner implements Runnable { private final boolean throwException; protected volatile Thread thread; public Runner() { this(false); } public Runner(boolean throwException) { this.throwException = throwException; } @Override public void run() { thread = Thread.currentThread(); if(throwException) { throw new RuntimeException("Boo!"); } } } }