miga-base 0.6.4.2 → 0.7.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,6 +1,4 @@
1
1
 
2
- require 'daemons'
3
- require 'date'
4
2
  require 'shellwords'
5
3
 
6
4
  class MiGA::Daemon < MiGA::MiGA
@@ -55,21 +53,5 @@ module MiGA::Daemon::Base
55
53
  # complete
56
54
  def shutdown_when_done?() !!runopts(:shutdown_when_done); end
57
55
 
58
- ##
59
- # Initializes the daemon with +opts+
60
- def start(opts = []) daemon('start', opts); end
61
-
62
- ##
63
- # Stops the daemon with +opts+
64
- def stop(opts = []) daemon('stop', opts); end
65
-
66
- ##
67
- # Restarts the daemon with +opts+
68
- def restart(opts = []) daemon('restart', opts); end
69
-
70
- ##
71
- # Returns the status of the daemon with +opts+.
72
- def status(opts = []) daemon('status', opts); end
73
-
74
56
  end
75
57
 
data/lib/miga/lair.rb CHANGED
@@ -1,20 +1,14 @@
1
1
  # @package MiGA
2
2
  # @license Artistic-2.0
3
3
 
4
- require 'daemons'
5
4
  require 'miga/daemon'
5
+ require 'miga/common/with_daemon'
6
6
 
7
7
  ##
8
8
  # Lair of MiGA Daemons handling job submissions
9
9
  class MiGA::Lair < MiGA::MiGA
10
- ##
11
- # When was the last time the chief daemon in this lair was seen active?
12
- # Returns Time
13
- def self.last_alive(path)
14
- f = File.expand_path('.lair-alive', path)
15
- return nil unless File.exist? f
16
- Time.parse(File.read(f))
17
- end
10
+ include MiGA::Common::WithDaemon
11
+ extend MiGA::Common::WithDaemonClass
18
12
 
19
13
  # Absolute path to the directory where the projects are located
20
14
  attr_reader :path
@@ -22,69 +16,81 @@ class MiGA::Lair < MiGA::MiGA
22
16
  # Options used to setup the chief daemon
23
17
  attr_accessor :options
24
18
 
25
- # Integer indicating the current iteration
26
- attr_reader :loop_i
27
-
28
19
  ##
29
20
  # Initialize an inactive daemon for the directory at +path+. See #daemon
30
21
  # to wake the chief daemon. Supported options include:
31
22
  # - json: json definition for all children daemons, by default: nil
32
23
  # - latency: time to wait between iterations in seconds, by default: 120
33
24
  # - wait_for: time to wait for a daemon to report being alive in seconds,
34
- # by default: 900
25
+ # by default: 30
35
26
  # - keep_inactive: boolean indicating if daemons should stay alive even when
36
27
  # inactive (when all tasks are complete), by default: false
37
28
  # - name: A name for the chief daemon process, by default: basename of +path+
29
+ # - trust_timestamp: boolean indicating if the +modified+ timestamp of the
30
+ # project is to be trusted to determine changes in the project,
31
+ # by default: true
32
+ # - dry: Only report when daemons would be launched, but don't actually launch
33
+ # them
38
34
  def initialize(path, opts = {})
39
35
  @path = File.expand_path(path)
40
36
  @options = opts
41
- @loop_i = -1
42
37
  {
43
38
  json: nil,
44
- latency: 120,
45
- wait_for: 900,
39
+ latency: 30,
40
+ wait_for: 30,
46
41
  keep_inactive: false,
47
- name: File.basename(@path)
42
+ trust_timestamp: true,
43
+ name: File.basename(@path),
44
+ dry: false
48
45
  }.each { |k, v| @options[k] = v if @options[k].nil? }
49
46
  end
50
47
 
51
48
  ##
52
- # When was the last time the chief daemon for was seen active here?
53
- # Returns Time.
54
- def last_alive
55
- MiGA::Lair.last_alive path
49
+ # Path to the lair's chief daemon's home
50
+ alias daemon_home path
51
+
52
+ ##
53
+ # Name of the lair's chief daemon
54
+ def daemon_name
55
+ "MiGA:#{options[:name]}"
56
+ end
57
+
58
+ ##
59
+ # First loop of the lair's chief daemon
60
+ def daemon_first_loop
61
+ say '-----------------------------------'
62
+ say '%s launched' % daemon_name
63
+ say '-----------------------------------'
64
+ say 'Configuration options:'
65
+ say options.to_s
56
66
  end
