rufus-scheduler 2.0.24 → 3.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (63) hide show
  1. data/CHANGELOG.txt +76 -0
  2. data/CREDITS.txt +23 -0
  3. data/LICENSE.txt +1 -1
  4. data/README.md +1439 -0
  5. data/Rakefile +1 -5
  6. data/TODO.txt +149 -55
  7. data/lib/rufus/{sc → scheduler}/cronline.rb +167 -53
  8. data/lib/rufus/scheduler/job_array.rb +92 -0
  9. data/lib/rufus/scheduler/jobs.rb +633 -0
  10. data/lib/rufus/scheduler/locks.rb +95 -0
  11. data/lib/rufus/scheduler/util.rb +306 -0
  12. data/lib/rufus/scheduler/zones.rb +174 -0
  13. data/lib/rufus/scheduler/zotime.rb +154 -0
  14. data/lib/rufus/scheduler.rb +608 -27
  15. data/rufus-scheduler.gemspec +6 -4
  16. data/spec/basics_spec.rb +54 -0
  17. data/spec/cronline_spec.rb +479 -152
  18. data/spec/error_spec.rb +139 -0
  19. data/spec/job_array_spec.rb +39 -0
  20. data/spec/job_at_spec.rb +58 -0
  21. data/spec/job_cron_spec.rb +128 -0
  22. data/spec/job_every_spec.rb +104 -0
  23. data/spec/job_in_spec.rb +20 -0
  24. data/spec/job_interval_spec.rb +68 -0
  25. data/spec/job_repeat_spec.rb +357 -0
  26. data/spec/job_spec.rb +498 -109
  27. data/spec/lock_custom_spec.rb +47 -0
  28. data/spec/lock_flock_spec.rb +47 -0
  29. data/spec/lock_lockfile_spec.rb +61 -0
  30. data/spec/lock_spec.rb +59 -0
  31. data/spec/parse_spec.rb +263 -0
  32. data/spec/schedule_at_spec.rb +158 -0
  33. data/spec/schedule_cron_spec.rb +66 -0
  34. data/spec/schedule_every_spec.rb +109 -0
  35. data/spec/schedule_in_spec.rb +80 -0
  36. data/spec/schedule_interval_spec.rb +128 -0
  37. data/spec/scheduler_spec.rb +928 -124
  38. data/spec/spec_helper.rb +126 -0
  39. data/spec/threads_spec.rb +96 -0
  40. data/spec/zotime_spec.rb +396 -0
  41. metadata +56 -33
  42. data/README.rdoc +0 -661
  43. data/lib/rufus/otime.rb +0 -3
  44. data/lib/rufus/sc/jobqueues.rb +0 -160
  45. data/lib/rufus/sc/jobs.rb +0 -471
  46. data/lib/rufus/sc/rtime.rb +0 -363
  47. data/lib/rufus/sc/scheduler.rb +0 -636
  48. data/lib/rufus/sc/version.rb +0 -32
  49. data/spec/at_in_spec.rb +0 -47
  50. data/spec/at_spec.rb +0 -125
  51. data/spec/blocking_spec.rb +0 -64
  52. data/spec/cron_spec.rb +0 -134
  53. data/spec/every_spec.rb +0 -304
  54. data/spec/exception_spec.rb +0 -113
  55. data/spec/in_spec.rb +0 -150
  56. data/spec/mutex_spec.rb +0 -159
  57. data/spec/rtime_spec.rb +0 -137
  58. data/spec/schedulable_spec.rb +0 -97
  59. data/spec/spec_base.rb +0 -87
  60. data/spec/stress_schedule_unschedule_spec.rb +0 -159
  61. data/spec/timeout_spec.rb +0 -148
  62. data/test/kjw.rb +0 -113
  63. data/test/t.rb +0 -20
@@ -5,23 +5,41 @@
5
5
  # Sat Mar 21 12:55:27 JST 2009
6
6
  #
7
7
 
8
- require 'spec_base'
8
+ require 'spec_helper'
9
9
 
10
10
 
11
- describe Rufus::CronLine do
11
+ describe Rufus::Scheduler::CronLine do
12
12
 
13
13
  def cl(cronline_string)
14
- Rufus::CronLine.new(cronline_string)
14
+ Rufus::Scheduler::CronLine.new(cronline_string)
15
+ end
16
+
17
+ def nt(cronline, now)
18
+ Rufus::Scheduler::CronLine.new(cronline).next_time(now)
19
+ end
20
+ def ntz(cronline, now)
21
+ tz = cronline.split.last
22
+ tu = nt(cronline, now).utc
23
+ in_zone(tz) { tu.getlocal }
24
+ end
25
+
26
+ def pt(cronline, now)
27
+ Rufus::Scheduler::CronLine.new(cronline).previous_time(now)
28
+ end
29
+ def ptz(cronline, now)
30
+ tz = cronline.split.last
31
+ tu = pt(cronline, now).utc
32
+ in_zone(tz) { tu.getlocal }
15
33
  end
16
34
 
17
35
  def match(line, time)
18
- cl(line).matches?(time).should == true
36
+ expect(cl(line).matches?(time)).to eq(true)
19
37
  end
20
38
  def no_match(line, time)
21
- cl(line).matches?(time).should == false
39
+ expect(cl(line).matches?(time)).to eq(false)
22
40
  end
23
41
  def to_a(line, array)
24
- cl(line).to_array.should == array
42
+ expect(cl(line).to_array).to eq(array)
25
43
  end
26
44
 
27
45
  describe '.new' do
