SO...
I have this utility class that performs retries (to anything, theoretically) after the initial call has not returned after a given amount of time, and continually does so until any source returns data or until all are exhausted. This uses ExecutorCompletionService
's .poll
method to trigger when to retry. See the following code for the utility class...
final RetrySources[] retrySources = getRetrySources(originalSource);
Future<T> resultFuture = null;
final List<Future<T>> futures = new ArrayList<>(retrySources.length);
for (int tryIndex = 0; tryIndex < retrySources.length && resultFuture == null; tryIndex++) {
final int tryIndexCopy = tryIndex;
futures.add(ecs.submit(() -> client.call(retrySources[tryIndexCopy], tryIndexCopy)));
resultFuture = ecs.poll(millisBeforeRetry, TimeUnit.MILLISECONDS);
}
if (resultFuture == null) {
resultFuture = ecs.take();
}
return resultFuture.get();
...my problem is that I am re-writing the tests to not use sleep, but instead to use CountDownLatch
. See one of my tests below...
@Test
public void call_firstRetryFinishesAfterLimitButBeforeSecondRetryDoes_triggersSecondRetryButUsesFirstResult() throws Exception {
final String readResult1 = "a";
final String readResult2 = "b";
final CountDownLatch signal1 = new CountDownLatch(1);
final CountDownLatch signal2 = new CountDownLatch(1);
expect(mockReadOperation.call(readOptions[0], 0)).andStubAnswer(() -> {
signal1.await(); // This causes the test to spin forever
// Thread.sleep(1000); // Swapping the line above for this, makes it work
return readResult1;
});
expect(mockReadOperation.call(readOptions[1], 1)).andStubAnswer(() -> {
signal1.countDown();
signal2.await(); // For this test case, make the second retry never return
return readResult2;
});
replay(mockReadOperation);
final ReadOption readOption = ReadOption.primary();
final String result = subject.call(readOption);
assertThat(result).isEqualTo("a");
}
...and note that my ExecutorCompletionService
is defined...
private final ExecutorCompletionService executorCompletionService = new ExecutorCompletionService(Executors.newFixedThreadPool(2));
...since my test is running in the main thread, and each call is running it it's own thread as part of the ExecutorCompletionService
pool, I don't understand why having the signal1.await();
causes the test to spin forever and note the comment, that switching that single line for a sleep, causes the test to pass.
Any help is much appreciated.
That's probably due to replay(mockReadOperation);
where you are activating(executing) the mocks. ??
Note: I have never used easy-mock, but a quick google reveals that replay
activates the mocks and this seems to fit the answer, I may be wrong.
If I am right, and you always have to replay
mocks in easymock, then maybe solution is, switch to Mockito :p!