/*
 * Written by Doug Lea with assistance from members of JCP JSR-166
 * Expert Group and released to the public domain, as explained at
 * http://creativecommons.org/publicdomain/zero/1.0/
 */

package jsr166;

import static java.util.concurrent.TimeUnit.SECONDS;

import java.util.HashSet;
import java.util.concurrent.CancellationException;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.ForkJoinTask;
import java.util.concurrent.RecursiveTask;
import java.util.concurrent.TimeoutException;

import junit.framework.Test;
import junit.framework.TestSuite;

public class RecursiveTaskTest extends JSR166TestCase {

    // android-note: Removed because the CTS runner does a bad job of
    // retrying tests that have suite() declarations.
    //
    // public static void main(String[] args) {
    //     main(suite(), args);
    // }
    // public static Test suite() {
    //     return new TestSuite(RecursiveTaskTest.class);
    // }

    private static ForkJoinPool mainPool() {
        return new ForkJoinPool();
    }

    private static ForkJoinPool singletonPool() {
        return new ForkJoinPool(1);
    }

    private static ForkJoinPool asyncSingletonPool() {
        return new ForkJoinPool(1,
                                ForkJoinPool.defaultForkJoinWorkerThreadFactory,
                                null, true);
    }

    private <T> T testInvokeOnPool(ForkJoinPool pool, RecursiveTask<T> a) {
        try (PoolCleaner cleaner = cleaner(pool)) {
            checkNotDone(a);

            T result = pool.invoke(a);

            checkCompletedNormally(a, result);
            return result;
        }
    }

    void checkNotDone(RecursiveTask a) {
        assertFalse(a.isDone());
        assertFalse(a.isCompletedNormally());
        assertFalse(a.isCompletedAbnormally());
        assertFalse(a.isCancelled());
        assertNull(a.getException());
        assertNull(a.getRawResult());

        if (! ForkJoinTask.inForkJoinPool()) {
            Thread.currentThread().interrupt();
            try {
                a.get();
                shouldThrow();
            } catch (InterruptedException success) {
            } catch (Throwable fail) { threadUnexpectedException(fail); }

            Thread.currentThread().interrupt();
            try {
                a.get(5L, SECONDS);
                shouldThrow();
            } catch (InterruptedException success) {
            } catch (Throwable fail) { threadUnexpectedException(fail); }
        }

        try {
            a.get(0L, SECONDS);
            shouldThrow();
        } catch (TimeoutException success) {
        } catch (Throwable fail) { threadUnexpectedException(fail); }
    }

    <T> void checkCompletedNormally(RecursiveTask<T> a, T expected) {
        assertTrue(a.isDone());
        assertFalse(a.isCancelled());
        assertTrue(a.isCompletedNormally());
        assertFalse(a.isCompletedAbnormally());
        assertNull(a.getException());
        assertSame(expected, a.getRawResult());
        assertSame(expected, a.join());
        assertFalse(a.cancel(false));
        assertFalse(a.cancel(true));
        try {
            assertSame(expected, a.get());
        } catch (Throwable fail) { threadUnexpectedException(fail); }
        try {
            assertSame(expected, a.get(5L, SECONDS));
        } catch (Throwable fail) { threadUnexpectedException(fail); }
    }

    /**
     * Waits for the task to complete, and checks that when it does,
     * it will have an Integer result equals to the given int.
     */
    void checkCompletesNormally(RecursiveTask<Integer> a, int expected) {
        Integer r = a.join();
        assertEquals(expected, (int) r);
        checkCompletedNormally(a, r);
    }

    /**
     * Like checkCompletesNormally, but verifies that the task has
     * already completed.
     */
    void checkCompletedNormally(RecursiveTask<Integer> a, int expected) {
        Integer r = a.getRawResult();
        assertEquals(expected, (int) r);
        checkCompletedNormally(a, r);
    }

