rufus-scheduler 2.0.24 → 3.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (53) hide show
  1. data/CHANGELOG.txt +6 -0
  2. data/CREDITS.txt +4 -0
  3. data/README.md +1064 -0
  4. data/Rakefile +1 -4
  5. data/TODO.txt +145 -55
  6. data/lib/rufus/scheduler.rb +502 -26
  7. data/lib/rufus/{sc → scheduler}/cronline.rb +46 -17
  8. data/lib/rufus/{sc/version.rb → scheduler/job_array.rb} +56 -4
  9. data/lib/rufus/scheduler/jobs.rb +548 -0
  10. data/lib/rufus/scheduler/util.rb +318 -0
  11. data/rufus-scheduler.gemspec +30 -4
  12. data/spec/cronline_spec.rb +29 -8
  13. data/spec/error_spec.rb +116 -0
  14. data/spec/job_array_spec.rb +39 -0
  15. data/spec/job_at_spec.rb +58 -0
  16. data/spec/job_cron_spec.rb +67 -0
  17. data/spec/job_every_spec.rb +71 -0
  18. data/spec/job_in_spec.rb +20 -0
  19. data/spec/job_interval_spec.rb +68 -0
  20. data/spec/job_repeat_spec.rb +308 -0
  21. data/spec/job_spec.rb +387 -115
  22. data/spec/lockfile_spec.rb +61 -0
  23. data/spec/parse_spec.rb +203 -0
  24. data/spec/schedule_at_spec.rb +129 -0
  25. data/spec/schedule_cron_spec.rb +66 -0
  26. data/spec/schedule_every_spec.rb +109 -0
  27. data/spec/schedule_in_spec.rb +80 -0
  28. data/spec/schedule_interval_spec.rb +128 -0
  29. data/spec/scheduler_spec.rb +831 -124
  30. data/spec/spec_helper.rb +65 -0
  31. data/spec/threads_spec.rb +75 -0
  32. metadata +64 -59
  33. data/README.rdoc +0 -661
  34. data/lib/rufus/otime.rb +0 -3
  35. data/lib/rufus/sc/jobqueues.rb +0 -160
  36. data/lib/rufus/sc/jobs.rb +0 -471
  37. data/lib/rufus/sc/rtime.rb +0 -363
  38. data/lib/rufus/sc/scheduler.rb +0 -636
  39. data/spec/at_in_spec.rb +0 -47
  40. data/spec/at_spec.rb +0 -125
  41. data/spec/blocking_spec.rb +0 -64
  42. data/spec/cron_spec.rb +0 -134
  43. data/spec/every_spec.rb +0 -304
  44. data/spec/exception_spec.rb +0 -113
  45. data/spec/in_spec.rb +0 -150
  46. data/spec/mutex_spec.rb +0 -159
  47. data/spec/rtime_spec.rb +0 -137
  48. data/spec/schedulable_spec.rb +0 -97
  49. data/spec/spec_base.rb +0 -87
  50. data/spec/stress_schedule_unschedule_spec.rb +0 -159
  51. data/spec/timeout_spec.rb +0 -148
  52. data/test/kjw.rb +0 -113
  53. data/test/t.rb +0 -20
