andrew-whenever 0.4.3

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,60 @@
1
+ module Whenever
2
+ module Output
3
+ class Cron
4
+ class OutputRedirection
5
+
6
+ def initialize(output)
7
+ @output = output
8
+ end
9
+
10
+ def to_s
11
+ return '' unless defined?(@output)
12
+ case @output
13
+ when String then redirect_from_string
14
+ when Hash then redirect_from_hash
15
+ when NilClass then ">> /dev/null 2>&1"
16
+ else ''
17
+ end
18
+ end
19
+
20
+ protected
21
+
22
+ def stdout
23
+ return unless @output.has_key?(:standard)
24
+ @output[:standard].nil? ? '/dev/null' : @output[:standard]
25
+ end
26
+
27
+ def stderr
28
+ return unless @output.has_key?(:error)
29
+ @output[:error].nil? ? '/dev/null' : @output[:error]
30
+ end
31
+
32
+ def redirect_from_hash
33
+ case
34
+ when stdout == '/dev/null' && stderr == '/dev/null'
35
+ "> /dev/null 2>&1"
36
+ when stdout && stderr == '/dev/null'
37
+ ">> #{stdout} 2> /dev/null"
38
+ when stdout && stderr
39
+ ">> #{stdout} 2>> #{stderr}"
40
+ when stderr == '/dev/null'
41
+ "2> /dev/null"
42
+ when stderr
43
+ "2>> #{stderr}"
44
+ when stdout == '/dev/null'
45
+ "> /dev/null"
46
+ when stdout
47
+ ">> #{stdout}"
48
+ else
49
+ ''
50
+ end
51
+ end
52
+
53
+ def redirect_from_string
54
+ ">> #{@output} 2>&1"
55
+ end
56
+
57
+ end
58
+ end
59
+ end
60
+ end
@@ -0,0 +1,3 @@
1
+ module Whenever
2
+ VERSION = '0.4.3'
3
+ end unless defined?(Whenever::VERSION)
@@ -0,0 +1,101 @@
1
+ require File.expand_path(File.dirname(__FILE__) + "/test_helper")
2
+
3
+ class CommandLineTest < Test::Unit::TestCase
4
+
5
+ context "A command line write" do
6
+ setup do
7
+ File.expects(:exists?).with('config/schedule.rb').returns(true)
8
+ @command = Whenever::CommandLine.new(:write => true, :identifier => 'My identifier')
9
+ @task = "#{two_hours} /my/command"
10
+ Whenever.expects(:cron).returns(@task)
11
+ end
12
+
13
+ should "output the cron job with identifier blocks" do
14
+ output = <<-EXPECTED
15
+ # Begin Whenever generated tasks for: My identifier
16
+ #{@task}
17
+ # End Whenever generated tasks for: My identifier
18
+ EXPECTED
19
+
20
+ assert_equal output, @command.send(:whenever_cron)
21
+ end
22
+
23
+ should "write the crontab when run" do
24
+ @command.expects(:write_crontab).returns(true)
25
+ assert @command.run
26
+ end
27
+ end
28
+
29
+ context "A command line update" do
30
+ setup do
31
+ File.expects(:exists?).with('config/schedule.rb').returns(true)
32
+ @command = Whenever::CommandLine.new(:update => true, :identifier => 'My identifier')
33
+ @task = "#{two_hours} /my/command"
34
+ Whenever.expects(:cron).returns(@task)
35
+ end
36
+
37
+ should "add the cron to the end of the file if there is no existing identifier block" do
38
+ existing = '# Existing crontab'
39
+ @command.expects(:read_crontab).at_least_once.returns(existing)
40
+
41
+ new_cron = <<-EXPECTED
42
+ #{existing}
43
+
44
+ # Begin Whenever generated tasks for: My identifier
45
+ #{@task}
46
+ # End Whenever generated tasks for: My identifier
47
+ EXPECTED
48
+
49
+ assert_equal new_cron, @command.send(:updated_crontab)
50
+
51
+ @command.expects(:write_crontab).with(new_cron).returns(true)
52
+ assert @command.run
53
+ end
54
+
55
+ should "replace an existing block if the identifier matches" do
56
+ existing = <<-EXISTING_CRON
57
+ # Something
58
+
59
+ # Begin Whenever generated tasks for: My identifier
60
+ My whenever job that was already here
61
+ # End Whenever generated tasks for: My identifier
62
+
63
+ # Begin Whenever generated tasks for: Other identifier
64
+ This shouldn't get replaced
65
+ # End Whenever generated tasks for: Other identifier
66
+ EXISTING_CRON
67
+
68
+ @command.expects(:read_crontab).at_least_once.returns(existing)
69
+
70
+ new_cron = <<-NEW_CRON
71
+ # Something
72
+
73
+ # Begin Whenever generated tasks for: My identifier
74
+ #{@task}
75
+ # End Whenever generated tasks for: My identifier
76
+
77
+ # Begin Whenever generated tasks for: Other identifier
78
+ This shouldn't get replaced
79
+ # End Whenever generated tasks for: Other identifier
80
+ NEW_CRON
81
+
82
+ assert_equal new_cron, @command.send(:updated_crontab)
83
+
84
+ @command.expects(:write_crontab).with(new_cron).returns(true)
85
+ assert @command.run
86
+ end
87
+ end
88
+
89
+ context "A command line update with no identifier" do
90
+ setup do
91
+ File.expects(:exists?).with('config/schedule.rb').returns(true)
92
+ Whenever::CommandLine.any_instance.expects(:default_identifier).returns('DEFAULT')
93
+ @command = Whenever::CommandLine.new(:update => true, :file => @file)
94
+ end
95
+
96
+ should "use the default identifier" do
97
+ assert_equal "Whenever generated tasks for: DEFAULT", @command.send(:comment_base)
98
+ end
99
+ end
100
+
101
+ end
@@ -0,0 +1,230 @@
1
+ require File.expand_path(File.dirname(__FILE__) + "/test_helper")
2
+
3
+ class CronTest < Test::Unit::TestCase
4
+
5
+ context "When parsing time in minutes" do
6
+ should "raise if less than 1 minute" do
7
+ assert_raises ArgumentError do
8
+ parse_time(59.seconds)
9
+ end
10
+
11
+ assert_raises ArgumentError do
12
+ parse_time(0.minutes)
13
+ end
14
+ end
15
+
16
+ # For santity, do some tests on straight String
17
+ should "parse correctly" do
18
+ assert_equal '* * * * *', parse_time(1.minute)
19
+ assert_equal '0,5,10,15,20,25,30,35,40,45,50,55 * * * *', parse_time(5.minutes)
20
+ assert_equal '7,14,21,28,35,42,49,56 * * * *', parse_time(7.minutes)
21
+ assert_equal '0,30 * * * *', parse_time(30.minutes)
22
+ assert_equal '32 * * * *', parse_time(32.minutes)
23
+ assert_not_equal '60 * * * *', parse_time(60.minutes) # 60 minutes bumps up into the hour range
24
+ end
25
+
26
+ # Test all minutes
27
+ (2..59).each do |num|
28
+ should "parse correctly for #{num} minutes" do
29
+ start = 0
30
+ start += num unless 60.modulo(num).zero?
31
+ minutes = (start..59).step(num).to_a
32
+
33
+ assert_equal "#{minutes.join(',')} * * * *", parse_time(num.minutes)
34
+ end
35
+ end
36
+
37
+ should 'parse correctly when given an "at" with hours as a integer' do
38
+ assert_equal "0,30 3 * * *", parse_time(30.minutes, nil, 3)
39
+ end
40
+ end
41
+
42
+ context "When parsing time in hours" do
43
+ should "parse correctly" do
44
+ assert_equal '0 * * * *', parse_time(1.hour)
45
+ assert_equal '0 0,2,4,6,8,10,12,14,16,18,20,22 * * *', parse_time(2.hours)
46
+ assert_equal '0 0,3,6,9,12,15,18,21 * * *', parse_time(3.hours)
47
+ assert_equal '0 5,10,15,20 * * *', parse_time(5.hours)
48
+ assert_equal '0 17 * * *', parse_time(17.hours)
49
+ assert_not_equal '0 24 * * *', parse_time(24.hours) # 24 hours bumps up into the day range
50
+ end
51
+
52
+ (2..23).each do |num|
53
+ should "parse correctly for #{num} hours" do
54
+ start = 0
55
+ start += num unless 24.modulo(num).zero?
56
+ hours = (start..23).step(num).to_a
57
+
58
+ assert_equal "0 #{hours.join(',')} * * *", parse_time(num.hours)
59
+ end
60
+ end
61
+
62
+ should "parse correctly when given an 'at' with minutes as an Integer" do
63
+ assert_minutes_equals "1", 1
64
+ assert_minutes_equals "14", 14
65
+ assert_minutes_equals "27", 27
66
+ assert_minutes_equals "55", 55
67
+ end
68
+
69
+ should "parse correctly when given an 'at' with minutes as a Time" do
70
+ # Basically just testing that Chronic parses some times and we get the minutes out of it
71
+ assert_minutes_equals "1", '3:01am'
72
+ assert_minutes_equals "1", 'January 21 2:01 PM'
73
+ assert_minutes_equals "0", 'midnight'
74
+ assert_minutes_equals "59", '13:59'
75
+ end
76
+ end
77
+
78
+ context "When parsing time in days (of month)" do
79
+ should "parse correctly" do
80
+ assert_equal '0 0 * * *', parse_time(1.days)
81
+ assert_equal '0 0 1,3,5,7,9,11,13,15,17,19,21,23,25,27,29,31 * *', parse_time(2.days)
82
+ assert_equal '0 0 1,5,9,13,17,21,25,29 * *', parse_time(4.days)
83
+ assert_equal '0 0 1,8,15,22 * *', parse_time(7.days)
84
+ assert_equal '0 0 1,17 * *', parse_time(16.days)
85
+ assert_equal '0 0 17 * *', parse_time(17.days)
86
+ assert_equal '0 0 29 * *', parse_time(29.days)
87
+ assert_not_equal '0 0 30 * *', parse_time(30.days) # 30 days bumps into the month range
88
+ end
89
+
90
+ should "parse correctly when given an 'at' with hours, minutes as a Time" do
91
+ # first param is an array with [hours, minutes]
92
+ assert_hours_and_minutes_equals %w(3 45), '3:45am'
93
+ assert_hours_and_minutes_equals %w(20 1), '8:01pm'
94
+ assert_hours_and_minutes_equals %w(0 0), 'midnight'
95
+ assert_hours_and_minutes_equals %w(1 23), '1:23 AM'
96
+ assert_hours_and_minutes_equals %w(23 59), 'March 21 11:59 pM'
97
+ end
98
+
99
+ should "parse correctly when given an 'at' with hours as an Integer" do
100
+ # first param is an array with [hours, minutes]
101
+ assert_hours_and_minutes_equals %w(1 0), 1
102
+ assert_hours_and_minutes_equals %w(3 0), 3
103
+ assert_hours_and_minutes_equals %w(15 0), 15
104
+ assert_hours_and_minutes_equals %w(19 0), 19
105
+ assert_hours_and_minutes_equals %w(23 0), 23
106
+ end
107
+ end
108
+
109
+ context "When parsing time in months" do
110
+ should "parse correctly" do
111
+ assert_equal '0 0 1 * *', parse_time(1.month)
112
+ assert_equal '0 0 1 1,3,5,7,9,11 *', parse_time(2.months)
113
+ assert_equal '0 0 1 1,4,7,10 *', parse_time(3.months)
114
+ assert_equal '0 0 1 1,5,9 *', parse_time(4.months)
115
+ assert_equal '0 0 1 1,6 *', parse_time(5.months)
116
+ assert_equal '0 0 1 7 *', parse_time(7.months)
117
+ assert_equal '0 0 1 8 *', parse_time(8.months)
118
+ assert_equal '0 0 1 9 *', parse_time(9.months)
119
+ assert_equal '0 0 1 10 *', parse_time(10.months)
120
+ assert_equal '0 0 1 11 *', parse_time(11.months)
121
+ assert_equal '0 0 1 12 *', parse_time(12.months)
122
+ end
123
+
124
+ should "parse correctly when given an 'at' with days, hours, minutes as a Time" do
125
+ # first param is an array with [days, hours, minutes]
126
+ assert_days_and_hours_and_minutes_equals %w(1 3 45), 'January 1st 3:45am'
127
+ assert_days_and_hours_and_minutes_equals %w(11 23 0), 'Feb 11 11PM'
128
+ assert_days_and_hours_and_minutes_equals %w(22 1 1), 'march 22nd at 1:01 am'
129
+ assert_days_and_hours_and_minutes_equals %w(23 0 0), 'march 22nd at midnight' # looks like midnight means the next day
130
+ end
131
+
132
+ should "parse correctly when given an 'at' with days as an Integer" do
133
+ # first param is an array with [days, hours, minutes]
134
+ assert_days_and_hours_and_minutes_equals %w(1 0 0), 1
135
+ assert_days_and_hours_and_minutes_equals %w(15 0 0), 15
136
+ assert_days_and_hours_and_minutes_equals %w(29 0 0), 29
137
+ end
138
+ end
139
+
140
+ context "When parsing time in days (of week)" do
141
+ should "parse days of the week correctly" do
142
+ {
143
+ '0' => %w(sun Sunday SUNDAY SUN),
144
+ '1' => %w(mon Monday MONDAY MON),
145
+ '2' => %w(tue tues Tuesday TUESDAY TUE),
146
+ '3' => %w(wed Wednesday WEDNESDAY WED),
147
+ '4' => %w(thu thurs thur Thursday THURSDAY THU),
148
+ '5' => %w(fri Friday FRIDAY FRI),
149
+ '6' => %w(sat Saturday SATURDAY SAT)
150
+ }.each do |day, day_tests|
151
+ day_tests.each do |day_test|
152
+ assert_equal "0 0 * * #{day}", parse_time(day_test)
153
+ end
154
+ end
155
+ end
156
+
157
+ should "allow additional directives" do
158
+ assert_equal '30 13 * * 5', parse_time('friday', nil, "1:30 pm")
159
+ assert_equal '22 2 * * 1', parse_time('Monday', nil, "2:22am")
160
+ assert_equal '55 17 * * 4', parse_time('THU', nil, "5:55PM")
161
+ end
162
+
163
+ should "parse weekday correctly" do
164
+ assert_equal '0 0 * * 1-5', parse_time('weekday')
165
+ assert_equal '0 0 * * 1-5', parse_time('Weekdays')
166
+ assert_equal '0 1 * * 1-5', parse_time('Weekdays', nil, "1:00 am")
167
+ assert_equal '59 5 * * 1-5', parse_time('Weekdays', nil, "5:59 am")
168
+ end
169
+
170
+ should "parse weekend correctly" do
171
+ assert_equal '0 0 * * 6,0', parse_time('weekend')
172
+ assert_equal '0 0 * * 6,0', parse_time('Weekends')
173
+ assert_equal '0 7 * * 6,0', parse_time('Weekends', nil, "7am")
174
+ assert_equal '2 18 * * 6,0', parse_time('Weekends', nil, "6:02PM")
175
+ end
176
+ end
177
+
178
+ context "When parsing time using the cron shortcuts" do
179
+ should "parse a :symbol into the correct shortcut" do
180
+ assert_equal '@reboot', parse_time(:reboot)
181
+ assert_equal '@annually', parse_time(:year)
182
+ assert_equal '@annually', parse_time(:yearly)
183
+ assert_equal '@daily', parse_time(:day)
184
+ assert_equal '@daily', parse_time(:daily)
185
+ assert_equal '@midnight', parse_time(:midnight)
186
+ assert_equal '@monthly', parse_time(:month)
187
+ assert_equal '@monthly', parse_time(:monthly)
188
+ assert_equal '@hourly', parse_time(:hour)
189
+ assert_equal '@hourly', parse_time(:hourly)
190
+ end
191
+
192
+ should "raise an exception if a valid shortcut is given but also an :at" do
193
+ assert_raises ArgumentError do
194
+ parse_time(:hour, nil, "1:00 am")
195
+ end
196
+
197
+ assert_raises ArgumentError do
198
+ parse_time(:reboot, nil, 5)
199
+ end
200
+
201
+ assert_raises ArgumentError do
202
+ parse_time(:day, nil, '4:20pm')
203
+ end
204
+ end
205
+ end
206
+
207
+ private
208
+
209
+ def assert_days_and_hours_and_minutes_equals(expected, time)
210
+ cron = parse_time(2.months, 'some task', time)
211
+ minutes, hours, days, *garbage = cron.split(' ')
212
+ assert_equal expected, [days, hours, minutes]
213
+ end
214
+
215
+ def assert_hours_and_minutes_equals(expected, time)
216
+ cron = parse_time(2.days, 'some task', time)
217
+ minutes, hours, *garbage = cron.split(' ')
218
+ assert_equal expected, [hours, minutes]
219
+ end
220
+
221
+ def assert_minutes_equals(expected, time)
222
+ cron = parse_time(2.hours, 'some task', time)
223
+ assert_equal expected, cron.split(' ')[0]
224
+ end
225
+
226
+ def parse_time(time = nil, task = nil, at = nil)
227
+ Whenever::Output::Cron.new(time, task, at).time_in_cron_syntax
228
+ end
229
+
230
+ end
@@ -0,0 +1,178 @@
1
+ require File.expand_path(File.dirname(__FILE__) + "/test_helper")
2
+
3
+ class OutputAtTest < Test::Unit::TestCase
4
+
5
+ context "weekday at a (single) given time" do
6
+ setup do
7
+ @output = Whenever.cron \
8
+ <<-file
9
+ every "weekday", :at => '5:02am' do
10
+ command "blahblah"
11
+ end
12
+ file
13
+ end
14
+
15
+ should "output the command using that time" do
16
+ assert_match '2 5 * * 1-5 blahblah', @output
17
+ end
18
+ end
19
+
20
+ context "weekday at a multiple diverse times, via an array" do
21
+ setup do
22
+ @output = Whenever.cron \
23
+ <<-file
24
+ every "weekday", :at => %w(5:02am 3:52pm) do
25
+ command "blahblah"
26
+ end
27
+ file
28
+ end
29
+
30
+ should "output the commands for both times given" do
31
+ assert_match '2 5 * * 1-5 blahblah', @output
32
+ assert_match '52 15 * * 1-5 blahblah', @output
33
+ end
34
+ end
35
+
36
+ context "weekday at a multiple diverse times, comma separated" do
37
+ setup do
38
+ @output = Whenever.cron \
39
+ <<-file
40
+ every "weekday", :at => '5:02am, 3:52pm' do
41
+ command "blahblah"
42
+ end
43
+ file
44
+ end
45
+
46
+ should "output the commands for both times given" do
47
+ assert_match '2 5 * * 1-5 blahblah', @output
48
+ assert_match '52 15 * * 1-5 blahblah', @output
49
+ end
50
+ end
51
+
52
+ context "weekday at a multiple aligned times" do
53
+ setup do
54
+ @output = Whenever.cron \
55
+ <<-file
56
+ every "weekday", :at => '5:02am, 3:02pm' do
57
+ command "blahblah"
58
+ end
59
+ file
60
+ end
61
+
62
+ should "output the command using one entry because the times are aligned" do
63
+ assert_match '2 5,15 * * 1-5 blahblah', @output
64
+ end
65
+ end
66
+
67
+ context "various days at a various aligned times" do
68
+ setup do
69
+ @output = Whenever.cron \
70
+ <<-file
71
+ every "mon,wed,fri", :at => '5:02am, 3:02pm' do
72
+ command "blahblah"
73
+ end
74
+ file
75
+ end
76
+
77
+ should "output the command using one entry because the times are aligned" do
78
+ assert_match '2 5,15 * * 1,3,5 blahblah', @output
79
+ end
80
+ end
81
+
82
+ context "various days at a various aligned times using a runner" do
83
+ setup do
84
+ @output = Whenever.cron \
85
+ <<-file
86
+ set :path, '/your/path'
87
+ every "mon,wed,fri", :at => '5:02am, 3:02pm' do
88
+ runner "blahblah"
89
+ end
90
+ file
91
+ end
92
+
93
+ should "output the runner using one entry because the times are aligned" do
94
+ assert_match '2 5,15 * * 1,3,5 cd /your/path && script/runner -e production "blahblah"', @output
95
+ end
96
+ end
97
+
98
+ context "various days at a various aligned times using a rake task" do
99
+ setup do
100
+ @output = Whenever.cron \
101
+ <<-file
102
+ set :path, '/your/path'
103
+ every "mon,wed,fri", :at => '5:02am, 3:02pm' do
104
+ rake "blah:blah"
105
+ end
106
+ file
107
+ end
108
+
109
+ should "output the rake task using one entry because the times are aligned" do
110
+ assert_match '2 5,15 * * 1,3,5 cd /your/path && RAILS_ENV=production /usr/bin/env rake blah:blah', @output
111
+ end
112
+ end
113
+
114
+ context "A command every 1.month at very diverse times" do
115
+ setup do
116
+ @output = Whenever.cron \
117
+ <<-file
118
+ every [1.month, 1.day], :at => 'january 5:02am, june 17th at 2:22pm, june 3rd at 3:33am' do
119
+ command "blahblah"
120
+ end
121
+ file
122
+ end
123
+
124
+ should "output 6 commands since none align" do
125
+ # The 1.month commands
126
+ assert_match '2 5 1 * * blahblah', @output
127
+ assert_match '22 14 17 * * blahblah', @output
128
+ assert_match '33 3 3 * * blahblah', @output
129
+
130
+ # The 1.day commands
131
+ assert_match '2 5 * * * blahblah', @output
132
+ assert_match '22 14 * * * blahblah', @output
133
+ assert_match '33 3 * * * blahblah', @output
134
+ end
135
+ end
136
+
137
+ context "Multiple commands output every :reboot" do
138
+ setup do
139
+ @output = Whenever.cron \
140
+ <<-file
141
+ every :reboot do
142
+ command "command_1"
143
+ command "command_2"
144
+ end
145
+ file
146
+ end
147
+
148
+ should "output both commands @reboot" do
149
+ assert_match "@reboot command_1", @output
150
+ assert_match "@reboot command_2", @output
151
+ end
152
+ end
153
+
154
+ context "Many different job types output every :day" do
155
+ setup do
156
+ @output = Whenever.cron \
157
+ <<-file
158
+ set :path, '/your/path'
159
+ every :day do
160
+ rake "blah:blah"
161
+ runner "runner_1"
162
+ command "command_1"
163
+ runner "runner_2"
164
+ command "command_2"
165
+ end
166
+ file
167
+ end
168
+
169
+ should "output all of the commands @daily" do
170
+ assert_match '@daily cd /your/path && RAILS_ENV=production /usr/bin/env rake blah:blah', @output
171
+ assert_match '@daily cd /your/path && script/runner -e production "runner_1"', @output
172
+ assert_match '@daily command_1', @output
173
+ assert_match '@daily cd /your/path && script/runner -e production "runner_2"', @output
174
+ assert_match '@daily command_2', @output
175
+ end
176
+ end
177
+
178
+ end