    void checkCancelled(RecursiveTask a) {
        assertTrue(a.isDone());
        assertTrue(a.isCancelled());
        assertFalse(a.isCompletedNormally());
        assertTrue(a.isCompletedAbnormally());
        assertTrue(a.getException() instanceof CancellationException);
        assertNull(a.getRawResult());

        try {
            a.join();
            shouldThrow();
        } catch (CancellationException success) {
        } catch (Throwable fail) { threadUnexpectedException(fail); }

        try {
            a.get();
            shouldThrow();
        } catch (CancellationException success) {
        } catch (Throwable fail) { threadUnexpectedException(fail); }

        try {
            a.get(5L, SECONDS);
            shouldThrow();
        } catch (CancellationException success) {
        } catch (Throwable fail) { threadUnexpectedException(fail); }
    }

    void checkCompletedAbnormally(RecursiveTask a, Throwable t) {
        assertTrue(a.isDone());
        assertFalse(a.isCancelled());
        assertFalse(a.isCompletedNormally());
        assertTrue(a.isCompletedAbnormally());
        assertSame(t.getClass(), a.getException().getClass());
        assertNull(a.getRawResult());
        assertFalse(a.cancel(false));
        assertFalse(a.cancel(true));

        try {
            a.join();
            shouldThrow();
        } catch (Throwable expected) {
            assertSame(t.getClass(), expected.getClass());
        }

        try {
            a.get();
            shouldThrow();
        } catch (ExecutionException success) {
            assertSame(t.getClass(), success.getCause().getClass());
        } catch (Throwable fail) { threadUnexpectedException(fail); }

        try {
            a.get(5L, SECONDS);
            shouldThrow();
        } catch (ExecutionException success) {
            assertSame(t.getClass(), success.getCause().getClass());
        } catch (Throwable fail) { threadUnexpectedException(fail); }
    }

    public static final class FJException extends RuntimeException {
        public FJException() { super(); }
    }

    // An invalid return value for Fib
    static final Integer NoResult = Integer.valueOf(-17);

    // A simple recursive task for testing
    final class FibTask extends CheckedRecursiveTask<Integer> {
        final int number;
        FibTask(int n) { number = n; }
        public Integer realCompute() {
            int n = number;
            if (n <= 1)
                return n;
            FibTask f1 = new FibTask(n - 1);
            f1.fork();
            return (new FibTask(n - 2)).compute() + f1.join();
        }

        public void publicSetRawResult(Integer result) {
            setRawResult(result);
        }
    }

    // A recursive action failing in base case
    final class FailingFibTask extends RecursiveTask<Integer> {
        final int number;
        int result;
        FailingFibTask(int n) { number = n; }
        public Integer compute() {
            int n = number;
            if (n <= 1)
                throw new FJException();
            FailingFibTask f1 = new FailingFibTask(n - 1);
            f1.fork();
            return (new FibTask(n - 2)).compute() + f1.join();
        }
    }

    /**
     * invoke returns value when task completes normally.
     * isCompletedAbnormally and isCancelled return false for normally
     * completed tasks. getRawResult of a completed non-null task
     * returns value;
     */
    public void testInvoke() {
        RecursiveTask<Integer> a = new CheckedRecursiveTask<Integer>() {
            public Integer realCompute() {
                FibTask f = new FibTask(8);
                Integer r = f.invoke();
                assertEquals(21, (int) r);
                checkCompletedNormally(f, r);
                return r;
            }};
        assertEquals(21, (int) testInvokeOnPool(mainPool(), a));
    }

    /**
     * quietlyInvoke task returns when task completes normally.
     * isCompletedAbnormally and isCancelled return false for normally
     * completed tasks
     */
    public void testQuietlyInvoke() {
        RecursiveTask<Integer> a = new CheckedRecursiveTask<Integer>() {
            public Integer realCompute() {
                FibTask f = new FibTask(8);
                f.quietlyInvoke();
                checkCompletedNormally(f, 21);
                return NoResult;
            }};
        assertSame(NoResult, testInvokeOnPool(mainPool(), a));
    }

    /**
     * join of a forked task returns when task completes
     */
    public void testForkJoin() {
        RecursiveTask<Integer> a = new CheckedRecursiveTask<Integer>() {
            public Integer realCompute() {
                FibTask f = new FibTask(8);
                assertSame(f, f.fork());
                Integer r = f.join();
                assertEquals(21, (int) r);
                checkCompletedNormally(f, r);
                return r;
            }};
        assertEquals(21, (int) testInvokeOnPool(mainPool(), a));
    }