57
67
 
58
68
  ##
59
- # Returns Hash containing the default options for the daemon.
60
- def default_options
61
- { dir_mode: :normal, dir: path, multiple: false, log_output: true }
69
+ # Run one loop step. Returns a Boolean indicating if the loop should continue.
70
+ def daemon_loop
71
+ check_directories
72
+ return false if options[:dry]
73
+ sleep(options[:latency])
74
+ true
62
75
  end
63
76
 
64
77
  ##
65
- # Launches the +task+ with options +opts+ (as command-line arguments) and
66
- # returns the process ID as an Integer. If +wait+ it waits for the process to
67
- # complete, immediately returns otherwise.
68
- # Supported tasks: start, stop, restart, status.
69
- def daemon(task, opts = [], wait = true)
70
- MiGA.DEBUG "Lair.daemon #{task} #{opts}"
71
- config = default_options
72
- opts.unshift(task.to_s)
73
- config[:ARGV] = opts
74
- # This additional degree of separation below was introduced so the Daemons
75
- # package doesn't kill the parent process in workflows.
76
- pid = fork do
77
- Daemons.run_proc("MiGA:#{options[:name]}", config) { while in_loop; end }
78
+ # Terminate all daemons in the lair (including the chief daemon)
79
+ def terminate_daemons
80
+ terminate_daemon(self)
81
+ each_project do |project|
82
+ terminate_daemon(MiGA::Daemon.new(project))
78
83
  end
79
- Process.wait(pid) if wait
80
- pid
81
84
  end
82
85
 
83
86
  ##
84
- # Tell the world that you're alive.
85
- def declare_alive
86
- File.open(File.join(path, '.lair-alive'), 'w') do |fh|
87
- fh.print Time.now.to_s
87
+ # Send termination message to +daemon+, an object implementing
88
+ # +MiGA::Common::WithDaemon+
89
+ def terminate_daemon(daemon)
90
+ say "Probing #{daemon.class} #{daemon.daemon_home}"
91
+ if daemon.active?
92
+ say 'Sending termination message'
93
+ FileUtils.touch(daemon.terminate_file)
88
94
  end
89
95
  end
90
96
 
@@ -94,13 +100,14 @@ class MiGA::Lair < MiGA::MiGA
94
100
  # Searches for MiGA projects recursively in all
95
101
  # subdirectories that are not MiGA projects.
96
102
  def each_project(dir = path)
97
- Dir.children(dir).each do |f|
103
+ Dir.entries(dir).each do |f|
104
+ next if %w[. ..].include?(f) # Ruby <= 2.3 doesn't have Dir.children
98
105
  f = File.join(dir, f)
99
106
  if MiGA::Project.exist? f
100
107
  project = MiGA::Project.load(f)
101
108
  raise "Cannot load project: #{f}" if project.nil?
102
109
  yield(project)
103
- elsif Dir.exists? f
110
+ elsif Dir.exist? f
104
111
  each_project(f) { |project| yield(project) }
105
112
  end
106
113
  end
@@ -110,8 +117,13 @@ class MiGA::Lair < MiGA::MiGA
110
117
  # Traverse directories checking MiGA projects
111
118
  def check_directories
112
119
  each_project do |project|
113
- alive = MiGA::Daemon.last_alive(project)
114
- next if !alive.nil? && alive > Time.now - options[:wait_for]
120
+ d = MiGA::Daemon.new(project)
121
+ next if d.active?
122
+ l_alive = d.last_alive
123
+ unless l_alive.nil?
124
+ next if options[:trust_timestamp] && project.metadata.updated < l_alive
125
+ next if l_alive > Time.now - options[:wait_for]
126
+ end
115
127
  launch_daemon(project)
116
128
  end
117
129
  end
@@ -122,32 +134,9 @@ class MiGA::Lair < MiGA::MiGA
122
134
  say "Launching daemon: #{project.path}"
123
135
  d = MiGA::Daemon.new(project, options[:json])
124
136
  d.runopts(:shutdown_when_done, true) unless options[:keep_inactive]
125
- d.daemon(:start, [], false)
126
- end
127
-
128
- ##
129
- # Run one loop step. Returns a Boolean indicating if the loop should continue.
130
- def in_loop
131
- declare_alive
132
- if loop_i == -1
133
- say '-----------------------------------'
134
- say 'MiGA:%s launched' % options[:name]
135
- say '-----------------------------------'
136
- say 'Configuration options:'
137
- say options.to_s
138
- @loop_i = 0
137
+ unless options[:dry]
138
+ d.start
139
+ sleep(1) # <- to make sure the daemon started up (it takes about 0.1 secs)
139
140
  end
