nulogy-hydra 0.23.2.1

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 (75) hide show
  1. data/.document +5 -0
  2. data/LICENSE +20 -0
  3. data/README.rdoc +39 -0
  4. data/Rakefile +56 -0
  5. data/TODO +18 -0
  6. data/VERSION +1 -0
  7. data/caliper.yml +6 -0
  8. data/hydra-icon-64x64.png +0 -0
  9. data/hydra.gemspec +131 -0
  10. data/hydra_gray.png +0 -0
  11. data/lib/hydra/cucumber/formatter.rb +29 -0
  12. data/lib/hydra/hash.rb +16 -0
  13. data/lib/hydra/js/lint.js +5150 -0
  14. data/lib/hydra/listener/abstract.rb +39 -0
  15. data/lib/hydra/listener/minimal_output.rb +24 -0
  16. data/lib/hydra/listener/notifier.rb +17 -0
  17. data/lib/hydra/listener/progress_bar.rb +48 -0
  18. data/lib/hydra/listener/report_generator.rb +31 -0
  19. data/lib/hydra/master.rb +252 -0
  20. data/lib/hydra/message/master_messages.rb +19 -0
  21. data/lib/hydra/message/runner_messages.rb +46 -0
  22. data/lib/hydra/message/worker_messages.rb +52 -0
  23. data/lib/hydra/message.rb +47 -0
  24. data/lib/hydra/messaging_io.rb +49 -0
  25. data/lib/hydra/pipe.rb +61 -0
  26. data/lib/hydra/proxy_config.rb +27 -0
  27. data/lib/hydra/runner.rb +306 -0
  28. data/lib/hydra/runner_listener/abstract.rb +23 -0
  29. data/lib/hydra/safe_fork.rb +31 -0
  30. data/lib/hydra/spec/autorun_override.rb +3 -0
  31. data/lib/hydra/spec/hydra_formatter.rb +26 -0
  32. data/lib/hydra/ssh.rb +41 -0
  33. data/lib/hydra/stdio.rb +16 -0
  34. data/lib/hydra/sync.rb +99 -0
  35. data/lib/hydra/tasks.rb +366 -0
  36. data/lib/hydra/threadsafe_io.rb +18 -0
  37. data/lib/hydra/tmpdir.rb +11 -0
  38. data/lib/hydra/trace.rb +29 -0
  39. data/lib/hydra/worker.rb +168 -0
  40. data/lib/hydra.rb +16 -0
  41. data/nulogy-hydra.gemspec +122 -0
  42. data/test/fixtures/assert_true.rb +7 -0
  43. data/test/fixtures/bad_proxy_config.yml +4 -0
  44. data/test/fixtures/config.yml +4 -0
  45. data/test/fixtures/conflicting.rb +10 -0
  46. data/test/fixtures/features/step_definitions.rb +21 -0
  47. data/test/fixtures/features/write_alternate_file.feature +7 -0
  48. data/test/fixtures/features/write_file.feature +7 -0
  49. data/test/fixtures/hello_world.rb +3 -0
  50. data/test/fixtures/hydra_worker_init.rb +2 -0
  51. data/test/fixtures/js_file.js +4 -0
  52. data/test/fixtures/json_data.json +4 -0
  53. data/test/fixtures/many_outputs_to_console.rb +9 -0
  54. data/test/fixtures/master_listeners.rb +10 -0
  55. data/test/fixtures/proxy_config.yml +4 -0
  56. data/test/fixtures/proxy_config_http.yml +4 -0
  57. data/test/fixtures/runner_listeners.rb +23 -0
  58. data/test/fixtures/slow.rb +9 -0
  59. data/test/fixtures/sync_test.rb +8 -0
  60. data/test/fixtures/task_test_config.yml +6 -0
  61. data/test/fixtures/write_file.rb +10 -0
  62. data/test/fixtures/write_file_alternate_spec.rb +10 -0
  63. data/test/fixtures/write_file_spec.rb +9 -0
  64. data/test/fixtures/write_file_with_pending_spec.rb +11 -0
  65. data/test/master_test.rb +383 -0
  66. data/test/message_test.rb +31 -0
  67. data/test/pipe_test.rb +38 -0
  68. data/test/proxy_config_test.rb +31 -0
  69. data/test/runner_test.rb +196 -0
  70. data/test/ssh_test.rb +25 -0
  71. data/test/sync_test.rb +113 -0
  72. data/test/task_test.rb +21 -0
  73. data/test/test_helper.rb +107 -0
  74. data/test/worker_test.rb +60 -0
  75. metadata +208 -0