    /**
     * get of a forked task returns when task completes
     */
    public void testForkGet() {
        RecursiveTask<Integer> a = new CheckedRecursiveTask<Integer>() {
            public Integer realCompute() throws Exception {
                FibTask f = new FibTask(8);
                assertSame(f, f.fork());
                Integer r = f.get();
                assertEquals(21, (int) r);
                checkCompletedNormally(f, r);
                return r;
            }};
        assertEquals(21, (int) testInvokeOnPool(mainPool(), a));
    }

    /**
     * timed get of a forked task returns when task completes
     */
    public void testForkTimedGet() {
        RecursiveTask<Integer> a = new CheckedRecursiveTask<Integer>() {
            public Integer realCompute() throws Exception {
                FibTask f = new FibTask(8);
                assertSame(f, f.fork());
                Integer r = f.get(5L, SECONDS);
                assertEquals(21, (int) r);
                checkCompletedNormally(f, r);
                return r;
            }};
        assertEquals(21, (int) testInvokeOnPool(mainPool(), a));
    }

    /**
     * quietlyJoin of a forked task returns when task completes
     */
    public void testForkQuietlyJoin() {
        RecursiveTask<Integer> a = new CheckedRecursiveTask<Integer>() {
            public Integer realCompute() {
                FibTask f = new FibTask(8);
                assertSame(f, f.fork());
                f.quietlyJoin();
                Integer r = f.getRawResult();
                assertEquals(21, (int) r);
                checkCompletedNormally(f, r);
                return r;
            }};
        assertEquals(21, (int) testInvokeOnPool(mainPool(), a));
    }

    /**
     * helpQuiesce returns when tasks are complete.
     * getQueuedTaskCount returns 0 when quiescent
     */
    public void testForkHelpQuiesce() {
        RecursiveTask<Integer> a = new CheckedRecursiveTask<Integer>() {
            public Integer realCompute() {
                FibTask f = new FibTask(8);
                assertSame(f, f.fork());
                helpQuiesce();
                while (!f.isDone()) // wait out race
                    ;
                assertEquals(0, getQueuedTaskCount());
                checkCompletedNormally(f, 21);
                return NoResult;
            }};
        assertSame(NoResult, testInvokeOnPool(mainPool(), a));
    }

    /**
     * invoke task throws exception when task completes abnormally
     */
    public void testAbnormalInvoke() {
        RecursiveTask<Integer> a = new CheckedRecursiveTask<Integer>() {
            public Integer realCompute() {
                FailingFibTask f = new FailingFibTask(8);
                try {
                    f.invoke();
                    shouldThrow();
                } catch (FJException success) {
                    checkCompletedAbnormally(f, success);
                }
                return NoResult;
            }};
        assertSame(NoResult, testInvokeOnPool(mainPool(), a));
    }

    /**
     * quietlyInvoke task returns when task completes abnormally
     */
    public void testAbnormalQuietlyInvoke() {
        RecursiveTask<Integer> a = new CheckedRecursiveTask<Integer>() {
            public Integer realCompute() {
                FailingFibTask f = new FailingFibTask(8);
                f.quietlyInvoke();
                assertTrue(f.getException() instanceof FJException);
                checkCompletedAbnormally(f, f.getException());
                return NoResult;
            }};
        assertSame(NoResult, testInvokeOnPool(mainPool(), a));
    }

    /**
     * join of a forked task throws exception when task completes abnormally
     */
    public void testAbnormalForkJoin() {
        RecursiveTask<Integer> a = new CheckedRecursiveTask<Integer>() {
            public Integer realCompute() {
                FailingFibTask f = new FailingFibTask(8);
                assertSame(f, f.fork());
                try {
                    Integer r = f.join();
                    shouldThrow();
                } catch (FJException success) {
                    checkCompletedAbnormally(f, success);
                }
                return NoResult;
            }};
        assertSame(NoResult, testInvokeOnPool(mainPool(), a));
    }

