diff --git a/org.springframework.context/src/main/java/org/springframework/scheduling/support/CronSequenceGenerator.java b/org.springframework.context/src/main/java/org/springframework/scheduling/support/CronSequenceGenerator.java index ecb21d5b461..8c2da446146 100644 --- a/org.springframework.context/src/main/java/org/springframework/scheduling/support/CronSequenceGenerator.java +++ b/org.springframework.context/src/main/java/org/springframework/scheduling/support/CronSequenceGenerator.java @@ -16,10 +16,13 @@ package org.springframework.scheduling.support; +import java.util.ArrayList; import java.util.BitSet; import java.util.Calendar; +import java.util.Collections; import java.util.Date; import java.util.GregorianCalendar; +import java.util.List; import org.springframework.util.StringUtils; @@ -90,41 +93,55 @@ class CronSequenceGenerator { calendar.add(Calendar.SECOND, 1); calendar.set(Calendar.MILLISECOND, 0); + List resets = new ArrayList(); + int second = calendar.get(Calendar.SECOND); - findNext(this.seconds, second, 60, calendar, Calendar.SECOND); + int updateSecond = findNext(this.seconds, second, 60, calendar, Calendar.SECOND, Collections. emptyList()); + if (second == updateSecond) { + resets.add(Calendar.SECOND); + } int minute = calendar.get(Calendar.MINUTE); - findNext(this.minutes, minute, 60, calendar, Calendar.MINUTE, Calendar.SECOND); + int updateMinute = findNext(this.minutes, minute, 60, calendar, Calendar.MINUTE, resets); + if (minute == updateMinute) { + resets.add(Calendar.MINUTE); + } int hour = calendar.get(Calendar.HOUR_OF_DAY); - findNext(this.hours, hour, 24, calendar, Calendar.HOUR_OF_DAY, Calendar.MINUTE, Calendar.SECOND); + int updateHour = findNext(this.hours, hour, 24, calendar, Calendar.HOUR_OF_DAY, resets); + if (hour == updateHour) { + resets.add(Calendar.HOUR_OF_DAY); + } int dayOfWeek = calendar.get(Calendar.DAY_OF_WEEK); int dayOfMonth = calendar.get(Calendar.DAY_OF_MONTH); - findNextDay(calendar, this.daysOfMonth, dayOfMonth, daysOfWeek, dayOfWeek, 366); + int updateDayOfMonth = findNextDay(calendar, this.daysOfMonth, dayOfMonth, daysOfWeek, dayOfWeek, 366, resets); + if (dayOfMonth == updateDayOfMonth) { + resets.add(Calendar.DAY_OF_MONTH); + } int month = calendar.get(Calendar.MONTH); - findNext(this.months, month, 12, calendar, Calendar.MONTH, Calendar.DAY_OF_MONTH, Calendar.HOUR_OF_DAY, - Calendar.MINUTE, Calendar.SECOND); + month = findNext(this.months, month, 12, calendar, Calendar.MONTH, resets); return calendar.getTime(); } - private void findNextDay( - Calendar calendar, BitSet daysOfMonth, int dayOfMonth, BitSet daysOfWeek, int dayOfWeek, int max) { + private int findNextDay(Calendar calendar, BitSet daysOfMonth, int dayOfMonth, BitSet daysOfWeek, + int dayOfWeek, int max, List resets) { int count = 0; // the DAY_OF_WEEK values in java.util.Calendar start with 1 (Sunday), // but in the cron pattern, they start with 0, so we subtract 1 here - while ((!daysOfMonth.get(dayOfMonth) || !daysOfWeek.get(dayOfWeek-1)) && count++ < max) { + while ((!daysOfMonth.get(dayOfMonth) || !daysOfWeek.get(dayOfWeek - 1)) && count++ < max) { calendar.add(Calendar.DAY_OF_MONTH, 1); dayOfMonth = calendar.get(Calendar.DAY_OF_MONTH); dayOfWeek = calendar.get(Calendar.DAY_OF_WEEK); - reset(calendar, Calendar.HOUR_OF_DAY, Calendar.MINUTE, Calendar.SECOND); + reset(calendar, resets); } if (count > max) { throw new IllegalStateException("Overflow in day for expression=" + this.expression); } + return dayOfMonth; } /** @@ -140,9 +157,9 @@ class CronSequenceGenerator { * ones of lower significance than the field of interest) * @return the value of the calendar field that is next in the sequence */ - private void findNext(BitSet bits, int value, int max, Calendar calendar, int field, int... lowerOrders) { + private int findNext(BitSet bits, int value, int max, Calendar calendar, int field, List lowerOrders) { int nextValue = bits.nextSetBit(value); - //roll over if needed + // roll over if needed if (nextValue == -1) { calendar.add(field, max - value); nextValue = bits.nextSetBit(0); @@ -151,12 +168,13 @@ class CronSequenceGenerator { calendar.set(field, nextValue); reset(calendar, lowerOrders); } + return nextValue; } /** * Reset the calendar setting all the fields provided to zero. */ - private void reset(Calendar calendar, int... fields) { + private void reset(Calendar calendar, List fields) { for (int field : fields) { calendar.set(field, 0); } @@ -203,7 +221,7 @@ class CronSequenceGenerator { private void setDaysOfMonth(BitSet bits, String field, int max) { // Days of month start with 1 (in Cron and Calendar) so add one - setDays(bits, field, max+1); + setDays(bits, field, max + 1); // ... and remove it from the front bits.clear(0); } @@ -244,7 +262,7 @@ class CronSequenceGenerator { int[] result = new int[2]; if (field.contains("*")) { result[0] = 0; - result[1] = max-1; + result[1] = max - 1; return result; } if (!field.contains("-")) { diff --git a/org.springframework.context/src/test/java/org/springframework/scheduling/support/CronTriggerTests.java b/org.springframework.context/src/test/java/org/springframework/scheduling/support/CronTriggerTests.java new file mode 100644 index 00000000000..3f66d3b259f --- /dev/null +++ b/org.springframework.context/src/test/java/org/springframework/scheduling/support/CronTriggerTests.java @@ -0,0 +1,460 @@ +/* + * Copyright 2002-2009 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 + * + * http://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.support; + +import static org.junit.Assert.assertEquals; + +import java.util.Calendar; +import java.util.Date; +import java.util.GregorianCalendar; + +import org.junit.Before; +import org.junit.Test; + +import org.springframework.scheduling.TriggerContext; + +/** + * @author Dave Syer + * @author Mark Fisher + */ +public class CronTriggerTests { + + private Calendar calendar = new GregorianCalendar(); + + private Date date = new Date(); + + + /** + * @param calendar + */ + private void roundup(Calendar calendar) { + calendar.add(Calendar.SECOND, 1); + calendar.set(Calendar.MILLISECOND, 0); + } + + + @Before + public void setUp() { + calendar.setTime(date); + roundup(calendar); + } + + @Test + public void testMatchAll() throws Exception { + CronTrigger trigger = new CronTrigger("* * * * * *"); + TriggerContext context = getTriggerContext(date); + assertEquals(calendar.getTime(), trigger.nextExecutionTime(context)); + } + + @Test + public void testMatchLastSecond() throws Exception { + CronTrigger trigger = new CronTrigger("* * * * * *"); + GregorianCalendar calendar = new GregorianCalendar(); + calendar.set(Calendar.SECOND, 58); + assertMatchesNextSecond(trigger, calendar); + } + + @Test + public void testMatchSpecificSecond() throws Exception { + CronTrigger trigger = new CronTrigger("10 * * * * *"); + GregorianCalendar calendar = new GregorianCalendar(); + calendar.set(Calendar.SECOND, 9); + assertMatchesNextSecond(trigger, calendar); + } + + @Test + public void testIncrementSecondByOne() throws Exception { + CronTrigger trigger = new CronTrigger("11 * * * * *"); + calendar.set(Calendar.SECOND, 10); + Date date = calendar.getTime(); + calendar.add(Calendar.SECOND, 1); + TriggerContext context = getTriggerContext(date); + assertEquals(calendar.getTime(), trigger.nextExecutionTime(context)); + } + + @Test + public void testIncrementSecondAndRollover() throws Exception { + CronTrigger trigger = new CronTrigger("10 * * * * *"); + calendar.set(Calendar.SECOND, 11); + Date date = calendar.getTime(); + calendar.add(Calendar.SECOND, 59); + TriggerContext context = getTriggerContext(date); + assertEquals(calendar.getTime(), trigger.nextExecutionTime(context)); + } + + @Test + public void testSecondRange() throws Exception { + CronTrigger trigger = new CronTrigger("10-15 * * * * *"); + calendar.set(Calendar.SECOND, 9); + assertMatchesNextSecond(trigger, calendar); + calendar.set(Calendar.SECOND, 14); + assertMatchesNextSecond(trigger, calendar); + } + + @Test + public void testIncrementMinuteByOne() throws Exception { + CronTrigger trigger = new CronTrigger("0 11 * * * *"); + calendar.set(Calendar.MINUTE, 10); + Date date = calendar.getTime(); + calendar.add(Calendar.MINUTE, 1); + calendar.set(Calendar.SECOND, 0); + TriggerContext context = getTriggerContext(date); + assertEquals(calendar.getTime(), trigger.nextExecutionTime(context)); + } + + @Test + public void testIncrementMinute() throws Exception { + CronTrigger trigger = new CronTrigger("0 * * * * *"); + calendar.set(Calendar.MINUTE, 10); + Date date = calendar.getTime(); + calendar.add(Calendar.MINUTE, 1); + calendar.set(Calendar.SECOND, 0); + TriggerContext context1 = getTriggerContext(date); + assertEquals(calendar.getTime(), date = trigger.nextExecutionTime(context1)); + calendar.add(Calendar.MINUTE, 1); + TriggerContext context2 = getTriggerContext(date); + assertEquals(calendar.getTime(), date = trigger.nextExecutionTime(context2)); + } + + @Test + public void testIncrementMinuteAndRollover() throws Exception { + CronTrigger trigger = new CronTrigger("0 10 * * * *"); + calendar.set(Calendar.MINUTE, 11); + calendar.set(Calendar.SECOND, 0); + Date date = calendar.getTime(); + calendar.add(Calendar.MINUTE, 59); + TriggerContext context = getTriggerContext(date); + assertEquals(calendar.getTime(), trigger.nextExecutionTime(context)); + } + + @Test + public void testIncrementHour() throws Exception { + CronTrigger trigger = new CronTrigger("0 0 * * * *"); + calendar.set(Calendar.MONTH, 9); + calendar.set(Calendar.DAY_OF_MONTH, 30); + calendar.set(Calendar.HOUR_OF_DAY, 11); + calendar.set(Calendar.MINUTE, 1); + calendar.set(Calendar.SECOND, 0); + Date date = calendar.getTime(); + calendar.set(Calendar.MINUTE, 0); + calendar.set(Calendar.HOUR_OF_DAY, 12); + TriggerContext context1 = getTriggerContext(date); + assertEquals(calendar.getTime(), date = trigger.nextExecutionTime(context1)); + calendar.set(Calendar.HOUR_OF_DAY, 13); + TriggerContext context2 = getTriggerContext(date); + assertEquals(calendar.getTime(), trigger.nextExecutionTime(context2)); + } + + @Test + public void testIncrementDayOfMonth() throws Exception { + CronTrigger trigger = new CronTrigger("0 0 0 * * *"); + calendar.set(Calendar.DAY_OF_MONTH, 1); + Date date = calendar.getTime(); + calendar.add(Calendar.DAY_OF_MONTH, 1); + calendar.set(Calendar.HOUR_OF_DAY, 0); + calendar.set(Calendar.MINUTE, 0); + calendar.set(Calendar.SECOND, 0); + TriggerContext context1 = getTriggerContext(date); + assertEquals(calendar.getTime(), date = trigger.nextExecutionTime(context1)); + assertEquals(2, calendar.get(Calendar.DAY_OF_MONTH)); + calendar.add(Calendar.DAY_OF_MONTH, 1); + TriggerContext context2 = getTriggerContext(date); + assertEquals(calendar.getTime(), date = trigger.nextExecutionTime(context2)); + assertEquals(3, calendar.get(Calendar.DAY_OF_MONTH)); + } + + @Test + public void testIncrementDayOfMonthByOne() throws Exception { + CronTrigger trigger = new CronTrigger("* * * 10 * *"); + calendar.set(Calendar.DAY_OF_MONTH, 9); + Date date = calendar.getTime(); + calendar.add(Calendar.DAY_OF_MONTH, 1); + calendar.set(Calendar.HOUR_OF_DAY, 0); + calendar.set(Calendar.MINUTE, 0); + calendar.set(Calendar.SECOND, 0); + TriggerContext context = getTriggerContext(date); + assertEquals(calendar.getTime(), trigger.nextExecutionTime(context)); + } + + @Test + public void testIncrementDayOfMonthAndRollover() throws Exception { + CronTrigger trigger = new CronTrigger("* * * 10 * *"); + calendar.set(Calendar.DAY_OF_MONTH, 11); + Date date = calendar.getTime(); + calendar.add(Calendar.MONTH, 1); + calendar.set(Calendar.DAY_OF_MONTH, 10); + calendar.set(Calendar.HOUR_OF_DAY, 0); + calendar.set(Calendar.MINUTE, 0); + calendar.set(Calendar.SECOND, 0); + TriggerContext context = getTriggerContext(date); + assertEquals(calendar.getTime(), trigger.nextExecutionTime(context)); + } + + @Test + public void testDailyTriggerInShortMonth() throws Exception { + CronTrigger trigger = new CronTrigger("0 0 0 * * *"); + calendar.set(Calendar.MONTH, 8); // September: 30 days + calendar.set(Calendar.DAY_OF_MONTH, 30); + Date date = calendar.getTime(); + calendar.set(Calendar.MONTH, 9); // October + calendar.set(Calendar.HOUR_OF_DAY, 0); + calendar.set(Calendar.MINUTE, 0); + calendar.set(Calendar.SECOND, 0); + calendar.set(Calendar.DAY_OF_MONTH, 1); + TriggerContext context1 = getTriggerContext(date); + assertEquals(calendar.getTime(), date = trigger.nextExecutionTime(context1)); + calendar.set(Calendar.DAY_OF_MONTH, 2); + TriggerContext context2 = getTriggerContext(date); + assertEquals(calendar.getTime(), trigger.nextExecutionTime(context2)); + } + + @Test + public void testDailyTriggerInLongMonth() throws Exception { + CronTrigger trigger = new CronTrigger("0 0 0 * * *"); + calendar.set(Calendar.MONTH, 9); // October: 31 days + calendar.set(Calendar.DAY_OF_MONTH, 30); + Date date = calendar.getTime(); + calendar.set(Calendar.HOUR_OF_DAY, 0); + calendar.set(Calendar.MINUTE, 0); + calendar.set(Calendar.SECOND, 0); + calendar.set(Calendar.DAY_OF_MONTH, 31); + TriggerContext context1 = getTriggerContext(date); + assertEquals(calendar.getTime(), date = trigger.nextExecutionTime(context1)); + calendar.set(Calendar.MONTH, 10); // November + calendar.set(Calendar.DAY_OF_MONTH, 1); + TriggerContext context2 = getTriggerContext(date); + assertEquals(calendar.getTime(), trigger.nextExecutionTime(context2)); + } + + @Test + public void testIncrementMonth() throws Exception { + CronTrigger trigger = new CronTrigger("0 0 0 1 * *"); + calendar.set(Calendar.MONTH, 9); + calendar.set(Calendar.DAY_OF_MONTH, 30); + Date date = calendar.getTime(); + calendar.set(Calendar.DAY_OF_MONTH, 1); + calendar.set(Calendar.HOUR_OF_DAY, 0); + calendar.set(Calendar.MINUTE, 0); + calendar.set(Calendar.SECOND, 0); + calendar.set(Calendar.MONTH, 10); + TriggerContext context1 = getTriggerContext(date); + assertEquals(calendar.getTime(), date = trigger.nextExecutionTime(context1)); + calendar.set(Calendar.MONTH, 11); + TriggerContext context2 = getTriggerContext(date); + assertEquals(calendar.getTime(), trigger.nextExecutionTime(context2)); + } + + @Test + public void testMonthlyTriggerInLongMonth() throws Exception { + CronTrigger trigger = new CronTrigger("0 0 0 31 * *"); + calendar.set(Calendar.MONTH, 9); + calendar.set(Calendar.DAY_OF_MONTH, 30); + Date date = calendar.getTime(); + calendar.set(Calendar.DAY_OF_MONTH, 31); + calendar.set(Calendar.HOUR_OF_DAY, 0); + calendar.set(Calendar.MINUTE, 0); + calendar.set(Calendar.SECOND, 0); + TriggerContext context = getTriggerContext(date); + assertEquals(calendar.getTime(), trigger.nextExecutionTime(context)); + } + + @Test + public void testMonthlyTriggerInShortMonth() throws Exception { + CronTrigger trigger = new CronTrigger("0 0 0 1 * *"); + calendar.set(Calendar.MONTH, 9); + calendar.set(Calendar.DAY_OF_MONTH, 30); + Date date = calendar.getTime(); + calendar.set(Calendar.MONTH, 10); + calendar.set(Calendar.DAY_OF_MONTH, 1); + calendar.set(Calendar.HOUR_OF_DAY, 0); + calendar.set(Calendar.MINUTE, 0); + calendar.set(Calendar.SECOND, 0); + TriggerContext context = getTriggerContext(date); + assertEquals(calendar.getTime(), trigger.nextExecutionTime(context)); + } + + @Test + public void testIncrementDayOfWeekByOne() throws Exception { + CronTrigger trigger = new CronTrigger("* * * * * 2"); + calendar.set(Calendar.DAY_OF_WEEK, 2); + Date date = calendar.getTime(); + calendar.add(Calendar.DAY_OF_WEEK, 1); + calendar.set(Calendar.HOUR_OF_DAY, 0); + calendar.set(Calendar.MINUTE, 0); + calendar.set(Calendar.SECOND, 0); + TriggerContext context = getTriggerContext(date); + assertEquals(calendar.getTime(), trigger.nextExecutionTime(context)); + assertEquals(Calendar.TUESDAY, calendar.get(Calendar.DAY_OF_WEEK)); + } + + @Test + public void testIncrementDayOfWeekAndRollover() throws Exception { + CronTrigger trigger = new CronTrigger("* * * * * 2"); + calendar.set(Calendar.DAY_OF_WEEK, 4); + Date date = calendar.getTime(); + calendar.add(Calendar.DAY_OF_MONTH, 6); + calendar.set(Calendar.HOUR_OF_DAY, 0); + calendar.set(Calendar.MINUTE, 0); + calendar.set(Calendar.SECOND, 0); + TriggerContext context = getTriggerContext(date); + assertEquals(calendar.getTime(), trigger.nextExecutionTime(context)); + assertEquals(Calendar.TUESDAY, calendar.get(Calendar.DAY_OF_WEEK)); + } + + @Test + public void testSpecificMinuteSecond() throws Exception { + CronTrigger trigger = new CronTrigger("2 5 * * * *"); + calendar.set(Calendar.MINUTE, 4); + Date date = calendar.getTime(); + calendar.add(Calendar.MINUTE, 1); + calendar.set(Calendar.SECOND, 2); + TriggerContext context1 = getTriggerContext(date); + assertEquals(calendar.getTime(), date = trigger.nextExecutionTime(context1)); + calendar.add(Calendar.HOUR, 1); + TriggerContext context2 = getTriggerContext(date); + assertEquals(calendar.getTime(), date = trigger.nextExecutionTime(context2)); + } + + @Test + public void testSpecificMinuteHour() throws Exception { + CronTrigger trigger = new CronTrigger("* 5 10 * * *"); + calendar.set(Calendar.MINUTE, 4); + calendar.set(Calendar.HOUR_OF_DAY, 9); + Date date = calendar.getTime(); + calendar.add(Calendar.MINUTE, 1); + calendar.add(Calendar.HOUR_OF_DAY, 1); + calendar.set(Calendar.SECOND, 0); + TriggerContext context1 = getTriggerContext(date); + assertEquals(calendar.getTime(), date = trigger.nextExecutionTime(context1)); + // next trigger is in one second because second is wildcard + calendar.add(Calendar.SECOND, 1); + TriggerContext context2 = getTriggerContext(date); + assertEquals(calendar.getTime(), date = trigger.nextExecutionTime(context2)); + } + + @Test + public void testWeekDaySequence() throws Exception { + CronTrigger trigger = new CronTrigger("0 0 7 ? * MON-FRI"); + // This is a Saturday + calendar.set(2009, 8, 26); + date = calendar.getTime(); + // 7 am is the trigger time + calendar.set(Calendar.HOUR_OF_DAY, 7); + calendar.set(Calendar.MINUTE, 0); + calendar.set(Calendar.SECOND, 0); + // Add two days because we start on Saturday + calendar.add(Calendar.DAY_OF_MONTH, 2); + TriggerContext context1 = getTriggerContext(date); + assertEquals(calendar.getTime(), date = trigger.nextExecutionTime(context1)); + // Next day is a week day so add one + calendar.add(Calendar.DAY_OF_MONTH, 1); + TriggerContext context2 = getTriggerContext(date); + assertEquals(calendar.getTime(), date = trigger.nextExecutionTime(context2)); + calendar.add(Calendar.DAY_OF_MONTH, 1); + TriggerContext context3 = getTriggerContext(date); + assertEquals(calendar.getTime(), date = trigger.nextExecutionTime(context3)); + } + + @Test + public void testDayOfWeekIndifferent() throws Exception { + CronTrigger trigger1 = new CronTrigger("* * * 2 * *"); + CronTrigger trigger2 = new CronTrigger("* * * 2 * ?"); + assertEquals(trigger1, trigger2); + } + + @Test + public void testSecondIncrementer() throws Exception { + CronTrigger trigger1 = new CronTrigger("57,59 * * * * *"); + CronTrigger trigger2 = new CronTrigger("57/2 * * * * *"); + assertEquals(trigger1, trigger2); + } + + @Test + public void testSecondIncrementerWithRange() throws Exception { + CronTrigger trigger1 = new CronTrigger("1,3,5 * * * * *"); + CronTrigger trigger2 = new CronTrigger("1-6/2 * * * * *"); + assertEquals(trigger1, trigger2); + } + + @Test + public void testHourIncrementer() throws Exception { + CronTrigger trigger1 = new CronTrigger("* * 4,8,12,16,20 * * *"); + CronTrigger trigger2 = new CronTrigger("* * 4/4 * * *"); + assertEquals(trigger1, trigger2); + } + + @Test + public void testDayNames() throws Exception { + CronTrigger trigger1 = new CronTrigger("* * * * * 0-6"); + CronTrigger trigger2 = new CronTrigger("* * * * * TUE,WED,THU,FRI,SAT,SUN,MON"); + assertEquals(trigger1, trigger2); + } + + @Test + public void testSundayIsZero() throws Exception { + CronTrigger trigger1 = new CronTrigger("* * * * * 0"); + CronTrigger trigger2 = new CronTrigger("* * * * * SUN"); + assertEquals(trigger1, trigger2); + } + + @Test + public void testSundaySynonym() throws Exception { + CronTrigger trigger1 = new CronTrigger("* * * * * 0"); + CronTrigger trigger2 = new CronTrigger("* * * * * 7"); + assertEquals(trigger1, trigger2); + } + + @Test + public void testMonthNames() throws Exception { + CronTrigger trigger1 = new CronTrigger("* * * * 0-11 *"); + CronTrigger trigger2 = new CronTrigger("* * * * FEB,JAN,MAR,APR,MAY,JUN,JUL,AUG,SEP,OCT,NOV,DEC *"); + assertEquals(trigger1, trigger2); + } + + @Test + public void testMonthNamesMixedCase() throws Exception { + CronTrigger trigger1 = new CronTrigger("* * * * 1 *"); + CronTrigger trigger2 = new CronTrigger("* * * * Feb *"); + assertEquals(trigger1, trigger2); + } + + @Test + public void testWhitespace() throws Exception { + CronTrigger trigger1 = new CronTrigger("* * * * 1 *"); + CronTrigger trigger2 = new CronTrigger("* * * * 1 *"); + assertEquals(trigger1, trigger2); + } + + /** + * @param trigger + * @param calendar + */ + private void assertMatchesNextSecond(CronTrigger trigger, Calendar calendar) { + Date date = calendar.getTime(); + roundup(calendar); + TriggerContext context = getTriggerContext(date); + assertEquals(calendar.getTime(), trigger.nextExecutionTime(context)); + } + + + private static TriggerContext getTriggerContext(Date lastCompletionTime) { + SimpleTriggerContext context = new SimpleTriggerContext(); + context.update(null, null, lastCompletionTime); + return context; + } + +}