@@ -0,0 +1,383 @@
1
+ require File.join(File.dirname(__FILE__), 'test_helper')
2
+ require File.join(File.dirname(__FILE__), 'fixtures', 'runner_listeners')
3
+ require File.join(File.dirname(__FILE__), 'fixtures', 'master_listeners')
4
+
5
+ class MasterTest < Test::Unit::TestCase
6
+ context "with a file to test and a destination to verify" do
7
+ setup do
8
+ # avoid having other tests interfering with us
9
+ sleep(0.2)
10
+ FileUtils.rm_f(target_file)
11
+ end
12
+
13
+ teardown do
14
+ FileUtils.rm_f(target_file)
15
+ end
16
+
17
+ should "run a test" do
18
+ Hydra::Master.new(
19
+ :files => [test_file]
20
+ )
21
+ assert File.exists?(target_file)
22
+ assert_equal "HYDRA", File.read(target_file)
23
+ end
24
+
25
+ # this test simulates what happens when we have 2 tests with the same
26
+ # class name but with different parent classes. This can happen when
27
+ # we have a functional and an integration test class with the same name.
28
+ should "run even with a test that will not require" do
29
+ class FileOutputListener < Hydra::Listener::Abstract
30
+ attr_accessor :output
31
+ def initialize(&block)
32
+ self.output = {}
33
+ end
34
+
35
+ def file_end(file, output)
36
+ self.output[file] = output
37
+ end
38
+ end
39
+
40
+ listener = FileOutputListener.new
41
+ sync_test = File.join(File.dirname(__FILE__), 'fixtures', 'sync_test.rb')
42
+ Hydra::Master.new(
43
+ # we want the actual test to run last to make sure the runner can still run tests
44
+ :files => [sync_test, conflicting_test_file, test_file],
45
+ :autosort => false,
46
+ :listeners => [listener]
47
+ )
48
+ assert_match /superclass mismatch for class SyncTest/, listener.output[conflicting_test_file]
49
+ assert_match conflicting_test_file, listener.output[conflicting_test_file]
50
+ assert File.exists?(target_file)
51
+ assert_equal "HYDRA", File.read(target_file)
52
+ end
53
+
54
+ should "run a spec with pending examples" do
55
+ progress_bar = Hydra::Listener::ProgressBar.new(StringIO.new)
56
+ Hydra::Master.new(
57
+ :files => [rspec_file_with_pending],
58
+ :listeners => [progress_bar]
59
+ )
60
+ assert File.exists?(target_file)
61
+ assert_equal "HYDRA", File.read(target_file)
62
+ assert_equal false, progress_bar.instance_variable_get('@errors')
63
+ end
64
+
65
+ should "generate a report" do
66
+ Hydra::Master.new(:files => [test_file])
67
+ assert File.exists?(target_file)
68
+ assert_equal "HYDRA", File.read(target_file)
69
+ report_file = File.join(Dir.consistent_tmpdir, 'hydra_heuristics.yml')
70
+ assert File.exists?(report_file)
71
+ assert report = YAML.load_file(report_file)
72
+ assert_not_nil report[test_file]
73
+ end
74
+
75
+ should "run a test 6 times on 1 worker with 2 runners" do
76
+ Hydra::Master.new(
77
+ :files => [test_file]*6,
78
+ :workers => [ { :type => :local, :runners => 2 } ]
79
+ )
80
+ assert File.exists?(target_file)
81
+ assert_equal "HYDRA"*6, File.read(target_file)
82
+ end
83
+
84
+ # The test being run sleeps for 5 seconds. So, if this was run in
85
+ # series, it would take at least 50 seconds. This test ensures that
86
+ # in runs in less than that amount of time. Since there are 10
87
+ # runners to run the file 10 times, it should only take 5-10 seconds
88
+ # based on overhead.
89
+ should "run a slow test 10 times on 1 worker with 10 runners quickly" do
90
+ start = Time.now
91
+ Hydra::Master.new(
92
+ :files => [File.join(File.dirname(__FILE__), 'fixtures', 'slow.rb')]*10,
93
+ :workers => [
94
+ { :type => :local, :runners => 10 }
95
+ ]
96
+ )
97
+ finish = Time.now
98
+ assert (finish-start) < 30, "took #{finish-start} seconds"
99
+ end
100
+
101
+ should "run a slow test 10 times on 2 workers with 5 runners each quickly" do
102
+ start = Time.now
103
+ Hydra::Master.new(
104
+ :files => [File.join(File.dirname(__FILE__), 'fixtures', 'slow.rb')]*10,
105
+ :workers => [
106
+ { :type => :local, :runners => 5 },
107
+ { :type => :local, :runners => 5 }
108
+ ]
109
+ )
110
+ finish = Time.now
111
+ assert (finish-start) < 15, "took #{finish-start} seconds"
112
+ end
113
+
114
+ should "run a test via ssh" do
115
+ Hydra::Master.new(
116
+ :files => [test_file],
117
+ :workers => [{
118
+ :type => :ssh,
119
+ :connect => 'localhost',
120
+ :directory => remote_dir_path,
121
+ :runners => 1
122
+ }]
123
+ )
124
+ assert File.exists?(target_file)
125
+ assert_equal "HYDRA", File.read(target_file)
126
+ end
127
+
128
+ should "run a test with config from a yaml file" do
129
+ Hydra::Master.new(
130
+ :files => [test_file],
131
+ :config => File.join(File.dirname(__FILE__), 'fixtures', 'config.yml')
132
+ )
133
+ assert File.exists?(target_file)
134
+ assert_equal "HYDRA", File.read(target_file)
135
+ end
136
+
137
+ should "synchronize a test file over ssh with rsync" do
138
+ local = File.join(Dir.consistent_tmpdir, 'hydra', 'local')
139
+ remote = File.join(Dir.consistent_tmpdir, 'hydra', 'remote')
140
+ sync_test = File.join(File.dirname(__FILE__), 'fixtures', 'sync_test.rb')
141
+ [local, remote].each{|f| FileUtils.rm_rf f; FileUtils.mkdir_p f}
142
+
143
+ # setup the folders:
144
+ # local:
145
+ # - test_a
146
+ # - test_c
147
+ # remote:
148
+ # - test_b
149
+ #
150
+ # add test_c to exludes
151
+ FileUtils.cp(sync_test, File.join(local, 'test_a.rb'))
152
+ FileUtils.cp(sync_test, File.join(local, 'test_c.rb'))
153
+ FileUtils.cp(sync_test, File.join(remote, 'test_b.rb'))
154
+
155
+ # ensure a is not on remote
156
+ assert !File.exists?(File.join(remote, 'test_a.rb')), "A should not be on remote"
157
+ # ensure c is not on remote
158
+ assert !File.exists?(File.join(remote, 'test_c.rb')), "C should not be on remote"
159
+ # ensure b is on remote
160
+ assert File.exists?(File.join(remote, 'test_b.rb')), "B should be on remote"
161
+
162
+ Hydra::Master.new(
163
+ :files => ['test_a.rb'],
164
+ :workers => [{
165
+ :type => :ssh,
166
+ :connect => 'localhost',
167
+ :directory => remote,
168
+ :runners => 1
169
+ }],
170
+ :sync => {
171
+ :directory => local,
172
+ :exclude => ['test_c.rb']
173
+ }
174
+ )
175
+ # ensure a is copied
176
+ assert File.exists?(File.join(remote, 'test_a.rb')), "A was not copied"
177
+ # ensure c is not copied
178
+ assert !File.exists?(File.join(remote, 'test_c.rb')), "C was copied, should be excluded"
179
+ # ensure b is deleted
180
+ assert !File.exists?(File.join(remote, 'test_b.rb')), "B was not deleted"
181
+ end
182
+ end
183
+
184
+ context "with a runner_end event" do
185
+ setup do
186
+ # avoid having other tests interfering with us
187
+ sleep(0.2)
188
+ FileUtils.rm_f(target_file)
189
+ FileUtils.rm_f(alternate_target_file)
190
+
191
+ @runner_began_flag = File.expand_path(File.join(Dir.consistent_tmpdir, 'runner_began_flag')) #used to know when the worker is ready
192
+ FileUtils.rm_f(@runner_began_flag)
193
+
194
+ @runner_listener = 'HydraExtension::RunnerListener::RunnerEndTest.new' # runner_end method that creates alternate_target_file
195
+ @master_listener = HydraExtension::Listener::WorkerBeganFlag.new #used to know when the runner is up
196
+ end
197
+
198
+ teardown do
199
+ FileUtils.rm_f(target_file)
200
+ FileUtils.rm_f(alternate_target_file)
201
+ end
202
+
203
+ context "running a local worker" do
204
+ should "run runner_end on successful termination" do
205
+ @pid = Process.fork do
206
+ Hydra::Master.new(
207
+ :files => [test_file] * 6,
208
+ :autosort => false,
209
+ :listeners => [@master_listener],
210
+ :runner_listeners => [@runner_listener],
211
+ :verbose => false
212
+ )
213
+ end
214
+ Process.waitpid @pid
215
+
216
+ assert_file_exists alternate_target_file
217
+ end
218
+
219
+ should "run runner_end after interruption signal" do
220
+ add_infinite_worker_begin_to @master_listener
221
+
222
+ capture_stderr do # redirect stderr
223
+ @pid = Process.fork do
224
+ Hydra::Master.new(
225
+ :files => [test_file],
226
+ :autosort => false,
227
+ :listeners => [@master_listener],
228
+ :runner_listeners => [@runner_listener],
229
+ :verbose => false
230
+ )
231
+ end
232
+ end
233
+ wait_for_runner_to_begin
234
+
235
+ Process.kill 'SIGINT', @pid
236
+ Process.waitpid @pid
237
+
238
+ assert_file_exists alternate_target_file
239
+ end
240
+ end
241
+
242
+ context "running a remote worker" do
243
+ setup do
244
+ copy_worker_init_file # this method has a protection to avoid erasing an existing worker_init_file
245
+ end
246
+
247
+ teardown do
248
+ FileUtils.rm_f(@remote_init_file) unless @protect_init_file
249
+ end
250
+
251
+ should "run runner_end on successful termination" do
252
+ capture_stderr do # redirect stderr
253
+ @pid = Process.fork do
254
+ Hydra::Master.new(
255
+ :files => [test_file],
256
+ :autosort => false,
257
+ :listeners => [@master_listener],
258
+ :runner_listeners => [@runner_listener],
259
+ :workers => [{
260
+ :type => :ssh,
261
+ :connect => 'localhost',
262
+ :directory => remote_dir_path,
263
+ :runners => 1
264
+ }],
265
+ :verbose => false
266
+ )
267
+ end
268
+ end
269
+ Process.waitpid @pid
270
+
271
+ assert_file_exists alternate_target_file
272
+ end
273
+ end
274
+ end
275
+
276
+ context "redirecting runner's output and errors" do
277
+ setup do
278
+ # avoid having other tests interfering with us
279
+ sleep(0.2)
280
+ FileUtils.rm_f(target_file)
281
+ FileUtils.rm_f(runner_log_file)
282
+ FileUtils.rm_f("#{remote_dir_path}/#{runner_log_file}")
283
+ end
284
+
285
+ teardown do
286
+ FileUtils.rm_f(target_file)
287
+ FileUtils.rm_f(runner_log_file)
288
+ FileUtils.rm_f("#{remote_dir_path}/#{runner_log_file}")
289
+ end
290
+
291
+ should "create a runner log file when usign local worker and passing a log file name" do
292
+ @pid = Process.fork do
293
+ Hydra::Master.new(
294
+ :files => [test_file],
295
+ :runner_log_file => runner_log_file,
296
+ :verbose => false
297
+ )
298
+ end
299
+ Process.waitpid @pid
300
+
301
+ assert_file_exists target_file # ensure the test was successfully ran
302
+ assert_file_exists runner_log_file
303
+ end
304
+
305
+ should "create a runner log file when usign remote worker and passing a log file name" do
306
+ @pid = Process.fork do
307
+ Hydra::Master.new(
308
+ :files => [test_file],
309
+ :workers => [{
310
+ :type => :ssh,
311
+ :connect => 'localhost',
312
+ :directory => remote_dir_path,
313
+ :runners => 1
314
+ }],
315
+ :verbose => false,
316
+ :runner_log_file => runner_log_file
317
+ )
318
+ end
319
+ Process.waitpid @pid
320
+
321
+ assert_file_exists target_file # ensure the test was successfully ran
322
+ assert_file_exists "#{remote_dir_path}/#{runner_log_file}"
323
+ end
324
+
325
+ should "create the default runner log file when passing an incorrect log file path" do
326
+ default_log_file = "#{remote_dir_path}/#{Hydra::Runner::DEFAULT_LOG_FILE}" # hydra-runner.log"
327
+ FileUtils.rm_f(default_log_file)
328
+
329
+ @pid = Process.fork do
330
+ Hydra::Master.new(
331
+ :files => [test_file],
332
+ :workers => [{
333
+ :type => :ssh,
334
+ :connect => 'localhost',
335
+ :directory => remote_dir_path,
336
+ :runners => 1
337
+ }],
338
+ :verbose => false,
339
+ :runner_log_file => 'invalid-dir/#{runner_log_file}'
340
+ )
341
+ end
342
+ Process.waitpid @pid
343
+
344
+ assert_file_exists target_file # ensure the test was successfully ran
345
+ assert_file_exists default_log_file #default log file
346
+ assert !File.exists?( "#{remote_dir_path}/#{runner_log_file}" )
347
+
348
+ FileUtils.rm_f(default_log_file)
349
+ end
350
+ end
351
+
352
+ private
353
+
354
+ def runner_log_file
355
+ "my-hydra-runner.log"
356
+ end
357
+
358
+ def add_infinite_worker_begin_to master_listener
359
+ class << master_listener
360
+ def worker_begin( worker )
361
+ super
362
+ sleep 1 while true #ensure the process doesn't finish before killing it
363
+ end
364
+ end
365
+ end
366
+
367
+ # this requires that a worker_begin listener creates a file named worker_began_flag in tmp directory
368
+ def wait_for_runner_to_begin
369
+ assert_file_exists @runner_began_flag
370
+ end
371
+
372
+ # with a protection to avoid erasing something important in lib
373
+ def copy_worker_init_file
374
+ @remote_init_file = "#{remote_dir_path}/#{File.basename( hydra_worker_init_file )}"
375
+ if File.exists?( @remote_init_file )
376
+ $stderr.puts "\nWARNING!!!: #{@remote_init_file} exits and this test needs to create a new file here. Make sure there is nothing inportant in that file and remove it before running this test\n\n"
377
+ @protect_init_file = true
378
+ exit
379
+ end
380
+ # copy the hydra_worker_init to the correct location
381
+ FileUtils.cp(hydra_worker_init_file, remote_dir_path)
382
+ end
383
+ end
@@ -0,0 +1,31 @@
1
+ require File.join(File.dirname(__FILE__), 'test_helper')
2
+
3
+ class MessageTest < Test::Unit::TestCase
4
+ class MyMessage < Hydra::Message
5
+ attr_accessor :my_var
6
+ def serialize
7
+ super(:my_var => @my_var)
8
+ end
9
+ end
10
+
11
+ context "with a message" do
12
+ setup do
13
+ @m = MyMessage.new(:my_var => 'my value')
14
+ end
15
+ should "set values" do
16
+ assert_equal 'my value', @m.my_var
17
+ end
18
+ should "serialize" do
19
+ assert_equal(
20
+ {:class=>MyMessage, :my_var=>"my value"},
21
+ eval(@m.serialize)
22
+ )
23
+ end
24
+ should "build from serialization" do
25
+ assert_equal(
26
+ @m.my_var,
27
+ Hydra::Message.build(eval(@m.serialize)).my_var
28
+ )
29
+ end
30
+ end
31
+ end
data/test/pipe_test.rb ADDED
@@ -0,0 +1,38 @@
1
+ require File.join(File.dirname(__FILE__), 'test_helper')
2
+
3
+ class PipeTest < Test::Unit::TestCase
4
+ context "a pipe" do
5
+ setup do
6
+ @pipe = Hydra::Pipe.new
7
+ end
8
+ teardown do
9
+ @pipe.close
10
+ end
11
+ should "be able to write messages" do
12
+ child = Process.fork do
13
+ @pipe.identify_as_child
14
+ assert_equal "Test Message", @pipe.gets.text
15
+ @pipe.write Hydra::Messages::TestMessage.new(:text => "Message Received")
16
+ @pipe.write Hydra::Messages::TestMessage.new(:text => "Second Message")
17
+ end
18
+ @pipe.identify_as_parent
19
+ @pipe.write Hydra::Messages::TestMessage.new(:text => "Test Message")
20
+ assert_equal "Message Received", @pipe.gets.text
21
+ assert_equal "Second Message", @pipe.gets.text
22
+ Process.wait(child) #ensure it quits, so there is nothing to write to
23
+ assert_raise IOError do
24
+ @pipe.write Hydra::Messages::TestMessage.new(:text => "anyone there?")
25
+ end
26
+ end
27
+ should "not allow writing if unidentified" do
28
+ assert_raise IOError do
29
+ @pipe.write Hydra::Messages::TestMessage.new(:text => "Test Message")
30
+ end
31
+ end
32
+ should "not allow reading if unidentified" do
33
+ assert_raise IOError do
34
+ @pipe.gets
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,31 @@
1
+ require File.join(File.dirname(__FILE__), 'test_helper')
2
+ require 'yaml'
3
+ require 'hydra/proxy_config'
4
+
5
+ class ProxyConfigTest < Test::Unit::TestCase
6
+
7
+ context "proxy config" do
8
+ should "fetch and parse yml from a file source" do
9
+ assert_equal YAML::load_file("test/fixtures/config.yml"), Hydra::ProxyConfig.load(File.read("test/fixtures/proxy_config.yml"))
10
+ end
11
+ should "fetch and parse yml from a http source" do
12
+ class Net::HTTP
13
+ def self.get(path)
14
+ return File.new("test/fixtures/config.yml", "r").read
15
+ end
16
+ end
17
+ assert_equal YAML::load_file("test/fixtures/config.yml"), Hydra::ProxyConfig.load(File.read("test/fixtures/proxy_config_http.yml"))
18
+ end
19
+ should "raise exception when type is unknown" do
20
+ assert_raise Hydra::UnknownProxyType do
21
+ Hydra::ProxyConfig.load(File.read("test/fixtures/bad_proxy_config.yml"))
22
+ end
23
+ end
24
+ end
25
+
26
+ context "normal hydra config" do
27
+ should "return unserialized configuration" do
28
+ assert_equal YAML::load_file("test/fixtures/config.yml"), Hydra::ProxyConfig.load(File.read("test/fixtures/config.yml"))
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,196 @@
1
+ require File.join(File.dirname(__FILE__), 'test_helper')
2
+ require File.join(File.dirname(__FILE__), 'fixtures', 'runner_listeners')
3
+
4
+ class RunnerTest < Test::Unit::TestCase
5
+ context "with a file to test and a destination to verify" do
6
+ setup do
7
+ sleep(0.2)
8
+ FileUtils.rm_f(target_file)
9
+ FileUtils.rm_f(alternate_target_file)
10
+ end
11
+
12
+ teardown do
13
+ FileUtils.rm_f(target_file)
14
+ FileUtils.rm_f(alternate_target_file)
15
+ end
16
+
17
+
18
+ should "run a test in the foreground" do
19
+ # flip it around to the parent is in the fork, this gives
20
+ # us more direct control over the runner and proper test
21
+ # coverage output
22
+ pipe = Hydra::Pipe.new
23
+ parent = Process.fork do
24
+ request_a_file_and_verify_completion(pipe, test_file)
25
+ end
26
+ run_the_runner(pipe)
27
+ Process.wait(parent)
28
+ end
29
+
30
+ # this flips the above test, so that the main process runs a bit of the parent
31
+ # code, but only with minimal assertion
32
+ should "run a test in the background" do
33
+ pipe = Hydra::Pipe.new
34
+ child = Process.fork do
35
+ run_the_runner(pipe)
36
+ end
37
+ request_a_file_and_verify_completion(pipe, test_file)
38
+ Process.wait(child)
39
+ end
40
+
41
+ should "run a js lint file and find errors" do
42
+ runner = Hydra::Runner.new(:io => File.new('/dev/null', 'w'))
43
+ results = runner.run_file(javascript_file)
44
+ assert results =~ /Missing semicolon/, results
45
+ end
46
+
47
+ should "run a json data file and find errors" do
48
+ runner = Hydra::Runner.new(:io => File.new('/dev/null', 'w'))
49
+ results = runner.run_file(json_file)
50
+ assert results =~ /trailing comma/, results
51
+ end
52
+
53
+ should "run two rspec tests" do
54
+ runner = Hydra::Runner.new(:io => File.new('/dev/null', 'w'))
55
+ runner.run_file(rspec_file)
56
+ assert File.exists?(target_file)
57
+ assert_equal "HYDRA", File.read(target_file)
58
+
59
+ FileUtils.rm_f(target_file)
60
+
61
+ runner.run_file(alternate_rspec_file)
62
+ assert File.exists?(alternate_target_file)
63
+ assert_equal "HYDRA", File.read(alternate_target_file)
64
+ assert !File.exists?(target_file), "Tests are double running!"
65
+ end
66
+
67
+ should "run rspec tests with pending examples" do
68
+ runner = Hydra::Runner.new(:io => File.new('/dev/null', 'w'))
69
+ assert File.exists?(rspec_file_with_pending)
70
+
71
+ runner.run_file(rspec_file_with_pending)
72
+
73
+ assert File.exists?(target_file)
74
+ assert_equal "HYDRA", File.read(target_file)
75
+
76
+ FileUtils.rm_f(target_file)
77
+ end
78
+
79
+ should "run two cucumber tests" do
80
+ # because of all the crap cucumber pulls in
81
+ # we run this in a fork to not contaminate
82
+ # the main test environment
83
+ capture_stderr do # redirect stderr
84
+ pid = Process.fork do
85
+ runner = Hydra::Runner.new(:io => File.new('/dev/null', 'w'))
86
+ runner.run_file(cucumber_feature_file)
87
+ assert File.exists?(target_file)
88
+ assert_equal "HYDRA", File.read(target_file)
89
+
90
+ FileUtils.rm_f(target_file)
91
+
92
+ runner.run_file(alternate_cucumber_feature_file)
93
+ assert File.exists?(alternate_target_file)
94
+ assert_equal "HYDRA", File.read(alternate_target_file)
95
+ assert !File.exists?(target_file)
96
+ end
97
+ Process.wait pid
98
+ end
99
+ end
100
+
101
+ should "be able to run a runner over ssh" do
102
+ ssh = Hydra::SSH.new(
103
+ 'localhost',
104
+ File.expand_path(File.join(File.dirname(__FILE__), '..', 'lib')),
105
+ "ruby -e \"require 'rubygems'; require 'hydra'; Hydra::Runner.new(:io => Hydra::Stdio.new, :verbose => true);\""
106
+ )
107
+ assert ssh.gets.is_a?(Hydra::Messages::Runner::RequestFile)
108
+ ssh.write(Hydra::Messages::Worker::RunFile.new(:file => test_file))
109
+
110
+ # grab its response. This makes us wait for it to finish
111
+ echo = ssh.gets # get the ssh echo
112
+ response = ssh.gets
113
+
114
+ assert_equal Hydra::Messages::Runner::Results, response.class
115
+
116
+ # tell it to shut down
117
+ ssh.write(Hydra::Messages::Worker::Shutdown.new)
118
+
119
+ ssh.close
120
+
121
+ # ensure it ran
122
+ assert File.exists?(target_file)
123
+ assert_equal "HYDRA", File.read(target_file)
124
+ end
125
+
126
+ context "using runner events" do
127
+ context "on successful termination" do
128
+ setup do
129
+ @pipe = Hydra::Pipe.new
130
+ @parent = Process.fork do
131
+ request_a_file_and_verify_completion(@pipe, test_file)
132
+ end
133
+ end
134
+
135
+ should "fire runner_begin event" do
136
+ run_the_runner(@pipe, [HydraExtension::RunnerListener::RunnerBeginTest.new] )
137
+ Process.wait(@parent)
138
+
139
+ # ensure runner_begin was fired
140
+ assert_file_exists alternate_target_file
141
+ end
142
+
143
+ should "fire runner_end event" do
144
+ run_the_runner(@pipe, [HydraExtension::RunnerListener::RunnerEndTest.new] )
145
+ Process.wait(@parent)
146
+
147
+ assert_file_exists alternate_target_file
148
+ end
149
+ end
150
+
151
+ should "fire runner_end event after losing communication with worker" do
152
+ pipe = Hydra::Pipe.new
153
+ parent = Process.fork do
154
+ pipe.identify_as_parent
155
+
156
+ # grab its response.
157
+ response = pipe.gets
158
+ pipe.close #this will be detected by the runner and it should call runner_end
159
+ end
160
+
161
+ run_the_runner(pipe, [HydraExtension::RunnerListener::RunnerEndTest.new] )
162
+ Process.wait(parent)
163
+
164
+ # ensure runner_end was fired
165
+ assert File.exists?( alternate_target_file )
166
+ end
167
+ end
168
+ end
169
+
170
+ module RunnerTestHelper
171
+ def request_a_file_and_verify_completion(pipe, file)
172
+ pipe.identify_as_parent
173
+
174
+ # make sure it asks for a file, then give it one
175
+ assert pipe.gets.is_a?(Hydra::Messages::Runner::RequestFile)
176
+ pipe.write(Hydra::Messages::Worker::RunFile.new(:file => file))
177
+
178
+ # grab its response. This makes us wait for it to finish
179
+ response = pipe.gets
180
+
181
+ # tell it to shut down
182
+ pipe.write(Hydra::Messages::Worker::Shutdown.new)
183
+
184
+ # ensure it ran
185
+ assert File.exists?(target_file)
186
+ assert_equal "HYDRA", File.read(target_file)
187
+ end
188
+
189
+ def run_the_runner(pipe, listeners = [])
190
+ pipe.identify_as_child
191
+ Hydra::Runner.new( :io => pipe, :runner_listeners => listeners )
192
+ end
193
+ end
194
+ include RunnerTestHelper
195
+ end
196
+