rufus-scheduler 2.0.24 → 3.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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