com.sworddance.taskcontrol.TestTaskControl.java Source code

Java tutorial

Introduction

Here is the source code for com.sworddance.taskcontrol.TestTaskControl.java

Source

/*
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License. You may obtain a copy
 * of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software distributed
 * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES
 * OR CONDITIONS OF ANY KIND, either express or implied. See the License for
 * the specific language governing permissions and limitations under the
 * License.
 */
package com.sworddance.taskcontrol;

import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.concurrent.TimeUnit;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.testng.annotations.DataProvider;
import org.testng.annotations.Test;

import static org.testng.Assert.*;

/**
 * test TaskControl.
 * @author Patrick Moore
 */
public class TestTaskControl {
    @SuppressWarnings("unchecked")
    @DataProvider(name = "testObjects")
    protected Object[][] getTestObjects() {
        TaskGroup taskGroup = new TaskGroup("taskGroup");
        taskGroup.setLog(LogFactory.getLog(this.getClass()));
        return new Object[][] { new Object[] { taskGroup, new OrderedOut(), LogFactory.getLog(this.getClass()) } };
    }

    /**
     * make sure that default in/out is FIFO.
     * @param taskGroup
     * @param orderOut
     * @param log
     * @throws Exception
     *
     */
    @Test(dataProvider = "testObjects")
    public void testTestOrdering(TaskGroup<?> taskGroup, OrderedOut orderOut, Log log) throws Exception {
        TaskControl taskControl = new TaskControl(new TestPriorityComparator(), 1, log);
        for (int i = 0; i < 200; i++) {
            TestTask task = new TestTask("testTestOrdering", i, orderOut);
            taskGroup.addTask(task);
        }
        startTaskControl(taskControl, taskGroup);
        assertEquals(orderOut.expected, 200);
        orderOut.printErrorMessage();
    }

    private void startTaskControl(TaskControl taskControl, TaskGroup<?> taskGroup) throws InterruptedException {
        // additional test that addTaskGroup after TaskControl start will work.
        taskControl.addTaskGroup(taskGroup);
        Thread t = new Thread(taskControl);
        taskControl.setStayActive(false);
        t.setName("TaskControl");
        t.start();
        t.join();
        assertFalse(t.isAlive());
    }

    @Test(dataProvider = "testObjects")
    public void testBadDependencies(TaskGroup<?> taskGroup, OrderedOut orderOut, Log log) {
        taskGroup.setLog(log);
        DefaultDependentPrioritizedTask blocking = new DefaultDependentPrioritizedTask();
        blocking.setName("blocking");
        // add some dead tasks.
        int i = 0;
        {
            TestTask task = new TestTask("testBadDependencies", i, orderOut);
            task.addDependency(blocking);
            try {
                taskGroup.addTask(task);
                fail("adding dependent tasks need to be added to TaskControl before dependencies");
            } catch (IllegalStateException e) {
                // good
            }
        }
        i++;
        {
            TestTask task = new TestTask("testBadDependencies", i, orderOut);
            try {
                task.addDependency(task);
                fail("cannot depend on self");
            } catch (IllegalStateException e) {
                // good
            }
        }
    }

    @Test(dataProvider = "testObjects")
    public void testBadJobs(TaskGroup<?> taskGroup, OrderedOut orderOut, Log log) throws Exception {
        TaskControl taskControl = new TaskControl(new TestPriorityComparator(), 1, log);
        List<TestTask> list = new ArrayList<TestTask>();
        DefaultPrioritizedTask<Object> blocking = new DefaultPrioritizedTask<Object>();
        blocking.setName("blocking");
        taskGroup.addTask(blocking);
        String testName = "testBadJobs";
        // add some dead tasks.
        int i = 0;
        {
            TestTask task = new TestTask(testName, i, orderOut);
            task.addDependency(blocking);
            list.add(task);
            taskGroup.addTask(task);
        }
        i++;
        {
            TestTask task = new TestTask(testName, i, orderOut);
            list.add(task);
            task.set(null);
            try {
                taskGroup.addTask(task);
                fail("adding completedjobs should fail");
            } catch (IllegalStateException e) {
                // good
            }
        }
        i++;
        {
            // task depending on itself
            TestTask task = new TestTask(testName, i, orderOut);
            list.add(task);
            task.set(null);
            try {
                task.addDependency(task);
                fail("tasks can't depend on themselves");
            } catch (IllegalStateException e) {
                // good
            }
        }
        i++;
        {
            // task depending on itself
            TestTask task = new TestTask(testName, i, orderOut);
            list.add(task);
            task.set(null);
            try {
                task.addAlwaysDependency(task);
                fail("tasks can't depend on themselves");
            } catch (IllegalStateException e) {
                // good
            }
        }
        i++;
        {
            TestTask task = new TestTask(testName, i, orderOut);
            list.add(task);
            taskGroup.addTask(task);
            // now make it bad...
            task.set(null);
        }
        startTaskControl(taskControl, taskGroup);
        orderOut.printErrorMessage();
        assertEquals(orderOut.expected, 0);
        for (int k = 0; k < list.size(); k++) {
            TestTask task = list.get(k);
            assertFalse(task.callBodyCalled, "Task #" + k + " should never be run");
        }
    }

