cucumber support
This commit is contained in:
parent
24f550f62c
commit
2fd871ed9b
13
README.rdoc
13
README.rdoc
|
@ -20,9 +20,12 @@ In your rakefile:
|
||||||
require 'hydra/tasks'
|
require 'hydra/tasks'
|
||||||
|
|
||||||
Hydra::TestTask.new('hydra') do |t|
|
Hydra::TestTask.new('hydra') do |t|
|
||||||
|
# test unit
|
||||||
t.add_files 'test/unit/**/*_test.rb'
|
t.add_files 'test/unit/**/*_test.rb'
|
||||||
t.add_files 'test/functional/**/*_test.rb'
|
t.add_files 'test/functional/**/*_test.rb'
|
||||||
t.add_files 'test/integration/**/*_test.rb'
|
t.add_files 'test/integration/**/*_test.rb'
|
||||||
|
# cucumber
|
||||||
|
t.add_files 'features/**/*.feature'
|
||||||
end
|
end
|
||||||
|
|
||||||
Run:
|
Run:
|
||||||
|
@ -32,6 +35,16 @@ Run:
|
||||||
Hydra defaults to Single Core mode, so you may want to configure it
|
Hydra defaults to Single Core mode, so you may want to configure it
|
||||||
to use two (or more) of your cores if you have a multi-processing machine.
|
to use two (or more) of your cores if you have a multi-processing machine.
|
||||||
|
|
||||||
|
== Supported frameworks
|
||||||
|
|
||||||
|
Right now hydra only supports a few frameworks:
|
||||||
|
|
||||||
|
* Test::Unit
|
||||||
|
* Cucumber
|
||||||
|
|
||||||
|
We're working on adding more frameworks, and if you'd like to help, please
|
||||||
|
send me a message and I'll show you where to code!
|
||||||
|
|
||||||
== Running Remote Tasks
|
== Running Remote Tasks
|
||||||
|
|
||||||
You can run tasks across all of your remote workers easily with Hydra. In your rake file, add:
|
You can run tasks across all of your remote workers easily with Hydra. In your rake file, add:
|
||||||
|
|
|
@ -9,7 +9,7 @@ Gem::Specification.new do |s|
|
||||||
|
|
||||||
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
||||||
s.authors = ["Nick Gauthier"]
|
s.authors = ["Nick Gauthier"]
|
||||||
s.date = %q{2010-03-25}
|
s.date = %q{2010-03-30}
|
||||||
s.description = %q{Spread your tests over multiple machines to test your code faster.}
|
s.description = %q{Spread your tests over multiple machines to test your code faster.}
|
||||||
s.email = %q{nick@smartlogicsolutions.com}
|
s.email = %q{nick@smartlogicsolutions.com}
|
||||||
s.extra_rdoc_files = [
|
s.extra_rdoc_files = [
|
||||||
|
@ -30,6 +30,7 @@ Gem::Specification.new do |s|
|
||||||
"hydra.gemspec",
|
"hydra.gemspec",
|
||||||
"hydra_gray.png",
|
"hydra_gray.png",
|
||||||
"lib/hydra.rb",
|
"lib/hydra.rb",
|
||||||
|
"lib/hydra/cucumber/formatter.rb",
|
||||||
"lib/hydra/hash.rb",
|
"lib/hydra/hash.rb",
|
||||||
"lib/hydra/listener/abstract.rb",
|
"lib/hydra/listener/abstract.rb",
|
||||||
"lib/hydra/listener/minimal_output.rb",
|
"lib/hydra/listener/minimal_output.rb",
|
||||||
|
@ -52,6 +53,8 @@ Gem::Specification.new do |s|
|
||||||
"lib/hydra/worker.rb",
|
"lib/hydra/worker.rb",
|
||||||
"test/fixtures/assert_true.rb",
|
"test/fixtures/assert_true.rb",
|
||||||
"test/fixtures/config.yml",
|
"test/fixtures/config.yml",
|
||||||
|
"test/fixtures/features/step_definitions.rb",
|
||||||
|
"test/fixtures/features/write_file.feature",
|
||||||
"test/fixtures/hello_world.rb",
|
"test/fixtures/hello_world.rb",
|
||||||
"test/fixtures/slow.rb",
|
"test/fixtures/slow.rb",
|
||||||
"test/fixtures/sync_test.rb",
|
"test/fixtures/sync_test.rb",
|
||||||
|
@ -78,6 +81,7 @@ Gem::Specification.new do |s|
|
||||||
"test/fixtures/sync_test.rb",
|
"test/fixtures/sync_test.rb",
|
||||||
"test/fixtures/assert_true.rb",
|
"test/fixtures/assert_true.rb",
|
||||||
"test/fixtures/hello_world.rb",
|
"test/fixtures/hello_world.rb",
|
||||||
|
"test/fixtures/features/step_definitions.rb",
|
||||||
"test/master_test.rb",
|
"test/master_test.rb",
|
||||||
"test/worker_test.rb",
|
"test/worker_test.rb",
|
||||||
"test/runner_test.rb",
|
"test/runner_test.rb",
|
||||||
|
|
|
@ -0,0 +1,31 @@
|
||||||
|
require 'cucumber/formatter/console'
|
||||||
|
require 'cucumber/formatter/io'
|
||||||
|
|
||||||
|
module Cucumber #:nodoc:
|
||||||
|
module Formatter #:nodoc:
|
||||||
|
# Hydra formatter for cucumber.
|
||||||
|
# Stifles all output except error messages
|
||||||
|
# Based on the
|
||||||
|
class Hydra < Cucumber::Formatter::Progress
|
||||||
|
# Removed the extra newlines here
|
||||||
|
def after_features(features)
|
||||||
|
print_summary(features)
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
# Removed the file statistics
|
||||||
|
def print_summary(features)
|
||||||
|
print_steps(:pending)
|
||||||
|
print_steps(:failed)
|
||||||
|
print_snippets(@options)
|
||||||
|
print_passing_wip(@options)
|
||||||
|
print_tag_limit_warnings(features)
|
||||||
|
end
|
||||||
|
|
||||||
|
# Removed all progress output
|
||||||
|
def progress(status)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -33,29 +33,17 @@ module Hydra #:nodoc:
|
||||||
# Run a test file and report the results
|
# Run a test file and report the results
|
||||||
def run_file(file)
|
def run_file(file)
|
||||||
trace "Running file: #{file}"
|
trace "Running file: #{file}"
|
||||||
begin
|
|
||||||
require file
|
output = ""
|
||||||
rescue LoadError => ex
|
if file =~ /.rb$/
|
||||||
trace "#{file} does not exist [#{ex.to_s}]"
|
output = run_ruby_file(file)
|
||||||
@io.write Results.new(:output => ex.to_s, :file => file)
|
elsif file =~ /.feature$/
|
||||||
return
|
output = run_cucumber_file(file)
|
||||||
end
|
|
||||||
output = []
|
|
||||||
@result = Test::Unit::TestResult.new
|
|
||||||
@result.add_listener(Test::Unit::TestResult::FAULT) do |value|
|
|
||||||
output << value
|
|
||||||
end
|
end
|
||||||
|
|
||||||
klasses = Runner.find_classes_in_file(file)
|
output = "." if output == ""
|
||||||
begin
|
|
||||||
klasses.each{|klass| klass.suite.run(@result){|status, name| ;}}
|
|
||||||
rescue => ex
|
|
||||||
output << ex.to_s
|
|
||||||
end
|
|
||||||
|
|
||||||
output << '.' if output.empty?
|
@io.write Results.new(:output => output, :file => file)
|
||||||
|
|
||||||
@io.write Results.new(:output => output.join("\n"), :file => file)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
# Stop running
|
# Stop running
|
||||||
|
@ -86,6 +74,94 @@ module Hydra #:nodoc:
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Run a ruby file (ending in .rb)
|
||||||
|
def run_ruby_file(file)
|
||||||
|
run_test_unit_file(file) + run_rspec_file(file)
|
||||||
|
end
|
||||||
|
|
||||||
|
# Run all the Test::Unit Suites in a ruby file
|
||||||
|
def run_test_unit_file(file)
|
||||||
|
begin
|
||||||
|
require file
|
||||||
|
rescue LoadError => ex
|
||||||
|
trace "#{file} does not exist [#{ex.to_s}]"
|
||||||
|
return ex.to_s
|
||||||
|
end
|
||||||
|
output = []
|
||||||
|
@result = Test::Unit::TestResult.new
|
||||||
|
@result.add_listener(Test::Unit::TestResult::FAULT) do |value|
|
||||||
|
output << value
|
||||||
|
end
|
||||||
|
|
||||||
|
klasses = Runner.find_classes_in_file(file)
|
||||||
|
begin
|
||||||
|
klasses.each{|klass| klass.suite.run(@result){|status, name| ;}}
|
||||||
|
rescue => ex
|
||||||
|
output << ex.to_s
|
||||||
|
end
|
||||||
|
|
||||||
|
return output.join("\n")
|
||||||
|
end
|
||||||
|
|
||||||
|
# run all the Specs in an RSpec file (NOT IMPLEMENTED)
|
||||||
|
def run_rspec_file(file)
|
||||||
|
#TODO
|
||||||
|
# Given the file
|
||||||
|
# return "" if all the tests passed
|
||||||
|
# or return the error messages for the entire file
|
||||||
|
return ""
|
||||||
|
end
|
||||||
|
|
||||||
|
# run all the scenarios in a cucumber feature file
|
||||||
|
def run_cucumber_file(file)
|
||||||
|
require 'cucumber'
|
||||||
|
require 'cucumber/formatter/progress'
|
||||||
|
require 'hydra/cucumber/formatter'
|
||||||
|
def tag_excess(features, limits)
|
||||||
|
limits.map do |tag_name, tag_limit|
|
||||||
|
tag_locations = features.tag_locations(tag_name)
|
||||||
|
if tag_limit && (tag_locations.length > tag_limit)
|
||||||
|
[tag_name, tag_limit, tag_locations]
|
||||||
|
else
|
||||||
|
nil
|
||||||
|
end
|
||||||
|
end.compact
|
||||||
|
end
|
||||||
|
|
||||||
|
files = [file]
|
||||||
|
dev_null = StringIO.new
|
||||||
|
|
||||||
|
options = Cucumber::Cli::Options.new
|
||||||
|
configuration = Cucumber::Cli::Configuration.new(dev_null, dev_null)
|
||||||
|
configuration.parse!([]+files)
|
||||||
|
step_mother = Cucumber::StepMother.new
|
||||||
|
|
||||||
|
step_mother.options = configuration.options
|
||||||
|
step_mother.log = configuration.log
|
||||||
|
step_mother.load_code_files(configuration.support_to_load)
|
||||||
|
step_mother.after_configuration(configuration)
|
||||||
|
features = step_mother.load_plain_text_features(files)
|
||||||
|
step_mother.load_code_files(configuration.step_defs_to_load)
|
||||||
|
|
||||||
|
tag_excess = tag_excess(features, configuration.options[:tag_expression].limits)
|
||||||
|
configuration.options[:tag_excess] = tag_excess
|
||||||
|
|
||||||
|
hydra_response = StringIO.new
|
||||||
|
formatter = Cucumber::Formatter::Hydra.new(
|
||||||
|
step_mother, hydra_response, configuration.options
|
||||||
|
)
|
||||||
|
|
||||||
|
runner = Cucumber::Ast::TreeWalker.new(
|
||||||
|
step_mother, [formatter], configuration.options, dev_null
|
||||||
|
)
|
||||||
|
step_mother.visitor = runner
|
||||||
|
runner.visit_features(features)
|
||||||
|
|
||||||
|
hydra_response.rewind
|
||||||
|
return hydra_response.read
|
||||||
|
end
|
||||||
|
|
||||||
|
# find all the test unit classes in a given file, so we can run their suites
|
||||||
def self.find_classes_in_file(f)
|
def self.find_classes_in_file(f)
|
||||||
code = ""
|
code = ""
|
||||||
File.open(f) {|buffer| code = buffer.read}
|
File.open(f) {|buffer| code = buffer.read}
|
||||||
|
|
|
@ -0,0 +1,17 @@
|
||||||
|
Given /^a target file$/ do
|
||||||
|
@target_file = File.expand_path(File.join(Dir.tmpdir, 'hydra_test.txt'))
|
||||||
|
end
|
||||||
|
|
||||||
|
When /^I write "([^\"]*)" to the file$/ do |text|
|
||||||
|
f = File.new(@target_file, 'w')
|
||||||
|
f.write text
|
||||||
|
f.flush
|
||||||
|
f.close
|
||||||
|
end
|
||||||
|
|
||||||
|
Then /^"([^\"]*)" should be written in the file$/ do |text|
|
||||||
|
f = File.new(@target_file, 'r')
|
||||||
|
raise 'Did not write to file' unless text == f.read
|
||||||
|
f.close
|
||||||
|
end
|
||||||
|
|
|
@ -0,0 +1,7 @@
|
||||||
|
Feature: Write a file
|
||||||
|
|
||||||
|
Scenario: Write to hydra_test.txt
|
||||||
|
Given a target file
|
||||||
|
When I write "HYDRA" to the file
|
||||||
|
Then "HYDRA" should be written in the file
|
||||||
|
|
|
@ -18,7 +18,7 @@ class RunnerTest < Test::Unit::TestCase
|
||||||
# coverage output
|
# coverage output
|
||||||
pipe = Hydra::Pipe.new
|
pipe = Hydra::Pipe.new
|
||||||
parent = Process.fork do
|
parent = Process.fork do
|
||||||
request_a_file_and_verify_completion(pipe)
|
request_a_file_and_verify_completion(pipe, test_file)
|
||||||
end
|
end
|
||||||
run_the_runner(pipe)
|
run_the_runner(pipe)
|
||||||
Process.wait(parent)
|
Process.wait(parent)
|
||||||
|
@ -31,10 +31,19 @@ class RunnerTest < Test::Unit::TestCase
|
||||||
child = Process.fork do
|
child = Process.fork do
|
||||||
run_the_runner(pipe)
|
run_the_runner(pipe)
|
||||||
end
|
end
|
||||||
request_a_file_and_verify_completion(pipe)
|
request_a_file_and_verify_completion(pipe, test_file)
|
||||||
Process.wait(child)
|
Process.wait(child)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
should "run a cucumber test" do
|
||||||
|
pipe = Hydra::Pipe.new
|
||||||
|
parent = Process.fork do
|
||||||
|
request_a_file_and_verify_completion(pipe, cucumber_feature_file)
|
||||||
|
end
|
||||||
|
run_the_runner(pipe)
|
||||||
|
Process.wait(parent)
|
||||||
|
end
|
||||||
|
|
||||||
should "be able to run a runner over ssh" do
|
should "be able to run a runner over ssh" do
|
||||||
ssh = Hydra::SSH.new(
|
ssh = Hydra::SSH.new(
|
||||||
'localhost',
|
'localhost',
|
||||||
|
@ -62,12 +71,12 @@ class RunnerTest < Test::Unit::TestCase
|
||||||
end
|
end
|
||||||
|
|
||||||
module RunnerTestHelper
|
module RunnerTestHelper
|
||||||
def request_a_file_and_verify_completion(pipe)
|
def request_a_file_and_verify_completion(pipe, file)
|
||||||
pipe.identify_as_parent
|
pipe.identify_as_parent
|
||||||
|
|
||||||
# make sure it asks for a file, then give it one
|
# make sure it asks for a file, then give it one
|
||||||
assert pipe.gets.is_a?(Hydra::Messages::Runner::RequestFile)
|
assert pipe.gets.is_a?(Hydra::Messages::Runner::RequestFile)
|
||||||
pipe.write(Hydra::Messages::Worker::RunFile.new(:file => test_file))
|
pipe.write(Hydra::Messages::Worker::RunFile.new(:file => file))
|
||||||
|
|
||||||
# grab its response. This makes us wait for it to finish
|
# grab its response. This makes us wait for it to finish
|
||||||
response = pipe.gets
|
response = pipe.gets
|
||||||
|
|
|
@ -18,6 +18,10 @@ class Test::Unit::TestCase
|
||||||
def test_file
|
def test_file
|
||||||
File.expand_path(File.join(File.dirname(__FILE__), 'fixtures', 'write_file.rb'))
|
File.expand_path(File.join(File.dirname(__FILE__), 'fixtures', 'write_file.rb'))
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def cucumber_feature_file
|
||||||
|
File.expand_path(File.join(File.dirname(__FILE__), 'fixtures', 'features', 'write_file.feature'))
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
module Hydra #:nodoc:
|
module Hydra #:nodoc:
|
||||||
|
|
Loading…
Reference in New Issue