/*
* JBoss, Home of Professional Open Source
* Copyright 2013 Red Hat Inc. and/or its affiliates and other contributors
* as indicated by the @authors tag. All rights reserved.
*/
package org.searchisko.api.tasker;
import java.lang.Thread.State;
import java.util.Set;
import org.junit.Assert;
import org.junit.Test;
import org.mockito.Mockito;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;
import org.searchisko.api.testtools.TestUtils;
/**
* Unit test for {@link TaskRunner}.
*
* @author Vlastimil Elias (velias at redhat dot com)
*/
public class TaskRunnerTest {
private static final String TASK_TYPE_TEST = "taskTypeTest";
private static final String TESTNODEID = "testnodeid";
@Test
public void constructor() {
TaskFactory taskFactory = Mockito.mock(TaskFactory.class);
TaskPersister taskPersister = Mockito.mock(TaskPersister.class);
TaskRunner tested = new TaskRunner("ndid", taskFactory, taskPersister);
Assert.assertEquals("ndid", tested.nodeId);
Assert.assertEquals(taskFactory, tested.taskFactory);
Assert.assertEquals(taskPersister, tested.taskPersister);
// case - assert last heartbeat initialization so no HB runs after start immediately
TestUtils.assertCurrentDate(tested.lastHb);
}
@Test
public void handleCancelRequests() throws InterruptedException {
TaskRunner tested = getTested();
Task t1 = createTaskMock(true);
Task t3 = createTaskMock(true);
Task t5 = createTaskMock(true);
Task t6 = createTaskMock(true);
t6.setCanceled(true);
tested.runningTasks.put("t1", t1);
tested.runningTasks.put("t2", createTaskMock(false));
tested.runningTasks.put("t3", t3);
tested.runningTasks.put("t4", createTaskMock(false));
tested.runningTasks.put("t5", t5);
tested.runningTasks.put("t6", t6);
Thread.sleep(1000);
Mockito.when(tested.taskPersister.getTaskStatusInfo("t1")).thenReturn(createTaskStatusInfoCancelTest(false));
Mockito.when(tested.taskPersister.getTaskStatusInfo("t3")).thenReturn(createTaskStatusInfoCancelTest(true));
Mockito.when(tested.taskPersister.getTaskStatusInfo("t5")).thenReturn(null);
tested.handleCancelRequests();
Assert.assertEquals(false, t1.isCanceledOrInterrupted());
Assert.assertEquals(true, t3.isCanceledOrInterrupted());
Assert.assertEquals(false, t5.isCanceledOrInterrupted());
Assert.assertEquals(true, t6.isCanceledOrInterrupted());
Mockito.verify(tested.taskPersister).getTaskStatusInfo("t1");
Mockito.verify(tested.taskPersister).getTaskStatusInfo("t3");
Mockito.verify(tested.taskPersister).getTaskStatusInfo("t5");
Mockito.verifyNoMoreInteractions(tested.taskPersister);
}
private TaskStatusInfo createTaskStatusInfoCancelTest(boolean cancelRequsted) {
TaskStatusInfo ret = new TaskStatusInfo();
ret.cancelRequested = cancelRequsted;
return ret;
}
@SuppressWarnings("unchecked")
@Test
public void startTasks() throws UnsupportedTaskException, TaskConfigurationException {
TaskRunner tested = getTested();
// case - nothing to run in persister
Mockito.when(tested.taskPersister.getTaskToRun(TESTNODEID)).thenReturn(null);
tested.startTasks();
Mockito.verify(tested.taskPersister).getTaskToRun(TESTNODEID);
Mockito.verifyNoMoreInteractions(tested.taskPersister);
Mockito.verifyNoMoreInteractions(tested.taskFactory);
// case - start two new tasks
Mockito.reset(tested.taskFactory, tested.taskPersister);
Mockito.when(tested.taskPersister.getTaskToRun(TESTNODEID)).thenAnswer(new Answer<TaskStatusInfo>() {
@Override
public TaskStatusInfo answer(InvocationOnMock invocation) throws Throwable {
return createTaskStatusInfoStartTest();
}
});
Mockito.when(tested.taskFactory.createTask(Mockito.eq(TASK_TYPE_TEST), Mockito.anyMap())).thenAnswer(
new Answer<Task>() {
@Override
public Task answer(InvocationOnMock invocation) throws Throwable {
return createTaskMock();
}
});
tested.startTasks();
Assert.assertEquals(2, tested.runningTasks.size());
for (Task task : tested.runningTasks.values()) {
Assert.assertNotNull(task.context);
Assert.assertNotNull(task.taskId);
Assert.assertTrue(task.getState() != State.NEW);
}
Mockito.verify(tested.taskPersister, Mockito.times(2)).getTaskToRun(TESTNODEID);
Mockito.verify(tested.taskFactory, Mockito.times(2)).createTask(Mockito.eq(TASK_TYPE_TEST), Mockito.anyMap());
// case - all running slot full so nothing started
Mockito.reset(tested.taskFactory, tested.taskPersister);
tested.startTasks();
Assert.assertEquals(2, tested.runningTasks.size());
Mockito.verifyZeroInteractions(tested.taskPersister);
Mockito.verifyZeroInteractions(tested.taskFactory);
// case - exception during start is not thworn out of method
Mockito.reset(tested.taskFactory, tested.taskPersister);
Mockito.when(tested.taskPersister.getTaskToRun(TESTNODEID)).thenThrow(new RuntimeException("test exception"));
tested.startTasks();
Mockito.verifyZeroInteractions(tested.taskFactory);
}
int ctc = 1;
private TaskStatusInfo createTaskStatusInfoStartTest() {
TaskStatusInfo ti = new TaskStatusInfo();
ti.id = "t" + ctc;
ctc++;
ti.taskType = TASK_TYPE_TEST;
return ti;
}
private Task createTaskMock() {
Task t = new Task() {
@Override
protected void performTask() throws Exception {
}
};
t.setDaemon(true);
return t;
}
@Test
public void interruptRunningTasks() throws InterruptedException {
TaskRunner tested = getTested();
tested.runningTasks.put("t1", createTaskMock(true));
tested.runningTasks.put("t2", createTaskMock(false));
tested.runningTasks.put("t3", createTaskMock(true));
tested.runningTasks.put("t4", createTaskMock(false));
tested.runningTasks.put("t5", createTaskMock(true));
tested.runningTasks.put("t6", createTaskMock(true));
Thread.sleep(200);
tested.interruptRunningTasks();
for (Task task : tested.runningTasks.values()) {
Assert.assertTrue(!task.isAlive() || task.isInterrupted());
}
}
@SuppressWarnings({ "unchecked", "rawtypes" })
@Test
public void heartbeat() {
TaskRunner tested = getTested();
// case - no heartbeat performed
{
long lhb = System.currentTimeMillis() - tested.hbPeriod + 200;
tested.lastHb = lhb;
tested.heartbeat();
Assert.assertEquals(lhb, tested.lastHb);
Mockito.verifyZeroInteractions(tested.taskPersister);
}
// case - heartbeat performed, no any running tasks in runner
{
Mockito.reset(tested.taskPersister);
long lhb = System.currentTimeMillis() - tested.hbPeriod - 200;
tested.lastHb = lhb;
Mockito.doAnswer(new Answer() {
@Override
public Object answer(InvocationOnMock invocation) throws Throwable {
Set<String> s = (Set<String>) invocation.getArguments()[1];
Assert.assertNotNull(s);
Assert.assertTrue(s.isEmpty());
return null;
}
}).when(tested.taskPersister).heartbeat(Mockito.eq(TESTNODEID), Mockito.anySet(), Mockito.anyLong());
tested.heartbeat();
TestUtils.assertCurrentDate(tested.lastHb);
Mockito.verify(tested.taskPersister).heartbeat(Mockito.eq(TESTNODEID), Mockito.anySet(),
Mockito.eq(tested.hbPeriod * 5));
Mockito.verifyNoMoreInteractions(tested.taskPersister);
}
// case - heartbeat performed, some running tasks in runner
{
Mockito.reset(tested.taskPersister);
tested.runningTasks.put("tid1", Mockito.mock(Task.class));
tested.runningTasks.put("tid2", Mockito.mock(Task.class));
long lhb = System.currentTimeMillis() - tested.hbPeriod - 200;
tested.lastHb = lhb;
Mockito.doAnswer(new Answer() {
@Override
public Object answer(InvocationOnMock invocation) throws Throwable {
Set<String> s = (Set<String>) invocation.getArguments()[1];
Assert.assertNotNull(s);
Assert.assertEquals(2, s.size());
Assert.assertTrue(s.contains("tid1"));
Assert.assertTrue(s.contains("tid2"));
return null;
}
}).when(tested.taskPersister).heartbeat(Mockito.eq(TESTNODEID), Mockito.anySet(), Mockito.anyLong());
tested.heartbeat();
TestUtils.assertCurrentDate(tested.lastHb);
Mockito.verify(tested.taskPersister).heartbeat(Mockito.eq(TESTNODEID), Mockito.anySet(),
Mockito.eq(tested.hbPeriod * 5));
Mockito.verifyNoMoreInteractions(tested.taskPersister);
}
}
@Test
public void cancelTask() throws InterruptedException {
TaskRunner tested = getTested();
tested.cancelTask("unknown");
Task t1 = createTaskMock(true);
Task t2 = createTaskMock(false);
tested.runningTasks.put("t1", t1);
tested.runningTasks.put("t2", t2);
Thread.sleep(700);
tested.cancelTask("t1");
tested.cancelTask("t2");
Assert.assertTrue(t1.isCanceledOrInterrupted());
Assert.assertFalse(t2.isCanceledOrInterrupted());
}
@Test
public void removeFinished() throws InterruptedException {
TaskRunner tested = getTested();
tested.removeFinished();
Assert.assertEquals(0, tested.runningTasks.size());
Task t1 = createTaskMock(true);
Task t3 = createTaskMock(true);
tested.runningTasks.put("t1", t1);
tested.runningTasks.put("t2", createTaskMock(false));
tested.runningTasks.put("t3", t3);
tested.runningTasks.put("t4", createTaskMock(false));
Thread.sleep(1000);
tested.removeFinished();
Assert.assertEquals(2, tested.runningTasks.size());
Assert.assertEquals(t1, tested.runningTasks.get("t1"));
Assert.assertEquals(t3, tested.runningTasks.get("t3"));
}
@Test
public void TaskExecutionContextImpl() {
TaskRunner tested = new TaskRunner();
tested.taskPersister = Mockito.mock(TaskPersister.class);
tested.taskExecutionContextInstance.changeTaskStatus("aa", TaskStatus.CANCELED, "mymessage");
Mockito.verify(tested.taskPersister).changeTaskStatus("aa", TaskStatus.CANCELED, "mymessage");
tested.taskExecutionContextInstance.writeTaskLog("aaa", "msg");
Mockito.verify(tested.taskPersister).writeTaskLog("aaa", "msg");
}
private TaskRunner getTested() {
TaskRunner tested = new TaskRunner();
tested.taskPersister = Mockito.mock(TaskPersister.class);
tested.taskFactory = Mockito.mock(TaskFactory.class);
tested.taskExecutionContextInstance = Mockito.mock(TaskExecutionContext.class);
tested.maxRunningTasks = 2;
tested.nodeId = TESTNODEID;
return tested;
}
private Task createTaskMock(final boolean alive) {
Task t = new Task() {
@Override
protected void performTask() throws Exception {
}
@Override
public void run() {
if (alive) {
try {
Thread.sleep(10000);
} catch (InterruptedException e) {
}
}
}
};
t.setDaemon(true);
t.start();
return t;
}
}