@@ -0,0 +1,308 @@
1
+
2
+ #
3
+ # Specifying rufus-scheduler
4
+ #
5
+ # Wed Apr 17 06:00:59 JST 2013
6
+ #
7
+
8
+ require 'spec_helper'
9
+
10
+
11
+ describe Rufus::Scheduler::RepeatJob do
12
+
13
+ before :each do
14
+ @scheduler = Rufus::Scheduler.new
15
+ end
16
+ after :each do
17
+ @scheduler.shutdown
18
+ end
19
+
20
+ describe '#pause' do
21
+
22
+ it 'pauses the job' do
23
+
24
+ counter = 0
25
+
26
+ job =
27
+ @scheduler.schedule_every('0.5s') do
28
+ counter += 1
29
+ end
30
+
31
+ counter.should == 0
32
+
33
+ while counter < 1; sleep(0.1); end
34
+
35
+ job.pause
36
+
37
+ sleep(1)
38
+
39
+ counter.should == 1
40
+ end
41
+ end
42
+
43
+ describe '#paused?' do
44
+
45
+ it 'returns true if the job is paused' do
46
+
47
+ job = @scheduler.schedule_every('10s') do; end
48
+
49
+ job.pause
50
+
51
+ job.paused?.should == true
52
+ end
53
+
54
+ it 'returns false if the job is not paused' do
55
+
56
+ job = @scheduler.schedule_every('10s') do; end
57
+
58
+ job.paused?.should == false
59
+ end
60
+ end
61
+
62
+ describe '#resume' do
63
+
64
+ it 'resumes a paused job' do
65
+
66
+ counter = 0
67
+
68
+ job =
69
+ @scheduler.schedule_every('0.5s') do
70
+ counter += 1
71
+ end
72
+
73
+ job.pause
74
+ job.resume
75
+
76
+ sleep(1.5)
77
+
78
+ counter.should > 1
79
+ end
80
+
81
+ it 'has no effect on a not paused job' do
82
+
83
+ job = @scheduler.schedule_every('10s') do; end
84
+
85
+ job.resume
86
+
87
+ job.paused?.should == false
88
+ end
89
+ end
90
+
91
+ describe ':times => i' do
92
+
93
+ it 'lets a job unschedule itself after i times' do
94
+
95
+ counter = 0
96
+
97
+ job =
98
+ @scheduler.schedule_every '0.5s', :times => 3 do
99
+ counter = counter + 1
100
+ end
101
+
102
+ sleep(2.6)
103
+
104
+ counter.should == 3
105
+ end
106
+
107
+ it 'is OK when passed a nil instead of an integer' do
108
+
109
+ counter = 0
110
+
111
+ job =
112
+ @scheduler.schedule_every '0.5s', :times => nil do
113
+ counter = counter + 1
114
+ end
115
+
116
+ sleep(2.5)
117
+
118
+ counter.should > 3
119
+ end
120
+
121
+ it 'raises when passed something else than nil or an integer' do
122
+
123
+ lambda {
124
+ @scheduler.schedule_every '0.5s', :times => 'nada' do; end
125
+ }.should raise_error(ArgumentError)
126
+ end
127
+ end
128
+
129
+ describe ':first/:first_in/:first_at => point in time' do
130
+
131
+ it 'accepts a Time instance' do
132
+
133
+ t = Time.now + 10
134
+
135
+ job = @scheduler.schedule_every '0.5s', :first => t do; end
136
+
137
+ job.first_at.should == t
138
+ end
139
+
140
+ it 'accepts a time string' do
141
+
142
+ t = Time.now + 10
143
+
144
+ job = @scheduler.schedule_every '0.5s', :first => t.to_s do; end
145
+
146
+ job.first_at.to_s.should == t.to_s
147
+ job.first_at.zone.should == t.zone
148
+ end
149
+
150
+ it 'only lets the job trigger after the :first' do
151
+
152
+ t = Time.now + 1.4
153
+ counter = 0
154
+
155
+ job =
156
+ @scheduler.schedule_every '0.5s', :first => t do
157
+ counter = counter + 1
158
+ end
159
+
160
+ sleep(1)
161
+
162
+ counter.should == 0
163
+
164
+ sleep(1)
165
+
166
+ counter.should > 0
167
+ end
168
+
169
+ it 'raises on points in the past' do
170
+
171
+ lambda {
172
+
173
+ @scheduler.schedule_every '0.5s', :first => Time.now - 60 do; end
174
+
175
+ }.should raise_error(ArgumentError)
176
+ end
177
+ end
178
+
179
+ describe ':first/:first_in/:first_at => duration' do
180
+
181
+ it 'accepts a duration string' do
182
+
183
+ t = Time.now
184
+
185
+ job = @scheduler.schedule_every '0.5s', :first => '1h' do; end
186
+
187
+ job.first_at.should >= t + 3600
188
+ job.first_at.should < t + 3601
189
+ end
190
+
191
+ it 'accepts a duration in seconds (integer)' do
192
+
193
+ t = Time.now
194
+
195
+ job = @scheduler.schedule_every '0.5s', :first => 3600 do; end
196
+
197
+ job.first_at.should >= t + 3600
198
+ job.first_at.should < t + 3601
199
+ end
200
+
201
+ it 'raises if the argument cannot be used' do
202
+
203
+ lambda {
204
+ @scheduler.every '0.5s', :first => :nada do; end
205
+ }.should raise_error(ArgumentError)
206
+ end
207
+ end
208
+
209
+ describe '#first_at=' do
210
+
211
+ it 'can be used to set first_at directly' do
212
+
213
+ job = @scheduler.schedule_every '0.5s', :first => 3600 do; end
214
+ job.first_at = '2030-12-12 12:00:30'
215
+
216
+ job.first_at.strftime('%c').should == 'Thu Dec 12 12:00:30 2030'
217
+ end
218
+ end
219
+
220
+ describe ':last/:last_in/:last_at => point in time' do
221
+
222
+ it 'accepts a Time instance' do
223
+
224
+ t = Time.now + 10
225
+
226
+ job = @scheduler.schedule_every '0.5s', :last => t do; end
227
+
228
+ job.last_at.should == t
229
+ end
230
+
231
+ it 'unschedules the job after the last_at time' do
232
+
233
+ t = Time.now + 2
234
+ counter = 0
235
+
236
+ job =
237
+ @scheduler.schedule_every '0.5s', :last => t do
238
+ counter = counter + 1
239
+ end
240
+
241
+ sleep 3
242
+
243
+ counter.should == 3
244
+ @scheduler.jobs.should_not include(job)
245
+ end
246
+
247
+ it 'accepts a time string' do
248
+
249
+ t = Time.now + 10
250
+
251
+ job = @scheduler.schedule_every '0.5s', :last => t.to_s do; end
252
+
253
+ job.last_at.to_s.should == t.to_s
254
+ job.last_at.zone.should == t.zone
255
+ end
256
+
257
+ it 'raises on a point in the past' do
258
+
259
+ lambda {
260
+
261
+ @scheduler.every '0.5s', :last => Time.now - 60 do; end
262
+
263
+ }.should raise_error(ArgumentError)
264
+ end
265
+ end
266
+
267
+ describe ':last/:last_in/:last_at => duration' do
268
+
269
+ it 'accepts a duration string' do
270
+
271
+ t = Time.now
272
+
273
+ job = @scheduler.schedule_every '0.5s', :last_in => '2s' do; end
274
+
275
+ job.last_at.should >= t + 2
276
+ job.last_at.should < t + 2.5
277
+ end
278
+
279
+ it 'accepts a duration in seconds (integer)' do
280
+
281
+ t = Time.now
282
+
283
+ job = @scheduler.schedule_every '0.5s', :last_in => 2.0 do; end
284
+
285
+ job.last_at.should >= t + 2
286
+ job.last_at.should < t + 2.5
287
+ end
288
+
289
+ it 'raises if the argument is worthless' do
290
+
291
+ lambda {
292
+ @scheduler.every '0.5s', :last => :nada do; end
293
+ }.should raise_error(ArgumentError)
294
+ end
295
+ end
296
+
297
+ describe '#last_at=' do
298
+
299
+ it 'can be used to set last_at directly' do
300
+
301
+ job = @scheduler.schedule_every '0.5s', :last_in => 10.0 do; end
302
+ job.last_at = '2030-12-12 12:00:30'
303
+
304
+ job.last_at.strftime('%c').should == 'Thu Dec 12 12:00:30 2030'
305
+ end
306
+ end
307
+ end
308
+
data/spec/job_spec.rb CHANGED
@@ -2,240 +2,512 @@
2
2
  #