@@ -48,43 +66,61 @@ describe Rufus::CronLine do
48
66
 
49
67
  to_a '0 0 1 1 *', [ [0], [0], [0], [1], [1], nil, nil, nil ]
50
68
 
51
- to_a '0 23-24 * * *', [ [0], [0], [23, 0], nil, nil, nil, nil, nil ]
69
+ if ruby18?
70
+ to_a '0 23-24 * * *', [ [0], [0], [0, 23], nil, nil, nil, nil, nil ]
71
+ else
72
+ to_a '0 23-24 * * *', [ [0], [0], [23, 0], nil, nil, nil, nil, nil ]
73
+ end
52
74
  #
53
75
  # as reported by Aimee Rose in
54
76
  # https://github.com/jmettraux/rufus-scheduler/issues/56
55
77
 
56
- to_a '0 23-2 * * *', [ [0], [0], [23, 0, 1, 2], nil, nil, nil, nil, nil ]
78
+ if ruby18?
79
+ to_a '0 23-2 * * *', [ [0], [0], [0, 1, 2, 23], nil, nil, nil, nil, nil ]
80
+ else
81
+ to_a '0 23-2 * * *', [ [0], [0], [23, 0, 1, 2], nil, nil, nil, nil, nil ]
82
+ end
57
83
  end
58
84
 
59
85
  it 'rejects invalid weekday expressions' do
60
86
 
61
- lambda { cl '0 17 * * MON_FRI' }.should raise_error
87
+ expect { cl '0 17 * * MON_FRI' }.to raise_error
62
88
  # underline instead of dash
63
89
 
64
- lambda { cl '* * * * 9' }.should raise_error
65
- lambda { cl '* * * * 0-12' }.should raise_error
66
- lambda { cl '* * * * BLABLA' }.should raise_error
90
+ expect { cl '* * * * 9' }.to raise_error
91
+ expect { cl '* * * * 0-12' }.to raise_error
92
+ expect { cl '* * * * BLABLA' }.to raise_error
67
93
  end
68
94
 
69
95
  it 'rejects invalid cronlines' do
70
96
 
71
- lambda { cl '* nada * * 9' }.should raise_error(ArgumentError)
97
+ expect { cl '* nada * * 9' }.to raise_error(ArgumentError)
72
98
  end
73
99
 
74
100
  it 'interprets cron strings with TZ correctly' do
75
101
 
76
- to_a '* * * * * EST', [ [0], nil, nil, nil, nil, nil, nil, 'EST' ]
77
- to_a '* * * * * * EST', [ nil, nil, nil, nil, nil, nil, nil, 'EST' ]
102
+ to_a('* * * * * EST', [ [0], nil, nil, nil, nil, nil, nil, 'EST' ])
103
+ to_a('* * * * * * EST', [ nil, nil, nil, nil, nil, nil, nil, 'EST' ])
104
+
105
+ to_a(
106
+ '* * * * * * America/Chicago',
107
+ [ nil, nil, nil, nil, nil, nil, nil, 'America/Chicago' ])
108
+ to_a(
109
+ '* * * * * * America/New_York',
110
+ [ nil, nil, nil, nil, nil, nil, nil, 'America/New_York' ])
78
111
 
79
- lambda { cl '* * * * * NotATimeZone' }.should raise_error
80
- lambda { cl '* * * * * * NotATimeZone' }.should raise_error
112
+ expect { cl '* * * * * NotATimeZone' }.to raise_error
113
+ expect { cl '* * * * * * NotATimeZone' }.to raise_error
81
114
  end
82
115
 
83
116
  it 'interprets cron strings with / (slashes) correctly' do
84
117
 
85
118
  to_a(
86
119
  '0 */2 * * *',
87
- [ [0], [0], (0..11).collect { |e| e * 2 }, nil, nil, nil, nil, nil ])
120
+ [ [0], [0], (0..23).select { |e| e.even? }, nil, nil, nil, nil, nil ])
121
+ to_a(
122
+ '0 0-23/2 * * *',
123
+ [ [0], [0], (0..23).select { |e| e.even? }, nil, nil, nil, nil, nil ])
88
124
  to_a(
89
125
  '0 7-23/2 * * *',
90
126
  [ [0], [0], (7..23).select { |e| e.odd? }, nil, nil, nil, nil, nil ])
@@ -108,54 +144,71 @@ describe Rufus::CronLine do
108
144
  [ [0], [ 0, 10, 20, 30, 40, 50], nil, nil, nil, nil, nil, nil ])
109
145
  end
110
146
 
147
+ it 'rejects / for days (every other wednesday)' do
148
+
149
+ expect {
150
+ Rufus::Scheduler::CronLine.new('* * * * wed/2')
151
+ }.to raise_error(ArgumentError)
152
+ end
153
+
111
154
  it 'does not support ranges for monthdays (sun#1-sun#2)' do
112
155
 
113
- lambda {
114
- Rufus::CronLine.new('* * * * sun#1-sun#2')
115
- }.should raise_error(ArgumentError)
156
+ expect {
157
+ Rufus::Scheduler::CronLine.new('* * * * sun#1-sun#2')
158
+ }.to raise_error(ArgumentError)
116
159
  end
117
160
 
118
161
  it 'accepts items with initial 0' do
119
162
 
