spork 0.9.0.rc8-x86-mswin32

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.
Files changed (59) hide show
  1. data/Gemfile +10 -0
  2. data/MIT-LICENSE +20 -0
  3. data/README.rdoc +134 -0
  4. data/assets/bootstrap.rb +47 -0
  5. data/bin/spork +20 -0
  6. data/features/at_exit_during_each_run.feature +36 -0
  7. data/features/cucumber_rails_integration.feature +107 -0
  8. data/features/diagnostic_mode.feature +41 -0
  9. data/features/gemfiles/rails3.0/Gemfile +14 -0
  10. data/features/gemfiles/rails3.0/Gemfile.lock +120 -0
  11. data/features/rails_delayed_loading_workarounds.feature +177 -0
  12. data/features/rspec_rails_integration.feature +92 -0
  13. data/features/spork_debugger.feature +108 -0
  14. data/features/steps/general_steps.rb +3 -0
  15. data/features/steps/rails_steps.rb +67 -0
  16. data/features/steps/sandbox_steps.rb +115 -0
  17. data/features/support/background_job.rb +63 -0
  18. data/features/support/bundler_helpers.rb +41 -0
  19. data/features/support/env.rb +105 -0
  20. data/features/unknown_app_framework.feature +42 -0
  21. data/lib/spork.rb +155 -0
  22. data/lib/spork/app_framework.rb +80 -0
  23. data/lib/spork/app_framework/padrino.rb +22 -0
  24. data/lib/spork/app_framework/rails.rb +82 -0
  25. data/lib/spork/app_framework/unknown.rb +6 -0
  26. data/lib/spork/custom_io_streams.rb +25 -0
  27. data/lib/spork/diagnoser.rb +105 -0
  28. data/lib/spork/ext/rails-reloader.rb +14 -0
  29. data/lib/spork/ext/ruby-debug.rb +150 -0
  30. data/lib/spork/forker.rb +71 -0
  31. data/lib/spork/gem_helpers.rb +38 -0
  32. data/lib/spork/run_strategy.rb +48 -0
  33. data/lib/spork/run_strategy/forking.rb +35 -0
  34. data/lib/spork/run_strategy/magazine.rb +151 -0
  35. data/lib/spork/run_strategy/magazine/magazine_slave.rb +30 -0
  36. data/lib/spork/run_strategy/magazine/magazine_slave_provider.rb +30 -0
  37. data/lib/spork/run_strategy/magazine/ring_server.rb +10 -0
  38. data/lib/spork/runner.rb +90 -0
  39. data/lib/spork/server.rb +77 -0
  40. data/lib/spork/test_framework.rb +167 -0
  41. data/lib/spork/test_framework/cucumber.rb +38 -0
  42. data/lib/spork/test_framework/rspec.rb +14 -0
  43. data/spec/spec_helper.rb +113 -0
  44. data/spec/spork/app_framework/rails_spec.rb +22 -0
  45. data/spec/spork/app_framework/unknown_spec.rb +12 -0
  46. data/spec/spork/app_framework_spec.rb +16 -0
  47. data/spec/spork/diagnoser_spec.rb +105 -0
  48. data/spec/spork/forker_spec.rb +44 -0
  49. data/spec/spork/run_strategy/forking_spec.rb +38 -0
  50. data/spec/spork/runner_spec.rb +50 -0
  51. data/spec/spork/server_spec.rb +15 -0
  52. data/spec/spork/test_framework/cucumber_spec.rb +11 -0
  53. data/spec/spork/test_framework/rspec_spec.rb +10 -0
  54. data/spec/spork/test_framework_shared_examples.rb +23 -0
  55. data/spec/spork/test_framework_spec.rb +90 -0
  56. data/spec/spork_spec.rb +153 -0
  57. data/spec/support/fake_framework.rb +15 -0
  58. data/spec/support/fake_run_strategy.rb +21 -0
  59. metadata +173 -0