3
3
  # Specifying rufus-scheduler
4
4
  #
5
- # Wed Apr 27 00:51:07 JST 2011
5
+ # Wed Apr 17 06:00:59 JST 2013
6
6
  #
7
7
 
8
- require 'spec_base'
8
+ require 'spec_helper'
9
9
 
10
10
 
11
- describe 'job classes' do
11
+ describe Rufus::Scheduler::Job do
12
12
 
13
- before(:each) do
14
- @s = start_scheduler
13
+ # specify behaviours common to all job classes
14
+
15
+ before :each do
16
+
17
+ @taoe = Thread.abort_on_exception
18
+ Thread.abort_on_exception = false
19
+
20
+ @ose = $stderr
21
+ $stderr = StringIO.new
22
+
23
+ @scheduler = Rufus::Scheduler.new
15
24
  end
16
- after(:each) do
17
- stop_scheduler(@s)
25
+
26
+ after :each do
27
+
28
+ @scheduler.shutdown
29
+
30
+ Thread.abort_on_exception = @taoe
31
+
32
+ $stderr = @ose
18
33
  end
19
34
 
20
- describe Rufus::Scheduler::Job do
35
+ describe '#last_time' do
21
36
 
22
- describe '#running' do
37
+ it 'returns nil if the job never fired' do
23
38
 