    /**
     * test that if the first task at the head of a chain fails the other
     * dependent tasks fail out so that AlwaysDependencies behave as expected.
     * @param log
     * @param taskGroup
     * @param orderOut
     * @throws Exception
     */
    @Test(dataProvider = "testObjects")
    public void testChainFailure1(TaskGroup<?> taskGroup, OrderedOut orderOut, Log log) throws Exception {
        int i = 0;
        TaskControl taskControl = new TaskControl(new TestPriorityComparator(), 1, log);
        String testName = "testChainFailure1";
        TestTask task1 = new FailTestTask(testName, i++, orderOut);
        taskGroup.addTask(task1);
        TestTask task2 = new TestTask(testName, i++, orderOut);
        task2.addDependency(task1);
        taskGroup.addTask(task2);

        startTaskControl(taskControl, taskGroup);
        assertTrue(task1.callBodyCalled, "Task #" + task1.id + " should be run");
        assertFalse(task2.callBodyCalled, "Task #" + task2.id + " should not be run");
    }

    /**
     * test that if the first task at the head of a chain fails the other
     * dependent tasks fail out so that AlwaysDependencies behave as expected.
     * @param log
     * @param taskGroup
     * @param orderOut
     * @throws Exception
     */
    @Test(dataProvider = "testObjects")
    public void testChainFailure2(TaskGroup<?> taskGroup, OrderedOut orderOut, Log log) throws Exception {
        int i = 0;
        TaskControl taskControl = new TaskControl(new TestPriorityComparator(), 1, log);

        String testName = "testChainFailure2";
        TestTask task1 = new FailTestTask(testName, i++, orderOut);
        taskGroup.addTask(task1);
        TestTask task2 = new TestTask(testName, i++, orderOut);
        task2.addDependency(task1);
        taskGroup.addTask(task2);
        TestTask task3 = new TestTask(testName, i++, orderOut);
        task3.setIgnoreTaskGroupFailure(true);
        task3.addAlwaysDependency(task2);
        taskGroup.addTask(task3);

        startTaskControl(taskControl, taskGroup);
        assertTrue(task1.callBodyCalled, "Task #" + task1.id + " should be run");
        Throwable error = task1.getException();
        assertNotNull(error);
        assertFalse(task2.callBodyCalled, "Task #" + task2.id + " should not be run");
        assertNotNull(task2.getException());
        assertTrue(task3.callBodyCalled, "Task #" + task3.id + " should be run");
        assertNull(task3.getException());
    }

    /**
     * test that if the first task at the head of a chain fails the other
     * dependent tasks fail out so that AlwaysDependencies behave as expected.
     * @param log
     * @param taskGroup
     * @param orderOut
     * @throws Exception
     */
    @Test(dataProvider = "testObjects")
    public void testChainFailure3(TaskGroup<?> taskGroup, OrderedOut orderOut, Log log) throws Exception {
        int i = 0;
        TaskControl taskControl = new TaskControl(new TestPriorityComparator(), 1, log);
        String testName = "testChainFailure3";

        TestTask task1 = new FailTestTask(testName, i++, orderOut);
        taskGroup.addTask(task1);
        TestTask task2 = new TestTask(testName, i++, orderOut);
        task2.setIgnoreTaskGroupFailure(true);
        task2.addAlwaysDependency(task1);
        taskGroup.addTask(task2);

        startTaskControl(taskControl, taskGroup);
        assertTrue(task1.callBodyCalled, "Task #" + task1.id + " should be run");
        assertTrue(task2.callBodyCalled, "Task #" + task2.id + " should be run");
    }

    @Test(dataProvider = "testObjects")
    public void testSimpleDependency(TaskGroup<?> taskGroup, OrderedOut orderOut, Log log) throws Exception {
        TaskControl taskControl = new TaskControl(new TestPriorityComparator(), 1, log);
        orderOut.down = true;
        List<TestTask> list = new ArrayList<TestTask>();
        // add dependencies
        int i;
        for (i = 0; i < 20; i++) {
            TestTask task = new TestTask("testSimpleDependency", i, orderOut);
            list.add(task);
            orderOut.expected = i;
        }
        for (int k = 0; k < list.size() - 1; k++) {
            // create a dependency that will reverse the normal FIFO
            TestTask task = list.get(k);
            task.addDependency(list.get(k + 1));
        }
        for (int k = list.size() - 1; k >= 0; k--) {
            // create a dependency that will reverse the normal FIFO
            TestTask task = list.get(k);
            taskGroup.addTask(task);
        }
        startTaskControl(taskControl, taskGroup);
        orderOut.printErrorMessage();
        assertEquals(orderOut.expected, -1);
        for (int k = 0; k < list.size(); k++) {
            TestTask task = list.get(k);
            assertTrue(task.callBodyCalled, "Task #" + k + " should be run");
        }
    }