    /**
     * get of a forked task throws exception when task completes abnormally
     */
    public void testAbnormalForkGet() {
        RecursiveTask<Integer> a = new CheckedRecursiveTask<Integer>() {
            public Integer realCompute() throws Exception {
                FailingFibTask f = new FailingFibTask(8);
                assertSame(f, f.fork());
                try {
                    Integer r = f.get();
                    shouldThrow();
                } catch (ExecutionException success) {
                    Throwable cause = success.getCause();
                    assertTrue(cause instanceof FJException);
                    checkCompletedAbnormally(f, cause);
                }
                return NoResult;
            }};
        assertSame(NoResult, testInvokeOnPool(mainPool(), a));
    }

    /**
     * timed get of a forked task throws exception when task completes abnormally
     */
    public void testAbnormalForkTimedGet() {
        RecursiveTask<Integer> a = new CheckedRecursiveTask<Integer>() {
            public Integer realCompute() throws Exception {
                FailingFibTask f = new FailingFibTask(8);
                assertSame(f, f.fork());
                try {
                    Integer r = f.get(5L, SECONDS);
                    shouldThrow();
                } catch (ExecutionException success) {
                    Throwable cause = success.getCause();
                    assertTrue(cause instanceof FJException);
                    checkCompletedAbnormally(f, cause);
                }
                return NoResult;
            }};
        assertSame(NoResult, testInvokeOnPool(mainPool(), a));
    }

    /**
     * quietlyJoin of a forked task returns when task completes abnormally
     */
    public void testAbnormalForkQuietlyJoin() {
        RecursiveTask<Integer> a = new CheckedRecursiveTask<Integer>() {
            public Integer realCompute() {
                FailingFibTask f = new FailingFibTask(8);
                assertSame(f, f.fork());
                f.quietlyJoin();
                assertTrue(f.getException() instanceof FJException);
                checkCompletedAbnormally(f, f.getException());
                return NoResult;
            }};
        assertSame(NoResult, testInvokeOnPool(mainPool(), a));
    }

    /**
     * invoke task throws exception when task cancelled
     */
    public void testCancelledInvoke() {
        RecursiveTask<Integer> a = new CheckedRecursiveTask<Integer>() {
            public Integer realCompute() {
                FibTask f = new FibTask(8);
                assertTrue(f.cancel(true));
                try {
                    Integer r = f.invoke();
                    shouldThrow();
                } catch (CancellationException success) {
                    checkCancelled(f);
                }
                return NoResult;
            }};
        assertSame(NoResult, testInvokeOnPool(mainPool(), a));
    }

    /**
     * join of a forked task throws exception when task cancelled
     */
    public void testCancelledForkJoin() {
        RecursiveTask<Integer> a = new CheckedRecursiveTask<Integer>() {
            public Integer realCompute() {
                FibTask f = new FibTask(8);
                assertTrue(f.cancel(true));
                assertSame(f, f.fork());
                try {
                    Integer r = f.join();
                    shouldThrow();
                } catch (CancellationException success) {
                    checkCancelled(f);
                }
                return NoResult;
            }};
        assertSame(NoResult, testInvokeOnPool(mainPool(), a));
    }

    /**
     * get of a forked task throws exception when task cancelled
     */
    public void testCancelledForkGet() {
        RecursiveTask<Integer> a = new CheckedRecursiveTask<Integer>() {
            public Integer realCompute() throws Exception {
                FibTask f = new FibTask(8);
                assertTrue(f.cancel(true));
                assertSame(f, f.fork());
                try {
                    Integer r = f.get();
                    shouldThrow();
                } catch (CancellationException success) {
                    checkCancelled(f);
                }
                return NoResult;
            }};
        assertSame(NoResult, testInvokeOnPool(mainPool(), a));
    }