24
- it 'returns false when the job is inactive' do
39
+ job = @scheduler.schedule_in '10d' do; end
25
40
 
26
- job = @s.in '2d' do
27
- end
41
+ job.last_time.should == nil
42
+ end
28
43
 
29
- job.running.should == false
30
- end
44
+ it 'returns the last time the job fired' do
31
45
 
32
- it 'returns true when the job is active' do
46
+ job = @scheduler.schedule_in '0s' do; end
33
47
 
34
- job = @s.in 0 do
35
- sleep(100)
36
- end
48
+ sleep 0.4
37
49
 
38
- wait_next_tick
50
+ job.last_time.should_not == nil
51
+ end
52
+ end
39
53
 
40
- job.running.should == true
41
- end
54
+ describe '#threads' do
42
55
 
43
- it 'returns false when the job hits some error' do
56
+ it 'returns an empty list when the job is not running' do
44
57
 
45
- $exception = nil
58
+ job = @scheduler.in('1d', :job => true) {}
46
59
 
47
- def @s.handle_exception(j, e)
48
- #p e
49
- $exception = e
50
- end
60
+ job.threads.size.should == 0
61
+ end
51
62
 
52
- job = @s.in 0 do
53
- raise "nada"
63
+ it 'returns an empty list after the job terminated' do
64
+
65
+ job = @scheduler.in('0s', :job => true) {}
66
+
67
+ sleep 0.8
68
+
69
+ job.threads.size.should == 0
70
+ end
71
+
72
+ it 'lists the threads the job currently runs in' do
73
+
74
+ job =
75
+ @scheduler.schedule_in('0s') do
76
+ sleep(1)
54
77
  end
55
78
 
56
- wait_next_tick
79
+ sleep 0.4
57
80
 
58
- $exception.should_not == nil
59
- job.running.should == false
60
- end
81
+ job.threads.size.should == 1
82
+
83
+ t = job.threads.first
84
+ t[:rufus_scheduler_job].should == job
61
85
  end
86
+ end
62
87
 
63
- describe '#running?' do
88
+ describe '#kill' do
64
89
 
65
- it 'is an alias for #running' do
90
+ it 'has no effect if the job is not running' do
66
91
 
67
- job = @s.in 0 do
68
- sleep(100)
92
+ job = @scheduler.schedule_in '10d' do; end
93
+
94
+ tls = Thread.list.size
95
+
96
+ job.kill
97
+
98
+ Thread.list.size.should == tls
99
+ end
100
+
101
+ it 'makes the threads vacant' do
102
+
103
+ counter = 0
104
+
105
+ job =
106
+ @scheduler.schedule_in '0s' do
107
+ sleep 2
108
+ counter = counter + 1
69
109
  end
70
110
 
71
- wait_next_tick
111
+ sleep 1
72
112
 
73
- job.running?.should == true
74
- end
113
+ v0 = @scheduler.work_threads(:vacant).size
114
+ a0 = @scheduler.work_threads(:active).size
115
+
116
+ job.kill
117
+
118
+ sleep 2
119
+
120
+ v1 = @scheduler.work_threads(:vacant).size
121
+ a1 = @scheduler.work_threads(:active).size
122
+
123
+ counter.should == 0
124
+
125
+ v0.should == 0
126
+ a0.should == 1
127
+
128
+ v1.should == 1
129
+ a1.should == 0
75
130
  end
76
131
  end
77
132
 
78
- describe Rufus::Scheduler::AtJob do
133
+ describe '#running?' do
79
134
 
80
- describe '#unschedule' do
135
+ it 'returns false when the job is not running in any thread' do
81
136
 
82
- it 'removes the job from the scheduler' do
137
+ job = @scheduler.in('1d', :job => true) {}
83
138
 
84
- job = @s.at Time.now + 3 * 3600 do
85
- end
139
+ job.running?.should == false
140
+ end
86
141
 
87
- wait_next_tick
142
+ it 'returns true when the job is running in at least one thread' do
88
143
 
89
- job.unschedule
144
+ job = @scheduler.in('0s', :job => true) { sleep(1) }
90
145
 
91
- @s.jobs.size.should == 0
92
- end
146
+ sleep 0.4
147
+
148
+ job.running?.should == true
93
149
  end
150
+ end
94
151
 
95
- describe '#next_time' do
152
+ describe '#scheduled?' do
96
153
 