120
- to_a '09 * * * *', [ [0], [9], nil, nil, nil, nil, nil, nil ]
121
- to_a '09-12 * * * *', [ [0], [9, 10, 11, 12], nil, nil, nil, nil, nil, nil ]
122
- to_a '07-08 * * * *', [ [0], [7, 8], nil, nil, nil, nil, nil, nil ]
123
- to_a '* */08 * * *', [ [0], nil, [0, 8, 16], nil, nil, nil, nil, nil ]
124
- to_a '* */07 * * *', [ [0], nil, [0, 7, 14, 21], nil, nil, nil, nil, nil ]
125
- to_a '* 01-09/04 * * *', [ [0], nil, [1, 5, 9], nil, nil, nil, nil, nil ]
126
- to_a '* * * * 06', [ [0], nil, nil, nil, nil, [6], nil, nil ]
163
+ to_a(
164
+ '09 * * * *', [ [0], [9], nil, nil, nil, nil, nil, nil ])
165
+ to_a(
166
+ '09-12 * * * *', [ [0], [9, 10, 11, 12], nil, nil, nil, nil, nil, nil ])
167
+ to_a(
168
+ '07-08 * * * *', [ [0], [7, 8], nil, nil, nil, nil, nil, nil ])
169
+ to_a(
170
+ '* */08 * * *', [ [0], nil, [0, 8, 16], nil, nil, nil, nil, nil ])
171
+ to_a(
172
+ '* */07 * * *', [ [0], nil, [0, 7, 14, 21], nil, nil, nil, nil, nil ])
173
+ to_a(
174
+ '* 01-09/04 * * *', [ [0], nil, [1, 5, 9], nil, nil, nil, nil, nil ])
175
+ to_a(
176
+ '* * * * 06', [ [0], nil, nil, nil, nil, [6], nil, nil ])
127
177
  end
128
178
 
129
179
  it 'interprets cron strings with L correctly' do
130
180
 
131
- to_a '* * L * *', [[0], nil, nil, ['L'], nil, nil, nil, nil ]
132
- to_a '* * 2-5,L * *', [[0], nil, nil, [2,3,4,5,'L'], nil, nil, nil, nil ]
133
- to_a '* * */8,L * *', [[0], nil, nil, [1,9,17,25,'L'], nil, nil, nil, nil ]
181
+ to_a(
182
+ '* * L * *', [[0], nil, nil, ['L'], nil, nil, nil, nil ])
183
+ to_a(
184
+ '* * 2-5,L * *', [[0], nil, nil, [2,3,4,5,'L'], nil, nil, nil, nil ])
185
+ to_a(
186
+ '* * */8,L * *', [[0], nil, nil, [1,9,17,25,'L'], nil, nil, nil, nil ])
134
187
  end
135
188
 
136
189
  it 'does not support ranges for L' do
137
190
 
138
- lambda { cl '* * 15-L * *'}.should raise_error(ArgumentError)
139
- lambda { cl '* * L/4 * *'}.should raise_error(ArgumentError)
191
+ expect { cl '* * 15-L * *'}.to raise_error(ArgumentError)
192
+ expect { cl '* * L/4 * *'}.to raise_error(ArgumentError)
140
193
  end
141
194
 
142
195
  it 'does not support multiple Ls' do
143
196
 
144
- lambda { cl '* * L,L * *'}.should raise_error(ArgumentError)
197
+ expect { cl '* * L,L * *'}.to raise_error(ArgumentError)
145
198
  end
146
199
 
147
200
  it 'raises if L is used for something else than days' do
148
201
 
149
- lambda { cl '* L * * *'}.should raise_error(ArgumentError)
202
+ expect { cl '* L * * *'}.to raise_error(ArgumentError)
150
203
  end
151
204
 
152
205
  it 'raises for out of range input' do
153
206
 
154
- lambda { cl '60-62 * * * *'}.should raise_error(ArgumentError)
155
- lambda { cl '62 * * * *'}.should raise_error(ArgumentError)
156
- lambda { cl '60 * * * *'}.should raise_error(ArgumentError)
157
- lambda { cl '* 25-26 * * *'}.should raise_error(ArgumentError)
158
- lambda { cl '* 25 * * *'}.should raise_error(ArgumentError)
207
+ expect { cl '60-62 * * * *'}.to raise_error(ArgumentError)
208
+ expect { cl '62 * * * *'}.to raise_error(ArgumentError)
209
+ expect { cl '60 * * * *'}.to raise_error(ArgumentError)
210
+ expect { cl '* 25-26 * * *'}.to raise_error(ArgumentError)
211
+ expect { cl '* 25 * * *'}.to raise_error(ArgumentError)
159
212
  #
160
213
  # as reported by Aimee Rose in
161
214
  # https://github.com/jmettraux/rufus-scheduler/pull/58
@@ -164,171 +217,223 @@ describe Rufus::CronLine do
164
217
 
165
218
  describe '#next_time' do
166
219
 
167
- def nt(cronline, now)
168
- Rufus::CronLine.new(cronline).next_time(now)
169
- end
170
-
171
220
  it 'computes the next occurence correctly' do
172
221
 
173
- now = Time.at(0).getutc # Thu Jan 01 00:00:00 UTC 1970
222
+ in_zone 'Europe/Berlin' do
174
223
 
175
- nt('* * * * *', now).should == now + 60
176
- nt('* * * * sun', now).should == now + 259200
177
- nt('* * * * * *', now).should == now + 1
178
- nt('* * 13 * fri', now).should == now + 3715200
224
+ now = Time.at(0) - 3600
179
225
 
