package org.wikimedia.wikipedia.test.concurrency;

import android.content.Intent;
import android.test.ActivityUnitTestCase;
import org.wikimedia.wikipedia.concurrency.SaneAsyncTask;
import org.wikimedia.wikipedia.test.TestDummyActivity;

import java.util.concurrent.*;

public class AsyncTaskTest extends ActivityUnitTestCase<TestDummyActivity> {
    public static final int TASK_COMPLETION_TIMEOUT = 1000;
    private Executor executor;

    public AsyncTaskTest() {
        super(TestDummyActivity.class);
    }

    private Executor getDefaultExecutor() {
        if (executor == null) {
            executor = new ScheduledThreadPoolExecutor(1);
        }
        return executor;
    }

    @Override
    public void setUp() throws Exception {
        super.setUp();
        startActivity(new Intent(), null, null);
    }

    public void testFinishHandling() throws Throwable {
        final CountDownLatch onFinishLatch = new CountDownLatch(1);
        final Integer returned = 42;
        runTestOnUiThread(new Runnable() {
            @Override
            public void run() {
                new SaneAsyncTask<Integer>(getDefaultExecutor()) {
                    @Override
                    public void onFinish(Integer result) {
                        assertEquals(returned, result);
                        onFinishLatch.countDown();
                    }

                    @Override
                    public void onCatch(Throwable caught) {
                        assertTrue("Exception called despite success", false);
                    }

                    @Override
                    public Integer performTask() throws Throwable {
                        return returned;
                    }
                }.execute();
            }
        });
        assertTrue(onFinishLatch.await(TASK_COMPLETION_TIMEOUT, TimeUnit.MILLISECONDS));
    }

    public void testExceptionHandling() throws Throwable {
        final CountDownLatch exceptionLatch = new CountDownLatch(1);
        final Throwable thrown = new Exception();
        runTestOnUiThread(new Runnable() {
            @Override
            public void run() {
                new SaneAsyncTask<Void>(getDefaultExecutor()) {
                    @Override
                    public void onFinish(Void result) {
                        assertTrue("onFinish called despite exception", false);
                    }

                    @Override
                    public void onCatch(Throwable caught) {
                        assertSame(caught, thrown);
                        exceptionLatch.countDown();
                    }

                    @Override
                    public Void performTask() throws Throwable {
                        throw thrown;
                    }
                }.execute();
            }
        });
        assertTrue(exceptionLatch.await(TASK_COMPLETION_TIMEOUT, TimeUnit.MILLISECONDS));
    }

    public void testAppropriateThreadFinish() throws Throwable {
        final CountDownLatch completionLatch = new CountDownLatch(1);
        runTestOnUiThread(new Runnable() {
            @Override
            public void run() {
                final Thread callingThread = Thread.currentThread();
                new SaneAsyncTask<Thread>(getDefaultExecutor()) {
                    @Override
                    public void onBeforeExecute() {
                        assertSame(callingThread, Thread.currentThread());
                    }

                    @Override
                    public void onFinish(Thread result) {
                        assertNotSame(result, Thread.currentThread());
                        assertSame(Thread.currentThread(), callingThread);
                        completionLatch.countDown();
                    }

                    @Override
                    public Thread performTask() throws Throwable {
                        assertNotSame(callingThread, Thread.currentThread());
                        return Thread.currentThread();
                    }
                }.execute();
            }
        });
        assertTrue(completionLatch.await(TASK_COMPLETION_TIMEOUT, TimeUnit.MILLISECONDS));
    }
}

@@ -4181,11 +4181,1445 @@
 );%0A    %7D
+%0A%0A    public void testAppropriateThreadException() throws Throwable %7B%0A        final CountDownLatch completionLatch = new CountDownLatch(1);%0A        final Throwable thrown = new Exception();%0A        runTestOnUiThread(new Runnable() %7B%0A            @Override%0A            public void run() %7B%0A                final Thread callingThread = Thread.currentThread();%0A                new SaneAsyncTask%3CThread%3E(getDefaultExecutor()) %7B%0A                    @Override%0A                    public void onBeforeExecute() %7B%0A                        assertSame(callingThread, Thread.currentThread());%0A                    %7D%0A%0A                    @Override%0A                    public void onFinish(Thread result) %7B%0A                        assertTrue(%22onFinish called even when there is an exception%22, false);%0A                    %7D%0A%0A                    @Override%0A                    public void onCatch(Throwable caught) %7B%0A                        assertSame(callingThread, Thread.currentThread());%0A                        completionLatch.countDown();%0A                    %7D%0A%0A                    @Override%0A                    public Thread performTask() throws Throwable %7B%0A                        assertNotSame(callingThread, Thread.currentThread());%0A                        throw thrown;%0A                    %7D%0A                %7D.execute();%0A            %7D%0A        %7D);%0A        assertTrue(completionLatch.await(TASK_COMPLETION_TIMEOUT, TimeUnit.MILLISECONDS));%0A    %7D
 %0A%7D%0A

Add test to verify SaneAsyncTask exception handler thread affinity