@@ -0,0 +1,71 @@
1
+ # A helper class that allows you to run a block inside of a fork, and then get the result from that block.
2
+ #
3
+ # == Example:
4
+ #
5
+ # forker = Spork::Forker.new do
6
+ # sleep 3
7
+ # "success"
8
+ # end
9
+ #
10
+ # forker.result # => "success"
11
+ class Spork::Forker
12
+
13
+ # Raised if the fork died (was killed) before it sent it's response back.
14
+ class ForkDiedException < Exception; end
15
+ def initialize(&block)
16
+ return unless block_given?
17
+ @child_io, @server_io = UNIXSocket.socketpair
18
+ @child_pid = Kernel.fork do
19
+ begin
20
+ @server_io.close
21
+ Marshal.dump(yield, @child_io)
22
+ # wait for the parent to acknowledge receipt of the result.
23
+ master_response = Marshal.load(@child_io)
24
+ rescue EOFError
25
+ nil
26
+ rescue Exception => e
27
+ puts "Exception encountered: #{e.inspect}\nbacktrace:\n#{e.backtrace * %(\n)}"
28
+ end
29
+
30
+ # terminate, skipping any at_exit blocks.
31
+ exit!(0)
32
+ end
33
+ @child_io.close
34
+ end
35
+
36
+ # Wait for the fork to finish running, and then return its return value.
37
+ #
38
+ # If the fork was aborted, then result returns nil.
39
+ def result
40
+ return unless running?
41
+ result_thread = Thread.new do
42
+ begin
43
+ @result = Marshal.load(@server_io)
44
+ Marshal.dump('ACK', @server_io)
45
+ rescue ForkDiedException, EOFError
46
+ @result = nil
47
+ end
48
+ end
49
+ Process.wait(@child_pid)
50
+ result_thread.raise(ForkDiedException) if @result.nil?
51
+ @child_pid = nil
52
+ @result
53
+ end
54
+
55
+ # abort the current running fork
56
+ def abort
57
+ if running?
58
+ Process.kill(Signal.list['TERM'], @child_pid)
59
+ @child_pid = nil
60
+ true
61
+ end
62
+ end
63
+
64
+ def running?
65
+ return false unless @child_pid
66
+ Process.getpgid(@child_pid)
67
+ true
68
+ rescue Errno::ESRCH
69
+ false
70
+ end
71
+ end
@@ -0,0 +1,38 @@
1
+ module Spork::GemHelpers
2
+ extend self
3
+
4
+ class GemPath
5
+ attr_reader :name, :version, :path, :version_numbers
6
+ include Comparable
7
+ def initialize(p)
8
+ @path = p
9
+ @name, @version = File.basename(p).scan(/^(.+?)-([^-]+)$/).flatten
10
+ @version_numbers = @version.split(/[^0-9]+/).map(&:to_i)
11
+ end
12
+
13
+ def <=>(other)
14
+ raise "Not comparable gem paths ('#{name}' is not '#{other.name}')" unless name == other.name
15
+ @version_numbers <=> other.version_numbers
16
+ end
17
+ end
18
+
19
+ def latest_load_paths
20
+ case
21
+ when defined?(Bundler)
22
+ $LOAD_PATH.uniq
23
+ when Gem.respond_to?(:path)
24
+ Dir["{#{Gem.path.join(',')}}" + "/gems/*"].inject({}) do |h,f|
25
+ gem_path = GemPath.new(f)
26
+ if h[gem_path.name]
27
+ h[gem_path.name] = gem_path if gem_path > h[gem_path.name]
28
+ else
29
+ h[gem_path.name] = gem_path
30
+ end
31
+ h
32
+ end.values.map(&:path)
33
+ else
34
+ STDERR.puts "No mechanism available to scan for other gems implementing spork hooks. "
35
+ []
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,48 @@
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 assert_ready!
26
+ raise NotImplementedError
27
+ end
28
+
29
+ def abort
30
+ raise NotImplementedError
31
+ end
32
+
33
+ protected
34
+ def self.factory(test_framework)
35
+ if RUBY_PLATFORM =~ /mswin|mingw|java/
36
+ Spork::RunStrategy::Magazine.new(test_framework)
37
+ else
38
+ Spork::RunStrategy::Forking.new(test_framework)
39
+ end
40
+ end
41
+
42
+ def self.inherited(subclass)
43
+ @@run_strategies << subclass
44
+ end
45
+
46
+ end
47
+
48
+ Dir[File.dirname(__FILE__) + "/run_strategy/*.rb"].each { |file| require file }
@@ -0,0 +1,35 @@
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
+ def assert_ready!
33
+ raise RuntimeError, "This process hasn't loaded the environment yet by loading the prefork block" unless Spork.using_spork?
34
+ end
35
+ end
@@ -0,0 +1,151 @@
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
+ if RUBY_PLATFORM =~ /mswin|mingw/ and RUBY_VERSION < '1.9.1'
7
+ begin
8
+ require 'win32/process'
9
+ rescue LoadError
10
+ puts "The 'win32-process' gem is required for windows Spork support with ruby 1.9.1 and lower. Install it, or if using bundler, add it to your Gemfile."
11
+ exit 1
12
+ end
13
+ end
14
+
15
+ require 'rubygems' # used for Gem.ruby
16
+
17
+ $:.unshift(File.dirname(__FILE__))
18
+ require 'magazine/magazine_slave'
19
+
20
+ class Spork::RunStrategy::Magazine < Spork::RunStrategy
21
+
22
+ Slave_Id_Range = 1..2 # Ringserver uses id: 0. Slave use: 1..MAX_SLAVES
23
+
24
+ def slave_max
25
+ Slave_Id_Range.to_a.size
26
+ end
27
+
28
+ def initialize(test_framework)
29
+ @test_framework = test_framework
30
+ this_path = File.expand_path(File.dirname(__FILE__))
31
+ @path = File.join(this_path, 'magazine')
32
+ @pids = []
33
+
34
+ @pids << start_Rinda_ringserver
35
+ sleep 1
36
+
37
+ fill_slave_pool
38
+ rescue RuntimeError => e
39
+ kill_all_processes
40
+ raise e
41
+ end
42
+
43
+ def start_Rinda_ringserver
44
+ app_name = "#{Gem.ruby} ring_server.rb"
45
+ spawn_process(app_name)
46
+ end
47
+
48
+ def fill_slave_pool
49
+ Slave_Id_Range.each do |id|
50
+ start_slave(id)
51
+ end
52
+ puts " -- Starting to fill pool..."
53
+ puts " Wait until at least one slave is provided before running tests..."
54
+ puts " ** CTRL+BREAK to stop Spork and kill all ruby slave processes **"
55
+ $stdout.flush
56
+ end
57
+
58
+ def start_slave(id)
59
+ app_pwd = Dir.pwd # path running app in
60
+ app = "#{Gem.ruby} magazine_slave_provider.rb #{id} '#{app_pwd}' #{@test_framework.short_name}"
61
+ @pids[id] = spawn_process(app)
62
+ end
63
+
64
+ def spawn_process(app)
65
+
66
+ if RUBY_PLATFORM =~ /java/
67
+ # jruby 1.8 has no easy way to just spawn, so use a thread
68
+ Dir.chdir(@path) do
69
+ io = IO.popen app
70
+ Thread.new { puts io.read }
71
+ return io.pid
72
+ end
73
+ end
74
+
75
+ if RUBY_VERSION < '1.9.1'
76
+ Process.create( :app_name => app, :cwd => @path ).process_id
77
+ else
78
+ Process.spawn( app, :chdir => @path )
79
+ end
80
+ end
81
+
82
+ def self.available?
83
+ true
84
+ end
85
+
86
+ def run(argv, stderr, stdout)
87
+ DRb.start_service
88
+ ts = Rinda::RingFinger.primary
89
+ if ts.read_all([:name, :MagazineSlave, nil, nil]).size > 0
90
+ print ' <-- take tuple'; stdout.flush
91
+ tuple = ts.take([:name, :MagazineSlave, nil, nil])
92
+ slave = tuple[2]
93
+ id = tuple[3]
94
+
95
+ puts "(#{slave.id_num}); slave.run..."; $stdout.flush
96
+ begin
97
+ slave.run(argv,stderr,stdout)
98
+ puts " -- (#{slave.id_num});run done"; $stdout.flush
99
+ ensure
100
+ restart_slave(id)
101
+ end
102
+ else
103
+ puts '- NO tuple'; $stdout.flush
104
+ end
105
+ end
106
+
107
+ def restart_slave(id)
108
+ pid = @pids[id]
109
+ Process.kill(9, pid)
110
+ start_slave(id)
111
+ end
112
+
113
+ def windows?
114
+ ENV['OS'] == 'Windows_NT'
115
+ end
116
+
117
+ def kill_all_processes
118
+
119
+ @pids.each {|pid|
120
+ if windows?
121
+ system("taskkill /f /pid #{pid}")
122
+ else
123
+ Process.kill(9, pid)
124
+ end
125
+ }
126
+ puts "\nKilling processes."; $stdout.flush
127
+ end
128
+
129
+ def slave_count
130
+ DRb.start_service
131
+ ts = Rinda::RingFinger.primary
132
+ ts.read_all([:name, :MagazineSlave, nil, nil]).size
133
+ end
134
+
135
+
136
+ def abort
137
+ kill_all_processes
138
+ end
139
+
140
+ def preload
141
+ true
142
+ # @test_framework.preload
143
+ end
144
+
145
+ def running?
146
+ @running
147
+ end
148
+
149
+ def assert_ready!
150
+ end
151
+ 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,30 @@
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
+
23
+ # never expire, the renewer returns nil, which means expiration of *nix clock
24
+ renewer = Rinda::SimpleRenewer.new(nil)
25
+ Rinda::RingProvider.new(:MagazineSlave, magazine_slave, id, renewer).provide
26
+
27
+ puts " --> DRb magazine_slave_service: #{id} provided..."; $stdout.flush
28
+
29
+ # wait for the DRb service to finish before exiting
30
+ DRb.thread.join
@@ -0,0 +1,10 @@
1
+ # ring_server.rb
2
+ require 'rinda/ring'
3
+ require 'rinda/tuplespace'
4
+
5
+ DRb.start_service
6
+
7
+ Rinda::RingServer.new(Rinda::TupleSpace.new)
8
+ puts " -- Rinda Ring Server listening for connections...\n\n"
9
+ $stdout.flush
10
+ DRb.thread.join
@@ -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
+