180
- nt('10 12 13 12 *', now).should == now + 29938200
181
- # this one is slow (1 year == 3 seconds)
182
- #
183
- # historical note:
184
- # (comment made in 2006 or 2007, the underlying libs got better and
185
- # that slowness is gone)
186
-
187
- nt('0 0 * * thu', now).should == now + 604800
188
-
189
- nt('0 0 * * *', now).should == now + 24 * 3600
190
- nt('0 24 * * *', now).should == now + 24 * 3600
226
+ expect(nt('* * * * *', now)).to eq(now + 60)
227
+ expect(nt('* * * * sun', now)).to eq(now + 259200)
228
+ expect(nt('* * * * * *', now)).to eq(now + 1)
229
+ expect(nt('* * 13 * fri', now)).to eq(now + 3715200)
191
230
 
192
- now = local(2008, 12, 31, 23, 59, 59, 0)
231
+ expect(nt('10 12 13 12 *', now)).to eq(now + 29938200)
232
+ # this one is slow (1 year == 3 seconds)
233
+ #
234
+ # historical note:
235
+ # (comment made in 2006 or 2007, the underlying libs got better and
236
+ # that slowness is gone)
193
237
 
194
- nt('* * * * *', now).should == now + 1
195
- end
196
-
197
- it 'computes the next occurence correctly in UTC (TZ not specified)' do
238
+ expect(nt('0 0 * * thu', now)).to eq(now + 604800)
239
+ expect(nt('00 0 * * thu', now)).to eq(now + 604800)
198
240
 
199
- now = utc(1970, 1, 1)
241
+ expect(nt('0 0 * * *', now)).to eq(now + 24 * 3600)
242
+ expect(nt('0 24 * * *', now)).to eq(now + 24 * 3600)
200
243
 
201
- nt('* * * * *', now).should == utc(1970, 1, 1, 0, 1)
202
- nt('* * * * sun', now).should == utc(1970, 1, 4)
203
- nt('* * * * * *', now).should == utc(1970, 1, 1, 0, 0, 1)
204
- nt('* * 13 * fri', now).should == utc(1970, 2, 13)
205
-
206
- nt('10 12 13 12 *', now).should == utc(1970, 12, 13, 12, 10)
207
- # this one is slow (1 year == 3 seconds)
208
- nt('* * 1 6 *', now).should == utc(1970, 6, 1)
244
+ now = local(2008, 12, 31, 23, 59, 59, 0)
209
245
 
210
- nt('0 0 * * thu', now).should == utc(1970, 1, 8)
246
+ expect(nt('* * * * *', now)).to eq(now + 1)
247
+ end
211
248
  end
212
249
 
213
250
  it 'computes the next occurence correctly in local TZ (TZ not specified)' do
214
251
 
215
252
  now = local(1970, 1, 1)
216
253
 
217
- nt('* * * * *', now).should == local(1970, 1, 1, 0, 1)
218
- nt('* * * * sun', now).should == local(1970, 1, 4)
219
- nt('* * * * * *', now).should == local(1970, 1, 1, 0, 0, 1)
220
- nt('* * 13 * fri', now).should == local(1970, 2, 13)
254
+ expect(nt('* * * * *', now)).to eq(local(1970, 1, 1, 0, 1))
255
+ expect(nt('* * * * sun', now)).to eq(local(1970, 1, 4))
256
+ expect(nt('* * * * * *', now)).to eq(local(1970, 1, 1, 0, 0, 1))
257
+ expect(nt('* * 13 * fri', now)).to eq(local(1970, 2, 13))
221
258
 
222
- nt('10 12 13 12 *', now).should == local(1970, 12, 13, 12, 10)
259
+ expect(nt('10 12 13 12 *', now)).to eq(local(1970, 12, 13, 12, 10))
223
260
  # this one is slow (1 year == 3 seconds)
224
- nt('* * 1 6 *', now).should == local(1970, 6, 1)
261
+ expect(nt('* * 1 6 *', now)).to eq(local(1970, 6, 1))
225
262
 
226
- nt('0 0 * * thu', now).should == local(1970, 1, 8)
263
+ expect(nt('0 0 * * thu', now)).to eq(local(1970, 1, 8))
227
264
  end
228
265
 
229
266
  it 'computes the next occurence correctly in UTC (TZ specified)' do
230
267
 
231
268
  zone = 'Europe/Stockholm'
