hydra 0.2.0 → 0.3.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/VERSION CHANGED
@@ -1 +1 @@
1
- 0.2.0
1
+ 0.3.0
@@ -5,7 +5,7 @@
5
5
 
6
6
  Gem::Specification.new do |s|
7
7
  s.name = %q{hydra}
8
- s.version = "0.2.0"
8
+ s.version = "0.3.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"]
@@ -29,12 +29,15 @@ Gem::Specification.new do |s|
29
29
  "lib/hydra.rb",
30
30
  "lib/hydra/io.rb",
31
31
  "lib/hydra/message.rb",
32
- "lib/hydra/message/runner_requests_file.rb",
32
+ "lib/hydra/message/runner_messages.rb",
33
33
  "lib/hydra/pipe.rb",
34
34
  "lib/hydra/runner.rb",
35
35
  "lib/hydra/ssh.rb",
36
36
  "test/echo_the_dolphin.rb",
37
37
  "test/helper.rb",
38
+ "test/sample_tests/assert_true.rb",
39
+ "test/sample_tests/write_file.rb",
40
+ "test/test_message.rb",
38
41
  "test/test_pipe.rb",
39
42
  "test/test_runner.rb",
40
43
  "test/test_ssh.rb"
@@ -47,8 +50,11 @@ Gem::Specification.new do |s|
47
50
  s.test_files = [
48
51
  "test/test_ssh.rb",
49
52
  "test/helper.rb",
53
+ "test/test_message.rb",
50
54
  "test/test_pipe.rb",
51
55
  "test/test_runner.rb",
56
+ "test/sample_tests/write_file.rb",
57
+ "test/sample_tests/assert_true.rb",
52
58
  "test/echo_the_dolphin.rb"
53
59
  ]
54
60
 
@@ -1,6 +1,12 @@
1
1
  module Hydra #:nodoc:
2
+ # Module that implemets methods that auto-serialize and deserialize messaging
3
+ # objects.
2
4
  module MessagingIO
3
- # Read a line from the input IO object.
5
+ # Read a Message from the input IO object. Automatically build
6
+ # a message from the response and return it.
7
+ #
8
+ # IO.gets
9
+ # => Hydra::Message # or subclass
4
10
  def gets
5
11
  raise IOError unless @reader
6
12
  message = @reader.gets
@@ -8,7 +14,9 @@ module Hydra #:nodoc:
8
14
  return Message.build(eval(message.chomp))
9
15
  end
10
16
 
11
- # Write a line to the output IO object
17
+ # Write a Message to the output IO object. It will automatically
18
+ # serialize a Message object.
19
+ # IO.write Hydra::Message.new
12
20
  def write(message)
13
21
  raise IOError unless @writer
14
22
  raise UnprocessableMessage unless message.is_a?(Hydra::Message)
@@ -19,13 +27,18 @@ module Hydra #:nodoc:
19
27
  end
20
28
  end
21
29
 
30
+ # Closes the IO object.
22
31
  def close
23
32
  @reader.close if @reader
24
33
  @writer.close if @writer
25
34
  end
26
35
 
36
+ # IO will return this error if it cannot process a message.
37
+ # For example, if you tried to write a string, it would fail,
38
+ # because the string is not a message.
27
39
  class UnprocessableMessage < RuntimeError
28
40
  attr_accessor :message
41
+ # Allow a custom message for the exception.
29
42
  def initialize(message = "Message expected")
30
43
  @message = message
31
44
  end
@@ -1,18 +1,44 @@
1
1
  module Hydra #:nodoc:
2
- class Message #:nodoc:
2
+ # Base message object. Used to pass messages with parameters around
3
+ # via IO objects.
4
+ # class MyMessage < Hydra::Message
5
+ # attr_accessor :my_var
6
+ # def serialize
7
+ # super(:my_var => @my_var)
8
+ # end
9
+ # end
10
+ # m = MyMessage.new(:my_var => 'my value')
11
+ # m.my_var
12
+ # => "my value"
13
+ # m.serialize
14
+ # => "{:class=>TestMessage::MyMessage, :my_var=>\"my value\"}"
15
+ # Hydra::Message.build(eval(@m.serialize)).my_var
16
+ # => "my value"
17
+ class Message
18
+ # Create a new message. Opts is a hash where the keys
19
+ # are attributes of the message and the values are
20
+ # set to the attribute.
3
21
  def initialize(opts = {})
4
- opts.each do |k,v|
5
- self.send(k,v)
22
+ opts.each do |variable,value|
23
+ self.send("#{variable}=",value)
6
24
  end
7
25
  end
26
+
27
+ # Build a message from a hash. The hash must contain
28
+ # the :class symbol, which is the class of the message
29
+ # that it will build to.
8
30
  def self.build(hash)
9
31
  hash.delete(:class).new(hash)
10
32
  end