140
- @loop_i += 1
141
- check_directories
142
- sleep(options[:latency])
143
- true
144
- end
145
-
146
- ##
147
- # Terminates a chief daemon
148
- def terminate
149
- say 'Terminating chief daemon...'
150
- f = File.expand_path('.lair-alive', project.path)
151
- File.unlink(f) if File.exist? f
152
141
  end
153
142
  end
data/lib/miga/metadata.rb CHANGED
@@ -135,6 +135,18 @@ class MiGA::Metadata < MiGA::MiGA
135
135
  data.each { |k, v| blk.call(k, v) }
136
136
  end
137
137
 
138
+ ##
139
+ # Time of last update
140
+ def updated
141
+ Time.parse(self[:updated]) unless self[:updated].nil?
142
+ end
143
+
144
+ ##
145
+ # Time of creation
146
+ def created
147
+ Time.parse(self[:created]) unless self[:created].nil?
148
+ end
149
+
138
150
  ##
139
151
  # Show contents in JSON format as a String
140
152
  def to_json
data/lib/miga/version.rb CHANGED
@@ -10,7 +10,7 @@ module MiGA
10
10
  # - Float representing the major.minor version.
11
11
  # - Integer representing gem releases of the current version.
12
12
  # - Integer representing minor changes that require new version number.
13
- VERSION = [0.6, 4, 2]
13
+ VERSION = [0.7, 0, 0]
14
14
 
15
15
  ##
16
16
  # Nickname for the current major.minor version.
@@ -18,7 +18,7 @@ module MiGA
18
18
 
19
19
  ##
20
20
  # Date of the current gem release.
21
- VERSION_DATE = Date.new(2020, 4, 15)
21
+ VERSION_DATE = Date.new(2020, 4, 18)
22
22
 
23
23
  ##
24
24
  # Reference of MiGA.
data/test/common_test.rb CHANGED
@@ -33,7 +33,7 @@ class CommonTest < Test::Unit::TestCase
33
33
  err = capture_stderr do
34
34
  MiGA::MiGA.DEBUG 'Dandadi'
35
35
  end
36
- assert(err.string =~ /Dandadi\n .*block in test_debug_trace/)
36
+ assert_match(/Dandadi\n .*block in test_debug_trace/, err.string)
37
37
  MiGA::MiGA.DEBUG_TRACE_OFF
38
38
  err = capture_stderr do
39
39
  MiGA::MiGA.DEBUG 'Dandada'
@@ -66,7 +66,7 @@ class CommonTest < Test::Unit::TestCase
66
66
  assert_raise do
67
67
  File.generic_transfer(hello, world, :monkey)
68
68
  end
69
- assert(!File.exist?(world), 'A monkey shouldn\'t create files.')
69
+ assert_path_not_exist(world, 'A monkey shouldn\'t create files.')
70
70
  ensure
71
71
  FileUtils.rm_rf $tmp
72
72
  end
@@ -75,8 +75,8 @@ class CommonTest < Test::Unit::TestCase
75
75
  assert_equal('Xa sp. C', 'Xa_sp__C'.unmiga_name)
76
76
  assert_equal('X_______', 'X^*.!{}!'.miga_name)
77
77
  assert_equal('aB09', 'aB09'.miga_name)
78
- assert('R2D2'.miga_name?)
79
- assert(!'C3-PO'.miga_name?)
78
+ assert_predicate('R2D2', :miga_name?)
79
+ assert_not_predicate('C3-PO', :miga_name?)
80
80
  assert_equal("123\n1\n", '1231'.wrap_width(3))
81
81
  end
82
82
 
data/test/daemon_test.rb CHANGED
@@ -42,8 +42,8 @@ class DaemonTest < Test::Unit::TestCase
42
42
  def test_check_project
43
43
  d1 = $d1
44
44
  helper_datasets_with_results.first.inactivate!
45
- out = capture_stdout { d1.check_project }
46
- assert(out.string =~ /Queueing miga-project:p/)
45
+ out = capture_stderr { d1.check_project }
46
+ assert_match(/Queueing miga-project:p/, out.string)
47
47
  assert_equal(1, d1.jobs_to_run.size)
48
48
  assert_equal(:p, d1.jobs_to_run.first[:job])
49
49
  assert_equal('project1:p:miga-project', d1.get_job(:p)[:task_name])