97
- it 'returns the time when the job will trigger' do
154
+ it 'returns true when the job is scheduled' do
98
155
 
99
- t = Time.now + 3 * 3600
156
+ job = @scheduler.schedule_in('1d') {}
100
157
 
101
- job = @s.at Time.now + 3 * 3600 do
102
- end
158
+ job.scheduled?.should == true
159
+ end
103
160
 
104
- job.next_time.class.should == Time
105
- job.next_time.to_i.should == t.to_i
106
- end
161
+ it 'returns false when the job is not scheduled' do
162
+
163
+ job = @scheduler.schedule_in('0.1s') {}
164
+
165
+ sleep 0.4
166
+
167
+ job.scheduled?.should == false
168
+ end
169
+
170
+ it 'returns true for repeat jobs that are running' do
171
+
172
+ job = @scheduler.schedule_interval('0.4s') { sleep(10) }
173
+
174
+ sleep 1
175
+
176
+ job.running?.should == true
177
+ job.scheduled?.should == true
107
178
  end
108
179
  end
109
180
 
110
- describe Rufus::Scheduler::InJob do
181
+ context 'job-local variables' do
111
182
 
112
- describe '#unschedule' do
183
+ describe '#[]=' do
113
184
 
114
- it 'removes the job from the scheduler' do
185
+ it 'sets a job-local variable' do
115
186
 
116
- job = @s.in '2d' do
117
- end
187
+ job =
188
+ @scheduler.schedule_every '1s' do |job|
189
+ job[:counter] ||= 0
190
+ job[:counter] += 1
191
+ end
192
+
193
+ sleep 3
118
194
 
119
- wait_next_tick
195
+ job[:counter].should > 1
196
+ end
197
+ end
120
198
 
121
- job.unschedule
199
+ describe '#[]' do
122
200
 
123
- @s.jobs.size.should == 0
201
+ it 'returns nil if there is no such entry' do
202
+
203
+ job = @scheduler.schedule_in '1s' do; end
204
+
205
+ job[:nada].should == nil
206
+ end
207
+
208
+ it 'returns the value of a job-local variable' do
209
+
210
+ job = @scheduler.schedule_in '1s' do; end
211
+ job[:x] = :y
212
+
213
+ job[:x].should == :y
124
214
  end
125
215
  end
126
216
 
127
- describe '#next_time' do
217
+ describe '#key?' do
128
218
 
129
- it 'returns the time when the job will trigger' do
219
+ it 'returns true if there is an entry with the given key' do
130
220
 
131
- t = Time.now + 3 * 3600
221
+ job = @scheduler.schedule_in '1s' do; end
222
+ job[:x] = :y
132
223
 
133
- job = @s.in '3h' do
134
- end
224
+ job.key?(:x).should == true
225
+ end
226
+ end
227
+
228
+ describe '#keys' do
229
+
230
+ it 'returns the array of keys of the job-local variables' do
135
231
 
136
- job.next_time.class.should == Time
137
- job.next_time.to_i.should == t.to_i
232
+ job = @scheduler.schedule_in '1s' do; end
233
+ job[:x] = :y
234
+ job['hello'] = :z
235
+ job[123] = {}
236
+
237
+ job.keys.sort_by { |k| k.to_s }.should == [ 123, 'hello', :x ]
138
238
  end
139
239
  end
140
240
  end
141
241
 
142
- describe Rufus::Scheduler::EveryJob do
242
+ context ':tag / :tags => [ t0, t1 ]' do
143
243
 
144
- describe '#next_time' do
244
+ it 'accepts one tag' do
145
245
 
146
- it 'returns the time when the job will trigger' do
246
+ job = @scheduler.in '10d', :job => true, :tag => 't0' do; end
147
247
 
148
- t = Time.now + 3 * 3600
248
+ job.tags.should == %w[ t0 ]
249
+ end
250
+
251
+ it 'accepts an array of tags' do
252
+
253
+ job = @scheduler.in '10d', :job => true, :tag => %w[ t0 t1 ] do; end
254
+
255
+ job.tags.should == %w[ t0 t1 ]
256
+ end
149
257
 
150
- job = @s.every '3h' do
258
+ it 'turns tags into strings' do
259
+
260
+ job = @scheduler.in '10d', :job => true, :tags => [ 1, 2 ] do; end
261
+
262
+ job.tags.should == %w[ 1 2 ]
263
+ end
264
+ end
265
+
266
+ context ':blocking => true' do
267
+
268
+ it 'runs the job in the same thread as the scheduler thread' do
269
+
270
+ job =
271
+ @scheduler.in('0s', :job => true, :blocking => true) do
272
+ sleep(1)
151
273
  end
