package com.koushikdutta.async.test;
import java.util.ArrayList;
import java.util.concurrent.CancellationException;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import junit.framework.TestCase;
import android.os.Handler;
import android.os.Looper;
import com.koushikdutta.async.AsyncServer;
import com.koushikdutta.async.callback.CompletedCallback;
import com.koushikdutta.async.callback.ContinuationCallback;
import com.koushikdutta.async.future.Continuation;
import com.koushikdutta.async.future.SimpleFuture;
public class FutureTests extends TestCase {
private static class IntegerFuture extends SimpleFuture<Integer> {
private IntegerFuture() {
}
public static IntegerFuture create(final int value, final long timeout) {
final IntegerFuture ret = new IntegerFuture();
new Thread() {
public void run() {
try {
Thread.sleep(timeout);
ret.setComplete(value);
}
catch (Exception e) {
ret.setComplete(e);
}
};
}.start();
return ret;
}
}
public void testFutureCancel() throws Exception {
// test a future being cancelled while waiting
final IntegerFuture future = IntegerFuture.create(20, 2000);
new Thread() {
public void run() {
try {
Thread.sleep(1000);
}
catch (InterruptedException e) {
}
future.cancel();
};
}
.start();
try {
future.get(3000, TimeUnit.MILLISECONDS);
// this should never reach here as it was cancelled
fail();
}
catch (TimeoutException e) {
// timeout should also fail, since it was cancelled
fail();
}
catch (ExecutionException e) {
// execution exception is correct, make sure inner exception is cancellation
assertTrue(e.getCause() instanceof CancellationException);
}
}
public void testIntegerFuture() throws Exception {
IntegerFuture i = IntegerFuture.create(10, 500L);
assertEquals((int)i.get(), 10);
}
int someValue;
public void testContinuation() throws Exception {
final Semaphore semaphore = new Semaphore(0);
someValue = 0;
final Continuation c = new Continuation(new CompletedCallback() {
@Override
public void onCompleted(Exception ex) {
assertNull(ex);
semaphore.release();
}
});
c.add(new ContinuationCallback() {
@Override
public void onContinue(Continuation continuation, final CompletedCallback next) throws Exception {
new Thread() {
public void run() {
someValue++;
next.onCompleted(null);
};
}.start();
}
});
c.add(new ContinuationCallback() {
@Override
public void onContinue(Continuation continuation, final CompletedCallback next) throws Exception {
new Thread() {
public void run() {
someValue++;
next.onCompleted(null);
};
}.start();
}
});
c.add(new ContinuationCallback() {
@Override
public void onContinue(Continuation continuation, final CompletedCallback next) throws Exception {
someValue++;
next.onCompleted(null);
}
});
new Thread() {
public void run() {
c.start();
};
}.start();
assertTrue(semaphore.tryAcquire(3000, TimeUnit.MILLISECONDS));
assertEquals(someValue, 3);
}
public void testFutureChain() throws Exception {
final Semaphore semaphore = new Semaphore(0);
final Continuation c = new Continuation(new CompletedCallback() {
@Override
public void onCompleted(Exception ex) {
semaphore.release();
}
});
IntegerFuture i1;
c.add(i1 = IntegerFuture.create(2, 200));
IntegerFuture i2;
c.add(i2 = IntegerFuture.create(3, 200));
new Thread() {
public void run() {
c.start();
};
}.start();
assertTrue(semaphore.tryAcquire(3000, TimeUnit.MILLISECONDS));
assertEquals((int)i1.get(), 2);
assertEquals((int)i2.get(), 3);
}
public void testContinuationFail() throws Exception {
final Semaphore semaphore = new Semaphore(0);
final Continuation c = new Continuation(new CompletedCallback() {
@Override
public void onCompleted(Exception ex) {
assertNotNull(ex);
semaphore.release();
}
});
c.add(new ContinuationCallback() {
@Override
public void onContinue(Continuation continuation, CompletedCallback next) throws Exception {
throw new Exception("fail");
}
});
new Thread() {
public void run() {
c.start();
};
}.start();
assertTrue(semaphore.tryAcquire(3000, TimeUnit.MILLISECONDS));
}
public void testContinuationCancel() throws Exception {
final Semaphore semaphore = new Semaphore(0);
final Continuation c = new Continuation(new CompletedCallback() {
@Override
public void onCompleted(Exception ex) {
fail();
semaphore.release();
}
});
c.setCancelCallback(new Runnable() {
@Override
public void run() {
semaphore.release();
}
});
c.add(new ContinuationCallback() {
@Override
public void onContinue(Continuation continuation, CompletedCallback next) throws Exception {
Thread.sleep(10000);
}
});
new Thread() {
public void run() {
c.start();
};
}.start();
new Thread() {
public void run() {
try {
Thread.sleep(1000);
}
catch (Exception e) {
}
c.cancel();
};
}.start();
assertTrue(semaphore.tryAcquire(3000, TimeUnit.MILLISECONDS));
}
public void testChildContinuationCancel() throws Exception {
final Semaphore semaphore = new Semaphore(0);
final Continuation c = new Continuation(new CompletedCallback() {
@Override
public void onCompleted(Exception ex) {
fail();
semaphore.release();
}
});
c.setCancelCallback(new Runnable() {
@Override
public void run() {
semaphore.release();
}
});
c.add(new ContinuationCallback() {
@Override
public void onContinue(Continuation continuation, CompletedCallback next) throws Exception {
Thread.sleep(10000);
}
});
final Continuation child = new Continuation();
child.add(new ContinuationCallback() {
@Override
public void onContinue(Continuation continuation, CompletedCallback next) throws Exception {
Thread.sleep(10000);
}
});
c.add(child);
new Thread() {
public void run() {
c.start();
};
}.start();
new Thread() {
public void run() {
try {
Thread.sleep(1000);
}
catch (Exception e) {
}
child.cancel();
};
}.start();
assertTrue(semaphore.tryAcquire(3000, TimeUnit.MILLISECONDS));
}
public void testContinuationArray() throws Exception {
final ArrayList<Integer> results = new ArrayList<Integer>();
final Semaphore semaphore = new Semaphore(0);
final Continuation c = new Continuation(new CompletedCallback() {
@Override
public void onCompleted(Exception ex) {
semaphore.release();
}
});
for (int i = 0; i < 10; i++) {
final int j = i;
c.add(new ContinuationCallback() {
@Override
public void onContinue(Continuation continuation, CompletedCallback next) throws Exception {
results.add(j);
next.onCompleted(null);
}
});
}
new Thread() {
public void run() {
c.start();
};
}.start();
assertTrue(semaphore.tryAcquire(3000, TimeUnit.MILLISECONDS));
assertEquals(10, results.size());
for (int i = 0; i < 10; i++) {
assertEquals((int)results.get(i), i);
}
}
class TriggerFuture extends SimpleFuture<Integer> {
public void trigger() {
setComplete(2020);
}
}
public void testReentrancy() throws Exception {
// verify reentrancy will work
assertNotNull(Looper.myLooper());
final Thread originalThread = Thread.currentThread();
final TriggerFuture trigger = new TriggerFuture();
final Handler handler = new Handler();
AsyncServer.getDefault().post(new Runnable() {
@Override
public void run() {
AsyncServer.post(handler, new Runnable() {
@Override
public void run() {
final TriggerFuture trigger2 = new TriggerFuture();
AsyncServer.getDefault().post(new Runnable() {
@Override
public void run() {
AsyncServer.post(handler, new Runnable() {
@Override
public void run() {
assertEquals(Thread.currentThread(), originalThread);
trigger2.trigger();
}
});
}
});
try {
assertEquals((int)trigger2.get(5000, TimeUnit.MILLISECONDS), 2020);
}
catch (Exception e) {
fail();
}
// callstack here should be on top of trigger.get below.
// reentrant.
assertEquals(Thread.currentThread(), originalThread);
trigger.trigger();
}
});
}
});
// trigger.get will do a reentrant block.
assertEquals((int)trigger.get(5000, TimeUnit.MILLISECONDS), 2020);
}
}