232
- tz = TZInfo::Timezone.get(zone)
233
- now = tz.local_to_utc(local(1970, 1, 1))
234
- # Midnight in zone, UTC
235
-
236
- nt("* * * * * #{zone}", now).should == utc(1969, 12, 31, 23, 1)
237
- nt("* * * * sun #{zone}", now).should == utc(1970, 1, 3, 23)
238
- nt("* * * * * * #{zone}", now).should == utc(1969, 12, 31, 23, 0, 1)
239
- nt("* * 13 * fri #{zone}", now).should == utc(1970, 2, 12, 23)
240
-
241
- nt("10 12 13 12 * #{zone}", now).should == utc(1970, 12, 13, 11, 10)
242
- nt("* * 1 6 * #{zone}", now).should == utc(1970, 5, 31, 23)
243
-
244
- nt("0 0 * * thu #{zone}", now).should == utc(1970, 1, 7, 23)
245
- end
246
-
247
- #it 'computes the next occurence correctly in local TZ (TZ specified)' do
248
- # zone = 'Europe/Stockholm'
249
- # tz = TZInfo::Timezone.get(zone)
250
- # now = tz.local_to_utc(utc(1970, 1, 1)).localtime
251
- # # Midnight in zone, local time
252
- # nt("* * * * * #{zone}", now).should == local(1969, 12, 31, 18, 1)
253
- # nt("* * * * sun #{zone}", now).should == local(1970, 1, 3, 18)
254
- # nt("* * * * * * #{zone}", now).should == local(1969, 12, 31, 18, 0, 1)
255
- # nt("* * 13 * fri #{zone}", now).should == local(1970, 2, 12, 18)
256
- # nt("10 12 13 12 * #{zone}", now).should == local(1970, 12, 13, 6, 10)
257
- # nt("* * 1 6 * #{zone}", now).should == local(1970, 5, 31, 19)
258
- # nt("0 0 * * thu #{zone}", now).should == local(1970, 1, 7, 18)
259
- #end
269
+ now = in_zone(zone) { Time.local(1970, 1, 1) }
270
+
271
+ expect(nt("* * * * * #{zone}", now)).to eq(utc(1969, 12, 31, 23, 1))
272
+ expect(nt("* * * * sun #{zone}", now)).to eq(utc(1970, 1, 3, 23))
273
+ expect(nt("* * * * * * #{zone}", now)).to eq(utc(1969, 12, 31, 23, 0, 1))
274
+ expect(nt("* * 13 * fri #{zone}", now)).to eq(utc(1970, 2, 12, 23))
275
+
276
+ expect(nt("10 12 13 12 * #{zone}", now)).to eq(utc(1970, 12, 13, 11, 10))
277
+ expect(nt("* * 1 6 * #{zone}", now)).to eq(utc(1970, 5, 31, 23))
278
+
279
+ expect(nt("0 0 * * thu #{zone}", now)).to eq(utc(1970, 1, 7, 23))
280
+ end
260
281
 
261
282
  it 'computes the next time correctly when there is a sun#2 involved' do
262
283
 
263
- nt('* * * * sun#1', local(1970, 1, 1)).should == local(1970, 1, 4)
264
- nt('* * * * sun#2', local(1970, 1, 1)).should == local(1970, 1, 11)
284
+ expect(nt('* * * * sun#1', local(1970, 1, 1))).to eq(local(1970, 1, 4))
285
+ expect(nt('* * * * sun#2', local(1970, 1, 1))).to eq(local(1970, 1, 11))
265
286
 
266
- nt('* * * * sun#2', local(1970, 1, 12)).should == local(1970, 2, 8)
287
+ expect(nt('* * * * sun#2', local(1970, 1, 12))).to eq(local(1970, 2, 8))
267
288
  end
268
289
 
269
- it 'computes the next time correctly when there is a sun#2,sun#3 involved' do
290
+ it 'computes next time correctly when there is a sun#2,sun#3 involved' do
270
291
 
271
- nt('* * * * sun#2,sun#3', local(1970, 1, 1)).should == local(1970, 1, 11)
272
- nt('* * * * sun#2,sun#3', local(1970, 1, 12)).should == local(1970, 1, 18)
292
+ expect(
293
+ nt('* * * * sun#2,sun#3', local(1970, 1, 1))).to eq(local(1970, 1, 11))
294
+ expect(
295
+ nt('* * * * sun#2,sun#3', local(1970, 1, 12))).to eq(local(1970, 1, 18))
273
296
  end
274
297
 
275
298
  it 'understands sun#L' do
276
299
 
277
- nt('* * * * sun#L', local(1970, 1, 1)).should == local(1970, 1, 25)
300
+ expect(nt('* * * * sun#L', local(1970, 1, 1))).to eq(local(1970, 1, 25))
278
301
  end
279
302
 
280
303
  it 'understands sun#-1' do
281
304
 
282
- nt('* * * * sun#-1', local(1970, 1, 1)).should == local(1970, 1, 25)
305
+ expect(nt('* * * * sun#-1', local(1970, 1, 1))).to eq(local(1970, 1, 25))
283
306
  end
284
307
 
285
308
  it 'understands sun#-2' do
286
309
 
287
- nt('* * * * sun#-2', local(1970, 1, 1)).should == local(1970, 1, 18)
310
+ expect(nt('* * * * sun#-2', local(1970, 1, 1))).to eq(local(1970, 1, 18))
288
311
  end
289
312
 
290
313
  it 'computes the next time correctly when "L" (last day of month)' do
291
314
 
292
- nt('* * L * *', lo(1970, 1, 1)).should == lo(1970, 1, 31)
293
- nt('* * L * *', lo(1970, 2, 1)).should == lo(1970, 2, 28)
294
- nt('* * L * *', lo(1972, 2, 1)).should == lo(1972, 2, 29)
295
- nt('* * L * *', lo(1970, 4, 1)).should == lo(1970, 4, 30)
315
+ expect(nt('* * L * *', lo(1970, 1, 1))).to eq(lo(1970, 1, 31))
316
+ expect(nt('* * L * *', lo(1970, 2, 1))).to eq(lo(1970, 2, 28))
317
+ expect(nt('* * L * *', lo(1972, 2, 1))).to eq(lo(1972, 2, 29))
318
+ expect(nt('* * L * *', lo(1970, 4, 1))).to eq(lo(1970, 4, 30))
319
+ end
320
+
321
+ it 'returns a time with subseconds chopped off' do
322
+
323
+ expect(
324
+ nt('* * * * *', Time.now).usec).to eq(0)
325
+ expect(
326
+ nt('* * * * *', Time.now).iso8601(10).match(/\.0+[^\d]/)).not_to eq(nil)
327
+ end
328
+
329
+ # New York EST: UTC-5
330
+ # summer (dst) EDT: UTC-4
331
+
332
+ # gh-127
333
+ #
334
+ it 'survives TZInfo::AmbiguousTime' do
335
+
336
+ if ruby18? or jruby?
337
+ expect(
338
+ ntz(
339
+ '30 1 31 10 * America/New_York',
340
+ ltz('America/New_York', 2004, 10, 1)
341
+ ).strftime('%Y-%m-%d %H:%M:%S')
342
+ ).to eq('2004-10-31 01:30:00')
343
+ else
344
+ expect(
345
+ ntz(
346
+ '30 1 31 10 * America/New_York',
347
+ ltz('America/New_York', 2004, 10, 1)
348
+ )
349
+ ).to eq(ltz('America/New_York', 2004, 10, 31, 1, 30, 0))
350
+ end
296
351
  end
