From f769dd86c98856479fde6ac64ca039d49d991276 Mon Sep 17 00:00:00 2001 From: dblock Date: Tue, 15 Jan 2013 13:48:33 -0500 Subject: [PATCH] Added integration spec and JRuby support. --- Gemfile | 9 ++- guard-rack.gemspec | 5 +- lib/guard/rack/runner.rb | 119 +++++++++++++++-------------- spec/lib/guard/integration.ru | 11 +++ spec/lib/guard/integration_spec.rb | 22 ++++++ spec/lib/guard/runner_spec.rb | 18 ++--- spec/spec_helper.rb | 2 +- 7 files changed, 118 insertions(+), 68 deletions(-) create mode 100644 spec/lib/guard/integration.ru create mode 100644 spec/lib/guard/integration_spec.rb diff --git a/Gemfile b/Gemfile index af2fb82..c4ae281 100644 --- a/Gemfile +++ b/Gemfile @@ -1,7 +1,10 @@ source "http://rubygems.org" gem "guard", "~> 1.1" -gem "posix-spawn" + +platforms :ruby do + gem "posix-spawn" +end group :development, :test do gem "rake" @@ -13,6 +16,10 @@ group :development, :test do gem "mocha" end +group :test do + gem "rack" +end + require "rbconfig" if RbConfig::CONFIG["target_os"] =~ /darwin/i diff --git a/guard-rack.gemspec b/guard-rack.gemspec index a7a0b62..16162a7 100644 --- a/guard-rack.gemspec +++ b/guard-rack.gemspec @@ -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.authors = ["Daniel Doubrovkine"] - s.date = "2012-12-19" + s.date = "2013-01-15" s.email = "dblock@dblock.org" s.extra_rdoc_files = [ "LICENSE.md", @@ -33,6 +33,7 @@ Gem::Specification.new do |s| if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then s.add_runtime_dependency(%q, ["~> 1.1"]) + s.add_runtime_dependency(%q, [">= 0"]) if RUBY_PLATFORM == 'ruby' s.add_runtime_dependency(%q, [">= 0.3.9"]) s.add_runtime_dependency(%q, ["~> 1.0.3"]) s.add_development_dependency(%q, [">= 0"]) @@ -44,6 +45,7 @@ Gem::Specification.new do |s| s.add_development_dependency(%q, [">= 0"]) else s.add_dependency(%q, ["~> 1.1"]) + s.add_dependency(%q, [">= 0"]) if RUBY_PLATFORM == 'ruby' s.add_dependency(%q, [">= 0.3.9"]) s.add_dependency(%q, ["~> 1.0.3"]) s.add_dependency(%q, [">= 0"]) @@ -56,6 +58,7 @@ Gem::Specification.new do |s| end else s.add_dependency(%q, ["~> 1.1"]) + s.add_dependency(%q, [">= 0"]) if RUBY_PLATFORM == 'ruby' s.add_dependency(%q, [">= 0.3.9"]) s.add_dependency(%q, ["~> 1.0.3"]) s.add_dependency(%q, [">= 0"]) diff --git a/lib/guard/rack/runner.rb b/lib/guard/rack/runner.rb index 1721e3b..ac7fda8 100644 --- a/lib/guard/rack/runner.rb +++ b/lib/guard/rack/runner.rb @@ -1,41 +1,22 @@ require 'fileutils' require 'timeout' -require 'posix/spawn' module Guard class RackRunner + begin + require 'posix/spawn' + include POSIX::Spawn + rescue LoadError => e + # JRuby and possibly others + end + attr_reader :options, :pid def initialize(options) @options = options end - def kill(pid, force = false) - result = -1 - - UI.debug("Trying to kill Rack (PID #{pid})...") - if !force - Process.kill("INT", pid) - begin - Timeout.timeout(options[:timeout]) do - _, status = Process.wait2(pid) - result = status.exitstatus - UI.debug("Killed Rack (Exit status: #{result})") - end - rescue Timeout::Error - UI.debug("Couldn't kill Rack with INT, switching to TERM") - force = true - end - end - - if force - Process.kill("TERM", pid) - end - - result - end - def start kill_unmanaged_pid! if options[:force_run] @pid = run_rack_command! @@ -48,7 +29,7 @@ def stop exitstatus = kill(@pid) @pid = nil - if exitstatus != 0 + if exitstatus && exitstatus != 0 UI.info "Rackup exited with non-zero exit status (#{exitstatus}) whilst trying to stop." return false end @@ -60,42 +41,68 @@ def restart stop and start end - def build_rack_command - command = %w{rackup} - command.push( - options[:config], - '--env', options[:environment].to_s, - '--port', options[:port].to_s - ) + private - command << '--daemonize' if options[:daemon] - command << '--debug' if options[:debugger] - command.push("--server", options[:server].to_s) if options[:server] + def build_rack_command + command = %w{rackup} + command.push( + options[:config], + '--env', options[:environment].to_s, + '--port', options[:port].to_s + ) - command - end + command << '--daemonize' if options[:daemon] + command << '--debug' if options[:debugger] + command.push("--server", options[:server].to_s) if options[:server] - private + command + end - def run_rack_command! - command = build_rack_command - UI.debug("Running Rack with command: #{command.inspect}") - POSIX::Spawn.spawn(*command) - end + def kill(pid, force = false) + result = -1 + + UI.debug("Trying to kill Rack (PID #{pid})...") + if !force + Process.kill("INT", pid) + begin + Timeout.timeout(options[:timeout]) do + _, status = Process.wait2(pid) + result = status.exitstatus + UI.debug("Killed Rack (Exit status: #{result})") + end + rescue Timeout::Error + UI.debug("Couldn't kill Rack with INT, switching to TERM") + force = true + end + end - def kill_unmanaged_pid! - if pid = unmanaged_pid - kill(pid, true) + if force + Process.kill("TERM", pid) + end + + result + end + + def run_rack_command! + command = build_rack_command + UI.debug("Running Rack with command: #{command.inspect}") + spawn(*command) end - end - def unmanaged_pid - %x{lsof -n -i TCP:#{options[:port]}}.each_line { |line| - if line["*:#{options[:port]} "] - return line.split("\s")[1].to_i + def kill_unmanaged_pid! + if pid = unmanaged_pid + kill(pid, true) end - } - nil - end + end + + def unmanaged_pid + %x{lsof -n -i TCP:#{options[:port]}}.each_line { |line| + if line["*:#{options[:port]} "] + return line.split("\s")[1].to_i + end + } + nil + end + end end diff --git a/spec/lib/guard/integration.ru b/spec/lib/guard/integration.ru new file mode 100644 index 0000000..ce05c03 --- /dev/null +++ b/spec/lib/guard/integration.ru @@ -0,0 +1,11 @@ +class HelloWorld + def call(env) + return [ + 200, + {'Content-Type' => 'text/html'}, + ["Hello world!"] + ] + end +end + +run HelloWorld.new diff --git a/spec/lib/guard/integration_spec.rb b/spec/lib/guard/integration_spec.rb new file mode 100644 index 0000000..fae8d2d --- /dev/null +++ b/spec/lib/guard/integration_spec.rb @@ -0,0 +1,22 @@ +require 'spec_helper' +require 'guard/rack/runner' + +describe "Integration" do + let(:runner) { Guard::RackRunner.new(options) } + let(:options) { { :environment => 'development', :port => 3000, :config => 'spec/lib/guard/integration.ru' } } + + describe '#start' do + context 'run' do + it "should run" do + runner.start.should be_true + pid = runner.pid + pid.should_not be_nil + Process.getpgid(pid).should > 0 + runner.stop + expect { + Process.getpgid(pid) + }.to raise_error Errno::ESRCH + end + end + end +end diff --git a/spec/lib/guard/runner_spec.rb b/spec/lib/guard/runner_spec.rb index 0f0feb0..f748523 100644 --- a/spec/lib/guard/runner_spec.rb +++ b/spec/lib/guard/runner_spec.rb @@ -18,7 +18,7 @@ let(:pid) { 1234 } before do - POSIX::Spawn.stubs(:spawn).returns(pid) + runner.stubs(:spawn).returns(pid) runner.start end @@ -37,7 +37,7 @@ describe '#build_rack_command' do context 'no daemon' do it "should not have a daemon switch" do - runner.build_rack_command.should_not include('--daemonize') + runner.send(:build_rack_command).should_not include('--daemonize') end end @@ -45,7 +45,7 @@ let(:options) { default_options.merge(:daemon => true) } it "should have a daemon switch" do - runner.build_rack_command.should include('--daemonize') + runner.send(:build_rack_command).should include('--daemonize') end end @@ -53,7 +53,7 @@ let(:options) { default_options.merge(:debugger => true) } it "should have a debugger switch" do - runner.build_rack_command.should include('--debug') + runner.send(:build_rack_command).should include('--debug') end end @@ -61,7 +61,7 @@ let(:options) { default_options.merge(:server => "thin") } it "should honour server switch" do - command = runner.build_rack_command + command = runner.send(:build_rack_command) index = command.index('--server') index.should be >= 0 command[index + 1].should == 'thin' @@ -71,7 +71,7 @@ context "config file" do context "default" do it "should default to config.ru" do - runner.build_rack_command.should include('config.ru') + runner.send(:build_rack_command).should include('config.ru') end end @@ -79,7 +79,7 @@ let(:options) { default_options.merge(:config => 'config2.ru') } it "should honour config option" do options = default_options.merge(:config => 'config2.ru') - runner.build_rack_command.should include('config2.ru') + runner.send(:build_rack_command).should include('config2.ru') end end end @@ -91,7 +91,7 @@ let(:kill_expectation) { Process.expects(:kill).with("TERM", unmanaged_pid) } before do - POSIX::Spawn.expects(:spawn).once.returns(pid) + runner.expects(:spawn).once.returns(pid) runner.stubs(:unmanaged_pid).returns(unmanaged_pid) end @@ -127,7 +127,7 @@ let(:wait_stub) { Process.stubs(:wait2) } before do - POSIX::Spawn.stubs(:spawn).returns(pid) + runner.stubs(:spawn).returns(pid) runner.start Process.expects(:kill).with('INT', pid) diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index cb125e3..1ffd401 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -2,7 +2,7 @@ $LOAD_PATH.unshift(File.dirname(__FILE__)) require 'guard-rack' -require 'mocha' +require 'mocha_standalone' RSpec.configure do |c| c.mock_with :mocha