View Javadoc

1   package com.atlassian.scheduler.core.tests;
2   
3   import org.joda.time.DateTime;
4   import org.joda.time.DateTimeZone;
5   import org.junit.Test;
6   
7   import static com.atlassian.scheduler.core.tests.CronExpressionAssertions.runTime;
8   import static com.atlassian.scheduler.core.tests.CronExpressionAssertions.satisfiedBy;
9   import static com.atlassian.scheduler.core.tests.CronExpressionAssertions.unsatisfiedBy;
10  import static java.util.concurrent.TimeUnit.MINUTES;
11  
12  /**
13   * Tests covering the functionality of the hours cron field.
14   *
15   * @since v1.5
16   */
17  public abstract class CronExpressionHoursTest extends AbstractCronExpressionTest {
18      protected CronExpressionHoursTest(CronFactory cronFactory) {
19          super(cronFactory);
20      }
21  
22      private void cronIncrements_hours_by7s(final String hoursExpr) {
23          assertCron(hoursExpr, "0 0 " + hoursExpr + " 1 1 ?",
24                  satisfiedBy(0, 0, 0, 1, 1, 2014),
25                  unsatisfiedBy(0, 0, 1, 1, 1, 2014),
26                  unsatisfiedBy(0, 0, 6, 1, 1, 2014),
27                  satisfiedBy(0, 0, 7, 1, 1, 2014),
28                  satisfiedBy(0, 0, 14, 1, 1, 2014),
29                  satisfiedBy(0, 0, 21, 1, 1, 2014),
30                  unsatisfiedBy(0, 0, 23, 1, 1, 2014));
31      }
32  
33      @Test
34      public void testHoursIncrement() {
35          cronIncrements_hours_by7s("/7");
36          cronIncrements_hours_by7s("*/7");
37          cronIncrements_hours_by7s("0/7");
38          cronIncrements_hours_by7s("0-22/7");
39  
40          assertCron("2/5", "0 0 2/5 1 1 ?",
41                  satisfiedBy(0, 0, 2, 1, 1, 2014),
42                  unsatisfiedBy(0, 0, 6, 1, 1, 2014),
43                  satisfiedBy(0, 0, 7, 1, 1, 2014),
44                  unsatisfiedBy(0, 0, 8, 1, 1, 2014),
45                  satisfiedBy(0, 0, 12, 1, 1, 2014),
46                  satisfiedBy(0, 0, 17, 1, 1, 2014),
47                  satisfiedBy(0, 0, 22, 1, 1, 2014),
48                  unsatisfiedBy(0, 0, 23, 1, 1, 2014));
49      }
50  
51      @Test
52      public void testHoursIncrementNormalRange() {
53          assertCron("9-17/3", "0 0 9-17/3 1 1 ?",
54                  unsatisfiedBy(0, 0, 2, 1, 1, 2014),
55                  unsatisfiedBy(0, 0, 5, 1, 1, 2014),
56                  unsatisfiedBy(0, 0, 6, 1, 1, 2014),
57                  unsatisfiedBy(0, 0, 8, 1, 1, 2014),
58                  satisfiedBy(0, 0, 9, 1, 1, 2014),
59                  unsatisfiedBy(0, 0, 11, 1, 1, 2014),
60                  satisfiedBy(0, 0, 12, 1, 1, 2014),
61                  unsatisfiedBy(0, 0, 13, 1, 1, 2014),
62                  unsatisfiedBy(0, 0, 14, 1, 1, 2014),
63                  satisfiedBy(0, 0, 15, 1, 1, 2014),
64                  unsatisfiedBy(0, 0, 16, 1, 1, 2014),
65                  unsatisfiedBy(0, 0, 17, 1, 1, 2014),
66                  unsatisfiedBy(0, 0, 18, 1, 1, 2014),
67                  unsatisfiedBy(0, 0, 19, 1, 1, 2014),
68                  unsatisfiedBy(0, 0, 20, 1, 1, 2014),
69                  unsatisfiedBy(0, 0, 21, 1, 1, 2014),
70                  unsatisfiedBy(0, 0, 23, 1, 1, 2014));
71      }
72  
73      @Test
74      public void testHoursIncrementWrappedRange() {
75          assertCron("17-9/3", "0 0 17-9/3 1 1 ?",
76                  satisfiedBy(0, 0, 2, 1, 1, 2014),
77                  satisfiedBy(0, 0, 5, 1, 1, 2014),
78                  unsatisfiedBy(0, 0, 6, 1, 1, 2014),
79                  satisfiedBy(0, 0, 8, 1, 1, 2014),
80                  unsatisfiedBy(0, 0, 9, 1, 1, 2014),
81                  unsatisfiedBy(0, 0, 11, 1, 1, 2014),
82                  unsatisfiedBy(0, 0, 12, 1, 1, 2014),
83                  unsatisfiedBy(0, 0, 13, 1, 1, 2014),
84                  unsatisfiedBy(0, 0, 14, 1, 1, 2014),
85                  unsatisfiedBy(0, 0, 15, 1, 1, 2014),
86                  unsatisfiedBy(0, 0, 16, 1, 1, 2014),
87                  satisfiedBy(0, 0, 17, 1, 1, 2014),
88                  unsatisfiedBy(0, 0, 18, 1, 1, 2014),
89                  unsatisfiedBy(0, 0, 19, 1, 1, 2014),
90                  satisfiedBy(0, 0, 20, 1, 1, 2014),
91                  unsatisfiedBy(0, 0, 21, 1, 1, 2014),
92                  satisfiedBy(0, 0, 23, 1, 1, 2014));
93      }
94  
95      @Test
96      public void testHoursDaylightSavingsGap() {
97          final DateTimeZone zone = DateTimeZone.forID("Australia/Sydney");
98          final DateTime startingAt = new DateTime(2014, 10, 4, 23, 30, 0, zone);
99          final long base = startingAt.getMillis();
100 
101         assertRunTimes("Australia/Sydney; 2014/10/05; 02 => 03 (On the hour)", "0 0 * * * ?", startingAt,
102                 runTime(0, 0, 0, 5, 10, 2014, zone, base + MINUTES.toMillis(30)),
103                 runTime(0, 0, 1, 5, 10, 2014, zone, base + MINUTES.toMillis(90)),
104                 runTime(0, 0, 3, 5, 10, 2014, zone, base + MINUTES.toMillis(150)),
105                 runTime(0, 0, 4, 5, 10, 2014, zone, base + MINUTES.toMillis(210)));
106 
107         assertRunTimes("Australia/Sydney; 2014/10/05; 02 => 03 (Within the hour)", "0 30 * * * ?", startingAt,
108                 runTime(0, 30, 0, 5, 10, 2014, zone, base + MINUTES.toMillis(60)),
109                 runTime(0, 30, 1, 5, 10, 2014, zone, base + MINUTES.toMillis(120)),
110                 runTime(0, 30, 3, 5, 10, 2014, zone, base + MINUTES.toMillis(180)),
111                 runTime(0, 30, 4, 5, 10, 2014, zone, base + MINUTES.toMillis(240)));
112     }
113 
114     @Test
115     public void testHoursDaylightSavingsOverlap() {
116         final DateTimeZone zone = DateTimeZone.forID("Australia/Sydney");
117         final DateTime startingAt = new DateTime(2014, 4, 5, 23, 30, 0, zone);
118         final long base = startingAt.getMillis();
119 
120         // The first 01:30 runs, the second one is skipped, and we run again at 02:30.  The result is
121         // that the runs at 01:30 and 02:30 are 2 hours apart instead of 1 hour.
122         assertRunTimes("Australia/Sydney; 2014/04/06", "0 30 * * * ?", startingAt,
123                 runTime(0, 30, 0, 6, 4, 2014, zone, base + MINUTES.toMillis(60)),
124                 runTime(0, 30, 1, 6, 4, 2014, zone, base + MINUTES.toMillis(120)),
125                 runTime(0, 30, 2, 6, 4, 2014, zone, base + MINUTES.toMillis(240)),
126                 runTime(0, 30, 3, 6, 4, 2014, zone, base + MINUTES.toMillis(300)),
127                 runTime(0, 30, 4, 6, 4, 2014, zone, base + MINUTES.toMillis(360)));
128     }
129 
130     /**
131      * This is probably not the behaviour that we really want, but unfortunately it's how the cron schedules are
132      * defined to work.  You may think you have scheduled something that will run once a minute, but it will not
133      * run during the DST overlap.
134      */
135     @Test
136     public void testHoursDaylightSavingsOverlapEveryMinute() {
137         final DateTimeZone zone = DateTimeZone.forID("Australia/Sydney");
138         final DateTime startingAt = new DateTime(2014, 4, 6, 1, 57, 0, zone);
139         final long base = startingAt.getMillis();
140 
141         // So it means once a minute, but the DST overlap causes us to skip a whole hour of it. :(
142         assertRunTimes("Australia/Sydney; 2014/04/06", "0 * * * * ?", startingAt,
143                 runTime(0, 58, 1, 6, 4, 2014, zone, base + MINUTES.toMillis(1)),
144                 runTime(0, 59, 1, 6, 4, 2014, zone, base + MINUTES.toMillis(2)),
145                 runTime(0, 0, 2, 6, 4, 2014, zone, base + MINUTES.toMillis(63)),
146                 runTime(0, 1, 2, 6, 4, 2014, zone, base + MINUTES.toMillis(64)));
147     }
148 
149     /**
150      * Lord Howe Island, Australia just had to be different.
151      * <p>
152      * Its daylight savings transition is 30 minutes instead of a full hour.  Welcome to crazy town!
153      * </p>
154      */
155     @Test
156     public void testHoursDaylightSavingsGapLordHowe() {
157         final DateTimeZone zone = DateTimeZone.forID("Australia/Lord_Howe");
158         final DateTime startingAt = new DateTime(2014, 10, 4, 23, 30, 0, zone);
159         final long base = startingAt.getMillis();
160 
161         assertRunTimes("Australia/Lord_Howe; 2014/10/05", "0 0 * * * ?", startingAt,
162                 runTime(0, 0, 0, 5, 10, 2014, zone, base + MINUTES.toMillis(30)),
163                 runTime(0, 0, 1, 5, 10, 2014, zone, base + MINUTES.toMillis(90)),
164                 runTime(0, 0, 3, 5, 10, 2014, zone, base + MINUTES.toMillis(180)),
165                 runTime(0, 0, 4, 5, 10, 2014, zone, base + MINUTES.toMillis(240)));
166     }
167 
168     @Test
169     public void testHoursDaylightSavingsOverlapLordHowe() {
170 
171         final DateTimeZone zone = DateTimeZone.forID("Australia/Lord_Howe");
172         final DateTime startingAt = new DateTime(2014, 4, 5, 23, 30, 0, zone);
173         final long base = startingAt.getMillis();
174 
175         // 00:30 and 01:30 are *90 minutes* apart.
176         assertRunTimes("Australia/Lord_Howe; 2014/04/06", "0 30 * * * ?", startingAt,
177                 runTime(0, 30, 0, 6, 4, 2014, zone, base + MINUTES.toMillis(60)),
178                 runTime(0, 30, 1, 6, 4, 2014, zone, base + MINUTES.toMillis(150)),
179                 runTime(0, 30, 2, 6, 4, 2014, zone, base + MINUTES.toMillis(210)),
180                 runTime(0, 30, 3, 6, 4, 2014, zone, base + MINUTES.toMillis(270)),
181                 runTime(0, 30, 4, 6, 4, 2014, zone, base + MINUTES.toMillis(330)));
182     }
183 
184 }