hydra 0.4.1 → 0.5.0

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.
data/TODO CHANGED
@@ -1,54 +1,16 @@
1
- # runners = []
2
- # cores.each do |c|
3
- # in = pipes[c][0]
4
- # out = pipes[c][1]
5
- # runners << TestRunner.new(in, out)
6
- # end
7
- #
8
- # files = [ ... ]
9
- # results = []
10
- #
11
- # runners.each do |r|
12
- # Thread.new do
13
- # while !files.empty?
14
- # results << r.run_file(files.pop)
15
- # end
16
- # r.shutdown
17
- # end
18
- # end
19
- #
20
- # puts results.join("\n")
21
- #
1
+ IO selection configuration for master
2
+ - allow pipe setup
3
+ - allow ssh setup
22
4
 
5
+ YML configuration
6
+
7
+ v0.6.0
8
+
9
+ multitest backwards compatible
10
+
11
+ v0.7.0
12
+
13
+ ???
14
+
15
+ v1.0.0
23
16
 
24
- # Master
25
- # boot up workers
26
- # listen for worker messages
27
- # add worker messages to message queue
28
- # process message queue
29
- # "reply" to a message allows sending a message back down to worker
30
- #
31
- # When worker asks for file but no files left, send shutdown message to worker
32
- # when worker connection breaks, end thread
33
- # wait on all threads
34
- # when all threads are done, all workers must be done
35
- #
36
- #
37
- # Worker
38
- # boot up runners
39
- # listen for runner messages
40
- # add runner messages to message queue
41
- # process message queue
42
- # "reply" to a message allows sending message back down to runner
43
- #
44
- # when a runner asks for file but master responds with shutdown, mark self
45
- # as terminated, shut down runners. Any runner that asks for a file is
46
- # auto-terminated
47
- # wait for runner threads to finish
48
- # then exit, breaking master connection
49
- #
50
- # Runner
51
- # when booted, ask for a file
52
- # then process messages on the queue
53
- # when it's a file, run it and send a results message
54
- # when it's a shutdown, break main loop
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.4.1
1
+ 0.5.0
@@ -5,11 +5,11 @@
5
5
 
6
6
  Gem::Specification.new do |s|
7
7
  s.name = %q{hydra}
8
- s.version = "0.4.1"
8
+ s.version = "0.5.0"
9
9
 
10
10
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
11
  s.authors = ["Nick Gauthier"]
12
- s.date = %q{2010-01-28}
12
+ s.date = %q{2010-01-29}
13
13
  s.description = %q{Spread your tests over multiple machines to test your code faster.}
14
14
  s.email = %q{nick@smartlogicsolutions.com}