    /**
     * timed get of a forked task throws exception when task cancelled
     */
    public void testCancelledForkTimedGet() {
        RecursiveTask<Integer> a = new CheckedRecursiveTask<Integer>() {
            public Integer realCompute() throws Exception {
                FibTask f = new FibTask(8);
                assertTrue(f.cancel(true));
                assertSame(f, f.fork());
                try {
                    Integer r = f.get(5L, SECONDS);
                    shouldThrow();
                } catch (CancellationException success) {
                    checkCancelled(f);
                }
                return NoResult;
            }};
        assertSame(NoResult, testInvokeOnPool(mainPool(), a));
    }

    /**
     * quietlyJoin of a forked task returns when task cancelled
     */
    public void testCancelledForkQuietlyJoin() {
        RecursiveTask<Integer> a = new CheckedRecursiveTask<Integer>() {
            public Integer realCompute() {
                FibTask f = new FibTask(8);
                assertTrue(f.cancel(true));
                assertSame(f, f.fork());
                f.quietlyJoin();
                checkCancelled(f);
                return NoResult;
            }};
        assertSame(NoResult, testInvokeOnPool(mainPool(), a));
    }

    /**
     * getPool of executing task returns its pool
     */
    public void testGetPool() {
        final ForkJoinPool mainPool = mainPool();
        RecursiveTask<Integer> a = new CheckedRecursiveTask<Integer>() {
            public Integer realCompute() {
                assertSame(mainPool, getPool());
                return NoResult;
            }};
        assertSame(NoResult, testInvokeOnPool(mainPool, a));
    }

    /**
     * getPool of non-FJ task returns null
     */
    public void testGetPool2() {
        RecursiveTask<Integer> a = new CheckedRecursiveTask<Integer>() {
            public Integer realCompute() {
                assertNull(getPool());
                return NoResult;
            }};
        assertSame(NoResult, a.invoke());
    }

    /**
     * inForkJoinPool of executing task returns true
     */
    public void testInForkJoinPool() {
        RecursiveTask<Integer> a = new CheckedRecursiveTask<Integer>() {
            public Integer realCompute() {
                assertTrue(inForkJoinPool());
                return NoResult;
            }};
        assertSame(NoResult, testInvokeOnPool(mainPool(), a));
    }

    /**
     * inForkJoinPool of non-FJ task returns false
     */
    public void testInForkJoinPool2() {
        RecursiveTask<Integer> a = new CheckedRecursiveTask<Integer>() {
            public Integer realCompute() {
                assertFalse(inForkJoinPool());
                return NoResult;
            }};
        assertSame(NoResult, a.invoke());
    }

    /**
     * The value set by setRawResult is returned by getRawResult
     */
    public void testSetRawResult() {
        RecursiveTask<Integer> a = new CheckedRecursiveTask<Integer>() {
            public Integer realCompute() {
                setRawResult(NoResult);
                assertSame(NoResult, getRawResult());
                return NoResult;
            }
        };
        assertSame(NoResult, a.invoke());
    }

    /**
     * A reinitialized normally completed task may be re-invoked
     */
    public void testReinitialize() {
        RecursiveTask<Integer> a = new CheckedRecursiveTask<Integer>() {
            public Integer realCompute() {
                FibTask f = new FibTask(8);
                checkNotDone(f);

                for (int i = 0; i < 3; i++) {
                    Integer r = f.invoke();
                    assertEquals(21, (int) r);
                    checkCompletedNormally(f, r);
                    f.reinitialize();
                    f.publicSetRawResult(null);
                    checkNotDone(f);
                }
                return NoResult;
            }};
        assertSame(NoResult, testInvokeOnPool(mainPool(), a));
    }

    /**
     * A reinitialized abnormally completed task may be re-invoked
     */
    public void testReinitializeAbnormal() {
        RecursiveTask<Integer> a = new CheckedRecursiveTask<Integer>() {
            public Integer realCompute() {
                FailingFibTask f = new FailingFibTask(8);
                checkNotDone(f);

                for (int i = 0; i < 3; i++) {
                    try {
                        f.invoke();
                        shouldThrow();
                    } catch (FJException success) {
                        checkCompletedAbnormally(f, success);
                    }
                    f.reinitialize();
                    checkNotDone(f);
                }
                return NoResult;
            }};
        assertSame(NoResult, testInvokeOnPool(mainPool(), a));
    }

