Add execution metadata to tasks and scheduled tasks

This commit adds new information about the execution and scheduling of
tasks.

The `Task` type now exposes the `TaskExecutionOutcome` of the latest
execution; this includes the instant the execution started, the
execution outcome and any thrown exception.

The `ScheduledTask` contract can now provide the time when the next
execution is scheduled.

Closes gh-24560
This commit is contained in:
Brian Clozel
2024-06-11 19:29:21 +02:00
parent 46dccd8f97
commit dc2c8d6094
8 changed files with 494 additions and 100 deletions

View File

@@ -21,7 +21,6 @@ import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.lang.reflect.Method;
import java.time.Duration;
import java.time.Instant;
import java.time.ZoneId;
@@ -32,6 +31,7 @@ import java.util.Map;
import java.util.Properties;
import java.util.concurrent.TimeUnit;
import org.assertj.core.api.AbstractAssert;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ParameterContext;
@@ -116,11 +116,7 @@ class ScheduledAnnotationBeanPostProcessorTests {
new DirectFieldAccessor(registrar).getPropertyValue("fixedDelayTasks");
assertThat(fixedDelayTasks).hasSize(1);
IntervalTask task = fixedDelayTasks.get(0);
ScheduledMethodRunnable runnable = (ScheduledMethodRunnable) task.getRunnable();
Object targetObject = runnable.getTarget();
Method targetMethod = runnable.getMethod();
assertThat(targetObject).isEqualTo(target);
assertThat(targetMethod.getName()).isEqualTo("fixedDelay");
assertThatScheduledRunnable(task.getRunnable()).hasTarget(target).hasMethodName("fixedDelay");
assertThat(task.getInitialDelayDuration()).isZero();
assertThat(task.getIntervalDuration()).isEqualTo(
Duration.ofMillis(expectedInterval < 0 ? Long.MAX_VALUE : expectedInterval));
@@ -150,11 +146,7 @@ class ScheduledAnnotationBeanPostProcessorTests {
new DirectFieldAccessor(registrar).getPropertyValue("fixedRateTasks");
assertThat(fixedRateTasks).hasSize(1);
IntervalTask task = fixedRateTasks.get(0);
ScheduledMethodRunnable runnable = (ScheduledMethodRunnable) task.getRunnable();
Object targetObject = runnable.getTarget();
Method targetMethod = runnable.getMethod();
assertThat(targetObject).isEqualTo(target);
assertThat(targetMethod.getName()).isEqualTo("fixedRate");
assertThatScheduledRunnable(task.getRunnable()).hasTarget(target).hasMethodName("fixedRate");
assertSoftly(softly -> {
softly.assertThat(task.getInitialDelayDuration()).as("initial delay").isZero();
softly.assertThat(task.getIntervalDuration()).as("interval").isEqualTo(Duration.ofMillis(expectedInterval));
@@ -185,11 +177,7 @@ class ScheduledAnnotationBeanPostProcessorTests {
new DirectFieldAccessor(registrar).getPropertyValue("fixedRateTasks");
assertThat(fixedRateTasks).hasSize(1);
IntervalTask task = fixedRateTasks.get(0);
ScheduledMethodRunnable runnable = (ScheduledMethodRunnable) task.getRunnable();
Object targetObject = runnable.getTarget();
Method targetMethod = runnable.getMethod();
assertThat(targetObject).isEqualTo(target);
assertThat(targetMethod.getName()).isEqualTo("fixedRate");
assertThatScheduledRunnable(task.getRunnable()).hasTarget(target).hasMethodName("fixedRate");
assertSoftly(softly -> {
softly.assertThat(task.getInitialDelayDuration()).as("initial delay").isEqualTo(Duration.ofMillis(expectedInitialDelay));
softly.assertThat(task.getIntervalDuration()).as("interval").isEqualTo(Duration.ofMillis(expectedInterval));
@@ -250,19 +238,11 @@ class ScheduledAnnotationBeanPostProcessorTests {
new DirectFieldAccessor(registrar).getPropertyValue("fixedRateTasks");
assertThat(fixedRateTasks).hasSize(2);
IntervalTask task1 = fixedRateTasks.get(0);
ScheduledMethodRunnable runnable1 = (ScheduledMethodRunnable) task1.getRunnable();
Object targetObject = runnable1.getTarget();
Method targetMethod = runnable1.getMethod();
assertThat(targetObject).isEqualTo(target);
assertThat(targetMethod.getName()).isEqualTo("fixedRate");
assertThatScheduledRunnable(task1.getRunnable()).hasTarget(target).hasMethodName("fixedRate");
assertThat(task1.getInitialDelayDuration()).isZero();
assertThat(task1.getIntervalDuration()).isEqualTo(Duration.ofMillis(4_000L));
IntervalTask task2 = fixedRateTasks.get(1);
ScheduledMethodRunnable runnable2 = (ScheduledMethodRunnable) task2.getRunnable();
targetObject = runnable2.getTarget();
targetMethod = runnable2.getMethod();
assertThat(targetObject).isEqualTo(target);
assertThat(targetMethod.getName()).isEqualTo("fixedRate");
assertThatScheduledRunnable(task2.getRunnable()).hasTarget(target).hasMethodName("fixedRate");
assertThat(task2.getInitialDelayDuration()).isEqualTo(Duration.ofMillis(2_000L));
assertThat(task2.getIntervalDuration()).isEqualTo(Duration.ofMillis(4_000L));
}
@@ -286,11 +266,7 @@ class ScheduledAnnotationBeanPostProcessorTests {
new DirectFieldAccessor(registrar).getPropertyValue("oneTimeTasks");
assertThat(oneTimeTasks).hasSize(1);
OneTimeTask task = oneTimeTasks.get(0);
ScheduledMethodRunnable runnable = (ScheduledMethodRunnable) task.getRunnable();
Object targetObject = runnable.getTarget();
Method targetMethod = runnable.getMethod();
assertThat(targetObject).isEqualTo(target);
assertThat(targetMethod.getName()).isEqualTo("oneTimeTask");
assertThatScheduledRunnable(task.getRunnable()).hasTarget(target).hasMethodName("oneTimeTask");
assertThat(task.getInitialDelayDuration()).isEqualTo(Duration.ofMillis(2_000L));
}
@@ -313,11 +289,7 @@ class ScheduledAnnotationBeanPostProcessorTests {
new DirectFieldAccessor(registrar).getPropertyValue("cronTasks");
assertThat(cronTasks).hasSize(1);
CronTask task = cronTasks.get(0);
ScheduledMethodRunnable runnable = (ScheduledMethodRunnable) task.getRunnable();
Object targetObject = runnable.getTarget();
Method targetMethod = runnable.getMethod();
assertThat(targetObject).isEqualTo(target);
assertThat(targetMethod.getName()).isEqualTo("cron");
assertThatScheduledRunnable(task.getRunnable()).hasTarget(target).hasMethodName("cron");
assertThat(task.getExpression()).isEqualTo("*/7 * * * * ?");
}
@@ -340,11 +312,7 @@ class ScheduledAnnotationBeanPostProcessorTests {
new DirectFieldAccessor(registrar).getPropertyValue("cronTasks");
assertThat(cronTasks).hasSize(1);
CronTask task = cronTasks.get(0);
ScheduledMethodRunnable runnable = (ScheduledMethodRunnable) task.getRunnable();
Object targetObject = runnable.getTarget();
Method targetMethod = runnable.getMethod();
assertThat(targetObject).isEqualTo(target);
assertThat(targetMethod.getName()).isEqualTo("cron");
assertThatScheduledRunnable(task.getRunnable()).hasTarget(target).hasMethodName("cron");
assertThat(task.getExpression()).isEqualTo("0 0 0-4,6-23 * * ?");
Trigger trigger = task.getTrigger();
assertThat(trigger).isNotNull();
@@ -404,11 +372,9 @@ class ScheduledAnnotationBeanPostProcessorTests {
new DirectFieldAccessor(registrar).getPropertyValue("cronTasks");
assertThat(cronTasks).hasSize(1);
CronTask task = cronTasks.get(0);
ScheduledMethodRunnable runnable = (ScheduledMethodRunnable) task.getRunnable();
Object targetObject = runnable.getTarget();
Method targetMethod = runnable.getMethod();
assertThat(targetObject).isEqualTo(context.getBean(ScopedProxyUtils.getTargetBeanName("target")));
assertThat(targetMethod.getName()).isEqualTo("cron");
assertThatScheduledRunnable(task.getRunnable())
.hasTarget(context.getBean(ScopedProxyUtils.getTargetBeanName("target")))
.hasMethodName("cron");
assertThat(task.getExpression()).isEqualTo("*/7 * * * * ?");
}
@@ -431,11 +397,7 @@ class ScheduledAnnotationBeanPostProcessorTests {
new DirectFieldAccessor(registrar).getPropertyValue("fixedRateTasks");
assertThat(fixedRateTasks).hasSize(1);
IntervalTask task = fixedRateTasks.get(0);
ScheduledMethodRunnable runnable = (ScheduledMethodRunnable) task.getRunnable();
Object targetObject = runnable.getTarget();
Method targetMethod = runnable.getMethod();
assertThat(targetObject).isEqualTo(target);
assertThat(targetMethod.getName()).isEqualTo("checkForUpdates");
assertThatScheduledRunnable(task.getRunnable()).hasTarget(target).hasMethodName("checkForUpdates");
assertThat(task.getIntervalDuration()).isEqualTo(Duration.ofMillis(5_000L));
}
@@ -458,11 +420,7 @@ class ScheduledAnnotationBeanPostProcessorTests {
new DirectFieldAccessor(registrar).getPropertyValue("fixedRateTasks");
assertThat(fixedRateTasks).hasSize(1);
IntervalTask task = fixedRateTasks.get(0);
ScheduledMethodRunnable runnable = (ScheduledMethodRunnable) task.getRunnable();
Object targetObject = runnable.getTarget();
Method targetMethod = runnable.getMethod();
assertThat(targetObject).isEqualTo(target);
assertThat(targetMethod.getName()).isEqualTo("checkForUpdates");
assertThatScheduledRunnable(task.getRunnable()).hasTarget(target).hasMethodName("checkForUpdates");
assertThat(task.getIntervalDuration()).isEqualTo(Duration.ofMillis(5_000L));
assertThat(task.getInitialDelayDuration()).isEqualTo(Duration.ofMillis(1_000L));
}
@@ -486,11 +444,7 @@ class ScheduledAnnotationBeanPostProcessorTests {
new DirectFieldAccessor(registrar).getPropertyValue("cronTasks");
assertThat(cronTasks).hasSize(1);
CronTask task = cronTasks.get(0);
ScheduledMethodRunnable runnable = (ScheduledMethodRunnable) task.getRunnable();
Object targetObject = runnable.getTarget();
Method targetMethod = runnable.getMethod();
assertThat(targetObject).isEqualTo(target);
assertThat(targetMethod.getName()).isEqualTo("generateReport");
assertThatScheduledRunnable(task.getRunnable()).hasTarget(target).hasMethodName("generateReport");
assertThat(task.getExpression()).isEqualTo("0 0 * * * ?");
}
@@ -519,11 +473,7 @@ class ScheduledAnnotationBeanPostProcessorTests {
new DirectFieldAccessor(registrar).getPropertyValue("cronTasks");
assertThat(cronTasks).hasSize(1);
CronTask task = cronTasks.get(0);
ScheduledMethodRunnable runnable = (ScheduledMethodRunnable) task.getRunnable();
Object targetObject = runnable.getTarget();
Method targetMethod = runnable.getMethod();
assertThat(targetObject).isEqualTo(target);
assertThat(targetMethod.getName()).isEqualTo("x");
assertThatScheduledRunnable(task.getRunnable()).hasTarget(target).hasMethodName("x");
assertThat(task.getExpression()).isEqualTo(businessHoursCronExpression);
}
@@ -578,11 +528,7 @@ class ScheduledAnnotationBeanPostProcessorTests {
new DirectFieldAccessor(registrar).getPropertyValue("fixedDelayTasks");
assertThat(fixedDelayTasks).hasSize(1);
IntervalTask task = fixedDelayTasks.get(0);
ScheduledMethodRunnable runnable = (ScheduledMethodRunnable) task.getRunnable();
Object targetObject = runnable.getTarget();
Method targetMethod = runnable.getMethod();
assertThat(targetObject).isEqualTo(target);
assertThat(targetMethod.getName()).isEqualTo("fixedDelay");
assertThatScheduledRunnable(task.getRunnable()).hasTarget(target).hasMethodName("fixedDelay");
assertSoftly(softly -> {
softly.assertThat(task.getInitialDelayDuration()).as("initial delay").isEqualTo(Duration.ofMillis(expectedInitialDelay));
softly.assertThat(task.getIntervalDuration()).as("interval").isEqualTo(Duration.ofMillis(expectedInterval));
@@ -622,11 +568,7 @@ class ScheduledAnnotationBeanPostProcessorTests {
new DirectFieldAccessor(registrar).getPropertyValue("fixedRateTasks");
assertThat(fixedRateTasks).hasSize(1);
IntervalTask task = fixedRateTasks.get(0);
ScheduledMethodRunnable runnable = (ScheduledMethodRunnable) task.getRunnable();
Object targetObject = runnable.getTarget();
Method targetMethod = runnable.getMethod();
assertThat(targetObject).isEqualTo(target);
assertThat(targetMethod.getName()).isEqualTo("fixedRate");
assertThatScheduledRunnable(task.getRunnable()).hasTarget(target).hasMethodName("fixedRate");
assertSoftly(softly -> {
softly.assertThat(task.getInitialDelayDuration()).as("initial delay").isEqualTo(Duration.ofMillis(expectedInitialDelay));
softly.assertThat(task.getIntervalDuration()).as("interval").isEqualTo(Duration.ofMillis(expectedInterval));
@@ -656,11 +598,7 @@ class ScheduledAnnotationBeanPostProcessorTests {
new DirectFieldAccessor(registrar).getPropertyValue("cronTasks");
assertThat(cronTasks).hasSize(1);
CronTask task = cronTasks.get(0);
ScheduledMethodRunnable runnable = (ScheduledMethodRunnable) task.getRunnable();
Object targetObject = runnable.getTarget();
Method targetMethod = runnable.getMethod();
assertThat(targetObject).isEqualTo(target);
assertThat(targetMethod.getName()).isEqualTo("x");
assertThatScheduledRunnable(task.getRunnable()).hasTarget(target).hasMethodName("x");
assertThat(task.getExpression()).isEqualTo(businessHoursCronExpression);
}
@@ -689,11 +627,7 @@ class ScheduledAnnotationBeanPostProcessorTests {
new DirectFieldAccessor(registrar).getPropertyValue("cronTasks");
assertThat(cronTasks).hasSize(1);
CronTask task = cronTasks.get(0);
ScheduledMethodRunnable runnable = (ScheduledMethodRunnable) task.getRunnable();
Object targetObject = runnable.getTarget();
Method targetMethod = runnable.getMethod();
assertThat(targetObject).isEqualTo(target);
assertThat(targetMethod.getName()).isEqualTo("y");
assertThatScheduledRunnable(task.getRunnable()).hasTarget(target).hasMethodName("y");
assertThat(task.getExpression()).isEqualTo(businessHoursCronExpression);
}
@@ -716,11 +650,7 @@ class ScheduledAnnotationBeanPostProcessorTests {
new DirectFieldAccessor(registrar).getPropertyValue("cronTasks");
assertThat(cronTasks).hasSize(1);
CronTask task = cronTasks.get(0);
ScheduledMethodRunnable runnable = (ScheduledMethodRunnable) task.getRunnable();
Object targetObject = runnable.getTarget();
Method targetMethod = runnable.getMethod();
assertThat(targetObject).isEqualTo(target);
assertThat(targetMethod.getName()).isEqualTo("cron");
assertThatScheduledRunnable(task.getRunnable()).hasTarget(target).hasMethodName("cron");
assertThat(task.getExpression()).isEqualTo("0 0 9-17 * * MON-FRI");
}
@@ -1088,4 +1018,29 @@ class ScheduledAnnotationBeanPostProcessorTests {
}
}
static ScheduledMethodRunnableAssert assertThatScheduledRunnable(Runnable runnable) {
return new ScheduledMethodRunnableAssert(runnable);
}
static class ScheduledMethodRunnableAssert extends AbstractAssert<ScheduledMethodRunnableAssert, Runnable> {
public ScheduledMethodRunnableAssert(Runnable actual) {
super(actual, ScheduledMethodRunnableAssert.class);
assertThat(actual).extracting("runnable").isInstanceOf(ScheduledMethodRunnable.class);
}
public ScheduledMethodRunnableAssert hasTarget(Object target) {
isNotNull();
assertThat(actual).extracting("runnable.target").isEqualTo(target);
return this;
}
public ScheduledMethodRunnableAssert hasMethodName(String name) {
isNotNull();
assertThat(actual).extracting("runnable.method.name").isEqualTo(name);
return this;
}
}
}

View File

@@ -0,0 +1,108 @@
/*
* Copyright 2002-2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.scheduling.config;
import java.time.Duration;
import java.time.Instant;
import org.awaitility.Awaitility;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.scheduling.concurrent.SimpleAsyncTaskScheduler;
import static org.assertj.core.api.Assertions.assertThat;
/**
* Tests for {@link ScheduledTask}.
* @author Brian Clozel
*/
class ScheduledTaskTests {
private CountingRunnable countingRunnable = new CountingRunnable();
private SimpleAsyncTaskScheduler taskScheduler = new SimpleAsyncTaskScheduler();
private ScheduledTaskRegistrar taskRegistrar = new ScheduledTaskRegistrar();
@BeforeEach
void setup() {
this.taskRegistrar.setTaskScheduler(this.taskScheduler);
taskScheduler.start();
}
@AfterEach
void tearDown() {
taskScheduler.stop();
}
@Test
void shouldReturnConfiguredTask() {
Task task = new Task(countingRunnable);
ScheduledTask scheduledTask = new ScheduledTask(task);
assertThat(scheduledTask.getTask()).isEqualTo(task);
}
@Test
void shouldUseTaskToString() {
Task task = new Task(countingRunnable);
ScheduledTask scheduledTask = new ScheduledTask(task);
assertThat(scheduledTask.toString()).isEqualTo(task.toString());
}
@Test
void unscheduledTaskShouldNotHaveNextExecution() {
ScheduledTask scheduledTask = new ScheduledTask(new Task(countingRunnable));
assertThat(scheduledTask.nextExecution()).isNull();
assertThat(countingRunnable.executionCount).isZero();
}
@Test
void scheduledTaskShouldHaveNextExecution() {
ScheduledTask scheduledTask = taskRegistrar.scheduleFixedDelayTask(new FixedDelayTask(countingRunnable,
Duration.ofSeconds(10), Duration.ofSeconds(10)));
assertThat(scheduledTask.nextExecution()).isBefore(Instant.now().plusSeconds(11));
}
@Test
void cancelledTaskShouldNotHaveNextExecution() {
ScheduledTask scheduledTask = taskRegistrar.scheduleFixedDelayTask(new FixedDelayTask(countingRunnable,
Duration.ofSeconds(10), Duration.ofSeconds(10)));
scheduledTask.cancel(true);
assertThat(scheduledTask.nextExecution()).isNull();
}
@Test
void singleExecutionShouldNotHaveNextExecution() {
ScheduledTask scheduledTask = taskRegistrar.scheduleOneTimeTask(new OneTimeTask(countingRunnable, Duration.ofSeconds(0)));
Awaitility.await().atMost(Duration.ofSeconds(5)).until(() -> countingRunnable.executionCount > 0);
assertThat(scheduledTask.nextExecution()).isNull();
}
class CountingRunnable implements Runnable {
int executionCount;
@Override
public void run() {
executionCount++;
}
}
}

View File

@@ -16,11 +16,11 @@
package org.springframework.scheduling.config;
import java.lang.reflect.Method;
import java.time.Duration;
import java.time.Instant;
import java.util.List;
import org.assertj.core.api.ObjectAssert;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
@@ -32,6 +32,7 @@ import org.springframework.scheduling.TriggerContext;
import org.springframework.scheduling.support.ScheduledMethodRunnable;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.InstanceOfAssertFactories.type;
/**
* @author Mark Fisher
@@ -68,11 +69,13 @@ public class ScheduledTasksBeanDefinitionParserTests {
List<IntervalTask> tasks = (List<IntervalTask>) new DirectFieldAccessor(
this.registrar).getPropertyValue("fixedRateTasks");
Runnable runnable = tasks.get(0).getRunnable();
assertThat(runnable.getClass()).isEqualTo(ScheduledMethodRunnable.class);
Object targetObject = ((ScheduledMethodRunnable) runnable).getTarget();
Method targetMethod = ((ScheduledMethodRunnable) runnable).getMethod();
assertThat(targetObject).isEqualTo(this.testBean);
assertThat(targetMethod.getName()).isEqualTo("test");
ObjectAssert<ScheduledMethodRunnable> runnableAssert = assertThat(runnable)
.extracting("runnable")
.isInstanceOf(ScheduledMethodRunnable.class)
.asInstanceOf(type(ScheduledMethodRunnable.class));
runnableAssert.extracting("target").isEqualTo(testBean);
runnableAssert.extracting("method.name").isEqualTo("test");
}
@Test

View File

@@ -0,0 +1,98 @@
/*
* Copyright 2002-2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.scheduling.config;
import java.time.Instant;
import org.junit.jupiter.api.Test;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatIllegalStateException;
/**
* Tests for {@link TaskExecutionOutcome}.
* @author Brian Clozel
*/
class TaskExecutionOutcomeTests {
@Test
void shouldCreateWithNoneStatus() {
TaskExecutionOutcome outcome = TaskExecutionOutcome.create();
assertThat(outcome.status()).isEqualTo(TaskExecutionOutcome.Status.NONE);
assertThat(outcome.executionTime()).isNull();
assertThat(outcome.throwable()).isNull();
}
@Test
void startedTaskShouldBeOngoing() {
TaskExecutionOutcome outcome = TaskExecutionOutcome.create();
Instant now = Instant.now();
outcome = outcome.start(now);
assertThat(outcome.status()).isEqualTo(TaskExecutionOutcome.Status.STARTED);
assertThat(outcome.executionTime()).isEqualTo(now);
assertThat(outcome.throwable()).isNull();
}
@Test
void shouldRejectSuccessWhenNotStarted() {
TaskExecutionOutcome outcome = TaskExecutionOutcome.create();
assertThatIllegalStateException().isThrownBy(outcome::success);
}
@Test
void shouldRejectErrorWhenNotStarted() {
TaskExecutionOutcome outcome = TaskExecutionOutcome.create();
assertThatIllegalStateException().isThrownBy(() -> outcome.failure(new IllegalArgumentException("test error")));
}
@Test
void finishedTaskShouldBeSuccessful() {
TaskExecutionOutcome outcome = TaskExecutionOutcome.create();
Instant now = Instant.now();
outcome = outcome.start(now);
outcome = outcome.success();
assertThat(outcome.status()).isEqualTo(TaskExecutionOutcome.Status.SUCCESS);
assertThat(outcome.executionTime()).isEqualTo(now);
assertThat(outcome.throwable()).isNull();
}
@Test
void errorTaskShouldBeFailure() {
TaskExecutionOutcome outcome = TaskExecutionOutcome.create();
Instant now = Instant.now();
outcome = outcome.start(now);
outcome = outcome.failure(new IllegalArgumentException(("test error")));
assertThat(outcome.status()).isEqualTo(TaskExecutionOutcome.Status.ERROR);
assertThat(outcome.executionTime()).isEqualTo(now);
assertThat(outcome.throwable()).isInstanceOf(IllegalArgumentException.class);
}
@Test
void newTaskExecutionShouldNotFail() {
TaskExecutionOutcome outcome = TaskExecutionOutcome.create();
Instant now = Instant.now();
outcome = outcome.start(now);
outcome = outcome.failure(new IllegalArgumentException(("test error")));
outcome = outcome.start(now.plusSeconds(2));
assertThat(outcome.status()).isEqualTo(TaskExecutionOutcome.Status.STARTED);
assertThat(outcome.executionTime()).isAfter(now);
assertThat(outcome.throwable()).isNull();
}
}

View File

@@ -0,0 +1,96 @@
/*
* Copyright 2002-2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.scheduling.config;
import org.junit.jupiter.api.Test;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
import static org.assertj.core.api.Assertions.assertThatIllegalStateException;
/**
* Tests for {@link Task}.
* @author Brian Clozel
*/
class TaskTests {
@Test
void shouldRejectNullRunnable() {
assertThatIllegalArgumentException().isThrownBy(() -> new Task(null));
}
@Test
void initialStateShouldBeUnknown() {
TestRunnable testRunnable = new TestRunnable();
Task task = new Task(testRunnable);
assertThat(testRunnable.hasRun).isFalse();
TaskExecutionOutcome executionOutcome = task.getLastExecutionOutcome();
assertThat(executionOutcome.executionTime()).isNull();
assertThat(executionOutcome.status()).isEqualTo(TaskExecutionOutcome.Status.NONE);
assertThat(executionOutcome.throwable()).isNull();
}
@Test
void stateShouldUpdateAfterRun() {
TestRunnable testRunnable = new TestRunnable();
Task task = new Task(testRunnable);
task.getRunnable().run();
assertThat(testRunnable.hasRun).isTrue();
TaskExecutionOutcome executionOutcome = task.getLastExecutionOutcome();
assertThat(executionOutcome.executionTime()).isInThePast();
assertThat(executionOutcome.status()).isEqualTo(TaskExecutionOutcome.Status.SUCCESS);
assertThat(executionOutcome.throwable()).isNull();
}
@Test
void stateShouldUpdateAfterFailingRun() {
FailingTestRunnable testRunnable = new FailingTestRunnable();
Task task = new Task(testRunnable);
assertThatIllegalStateException().isThrownBy(() -> task.getRunnable().run());
assertThat(testRunnable.hasRun).isTrue();
TaskExecutionOutcome executionOutcome = task.getLastExecutionOutcome();
assertThat(executionOutcome.executionTime()).isInThePast();
assertThat(executionOutcome.status()).isEqualTo(TaskExecutionOutcome.Status.ERROR);
assertThat(executionOutcome.throwable()).isInstanceOf(IllegalStateException.class);
}
static class TestRunnable implements Runnable {
boolean hasRun;
@Override
public void run() {
this.hasRun = true;
}
}
static class FailingTestRunnable implements Runnable {
boolean hasRun;
@Override
public void run() {
this.hasRun = true;
throw new IllegalStateException("test exception");
}
}
}