152
274
 
153
- job.next_time.class.should == Time
154
- job.next_time.to_i.should == t.to_i
155
- end
275
+ sleep 0.4
276
+
277
+ job.threads.first.should == @scheduler.thread
278
+
279
+ sleep 1.4
280
+
281
+ job.threads.size.should == 0
156
282
  end
283
+ end
157
284
 
158
- describe '#paused?' do
285
+ context 'default one thread per job behaviour' do
159
286
 
160
- it 'returns false initially' do
287
+ it 'runs the job in a dedicated thread' do
161
288
 
162
- job = @s.every '3h' do; end
289
+ job =
290
+ @scheduler.in('0s', :job => true) do
291
+ sleep(1)
292
+ end
163
293
 
164
- job.paused?.should == false
165
- end
294
+ sleep 0.4
295
+
296
+ job.threads.first.should_not == @scheduler.thread
297
+
298
+ sleep 1.4
299
+
300
+ job.threads.size.should == 0
166
301
  end
302
+ end
303
+
304
+ context ':allow_overlapping / :allow_overlap / :overlap' do
167
305
 
168
- describe '#pause' do
306
+ context 'default (:overlap => true)' do
169
307
 
170
- it 'pauses the job' do
308
+ it 'lets a job overlap itself' do
171
309
 
172
- job = @s.every '3h' do; end
310
+ job =
311
+ @scheduler.every('0.3', :job => true) do
312
+ sleep(5)
313
+ end
173
314
 
174
- job.pause
315
+ sleep 3
175
316
 
176
- job.paused?.should == true
317
+ job.threads.size.should > 1
177
318
  end
178
319
  end
179
320
 
180
- describe '#resume' do
321
+ context 'when :overlap => false' do
181
322
 
182
- it 'resumes the job' do
323
+ it 'prevents a job from overlapping itself' do
183
324
 
184
- job = @s.every '3h' do; end
325
+ job =
326
+ @scheduler.every('0.3', :job => true, :overlap => false) do
327
+ sleep(5)
328
+ end
185
329
 
186
- job.resume
330
+ sleep 3
187
331
 
188
- job.paused?.should == false
332
+ job.threads.size.should == 1
189
333
  end
190
334
  end
191
335
  end
192
336
 
193
- describe Rufus::Scheduler::CronJob do
337
+ context ':mutex' do
338
+
339
+ context ':mutex => "mutex_name"' do
340
+
341
+ it 'prevents concurrent executions' do
342
+
343
+ j0 =
344
+ @scheduler.in('0s', :job => true, :mutex => 'vladivostok') do
345
+ sleep(3)
346
+ end
347
+ j1 =
348
+ @scheduler.in('0s', :job => true, :mutex => 'vladivostok') do
349
+ sleep(3)
350
+ end
351
+
352
+ sleep 0.7
353
+
354
+ if j0.threads.any?
355
+ j0.threads.size.should == 1
356
+ j1.threads.size.should == 0
357
+ else
358
+ j0.threads.size.should == 0
359
+ j1.threads.size.should == 1
360
+ end
361
+
362
+ @scheduler.mutexes.keys.should == %w[ vladivostok ]
363
+ end
364
+ end
365
+
366
+ context ':mutex => mutex_instance' do
367
+
368
+ it 'prevents concurrent executions' do
194
369
 
195
- describe '#next_time' do
370
+ m = Mutex.new
196
371
 
197
- it 'returns the time when the job will trigger' do
372
+ j0 = @scheduler.in('0s', :job => true, :mutex => m) { sleep(3) }
373
+ j1 = @scheduler.in('0s', :job => true, :mutex => m) { sleep(3) }
198
374
 
199
- job = @s.cron '* * * * *' do
375
+ sleep 0.7
376
+
377
+ if j0.threads.any?
378
+ j0.threads.size.should == 1
379
+ j1.threads.size.should == 0
380
+ else
381
+ j0.threads.size.should == 0
382
+ j1.threads.size.should == 1
200
383
  end
201
384
 
202
- job.next_time.class.should == Time
203
- (job.next_time.to_i - Time.now.to_i).should satisfy { |v| v < 60 }
385
+ @scheduler.mutexes.keys.should == []
204
386
  end
