|
|
|
@ -14,6 +14,7 @@ package tech.pegasys.pantheon.services.pipeline; |
|
|
|
|
|
|
|
|
|
import static java.util.concurrent.CompletableFuture.completedFuture; |
|
|
|
|
import static org.assertj.core.api.Assertions.assertThat; |
|
|
|
|
import static org.assertj.core.api.Assertions.assertThatThrownBy; |
|
|
|
|
import static org.mockito.Mockito.mock; |
|
|
|
|
import static org.mockito.Mockito.times; |
|
|
|
|
import static org.mockito.Mockito.verify; |
|
|
|
@ -33,11 +34,10 @@ public class AsyncOperationProcessorTest { |
|
|
|
|
@SuppressWarnings("unchecked") |
|
|
|
|
private final WritePipe<String> writePipe = mock(WritePipe.class); |
|
|
|
|
|
|
|
|
|
private final AsyncOperationProcessor<CompletableFuture<String>, String> processor = |
|
|
|
|
new AsyncOperationProcessor<>(Function.identity(), 3); |
|
|
|
|
|
|
|
|
|
@Test |
|
|
|
|
public void shouldImmediatelyOutputTasksThatAreAlreadyCompleteEvenIfOutputPipeIsFull() { |
|
|
|
|
final AsyncOperationProcessor<CompletableFuture<String>, String> processor = |
|
|
|
|
createProcessor(false); |
|
|
|
|
when(writePipe.hasRemainingCapacity()).thenReturn(false); |
|
|
|
|
when(readPipe.get()).thenReturn(completedFuture("a")); |
|
|
|
|
|
|
|
|
@ -47,7 +47,8 @@ public class AsyncOperationProcessorTest { |
|
|
|
|
|
|
|
|
|
@Test |
|
|
|
|
public void shouldNotExceedConcurrentJobLimit() { |
|
|
|
|
|
|
|
|
|
final AsyncOperationProcessor<CompletableFuture<String>, String> processor = |
|
|
|
|
createProcessor(false); |
|
|
|
|
final CompletableFuture<String> task1 = new CompletableFuture<>(); |
|
|
|
|
final CompletableFuture<String> task2 = new CompletableFuture<>(); |
|
|
|
|
final CompletableFuture<String> task3 = new CompletableFuture<>(); |
|
|
|
@ -77,6 +78,8 @@ public class AsyncOperationProcessorTest { |
|
|
|
|
|
|
|
|
|
@Test |
|
|
|
|
public void shouldOutputRemainingInProgressTasksWhenFinalizing() { |
|
|
|
|
final AsyncOperationProcessor<CompletableFuture<String>, String> processor = |
|
|
|
|
createProcessor(false); |
|
|
|
|
final CompletableFuture<String> task1 = new CompletableFuture<>(); |
|
|
|
|
final CompletableFuture<String> task2 = new CompletableFuture<>(); |
|
|
|
|
when(readPipe.get()).thenReturn(task1).thenReturn(task2); |
|
|
|
@ -98,6 +101,8 @@ public class AsyncOperationProcessorTest { |
|
|
|
|
|
|
|
|
|
@Test |
|
|
|
|
public void shouldCancelInProgressTasksWhenAborted() { |
|
|
|
|
final AsyncOperationProcessor<CompletableFuture<String>, String> processor = |
|
|
|
|
createProcessor(false); |
|
|
|
|
final CompletableFuture<String> task1 = new CompletableFuture<>(); |
|
|
|
|
final CompletableFuture<String> task2 = new CompletableFuture<>(); |
|
|
|
|
when(readPipe.get()).thenReturn(task1).thenReturn(task2); |
|
|
|
@ -114,6 +119,8 @@ public class AsyncOperationProcessorTest { |
|
|
|
|
@Test |
|
|
|
|
public void shouldInterruptThreadWhenFutureCompletes() { |
|
|
|
|
// Ensures that if we're waiting for the next input we wake up and output completed tasks
|
|
|
|
|
final AsyncOperationProcessor<CompletableFuture<String>, String> processor = |
|
|
|
|
createProcessor(false); |
|
|
|
|
|
|
|
|
|
final CompletableFuture<String> task1 = new CompletableFuture<>(); |
|
|
|
|
when(readPipe.get()).thenReturn(task1); |
|
|
|
@ -125,4 +132,71 @@ public class AsyncOperationProcessorTest { |
|
|
|
|
|
|
|
|
|
assertThat(Thread.currentThread().isInterrupted()).isTrue(); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
@Test |
|
|
|
|
public void shouldPreserveOrderWhenRequested() { |
|
|
|
|
final AsyncOperationProcessor<CompletableFuture<String>, String> processor = |
|
|
|
|
createProcessor(true); |
|
|
|
|
final CompletableFuture<String> task1 = new CompletableFuture<>(); |
|
|
|
|
final CompletableFuture<String> task2 = new CompletableFuture<>(); |
|
|
|
|
final CompletableFuture<String> task3 = new CompletableFuture<>(); |
|
|
|
|
final CompletableFuture<String> task4 = new CompletableFuture<>(); |
|
|
|
|
when(readPipe.get()).thenReturn(task1).thenReturn(task2).thenReturn(task3).thenReturn(task4); |
|
|
|
|
|
|
|
|
|
// 3 tasks started
|
|
|
|
|
processor.processNextInput(readPipe, writePipe); |
|
|
|
|
processor.processNextInput(readPipe, writePipe); |
|
|
|
|
processor.processNextInput(readPipe, writePipe); |
|
|
|
|
verify(readPipe, times(3)).get(); |
|
|
|
|
|
|
|
|
|
// Reached limit of concurrent tasks so this round does nothing.
|
|
|
|
|
processor.processNextInput(readPipe, writePipe); |
|
|
|
|
verify(readPipe, times(3)).get(); |
|
|
|
|
|
|
|
|
|
// Second task completes but shouldn't be output because task1 is not complete yet
|
|
|
|
|
task2.complete("b"); |
|
|
|
|
|
|
|
|
|
processor.processNextInput(readPipe, writePipe); |
|
|
|
|
verify(readPipe, times(3)).get(); |
|
|
|
|
verifyZeroInteractions(writePipe); |
|
|
|
|
|
|
|
|
|
task1.complete("a"); |
|
|
|
|
|
|
|
|
|
// Next round will output the two completed tasks in order
|
|
|
|
|
processor.processNextInput(readPipe, writePipe); |
|
|
|
|
verify(writePipe).put("a"); |
|
|
|
|
verify(writePipe).put("b"); |
|
|
|
|
|
|
|
|
|
// And so now we are able to start another one.
|
|
|
|
|
processor.processNextInput(readPipe, writePipe); |
|
|
|
|
verify(readPipe, times(4)).get(); |
|
|
|
|
|
|
|
|
|
// And should finalize in order
|
|
|
|
|
task4.complete("d"); |
|
|
|
|
task3.complete("c"); |
|
|
|
|
processor.finalize(writePipe); |
|
|
|
|
verify(writePipe).put("c"); |
|
|
|
|
verify(writePipe).put("d"); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
@Test |
|
|
|
|
public void shouldThrowExceptionWhenFutureCompletesExceptionally() { |
|
|
|
|
final AsyncOperationProcessor<CompletableFuture<String>, String> processor = |
|
|
|
|
createProcessor(false); |
|
|
|
|
final CompletableFuture<String> task1 = new CompletableFuture<>(); |
|
|
|
|
when(readPipe.get()).thenReturn(task1); |
|
|
|
|
|
|
|
|
|
processor.processNextInput(readPipe, writePipe); |
|
|
|
|
|
|
|
|
|
final Exception exception = new IndexOutOfBoundsException("Oh dear"); |
|
|
|
|
task1.completeExceptionally(exception); |
|
|
|
|
|
|
|
|
|
assertThatThrownBy(() -> processor.processNextInput(readPipe, writePipe)) |
|
|
|
|
.hasRootCause(exception); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
private AsyncOperationProcessor<CompletableFuture<String>, String> createProcessor( |
|
|
|
|
final boolean preserveOrder) { |
|
|
|
|
return new AsyncOperationProcessor<>(Function.identity(), 3, preserveOrder); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|