rufus-scheduler 2.0.6 → 2.0.7
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +2 -0
- data/.rspec +1 -0
- data/CHANGELOG.txt +6 -0
- data/CREDITS.txt +2 -0
- data/README.rdoc +45 -12
- data/Rakefile +27 -22
- data/lib/rufus/sc/cronline.rb +61 -44
- data/lib/rufus/sc/jobqueues.rb +6 -7
- data/lib/rufus/sc/jobs.rb +27 -10
- data/lib/rufus/sc/rtime.rb +46 -38
- data/lib/rufus/sc/scheduler.rb +33 -23
- data/lib/rufus/sc/version.rb +1 -1
- data/lib/rufus/scheduler.rb +2 -2
- data/rufus-scheduler.gemspec +29 -13
- data/spec/at_in_spec.rb +8 -9
- data/spec/at_spec.rb +25 -26
- data/spec/blocking_spec.rb +7 -7
- data/spec/cron_spec.rb +23 -23
- data/spec/cronline_spec.rb +175 -40
- data/spec/every_spec.rb +71 -33
- data/spec/exception_spec.rb +11 -12
- data/spec/in_spec.rb +35 -36
- data/spec/rtime_spec.rb +47 -47
- data/spec/schedulable_spec.rb +15 -15
- data/spec/scheduler_spec.rb +14 -15
- data/spec/spec_base.rb +6 -13
- data/spec/stress_schedule_unschedule_spec.rb +124 -120
- data/spec/timeout_spec.rb +24 -24
- metadata +32 -10
- data/spec/spec.rb +0 -14
data/spec/cronline_spec.rb
CHANGED
@@ -5,63 +5,198 @@
|
|
5
5
|
# Sat Mar 21 12:55:27 JST 2009
|
6
6
|
#
|
7
7
|
|
8
|
-
require File.dirname(__FILE__)
|
9
|
-
|
10
|
-
|
11
|
-
def cl (cronline_string)
|
12
|
-
Rufus::CronLine.new(cronline_string)
|
13
|
-
end
|
8
|
+
require File.join(File.dirname(__FILE__), 'spec_base')
|
14
9
|
|
15
10
|
|
16
11
|
describe Rufus::CronLine do
|
17
12
|
|
18
|
-
def
|
13
|
+
def cl(cronline_string)
|
14
|
+
Rufus::CronLine.new(cronline_string)
|
15
|
+
end
|
19
16
|
|
20
|
-
|
17
|
+
def match(line, time)
|
18
|
+
cl(line).matches?(time).should == true
|
19
|
+
end
|
20
|
+
def to_a(line, array)
|
21
|
+
cl(line).to_array.should == array
|
21
22
|
end
|
22
23
|
|
23
|
-
|
24
|
-
|
25
|
-
should '* * * * *', [ [0], nil, nil, nil, nil, nil ]
|
26
|
-
should '10-12 * * * *', [ [0], [10, 11, 12], nil, nil, nil, nil ]
|
27
|
-
should '* * * * sun,mon', [ [0], nil, nil, nil, nil, [0, 1] ]
|
28
|
-
should '* * * * mon-wed', [ [0], nil, nil, nil, nil, [1, 2, 3] ]
|
29
|
-
should '* * * * 7', [ [0], nil, nil, nil, nil, [0] ]
|
30
|
-
should '* * * * 0', [ [0], nil, nil, nil, nil, [0] ]
|
31
|
-
should '* * * * 0,1', [ [0], nil, nil, nil, nil, [0,1] ]
|
32
|
-
should '* * * * 7,1', [ [0], nil, nil, nil, nil, [0,1] ]
|
33
|
-
should '* * * * 7,0', [ [0], nil, nil, nil, nil, [0] ]
|
34
|
-
should '* * * * sun,2-4', [ [0], nil, nil, nil, nil, [0, 2, 3, 4] ]
|
35
|
-
|
36
|
-
should '* * * * sun,mon-tue', [ [0], nil, nil, nil, nil, [0, 1, 2] ]
|
37
|
-
|
38
|
-
should '* * * * * *', [ nil, nil, nil, nil, nil, nil ]
|
39
|
-
should '1 * * * * *', [ [1], nil, nil, nil, nil, nil ]
|
40
|
-
should '7 10-12 * * * *', [ [7], [10, 11, 12], nil, nil, nil, nil ]
|
41
|
-
should '1-5 * * * * *', [ [1,2,3,4,5], nil, nil, nil, nil, nil ]
|
24
|
+
def local(*args)
|
25
|
+
Time.local(*args)
|
42
26
|
end
|
43
|
-
|
27
|
+
def utc(*args)
|
28
|
+
Time.utc(*args)
|
29
|
+
end
|
30
|
+
|
31
|
+
describe '.new' do
|
32
|
+
|
33
|
+
it 'interprets cron strings correctly' do
|
34
|
+
|
35
|
+
to_a '* * * * *', [ [0], nil, nil, nil, nil, nil, nil ]
|
36
|
+
to_a '10-12 * * * *', [ [0], [10, 11, 12], nil, nil, nil, nil, nil ]
|
37
|
+
to_a '* * * * sun,mon', [ [0], nil, nil, nil, nil, [0, 1], nil ]
|
38
|
+
to_a '* * * * mon-wed', [ [0], nil, nil, nil, nil, [1, 2, 3], nil ]
|
39
|
+
to_a '* * * * 7', [ [0], nil, nil, nil, nil, [0], nil ]
|
40
|
+
to_a '* * * * 0', [ [0], nil, nil, nil, nil, [0], nil ]
|
41
|
+
to_a '* * * * 0,1', [ [0], nil, nil, nil, nil, [0,1], nil ]
|
42
|
+
to_a '* * * * 7,1', [ [0], nil, nil, nil, nil, [0,1], nil ]
|
43
|
+
to_a '* * * * 7,0', [ [0], nil, nil, nil, nil, [0], nil ]
|
44
|
+
to_a '* * * * sun,2-4', [ [0], nil, nil, nil, nil, [0, 2, 3, 4], nil ]
|
45
|
+
|
46
|
+
to_a '* * * * sun,mon-tue', [ [0], nil, nil, nil, nil, [0, 1, 2], nil ]
|
47
|
+
|
48
|
+
to_a '* * * * * *', [ nil, nil, nil, nil, nil, nil, nil ]
|
49
|
+
to_a '1 * * * * *', [ [1], nil, nil, nil, nil, nil, nil ]
|
50
|
+
to_a '7 10-12 * * * *', [ [7], [10, 11, 12], nil, nil, nil, nil, nil ]
|
51
|
+
to_a '1-5 * * * * *', [ [1,2,3,4,5], nil, nil, nil, nil, nil, nil ]
|
52
|
+
end
|
53
|
+
|
54
|
+
it 'interprets cron strings with TZ correctly' do
|
55
|
+
|
56
|
+
to_a '* * * * * EST', [ [0], nil, nil, nil, nil, nil, 'EST' ]
|
57
|
+
to_a '* * * * * * EST', [ nil, nil, nil, nil, nil, nil, 'EST' ]
|
58
|
+
|
59
|
+
lambda { cl '* * * * * NotATimeZone' }.should raise_error
|
60
|
+
lambda { cl '* * * * * * NotATimeZone' }.should raise_error
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
describe '#next_time' do
|
65
|
+
|
66
|
+
def nt(cronline, now)
|
67
|
+
Rufus::CronLine.new(cronline).next_time(now)
|
68
|
+
end
|
69
|
+
|
70
|
+
it 'computes the next occurence correctly' do
|
71
|
+
|
72
|
+
now = Time.at(0).getutc # Thu Jan 01 00:00:00 UTC 1970
|
73
|
+
|
74
|
+
nt('* * * * *', now).should == now + 60
|
75
|
+
nt('* * * * sun', now).should == now + 259200
|
76
|
+
nt('* * * * * *', now).should == now + 1
|
77
|
+
nt('* * 13 * fri', now).should == now + 3715200
|
78
|
+
|
79
|
+
nt('10 12 13 12 *', now).should == now + 29938200
|
80
|
+
# this one is slow (1 year == 3 seconds)
|
81
|
+
|
82
|
+
nt('0 0 * * thu', now).should == now + 604800
|
83
|
+
|
84
|
+
now = local(2008, 12, 31, 23, 59, 59, 0)
|
85
|
+
|
86
|
+
nt('* * * * *', now).should == now + 1
|
87
|
+
end
|
44
88
|
|
45
|
-
|
89
|
+
#
|
90
|
+
# the specs that follow are from Tanzeeb Khalili
|
91
|
+
#
|
92
|
+
|
93
|
+
it 'computes the next occurence correctly in UTC (TZ not specified)' do
|
94
|
+
|
95
|
+
now = utc(1970, 1, 1)
|
96
|
+
|
97
|
+
nt('* * * * *', now).should == utc(1970, 1, 1, 0, 1)
|
98
|
+
nt('* * * * sun', now).should == utc(1970, 1, 4)
|
99
|
+
nt('* * * * * *', now).should == utc(1970, 1, 1, 0, 0, 1)
|
100
|
+
nt('* * 13 * fri', now).should == utc(1970, 2, 13)
|
101
|
+
|
102
|
+
nt('10 12 13 12 *', now).should == utc(1970, 12, 13, 12, 10)
|
103
|
+
# this one is slow (1 year == 3 seconds)
|
104
|
+
nt('* * 1 6 *', now).should == utc(1970, 6, 1)
|
105
|
+
|
106
|
+
nt('0 0 * * thu', now).should == utc(1970, 1, 8)
|
107
|
+
end
|
46
108
|
|
47
|
-
|
109
|
+
it 'computes the next occurence correctly in local TZ (TZ not specified)' do
|
48
110
|
|
49
|
-
|
111
|
+
now = local(1970, 1, 1)
|
50
112
|
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
113
|
+
nt('* * * * *', now).should == local(1970, 1, 1, 0, 1)
|
114
|
+
nt('* * * * sun', now).should == local(1970, 1, 4)
|
115
|
+
nt('* * * * * *', now).should == local(1970, 1, 1, 0, 0, 1)
|
116
|
+
nt('* * 13 * fri', now).should == local(1970, 2, 13)
|
55
117
|
|
56
|
-
|
57
|
-
|
118
|
+
nt('10 12 13 12 *', now).should == local(1970, 12, 13, 12, 10)
|
119
|
+
# this one is slow (1 year == 3 seconds)
|
120
|
+
nt('* * 1 6 *', now).should == local(1970, 6, 1)
|
58
121
|
|
59
|
-
|
122
|
+
nt('0 0 * * thu', now).should == local(1970, 1, 8)
|
123
|
+
end
|
60
124
|
|
61
|
-
|
125
|
+
it 'computes the next occurence correctly in UTC (TZ specified)' do
|
62
126
|
|
63
|
-
|
127
|
+
zone = 'Europe/Stockholm'
|
128
|
+
tz = TZInfo::Timezone.get(zone)
|
129
|
+
now = tz.local_to_utc(local(1970, 1, 1))
|
130
|
+
# Midnight in zone, UTC
|
131
|
+
|
132
|
+
nt("* * * * * #{zone}", now).should == utc(1969, 12, 31, 23, 1)
|
133
|
+
nt("* * * * sun #{zone}", now).should == utc(1970, 1, 3, 23)
|
134
|
+
nt("* * * * * * #{zone}", now).should == utc(1969, 12, 31, 23, 0, 1)
|
135
|
+
nt("* * 13 * fri #{zone}", now).should == utc(1970, 2, 12, 23)
|
136
|
+
|
137
|
+
nt("10 12 13 12 * #{zone}", now).should == utc(1970, 12, 13, 11, 10)
|
138
|
+
nt("* * 1 6 * #{zone}", now).should == utc(1970, 5, 31, 23)
|
139
|
+
|
140
|
+
nt("0 0 * * thu #{zone}", now).should == utc(1970, 1, 7, 23)
|
141
|
+
end
|
142
|
+
|
143
|
+
#it 'computes the next occurence correctly in local TZ (TZ specified)' do
|
144
|
+
# zone = 'Europe/Stockholm'
|
145
|
+
# tz = TZInfo::Timezone.get(zone)
|
146
|
+
# now = tz.local_to_utc(utc(1970, 1, 1)).localtime
|
147
|
+
# # Midnight in zone, local time
|
148
|
+
# nt("* * * * * #{zone}", now).should == local(1969, 12, 31, 18, 1)
|
149
|
+
# nt("* * * * sun #{zone}", now).should == local(1970, 1, 3, 18)
|
150
|
+
# nt("* * * * * * #{zone}", now).should == local(1969, 12, 31, 18, 0, 1)
|
151
|
+
# nt("* * 13 * fri #{zone}", now).should == local(1970, 2, 12, 18)
|
152
|
+
# nt("10 12 13 12 * #{zone}", now).should == local(1970, 12, 13, 6, 10)
|
153
|
+
# nt("* * 1 6 * #{zone}", now).should == local(1970, 5, 31, 19)
|
154
|
+
# nt("0 0 * * thu #{zone}", now).should == local(1970, 1, 7, 18)
|
155
|
+
#end
|
64
156
|
end
|
65
157
|
|
158
|
+
describe '#matches?' do
|
159
|
+
|
160
|
+
it 'matches correctly in UTC (TZ not specified)' do
|
161
|
+
|
162
|
+
match '* * * * *', utc(1970, 1, 1, 0, 1)
|
163
|
+
match '* * * * sun', utc(1970, 1, 4)
|
164
|
+
match '* * * * * *', utc(1970, 1, 1, 0, 0, 1)
|
165
|
+
match '* * 13 * fri', utc(1970, 2, 13)
|
166
|
+
|
167
|
+
match '10 12 13 12 *', utc(1970, 12, 13, 12, 10)
|
168
|
+
match '* * 1 6 *', utc(1970, 6, 1)
|
169
|
+
|
170
|
+
match '0 0 * * thu', utc(1970, 1, 8)
|
171
|
+
end
|
172
|
+
|
173
|
+
it 'matches correctly in local TZ (TZ not specified)' do
|
174
|
+
|
175
|
+
match '* * * * *', local(1970, 1, 1, 0, 1)
|
176
|
+
match '* * * * sun', local(1970, 1, 4)
|
177
|
+
match '* * * * * *', local(1970, 1, 1, 0, 0, 1)
|
178
|
+
match '* * 13 * fri', local(1970, 2, 13)
|
179
|
+
|
180
|
+
match '10 12 13 12 *', local(1970, 12, 13, 12, 10)
|
181
|
+
match '* * 1 6 *', local(1970, 6, 1)
|
182
|
+
|
183
|
+
match '0 0 * * thu', local(1970, 1, 8)
|
184
|
+
end
|
185
|
+
|
186
|
+
it 'matches correctly in UTC (TZ specified)' do
|
187
|
+
|
188
|
+
zone = 'Europe/Stockholm'
|
189
|
+
|
190
|
+
match "* * * * * #{zone}", utc(1969, 12, 31, 23, 1)
|
191
|
+
match "* * * * sun #{zone}", utc(1970, 1, 3, 23)
|
192
|
+
match "* * * * * * #{zone}", utc(1969, 12, 31, 23, 0, 1)
|
193
|
+
match "* * 13 * fri #{zone}", utc(1970, 2, 12, 23)
|
194
|
+
|
195
|
+
match "10 12 13 12 * #{zone}", utc(1970, 12, 13, 11, 10)
|
196
|
+
match "* * 1 6 * #{zone}", utc(1970, 5, 31, 23)
|
197
|
+
|
198
|
+
match "0 0 * * thu #{zone}", utc(1970, 1, 7, 23)
|
199
|
+
end
|
200
|
+
end
|
66
201
|
end
|
67
202
|
|
data/spec/every_spec.rb
CHANGED
@@ -5,32 +5,32 @@
|
|
5
5
|
# Sun Mar 22 12:26:07 JST 2009
|
6
6
|
#
|
7
7
|
|
8
|
-
require File.dirname(__FILE__)
|
8
|
+
require File.join(File.dirname(__FILE__), 'spec_base')
|
9
9
|
|
10
10
|
|
11
11
|
describe "#{SCHEDULER_CLASS}#every" do
|
12
12
|
|
13
|
-
before do
|
13
|
+
before(:each) do
|
14
14
|
@s = start_scheduler
|
15
15
|
end
|
16
|
-
after do
|
16
|
+
after(:each) do
|
17
17
|
stop_scheduler(@s)
|
18
18
|
end
|
19
19
|
|
20
|
-
it '
|
20
|
+
it 'has job ids with the class name in it' do
|
21
21
|
|
22
22
|
j0 = @s.every(1) {}
|
23
|
-
j0.job_id.should
|
23
|
+
j0.job_id.should match(/Rufus::Scheduler::EveryJob/)
|
24
24
|
end
|
25
25
|
|
26
|
-
it '
|
26
|
+
it 'computes frequency' do
|
27
27
|
|
28
28
|
job = @s.every '2h1m' do
|
29
29
|
end
|
30
|
-
job.frequency.should
|
30
|
+
job.frequency.should == 7260
|
31
31
|
end
|
32
32
|
|
33
|
-
it '
|
33
|
+
it 'schedules every 1s' do
|
34
34
|
|
35
35
|
var = 0
|
36
36
|
|
@@ -40,10 +40,10 @@ describe "#{SCHEDULER_CLASS}#every" do
|
|
40
40
|
|
41
41
|
sleep 3.7
|
42
42
|
|
43
|
-
var.should
|
43
|
+
var.should == 3
|
44
44
|
end
|
45
45
|
|
46
|
-
it '
|
46
|
+
it 'is punctilious' do
|
47
47
|
|
48
48
|
hits = []
|
49
49
|
|
@@ -58,10 +58,10 @@ describe "#{SCHEDULER_CLASS}#every" do
|
|
58
58
|
hits.each { |h| f = h; deltas << (f - hh) if hh; hh = f }
|
59
59
|
|
60
60
|
#puts; p deltas
|
61
|
-
deltas.max.should
|
61
|
+
deltas.max.should satisfy { |d| d < 1.200 }
|
62
62
|
end
|
63
63
|
|
64
|
-
it '
|
64
|
+
it 'unschedules' do
|
65
65
|
|
66
66
|
var = 0
|
67
67
|
|
@@ -73,25 +73,25 @@ describe "#{SCHEDULER_CLASS}#every" do
|
|
73
73
|
|
74
74
|
@s.unschedule(job.job_id)
|
75
75
|
|
76
|
-
var.should
|
76
|
+
var.should == 2
|
77
77
|
|
78
78
|
sleep 1.7
|
79
79
|
|
80
|
-
var.should
|
80
|
+
var.should == 2
|
81
81
|
end
|
82
82
|
|
83
|
-
it '
|
83
|
+
it 'accepts tags for jobs' do
|
84
84
|
|
85
85
|
job = @s.every '1s', :tags => 'spec' do
|
86
86
|
end
|
87
87
|
|
88
88
|
wait_next_tick
|
89
89
|
|
90
|
-
@s.find_by_tag('spec').size.should
|
91
|
-
@s.find_by_tag('spec').first.job_id.should
|
90
|
+
@s.find_by_tag('spec').size.should == 1
|
91
|
+
@s.find_by_tag('spec').first.job_id.should == job.job_id
|
92
92
|
end
|
93
93
|
|
94
|
-
it '
|
94
|
+
it 'honours :first_at' do
|
95
95
|
|
96
96
|
counter = 0
|
97
97
|
|
@@ -100,13 +100,13 @@ describe "#{SCHEDULER_CLASS}#every" do
|
|
100
100
|
end
|
101
101
|
|
102
102
|
sleep 1
|
103
|
-
counter.should
|
103
|
+
counter.should == 0
|
104
104
|
|
105
105
|
sleep 2.5
|
106
|
-
counter.should
|
106
|
+
counter.should == 2
|
107
107
|
end
|
108
108
|
|
109
|
-
it '
|
109
|
+
it 'honours :first_in' do
|
110
110
|
|
111
111
|
counter = 0
|
112
112
|
|
@@ -115,13 +115,13 @@ describe "#{SCHEDULER_CLASS}#every" do
|
|
115
115
|
end
|
116
116
|
|
117
117
|
sleep 1
|
118
|
-
counter.should
|
118
|
+
counter.should == 0
|
119
119
|
|
120
120
|
sleep 2.5
|
121
|
-
counter.should
|
121
|
+
counter.should == 2
|
122
122
|
end
|
123
123
|
|
124
|
-
#it '
|
124
|
+
#it 'honours :dont_reschedule' do
|
125
125
|
# stack = []
|
126
126
|
# @s.every 0.400 do |job|
|
127
127
|
# if stack.size > 3
|
@@ -136,7 +136,7 @@ describe "#{SCHEDULER_CLASS}#every" do
|
|
136
136
|
# stack.should.equal(%w[ ok ok ok ok done ])
|
137
137
|
#end
|
138
138
|
|
139
|
-
it '
|
139
|
+
it 'accepts job.unschedule within the job' do
|
140
140
|
|
141
141
|
stack = []
|
142
142
|
|
@@ -151,11 +151,11 @@ describe "#{SCHEDULER_CLASS}#every" do
|
|
151
151
|
|
152
152
|
sleep 4
|
153
153
|
|
154
|
-
@s.jobs.size.should
|
155
|
-
stack.should
|
154
|
+
@s.jobs.size.should == 0
|
155
|
+
stack.should == %w[ ok ok ok ok done ]
|
156
156
|
end
|
157
157
|
|
158
|
-
it '
|
158
|
+
it 'respects :blocking => true' do
|
159
159
|
|
160
160
|
stack = []
|
161
161
|
|
@@ -166,28 +166,66 @@ describe "#{SCHEDULER_CLASS}#every" do
|
|
166
166
|
|
167
167
|
sleep 5
|
168
168
|
|
169
|
-
stack.should
|
169
|
+
stack.should == %w[ ok ok ]
|
170
170
|
end
|
171
171
|
|
172
|
+
it 'lists the "trigger threads"' do
|
173
|
+
|
174
|
+
@s.every '1s' do
|
175
|
+
sleep 10
|
176
|
+
end
|
177
|
+
sleep 5
|
178
|
+
|
179
|
+
@s.trigger_threads.size.should == 4
|
180
|
+
end
|
172
181
|
end
|
173
182
|
|
174
183
|
describe Rufus::Scheduler::EveryJob do
|
175
184
|
|
176
|
-
before do
|
185
|
+
before(:each) do
|
177
186
|
@s = start_scheduler
|
178
187
|
end
|
179
|
-
after do
|
188
|
+
after(:each) do
|
180
189
|
stop_scheduler(@s)
|
181
190
|
end
|
182
191
|
|
183
|
-
it '
|
192
|
+
it 'responds to #next_time' do
|
184
193
|
|
185
194
|
t = Time.now + 3 * 3600
|
186
195
|
|
187
196
|
job = @s.every '3h' do
|
188
197
|
end
|
189
198
|
|
190
|
-
job.next_time.to_i.should
|
199
|
+
job.next_time.to_i.should == t.to_i
|
200
|
+
end
|
201
|
+
|
202
|
+
|
203
|
+
it 'does not allow a job to overlap execution if set !allow_overlapping' do
|
204
|
+
|
205
|
+
stack = []
|
206
|
+
|
207
|
+
@s.every '1s', :allow_overlapping => false do |job|
|
208
|
+
stack << 'ok'
|
209
|
+
sleep 2
|
210
|
+
end
|
211
|
+
|
212
|
+
sleep 5
|
213
|
+
|
214
|
+
stack.size.should == 2
|
215
|
+
end
|
216
|
+
|
217
|
+
it 'allows a job to overlap execution (backward compatibility?)' do
|
218
|
+
|
219
|
+
stack = []
|
220
|
+
|
221
|
+
@s.every '1s' do |job|
|
222
|
+
stack << 'ok'
|
223
|
+
sleep 2
|
224
|
+
end
|
225
|
+
|
226
|
+
sleep 5
|
227
|
+
|
228
|
+
stack.size.should == 4
|
191
229
|
end
|
192
230
|
end
|
193
231
|
|