    @Test(dataProvider = "testObjects")
    public void testComplexDependency(TaskGroup<?> taskGroup, OrderedOut orderOut, Log log) throws Exception {
        TaskControl taskControl = new TaskControl(new TestPriorityComparator(), 5, log);
        orderOut.down = true;
        List<TestTask> list = new ArrayList<TestTask>();
        // add dependencies
        int i;
        for (i = 0; i < 20; i++) {
            TestTask task = new TestTask("testComplexDependency", i, orderOut);
            list.add(task);
            orderOut.expected = i;
        }
        for (int k = 0; k < list.size(); k++) {
            // create a dependency that will reverse the normal FIFO
            TestTask task = list.get(k);
            for (int m = 1; m < 4 && m + k < list.size(); m++) {
                task.addDependency(list.get(k + m));
            }
        }
        for (int k = list.size() - 1; k >= 0; k--) {
            // create a dependency that will reverse the normal FIFO
            TestTask task = list.get(k);
            taskGroup.addTask(task);
        }
        startTaskControl(taskControl, taskGroup);
        orderOut.printErrorMessage();
        for (int k = 0; k < list.size(); k++) {
            TestTask task = list.get(k);
            assertTrue(task.callBodyCalled, "Task #" + k + " should be run");
        }
        assertEquals(orderOut.expected, -1);
    }

    /**
     * make sure an empty taskgroup immediately reports that it is done (especially with result)
     * @throws Exception
     *
     */
    @SuppressWarnings("unchecked")
    public void testEmptyTaskGroup() throws Exception {
        TaskControl taskControl = new TaskControl(new TestPriorityComparator(), 1,
                LogFactory.getLog(this.getClass()));
        TaskGroup taskGroup = taskControl.newTaskGroup("empty");
        startTaskControl(taskControl, taskGroup);
        FutureResult result = taskGroup.getResult();

        // should immediately return result.
        result.get(1L, TimeUnit.NANOSECONDS);

    }

    /**
     * a test implementation of {@link DefaultDependentPrioritizedTask}.
     * @author Patrick Moore
     */
    private class TestTask extends DefaultDependentPrioritizedTask {
        private int id;

        boolean callBodyCalled;

        private OrderedOut orderOut;

        public TestTask(String testName, int id, OrderedOut orderOut) {
            setName(testName + "_TestTask_" + id);
            this.id = id;
            this.orderOut = orderOut;
        }

        @Override
        protected Object callBody() throws Exception {
            callBodyCalled = true;
            if (orderOut != null) {
                orderOut.ran(id);
            }
            long sleepTime = (long) (Math.random() * 15L);
            Thread.sleep(sleepTime);
            return null;
        }
    }

    /**
     * a task that always fails.
     * @author Patrick Moore
     */
    private class FailTestTask extends TestTask {
        /**
         * @param testName TODO
         * @param i
         * @param orderOut
         */
        public FailTestTask(String testName, int i, OrderedOut orderOut) {
            super(testName, i, orderOut);
        }

        @Override
        protected Object callBody() throws Exception {
            super.callBody();
            throw new Exception("EXPECTED");
        }
    }

    /**
     * used to track that the order of task execution is correct.
     * @author Patrick Moore
     */
    private static class OrderedOut {
        private int expected;

        private boolean down;

        private int lastExpected = Integer.MIN_VALUE;

        private int lastActual = Integer.MIN_VALUE;

        public void ran(int id) {
            if (expected != id) {
                lastExpected = expected;
                lastActual = id;
            }
            if (down) {
                expected--;
            } else {
                expected++;
            }
        }

        public void printErrorMessage() {
            assertEquals(lastActual, lastExpected);
        }
    }

    /**
     * a test comparator.
     * @author Patrick Moore
     */
    private static class TestPriorityComparator implements Comparator<PrioritizedTask> {

        public int compare(PrioritizedTask o1, PrioritizedTask o2) {
            TestTask t1 = (TestTask) ((TaskWrapper) o1).getBaseWrappedTask();
            TestTask t2 = (TestTask) ((TaskWrapper) o2).getBaseWrappedTask();
            return t1.id - t2.id;
        }

    }
}