@@ -53,10 +53,10 @@ class DaemonTest < Test::Unit::TestCase
53
53
  p = $p1
54
54
  d = $d1
55
55
  d.runopts(:maxjobs, 0, true)
56
- assert(d.jobs_to_run.empty?)
56
+ assert_empty(d.jobs_to_run)
57
57
  ds = p.add_dataset('ds1')
58
58
  d.check_datasets
59
- assert(d.jobs_to_run.empty?)
59
+ assert_empty(d.jobs_to_run)
60
60
  FileUtils.cp(
61
61
  File.expand_path('daemon/daemon.json', p.path),
62
62
  File.expand_path('data/01.raw_reads/ds1.1.fastq', p.path)
@@ -66,10 +66,10 @@ class DaemonTest < Test::Unit::TestCase
66
66
  File.expand_path('data/01.raw_reads/ds1.done', p.path)
67
67
  )
68
68
  ds.first_preprocessing(true)
69
- out = capture_stdout do
69
+ out = capture_stderr do
70
70
  d.check_datasets
71
71
  end
72
- assert(out.string =~ /Queueing #{ds.name}:d/)
72
+ assert_match(/Queueing #{ds.name}:d/, out.string)
73
73
  assert_equal(1, d.jobs_to_run.size)
74
74
  assert_equal('echo project1:d:ds1 >/dev/null', d.jobs_to_run.first[:cmd])
75
75
  assert_equal(d.jobs_to_run.first, d.get_job(:d, ds))
@@ -79,15 +79,15 @@ class DaemonTest < Test::Unit::TestCase
79
79
  p = $p1
80
80
  d = $d1
81
81
  d.runopts(:latency, 0, true)
82
- assert_equal(-1, d.loop_i)
82
+ assert_nil(d.loop_i)
83
83
  assert_nil(d.last_alive)
84
- out = capture_stdout { d.in_loop }
84
+ out = capture_stderr { d.in_loop }.string
85
85
  assert_equal(Time, d.last_alive.class)
86
- assert(out.string =~ /-{20}\n.*MiGA:#{p.name} launched/)
87
- 10.times{ d.in_loop }
86
+ assert_match(/-{20}\n.*MiGA:#{p.name} launched/, out)
87
+ 11.times { d.in_loop }
88
88
  assert_equal(11, d.loop_i)
89
- out = capture_stdout { d.in_loop }
90
- assert(out.string =~ /Probing running jobs/)
89
+ out = capture_stderr { d.in_loop }.string
90
+ assert_match(/Probing running jobs/, out)
91
91
  assert_equal(0, d.loop_i)
92
92
  end
93
93
 
@@ -98,25 +98,24 @@ class DaemonTest < Test::Unit::TestCase
98
98
  assert_equal(0, d.latency)
99
99
  omit_if($jruby_tests, 'JRuby doesn\'t implement fork.')
100
100
  child = $child = d.daemon(:start, ['--shush'])
101
- assert(child.is_a? Integer)
102
- assert(child != 0, 'The daemond process should have non-zero PID')
101
+ assert_not_nil(child)
102
+ assert_gt(child, 1)
103
103
  assert_equal(0, `ps -p "#{child}" -o ppid=`.strip.to_i,
104
104
  'The daemon process should be detached')
105
105
  sleep(3)
106
- dpath = File.join(p.path, 'daemon', "MiGA:#{p.name}")
107
- assert(File.exist?("#{dpath}.pid"))
108
- out = capture_stdout { d.stop }
106
+ assert_path_exist(d.pid_file)
107
+ out = capture_stderr { d.stop }.string
109
108
  assert_raise(Errno::ESRCH) { Process.kill(0, child) }
110
- assert_equal('', out.string)
111
- assert(!File.exist?("#{dpath}.pid"))
112
- assert(File.exist?("#{dpath}.output"))
113
- File.open("#{dpath}.output", "r") do |fh|
114
- l = fh.each_line.to_a
115
- assert(l[0] =~ /-{20}\n/)
116
- assert(l[1] =~ /MiGA:#{p.name} launched/)
117
- assert(l[2] =~ /-{20}\n/)
118
- assert(l[5] =~ /Probing running jobs\n/)
119
- end
109
+ assert_match(/Sending termination message/, out)
110
+ assert_path_not_exist(d.pid_file)
111
+ assert_path_exist(d.output_file)
112
+ l = File.readlines(d.output_file)
113
+ {
114
+ 0 => /-{20}\n/,
115
+ 1 => /MiGA:#{p.name} launched/,
116
+ 2 => /-{20}\n/,
117
+ 5 => /Probing running jobs\n/
118
+ }.each { |k, v| assert_match(v, l[k], "unexpected line: #{k}") }
120
119
  ensure
121
120
  begin
122
121
  Process.kill('KILL', $child) if !$child.nil?
@@ -129,8 +128,9 @@ class DaemonTest < Test::Unit::TestCase
129
128
  p = MiGA::Project.new(File.expand_path('last_alive', $tmp))
130
129
  d = MiGA::Daemon.new(p)
131
130
  assert_nil(d.last_alive)
131
+ omit_if($jruby_tests, 'JRuby doesn\'t implement fork.')
132
132
  d.declare_alive
133
- assert(d.last_alive - Time.now < 1)
133
+ assert_lt(d.last_alive, Time.now)
134
134
  end
135
135
 
136
136
  def test_options
@@ -150,17 +150,28 @@ class DaemonTest < Test::Unit::TestCase
150
150
  end
151
151
 
152
152
  def test_say
153
- out = capture_stdout { $d1.say 'Olm' }
154
- assert(out.string =~ /^\[.*\] Olm/)
153
+ out = capture_stderr { $d1.say 'Olm' }.string
154
+ assert_match(/^\[.*\] Olm/, out)
155
155
  end
156
156
 
157
157
  def test_terminate
158
158
  d = $d1
159
+ assert_not_predicate(d, :active?)
160
+ assert_path_not_exist(d.alive_file)
161
+ assert_path_not_exist(d.terminated_file)
162
+
163
+ omit_if($jruby_tests, 'JRuby doesn\'t implement fork.')
159
164
  d.declare_alive
165
+ assert_predicate(d, :active?)
166
+ assert_not_nil(d.last_alive)
167
+ assert_path_exist(d.alive_file)
168
+ assert_path_not_exist(d.terminated_file)
169
+
170
+ d.terminate
171
+ assert_path_not_exist(d.alive_file)
172
+ assert_path_exist(d.terminated_file)
173
+ assert_not_predicate(d, :active?)
160
174
  assert_not_nil(d.last_alive)
161
- out = capture_stdout { d.terminate }
162
- assert(out.string =~ /Terminating daemon/)
163
- assert_nil(d.last_alive)
164
175
  end
165
176
 
166
177
  def test_maxjobs_json
@@ -168,7 +179,7 @@ class DaemonTest < Test::Unit::TestCase
168
179
  helper_datasets_with_results(3)
169
180
  assert_equal(0, d1.jobs_running.size)
170
181
  assert_equal(0, d1.jobs_to_run.size)
171
- capture_stdout { d1.in_loop }
182
+ capture_stderr { d1.in_loop }
172
183
  assert_equal(1, d1.jobs_running.size)
173
184
  assert_equal(2, d1.jobs_to_run.size)
174
185
  end
@@ -179,7 +190,7 @@ class DaemonTest < Test::Unit::TestCase
179
190
  d1.runopts(:maxjobs, 2)
180
191
  assert_equal(0, d1.jobs_running.size)
181
192
  assert_equal(0, d1.jobs_to_run.size)
182
- capture_stdout { d1.in_loop }
193
+ capture_stderr { d1.in_loop }
183
194
  assert_equal(2, d1.jobs_running.size)
184
195
  assert_equal(1, d1.jobs_to_run.size)
185
196
  end
@@ -195,9 +206,9 @@ class DaemonTest < Test::Unit::TestCase
195
206
  File.open(f, 'w') do |h|
196
207
  h.puts '{"jobs_running":[{"ds":1,"ds_name":"d1"},{}],"jobs_to_run":[]}'
197
208
  end
198
- out = capture_stdout { d1.load_status }
209
+ out = capture_stderr { d1.load_status }.string
199
210
  assert_equal(2, d1.jobs_running.size)
200
- assert(out.string =~ /Loading previous status/)
211
+ assert_match(/Loading previous status/, out)
201
212
  assert_equal(MiGA::Dataset, d1.jobs_running[0][:ds].class)
202
213
  assert_nil(d1.jobs_running[1][:ds])
203
214
  end
@@ -215,10 +226,10 @@ class DaemonTest < Test::Unit::TestCase
215
226
  '{"job":"trimmed_reads","ds":1,"ds_name":"d0"}]' \
216
227
  ',"jobs_to_run":[]}'
217
228
  end
218
- capture_stdout { d1.load_status }
229
+ capture_stderr { d1.load_status }
219
230
  assert_equal(3, d1.jobs_running.size)
220
- out = capture_stdout { d1.flush! }
221
- assert(out.string =~ /Completed pid:/)
231
+ out = capture_stderr { d1.flush! }.string
232
+ assert_match(/Completed pid:/, out)
222
233
  assert_equal([], d1.jobs_running)
223
234
  end
224
235
 
@@ -227,8 +238,8 @@ class DaemonTest < Test::Unit::TestCase
227
238
  f = File.join($tmp, 'nodes.txt')
228
239
  File.open(f, 'w') { |h| h.puts 'localhost' }
229
240
  assert_equal(true, d1.next_host)
230
- out = capture_stdout { d1.runopts(:nodelist, f) }
231
- assert(out.string =~ /Reading node list:/)
241
+ out = capture_stderr { d1.runopts(:nodelist, f) }.string
242
+ assert_match(/Reading node list:/, out)
232
243
  assert_equal(true, d1.next_host)
233
244
  d1.runopts(:type, 'ssh')
234
245
  assert_equal(0, d1.next_host)
@@ -236,14 +247,14 @@ class DaemonTest < Test::Unit::TestCase
236
247
  File.open(f, 'w') do |h|
237
248
  h.puts '{"jobs_running":[{"job":"p","hostk":0}], "jobs_to_run":[]}'
238
249
  end
239
- capture_stdout { d1.load_status }
250
+ capture_stderr { d1.load_status }
240
251
  assert_nil(d1.next_host)
241
252
  end
242
253
 
243
254
  def test_shutdown_when_done
244
255
  $d1.runopts(:shutdown_when_done, true)
245
- out = capture_stdout { assert(!$d1.in_loop) }
246
- assert(out.string =~ /Nothing else to do/)
256
+ out = capture_stderr { assert { !$d1.in_loop } }.string
257
+ assert_match(/Nothing else to do/, out)
247
258
  end
248
259
 
249
260
  def test_update_format_0
@@ -276,7 +287,7 @@ class DaemonTest < Test::Unit::TestCase
276
287
  d1.runopts(:nodelist, '$MIGA_TEST_NODELIST')
277
288
  end
278
289
  ENV['MIGA_TEST_NODELIST'] = f
279
- capture_stdout { d1.runopts(:nodelist, '$MIGA_TEST_NODELIST') }
290
+ capture_stderr { d1.runopts(:nodelist, '$MIGA_TEST_NODELIST') }
280
291
  helper_daemon_launch_job
281
292
  assert_equal("project1:p:miga-project\n", File.read(t))
282
293
  end
@@ -293,20 +304,22 @@ class DaemonTest < Test::Unit::TestCase
293
304
  d1.runopts(:type, 'qsub')
294
305
  d1.runopts(:cmd, 'echo ""')
295
306
  helper_datasets_with_results.first.inactivate!
296
- capture_stdout { d1.check_project }
297
- out = capture_stdout { d1.launch_job(d1.jobs_to_run.shift) }
298
- assert(out.string =~ /Unsuccessful project1:p:miga-project, rescheduling/)
307
+ capture_stderr { d1.check_project }
308
+ omit_if($jruby_tests, 'JRuby doesn\'t implement fork.')
309
+ out = capture_stderr { d1.launch_job(d1.jobs_to_run.shift) }.string
310
+ assert_match(/Unsuccessful project1:p:miga-project, rescheduling/, out)
299
311
  assert_equal(0, d1.jobs_running.size)
300
312
  assert_equal(1, d1.jobs_to_run.size)
301
313
  end
302
314
 
303
315
  def helper_daemon_launch_job
316
+ omit_if($jruby_tests, 'JRuby doesn\'t implement fork.')
304
317
  d1 = $d1
305
318
  helper_datasets_with_results.first.inactivate!
306
319
  assert_equal(0, d1.jobs_to_run.size, 'The queue should be empty')
307
- capture_stdout { d1.check_project }
320
+ capture_stderr { d1.check_project }
308
321
  assert_equal(1, d1.jobs_to_run.size, 'The queue should have one job')
309
- capture_stdout { d1.flush! }
322
+ capture_stderr { d1.flush! }
310
323
  sleep(1)
311
324
  assert_equal(0, d1.jobs_to_run.size, 'There should be nothing running')
312
325
  assert_equal(1, d1.jobs_running.size, 'There should be one job running')