TaskOperationProtocol taskManager = mock(TaskOperationProtocol.class);
when(taskManager.submitTask(Matchers.any(TaskDeploymentDescriptor.class))).thenReturn(new TaskOperationResult(execId, true));
when(taskManager.cancelTask(execId)).thenReturn(new TaskOperationResult(execId, true), new TaskOperationResult(execId, false));
Instance instance = getInstance(taskManager);
AllocatedSlot slot = instance.allocateSlot(new JobID());
vertex.deployToSlot(slot);
assertEquals(ExecutionState.DEPLOYING, vertex.getExecutionState());
vertex.cancel();
assertEquals(ExecutionState.CANCELING, vertex.getExecutionState());
verify(taskManager, times(0)).submitTask(Matchers.any(TaskDeploymentDescriptor.class));
verify(taskManager, times(0)).cancelTask(execId);
// first action happens (deploy)
actions.triggerNextAction();
assertEquals(ExecutionState.CANCELING, vertex.getExecutionState());
verify(taskManager, times(1)).submitTask(Matchers.any(TaskDeploymentDescriptor.class));
// the deploy call found itself in canceling after it returned and needs to send a cancel call
// the call did not yet execute, so it is still in canceling
assertEquals(ExecutionState.CANCELING, vertex.getExecutionState());
// second action happens (cancel call from cancel function)
actions.triggerNextAction();
// TaskManager reports back (canceling done)
vertex.getCurrentExecutionAttempt().cancelingComplete();
// should properly set state to cancelled
assertEquals(ExecutionState.CANCELED, vertex.getExecutionState());
// trigger the correction canceling call
actions.triggerNextAction();
assertEquals(ExecutionState.CANCELED, vertex.getExecutionState());
verify(taskManager, times(2)).cancelTask(execId);
assertTrue(slot.isReleased());
assertNull(vertex.getFailureCause());
assertTrue(vertex.getStateTimestamp(ExecutionState.CREATED) > 0);
assertTrue(vertex.getStateTimestamp(ExecutionState.CANCELING) > 0);