297
- end
298
352
 
299
- describe '#previous_time' do
353
+ # gh-127
354
+ #
355
+ it 'survives TZInfo::PeriodNotFound' do
300
356
 
301
- def pt(cronline, now)
302
- Rufus::CronLine.new(cronline).previous_time(now)
357
+ expect(
358
+ ntz(
359
+ '0 2 9 3 * America/New_York',
360
+ ltz('America/New_York', 2014, 3, 1)
361
+ )
362
+ ).to eq(ltz('America/New_York', 2015, 3, 9, 2, 0, 0))
303
363
  end
364
+ end
365
+
366
+ describe '#previous_time' do
304
367
 
305
368
  it 'returns the previous time the cron should have triggered' do
306
369
 
307
- pt('* * * * sun', lo(1970, 1, 1)).should == lo(1969, 12, 28, 23, 59, 00)
308
- pt('* * 13 * *', lo(1970, 1, 1)).should == lo(1969, 12, 13, 23, 59, 00)
309
- pt('0 12 13 * *', lo(1970, 1, 1)).should == lo(1969, 12, 13, 12, 00)
370
+ expect(
371
+ pt('* * * * sun', lo(1970, 1, 1))).to eq(lo(1969, 12, 28, 23, 59, 00))
372
+ expect(
373
+ pt('* * 13 * *', lo(1970, 1, 1))).to eq(lo(1969, 12, 13, 23, 59, 00))
374
+ expect(
375
+ pt('0 12 13 * *', lo(1970, 1, 1))).to eq(lo(1969, 12, 13, 12, 00))
376
+ expect(
377
+ pt('0 0 2 1 *', lo(1970, 1, 1))).to eq(lo(1969, 1, 2, 0, 00))
378
+
379
+ expect(
380
+ pt('* * * * * sun', lo(1970, 1, 1))).to eq(lo(1969, 12, 28, 23, 59, 59))
381
+ end
382
+
383
+ # New York EST: UTC-5
384
+ # summer (dst) EDT: UTC-4
385
+
386
+ # gh-127
387
+ #
388
+ it 'survives TZInfo::AmbiguousTime' do
389
+
390
+ if ruby18? or jruby?
391
+ expect(
392
+ ptz(
393
+ '30 1 31 10 * America/New_York',
394
+ ltz('America/New_York', 2004, 10, 31, 14, 30, 0)
395
+ ).strftime('%Y-%m-%d %H:%M:%S')
396
+ ).to eq('2004-10-31 01:30:00')
397
+ else
398
+ expect(
399
+ ptz(
400
+ '30 1 31 10 * America/New_York',
401
+ ltz('America/New_York', 2004, 10, 31, 14, 30, 0)
402
+ )
403
+ ).to eq(ltz('America/New_York', 2004, 10, 31, 1, 30, 0))
404
+ end
405
+ end
406
+
407
+ # gh-127
408
+ #
409
+ it 'survives TZInfo::PeriodNotFound' do
310
410
 
311
- pt('* * * * * sun', lo(1970, 1, 1)).should == lo(1969, 12, 28, 23, 59, 59)
411
+ expect(
412
+ ptz(
413
+ '0 2 9 3 * America/New_York',
414
+ ltz('America/New_York', 2015, 3, 9, 12, 0, 0)
415
+ )
416
+ ).to eq(ltz('America/New_York', 2015, 3, 9, 2, 0, 0))
312
417
  end
313
418
  end
314
419
 
315
420
  describe '#matches?' do
316
421
 
317
- it 'matches correctly in UTC (TZ not specified)' do
318
-
319
- match '* * * * *', utc(1970, 1, 1, 0, 1)
320
- match '* * * * sun', utc(1970, 1, 4)
321
- match '* * * * * *', utc(1970, 1, 1, 0, 0, 1)
322
- match '* * 13 * fri', utc(1970, 2, 13)
323
-
324
- match '10 12 13 12 *', utc(1970, 12, 13, 12, 10)
325
- match '* * 1 6 *', utc(1970, 6, 1)
326
-
327
- match '0 0 * * thu', utc(1970, 1, 8)
328
-
329
- match '0 0 1 1 *', utc(2012, 1, 1)
330
- no_match '0 0 1 1 *', utc(2012, 1, 1, 1, 0)
331
- end
422
+ # it 'matches correctly in UTC (TZ not specified)' do
423
+ #
424
+ # match '* * * * *', utc(1970, 1, 1, 0, 1)
425
+ # match '* * * * sun', utc(1970, 1, 4)
426
+ # match '* * * * * *', utc(1970, 1, 1, 0, 0, 1)
427
+ # match '* * 13 * fri', utc(1970, 2, 13)
428
+ #
429
+ # match '10 12 13 12 *', utc(1970, 12, 13, 12, 10)
430
+ # match '* * 1 6 *', utc(1970, 6, 1)
431
+ #
432
+ # match '0 0 * * thu', utc(1970, 1, 8)
433
+ #
434
+ # match '0 0 1 1 *', utc(2012, 1, 1)
435
+ # no_match '0 0 1 1 *', utc(2012, 1, 1, 1, 0)
436
+ # end
332
437
 
