BoltsTest/src/bolts/TaskTest.java (431 lines of code) (raw):
/*
 *  Copyright (c) 2014, Facebook, Inc.
 *  All rights reserved.
 *
 *  This source code is licensed under the BSD-style license found in the
 *  LICENSE file in the root directory of this source tree. An additional grant 
 *  of patent rights can be found in the PATENTS file in the same directory.
 *
 */
package bolts;
import java.util.ArrayList;
import java.util.concurrent.Callable;
import java.util.concurrent.CancellationException;
import java.util.concurrent.Executors;
import java.util.concurrent.atomic.AtomicInteger;
import bolts.AggregateException;
import bolts.Continuation;
import bolts.Task;
import junit.framework.TestCase;
public class TaskTest extends TestCase {
  private void runTaskTest(Callable<Task<?>> callable) {
    try {
      Task<?> task = callable.call();
      task.waitForCompletion();
      if (task.isFaulted()) {
        Exception error = task.getError();
        if (error instanceof RuntimeException) {
          throw (RuntimeException) error;
        }
        throw new RuntimeException(error);
      } else if (task.isCancelled()) {
        throw new RuntimeException(new CancellationException());
      }
    } catch (Exception e) {
      throw new RuntimeException(e);
    }
  }
  public void testPrimitives() {
    Task<Integer> complete = Task.forResult(5);
    Task<Integer> error = Task.forError(new RuntimeException());
    Task<Integer> cancelled = Task.cancelled();
    assertTrue(complete.isCompleted());
    assertEquals(5, complete.getResult().intValue());
    assertFalse(complete.isFaulted());
    assertFalse(complete.isCancelled());
    assertTrue(error.isCompleted());
    assertTrue(error.getError() instanceof RuntimeException);
    assertTrue(error.isFaulted());
    assertFalse(error.isCancelled());
    assertTrue(cancelled.isCompleted());
    assertFalse(cancelled.isFaulted());
    assertTrue(cancelled.isCancelled());
  }
  public void testSynchronousContinuation() {
    final Task<Integer> complete = Task.forResult(5);
    final Task<Integer> error = Task.forError(new RuntimeException());
    final Task<Integer> cancelled = Task.cancelled();
    complete.continueWith(new Continuation<Integer, Void>() {
      public Void then(Task<Integer> task) {
        assertEquals(complete, task);
        assertTrue(task.isCompleted());
        assertEquals(5, task.getResult().intValue());
        assertFalse(task.isFaulted());
        assertFalse(task.isCancelled());
        return null;
      }
    });
    error.continueWith(new Continuation<Integer, Void>() {
      public Void then(Task<Integer> task) {
        assertEquals(error, task);
        assertTrue(task.isCompleted());
        assertTrue(task.getError() instanceof RuntimeException);
        assertTrue(task.isFaulted());
        assertFalse(task.isCancelled());
        return null;
      }
    });
    cancelled.continueWith(new Continuation<Integer, Void>() {
      public Void then(Task<Integer> task) {
        assertEquals(cancelled, task);
        assertTrue(cancelled.isCompleted());
        assertFalse(cancelled.isFaulted());
        assertTrue(cancelled.isCancelled());
        return null;
      }
    });
  }
  public void testSynchronousChaining() {
    Task<Integer> first = Task.forResult(1);
    Task<Integer> second = first.continueWith(new Continuation<Integer, Integer>() {
      public Integer then(Task<Integer> task) {
        return 2;
      }
    });
    Task<Integer> third = second.continueWithTask(new Continuation<Integer, Task<Integer>>() {
      public Task<Integer> then(Task<Integer> task) {
        return Task.forResult(3);
      }
    });
    assertTrue(first.isCompleted());
    assertTrue(second.isCompleted());
    assertTrue(third.isCompleted());
    assertEquals(1, first.getResult().intValue());
    assertEquals(2, second.getResult().intValue());
    assertEquals(3, third.getResult().intValue());
  }
  public void testBackgroundCall() {
    runTaskTest(new Callable<Task<?>>() {
      public Task<?> call() throws Exception {
        return Task.callInBackground(new Callable<Integer>() {
          public Integer call() throws Exception {
            Thread.sleep(100);
            return 5;
          }
        }).continueWith(new Continuation<Integer, Void>() {
          public Void then(Task<Integer> task) {
            assertEquals(5, task.getResult().intValue());
            return null;
          }
        });
      }
    });
  }
  public void testBackgroundCallWaiting() throws Exception {
    Task<Integer> task = Task.callInBackground(new Callable<Integer>() {
      public Integer call() throws Exception {
        Thread.sleep(100);
        return 5;
      }
    });
    task.waitForCompletion();
    assertTrue(task.isCompleted());
    assertEquals(5, task.getResult().intValue());
  }
  public void testBackgroundCallWaitingOnError() throws Exception {
    Task<Integer> task = Task.callInBackground(new Callable<Integer>() {
      public Integer call() throws Exception {
        Thread.sleep(100);
        throw new RuntimeException();
      }
    });
    task.waitForCompletion();
    assertTrue(task.isCompleted());
    assertTrue(task.isFaulted());
  }
  public void testBackgroundCallWaitOnCancellation() throws Exception {
    Task<Integer> task = Task.callInBackground(new Callable<Integer>() {
      public Integer call() throws Exception {
        Thread.sleep(100);
        return 5;
      }
    }).continueWithTask(new Continuation<Integer, Task<Integer>>() {
      public Task<Integer> then(Task<Integer> task) {
        return Task.cancelled();
      }
    });
    task.waitForCompletion();
    assertTrue(task.isCompleted());
    assertTrue(task.isCancelled());
  }
  public void testBackgroundError() {
    runTaskTest(new Callable<Task<?>>() {
      public Task<?> call() throws Exception {
        return Task.callInBackground(new Callable<Integer>() {
          public Integer call() throws Exception {
            throw new IllegalStateException();
          }
        }).continueWith(new Continuation<Integer, Void>() {
          public Void then(Task<Integer> task) {
            assertTrue(task.isFaulted());
            assertTrue(task.getError() instanceof IllegalStateException);
            return null;
          }
        });
      }
    });
  }
  public void testWhenAllSuccess() {
    runTaskTest(new Callable<Task<?>>() {
      @Override
      public Task<?> call() throws Exception {
        final ArrayList<Task<Void>> tasks = new ArrayList<Task<Void>>();
        for (int i = 0; i < 20; i++) {
          Task<Void> task = Task.callInBackground(new Callable<Void>() {
            @Override
            public Void call() throws Exception {
              Thread.sleep((long) (Math.random() * 1000));
              return null;
            }
          });
          tasks.add(task);
        }
        return Task.whenAll(tasks).continueWith(new Continuation<Void, Void>() {
          @Override
          public Void then(Task<Void> task) {
            assertTrue(task.isCompleted());
            assertFalse(task.isFaulted());
            assertFalse(task.isCancelled());
            for (Task<Void> t : tasks) {
              assertTrue(t.isCompleted());
            }
            return null;
          }
        });
      }
    });
  }
  public void testWhenAllOneError() {
    final Exception error = new RuntimeException("This task failed.");
    
    runTaskTest(new Callable<Task<?>>() {
      @Override
      public Task<?> call() throws Exception {
        final ArrayList<Task<Void>> tasks = new ArrayList<Task<Void>>();
        for (int i = 0; i < 20; i++) {
          final int number = i;
          Task<Void> task = Task.callInBackground(new Callable<Void>() {
            @Override
            public Void call() throws Exception {
              Thread.sleep((long) (Math.random() * 1000));
              if (number == 10) {
                throw error;
              }
              return null;
            }
          });
          tasks.add(task);
        }
        return Task.whenAll(tasks).continueWith(new Continuation<Void, Void>() {
          @Override
          public Void then(Task<Void> task) {
            assertTrue(task.isCompleted());
            assertTrue(task.isFaulted());
            assertFalse(task.isCancelled());
            assertFalse(task.getError() instanceof AggregateException);
            assertEquals(error, task.getError());
            
            for (Task<Void> t : tasks) {
              assertTrue(t.isCompleted());
            }
            return null;
          }
        });
      }
    });
  }
  public void testWhenAllTwoErrors() {
    final Exception error = new RuntimeException("This task failed.");
    
    runTaskTest(new Callable<Task<?>>() {
      @Override
      public Task<?> call() throws Exception {
        final ArrayList<Task<Void>> tasks = new ArrayList<Task<Void>>();
        for (int i = 0; i < 20; i++) {
          final int number = i;
          Task<Void> task = Task.callInBackground(new Callable<Void>() {
            @Override
            public Void call() throws Exception {
              Thread.sleep((long) (Math.random() * 1000));
              if (number == 10 || number == 11) {
                throw error;
              }
              return null;
            }
          });
          tasks.add(task);
        }
        return Task.whenAll(tasks).continueWith(new Continuation<Void, Void>() {
          @Override
          public Void then(Task<Void> task) {
            assertTrue(task.isCompleted());
            assertTrue(task.isFaulted());
            assertFalse(task.isCancelled());
            assertTrue(task.getError() instanceof AggregateException);
            assertEquals(2, ((AggregateException)task.getError()).getErrors().size());
            assertEquals(error, ((AggregateException)task.getError()).getErrors().get(0));
            assertEquals(error, ((AggregateException)task.getError()).getErrors().get(1));
            for (Task<Void> t : tasks) {
              assertTrue(t.isCompleted());
            }
            return null;
          }
        });
      }
    });
  }
  public void testWhenAllCancel() {
    runTaskTest(new Callable<Task<?>>() {
      @Override
      public Task<?> call() throws Exception {
        final ArrayList<Task<Void>> tasks = new ArrayList<Task<Void>>();
        for (int i = 0; i < 20; i++) {
          final Task<Void>.TaskCompletionSource tcs = Task.create();
          final int number = i;
          Task.callInBackground(new Callable<Void>() {
            @Override
            public Void call() throws Exception {
              Thread.sleep((long) (Math.random() * 1000));
              if (number == 10) {
                tcs.setCancelled();
              } else {
                tcs.setResult(null);
              }
              return null;
            }
          });
          tasks.add(tcs.getTask());
        }
        return Task.whenAll(tasks).continueWith(new Continuation<Void, Void>() {
          @Override
          public Void then(Task<Void> task) {
            assertTrue(task.isCompleted());
            assertFalse(task.isFaulted());
            assertTrue(task.isCancelled());
            for (Task<Void> t : tasks) {
              assertTrue(t.isCompleted());
            }
            return null;
          }
        });
      }
    });
  }
  public void testAsyncChaining() {
    runTaskTest(new Callable<Task<?>>() {
      public Task<?> call() throws Exception {
        final ArrayList<Integer> sequence = new ArrayList<Integer>();
        Task<Void> result = Task.forResult(null);
        for (int i = 0; i < 20; i++) {
          final int taskNumber = i;
          result = result.continueWithTask(new Continuation<Void, Task<Void>>() {
            public Task<Void> then(Task<Void> task) {
              return Task.callInBackground(new Callable<Void>() {
                public Void call() throws Exception {
                  sequence.add(taskNumber);
                  return null;
                }
              });
            }
          });
        }
        result = result.continueWith(new Continuation<Void, Void>() {
          public Void then(Task<Void> task) {
            assertEquals(20, sequence.size());
            for (int i = 0; i < 20; i++) {
              assertEquals(i, sequence.get(i).intValue());
            }
            return null;
          }
        });
        return result;
      }
    });
  }
  public void testOnSuccess() {
    Continuation<Integer, Integer> continuation = new Continuation<Integer, Integer>() {
      public Integer then(Task<Integer> task) {
        return task.getResult().intValue() + 1;
      }
    };
    Task<Integer> complete = Task.forResult(5).onSuccess(continuation);
    Task<Integer> error = Task.<Integer> forError(new IllegalStateException()).onSuccess(
        continuation);
    Task<Integer> cancelled = Task.<Integer> cancelled().onSuccess(continuation);
    assertTrue(complete.isCompleted());
    assertEquals(6, complete.getResult().intValue());
    assertFalse(complete.isFaulted());
    assertFalse(complete.isCancelled());
    assertTrue(error.isCompleted());
    assertTrue(error.getError() instanceof RuntimeException);
    assertTrue(error.isFaulted());
    assertFalse(error.isCancelled());
    assertTrue(cancelled.isCompleted());
    assertFalse(cancelled.isFaulted());
    assertTrue(cancelled.isCancelled());
  }
  public void testOnSuccessTask() {
    Continuation<Integer, Task<Integer>> continuation = new Continuation<Integer, Task<Integer>>() {
      public Task<Integer> then(Task<Integer> task) {
        return Task.forResult(task.getResult().intValue() + 1);
      }
    };
    Task<Integer> complete = Task.forResult(5).onSuccessTask(continuation);
    Task<Integer> error = Task.<Integer> forError(new IllegalStateException()).onSuccessTask(
        continuation);
    Task<Integer> cancelled = Task.<Integer> cancelled().onSuccessTask(continuation);
    assertTrue(complete.isCompleted());
    assertEquals(6, complete.getResult().intValue());
    assertFalse(complete.isFaulted());
    assertFalse(complete.isCancelled());
    assertTrue(error.isCompleted());
    assertTrue(error.getError() instanceof RuntimeException);
    assertTrue(error.isFaulted());
    assertFalse(error.isCancelled());
    assertTrue(cancelled.isCompleted());
    assertFalse(cancelled.isFaulted());
    assertTrue(cancelled.isCancelled());
  }
  public void testContinueWhile() {
    final AtomicInteger count = new AtomicInteger(0);
    runTaskTest(new Callable<Task<?>>() {
      public Task<?> call() throws Exception {
        return Task.forResult(null).continueWhile(new Callable<Boolean>() {
          public Boolean call() throws Exception {
            return count.get() < 10;
          }
        }, new Continuation<Void, Task<Void>>() {
          public Task<Void> then(Task<Void> task) throws Exception {
            count.incrementAndGet();
            return null;
          }
        }).continueWith(new Continuation<Void, Void>() {
          public Void then(Task<Void> task) throws Exception {
            assertEquals(10, count.get());
            return null;
          }
        });
      }
    });
  }
  public void testContinueWhileAsync() {
    final AtomicInteger count = new AtomicInteger(0);
    runTaskTest(new Callable<Task<?>>() {
      public Task<?> call() throws Exception {
        return Task.forResult(null).continueWhile(new Callable<Boolean>() {
          public Boolean call() throws Exception {
            return count.get() < 10;
          }
        }, new Continuation<Void, Task<Void>>() {
          public Task<Void> then(Task<Void> task) throws Exception {
            count.incrementAndGet();
            return null;
          }
        }, Executors.newCachedThreadPool()).continueWith(new Continuation<Void, Void>() {
          public Void then(Task<Void> task) throws Exception {
            assertEquals(10, count.get());
            return null;
          }
        });
      }
    });
  }
}