    /**
     * invoke task throws exception after invoking completeExceptionally
     */
    public void testCompleteExceptionally() {
        RecursiveTask<Integer> a = new CheckedRecursiveTask<Integer>() {
            public Integer realCompute() {
                FibTask f = new FibTask(8);
                f.completeExceptionally(new FJException());
                try {
                    Integer r = f.invoke();
                    shouldThrow();
                } catch (FJException success) {
                    checkCompletedAbnormally(f, success);
                }
                return NoResult;
            }};
        assertSame(NoResult, testInvokeOnPool(mainPool(), a));
    }

    /**
     * invoke task suppresses execution invoking complete
     */
    public void testComplete() {
        RecursiveTask<Integer> a = new CheckedRecursiveTask<Integer>() {
            public Integer realCompute() {
                FibTask f = new FibTask(8);
                f.complete(NoResult);
                Integer r = f.invoke();
                assertSame(NoResult, r);
                checkCompletedNormally(f, NoResult);
                return r;
            }};
        assertSame(NoResult, testInvokeOnPool(mainPool(), a));
    }

    /**
     * invokeAll(t1, t2) invokes all task arguments
     */
    public void testInvokeAll2() {
        RecursiveTask<Integer> a = new CheckedRecursiveTask<Integer>() {
            public Integer realCompute() {
                FibTask f = new FibTask(8);
                FibTask g = new FibTask(9);
                invokeAll(f, g);
                checkCompletedNormally(f, 21);
                checkCompletedNormally(g, 34);
                return NoResult;
            }};
        assertSame(NoResult, testInvokeOnPool(mainPool(), a));
    }

    /**
     * invokeAll(tasks) with 1 argument invokes task
     */
    public void testInvokeAll1() {
        RecursiveTask<Integer> a = new CheckedRecursiveTask<Integer>() {
            public Integer realCompute() {
                FibTask f = new FibTask(8);
                invokeAll(f);
                checkCompletedNormally(f, 21);
                return NoResult;
            }};
        assertSame(NoResult, testInvokeOnPool(mainPool(), a));
    }

    /**
     * invokeAll(tasks) with > 2 argument invokes tasks
     */
    public void testInvokeAll3() {
        RecursiveTask<Integer> a = new CheckedRecursiveTask<Integer>() {
            public Integer realCompute() {
                FibTask f = new FibTask(8);
                FibTask g = new FibTask(9);
                FibTask h = new FibTask(7);
                invokeAll(f, g, h);
                assertTrue(f.isDone());
                assertTrue(g.isDone());
                assertTrue(h.isDone());
                checkCompletedNormally(f, 21);
                checkCompletedNormally(g, 34);
                checkCompletedNormally(h, 13);
                return NoResult;
            }};
        assertSame(NoResult, testInvokeOnPool(mainPool(), a));
    }

    /**
     * invokeAll(collection) invokes all tasks in the collection
     */
    public void testInvokeAllCollection() {
        RecursiveTask<Integer> a = new CheckedRecursiveTask<Integer>() {
            public Integer realCompute() {
                FibTask f = new FibTask(8);
                FibTask g = new FibTask(9);
                FibTask h = new FibTask(7);
                HashSet set = new HashSet();
                set.add(f);
                set.add(g);
                set.add(h);
                invokeAll(set);
                assertTrue(f.isDone());
                assertTrue(g.isDone());
                assertTrue(h.isDone());
                checkCompletedNormally(f, 21);
                checkCompletedNormally(g, 34);
                checkCompletedNormally(h, 13);
                return NoResult;
            }};
        assertSame(NoResult, testInvokeOnPool(mainPool(), a));
    }

    /**
     * invokeAll(tasks) with any null task throws NPE
     */
    public void testInvokeAllNPE() {
        RecursiveTask<Integer> a = new CheckedRecursiveTask<Integer>() {
            public Integer realCompute() {
                FibTask f = new FibTask(8);
                FibTask g = new FibTask(9);
                FibTask h = null;
                try {
                    invokeAll(f, g, h);
                    shouldThrow();
                } catch (NullPointerException success) {}
                return NoResult;
            }};
        assertSame(NoResult, testInvokeOnPool(mainPool(), a));
    }