333
438
  it 'matches correctly in local TZ (TZ not specified)' do
334
439
 
@@ -380,23 +485,245 @@ describe Rufus::CronLine do
380
485
  match '* * * * sun#2,sun#3', local(1970, 1, 18)
381
486
  no_match '* * * * sun#2,sun#3', local(1970, 1, 25)
382
487
  end
488
+
489
+ it 'matches correctly for seconds' do
490
+
491
+ match '* * * * * *', local(1970, 1, 11)
492
+ match '* * * * * *', local(1970, 1, 11, 0, 0, 13)
493
+ end
494
+
495
+ it 'matches correctly for seconds / interval' do
496
+
497
+ match '*/2 * * * * *', local(1970, 1, 11)
498
+ match '*/5 * * * * *', local(1970, 1, 11)
499
+ match '*/5 * * * * *', local(1970, 1, 11, 0, 0, 0)
500
+ no_match '*/5 * * * * *', local(1970, 1, 11, 0, 0, 1)
501
+ match '*/5 * * * * *', local(1970, 1, 11, 0, 0, 5)
502
+ match '*/2 * * * * *', local(1970, 1, 11, 0, 0, 2)
503
+ match '*/2 * * * * *', local(1970, 1, 11, 0, 0, 2, 500)
504
+ end
383
505
  end
384
506
 
385
507
  describe '#monthdays' do
386
508
 
387
509
  it 'returns the appropriate "sun#2"-like string' do
388
510
 
389
- class Rufus::CronLine
511
+ class Rufus::Scheduler::CronLine
390
512
  public :monthdays
391
513
  end
392
514
 
