hydra 0.2.0 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
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