Make TaskExecutor and TaskScheduler tests more robust

This commit is contained in:
Sam Brannen 2020-05-27 15:25:39 +02:00
parent c4ef002392
commit 763f7b9be8
7 changed files with 131 additions and 169 deletions

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2002-2019 the original author or authors. * Copyright 2002-2020 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -23,11 +23,13 @@ import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future; import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;
import org.awaitility.Awaitility; import org.awaitility.Awaitility;
import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.TestInfo;
import org.springframework.beans.factory.DisposableBean; import org.springframework.beans.factory.DisposableBean;
import org.springframework.core.task.AsyncListenableTaskExecutor; import org.springframework.core.task.AsyncListenableTaskExecutor;
@ -41,24 +43,27 @@ import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
* @author Sam Brannen * @author Sam Brannen
* @since 5.0.5 * @since 5.0.5
*/ */
public abstract class AbstractSchedulingTaskExecutorTests { abstract class AbstractSchedulingTaskExecutorTests {
static final String THREAD_NAME_PREFIX = "test-"; static final String THREAD_NAME_PREFIX = "test-";
private AsyncListenableTaskExecutor executor; private AsyncListenableTaskExecutor executor;
protected String testName;
private volatile Object outcome; private volatile Object outcome;
@BeforeEach @BeforeEach
public void initExecutor() { void setUp(TestInfo testInfo) {
executor = buildExecutor(); this.testName = testInfo.getTestMethod().get().getName();
this.executor = buildExecutor();
} }
protected abstract AsyncListenableTaskExecutor buildExecutor(); protected abstract AsyncListenableTaskExecutor buildExecutor();
@AfterEach @AfterEach
public void shutdownExecutor() throws Exception { void shutdownExecutor() throws Exception {
if (executor instanceof DisposableBean) { if (executor instanceof DisposableBean) {
((DisposableBean) executor).destroy(); ((DisposableBean) executor).destroy();
} }
@ -66,23 +71,28 @@ public abstract class AbstractSchedulingTaskExecutorTests {
@Test @Test
public void executeRunnable() { void executeRunnable() {
TestTask task = new TestTask(1); TestTask task = new TestTask(this.testName, 1);
executor.execute(task); executor.execute(task);
await(task); await(task);
assertThreadNamePrefix(task); assertThreadNamePrefix(task);
} }
@Test @Test
public void executeFailingRunnable() { void executeFailingRunnable() {
TestTask task = new TestTask(0); TestTask task = new TestTask(this.testName, 0);
executor.execute(task); executor.execute(task);
// nothing to assert Awaitility.await()
.dontCatchUncaughtExceptions()
.atMost(1, TimeUnit.SECONDS)
.pollInterval(10, TimeUnit.MILLISECONDS)
.until(() -> task.exception.get() != null && task.exception.get().getMessage().equals(
"TestTask failure for test 'executeFailingRunnable': expectedRunCount:<0>, actualRunCount:<1>"));
} }
@Test @Test
public void submitRunnable() throws Exception { void submitRunnable() throws Exception {
TestTask task = new TestTask(1); TestTask task = new TestTask(this.testName, 1);
Future<?> future = executor.submit(task); Future<?> future = executor.submit(task);
Object result = future.get(1000, TimeUnit.MILLISECONDS); Object result = future.get(1000, TimeUnit.MILLISECONDS);
assertThat(result).isNull(); assertThat(result).isNull();
@ -90,8 +100,8 @@ public abstract class AbstractSchedulingTaskExecutorTests {
} }
@Test @Test
public void submitFailingRunnable() throws Exception { void submitFailingRunnable() throws Exception {
TestTask task = new TestTask(0); TestTask task = new TestTask(this.testName, 0);
Future<?> future = executor.submit(task); Future<?> future = executor.submit(task);
assertThatExceptionOfType(ExecutionException.class).isThrownBy(() -> assertThatExceptionOfType(ExecutionException.class).isThrownBy(() ->
future.get(1000, TimeUnit.MILLISECONDS)); future.get(1000, TimeUnit.MILLISECONDS));
@ -99,9 +109,9 @@ public abstract class AbstractSchedulingTaskExecutorTests {
} }
@Test @Test
public void submitRunnableWithGetAfterShutdown() throws Exception { void submitRunnableWithGetAfterShutdown() throws Exception {
Future<?> future1 = executor.submit(new TestTask(-1)); Future<?> future1 = executor.submit(new TestTask(this.testName, -1));
Future<?> future2 = executor.submit(new TestTask(-1)); Future<?> future2 = executor.submit(new TestTask(this.testName, -1));
shutdownExecutor(); shutdownExecutor();
assertThatExceptionOfType(CancellationException.class).isThrownBy(() -> { assertThatExceptionOfType(CancellationException.class).isThrownBy(() -> {
future1.get(1000, TimeUnit.MILLISECONDS); future1.get(1000, TimeUnit.MILLISECONDS);
@ -110,8 +120,8 @@ public abstract class AbstractSchedulingTaskExecutorTests {
} }
@Test @Test
public void submitListenableRunnable() throws Exception { void submitListenableRunnable() throws Exception {
TestTask task = new TestTask(1); TestTask task = new TestTask(this.testName, 1);
// Act // Act
ListenableFuture<?> future = executor.submitListenable(task); ListenableFuture<?> future = executor.submitListenable(task);
future.addCallback(result -> outcome = result, ex -> outcome = ex); future.addCallback(result -> outcome = result, ex -> outcome = ex);
@ -125,8 +135,8 @@ public abstract class AbstractSchedulingTaskExecutorTests {
} }
@Test @Test
public void submitFailingListenableRunnable() throws Exception { void submitFailingListenableRunnable() throws Exception {
TestTask task = new TestTask(0); TestTask task = new TestTask(this.testName, 0);
ListenableFuture<?> future = executor.submitListenable(task); ListenableFuture<?> future = executor.submitListenable(task);
future.addCallback(result -> outcome = result, ex -> outcome = ex); future.addCallback(result -> outcome = result, ex -> outcome = ex);
@ -139,9 +149,9 @@ public abstract class AbstractSchedulingTaskExecutorTests {
} }
@Test @Test
public void submitListenableRunnableWithGetAfterShutdown() throws Exception { void submitListenableRunnableWithGetAfterShutdown() throws Exception {
ListenableFuture<?> future1 = executor.submitListenable(new TestTask(-1)); ListenableFuture<?> future1 = executor.submitListenable(new TestTask(this.testName, -1));
ListenableFuture<?> future2 = executor.submitListenable(new TestTask(-1)); ListenableFuture<?> future2 = executor.submitListenable(new TestTask(this.testName, -1));
shutdownExecutor(); shutdownExecutor();
try { try {
@ -159,16 +169,16 @@ public abstract class AbstractSchedulingTaskExecutorTests {
} }
@Test @Test
public void submitCallable() throws Exception { void submitCallable() throws Exception {
TestCallable task = new TestCallable(1); TestCallable task = new TestCallable(this.testName, 1);
Future<String> future = executor.submit(task); Future<String> future = executor.submit(task);
String result = future.get(1000, TimeUnit.MILLISECONDS); String result = future.get(1000, TimeUnit.MILLISECONDS);
assertThat(result.substring(0, THREAD_NAME_PREFIX.length())).isEqualTo(THREAD_NAME_PREFIX); assertThat(result.substring(0, THREAD_NAME_PREFIX.length())).isEqualTo(THREAD_NAME_PREFIX);
} }
@Test @Test
public void submitFailingCallable() throws Exception { void submitFailingCallable() throws Exception {
TestCallable task = new TestCallable(0); TestCallable task = new TestCallable(this.testName, 0);
Future<String> future = executor.submit(task); Future<String> future = executor.submit(task);
assertThatExceptionOfType(ExecutionException.class).isThrownBy(() -> assertThatExceptionOfType(ExecutionException.class).isThrownBy(() ->
future.get(1000, TimeUnit.MILLISECONDS)); future.get(1000, TimeUnit.MILLISECONDS));
@ -176,9 +186,9 @@ public abstract class AbstractSchedulingTaskExecutorTests {
} }
@Test @Test
public void submitCallableWithGetAfterShutdown() throws Exception { void submitCallableWithGetAfterShutdown() throws Exception {
Future<?> future1 = executor.submit(new TestCallable(-1)); Future<?> future1 = executor.submit(new TestCallable(this.testName, -1));
Future<?> future2 = executor.submit(new TestCallable(-1)); Future<?> future2 = executor.submit(new TestCallable(this.testName, -1));
shutdownExecutor(); shutdownExecutor();
try { try {
@ -196,8 +206,8 @@ public abstract class AbstractSchedulingTaskExecutorTests {
} }
@Test @Test
public void submitListenableCallable() throws Exception { void submitListenableCallable() throws Exception {
TestCallable task = new TestCallable(1); TestCallable task = new TestCallable(this.testName, 1);
// Act // Act
ListenableFuture<String> future = executor.submitListenable(task); ListenableFuture<String> future = executor.submitListenable(task);
future.addCallback(result -> outcome = result, ex -> outcome = ex); future.addCallback(result -> outcome = result, ex -> outcome = ex);
@ -210,8 +220,8 @@ public abstract class AbstractSchedulingTaskExecutorTests {
} }
@Test @Test
public void submitFailingListenableCallable() throws Exception { void submitFailingListenableCallable() throws Exception {
TestCallable task = new TestCallable(0); TestCallable task = new TestCallable(this.testName, 0);
// Act // Act
ListenableFuture<String> future = executor.submitListenable(task); ListenableFuture<String> future = executor.submitListenable(task);
future.addCallback(result -> outcome = result, ex -> outcome = ex); future.addCallback(result -> outcome = result, ex -> outcome = ex);
@ -225,9 +235,9 @@ public abstract class AbstractSchedulingTaskExecutorTests {
} }
@Test @Test
public void submitListenableCallableWithGetAfterShutdown() throws Exception { void submitListenableCallableWithGetAfterShutdown() throws Exception {
ListenableFuture<?> future1 = executor.submitListenable(new TestCallable(-1)); ListenableFuture<?> future1 = executor.submitListenable(new TestCallable(this.testName, -1));
ListenableFuture<?> future2 = executor.submitListenable(new TestCallable(-1)); ListenableFuture<?> future2 = executor.submitListenable(new TestCallable(this.testName, -1));
shutdownExecutor(); shutdownExecutor();
assertThatExceptionOfType(CancellationException.class).isThrownBy(() -> { assertThatExceptionOfType(CancellationException.class).isThrownBy(() -> {
future1.get(1000, TimeUnit.MILLISECONDS); future1.get(1000, TimeUnit.MILLISECONDS);
@ -255,17 +265,22 @@ public abstract class AbstractSchedulingTaskExecutorTests {
} }
private static class TestTask implements Runnable { static class TestTask implements Runnable {
private final int expectedRunCount; private final int expectedRunCount;
private final String testName;
private final AtomicInteger actualRunCount = new AtomicInteger(); private final AtomicInteger actualRunCount = new AtomicInteger();
private final CountDownLatch latch; private final AtomicReference<Exception> exception = new AtomicReference<>();
private Thread lastThread; final CountDownLatch latch;
TestTask(int expectedRunCount) { Thread lastThread;
TestTask(String testName, int expectedRunCount) {
this.testName = testName;
this.expectedRunCount = expectedRunCount; this.expectedRunCount = expectedRunCount;
this.latch = (expectedRunCount > 0 ? new CountDownLatch(expectedRunCount) : null); this.latch = (expectedRunCount > 0 ? new CountDownLatch(expectedRunCount) : null);
} }
@ -280,7 +295,10 @@ public abstract class AbstractSchedulingTaskExecutorTests {
} }
if (expectedRunCount >= 0) { if (expectedRunCount >= 0) {
if (actualRunCount.incrementAndGet() > expectedRunCount) { if (actualRunCount.incrementAndGet() > expectedRunCount) {
throw new RuntimeException("intentional test failure"); RuntimeException exception = new RuntimeException(String.format("%s failure for test '%s': expectedRunCount:<%d>, actualRunCount:<%d>",
getClass().getSimpleName(), this.testName, expectedRunCount, actualRunCount.get()));
this.exception.set(exception);
throw exception;
} }
latch.countDown(); latch.countDown();
} }
@ -288,13 +306,16 @@ public abstract class AbstractSchedulingTaskExecutorTests {
} }
private static class TestCallable implements Callable<String> { static class TestCallable implements Callable<String> {
private final String testName;
private final int expectedRunCount; private final int expectedRunCount;
private final AtomicInteger actualRunCount = new AtomicInteger(); private final AtomicInteger actualRunCount = new AtomicInteger();
TestCallable(int expectedRunCount) { TestCallable(String testName, int expectedRunCount) {
this.testName = testName;
this.expectedRunCount = expectedRunCount; this.expectedRunCount = expectedRunCount;
} }
@ -307,7 +328,8 @@ public abstract class AbstractSchedulingTaskExecutorTests {
} }
if (expectedRunCount >= 0) { if (expectedRunCount >= 0) {
if (actualRunCount.incrementAndGet() > expectedRunCount) { if (actualRunCount.incrementAndGet() > expectedRunCount) {
throw new RuntimeException("intentional test failure"); throw new RuntimeException(String.format("%s failure for test '%s': expectedRunCount:<%d>, actualRunCount:<%d>",
getClass().getSimpleName(), this.testName, expectedRunCount, actualRunCount.get()));
} }
} }
return Thread.currentThread().getName(); return Thread.currentThread().getName();

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2002-2019 the original author or authors. * Copyright 2002-2020 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -16,7 +16,6 @@
package org.springframework.scheduling.concurrent; package org.springframework.scheduling.concurrent;
import java.util.List;
import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.RunnableFuture; import java.util.concurrent.RunnableFuture;
import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.ThreadPoolExecutor;
@ -28,11 +27,13 @@ import org.junit.jupiter.api.Test;
import org.springframework.core.task.AsyncListenableTaskExecutor; import org.springframework.core.task.AsyncListenableTaskExecutor;
import org.springframework.core.task.NoOpRunnable; import org.springframework.core.task.NoOpRunnable;
import static org.assertj.core.api.Assertions.assertThatCode;
/** /**
* @author Rick Evans * @author Rick Evans
* @author Juergen Hoeller * @author Juergen Hoeller
*/ */
public class ConcurrentTaskExecutorTests extends AbstractSchedulingTaskExecutorTests { class ConcurrentTaskExecutorTests extends AbstractSchedulingTaskExecutorTests {
private final ThreadPoolExecutor concurrentExecutor = private final ThreadPoolExecutor concurrentExecutor =
new ThreadPoolExecutor(1, 1, 60, TimeUnit.SECONDS, new LinkedBlockingQueue<>()); new ThreadPoolExecutor(1, 1, 60, TimeUnit.SECONDS, new LinkedBlockingQueue<>());
@ -46,9 +47,8 @@ public class ConcurrentTaskExecutorTests extends AbstractSchedulingTaskExecutorT
@Override @Override
@AfterEach @AfterEach
public void shutdownExecutor() { void shutdownExecutor() {
List<Runnable> remainingTasks = concurrentExecutor.shutdownNow(); for (Runnable task : concurrentExecutor.shutdownNow()) {
for (Runnable task : remainingTasks) {
if (task instanceof RunnableFuture) { if (task instanceof RunnableFuture) {
((RunnableFuture<?>) task).cancel(true); ((RunnableFuture<?>) task).cancel(true);
} }
@ -57,25 +57,22 @@ public class ConcurrentTaskExecutorTests extends AbstractSchedulingTaskExecutorT
@Test @Test
public void zeroArgCtorResultsInDefaultTaskExecutorBeingUsed() { void zeroArgCtorResultsInDefaultTaskExecutorBeingUsed() {
ConcurrentTaskExecutor executor = new ConcurrentTaskExecutor(); ConcurrentTaskExecutor executor = new ConcurrentTaskExecutor();
// must not throw a NullPointerException assertThatCode(() -> executor.execute(new NoOpRunnable())).doesNotThrowAnyException();
executor.execute(new NoOpRunnable());
} }
@Test @Test
public void passingNullExecutorToCtorResultsInDefaultTaskExecutorBeingUsed() { void passingNullExecutorToCtorResultsInDefaultTaskExecutorBeingUsed() {
ConcurrentTaskExecutor executor = new ConcurrentTaskExecutor(null); ConcurrentTaskExecutor executor = new ConcurrentTaskExecutor(null);
// must not throw a NullPointerException assertThatCode(() -> executor.execute(new NoOpRunnable())).doesNotThrowAnyException();
executor.execute(new NoOpRunnable());
} }
@Test @Test
public void passingNullExecutorToSetterResultsInDefaultTaskExecutorBeingUsed() { void passingNullExecutorToSetterResultsInDefaultTaskExecutorBeingUsed() {
ConcurrentTaskExecutor executor = new ConcurrentTaskExecutor(); ConcurrentTaskExecutor executor = new ConcurrentTaskExecutor();
executor.setConcurrentExecutor(null); executor.setConcurrentExecutor(null);
// must not throw a NullPointerException assertThatCode(() -> executor.execute(new NoOpRunnable())).doesNotThrowAnyException();
executor.execute(new NoOpRunnable());
} }
} }

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2002-2018 the original author or authors. * Copyright 2002-2020 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -24,7 +24,7 @@ import org.springframework.scheduling.support.TaskUtils;
* @author Juergen Hoeller * @author Juergen Hoeller
* @since 5.0.5 * @since 5.0.5
*/ */
public class DecoratedThreadPoolTaskExecutorTests extends AbstractSchedulingTaskExecutorTests { class DecoratedThreadPoolTaskExecutorTests extends AbstractSchedulingTaskExecutorTests {
@Override @Override
protected AsyncListenableTaskExecutor buildExecutor() { protected AsyncListenableTaskExecutor buildExecutor() {

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2002-2019 the original author or authors. * Copyright 2002-2020 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -38,17 +38,17 @@ import static org.springframework.core.testfixture.TestGroup.PERFORMANCE;
* @author Rick Evans * @author Rick Evans
* @author Juergen Hoeller * @author Juergen Hoeller
*/ */
public class ScheduledExecutorFactoryBeanTests { class ScheduledExecutorFactoryBeanTests {
@Test @Test
public void testThrowsExceptionIfPoolSizeIsLessThanZero() throws Exception { void throwsExceptionIfPoolSizeIsLessThanZero() throws Exception {
ScheduledExecutorFactoryBean factory = new ScheduledExecutorFactoryBean(); ScheduledExecutorFactoryBean factory = new ScheduledExecutorFactoryBean();
assertThatIllegalArgumentException().isThrownBy(() -> factory.setPoolSize(-1)); assertThatIllegalArgumentException().isThrownBy(() -> factory.setPoolSize(-1));
} }
@Test @Test
@SuppressWarnings("serial") @SuppressWarnings("serial")
public void testShutdownNowIsPropagatedToTheExecutorOnDestroy() throws Exception { void shutdownNowIsPropagatedToTheExecutorOnDestroy() throws Exception {
final ScheduledExecutorService executor = mock(ScheduledExecutorService.class); final ScheduledExecutorService executor = mock(ScheduledExecutorService.class);
ScheduledExecutorFactoryBean factory = new ScheduledExecutorFactoryBean() { ScheduledExecutorFactoryBean factory = new ScheduledExecutorFactoryBean() {
@ -57,9 +57,7 @@ public class ScheduledExecutorFactoryBeanTests {
return executor; return executor;
} }
}; };
factory.setScheduledExecutorTasks(new ScheduledExecutorTask[]{ factory.setScheduledExecutorTasks(new NoOpScheduledExecutorTask());
new NoOpScheduledExecutorTask()
});
factory.afterPropertiesSet(); factory.afterPropertiesSet();
factory.destroy(); factory.destroy();
@ -68,7 +66,7 @@ public class ScheduledExecutorFactoryBeanTests {
@Test @Test
@SuppressWarnings("serial") @SuppressWarnings("serial")
public void testShutdownIsPropagatedToTheExecutorOnDestroy() throws Exception { void shutdownIsPropagatedToTheExecutorOnDestroy() throws Exception {
final ScheduledExecutorService executor = mock(ScheduledExecutorService.class); final ScheduledExecutorService executor = mock(ScheduledExecutorService.class);
ScheduledExecutorFactoryBean factory = new ScheduledExecutorFactoryBean() { ScheduledExecutorFactoryBean factory = new ScheduledExecutorFactoryBean() {
@ -77,9 +75,7 @@ public class ScheduledExecutorFactoryBeanTests {
return executor; return executor;
} }
}; };
factory.setScheduledExecutorTasks(new ScheduledExecutorTask[]{ factory.setScheduledExecutorTasks(new NoOpScheduledExecutorTask());
new NoOpScheduledExecutorTask()
});
factory.setWaitForTasksToCompleteOnShutdown(true); factory.setWaitForTasksToCompleteOnShutdown(true);
factory.afterPropertiesSet(); factory.afterPropertiesSet();
factory.destroy(); factory.destroy();
@ -89,13 +85,11 @@ public class ScheduledExecutorFactoryBeanTests {
@Test @Test
@EnabledForTestGroups(PERFORMANCE) @EnabledForTestGroups(PERFORMANCE)
public void testOneTimeExecutionIsSetUpAndFiresCorrectly() throws Exception { void oneTimeExecutionIsSetUpAndFiresCorrectly() throws Exception {
Runnable runnable = mock(Runnable.class); Runnable runnable = mock(Runnable.class);
ScheduledExecutorFactoryBean factory = new ScheduledExecutorFactoryBean(); ScheduledExecutorFactoryBean factory = new ScheduledExecutorFactoryBean();
factory.setScheduledExecutorTasks(new ScheduledExecutorTask[]{ factory.setScheduledExecutorTasks(new ScheduledExecutorTask(runnable));
new ScheduledExecutorTask(runnable)
});
factory.afterPropertiesSet(); factory.afterPropertiesSet();
pauseToLetTaskStart(1); pauseToLetTaskStart(1);
factory.destroy(); factory.destroy();
@ -105,7 +99,7 @@ public class ScheduledExecutorFactoryBeanTests {
@Test @Test
@EnabledForTestGroups(PERFORMANCE) @EnabledForTestGroups(PERFORMANCE)
public void testFixedRepeatedExecutionIsSetUpAndFiresCorrectly() throws Exception { void fixedRepeatedExecutionIsSetUpAndFiresCorrectly() throws Exception {
Runnable runnable = mock(Runnable.class); Runnable runnable = mock(Runnable.class);
ScheduledExecutorTask task = new ScheduledExecutorTask(runnable); ScheduledExecutorTask task = new ScheduledExecutorTask(runnable);
@ -113,7 +107,7 @@ public class ScheduledExecutorFactoryBeanTests {
task.setFixedRate(true); task.setFixedRate(true);
ScheduledExecutorFactoryBean factory = new ScheduledExecutorFactoryBean(); ScheduledExecutorFactoryBean factory = new ScheduledExecutorFactoryBean();
factory.setScheduledExecutorTasks(new ScheduledExecutorTask[]{task}); factory.setScheduledExecutorTasks(task);
factory.afterPropertiesSet(); factory.afterPropertiesSet();
pauseToLetTaskStart(2); pauseToLetTaskStart(2);
factory.destroy(); factory.destroy();
@ -123,7 +117,7 @@ public class ScheduledExecutorFactoryBeanTests {
@Test @Test
@EnabledForTestGroups(PERFORMANCE) @EnabledForTestGroups(PERFORMANCE)
public void testFixedRepeatedExecutionIsSetUpAndFiresCorrectlyAfterException() throws Exception { void fixedRepeatedExecutionIsSetUpAndFiresCorrectlyAfterException() throws Exception {
Runnable runnable = mock(Runnable.class); Runnable runnable = mock(Runnable.class);
willThrow(new IllegalStateException()).given(runnable).run(); willThrow(new IllegalStateException()).given(runnable).run();
@ -132,7 +126,7 @@ public class ScheduledExecutorFactoryBeanTests {
task.setFixedRate(true); task.setFixedRate(true);
ScheduledExecutorFactoryBean factory = new ScheduledExecutorFactoryBean(); ScheduledExecutorFactoryBean factory = new ScheduledExecutorFactoryBean();
factory.setScheduledExecutorTasks(new ScheduledExecutorTask[]{task}); factory.setScheduledExecutorTasks(task);
factory.setContinueScheduledExecutionAfterException(true); factory.setContinueScheduledExecutionAfterException(true);
factory.afterPropertiesSet(); factory.afterPropertiesSet();
pauseToLetTaskStart(2); pauseToLetTaskStart(2);
@ -143,7 +137,7 @@ public class ScheduledExecutorFactoryBeanTests {
@Test @Test
@EnabledForTestGroups(PERFORMANCE) @EnabledForTestGroups(PERFORMANCE)
public void testWithInitialDelayRepeatedExecutionIsSetUpAndFiresCorrectly() throws Exception { void withInitialDelayRepeatedExecutionIsSetUpAndFiresCorrectly() throws Exception {
Runnable runnable = mock(Runnable.class); Runnable runnable = mock(Runnable.class);
ScheduledExecutorTask task = new ScheduledExecutorTask(runnable); ScheduledExecutorTask task = new ScheduledExecutorTask(runnable);
@ -151,7 +145,7 @@ public class ScheduledExecutorFactoryBeanTests {
task.setDelay(3000); // nice long wait... task.setDelay(3000); // nice long wait...
ScheduledExecutorFactoryBean factory = new ScheduledExecutorFactoryBean(); ScheduledExecutorFactoryBean factory = new ScheduledExecutorFactoryBean();
factory.setScheduledExecutorTasks(new ScheduledExecutorTask[] {task}); factory.setScheduledExecutorTasks(task);
factory.afterPropertiesSet(); factory.afterPropertiesSet();
pauseToLetTaskStart(1); pauseToLetTaskStart(1);
// invoke destroy before tasks have even been scheduled... // invoke destroy before tasks have even been scheduled...
@ -163,7 +157,7 @@ public class ScheduledExecutorFactoryBeanTests {
@Test @Test
@EnabledForTestGroups(PERFORMANCE) @EnabledForTestGroups(PERFORMANCE)
public void testWithInitialDelayRepeatedExecutionIsSetUpAndFiresCorrectlyAfterException() throws Exception { void withInitialDelayRepeatedExecutionIsSetUpAndFiresCorrectlyAfterException() throws Exception {
Runnable runnable = mock(Runnable.class); Runnable runnable = mock(Runnable.class);
willThrow(new IllegalStateException()).given(runnable).run(); willThrow(new IllegalStateException()).given(runnable).run();
@ -172,7 +166,7 @@ public class ScheduledExecutorFactoryBeanTests {
task.setDelay(3000); // nice long wait... task.setDelay(3000); // nice long wait...
ScheduledExecutorFactoryBean factory = new ScheduledExecutorFactoryBean(); ScheduledExecutorFactoryBean factory = new ScheduledExecutorFactoryBean();
factory.setScheduledExecutorTasks(new ScheduledExecutorTask[] {task}); factory.setScheduledExecutorTasks(task);
factory.setContinueScheduledExecutionAfterException(true); factory.setContinueScheduledExecutionAfterException(true);
factory.afterPropertiesSet(); factory.afterPropertiesSet();
pauseToLetTaskStart(1); pauseToLetTaskStart(1);
@ -185,7 +179,7 @@ public class ScheduledExecutorFactoryBeanTests {
@Test @Test
@SuppressWarnings("serial") @SuppressWarnings("serial")
public void testSettingThreadFactoryToNullForcesUseOfDefaultButIsOtherwiseCool() throws Exception { void settingThreadFactoryToNullForcesUseOfDefaultButIsOtherwiseCool() throws Exception {
ScheduledExecutorFactoryBean factory = new ScheduledExecutorFactoryBean() { ScheduledExecutorFactoryBean factory = new ScheduledExecutorFactoryBean() {
@Override @Override
protected ScheduledExecutorService createExecutor(int poolSize, ThreadFactory threadFactory, RejectedExecutionHandler rejectedExecutionHandler) { protected ScheduledExecutorService createExecutor(int poolSize, ThreadFactory threadFactory, RejectedExecutionHandler rejectedExecutionHandler) {
@ -193,9 +187,7 @@ public class ScheduledExecutorFactoryBeanTests {
return super.createExecutor(poolSize, threadFactory, rejectedExecutionHandler); return super.createExecutor(poolSize, threadFactory, rejectedExecutionHandler);
} }
}; };
factory.setScheduledExecutorTasks(new ScheduledExecutorTask[]{ factory.setScheduledExecutorTasks(new NoOpScheduledExecutorTask());
new NoOpScheduledExecutorTask()
});
factory.setThreadFactory(null); // the null must not propagate factory.setThreadFactory(null); // the null must not propagate
factory.afterPropertiesSet(); factory.afterPropertiesSet();
factory.destroy(); factory.destroy();
@ -203,7 +195,7 @@ public class ScheduledExecutorFactoryBeanTests {
@Test @Test
@SuppressWarnings("serial") @SuppressWarnings("serial")
public void testSettingRejectedExecutionHandlerToNullForcesUseOfDefaultButIsOtherwiseCool() throws Exception { void settingRejectedExecutionHandlerToNullForcesUseOfDefaultButIsOtherwiseCool() throws Exception {
ScheduledExecutorFactoryBean factory = new ScheduledExecutorFactoryBean() { ScheduledExecutorFactoryBean factory = new ScheduledExecutorFactoryBean() {
@Override @Override
protected ScheduledExecutorService createExecutor(int poolSize, ThreadFactory threadFactory, RejectedExecutionHandler rejectedExecutionHandler) { protected ScheduledExecutorService createExecutor(int poolSize, ThreadFactory threadFactory, RejectedExecutionHandler rejectedExecutionHandler) {
@ -211,16 +203,14 @@ public class ScheduledExecutorFactoryBeanTests {
return super.createExecutor(poolSize, threadFactory, rejectedExecutionHandler); return super.createExecutor(poolSize, threadFactory, rejectedExecutionHandler);
} }
}; };
factory.setScheduledExecutorTasks(new ScheduledExecutorTask[]{ factory.setScheduledExecutorTasks(new NoOpScheduledExecutorTask());
new NoOpScheduledExecutorTask()
});
factory.setRejectedExecutionHandler(null); // the null must not propagate factory.setRejectedExecutionHandler(null); // the null must not propagate
factory.afterPropertiesSet(); factory.afterPropertiesSet();
factory.destroy(); factory.destroy();
} }
@Test @Test
public void testObjectTypeReportsCorrectType() throws Exception { void objectTypeReportsCorrectType() throws Exception {
ScheduledExecutorFactoryBean factory = new ScheduledExecutorFactoryBean(); ScheduledExecutorFactoryBean factory = new ScheduledExecutorFactoryBean();
assertThat(factory.getObjectType()).isEqualTo(ScheduledExecutorService.class); assertThat(factory.getObjectType()).isEqualTo(ScheduledExecutorService.class);
} }
@ -237,7 +227,7 @@ public class ScheduledExecutorFactoryBeanTests {
private static class NoOpScheduledExecutorTask extends ScheduledExecutorTask { private static class NoOpScheduledExecutorTask extends ScheduledExecutorTask {
public NoOpScheduledExecutorTask() { NoOpScheduledExecutorTask() {
super(new NoOpRunnable()); super(new NoOpRunnable());
} }
} }

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2002-2019 the original author or authors. * Copyright 2002-2020 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -31,10 +31,10 @@ import static org.assertj.core.api.Assertions.assertThat;
/** /**
* @author Juergen Hoeller * @author Juergen Hoeller
*/ */
public class ThreadPoolExecutorFactoryBeanTests { class ThreadPoolExecutorFactoryBeanTests {
@Test @Test
public void defaultExecutor() throws Exception { void defaultExecutor() throws Exception {
ConfigurableApplicationContext context = new AnnotationConfigApplicationContext(ExecutorConfig.class); ConfigurableApplicationContext context = new AnnotationConfigApplicationContext(ExecutorConfig.class);
ExecutorService executor = context.getBean(ExecutorService.class); ExecutorService executor = context.getBean(ExecutorService.class);
@ -46,10 +46,10 @@ public class ThreadPoolExecutorFactoryBeanTests {
@Configuration @Configuration
public static class ExecutorConfig { static class ExecutorConfig {
@Bean @Bean
public ThreadPoolExecutorFactoryBean executor() { ThreadPoolExecutorFactoryBean executor() {
return new ThreadPoolExecutorFactoryBean(); return new ThreadPoolExecutorFactoryBean();
} }

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2002-2018 the original author or authors. * Copyright 2002-2020 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -22,7 +22,7 @@ import org.springframework.core.task.AsyncListenableTaskExecutor;
* @author Juergen Hoeller * @author Juergen Hoeller
* @since 5.0.5 * @since 5.0.5
*/ */
public class ThreadPoolTaskExecutorTests extends AbstractSchedulingTaskExecutorTests { class ThreadPoolTaskExecutorTests extends AbstractSchedulingTaskExecutorTests {
@Override @Override
protected AsyncListenableTaskExecutor buildExecutor() { protected AsyncListenableTaskExecutor buildExecutor() {

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2002-2019 the original author or authors. * Copyright 2002-2020 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -17,7 +17,6 @@
package org.springframework.scheduling.concurrent; package org.springframework.scheduling.concurrent;
import java.util.Date; import java.util.Date;
import java.util.concurrent.Callable;
import java.util.concurrent.CountDownLatch; import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future; import java.util.concurrent.Future;
@ -37,6 +36,7 @@ import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
/** /**
* @author Mark Fisher * @author Mark Fisher
* @author Juergen Hoeller * @author Juergen Hoeller
* @author Sam Brannen
* @since 3.0 * @since 3.0
*/ */
public class ThreadPoolTaskSchedulerTests extends AbstractSchedulingTaskExecutorTests { public class ThreadPoolTaskSchedulerTests extends AbstractSchedulingTaskExecutorTests {
@ -53,8 +53,8 @@ public class ThreadPoolTaskSchedulerTests extends AbstractSchedulingTaskExecutor
@Test @Test
public void executeFailingRunnableWithErrorHandler() { void executeFailingRunnableWithErrorHandler() {
TestTask task = new TestTask(0); TestTask task = new TestTask(this.testName, 0);
TestErrorHandler errorHandler = new TestErrorHandler(1); TestErrorHandler errorHandler = new TestErrorHandler(1);
scheduler.setErrorHandler(errorHandler); scheduler.setErrorHandler(errorHandler);
scheduler.execute(task); scheduler.execute(task);
@ -63,8 +63,8 @@ public class ThreadPoolTaskSchedulerTests extends AbstractSchedulingTaskExecutor
} }
@Test @Test
public void submitFailingRunnableWithErrorHandler() throws Exception { void submitFailingRunnableWithErrorHandler() throws Exception {
TestTask task = new TestTask(0); TestTask task = new TestTask(this.testName, 0);
TestErrorHandler errorHandler = new TestErrorHandler(1); TestErrorHandler errorHandler = new TestErrorHandler(1);
scheduler.setErrorHandler(errorHandler); scheduler.setErrorHandler(errorHandler);
Future<?> future = scheduler.submit(task); Future<?> future = scheduler.submit(task);
@ -75,8 +75,8 @@ public class ThreadPoolTaskSchedulerTests extends AbstractSchedulingTaskExecutor
} }
@Test @Test
public void submitFailingCallableWithErrorHandler() throws Exception { void submitFailingCallableWithErrorHandler() throws Exception {
TestCallable task = new TestCallable(0); TestCallable task = new TestCallable(this.testName, 0);
TestErrorHandler errorHandler = new TestErrorHandler(1); TestErrorHandler errorHandler = new TestErrorHandler(1);
scheduler.setErrorHandler(errorHandler); scheduler.setErrorHandler(errorHandler);
Future<String> future = scheduler.submit(task); Future<String> future = scheduler.submit(task);
@ -87,8 +87,8 @@ public class ThreadPoolTaskSchedulerTests extends AbstractSchedulingTaskExecutor
} }
@Test @Test
public void scheduleOneTimeTask() throws Exception { void scheduleOneTimeTask() throws Exception {
TestTask task = new TestTask(1); TestTask task = new TestTask(this.testName, 1);
Future<?> future = scheduler.schedule(task, new Date()); Future<?> future = scheduler.schedule(task, new Date());
Object result = future.get(1000, TimeUnit.MILLISECONDS); Object result = future.get(1000, TimeUnit.MILLISECONDS);
assertThat(result).isNull(); assertThat(result).isNull();
@ -97,17 +97,16 @@ public class ThreadPoolTaskSchedulerTests extends AbstractSchedulingTaskExecutor
} }
@Test @Test
public void scheduleOneTimeFailingTaskWithoutErrorHandler() throws Exception { void scheduleOneTimeFailingTaskWithoutErrorHandler() throws Exception {
TestTask task = new TestTask(0); TestTask task = new TestTask(this.testName, 0);
Future<?> future = scheduler.schedule(task, new Date()); Future<?> future = scheduler.schedule(task, new Date());
assertThatExceptionOfType(ExecutionException.class).isThrownBy(() -> assertThatExceptionOfType(ExecutionException.class).isThrownBy(() -> future.get(1000, TimeUnit.MILLISECONDS));
future.get(1000, TimeUnit.MILLISECONDS));
assertThat(future.isDone()).isTrue(); assertThat(future.isDone()).isTrue();
} }
@Test @Test
public void scheduleOneTimeFailingTaskWithErrorHandler() throws Exception { void scheduleOneTimeFailingTaskWithErrorHandler() throws Exception {
TestTask task = new TestTask(0); TestTask task = new TestTask(this.testName, 0);
TestErrorHandler errorHandler = new TestErrorHandler(1); TestErrorHandler errorHandler = new TestErrorHandler(1);
scheduler.setErrorHandler(errorHandler); scheduler.setErrorHandler(errorHandler);
Future<?> future = scheduler.schedule(task, new Date()); Future<?> future = scheduler.schedule(task, new Date());
@ -118,8 +117,8 @@ public class ThreadPoolTaskSchedulerTests extends AbstractSchedulingTaskExecutor
} }
@Test @Test
public void scheduleTriggerTask() throws Exception { void scheduleTriggerTask() throws Exception {
TestTask task = new TestTask(3); TestTask task = new TestTask(this.testName, 3);
Future<?> future = scheduler.schedule(task, new TestTrigger(3)); Future<?> future = scheduler.schedule(task, new TestTrigger(3));
Object result = future.get(1000, TimeUnit.MILLISECONDS); Object result = future.get(1000, TimeUnit.MILLISECONDS);
assertThat(result).isNull(); assertThat(result).isNull();
@ -128,8 +127,8 @@ public class ThreadPoolTaskSchedulerTests extends AbstractSchedulingTaskExecutor
} }
@Test @Test
public void scheduleMultipleTriggerTasks() throws Exception { void scheduleMultipleTriggerTasks() throws Exception {
for (int i = 0; i < 1000; i++) { for (int i = 0; i < 100; i++) {
scheduleTriggerTask(); scheduleTriggerTask();
} }
} }
@ -158,52 +157,6 @@ public class ThreadPoolTaskSchedulerTests extends AbstractSchedulingTaskExecutor
} }
private static class TestTask implements Runnable {
private final int expectedRunCount;
private final AtomicInteger actualRunCount = new AtomicInteger();
private final CountDownLatch latch;
private Thread lastThread;
TestTask(int expectedRunCount) {
this.expectedRunCount = expectedRunCount;
this.latch = new CountDownLatch(expectedRunCount);
}
@Override
public void run() {
lastThread = Thread.currentThread();
if (actualRunCount.incrementAndGet() > expectedRunCount) {
throw new RuntimeException("intentional test failure");
}
latch.countDown();
}
}
private static class TestCallable implements Callable<String> {
private final int expectedRunCount;
private final AtomicInteger actualRunCount = new AtomicInteger();
TestCallable(int expectedRunCount) {
this.expectedRunCount = expectedRunCount;
}
@Override
public String call() throws Exception {
if (actualRunCount.incrementAndGet() > expectedRunCount) {
throw new RuntimeException("intentional test failure");
}
return Thread.currentThread().getName();
}
}
private static class TestErrorHandler implements ErrorHandler { private static class TestErrorHandler implements ErrorHandler {
private final CountDownLatch latch; private final CountDownLatch latch;