11
33
 
34
+ # Serialize the message for output on an IO channel.
35
+ # This is really just a string representation of a hash
36
+ # with no newlines. It adds in the class automatically
12
37
  def serialize(opts = {})
13
- opts[:class] = self.class
14
- opts.inspect
38
+ opts.merge({:class => self.class}).inspect
15
39
  end
16
40
  end
17
41
  end
18
- require 'hydra/message/runner_requests_file'
42
+
43
+ require 'hydra/message/runner_messages'
44
+
@@ -0,0 +1,36 @@
1
+ module Hydra #:nodoc:
2
+ module Messages #:nodoc:
3
+ module Runner #:nodoc:
4
+ # Message indicating that a Runner needs a file to run
5
+ class RequestFile < Hydra::Message
6
+ end
7
+
8
+ # Message telling the Runner to run a file
9
+ class RunFile < Hydra::Message
10
+ attr_accessor :file
11
+ def serialize #:nodoc:
12
+ super(:file => @file)
13
+ end
14
+ def handle(runner) #:nodoc:
15
+ runner.run_file(@file)
16
+ end
17
+ end
18
+
19
+ # Message for the Runner to respond with its results
20
+ class Results < Hydra::Message
21
+ attr_accessor :output
22
+ attr_accessor :file
23
+ def serialize #:nodoc:
24
+ super(:output => @output, :file => @file)
25
+ end
26
+ end
27
+
28
+ # Message to tell the Runner to shut down
29
+ class Shutdown < Hydra::Message
30
+ def handle(runner) #:nodoc:
31
+ runner.stop
32
+ end
33
+ end
34
+ end
35
+ end
36
+ end
@@ -2,15 +2,19 @@ require 'hydra/io'
2
2
  module Hydra #:nodoc:
3
3
  # Read and write between two processes via pipes. For example:
4
4
  # @pipe = Hydra::Pipe.new
5
- # Process.fork do
5
+ # @child = Process.fork do
6
6
  # @pipe.identify_as_child
7
- # sleep(1)
8
- # puts "A message from my parent:\n#{@pipe.gets}"
7
+ # puts "A message from my parent:\n#{@pipe.gets.text}"
9
8
  # @pipe.close
10
9
  # end
11
10
  # @pipe.identify_as_parent
12
- # @pipe.write "Hello, Child!"
11
+ # @pipe.write Hydra::Messages::TestMessage.new(:text => "Hello!")
13
12
  # @pipe.close
13
+ #
14
+ # Note that the TestMessage class is only available in tests, and
15
+ # not in Hydra by default.
16
+ #
17
+ #
14
18
  # When the process forks, the pipe is copied. When a pipe is
15
19
  # identified as a parent or child, it is choosing which ends
16
20
  # of the pipe to use.
@@ -1,8 +1,32 @@
1
1
  module Hydra #:nodoc:
2
+ # Hydra class responsible for running test files
2
3
  class Runner
4
+ # Boot up a runner. It takes an IO object (generally a pipe from its
5
+ # parent) to send it messages on which files to execute.
3
6
  def initialize(io)
4
7
  @io = io
5
- @io.write Hydra::Messages::RunnerRequestsFile.new
8
+ @io.write Hydra::Messages::Runner::RequestFile.new
9
+ process_messages
10
+ end
11
+
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
19
+ end
20
+
21
+ # Run a test file and report the results
22
+ def run_file(file)
23
+ `ruby #{file}`
24
+ @io.write Hydra::Messages::Runner::Results.new(:output => "Finished", :file => file)
25
+ end
26
+
27
+ # Stop running
28
+ def stop
29
+ @running = false
6
30
  end
7
31
  end
8
32
  end
@@ -2,20 +2,17 @@ require 'open3'
2
2
  require 'hydra/io'
3
3
  module Hydra #:nodoc:
4
4
  # Read and write with an ssh connection. For example:
5
- # @ssh = Hydra::SSH.new('nick@nite')
6
- # @ssh.write("echo hi")
7
- # puts @ssh.gets
8
- # => hi
5
+ # @ssh = Hydra::SSH.new(
6
+ # 'localhost', # connect to this machine
7
+ # '/home/user', # move to the home directory
8
+ # "ruby hydra/test/echo_the_dolphin.rb" # run the echo script
9
+ # )
10
+ # @message = Hydra::Messages::TestMessage.new("Hey there!")
11
+ # @ssh.write @message
12
+ # puts @ssh.gets.text
13
+ # => "Hey there!"
9
14
  #
10
- # You can also use this to launch an interactive process. For
11
- # example:
12
- # @ssh = Hydra::SSH.new('nick@nite')
13
- # @ssh.write('irb')
14
- # @ssh.write("5+3")
15
- # @ssh.gets
16
- # => "5+3\n" # because irb echoes commands
17
- # @ssh.gets
18
- # => "8" # the output from irb
15
+ # Note that what ever process you run should respond with Hydra messages.
19
16
  class SSH