205
387
  end
206
388
 
207
- describe '#paused?' do
389
+ context ':mutex => [ array_of_mutex_names_or_instances ]' do
390
+
391
+ it 'prevents concurrent executions' do
208
392
 
209
- it 'returns false initially' do
393
+ j0 =
394
+ @scheduler.in('0s', :job => true, :mutex => %w[ a b ]) do
395
+ sleep(3)
396
+ end
397
+ j1 =
398
+ @scheduler.in('0s', :job => true, :mutex => %w[ a b ]) do
399
+ sleep(3)
400
+ end
210
401
 
211
- job = @s.cron '* * * * *' do; end
402
+ sleep 0.7
403
+
404
+ if j0.threads.any?
405
+ j0.threads.size.should == 1
406
+ j1.threads.size.should == 0
407
+ else
408
+ j0.threads.size.should == 0
409
+ j1.threads.size.should == 1
410
+ end
212
411
 
213
- job.paused?.should == false
412
+ @scheduler.mutexes.keys.sort.should == %w[ a b ]
214
413
  end
215
414
  end
415
+ end
416
+
417
+ context ':timeout => duration_or_point_in_time' do
418
+
419
+ it 'interrupts the job it is stashed to (duration)' do
420
+
421
+ counter = 0
422
+ toe = nil
423
+
424
+ job =
425
+ @scheduler.schedule_in '0s', :timeout => '1s' do
426
+ begin
427
+ counter = counter + 1
428
+ sleep 1.5
429
+ counter = counter + 1
430
+ rescue Rufus::Scheduler::TimeoutError => e
431
+ toe = e
432
+ end
433
+ end
434
+
435
+ sleep(3)
436
+
437
+ counter.should == 1
438
+ toe.class.should == Rufus::Scheduler::TimeoutError
439
+ end
440
+
441
+ it 'interrupts the job it is stashed to (point in time)' do
216
442
 
217
- describe '#pause' do
443
+ counter = 0
218
444
 
219
- it 'pauses the job' do
445
+ job =
446
+ @scheduler.schedule_in '0s', :timeout => Time.now + 1 do
447
+ begin
448
+ counter = counter + 1
449
+ sleep 1.5
450
+ counter = counter + 1
451
+ rescue Rufus::Scheduler::TimeoutError => e
452
+ end
453
+ end
220
454
 
221
- job = @s.cron '* * * * *' do; end
455
+ sleep(3)
456
+
457
+ counter.should == 1
458
+ end
222
459
 
223
- job.pause
460
+ it 'starts timing when the job enters successfully all its mutexes' do
224
461
 
225
- job.paused?.should == true
462
+ t0, t1, t2 = nil
463
+
464
+ @scheduler.schedule_in '0s', :mutex => 'a' do
465
+ sleep 1
466
+ t0 = Time.now
226
467
  end
468
+
469
+ job =
470
+ @scheduler.schedule_in '0.5s', :mutex => 'a', :timeout => '1s' do
471
+ begin
472
+ t1 = Time.now
473
+ sleep 2
474
+ rescue Rufus::Scheduler::TimeoutError => e
475
+ t2 = Time.now
476
+ end
477
+ end
478
+
479
+ sleep 3
480
+
481
+ t0.should <= t1
482
+
483
+ d = t2 - t1
484
+ d.should >= 1.0
485
+ d.should < 1.5
227
486
  end
228
487
 
229
- describe '#resume' do
488
+ it 'emits the timeout information to $stderr (default #on_error)' do
489
+
490
+ @scheduler.every('1s', :timeout => '0.5s') do
491
+ sleep 0.9
492
+ end
493
+
494
+ sleep 2
230
495
 
231
- it 'resumes the job' do
496
+ $stderr.string.should match(/Rufus::Scheduler::TimeoutError/)
497
+ end
232
498
 
233
- job = @s.cron '* * * * *' do; end
499
+ it 'does not prevent a repeat job from recurring' do
234
500
 
235
- job.resume
501
+ counter = 0
236
502
 
237
- job.paused?.should == false
503
+ @scheduler.every('1s', :timeout => '0.5s') do
504
+ counter = counter + 1
505
+ sleep 0.9
238
506
  end
507
+
508
+ sleep 3
509
+
510
+ counter.should > 1
239
511
  end
240
512
  end
241
513
  end