Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 20 additions & 2 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -92,13 +92,31 @@
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>
<artifactId>junit-jupiter-api</artifactId>
<version>${junit.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<version>${junit.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-params</artifactId>
<version>${junit.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<version>${mockito.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-inline</artifactId>
<artifactId>mockito-junit-jupiter</artifactId>
<version>${mockito.version}</version>
<scope>test</scope>
</dependency>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -240,8 +240,7 @@ public void register(final FieldDefinition definition) {
* @return returns CronDefinition instance, never null
*/
public CronDefinition instance() {
final Set<CronConstraint> validations = new HashSet<>();
validations.addAll(cronConstraints);
final Set<CronConstraint> validations = new HashSet<>(cronConstraints);
final List<FieldDefinition> values = new ArrayList<>(fields.values());
values.sort(FieldDefinition.createFieldDefinitionComparator());
return new CronDefinition(values, validations, cronNicknames, matchDayOfWeekAndDayOfMonth);
Expand Down Expand Up @@ -323,8 +322,8 @@ private static CronDefinition cron4j() {
* </table>
*
* <p>Thus in general Quartz cron expressions are as follows:
*
* <p>S M H DoM M DoW [Y]
* "0 30 17 ? * 7L *"
* <p>S M H DoM M DoW [Y]
*
* @return {@link CronDefinition} instance, never {@code null}
*/
Expand Down Expand Up @@ -414,7 +413,7 @@ private static CronDefinition spring() {

/**
* Creates CronDefinition instance matching Spring (v5.2 onwards) specification.
* https://spring.io/blog/2020/11/10/new-in-spring-5-3-improved-cron-expressions
* <a href="https://spring.io/blog/2020/11/10/new-in-spring-5-3-improved-cron-expressions">...</a>
*
* <p>The cron expression is expected to be a string comprised of 6
* fields separated by white space. Fields can contain any of the allowed
Expand Down Expand Up @@ -498,9 +497,9 @@ private static CronDefinition unixCrontab() {
return CronDefinitionBuilder.defineCron()
.withMinutes().withValidRange(0, 59).withStrictRange().and()
.withHours().withValidRange(0, 23).withStrictRange().and()
.withDayOfMonth().withValidRange(1, 31).withStrictRange().and()
.withDayOfMonth().withValidRange(1, 31).supportsL().supportsLW().supportsW().withStrictRange().and()
.withMonth().withValidRange(1, 12).withStrictRange().and()
.withDayOfWeek().withValidRange(0, 7).withMondayDoWValue(1).withIntMapping(7, 0).withStrictRange().and()
.withDayOfWeek().withValidRange(0, 7).withMondayDoWValue(1).withIntMapping(7, 0).supportsL().supportsW().withStrictRange().and()
.instance();
}

Expand All @@ -511,19 +510,12 @@ private static CronDefinition unixCrontab() {
* @return CronDefinition instance if definition is found; a RuntimeException otherwise.
*/
public static CronDefinition instanceDefinitionFor(final CronType cronType) {
switch (cronType) {
case CRON4J:
return cron4j();
case QUARTZ:
return quartz();
case UNIX:
return unixCrontab();
case SPRING:
return spring();
case SPRING53:
return spring53();
default:
throw new IllegalArgumentException(String.format("No cron definition found for %s", cronType));
}
return switch (cronType) {
case CRON4J -> cron4j();
case QUARTZ -> quartz();
case UNIX -> unixCrontab();
case SPRING -> spring();
case SPRING53 -> spring53();
};
}
}
}
10 changes: 5 additions & 5 deletions src/test/java/com/cronutils/Issue143Test.java
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.fail;

public class Issue143Test {
class Issue143Test {

private static final String LAST_EXECUTION_NOT_PRESENT_ERROR = "last execution was not present";
private CronParser parser;
Expand All @@ -44,7 +44,7 @@ public void setUp() {
}

@Test
public void testCase1() {
void testCase1() {
ExecutionTime et = ExecutionTime.forCron(parser.parse("0 0 12 31 12 ? *"));
Optional<ZonedDateTime> olast = et.lastExecution(currentDateTime);
ZonedDateTime last = olast.orElse(null);
Expand All @@ -55,7 +55,7 @@ public void testCase1() {
}

@Test
public void testCase2() {
void testCase2() {
final ExecutionTime et = ExecutionTime.forCron(parser.parse("0 0 12 ? 12 SAT#5 *"));
final Optional<ZonedDateTime> lastExecution = et.lastExecution(currentDateTime);
if (lastExecution.isPresent()) {
Expand All @@ -67,7 +67,7 @@ public void testCase2() {
}

@Test
public void testCase3() {
void testCase3() {
final ExecutionTime et = ExecutionTime.forCron(parser.parse("0 0 12 31 1/1 ? *"));
final Optional<ZonedDateTime> lastExecution = et.lastExecution(currentDateTime);
if (lastExecution.isPresent()) {
Expand All @@ -79,7 +79,7 @@ public void testCase3() {
}

@Test
public void testCase4() {
void testCase4() {
final ExecutionTime et = ExecutionTime.forCron(parser.parse("0 0 12 ? 1/1 SAT#5 *"));
final Optional<ZonedDateTime> lastExecution = et.lastExecution(currentDateTime);
if (lastExecution.isPresent()) {
Expand Down
123 changes: 65 additions & 58 deletions src/test/java/com/cronutils/mapper/CronMapperIntegrationTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -13,74 +13,83 @@

package com.cronutils.mapper;

import com.cronutils.model.Cron;
import com.cronutils.model.CronType;
import com.cronutils.model.definition.CronDefinition;
import com.cronutils.model.definition.CronDefinitionBuilder;
import com.cronutils.parser.CronParser;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.MethodSource;

import java.util.Arrays;
import java.util.stream.Stream;

import static com.cronutils.model.CronType.CRON4J;
import static com.cronutils.model.CronType.QUARTZ;
import static com.cronutils.model.CronType.SPRING;
import static com.cronutils.model.CronType.UNIX;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;

public class CronMapperIntegrationTest {

@Test
public void testSpecificTimeCron4jToQuartz() {
assertEquals("0 30 8 10 6 ? *", CronMapper.fromCron4jToQuartz().map(cron4jParser().parse("30 8 10 6 *")).asString());
}

@Test
public void testMoreThanOneInstanceCron4jToQuartz() {
assertEquals("0 0 11,16 * * ? *", CronMapper.fromCron4jToQuartz().map(cron4jParser().parse("0 11,16 * * *")).asString());
}

@Test
public void testRangeOfTimeCron4jToQuartz() {
final String expression = "0 9-18 * * 1-3";
final String expected = "0 0 9-18 ? * 2-4 *";
assertEquals(expected, CronMapper.fromCron4jToQuartz().map(cron4jParser().parse(expression)).asString());
}

@Test
public void testSpecificTimeQuartzToCron4j() {
final String expression = "5 30 8 10 6 ? 1984";
assertEquals("30 8 10 6 *", CronMapper.fromQuartzToCron4j().map(quartzParser().parse(expression)).asString());
}

@Test
public void testMoreThanOneInstanceQuartzToCron4j() {
final String expression = "5 0 11,16 * * ? 1984";
assertEquals("0 11,16 * * *", CronMapper.fromQuartzToCron4j().map(quartzParser().parse(expression)).asString());
}

@Test
public void testRangeOfTimeQuartzToCron4j() {
final String expected = "0 9-18 * * 0-2";
final String expression = "5 0 9-18 ? * 1-3 1984";
assertEquals(expected, CronMapper.fromQuartzToCron4j().map(quartzParser().parse(expression)).asString());
}

@Test
public void testRangeOfTimeQuartzToSpring() {
final String expected = "5 0 9-18 ? * 0-2";
final String expression = "5 0 9-18 ? * 1-3 1984";
assertEquals(expected, CronMapper.fromQuartzToSpring().map(quartzParser().parse(expression)).asString());
class CronMapperIntegrationTest {
static Stream<Arguments> cronExpressions() {
return Stream.of(
Arguments.of(CRON4J, CronMapper.fromCron4jToQuartz(), "0 11,16 * * *", "0 0 11,16 * * ? *"),
Arguments.of(CRON4J, CronMapper.fromCron4jToQuartz(), "0 9-18 * * 1-3", "0 0 9-18 ? * 2-4 *"),
Arguments.of(CRON4J, CronMapper.fromCron4jToQuartz(), "30 8 10 6 *", "0 30 8 10 6 ? *"),
Arguments.of(QUARTZ, CronMapper.fromQuartzToCron4j(), "0 0 0 ? * 5#1", "0 0 * * 4#1"),
Arguments.of(QUARTZ, CronMapper.fromQuartzToCron4j(), "5 0 11,16 * * ? 1984", "0 11,16 * * *"),
Arguments.of(QUARTZ, CronMapper.fromQuartzToCron4j(), "5 0 9-18 ? * 1-3 1984", "0 9-18 * * 0-2"),
Arguments.of(QUARTZ, CronMapper.fromQuartzToCron4j(), "5 30 8 10 6 ? 1984", "30 8 10 6 *"),
Arguments.of(QUARTZ, CronMapper.fromQuartzToSpring(), "0 0 0 ? * 5#1", "0 0 0 ? * 4#1"),
Arguments.of(QUARTZ, CronMapper.fromQuartzToSpring(), "5 0 9-18 ? * 1-3 1984", "5 0 9-18 ? * 0-2"),
Arguments.of(QUARTZ, CronMapper.fromQuartzToUnix(), "0 0 1 ? 1/3 FRI#1 *", "0 1 * 1/3 5#1"),
Arguments.of(QUARTZ, CronMapper.fromQuartzToUnix(), "0 0 2 ? 1/1 SUN#2", "0 2 * 1/1 0#2"),
Arguments.of(QUARTZ, CronMapper.fromQuartzToUnix(), "0 0 8,12 ? * *", "0 8,12 * * *"),
Arguments.of(QUARTZ, CronMapper.fromQuartzToUnix(), "* * 8 ? * SAT", "* 8 * * 6"),
Arguments.of(QUARTZ, CronMapper.fromQuartzToUnix(), "0 0 22 ? * MON,TUE,WED,THU,FRI *", "0 22 * * 1,2,3,4,5"),
Arguments.of(QUARTZ, CronMapper.fromQuartzToUnix(), "0 0 13 LW * ?", "0 13 LW * *"),
Arguments.of(QUARTZ, CronMapper.fromQuartzToUnix(), "0 0 0 1 1-12 ? *", "0 0 1 1-12 *"),
Arguments.of(QUARTZ, CronMapper.fromQuartzToUnix(), "0 0 0 1 JAN ? 2099", "0 0 1 1 *"),
Arguments.of(QUARTZ, CronMapper.fromQuartzToUnix(), "0 0 0 1,15 * ? *", "0 0 1,15 * *"),
Arguments.of(QUARTZ, CronMapper.fromQuartzToUnix(), "0 0 0 19 1-12 ? *", "0 0 19 1-12 *"),
Arguments.of(QUARTZ, CronMapper.fromQuartzToUnix(), "0 0 0 21 * ? 2020-2025", "0 0 21 * *"),
Arguments.of(QUARTZ, CronMapper.fromQuartzToUnix(), "0 0 0 ? * 3L *", "0 0 * * 2L"),
Arguments.of(QUARTZ, CronMapper.fromQuartzToUnix(), "0 0 0 ? 1/1 MON#2 *", "0 0 * 1/1 1#2"),
Arguments.of(QUARTZ, CronMapper.fromQuartzToUnix(), "0 0 0,12 ? JAN,MAY,OCT * *", "0 0,12 * 1,5,10 *"),
Arguments.of(QUARTZ, CronMapper.fromQuartzToUnix(), "0 0 13 ? 1,4,7,10 6#2", "0 13 * 1,4,7,10 5#2"),
Arguments.of(QUARTZ, CronMapper.fromQuartzToUnix(), "0 0 13 ? MAR,JUN,SEP,DEC 1L", "0 13 * 3,6,9,12 0L"),
Arguments.of(QUARTZ, CronMapper.fromQuartzToUnix(), "0 0 0 L-10 1-12 ? *", "0 0 L-10 1-12 *"),
Arguments.of(QUARTZ, CronMapper.fromQuartzToUnix(), "0 0/5 * ? 1-12 4#2 *", "0/5 * * 1-12 3#2"),
Arguments.of(QUARTZ, CronMapper.fromQuartzToUnix(), "0 0 1 1W * ?", "0 1 1W * *"),
Arguments.of(QUARTZ, CronMapper.fromQuartzToUnix(), "0 0 13 ? 1,4,7,10 6#2", "0 13 * 1,4,7,10 5#2"),
Arguments.of(QUARTZ, CronMapper.fromQuartzToUnix(), "0 15 14,19 ? JAN,MAY,JUL,OCT * *", "15 14,19 * 1,5,7,10 *"),

Arguments.of(QUARTZ, CronMapper.fromQuartzToUnix(), "0 0 0 ? * 5#1", "0 0 * * 4#1"),
Arguments.of(QUARTZ, CronMapper.fromQuartzToUnix(), "0 0 0 L-10 1-12 ? *", "0 0 L-10 1-12 *"),
Arguments.of(QUARTZ, CronMapper.fromQuartzToUnix(), "0 30 17 ? * 7L *", "30 17 * * 6L"),
Arguments.of(SPRING, CronMapper.fromSpringToQuartz(), "0 0 0 ? * 5#1", "0 0 0 ? * 6#1 *"),
Arguments.of(UNIX, CronMapper.fromUnixToQuartz(), "* * * * 3,5-6,*/2,2/3,7/4", "0 * * ? * 4,6-7,*/2,3/3,1/4 *"),
Arguments.of(UNIX, CronMapper.fromUnixToQuartz(), "0 0 * * 1", "0 0 0 ? * 2 *")
);
}

@Test
public void testDaysOfWeekUnixToQuartz() {
final String input = "* * * * 3,5-6,*/2,2/3,7/4";
final String expected = "0 * * ? * 4,6-7,*/2,3/3,1/4 *";
assertEquals(expected, CronMapper.fromUnixToQuartz().map(unixParser().parse(input)).asString());
@ParameterizedTest
@MethodSource("cronExpressions")
void testCronMapping(CronType cronType, CronMapper mapper, String quartzExpression, String expectedExpression) {
Cron sourceCron = getCron(cronType, quartzExpression);
String actualCron = mapper.map(sourceCron).asString();
assertEquals(expectedExpression, actualCron, String.format("Expected [%s] but got [%s]", expectedExpression, actualCron));
}

/**
* Issue #36, #56: Unix to Quartz not accurately mapping every minute pattern
* or patterns that involve every day of month and every day of week.
*/
@Test
public void testEveryMinuteUnixToQuartz() {
void testEveryMinuteUnixToQuartz() {
final String input = "* * * * *";
final String expected1 = "0 * * * * ? *";
final String expected2 = "0 * * ? * * *";
Expand All @@ -96,22 +105,20 @@ public void testEveryMinuteUnixToQuartz() {
* or patterns that involve every day of month and every day of week.
*/
@Test
public void testUnixToQuartzQuestionMarkRequired() {
void testUnixToQuartzQuestionMarkRequired() {
final String input = "0 0 * * 1";
final String expected = "0 0 0 ? * 2 *";
final String mapping = CronMapper.fromUnixToQuartz().map(unixParser().parse(input)).asString();
assertEquals(expected, mapping, String.format("Expected [%s] but got [%s]", expected, mapping));
}

private CronParser cron4jParser() {
return new CronParser(CronDefinitionBuilder.instanceDefinitionFor(CronType.CRON4J));
}

private CronParser quartzParser() {
return new CronParser(CronDefinitionBuilder.instanceDefinitionFor(CronType.QUARTZ));
private CronParser unixParser() {
return new CronParser(CronDefinitionBuilder.instanceDefinitionFor(UNIX));
}

private CronParser unixParser() {
return new CronParser(CronDefinitionBuilder.instanceDefinitionFor(CronType.UNIX));
private Cron getCron(CronType cronType, String quartzExpression) {
final CronDefinition cronDefinition = CronDefinitionBuilder.instanceDefinitionFor(cronType);
final CronParser parser = new CronParser(cronDefinition);
return parser.parse(quartzExpression);
}
}
34 changes: 15 additions & 19 deletions src/test/java/com/cronutils/mapper/CronMapperTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -19,56 +19,52 @@
import com.cronutils.model.field.CronFieldName;
import com.cronutils.model.field.expression.Always;
import com.cronutils.model.field.expression.On;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.mockito.junit.jupiter.MockitoExtension;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.mockito.Mockito.mock;

public class CronMapperTest {
private CronFieldName testCronFieldName;
@ExtendWith(MockitoExtension.class)
class CronMapperTest {
private static final CronFieldName TEST_CRON_FIELD_NAME = CronFieldName.SECOND;

@Mock
private CronField mockCronField;

@BeforeEach
public void setUp() {
MockitoAnnotations.initMocks(this);
testCronFieldName = CronFieldName.SECOND;
}

@Test
public void testConstructorSourceDefinitionNull() {
void testConstructorSourceDefinitionNull() {
assertThrows(NullPointerException.class, () -> new CronMapper(mock(CronDefinition.class), null, null));
}

@Test
public void testConstructorTargetDefinitionNull() {
void testConstructorTargetDefinitionNull() {
assertThrows(NullPointerException.class, () -> new CronMapper(null, mock(CronDefinition.class), null));
}

@Test
public void testReturnSameExpression() {
void testReturnSameExpression() {
final Function<CronField, CronField> function = CronMapper.returnSameExpression();
assertEquals(mockCronField, function.apply(mockCronField));
}

@Test
public void testReturnOnZeroExpression() {
final Function<CronField, CronField> function = CronMapper.returnOnZeroExpression(testCronFieldName);
void testReturnOnZeroExpression() {
final Function<CronField, CronField> function = CronMapper.returnOnZeroExpression(TEST_CRON_FIELD_NAME);

assertEquals(testCronFieldName, function.apply(mockCronField).getField());
assertEquals(TEST_CRON_FIELD_NAME, function.apply(mockCronField).getField());
final On result = (On) function.apply(mockCronField).getExpression();
assertEquals(0, (int) result.getTime().getValue());
}

@Test
public void testReturnAlwaysExpression() {
final Function<CronField, CronField> function = CronMapper.returnAlwaysExpression(testCronFieldName);
void testReturnAlwaysExpression() {
final Function<CronField, CronField> function = CronMapper.returnAlwaysExpression(TEST_CRON_FIELD_NAME);

assertEquals(testCronFieldName, function.apply(mockCronField).getField());
assertEquals(TEST_CRON_FIELD_NAME, function.apply(mockCronField).getField());
assertEquals(Always.class, function.apply(mockCronField).getExpression().getClass());
}
}