20
17
  include Open3
21
18
  include Hydra::MessagingIO
@@ -1,6 +1,7 @@
1
1
  require 'rubygems'
2
2
  require 'test/unit'
3
3
  require 'shoulda'
4
+ require 'tmpdir'
4
5
 
5
6
  $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
6
7
  $LOAD_PATH.unshift(File.dirname(__FILE__))
@@ -0,0 +1,7 @@
1
+ require File.join(File.dirname(__FILE__), '..', 'helper')
2
+
3
+ class TestAssertTrue < Test::Unit::TestCase
4
+ should "be true" do
5
+ assert true
6
+ end
7
+ end
@@ -0,0 +1,10 @@
1
+ require File.join(File.dirname(__FILE__), '..', 'helper')
2
+
3
+ class TestWriteFile < Test::Unit::TestCase
4
+ should "write file" do
5
+ File.open(File.join(Dir.tmpdir, 'hydra_test.txt'), 'w') do |f|
6
+ f.write "HYDRA"
7
+ end
8
+ end
9
+ end
10
+
@@ -0,0 +1,31 @@
1
+ require File.join(File.dirname(__FILE__), 'helper')
2
+
3
+ class TestMessage < 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=>TestMessage::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
@@ -11,14 +11,32 @@ class TestRunner < Test::Unit::TestCase
11
11
  @pipe.identify_as_parent
12
12
  end
13
13
  teardown do
14
+ @pipe.close
14
15
  Process.wait(@child)
15
16
  end
16
- should "request a file on boot" do
17
- assert @pipe.gets.is_a?(Hydra::Messages::RunnerRequestsFile)
17
+ should "boot and run a file and shut down" do
18
+ assert @pipe.gets.is_a?(Hydra::Messages::Runner::RequestFile)
19
+
20
+ file = File.join(File.dirname(__FILE__), 'sample_tests', 'assert_true.rb')
21
+ @pipe.write(Hydra::Messages::Runner::RunFile.new(:file => file))
22
+ response = @pipe.gets
23
+ assert response.is_a?(Hydra::Messages::Runner::Results)
24
+ assert response.output =~ /Finished/
25
+ assert_equal file, response.file
26
+ @pipe.write(Hydra::Messages::Runner::Shutdown.new)
18
27
  end
19
- should "return a result message after processing a file" do
20
-
28
+
29
+ should "run a test" do
30
+ target = File.join(Dir.tmpdir, 'hydra_test.txt')
31
+ FileUtils.rm_f(target)
32
+ assert !File.exists?(target)
33
+ file = File.join(File.dirname(__FILE__), 'sample_tests', 'write_file.rb')
34
+ assert @pipe.gets.is_a?(Hydra::Messages::Runner::RequestFile)
35
+ @pipe.write(Hydra::Messages::Runner::RunFile.new(:file => file))
36
+ response = @pipe.gets
37
+ @pipe.write(Hydra::Messages::Runner::Shutdown.new)
38
+ assert File.exists?(target)
39
+ assert_equal "HYDRA", File.read(target)
21
40
  end
22
- should "terminate when sent a shutdown message"
23
41
  end
24
42
  end
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.2.0
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Nick Gauthier
@@ -54,12 +54,15 @@ files:
54
54
  - lib/hydra.rb
55
55
  - lib/hydra/io.rb
56
56
  - lib/hydra/message.rb
57
- - lib/hydra/message/runner_requests_file.rb
57
+ - lib/hydra/message/runner_messages.rb
58
58
  - lib/hydra/pipe.rb
59
59
  - lib/hydra/runner.rb
60
60
  - lib/hydra/ssh.rb
61
61
  - test/echo_the_dolphin.rb
62
62
  - test/helper.rb
63
+ - test/sample_tests/assert_true.rb
64
+ - test/sample_tests/write_file.rb
65
+ - test/test_message.rb
63
66
  - test/test_pipe.rb
64
67
  - test/test_runner.rb
65
68
  - test/test_ssh.rb
@@ -94,6 +97,9 @@ summary: Distributed testing toolkit
94
97
  test_files:
95
98
  - test/test_ssh.rb
96
99
  - test/helper.rb
100
+ - test/test_message.rb
97
101
  - test/test_pipe.rb
98
102
  - test/test_runner.rb
103
+ - test/sample_tests/write_file.rb
104
+ - test/sample_tests/assert_true.rb
99
105
  - test/echo_the_dolphin.rb
@@ -1,7 +0,0 @@
1
- module Hydra #:nodoc:
2
- module Messages #:nodoc:
3
- # Message indicating that a Runner needs a file to run
4
- class RunnerRequestsFile < Hydra::Message
5
- end
6
- end
7
- end