    /**
     * invokeAll(t1, t2) throw exception if any task does
     */
    public void testAbnormalInvokeAll2() {
        RecursiveTask<Integer> a = new CheckedRecursiveTask<Integer>() {
            public Integer realCompute() {
                FibTask f = new FibTask(8);
                FailingFibTask g = new FailingFibTask(9);
                try {
                    invokeAll(f, g);
                    shouldThrow();
                } catch (FJException success) {
                    checkCompletedAbnormally(g, success);
                }
                return NoResult;
            }};
        assertSame(NoResult, testInvokeOnPool(mainPool(), a));
    }

    /**
     * invokeAll(tasks) with 1 argument throws exception if task does
     */
    public void testAbnormalInvokeAll1() {
        RecursiveTask<Integer> a = new CheckedRecursiveTask<Integer>() {
            public Integer realCompute() {
                FailingFibTask g = new FailingFibTask(9);
                try {
                    invokeAll(g);
                    shouldThrow();
                } catch (FJException success) {
                    checkCompletedAbnormally(g, success);
                }
                return NoResult;
            }};
        assertSame(NoResult, testInvokeOnPool(mainPool(), a));
    }

    /**
     * invokeAll(tasks) with > 2 argument throws exception if any task does
     */
    public void testAbnormalInvokeAll3() {
        RecursiveTask<Integer> a = new CheckedRecursiveTask<Integer>() {
            public Integer realCompute() {
                FibTask f = new FibTask(8);
                FailingFibTask g = new FailingFibTask(9);
                FibTask h = new FibTask(7);
                try {
                    invokeAll(f, g, h);
                    shouldThrow();
                } catch (FJException success) {
                    checkCompletedAbnormally(g, success);
                }
                return NoResult;
            }};
        assertSame(NoResult, testInvokeOnPool(mainPool(), a));
    }

    /**
     * invokeAll(collection) throws exception if any task does
     */
    public void testAbnormalInvokeAllCollection() {
        RecursiveTask<Integer> a = new CheckedRecursiveTask<Integer>() {
            public Integer realCompute() {
                FailingFibTask f = new FailingFibTask(8);
                FibTask g = new FibTask(9);
                FibTask h = new FibTask(7);
                HashSet set = new HashSet();
                set.add(f);
                set.add(g);
                set.add(h);
                try {
                    invokeAll(set);
                    shouldThrow();
                } catch (FJException success) {
                    checkCompletedAbnormally(f, success);
                }
                return NoResult;
            }};
        assertSame(NoResult, testInvokeOnPool(mainPool(), a));
    }

    /**
     * tryUnfork returns true for most recent unexecuted task,
     * and suppresses execution
     */
    public void testTryUnfork() {
        RecursiveTask<Integer> a = new CheckedRecursiveTask<Integer>() {
            public Integer realCompute() {
                FibTask g = new FibTask(9);
                assertSame(g, g.fork());
                FibTask f = new FibTask(8);
                assertSame(f, f.fork());
                assertTrue(f.tryUnfork());
                helpQuiesce();
                checkNotDone(f);
                checkCompletedNormally(g, 34);
                return NoResult;
            }};
        assertSame(NoResult, testInvokeOnPool(singletonPool(), a));
    }

    /**
     * getSurplusQueuedTaskCount returns > 0 when
     * there are more tasks than threads
     */
    public void testGetSurplusQueuedTaskCount() {
        RecursiveTask<Integer> a = new CheckedRecursiveTask<Integer>() {
            public Integer realCompute() {
                FibTask h = new FibTask(7);
                assertSame(h, h.fork());
                FibTask g = new FibTask(9);
                assertSame(g, g.fork());
                FibTask f = new FibTask(8);
                assertSame(f, f.fork());
                assertTrue(getSurplusQueuedTaskCount() > 0);
                helpQuiesce();
                assertEquals(0, getSurplusQueuedTaskCount());
                checkCompletedNormally(f, 21);
                checkCompletedNormally(g, 34);
                checkCompletedNormally(h, 13);
                return NoResult;
            }};
        assertSame(NoResult, testInvokeOnPool(singletonPool(), a));
    }