393
- cl = Rufus::CronLine.new('* * * * *')
515
+ cl = Rufus::Scheduler::CronLine.new('* * * * *')
516
+
517
+ expect(cl.monthdays(local(1970, 1, 1))).to eq(%w[ thu#1 thu#-5 ])
518
+ expect(cl.monthdays(local(1970, 1, 7))).to eq(%w[ wed#1 wed#-4 ])
519
+ expect(cl.monthdays(local(1970, 1, 14))).to eq(%w[ wed#2 wed#-3 ])
520
+
521
+ expect(cl.monthdays(local(2011, 3, 11))).to eq(%w[ fri#2 fri#-3 ])
522
+ end
523
+ end
524
+
525
+ describe '#frequency' do
394
526
 
395
- cl.monthdays(local(1970, 1, 1)).should == %w[ thu#1 thu#-5 ]
396
- cl.monthdays(local(1970, 1, 7)).should == %w[ wed#1 wed#-4 ]
397
- cl.monthdays(local(1970, 1, 14)).should == %w[ wed#2 wed#-3 ]
527
+ it 'returns the shortest delta between two occurrences' do
398
528
 
399
- cl.monthdays(local(2011, 3, 11)).should == %w[ fri#2 fri#-3 ]
529
+ expect(Rufus::Scheduler::CronLine.new(
530
+ '* * * * *').frequency).to eq(60)
531
+ expect(Rufus::Scheduler::CronLine.new(
532
+ '* * * * * *').frequency).to eq(1)
533
+
534
+ expect(Rufus::Scheduler::CronLine.new(
535
+ '5 23 * * *').frequency).to eq(24 * 3600)
536
+ expect(Rufus::Scheduler::CronLine.new(
537
+ '5 * * * *').frequency).to eq(3600)
538
+ expect(Rufus::Scheduler::CronLine.new(
539
+ '10,20,30 * * * *').frequency).to eq(600)
540
+
541
+ expect(Rufus::Scheduler::CronLine.new(
542
+ '10,20,30 * * * * *').frequency).to eq(10)
543
+ end
544
+ end
545
+
546
+ describe '#brute_frequency' do
547
+
548
+ it 'returns the shortest delta between two occurrences' do
549
+
550
+ expect(Rufus::Scheduler::CronLine.new(
551
+ '* * * * *').brute_frequency).to eq(60)
552
+ expect(Rufus::Scheduler::CronLine.new(
553
+ '* * * * * *').brute_frequency).to eq(1)
554
+
555
+ expect(Rufus::Scheduler::CronLine.new(
556
+ '5 23 * * *').brute_frequency).to eq(24 * 3600)
557
+ expect(Rufus::Scheduler::CronLine.new(
558
+ '5 * * * *').brute_frequency).to eq(3600)
559
+ expect(Rufus::Scheduler::CronLine.new(
560
+ '10,20,30 * * * *').brute_frequency).to eq(600)
561
+
562
+ #Rufus::Scheduler::CronLine.new(
563
+ # '10,20,30 * * * * *').brute_frequency.should == 10
564
+ #
565
+ # takes > 20s ...
566
+ end
567
+
568
+ # some combos only appear every other year...
569
+ #
570
+ it 'does not go into an infinite loop' do
571
+
572
+ expect(Rufus::Scheduler::CronLine.new(
573
+ '1 2 3 4 5').brute_frequency).to eq(31622400)
574
+ end
575
+ end
576
+
577
+ context 'summer time' do
578
+
579
+ # let's assume summer time jumps always occur on sundays
580
+
581
+ # cf gh-114
582
+ #
583
+ it 'schedules correctly through a switch into summer time' do
584
+
585
+ in_zone 'Europe/Berlin' do
586
+
587
+ # find the summer jump
588
+
589
+ j = Time.parse('2014-02-28 12:00')
590
+ loop do
591
+ jj = j + 24 * 3600
592
+ break if jj.isdst
593
+ j = jj
594
+ end
595
+
596
+ # test
597
+
598
+ friday = j - 24 * 3600 # one day before
599
+
600
+ # verify the playground...
601
+ #
602
+ expect(friday.isdst).to eq(false)
603
+ expect((friday + 24 * 3600 * 3).isdst).to eq(true)
604
+
605
+ cl0 = Rufus::Scheduler::CronLine.new('02 00 * * 1,2,3,4,5')
606
+ cl1 = Rufus::Scheduler::CronLine.new('45 08 * * 1,2,3,4,5')
607
+
608
+ n0 = cl0.next_time(friday)
609
+ n1 = cl1.next_time(friday)
610
+
611
+ expect(n0.strftime('%H:%M:%S %^a')).to eq('00:02:00 TUE')
612
+ expect(n1.strftime('%H:%M:%S %^a')).to eq('08:45:00 MON')
613
+
614
+ expect(n0.isdst).to eq(true)
615
+ expect(n1.isdst).to eq(true)
616
+
617
+ expect(
618
+ (n0 - 24 * 3600 * 3).strftime('%H:%M:%S %^a')).to eq('23:02:00 FRI')
619
+ expect(
620
+ (n1 - 24 * 3600 * 3).strftime('%H:%M:%S %^a')).to eq('07:45:00 FRI')
621
+ end
622
+ end
623
+
624
+ it 'schedules correctly through a switch out of summer time' do
625
+
626
+ in_zone 'Europe/Berlin' do
627
+
628
+ # find the winter jump
629
+
630
+ j = Time.parse('2014-08-31 12:00')
631
+ loop do
632
+ jj = j + 24 * 3600
633
+ break if jj.isdst == false
634
+ j = jj
635
+ end
636
+
637
+ # test
638
+
639
+ friday = j - 24 * 3600 # one day before
640
+
641
+ # verify the playground...
642
+ #
643
+ expect(friday.isdst).to eq(true)
644
+ expect((friday + 24 * 3600 * 3).isdst).to eq(false)
645
+
646
+ cl0 = Rufus::Scheduler::CronLine.new('02 00 * * 1,2,3,4,5')
647
+ cl1 = Rufus::Scheduler::CronLine.new('45 08 * * 1,2,3,4,5')
648
+
649
+ n0 = cl0.next_time(friday)
650
+ n1 = cl1.next_time(friday)
651
+
652
+ expect(n0.strftime('%H:%M:%S %^a')).to eq('00:02:00 MON')
653
+ expect(n1.strftime('%H:%M:%S %^a')).to eq('08:45:00 MON')
654
+
655
+ expect(n0.isdst).to eq(false)
656
+ expect(n1.isdst).to eq(false)
657
+
658
+ expect(
659
+ (n0 - 24 * 3600 * 3).strftime('%H:%M:%S %^a')).to eq('01:02:00 FRI')
660
+ expect(
661
+ (n1 - 24 * 3600 * 3).strftime('%H:%M:%S %^a')).to eq('09:45:00 FRI')
662
+ end
663
+ end
664
+
665
+ it 'correctly increments through a DST transition' do
666
+
667
+ expect(
668
+ nt('* * * * * America/Los_Angeles', Time.utc(2015, 3, 8, 9, 59))
669
+ ).to eq(Time.utc(2015, 3, 8, 10, 00))
670
+ end
671
+
672
+ it 'correctly increments every minute through a DST transition' do
673
+
674
+ in_zone 'America/Los_Angeles' do
675
+
676
+ line = cl('* * * * * America/Los_Angeles')
677
+
678
+ t = Time.local(2015, 3, 8, 1, 57)
679
+
680
+ points =
681
+ [ 0, 1, 2, 3 ].collect do
682
+ t = line.next_time(t)
683
+ t.strftime('%H:%M:%Sl') + ' ' + t.dup.utc.strftime('%H:%M:%Su')
684
+ end
685
+
686
+ expect(points).to eq(
687
+ [
688
+ '01:58:00l 09:58:00u',
689
+ '01:59:00l 09:59:00u',
690
+ '03:00:00l 10:00:00u',
691
+ '03:01:00l 10:01:00u'
692
+ ]
693
+ )
694
+ end
695
+ end
696
+
697
+ it 'correctly decrements through a DST transition' do
698
+
699
+ expect(
700
+ pt('* * * * * America/Los_Angeles', Time.utc(2015, 3, 8, 10, 00))
701
+ ).to eq(Time.utc(2015, 3, 8, 9, 59))
702
+ end
703
+
704
+ it 'correctly decrements every minute through a DST transition' do
705
+
706
+ in_zone 'America/Los_Angeles' do
707
+
708
+ line = cl('* * * * * America/Los_Angeles')
709
+
710
+ t = Time.local(2015, 3, 8, 3, 2)
711
+
712
+ points =
713
+ [ 0, 1, 2, 3 ].collect do
714
+ t = line.previous_time(t)
715
+ t.strftime('%H:%M:%Sl') + ' ' + t.dup.utc.strftime('%H:%M:%Su')
716
+ end
717
+
718
+ expect(points).to eq(
719
+ [
720
+ '03:01:00l 10:01:00u',
721
+ '03:00:00l 10:00:00u',
722
+ '01:59:00l 09:59:00u',
723
+ '01:58:00l 09:58:00u'
724
+ ]
725
+ )
726
+ end
400
727
  end
401
728
  end
402
729
  end