1 package cron
2
3 import (
4 "strings"
5 "testing"
6 "time"
7 )
8
9 func TestActivation(t *testing.T) {
10 tests := []struct {
11 time, spec string
12 expected bool
13 }{
14
15 {"Mon Jul 9 15:00 2012", "0/15 * * * *", true},
16 {"Mon Jul 9 15:45 2012", "0/15 * * * *", true},
17 {"Mon Jul 9 15:40 2012", "0/15 * * * *", false},
18
19
20 {"Mon Jul 9 15:05 2012", "5/15 * * * *", true},
21 {"Mon Jul 9 15:20 2012", "5/15 * * * *", true},
22 {"Mon Jul 9 15:50 2012", "5/15 * * * *", true},
23
24
25 {"Sun Jul 15 15:00 2012", "0/15 * * Jul *", true},
26 {"Sun Jul 15 15:00 2012", "0/15 * * Jun *", false},
27
28
29 {"Sun Jul 15 08:30 2012", "30 08 ? Jul Sun", true},
30 {"Sun Jul 15 08:30 2012", "30 08 15 Jul ?", true},
31 {"Mon Jul 16 08:30 2012", "30 08 ? Jul Sun", false},
32 {"Mon Jul 16 08:30 2012", "30 08 15 Jul ?", false},
33
34
35 {"Mon Jul 9 15:00 2012", "@hourly", true},
36 {"Mon Jul 9 15:04 2012", "@hourly", false},
37 {"Mon Jul 9 15:00 2012", "@daily", false},
38 {"Mon Jul 9 00:00 2012", "@daily", true},
39 {"Mon Jul 9 00:00 2012", "@weekly", false},
40 {"Sun Jul 8 00:00 2012", "@weekly", true},
41 {"Sun Jul 8 01:00 2012", "@weekly", false},
42 {"Sun Jul 8 00:00 2012", "@monthly", false},
43 {"Sun Jul 1 00:00 2012", "@monthly", true},
44
45
46
47 {"Sun Jul 15 00:00 2012", "* * 1,15 * Sun", true},
48 {"Fri Jun 15 00:00 2012", "* * 1,15 * Sun", true},
49 {"Wed Aug 1 00:00 2012", "* * 1,15 * Sun", true},
50 {"Sun Jul 15 00:00 2012", "* * */10 * Sun", true},
51
52
53 {"Sun Jul 15 00:00 2012", "* * * * Mon", false},
54 {"Mon Jul 9 00:00 2012", "* * 1,15 * *", false},
55 {"Sun Jul 15 00:00 2012", "* * 1,15 * *", true},
56 {"Sun Jul 15 00:00 2012", "* * */2 * Sun", true},
57 }
58
59 for _, test := range tests {
60 sched, err := ParseStandard(test.spec)
61 if err != nil {
62 t.Error(err)
63 continue
64 }
65 actual := sched.Next(getTime(test.time).Add(-1 * time.Second))
66 expected := getTime(test.time)
67 if test.expected && expected != actual || !test.expected && expected == actual {
68 t.Errorf("Fail evaluating %s on %s: (expected) %s != %s (actual)",
69 test.spec, test.time, expected, actual)
70 }
71 }
72 }
73
74 func TestNext(t *testing.T) {
75 runs := []struct {
76 time, spec string
77 expected string
78 }{
79
80 {"Mon Jul 9 14:45 2012", "0 0/15 * * * *", "Mon Jul 9 15:00 2012"},
81 {"Mon Jul 9 14:59 2012", "0 0/15 * * * *", "Mon Jul 9 15:00 2012"},
82 {"Mon Jul 9 14:59:59 2012", "0 0/15 * * * *", "Mon Jul 9 15:00 2012"},
83
84
85 {"Mon Jul 9 15:45 2012", "0 20-35/15 * * * *", "Mon Jul 9 16:20 2012"},
86
87
88 {"Mon Jul 9 23:46 2012", "0 */15 * * * *", "Tue Jul 10 00:00 2012"},
89 {"Mon Jul 9 23:45 2012", "0 20-35/15 * * * *", "Tue Jul 10 00:20 2012"},
90 {"Mon Jul 9 23:35:51 2012", "15/35 20-35/15 * * * *", "Tue Jul 10 00:20:15 2012"},
91 {"Mon Jul 9 23:35:51 2012", "15/35 20-35/15 1/2 * * *", "Tue Jul 10 01:20:15 2012"},
92 {"Mon Jul 9 23:35:51 2012", "15/35 20-35/15 10-12 * * *", "Tue Jul 10 10:20:15 2012"},
93
94 {"Mon Jul 9 23:35:51 2012", "15/35 20-35/15 1/2 */2 * *", "Thu Jul 11 01:20:15 2012"},
95 {"Mon Jul 9 23:35:51 2012", "15/35 20-35/15 * 9-20 * *", "Wed Jul 10 00:20:15 2012"},
96 {"Mon Jul 9 23:35:51 2012", "15/35 20-35/15 * 9-20 Jul *", "Wed Jul 10 00:20:15 2012"},
97
98
99 {"Mon Jul 9 23:35 2012", "0 0 0 9 Apr-Oct ?", "Thu Aug 9 00:00 2012"},
100 {"Mon Jul 9 23:35 2012", "0 0 0 */5 Apr,Aug,Oct Mon", "Tue Aug 1 00:00 2012"},
101 {"Mon Jul 9 23:35 2012", "0 0 0 */5 Oct Mon", "Mon Oct 1 00:00 2012"},
102
103
104 {"Mon Jul 9 23:35 2012", "0 0 0 * Feb Mon", "Mon Feb 4 00:00 2013"},
105 {"Mon Jul 9 23:35 2012", "0 0 0 * Feb Mon/2", "Fri Feb 1 00:00 2013"},
106
107
108 {"Mon Dec 31 23:59:45 2012", "0 * * * * *", "Tue Jan 1 00:00:00 2013"},
109
110
111 {"Mon Jul 9 23:35 2012", "0 0 0 29 Feb ?", "Mon Feb 29 00:00 2016"},
112
113
114 {"2012-03-11T00:00:00-0500", "TZ=America/New_York 0 30 2 11 Mar ?", "2013-03-11T02:30:00-0400"},
115
116
117 {"2012-03-11T00:00:00-0500", "TZ=America/New_York 0 0 * * * ?", "2012-03-11T01:00:00-0500"},
118 {"2012-03-11T01:00:00-0500", "TZ=America/New_York 0 0 * * * ?", "2012-03-11T03:00:00-0400"},
119 {"2012-03-11T03:00:00-0400", "TZ=America/New_York 0 0 * * * ?", "2012-03-11T04:00:00-0400"},
120 {"2012-03-11T04:00:00-0400", "TZ=America/New_York 0 0 * * * ?", "2012-03-11T05:00:00-0400"},
121
122
123 {"2012-03-11T00:00:00-0500", "CRON_TZ=America/New_York 0 0 * * * ?", "2012-03-11T01:00:00-0500"},
124 {"2012-03-11T01:00:00-0500", "CRON_TZ=America/New_York 0 0 * * * ?", "2012-03-11T03:00:00-0400"},
125 {"2012-03-11T03:00:00-0400", "CRON_TZ=America/New_York 0 0 * * * ?", "2012-03-11T04:00:00-0400"},
126 {"2012-03-11T04:00:00-0400", "CRON_TZ=America/New_York 0 0 * * * ?", "2012-03-11T05:00:00-0400"},
127
128
129 {"2012-03-11T00:00:00-0500", "TZ=America/New_York 0 0 1 * * ?", "2012-03-11T01:00:00-0500"},
130 {"2012-03-11T01:00:00-0500", "TZ=America/New_York 0 0 1 * * ?", "2012-03-12T01:00:00-0400"},
131
132
133 {"2012-03-11T00:00:00-0500", "TZ=America/New_York 0 0 2 * * ?", "2012-03-12T02:00:00-0400"},
134
135
136 {"2012-11-04T00:00:00-0400", "TZ=America/New_York 0 30 2 04 Nov ?", "2012-11-04T02:30:00-0500"},
137 {"2012-11-04T01:45:00-0400", "TZ=America/New_York 0 30 1 04 Nov ?", "2012-11-04T01:30:00-0500"},
138
139
140 {"2012-11-04T00:00:00-0400", "TZ=America/New_York 0 0 * * * ?", "2012-11-04T01:00:00-0400"},
141 {"2012-11-04T01:00:00-0400", "TZ=America/New_York 0 0 * * * ?", "2012-11-04T01:00:00-0500"},
142 {"2012-11-04T01:00:00-0500", "TZ=America/New_York 0 0 * * * ?", "2012-11-04T02:00:00-0500"},
143
144
145 {"2012-11-04T00:00:00-0400", "TZ=America/New_York 0 0 1 * * ?", "2012-11-04T01:00:00-0400"},
146 {"2012-11-04T01:00:00-0400", "TZ=America/New_York 0 0 1 * * ?", "2012-11-04T01:00:00-0500"},
147 {"2012-11-04T01:00:00-0500", "TZ=America/New_York 0 0 1 * * ?", "2012-11-05T01:00:00-0500"},
148
149
150 {"2012-11-04T00:00:00-0400", "TZ=America/New_York 0 0 2 * * ?", "2012-11-04T02:00:00-0500"},
151 {"2012-11-04T02:00:00-0500", "TZ=America/New_York 0 0 2 * * ?", "2012-11-05T02:00:00-0500"},
152
153
154 {"2012-11-04T00:00:00-0400", "TZ=America/New_York 0 0 3 * * ?", "2012-11-04T03:00:00-0500"},
155 {"2012-11-04T03:00:00-0500", "TZ=America/New_York 0 0 3 * * ?", "2012-11-05T03:00:00-0500"},
156
157
158 {"TZ=America/New_York 2012-11-04T00:00:00-0400", "0 0 * * * ?", "2012-11-04T01:00:00-0400"},
159 {"TZ=America/New_York 2012-11-04T01:00:00-0400", "0 0 * * * ?", "2012-11-04T01:00:00-0500"},
160 {"TZ=America/New_York 2012-11-04T01:00:00-0500", "0 0 * * * ?", "2012-11-04T02:00:00-0500"},
161
162
163 {"TZ=America/New_York 2012-11-04T00:00:00-0400", "0 0 1 * * ?", "2012-11-04T01:00:00-0400"},
164 {"TZ=America/New_York 2012-11-04T01:00:00-0400", "0 0 1 * * ?", "2012-11-04T01:00:00-0500"},
165 {"TZ=America/New_York 2012-11-04T01:00:00-0500", "0 0 1 * * ?", "2012-11-05T01:00:00-0500"},
166
167
168 {"TZ=America/New_York 2012-11-04T00:00:00-0400", "0 0 2 * * ?", "2012-11-04T02:00:00-0500"},
169 {"TZ=America/New_York 2012-11-04T02:00:00-0500", "0 0 2 * * ?", "2012-11-05T02:00:00-0500"},
170
171
172 {"TZ=America/New_York 2012-11-04T00:00:00-0400", "0 0 3 * * ?", "2012-11-04T03:00:00-0500"},
173 {"TZ=America/New_York 2012-11-04T03:00:00-0500", "0 0 3 * * ?", "2012-11-05T03:00:00-0500"},
174
175
176 {"Mon Jul 9 23:35 2012", "0 0 0 30 Feb ?", ""},
177 {"Mon Jul 9 23:35 2012", "0 0 0 31 Apr ?", ""},
178
179
180 {"TZ=America/New_York 2012-11-04T00:00:00-0400", "0 0 3 3 * ?", "2012-12-03T03:00:00-0500"},
181
182
183
184 {"2018-10-17T05:00:00-0400", "TZ=America/Sao_Paulo 0 0 9 10 * ?", "2018-11-10T06:00:00-0500"},
185 {"2018-02-14T05:00:00-0500", "TZ=America/Sao_Paulo 0 0 9 22 * ?", "2018-02-22T07:00:00-0500"},
186 }
187
188 for _, c := range runs {
189 sched, err := secondParser.Parse(c.spec)
190 if err != nil {
191 t.Error(err)
192 continue
193 }
194 actual := sched.Next(getTime(c.time))
195 expected := getTime(c.expected)
196 if !actual.Equal(expected) {
197 t.Errorf("%s, \"%s\": (expected) %v != %v (actual)", c.time, c.spec, expected, actual)
198 }
199 }
200 }
201
202 func TestErrors(t *testing.T) {
203 invalidSpecs := []string{
204 "xyz",
205 "60 0 * * *",
206 "0 60 * * *",
207 "0 0 * * XYZ",
208 }
209 for _, spec := range invalidSpecs {
210 _, err := ParseStandard(spec)
211 if err == nil {
212 t.Error("expected an error parsing: ", spec)
213 }
214 }
215 }
216
217 func getTime(value string) time.Time {
218 if value == "" {
219 return time.Time{}
220 }
221
222 var location = time.Local
223 if strings.HasPrefix(value, "TZ=") {
224 parts := strings.Fields(value)
225 loc, err := time.LoadLocation(parts[0][len("TZ="):])
226 if err != nil {
227 panic("could not parse location:" + err.Error())
228 }
229 location = loc
230 value = parts[1]
231 }
232
233 var layouts = []string{
234 "Mon Jan 2 15:04 2006",
235 "Mon Jan 2 15:04:05 2006",
236 }
237 for _, layout := range layouts {
238 if t, err := time.ParseInLocation(layout, value, location); err == nil {
239 return t
240 }
241 }
242 if t, err := time.ParseInLocation("2006-01-02T15:04:05-0700", value, location); err == nil {
243 return t
244 }
245 panic("could not parse time value " + value)
246 }
247
248 func TestNextWithTz(t *testing.T) {
249 runs := []struct {
250 time, spec string
251 expected string
252 }{
253
254 {"2016-01-03T13:09:03+0530", "14 14 * * *", "2016-01-03T14:14:00+0530"},
255 {"2016-01-03T04:09:03+0530", "14 14 * * ?", "2016-01-03T14:14:00+0530"},
256
257
258 {"2016-01-03T14:09:03+0530", "14 14 * * *", "2016-01-03T14:14:00+0530"},
259 {"2016-01-03T14:00:00+0530", "14 14 * * ?", "2016-01-03T14:14:00+0530"},
260 }
261 for _, c := range runs {
262 sched, err := ParseStandard(c.spec)
263 if err != nil {
264 t.Error(err)
265 continue
266 }
267 actual := sched.Next(getTimeTZ(c.time))
268 expected := getTimeTZ(c.expected)
269 if !actual.Equal(expected) {
270 t.Errorf("%s, \"%s\": (expected) %v != %v (actual)", c.time, c.spec, expected, actual)
271 }
272 }
273 }
274
275 func getTimeTZ(value string) time.Time {
276 if value == "" {
277 return time.Time{}
278 }
279 t, err := time.Parse("Mon Jan 2 15:04 2006", value)
280 if err != nil {
281 t, err = time.Parse("Mon Jan 2 15:04:05 2006", value)
282 if err != nil {
283 t, err = time.Parse("2006-01-02T15:04:05-0700", value)
284 if err != nil {
285 panic(err)
286 }
287 }
288 }
289
290 return t
291 }
292
293
294 func TestSlash0NoHang(t *testing.T) {
295 schedule := "TZ=America/New_York 15/0 * * * *"
296 _, err := ParseStandard(schedule)
297 if err == nil {
298 t.Error("expected an error on 0 increment")
299 }
300 }
301
View as plain text