    /**
     * peekNextLocalTask returns most recent unexecuted task.
     */
    public void testPeekNextLocalTask() {
        RecursiveTask<Integer> a = new CheckedRecursiveTask<Integer>() {
            public Integer realCompute() {
                FibTask g = new FibTask(9);
                assertSame(g, g.fork());
                FibTask f = new FibTask(8);
                assertSame(f, f.fork());
                assertSame(f, peekNextLocalTask());
                checkCompletesNormally(f, 21);
                helpQuiesce();
                checkCompletedNormally(g, 34);
                return NoResult;
            }};
        assertSame(NoResult, testInvokeOnPool(singletonPool(), a));
    }

    /**
     * pollNextLocalTask returns most recent unexecuted task
     * without executing it
     */
    public void testPollNextLocalTask() {
        RecursiveTask<Integer> a = new CheckedRecursiveTask<Integer>() {
            public Integer realCompute() {
                FibTask g = new FibTask(9);
                assertSame(g, g.fork());
                FibTask f = new FibTask(8);
                assertSame(f, f.fork());
                assertSame(f, pollNextLocalTask());
                helpQuiesce();
                checkNotDone(f);
                checkCompletedNormally(g, 34);
                return NoResult;
            }};
        assertSame(NoResult, testInvokeOnPool(singletonPool(), a));
    }

    /**
     * pollTask returns an unexecuted task without executing it
     */
    public void testPollTask() {
        RecursiveTask<Integer> a = new CheckedRecursiveTask<Integer>() {
            public Integer realCompute() {
                FibTask g = new FibTask(9);
                assertSame(g, g.fork());
                FibTask f = new FibTask(8);
                assertSame(f, f.fork());
                assertSame(f, pollTask());
                helpQuiesce();
                checkNotDone(f);
                checkCompletedNormally(g, 34);
                return NoResult;
            }};
        assertSame(NoResult, testInvokeOnPool(singletonPool(), a));
    }

    /**
     * peekNextLocalTask returns least recent unexecuted task in async mode
     */
    public void testPeekNextLocalTaskAsync() {
        RecursiveTask<Integer> a = new CheckedRecursiveTask<Integer>() {
            public Integer realCompute() {
                FibTask g = new FibTask(9);
                assertSame(g, g.fork());
                FibTask f = new FibTask(8);
                assertSame(f, f.fork());
                assertSame(g, peekNextLocalTask());
                assertEquals(21, (int) f.join());
                helpQuiesce();
                checkCompletedNormally(f, 21);
                checkCompletedNormally(g, 34);
                return NoResult;
            }};
        assertSame(NoResult, testInvokeOnPool(asyncSingletonPool(), a));
    }

    /**
     * pollNextLocalTask returns least recent unexecuted task without
     * executing it, in async mode
     */
    public void testPollNextLocalTaskAsync() {
        RecursiveTask<Integer> a = new CheckedRecursiveTask<Integer>() {
            public Integer realCompute() {
                FibTask g = new FibTask(9);
                assertSame(g, g.fork());
                FibTask f = new FibTask(8);
                assertSame(f, f.fork());
                assertSame(g, pollNextLocalTask());
                helpQuiesce();
                checkCompletedNormally(f, 21);
                checkNotDone(g);
                return NoResult;
            }};
        assertSame(NoResult, testInvokeOnPool(asyncSingletonPool(), a));
    }

    /**
     * pollTask returns an unexecuted task without executing it, in
     * async mode
     */
    public void testPollTaskAsync() {
        RecursiveTask<Integer> a = new CheckedRecursiveTask<Integer>() {
            public Integer realCompute() {
                FibTask g = new FibTask(9);
                assertSame(g, g.fork());
                FibTask f = new FibTask(8);
                assertSame(f, f.fork());
                assertSame(g, pollTask());
                helpQuiesce();
                checkCompletedNormally(f, 21);
                checkNotDone(g);
                return NoResult;
            }};
        assertSame(NoResult, testInvokeOnPool(asyncSingletonPool(), a));
    }

}