nimboids-spork 0.8.99
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/Gemfile +6 -0
- data/MIT-LICENSE +20 -0
- data/README.rdoc +129 -0
- data/assets/bootstrap.rb +29 -0
- data/bin/spork +20 -0
- data/ext/mkrf_conf.rb +26 -0
- data/features/at_exit_during_each_run.feature +36 -0
- data/features/cucumber_rails_integration.feature +107 -0
- data/features/diagnostic_mode.feature +41 -0
- data/features/gemfiles/rails3.0/Gemfile +10 -0
- data/features/gemfiles/rails3.0/Gemfile.lock +116 -0
- data/features/rails_delayed_loading_workarounds.feature +150 -0
- data/features/rspec_rails_integration.feature +92 -0
- data/features/spork_debugger.feature +108 -0
- data/features/steps/general_steps.rb +3 -0
- data/features/steps/rails_steps.rb +67 -0
- data/features/steps/sandbox_steps.rb +115 -0
- data/features/support/background_job.rb +63 -0
- data/features/support/bundler_helpers.rb +41 -0
- data/features/support/env.rb +105 -0
- data/features/unknown_app_framework.feature +42 -0
- data/lib/spork.rb +155 -0
- data/lib/spork/app_framework.rb +80 -0
- data/lib/spork/app_framework/padrino.rb +22 -0
- data/lib/spork/app_framework/rails.rb +82 -0
- data/lib/spork/app_framework/unknown.rb +6 -0
- data/lib/spork/custom_io_streams.rb +25 -0
- data/lib/spork/diagnoser.rb +105 -0
- data/lib/spork/ext/rails-reloader.rb +14 -0
- data/lib/spork/ext/ruby-debug.rb +150 -0
- data/lib/spork/forker.rb +71 -0
- data/lib/spork/run_strategy.rb +44 -0
- data/lib/spork/run_strategy/forking.rb +32 -0
- data/lib/spork/run_strategy/magazine.rb +141 -0
- data/lib/spork/run_strategy/magazine/magazine_slave.rb +30 -0
- data/lib/spork/run_strategy/magazine/magazine_slave_provider.rb +27 -0
- data/lib/spork/run_strategy/magazine/ring_server.rb +10 -0
- data/lib/spork/runner.rb +90 -0
- data/lib/spork/server.rb +76 -0
- data/lib/spork/test_framework.rb +167 -0
- data/lib/spork/test_framework/cucumber.rb +24 -0
- data/lib/spork/test_framework/rspec.rb +14 -0
- data/spec/spec_helper.rb +113 -0
- data/spec/spork/app_framework/rails_spec.rb +22 -0
- data/spec/spork/app_framework/unknown_spec.rb +12 -0
- data/spec/spork/app_framework_spec.rb +16 -0
- data/spec/spork/diagnoser_spec.rb +105 -0
- data/spec/spork/forker_spec.rb +44 -0
- data/spec/spork/run_strategy/forking_spec.rb +38 -0
- data/spec/spork/runner_spec.rb +50 -0
- data/spec/spork/server_spec.rb +15 -0
- data/spec/spork/test_framework/cucumber_spec.rb +11 -0
- data/spec/spork/test_framework/rspec_spec.rb +10 -0
- data/spec/spork/test_framework_shared_examples.rb +23 -0
- data/spec/spork/test_framework_spec.rb +90 -0
- data/spec/spork_spec.rb +153 -0
- data/spec/support/fake_framework.rb +15 -0
- data/spec/support/fake_run_strategy.rb +21 -0
- metadata +158 -0
@@ -0,0 +1,44 @@
|
|
1
|
+
class Spork::RunStrategy
|
2
|
+
attr_reader :test_framework
|
3
|
+
@@run_strategies = []
|
4
|
+
|
5
|
+
def initialize(test_framework)
|
6
|
+
@test_framework = test_framework
|
7
|
+
end
|
8
|
+
|
9
|
+
def preload
|
10
|
+
raise NotImplementedError
|
11
|
+
end
|
12
|
+
|
13
|
+
def run(argv, input, output)
|
14
|
+
raise NotImplementedError
|
15
|
+
end
|
16
|
+
|
17
|
+
def cleanup
|
18
|
+
raise NotImplementedError
|
19
|
+
end
|
20
|
+
|
21
|
+
def running?
|
22
|
+
raise NotImplementedError
|
23
|
+
end
|
24
|
+
|
25
|
+
def abort
|
26
|
+
raise NotImplementedError
|
27
|
+
end
|
28
|
+
|
29
|
+
protected
|
30
|
+
def self.factory(test_framework)
|
31
|
+
if RUBY_PLATFORM =~ /mswin|mingw|java/
|
32
|
+
Spork::RunStrategy::Magazine.new(test_framework)
|
33
|
+
else
|
34
|
+
Spork::RunStrategy::Forking.new(test_framework)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def self.inherited(subclass)
|
39
|
+
@@run_strategies << subclass
|
40
|
+
end
|
41
|
+
|
42
|
+
end
|
43
|
+
|
44
|
+
Dir[File.dirname(__FILE__) + "/run_strategy/*.rb"].each { |file| require file }
|
@@ -0,0 +1,32 @@
|
|
1
|
+
class Spork::RunStrategy::Forking < Spork::RunStrategy
|
2
|
+
def self.available?
|
3
|
+
Kernel.respond_to?(:fork)
|
4
|
+
end
|
5
|
+
|
6
|
+
def run(argv, stderr, stdout)
|
7
|
+
abort if running?
|
8
|
+
|
9
|
+
@child = ::Spork::Forker.new do
|
10
|
+
$stdout, $stderr = stdout, stderr
|
11
|
+
load test_framework.helper_file
|
12
|
+
Spork.exec_each_run
|
13
|
+
result = test_framework.run_tests(argv, stderr, stdout)
|
14
|
+
Spork.exec_after_each_run
|
15
|
+
result
|
16
|
+
end
|
17
|
+
@child.result
|
18
|
+
end
|
19
|
+
|
20
|
+
def abort
|
21
|
+
@child && @child.abort
|
22
|
+
end
|
23
|
+
|
24
|
+
def preload
|
25
|
+
test_framework.preload
|
26
|
+
end
|
27
|
+
|
28
|
+
def running?
|
29
|
+
@child && @child.running?
|
30
|
+
end
|
31
|
+
|
32
|
+
end
|
@@ -0,0 +1,141 @@
|
|
1
|
+
# this class' goal:
|
2
|
+
# to boldly just run test after test
|
3
|
+
# as they come in
|
4
|
+
require 'drb'
|
5
|
+
require 'rinda/ring'
|
6
|
+
require 'win32/process' if RUBY_PLATFORM =~ /mswin|mingw/ and RUBY_VERSION < '1.9.1'
|
7
|
+
require 'rubygems' # used for Gem.ruby
|
8
|
+
|
9
|
+
$:.unshift(File.dirname(__FILE__))
|
10
|
+
require 'magazine/magazine_slave'
|
11
|
+
|
12
|
+
class Spork::RunStrategy::Magazine < Spork::RunStrategy
|
13
|
+
|
14
|
+
Slave_Id_Range = 1..2 # Ringserver uses id: 0. Slave use: 1..MAX_SLAVES
|
15
|
+
|
16
|
+
def slave_max
|
17
|
+
Slave_Id_Range.to_a.size
|
18
|
+
end
|
19
|
+
|
20
|
+
def initialize(test_framework)
|
21
|
+
@test_framework = test_framework
|
22
|
+
this_path = File.expand_path(File.dirname(__FILE__))
|
23
|
+
@path = File.join(this_path, 'magazine')
|
24
|
+
@pids = []
|
25
|
+
|
26
|
+
@pids << start_Rinda_ringserver
|
27
|
+
sleep 1
|
28
|
+
|
29
|
+
fill_slave_pool
|
30
|
+
rescue RuntimeError => e
|
31
|
+
kill_all_processes
|
32
|
+
raise e
|
33
|
+
end
|
34
|
+
|
35
|
+
def start_Rinda_ringserver
|
36
|
+
app_name = "#{Gem.ruby} ring_server.rb"
|
37
|
+
spawn_process(app_name)
|
38
|
+
end
|
39
|
+
|
40
|
+
def fill_slave_pool
|
41
|
+
Slave_Id_Range.each do |id|
|
42
|
+
start_slave(id)
|
43
|
+
end
|
44
|
+
puts " -- Starting to fill pool..."
|
45
|
+
puts " Wait until at least one slave is provided before running tests..."
|
46
|
+
puts " ** CTRL+BREAK to stop Spork and kill all ruby slave processes **"
|
47
|
+
$stdout.flush
|
48
|
+
end
|
49
|
+
|
50
|
+
def start_slave(id)
|
51
|
+
app_pwd = Dir.pwd # path running app in
|
52
|
+
app = "#{Gem.ruby} magazine_slave_provider.rb #{id} '#{app_pwd}' #{@test_framework.short_name}"
|
53
|
+
@pids[id] = spawn_process(app)
|
54
|
+
end
|
55
|
+
|
56
|
+
def spawn_process(app)
|
57
|
+
|
58
|
+
if RUBY_PLATFORM =~ /java/
|
59
|
+
# jruby 1.8 has no easy way to just spawn, so use a thread
|
60
|
+
Dir.chdir(@path) do
|
61
|
+
io = IO.popen app
|
62
|
+
Thread.new { puts io.read }
|
63
|
+
return io.pid
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
if RUBY_VERSION < '1.9.1'
|
68
|
+
Process.create( :app_name => app, :cwd => @path ).process_id
|
69
|
+
else
|
70
|
+
Process.spawn( app, :chdir => @path )
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
def self.available?
|
75
|
+
true
|
76
|
+
end
|
77
|
+
|
78
|
+
def run(argv, stderr, stdout)
|
79
|
+
DRb.start_service
|
80
|
+
ts = Rinda::RingFinger.primary
|
81
|
+
if ts.read_all([:name, :MagazineSlave, nil, nil]).size > 0
|
82
|
+
print ' <-- take tuple'; stdout.flush
|
83
|
+
tuple = ts.take([:name, :MagazineSlave, nil, nil])
|
84
|
+
slave = tuple[2]
|
85
|
+
id = tuple[3]
|
86
|
+
|
87
|
+
puts "(#{slave.id_num}); slave.run..."; $stdout.flush
|
88
|
+
begin
|
89
|
+
slave.run(argv,stderr,stdout)
|
90
|
+
puts " -- (#{slave.id_num});run done"; $stdout.flush
|
91
|
+
ensure
|
92
|
+
restart_slave(id)
|
93
|
+
end
|
94
|
+
else
|
95
|
+
puts '- NO tuple'; $stdout.flush
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
def restart_slave(id)
|
100
|
+
pid = @pids[id]
|
101
|
+
Process.kill(9, pid)
|
102
|
+
start_slave(id)
|
103
|
+
end
|
104
|
+
|
105
|
+
def windows?
|
106
|
+
ENV['OS'] == 'Windows_NT'
|
107
|
+
end
|
108
|
+
|
109
|
+
def kill_all_processes
|
110
|
+
|
111
|
+
@pids.each {|pid|
|
112
|
+
if windows?
|
113
|
+
system("taskkill /f /pid #{pid}")
|
114
|
+
else
|
115
|
+
Process.kill(9, pid)
|
116
|
+
end
|
117
|
+
}
|
118
|
+
puts "\nKilling processes."; $stdout.flush
|
119
|
+
end
|
120
|
+
|
121
|
+
def slave_count
|
122
|
+
DRb.start_service
|
123
|
+
ts = Rinda::RingFinger.primary
|
124
|
+
ts.read_all([:name, :MagazineSlave, nil, nil]).size
|
125
|
+
end
|
126
|
+
|
127
|
+
|
128
|
+
def abort
|
129
|
+
kill_all_processes
|
130
|
+
end
|
131
|
+
|
132
|
+
def preload
|
133
|
+
true
|
134
|
+
# @test_framework.preload
|
135
|
+
end
|
136
|
+
|
137
|
+
def running?
|
138
|
+
@running
|
139
|
+
end
|
140
|
+
|
141
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'drb'
|
3
|
+
$:.unshift(File.dirname(__FILE__) + "/../../..") # directory of spork.rb
|
4
|
+
require 'spork'
|
5
|
+
|
6
|
+
class MagazineSlave
|
7
|
+
include DRb::DRbUndumped
|
8
|
+
attr_reader :id_num
|
9
|
+
def initialize(id_num, test_framework_short_name)
|
10
|
+
@id_num = id_num
|
11
|
+
@test_framework = Spork::TestFramework.factory(STDOUT, STDERR,
|
12
|
+
test_framework_short_name)
|
13
|
+
# ENV["DRB"] = 'true'
|
14
|
+
# Spork.using_spork!
|
15
|
+
return(nil) unless preload
|
16
|
+
end
|
17
|
+
|
18
|
+
def run(argv, stderr, stdout)
|
19
|
+
$stdout, $stderr = stdout, stderr
|
20
|
+
Spork.exec_each_run
|
21
|
+
load @test_framework.helper_file
|
22
|
+
@test_framework.run_tests(argv, stderr, stdout)
|
23
|
+
puts " <-- Slave(#{@id_num}) run done!"; stdout.flush
|
24
|
+
end
|
25
|
+
|
26
|
+
def preload
|
27
|
+
@test_framework.preload
|
28
|
+
end
|
29
|
+
|
30
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
# magazine_slave_provider.rb
|
2
|
+
require 'drb'
|
3
|
+
require 'rinda/ring'
|
4
|
+
require 'rinda/tuplespace'
|
5
|
+
require 'magazine_slave'
|
6
|
+
|
7
|
+
|
8
|
+
|
9
|
+
# pass on
|
10
|
+
|
11
|
+
id = ARGV[0].to_i || "?"
|
12
|
+
app_pwd = ARGV[1]
|
13
|
+
test_framework_short_name = ARGV[2]
|
14
|
+
|
15
|
+
# start up the Rinda service
|
16
|
+
|
17
|
+
DRb.start_service
|
18
|
+
|
19
|
+
Dir.chdir app_pwd
|
20
|
+
puts " -- build slave #{id}..."; $stdout.flush
|
21
|
+
magazine_slave = MagazineSlave.new(id, test_framework_short_name )
|
22
|
+
Rinda::RingProvider.new(:MagazineSlave, magazine_slave, id).provide
|
23
|
+
|
24
|
+
puts " --> DRb magazine_slave_service: #{id} provided..."; $stdout.flush
|
25
|
+
|
26
|
+
# wait for the DRb service to finish before exiting
|
27
|
+
DRb.thread.join
|
data/lib/spork/runner.rb
ADDED
@@ -0,0 +1,90 @@
|
|
1
|
+
require 'optparse'
|
2
|
+
require 'stringio'
|
3
|
+
|
4
|
+
module Spork
|
5
|
+
# This is used by bin/spork. It's wrapped in a class because it's easier to test that way.
|
6
|
+
class Runner
|
7
|
+
attr_reader :test_framework
|
8
|
+
|
9
|
+
def self.run(args, output, error)
|
10
|
+
self.new(args, output, error).run
|
11
|
+
end
|
12
|
+
|
13
|
+
def initialize(args, output, error)
|
14
|
+
raise ArgumentError, "expected array of args" unless args.is_a?(Array)
|
15
|
+
@output = output
|
16
|
+
@error = error
|
17
|
+
@options = {}
|
18
|
+
opt = OptionParser.new
|
19
|
+
opt.banner = "Usage: spork [test framework name] [options]\n\n"
|
20
|
+
|
21
|
+
opt.separator "Options:"
|
22
|
+
opt.on("-b", "--bootstrap") {|ignore| @options[:bootstrap] = true }
|
23
|
+
opt.on("-d", "--diagnose") {|ignore| @options[:diagnose] = true }
|
24
|
+
opt.on("-h", "--help") {|ignore| @options[:help] = true }
|
25
|
+
opt.on("-p", "--port [PORT]") {|port| @options[:port] = port }
|
26
|
+
non_option_args = args.select { |arg| ! args[0].match(/^-/) }
|
27
|
+
@options[:server_matcher] = non_option_args[0]
|
28
|
+
opt.parse!(args)
|
29
|
+
|
30
|
+
if @options[:help]
|
31
|
+
@output.puts opt
|
32
|
+
@output.puts
|
33
|
+
@output.puts supported_test_frameworks_text
|
34
|
+
exit(0)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def supported_test_frameworks_text
|
39
|
+
text = StringIO.new
|
40
|
+
|
41
|
+
text.puts "Supported test frameworks:"
|
42
|
+
text.puts Spork::TestFramework.supported_test_frameworks.sort { |a,b| a.short_name <=> b.short_name }.map { |s| (s.available? ? '(*) ' : '( ) ') + s.short_name }
|
43
|
+
text.puts "\nLegend: ( ) - not detected in project (*) - detected\n"
|
44
|
+
text.string
|
45
|
+
end
|
46
|
+
|
47
|
+
# Returns a server for the specified (or the detected default) testing framework. Returns nil if none detected, or if the specified is not supported or available.
|
48
|
+
def find_test_framework
|
49
|
+
Spork::TestFramework.factory(@output, @error, options[:server_matcher])
|
50
|
+
rescue Spork::TestFramework::NoFrameworksAvailable => e
|
51
|
+
@error.puts e.message
|
52
|
+
rescue Spork::TestFramework::FactoryException => e
|
53
|
+
@error.puts "#{e.message}\n\n#{supported_test_frameworks_text}"
|
54
|
+
end
|
55
|
+
|
56
|
+
def run
|
57
|
+
return false unless test_framework = find_test_framework
|
58
|
+
ENV["DRB"] = 'true'
|
59
|
+
@error.puts "Using #{test_framework.short_name}"
|
60
|
+
@error.flush
|
61
|
+
|
62
|
+
case
|
63
|
+
when options[:bootstrap]
|
64
|
+
test_framework.bootstrap
|
65
|
+
when options[:diagnose]
|
66
|
+
require 'spork/diagnoser'
|
67
|
+
|
68
|
+
Spork::Diagnoser.install_hook!(test_framework.entry_point)
|
69
|
+
test_framework.preload
|
70
|
+
Spork::Diagnoser.output_results(@output)
|
71
|
+
return true
|
72
|
+
else
|
73
|
+
run_strategy = Spork::RunStrategy.factory(test_framework)
|
74
|
+
return(false) unless run_strategy.preload
|
75
|
+
Spork::Server.run(:port => @options[:port] || test_framework.default_port, :run_strategy => run_strategy)
|
76
|
+
return true
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
private
|
81
|
+
attr_reader :options
|
82
|
+
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
|
87
|
+
|
88
|
+
|
89
|
+
|
90
|
+
|
data/lib/spork/server.rb
ADDED
@@ -0,0 +1,76 @@
|
|
1
|
+
require 'drb/drb'
|
2
|
+
require 'rbconfig'
|
3
|
+
require 'spork/forker.rb'
|
4
|
+
require 'spork/custom_io_streams.rb'
|
5
|
+
require 'spork/app_framework.rb'
|
6
|
+
|
7
|
+
# An abstract class that is implemented to create a server
|
8
|
+
#
|
9
|
+
# (This was originally based off of spec_server.rb from rspec-rails (David Chelimsky), which was based on Florian Weber's TDDMate)
|
10
|
+
class Spork::Server
|
11
|
+
attr_reader :run_strategy
|
12
|
+
include Spork::CustomIOStreams
|
13
|
+
|
14
|
+
def initialize(options = {})
|
15
|
+
@run_strategy = options[:run_strategy]
|
16
|
+
@port = options[:port]
|
17
|
+
end
|
18
|
+
|
19
|
+
def self.run(options = {})
|
20
|
+
new(options).listen
|
21
|
+
end
|
22
|
+
|
23
|
+
# Sets up signals and starts the DRb service. If it's successful, it doesn't return. Not ever. You don't need to override this.
|
24
|
+
def listen
|
25
|
+
raise RuntimeError, "you must call Spork.using_spork! before starting the server" unless Spork.using_spork?
|
26
|
+
trap("SIGINT") { sig_int_received }
|
27
|
+
trap("SIGTERM") { abort; exit!(0) }
|
28
|
+
trap("USR2") { abort; restart } if Signal.list.has_key?("USR2")
|
29
|
+
@drb_service = DRb.start_service("druby://127.0.0.1:#{port}", self)
|
30
|
+
Spork.each_run { @drb_service.stop_service } if @run_strategy.class == Spork::RunStrategy::Forking
|
31
|
+
stderr.puts "Spork is ready and listening on #{port}!"
|
32
|
+
stderr.flush
|
33
|
+
DRb.thread.join
|
34
|
+
end
|
35
|
+
|
36
|
+
attr_accessor :port
|
37
|
+
|
38
|
+
# This is the public facing method that is served up by DRb. To use it from the client side (in a testing framework):
|
39
|
+
#
|
40
|
+
# DRb.start_service("druby://localhost:0") # this allows Ruby to do some magical stuff so you can pass an output stream over DRb.
|
41
|
+
# # see http://redmine.ruby-lang.org/issues/show/496 to see why localhost:0 is used.
|
42
|
+
# spec_server = DRbObject.new_with_uri("druby://127.0.0.1:8989")
|
43
|
+
# spec_server.run(options.argv, $stderr, $stdout)
|
44
|
+
#
|
45
|
+
# When implementing a test server, don't override this method: override run_tests instead.
|
46
|
+
def run(argv, stderr, stdout)
|
47
|
+
puts "Running tests with args #{argv.inspect}..."
|
48
|
+
run_strategy.run(argv, stderr, stdout)
|
49
|
+
puts "Done.\n\n"
|
50
|
+
end
|
51
|
+
|
52
|
+
def abort
|
53
|
+
run_strategy.abort
|
54
|
+
end
|
55
|
+
|
56
|
+
private
|
57
|
+
def restart
|
58
|
+
stderr.puts "restarting"
|
59
|
+
stderr.flush
|
60
|
+
config = ::Config::CONFIG
|
61
|
+
ruby = File::join(config['bindir'], config['ruby_install_name']) + config['EXEEXT']
|
62
|
+
command_line = [ruby, $0, ARGV].flatten.join(' ')
|
63
|
+
exec(command_line)
|
64
|
+
end
|
65
|
+
|
66
|
+
def sig_int_received
|
67
|
+
stdout.puts "\n"
|
68
|
+
if run_strategy.running?
|
69
|
+
abort
|
70
|
+
stderr.puts "Running tests stopped. Press CTRL-C again to stop the server."
|
71
|
+
stderr.flush
|
72
|
+
else
|
73
|
+
exit!(0)
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|