package water; import org.junit.Assert; import org.junit.BeforeClass; import org.junit.Test; import java.util.concurrent.*; /** * Created by tomas on 8/16/16. */ public class FuturesTest extends TestUtil { @BeforeClass public static void setup() { stall_till_cloudsize(1); } public static class TstFuture implements Future { private boolean _isDone; private boolean _isCancelled; public ExecutionException _exex; public RuntimeException _rex; @Override public synchronized boolean cancel(boolean mayInterruptIfRunning) { _isCancelled = true; notifyAll(); return true; } public synchronized void complete(){ _isDone = true; notifyAll(); } public synchronized void complete(Throwable t){ if(t instanceof ExecutionException) _exex = (ExecutionException) t; else if(t instanceof RuntimeException) _rex = (RuntimeException) t; else throw new IllegalArgumentException(); _isDone = true; notifyAll(); } @Override public boolean isCancelled() {return _isCancelled;} @Override public boolean isDone() {return _isDone || _isCancelled;} @Override public Object get() throws InterruptedException, ExecutionException { while(!isDone()){ synchronized(this){ wait(); } } if(_isCancelled) throw new CancellationException(); if(_exex != null) throw _exex; if(_rex != null) throw _rex; return this; } @Override public Object get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException { return null; } } private static class TstException extends RuntimeException { public TstException(String msg){super(msg);} } @Test // Test exceptions are correctly rethrown even if the future is eagerly removed public void testExceptions(){ // 0 test exception from pending task is thrown Futures fs = new Futures(); TstFuture cf = new TstFuture(); fs.add(cf); Assert.assertEquals(1,fs._pending_cnt); // task is already completed cf.complete(new ExecutionException(new TstException("a"))); try{ fs.blockForPending(); Assert.assertTrue("should've thrown",false); } catch(RuntimeException t) { Assert.assertTrue(t.getCause() instanceof TstException); } // 1 test exception from already completed task is rethrown in blockForPending fs = new Futures(); cf = new TstFuture(); cf.complete(new ExecutionException(new TstException("a"))); fs.add(cf); Assert.assertEquals(0,fs._pending_cnt); // task is already completed try{ fs.blockForPending(); Assert.assertTrue("should've thrown",false); } catch(RuntimeException t) { Assert.assertTrue(t.getCause() instanceof TstException); } // 2 test exception is recorded and re-thrown if task is eagerly cleaned fs = new Futures(); cf = new TstFuture(); fs.add(cf); Assert.assertEquals(1,fs._pending_cnt); cf.complete(new TstException("eager cleanup")); for(int i =0; i < 3; ++i) { fs.add(cf = new TstFuture()); Assert.assertEquals(1, fs._pending_cnt); cf.complete(); } try{ fs.blockForPending(); Assert.assertTrue("should've thrown",false); } catch(RuntimeException t) { Assert.assertTrue(t.getCause() instanceof TstException); Assert.assertEquals("eager cleanup",t.getCause().getMessage()); } // 3 test cancellation exceptions are ignored fs = new Futures(); cf = new TstFuture(); cf.cancel(true); fs.add(cf); fs.blockForPending(); } }