15
15
  s.extra_rdoc_files = [
@@ -27,7 +27,9 @@ Gem::Specification.new do |s|
27
27
  "VERSION",
28
28
  "hydra.gemspec",
29
29
  "lib/hydra.rb",
30
+ "lib/hydra/master.rb",
30
31
  "lib/hydra/message.rb",
32
+ "lib/hydra/message/master_messages.rb",
31
33
  "lib/hydra/message/runner_messages.rb",
32
34
  "lib/hydra/message/worker_messages.rb",
33
35
  "lib/hydra/messaging_io.rb",
@@ -38,6 +40,7 @@ Gem::Specification.new do |s|
38
40
  "test/fixtures/assert_true.rb",
39
41
  "test/fixtures/echo_the_dolphin.rb",
40
42
  "test/fixtures/write_file.rb",
43
+ "test/master_test.rb",
41
44
  "test/message_test.rb",
42
45
  "test/pipe_test.rb",
43
46
  "test/runner_test.rb",
@@ -57,6 +60,7 @@ Gem::Specification.new do |s|
57
60
  "test/fixtures/write_file.rb",
58
61
  "test/fixtures/assert_true.rb",
59
62
  "test/fixtures/echo_the_dolphin.rb",
63
+ "test/master_test.rb",
60
64
  "test/worker_test.rb",
61
65
  "test/runner_test.rb",
62
66
  "test/pipe_test.rb"
@@ -3,4 +3,5 @@ require 'hydra/ssh'
3
3
  require 'hydra/message'
4
4
  require 'hydra/runner'
5
5
  require 'hydra/worker'
6
+ require 'hydra/master'
6
7
 
@@ -0,0 +1,75 @@
1
+ module Hydra #:nodoc:
2
+ # Hydra class responsible for delegate work down to workers.
3
+ #
4
+ # The Master is run once for any given testing session.
5
+ class Master
6
+ include Hydra::Messages::Master
7
+ # Create a new Master
8
+ #
9
+ # Options:
10
+ # * :files
11
+ # * An array of test files to be run. These should be relative paths from
12
+ # the root of the project, since they may be run on different machines
13
+ # which may have different paths.
14
+ # * :workers
15
+ # * An array of hashes. Each hash should be the configuration options
16
+ # for a worker.
17
+ def initialize(opts = { })
18
+ @files = opts.fetch(:files) { [] }
19
+ @workers = []
20
+ @listeners = []
21
+ boot_workers(opts.fetch(:workers) { [ {:runners => 1} ] } )
22
+ process_messages
23
+ end
24
+
25
+ # Message handling
26
+
27
+ # Send a file down to a worker. If there are no more files, this will shut the
28
+ # worker down.
29
+ def send_file(worker)
30
+ f = @files.pop
31
+ if f
32
+ worker[:io].write(RunFile.new(:file => f))
33
+ else
34
+ worker[:io].write(Shutdown.new)
35
+ Thread.exit
36
+ end
37
+ end
38
+
39
+ private
40
+
41
+ def boot_workers(workers)
42
+ workers.each do |worker|
43
+ pipe = Hydra::Pipe.new
44
+ child = Process.fork do
45
+ pipe.identify_as_child
46
+ Hydra::Worker.new(:io => pipe, :runners => worker[:runners])
47
+ end
48
+ pipe.identify_as_parent
49
+ @workers << { :pid => child, :io => pipe, :idle => false }
50
+ end
51
+ end
52
+
53
+ def process_messages
54
+ Thread.abort_on_exception = true
55
+
56
+ @workers.each do |worker|
57
+ @listeners << Thread.new do
58
+ while true
59
+ begin
60
+ message = worker[:io].gets
61
+ message.handle(self, worker) if message
62
+ rescue IOError => ex
63
+ $stderr.write "Master lost Worker [#{worker.inspect}]\n"
64
+ worker[:io].close
65
+ @workers.delete(worker)
66
+ Thread.exit
67
+ end
68
+ end
69
+ end
70
+ end
71
+
72
+ @listeners.each{|l| l.join}
73
+ end
74
+ end
75
+ end
@@ -43,4 +43,5 @@ end
43
43
 
44
44
  require 'hydra/message/runner_messages'
45
45
  require 'hydra/message/worker_messages'
46
+ require 'hydra/message/master_messages'
46
47
 
@@ -0,0 +1,19 @@
1
+ module Hydra #:nodoc:
2
+ module Messages #:nodoc:
3
+ module Master #:nodoc:
4
+ # Message telling a worker to delegate a file to a runner
5
+ class RunFile < Hydra::Messages::Worker::RunFile
6
+ def handle(worker) #:nodoc:
7
+ worker.delegate_file(self)
8
+ end
9
+ end
10
+
11
+ # Message telling the worker to shut down.
12
+ class Shutdown < Hydra::Messages::Worker::Shutdown
13
+ def handle(worker) #:nodoc:
14
+ worker.shutdown
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end
@@ -8,20 +8,11 @@ module Hydra #:nodoc:
8
8
  end
9
9
  end
10
10
 
11
- # Message telling the Runner to run a file
12
- class RunFile < Hydra::Message
13
- attr_accessor :file
14
- def serialize #:nodoc:
15
- super(:file => @file)
16
- end
17
- def handle(runner) #:nodoc:
18
- runner.run_file(@file)
19
- end
20
- end
21
-
22
11
  # Message for the Runner to respond with its results
23
12
  class Results < Hydra::Message
13
+ # The output from running the test
24
14
  attr_accessor :output
15
+ # The file that was run
25
16
  attr_accessor :file
26
17
  def serialize #:nodoc:
27
18
  super(:output => @output, :file => @file)
@@ -31,10 +22,11 @@ module Hydra #:nodoc:
31
22
  end
32
23
  end
33
24
 
34
- # Message to tell the Runner to shut down
35
- class Shutdown < Hydra::Message
36
- def handle(runner) #:nodoc:
37
- runner.stop
25
+ # Message a runner sends to a worker to verify the connection
26
+ class Ping < Hydra::Message
27
+ def handle(worker, runner) #:nodoc:
28
+ # We don't do anything to handle a ping. It's just to test
29
+ # the connectivity of the IO
38
30
  end
39
31
  end
40
32
  end
@@ -1,25 +1,45 @@
1
1
  module Hydra #:nodoc:
2
2
  module Messages #:nodoc:
3
3
  module Worker #:nodoc:
4
- # Message indicating that a work needs a file to delegate to a runner
4
+ # Message indicating that a worker needs a file to delegate to a runner
5
5
  class RequestFile < Hydra::Message
6
+ def handle(master, worker) #:nodoc:
7
+ master.send_file(worker)
8
+ end
9
+ end
10
+
11
+ # Message telling the Runner to run a file
12
+ class RunFile < Hydra::Message
13
+ # The file that should be run
14
+ attr_accessor :file
15
+ def serialize #:nodoc:
16
+ super(:file => @file)
17
+ end
18
+ def handle(runner) #:nodoc:
19
+ runner.run_file(@file)
20
+ end
6
21
  end
7
22
 
8
- # Message telling a worker to delegate a file to a runner
9
- class RunFile < Hydra::Messages::Runner::RunFile
10
- def handle(worker)
11
- worker.delegate_file(self)
23
+ # Message to tell the Runner to shut down
24
+ class Shutdown < Hydra::Message
25
+ def handle(runner) #:nodoc:
26
+ runner.stop
12
27
  end
13
28
  end
14
29
 
15
30
  # Message relaying the results of a worker up to the master
16
31
  class Results < Hydra::Messages::Runner::Results
32
+ def handle(master, worker) #:nodoc:
33
+ $stdout.write output
34
+ master.send_file(worker)
35
+ end
17
36
  end
18
37
 
19
- # Message telling the worker to shut down.
20
- class Shutdown < Hydra::Messages::Runner::Shutdown
21
- def handle(worker)
22
- worker.shutdown
38
+ # Message a worker sends to a master to verify the connection
39
+ class Ping < Hydra::Message
40
+ def handle(master, worker) #:nodoc:
41
+ # We don't do anything to handle a ping. It's just to test
42
+ # the connectivity of the IO
23
43
  end
24
44
  end
25
45
  end
@@ -20,11 +20,9 @@ module Hydra #:nodoc:
20
20
  def write(message)
21
21
  raise IOError unless @writer
22
22
  raise UnprocessableMessage unless message.is_a?(Hydra::Message)
23
- begin
24
- @writer.write(message.serialize+"\n")
25
- rescue Errno::EPIPE
26
- raise IOError
27
- end
23
+ @writer.write(message.serialize+"\n")
24
+ rescue Errno::EPIPE
25
+ raise IOError
28
26
  end
29
27
 
30
28
  # Closes the IO object.
@@ -37,6 +35,7 @@ module Hydra #:nodoc:
37
35
  # For example, if you tried to write a string, it would fail,
38
36
  # because the string is not a message.
39
37
  class UnprocessableMessage < RuntimeError
38
+ # Custom error message
40
39
  attr_accessor :message
41
40
  end
42
41
  end
@@ -51,5 +51,11 @@ module Hydra #:nodoc:
51
51
  @reader = @parent_read
52
52
  @writer = @parent_write
53
53
  end
54
+
55
+ # Output pipe nicely
56
+ def inspect
57
+ "#<#{self.class} @reader=#{@reader.to_s}, @writer=#{@writer.to_s}>"
58
+ end
59
+
54
60
  end
55
61
  end
@@ -1,32 +1,94 @@
1
1
  module Hydra #:nodoc:
2
- # Hydra class responsible for running test files
2
+ # Hydra class responsible for running test files.
3
+ #
4
+ # The Runner is never run directly by a user. Runners are created by a
5
+ # Worker to run test files.
6
+ #
7
+ # The general convention is to have one Runner for each logical processor
8
+ # of a machine.
3
9
  class Runner
10
+ include Hydra::Messages::Runner
4
11
  # Boot up a runner. It takes an IO object (generally a pipe from its
5
12
  # parent) to send it messages on which files to execute.
6
- def initialize(io)
7
- @io = io
8
- @io.write Hydra::Messages::Runner::RequestFile.new
9
- process_messages
10
- end
13
+ def initialize(opts = {})
14
+ @io = opts.fetch(:io) { raise "No IO Object" }
15
+ @verbose = opts.fetch(:verbose) { false }
11
16
 
12
- # The runner will continually read messages and handle them.
13
- def process_messages
14
- @running = true
15
- while @running
16
- message = @io.gets
17
- message.handle(self) if message
18
- end
17
+ Test::Unit.run = true
18
+
19
+ @io.write RequestFile.new
20
+ process_messages
19
21
  end
20
22
 
21
23
  # Run a test file and report the results
22
24
  def run_file(file)
23
- `ruby #{file}`
24
- @io.write Hydra::Messages::Runner::Results.new(:output => "Finished", :file => file)
25
+ require file
26
+ output = []
27
+ @result = Test::Unit::TestResult.new
28
+ @result.add_listener(Test::Unit::TestResult::FAULT) do |value|
29
+ output << value
30
+ end
31
+
32
+ klasses = Runner.find_classes_in_file(file)
33
+ begin
34
+ klasses.each{|klass| klass.suite.run(@result){|status, name| ;}}
35
+ rescue => ex
36
+ output << ex.to_s
37
+ end
38
+
39
+ output << '.' if output.empty?
40
+
41
+ @io.write Results.new(:output => output.join("\n"), :file => file)
25
42
  end
26
43
 
27
44
  # Stop running
28
45
  def stop
29
46
  @running = false
30
47
  end
48
+
49
+ private
50
+
51
+ # The runner will continually read messages and handle them.
52
+ def process_messages
53
+ $stdout.write "RUNNER| Processing Messages\n" if @verbose
54
+ @running = true
55
+ while @running
56
+ begin
57
+ message = @io.gets
58
+ if message
59
+ $stdout.write "RUNNER| Received message from worker\n" if @verbose
60
+ $stdout.write " | #{message.inspect}\n" if @verbose
61
+ message.handle(self)
62
+ else
63
+ @io.write Ping.new
64
+ end
65
+ rescue IOError => ex
66
+ $stderr.write "Runner lost Worker\n" if @verbose
67
+ @running = false
68
+ end
69
+ end
70
+ end
71
+
72
+ def self.find_classes_in_file(f)
73
+ code = ""
74
+ File.open(f) {|buffer| code = buffer.read}
75
+ matches = code.scan(/class\s+([\S]+)/)
76
+ klasses = matches.collect do |c|
77
+ begin
78
+ if c.first.respond_to? :constantize
79
+ c.first.constantize
80
+ else
81
+ eval(c.first)
82
+ end
83
+ rescue NameError
84
+ # $stderr.write "Could not load [#{c.first}] from [#{f}]\n"
85
+ nil
86
+ rescue SyntaxError
87
+ # $stderr.write "Could not load [#{c.first}] from [#{f}]\n"
88
+ nil
89
+ end
90
+ end
91
+ return klasses.select{|k| k.respond_to? 'suite'}
92
+ end
31
93
  end
32
94
  end
@@ -1,15 +1,25 @@
1
1
  module Hydra #:nodoc:
2
2
  # Hydra class responsible to dispatching runners and communicating with the master.
3
+ #
4
+ # The Worker is never run directly by a user. Workers are created by a
5
+ # Master to delegate to Runners.
6
+ #
7
+ # The general convention is to have one Worker per machine on a distributed
8
+ # network.
3
9
  class Worker
10
+ include Hydra::Messages::Worker
4
11
  # Create a new worker.
5
12
  # * io: The IO object to use to communicate with the master
6
13
  # * num_runners: The number of runners to launch
7
- def initialize(io, num_runners)
8
- @io = io
14
+ def initialize(opts = {})
15
+ @verbose = opts.fetch(:verbose) { false }
16
+ @io = opts.fetch(:io) { raise "No IO Object" }
9
17
  @runners = []
10
18
  @listeners = []
11
- boot_runners(num_runners)
19
+
20
+ boot_runners(opts.fetch(:runners) { 1 })
12
21
  process_messages
22
+
13
23
  @runners.each{|r| Process.wait r[:pid] }
14
24
  end
15
25
 
@@ -19,93 +29,114 @@ module Hydra #:nodoc:
19
29
  # When a runner wants a file, it hits this method with a message.
20
30
  # Then the worker bubbles the file request up to the master.
21
31
  def request_file(message, runner)
22
- @io.write(Hydra::Messages::Worker::RequestFile.new)
32
+ @io.write(RequestFile.new)
23
33
  runner[:idle] = true
24
34
  end
25
35
 
26
36
  # When the master sends a file down to the worker, it hits this
27
37
  # method. Then the worker delegates the file down to a runner.
28
38
  def delegate_file(message)
29
- r = idle_runner
30
- r[:idle] = false
31
- r[:io].write(Hydra::Messages::Runner::RunFile.new(eval(message.serialize)))
39
+ runner = idle_runner
40
+ runner[:idle] = false
41
+ runner[:io].write(RunFile.new(eval(message.serialize)))
32
42
  end
33
43
 
34
44
  # When a runner finishes, it sends the results up to the worker. Then the
35
45
  # worker sends the results up to the master.
36
- # TODO: when we relay results, it should trigger a RunFile or Shutdown from
37
- # the master implicitly
38
46
  def relay_results(message, runner)
39
47
  runner[:idle] = true
40
- @io.write(Hydra::Messages::Worker::Results.new(eval(message.serialize)))
48
+ @io.write(Results.new(eval(message.serialize)))
41
49
  end
42
50
 
43
51
  # When a master issues a shutdown order, it hits this method, which causes
44
52
  # the worker to send shutdown messages to its runners.
45
- # TODO: implement a ShutdownComplete message, so that we can kill the
46
- # processes if necessary.
47
53
  def shutdown
48
54
  @running = false
55
+ $stdout.write "WORKER| Notifying #{@runners.size} Runners of Shutdown\n" if @verbose
49
56
  @runners.each do |r|
50
- r[:io].write(Hydra::Messages::Runner::Shutdown.new)
57
+ $stdout.write "WORKER| Sending Shutdown to Runner\n" if @verbose
58
+ $stdout.write " | #{r.inspect}\n" if @verbose
59
+ r[:io].write(Shutdown.new)
51
60
  end
61
+ Thread.exit
52
62
  end
53
63
 
54
64
  private
55
65
 
56
66
  def boot_runners(num_runners) #:nodoc:
67
+ $stdout.write "WORKER| Booting #{num_runners} Runners\n" if @verbose
57
68
  num_runners.times do
58
69
  pipe = Hydra::Pipe.new
59
70
  child = Process.fork do
60
71
  pipe.identify_as_child
61
- Hydra::Runner.new(pipe)
72
+ Hydra::Runner.new(:io => pipe, :verbose => @verbose)
62
73
  end
63
74
  pipe.identify_as_parent
64
75
  @runners << { :pid => child, :io => pipe, :idle => false }
65
76
  end
77
+ $stdout.write "WORKER| #{@runners.size} Runners booted\n" if @verbose
66
78
  end
67
79
 
68
80
  # Continuously process messages
69
81
  def process_messages #:nodoc:
82
+ $stdout.write "WORKER| Processing Messages\n" if @verbose
70
83
  @running = true
71
84
 
72
- # Abort the worker if one of the runners has an exception
73
- # TODO: catch this exception, return a dying message to the master
74
- # then shutdown
75
85
  Thread.abort_on_exception = true
76
86
 
77
- # Worker listens and handles messages
87
+ process_messages_from_master
88
+ process_messages_from_runners
89
+
90
+ @listeners.each{|l| l.join }
91
+ @io.close
92
+ $stdout.write "WORKER| Done processing messages\n" if @verbose
93
+ end
94
+
95
+ def process_messages_from_master
78
96
  @listeners << Thread.new do
79
97
  while @running
80
- message = @io.gets
81
- message.handle(self) if message
98
+ begin
99
+ message = @io.gets
100
+ if message
101
+ $stdout.write "WORKER| Received Message from Master\n" if @verbose
102
+ $stdout.write " | #{message.inspect}\n" if @verbose
103
+ message.handle(self)
104
+ else
105
+ @io.write Ping.new
106
+ end
107
+ rescue IOError => ex
108
+ $stderr.write "Worker lost Master\n" if @verbose
109
+ Thread.exit
110
+ end
82
111
  end
83
112
  end
113
+ end
84
114
 
85
- # Runners listen, but when they handle they pass themselves
86
- # so we can reference them when we deal with their messages
115
+ def process_messages_from_runners
87
116
  @runners.each do |r|
88
117
  @listeners << Thread.new do
89
118
  while @running
90
119
  begin
91
120
  message = r[:io].gets
92
- message.handle(self, r) if message
121
+ if message
122
+ $stdout.write "WORKER| Received Message from Runner\n" if @verbose
123
+ $stdout.write " | #{message.inspect}\n" if @verbose
124
+ message.handle(self, r)
125
+ end
93
126
  rescue IOError => ex
94
- # If the other end of the pipe closes
95
- # we will continue, because we're probably
96
- # not @running anymore
127
+ $stderr.write "Worker lost Runner [#{r.inspect}]\n"
128
+ Thread.exit
97
129
  end
98
130
  end
99
131
  end
100
132
  end
101
- @listeners.each{|l| l.join }
102
133
  end
103
134
 
104
135
  # Get the next idle runner
105
136
  def idle_runner #:nodoc:
106
137
  idle_r = nil
107
138
  while idle_r.nil?
108
- idle_r = @runners.detect{|r| r[:idle]}
139
+ idle_r = @runners.detect{|runner| runner[:idle]}
109
140
  sleep(1)
110
141
  end
111
142
  return idle_r
@@ -1,7 +1,7 @@
1
1
  require File.join(File.dirname(__FILE__), '..', 'test_helper')
2
2
 
3
3
  class WriteFileTest < Test::Unit::TestCase
4
- should "write file" do
4
+ def test_write_a_file
5
5
  File.open(File.join(Dir.tmpdir, 'hydra_test.txt'), 'w') do |f|
6
6
  f.write "HYDRA"
7
7
  end
@@ -0,0 +1,21 @@
1
+ require File.join(File.dirname(__FILE__), 'test_helper')
2
+
3
+ class MasterTest < Test::Unit::TestCase
4
+ context "with a file to test and a destination to verify" do
5
+ setup do
6
+ FileUtils.rm_f(target_file)
7
+ end
8
+
9
+ teardown do
10
+ FileUtils.rm_f(target_file)
11
+ end
12
+
13
+ should "run a test" do
14
+ m = Hydra::Master.new({
15
+ :files => Array(test_file)
16
+ })
17
+ assert File.exists?(target_file)
18
+ assert_equal "HYDRA", File.read(target_file)
19
+ end
20
+ end
21
+ end
@@ -1,16 +1,13 @@
1
1
  require File.join(File.dirname(__FILE__), 'test_helper')
2
2
 
3
- TARGET = File.join(Dir.tmpdir, 'hydra_test.txt')
4
- TESTFILE = File.join(File.dirname(__FILE__), 'fixtures', 'write_file.rb')
5
-
6
3
  class RunnerTest < Test::Unit::TestCase
7
4
  context "with a file to test and a destination to verify" do
8
5
  setup do
9
- FileUtils.rm_f(TARGET)
6
+ FileUtils.rm_f(target_file)
10
7
  end
11
8
 
12
9
  teardown do
13
- FileUtils.rm_f(TARGET)
10
+ FileUtils.rm_f(target_file)
14
11
  end
15
12
 
16
13
 
@@ -44,25 +41,22 @@ class RunnerTest < Test::Unit::TestCase
44
41
 
45
42
  # make sure it asks for a file, then give it one
46
43
  assert pipe.gets.is_a?(Hydra::Messages::Runner::RequestFile)
47
- pipe.write(Hydra::Messages::Runner::RunFile.new(:file => TESTFILE))
44
+ pipe.write(Hydra::Messages::Worker::RunFile.new(:file => test_file))
48
45
 
49
46
  # grab its response. This makes us wait for it to finish
50
47
  response = pipe.gets
51
48
 
52
49
  # tell it to shut down
53
- pipe.write(Hydra::Messages::Runner::Shutdown.new)
50
+ pipe.write(Hydra::Messages::Worker::Shutdown.new)
54
51
 
55
52
  # ensure it ran
56
- assert File.exists?(TARGET)
57
- assert_equal "HYDRA", File.read(TARGET)
58
-
59
- pipe.close
53
+ assert File.exists?(target_file)
54
+ assert_equal "HYDRA", File.read(target_file)
60
55
  end
61
56
 
62
57
  def run_the_runner(pipe)
63
58
  pipe.identify_as_child
64
- Hydra::Runner.new(pipe)
65
- pipe.close
59
+ Hydra::Runner.new({:io => pipe})
66
60
  end
67
61
  end
68
62
  include RunnerTestHelper
@@ -8,6 +8,13 @@ $LOAD_PATH.unshift(File.dirname(__FILE__))
8
8
  require 'hydra'
9
9
 
10
10
  class Test::Unit::TestCase
11
+ def target_file
12
+ File.join(Dir.tmpdir, 'hydra_test.txt')
13
+ end
14
+
15
+ def test_file
16
+ File.join(File.dirname(__FILE__), 'fixtures', 'write_file.rb')
17
+ end
11
18
  end
12
19
 
13
20
  module Hydra #:nodoc:
@@ -1,32 +1,28 @@
1
1
  require File.join(File.dirname(__FILE__), 'test_helper')
2
2
 
3
- TARGET = File.join(Dir.tmpdir, 'hydra_test.txt')
4
- TESTFILE = File.join(File.dirname(__FILE__), 'fixtures', 'write_file.rb')
5
-
6
3
  class WorkerTest < Test::Unit::TestCase
7
4
  context "with a file to test and a destination to verify" do
8
5
  setup do
9
- FileUtils.rm_f(TARGET)
6
+ FileUtils.rm_f(target_file)
10
7
  end
11
8
 
12
9
  teardown do
13
- FileUtils.rm_f(TARGET)
10
+ FileUtils.rm_f(target_file)
14
11
  end
15
12
 
16
13
  # run the worker in the foreground and the requests in the background
17
- should "run a test" do
14
+ should "run a test in the foreground" do
18
15
  num_runners = 4
19
16
  pipe = Hydra::Pipe.new
20
17
  child = Process.fork do
21
18
  request_a_file_and_verify_completion(pipe, num_runners)
22
- pipe.close
23
19
  end
24
20
  run_the_worker(pipe, num_runners)
25
21
  Process.wait(child)
26
22
  end
27
23
 
28
24
  # inverse of the above test to run the worker in the background
29
- should "be able to tell a worker to run a test" do
25
+ should "run a test in the background" do
30
26
  num_runners = 4
31
27
  pipe = Hydra::Pipe.new
32
28
  child = Process.fork do
@@ -34,15 +30,13 @@ class WorkerTest < Test::Unit::TestCase
34
30
  end
35
31
  request_a_file_and_verify_completion(pipe, num_runners)
36
32
  Process.wait(child)
37
- pipe.close
38
33
  end
39
34
  end
40
35
 
41
36
  module WorkerTestHelper
42
37
  def run_the_worker(pipe, num_runners)
43
38
  pipe.identify_as_child
44
- Hydra::Worker.new(pipe, num_runners)
45
- pipe.close
39
+ Hydra::Worker.new({:io => pipe, :runners => num_runners})
46
40
  end
47
41
 
48
42
  def request_a_file_and_verify_completion(pipe, num_runners)
@@ -50,15 +44,14 @@ class WorkerTest < Test::Unit::TestCase
50
44
  num_runners.times do
51
45
  assert pipe.gets.is_a?(Hydra::Messages::Worker::RequestFile)
52
46
  end
53
- pipe.write(Hydra::Messages::Worker::RunFile.new(:file => TESTFILE))
47
+ pipe.write(Hydra::Messages::Master::RunFile.new(:file => test_file))
54
48
 
55
- response = pipe.gets
56
- assert response.is_a?(Hydra::Messages::Worker::Results)
49
+ assert pipe.gets.is_a?(Hydra::Messages::Worker::Results)
57
50
 
58
- pipe.write(Hydra::Messages::Worker::Shutdown.new)
51
+ pipe.write(Hydra::Messages::Master::Shutdown.new)
59
52
 
60
- assert File.exists?(TARGET)
61
- assert_equal "HYDRA", File.read(TARGET)
53
+ assert File.exists?(target_file)
54
+ assert_equal "HYDRA", File.read(target_file)
62
55
  end
63
56
  end
64
57
  include WorkerTestHelper
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: hydra
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.1
4
+ version: 0.5.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Nick Gauthier
@@ -9,7 +9,7 @@ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
11
 
12
- date: 2010-01-28 00:00:00 -05:00
12
+ date: 2010-01-29 00:00:00 -05:00
13
13
  default_executable:
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
@@ -52,7 +52,9 @@ files:
52
52
  - VERSION
53
53
  - hydra.gemspec
54
54
  - lib/hydra.rb
55
+ - lib/hydra/master.rb
55
56
  - lib/hydra/message.rb
57
+ - lib/hydra/message/master_messages.rb
56
58
  - lib/hydra/message/runner_messages.rb
57
59
  - lib/hydra/message/worker_messages.rb
58
60
  - lib/hydra/messaging_io.rb
@@ -63,6 +65,7 @@ files:
63
65
  - test/fixtures/assert_true.rb
64
66
  - test/fixtures/echo_the_dolphin.rb
65
67
  - test/fixtures/write_file.rb
68
+ - test/master_test.rb
66
69
  - test/message_test.rb
67
70
  - test/pipe_test.rb
68
71
  - test/runner_test.rb
@@ -104,6 +107,7 @@ test_files:
104
107
  - test/fixtures/write_file.rb
105
108
  - test/fixtures/assert_true.rb
106
109
  - test/fixtures/echo_the_dolphin.rb
110
+ - test/master_test.rb
107
111
  - test/worker_test.rb
108
112
  - test/runner_test.rb
109
113
  - test/pipe_test.rb