Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP

Comparing changes

Choose two branches to see what's changed or to start a new pull request. If you need to, you can also compare across forks.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also compare across forks.
  • 15 commits
  • 10 files changed
  • 0 commit comments
  • 2 contributors
Commits on Jan 21, 2012
@trym trym Use process group to kill child processes
With the previous implementation, you would end up
with zombie processes e.g. a firefox instance
launched by capybara.
077c0cd
@trym trym Execute config/testbot/before_run.rb if present
For slow rails environments, using a ruby file
directly can speed things up.
c5370eb
Commits on Jan 23, 2012
@trym trym Make testbot.rake optional 494a851
@trym trym Remove use of deprecated Gem.available? 7df743d
@trym trym Update sinatra dependency 0d6b219
@trym trym Only bundle once 304831a
@trym trym Delete excluded files when syncing 65316ef
@trym trym Kill potential zombie child processes for job ce36491
Commits on Apr 27, 2012
@joakimk Catching exit status in the right place so that it works. 43f17a9
@joakimk This locked up my entire tmux window :D
Revert "Always delete the build when the requester exists."

This reverts commit eca76b0.
81e38a7
@joakimk The tmux error is because of something else, re-adding at_exit so it …
…sends the delete if the requester process is killed.
4f706c5
Commits on Apr 29, 2012
@joakimk Cleaning up a bit. 30a2450
@joakimk Merge branch 'master' into skalar_fixes
Conflicts:
	lib/requester/requester.rb
	lib/runner/job.rb
	testbot.gemspec
9bcf375
@joakimk Updating and adapting new process code to the latest master. Using th…
…e posix-spawn gem to get the same process API for ruby 1.8.7.
e17202b
@joakimk Always delete the build when the requester exists. eca76b0
View
18 Gemfile.lock
@@ -1,35 +1,37 @@
PATH
remote: .
specs:
- testbot (0.6.3)
+ testbot (0.6.5)
acts_as_rails3_generator
daemons (>= 1.0.10)
httparty (>= 0.6.1)
json_pure (>= 1.4.6)
macaddr (>= 1.0.0)
net-ssh (>= 2.0.23)
+ posix-spawn (>= 0.3.6)
sinatra (>= 1.0.0)
GEM
remote: http://rubygems.org/
specs:
acts_as_rails3_generator (0.0.1)
- daemons (1.1.6)
+ daemons (1.1.8)
flexmock (0.8.11)
guard (0.4.2)
thor (~> 0.14.6)
guard-test (0.3.0)
guard (>= 0.2.2)
test-unit (~> 2.2)
- httparty (0.8.1)
- multi_json
+ httparty (0.8.3)
+ multi_json (~> 1.0)
multi_xml
- json_pure (1.6.5)
+ json_pure (1.7.0)
macaddr (1.5.0)
systemu (>= 2.4.0)
- multi_json (1.0.4)
- multi_xml (0.4.1)
+ multi_json (1.3.4)
+ multi_xml (0.4.4)
net-ssh (2.3.0)
+ posix-spawn (0.3.6)
rack (1.2.1)
rack-test (0.5.6)
rack (>= 1.0)
@@ -39,7 +41,7 @@ GEM
sinatra (1.2.8)
rack (~> 1.1)
tilt (>= 1.2.2, < 2.0)
- systemu (2.4.2)
+ systemu (2.5.0)
test-unit (2.3.0)
thor (0.14.6)
tilt (1.3.3)
View
12 lib/requester/requester.rb
@@ -43,7 +43,8 @@ def run_tests(adapter, dir)
log "Syncing files" do
rsync_ignores = config.rsync_ignores.to_s.split.map { |pattern| "--exclude='#{pattern}'" }.join(' ')
- system "rsync -az --delete -e ssh #{rsync_ignores} . #{rsync_uri}"
+ # todo: exit when this fails
+ system "rsync -az --delete --delete-excluded -e ssh #{rsync_ignores} . #{rsync_uri}"
end
files = adapter.test_files(dir)
@@ -70,7 +71,13 @@ def run_tests(adapter, dir)
end
end
- trap("SIGINT") { HTTParty.delete("#{server_uri}/builds/#{build_id}"); return false }
+ at_exit do
+ unless ENV['IN_TEST'] || @done
+ log "Notifying server we want to stop the run" do
+ HTTParty.delete("#{server_uri}/builds/#{build_id}")
+ end
+ end
+ end
puts if config.logging
@@ -114,6 +121,7 @@ def run_tests(adapter, dir)
puts "\n" + adapter.sum_results(@build['results'])
end
+ @done = true
@build["success"]
end
View
43 lib/runner/job.rb
@@ -1,5 +1,6 @@
require File.expand_path(File.join(File.dirname(__FILE__), 'runner.rb'))
require File.expand_path(File.join(File.dirname(__FILE__), 'safe_result_text.rb'))
+require 'posix/spawn'
module Testbot::Runner
class Job
@@ -8,6 +9,7 @@ class Job
def initialize(runner, id, build_id, project, root, type, ruby_interpreter, files)
@runner, @id, @build_id, @project, @root, @type, @ruby_interpreter, @files =
runner, id, build_id, project, root, type, ruby_interpreter, files
+ @success = true
end
def jruby?
@@ -31,15 +33,19 @@ def run(instance)
end
def kill!(build_id)
- if @build_id == build_id && @test_process
- # The child process that runs the tests is a shell, we need to kill it's child process
- system("pkill -KILL -P #{@test_process.pid}")
+ if @build_id == build_id && @pid
+ kill_processes
@killed = true
end
end
private
+ def kill_processes
+ # Kill process and its children (processes in the same group)
+ Process.kill('KILL', -@pid) rescue :failed_to_kill_process
+ end
+
def status
success? ? "successful" : "failed"
end
@@ -55,23 +61,40 @@ def post_results(output)
end
def run_and_return_result(command)
- @test_process = open("|#{command} 2>&1", 'r')
+ read_pipe = spawn_process(command)
+
output = ""
- t = Time.now
- while char = @test_process.getc
+ last_post_time = Time.now
+ while char = read_pipe.getc
char = (char.is_a?(Fixnum) ? char.chr : char) # 1.8 <-> 1.9
output << char
- if Time.now - t > 0.5
+ if Time.now - last_post_time > 0.5
post_results(output)
- t = Time.now
+ last_post_time = Time.now
end
end
- @test_process.close
+
+ # Kill child processes, if any
+ kill_processes
+
output
end
+ def spawn_process(command)
+ read_pipe, write_pipe = IO.pipe
+ @pid = POSIX::Spawn::spawn(command, :err => write_pipe, :out => write_pipe, :pgroup => true)
+
+ Thread.new do
+ Process.waitpid(@pid)
+ @success = ($?.exitstatus == 0)
+ write_pipe.close
+ end
+
+ read_pipe
+ end
+
def success?
- $?.exitstatus == 0
+ @success
end
def ruby_cmd
View
14 lib/runner/runner.rb
@@ -104,7 +104,7 @@ def wait_for_jobs
job = Job.new(*([ self, next_job.split(',') ].flatten))
if first_job_from_build?
fetch_code(job)
- before_run(job) if File.exists?("#{job.project}/lib/tasks/testbot.rake")
+ before_run(job)
end
@last_build_id = job.build_id
@@ -123,12 +123,18 @@ def max_jruby_instances?
end
def fetch_code(job)
- system "rsync -az --delete -e ssh #{job.root}/ #{job.project}"
+ system "rsync -az --delete --delete-excluded -e ssh #{job.root}/ #{job.project}"
end
def before_run(job)
- bundler_cmd = RubyEnv.bundler?(job.project) ? "bundle; bundle exec" : ""
- system "export RAILS_ENV=test; export TEST_INSTANCES=#{@config.max_instances}; cd #{job.project}; #{bundler_cmd} rake testbot:before_run"
+ using_bundler = RubyEnv.bundler?(job.project)
+ bundler_cmd = using_bundler ? "bundle exec" : ""
+ command_prefix = "cd #{job.project}; RAILS_ENV=test TEST_INSTANCES=#{@config.max_instances} #{bundler_cmd}"
+
+ # todo: exit if this fails, report back, etc.
+ system("cd #{job.project}; bundle") if using_bundler
+ system "#{command_prefix} rake testbot:before_run" if File.exists?("#{job.project}/lib/tasks/testbot.rake")
+ system "#{command_prefix} ruby config/testbot/before_run.rb" if File.exists?("#{job.project}/config/testbot/before_run.rb")
end
def first_job_from_build?
View
2  lib/shared/adapters/helpers/ruby_env.rb
@@ -1,7 +1,7 @@
class RubyEnv
def self.bundler?(project_path)
- Gem.available?("bundler") && File.exists?("#{project_path}/Gemfile")
+ Gem::Specification.find_by_name("bundler") && File.exists?("#{project_path}/Gemfile") rescue false
end
def self.ruby_command(project_path, opts = {})
View
2  lib/tasks/testbot.rake
@@ -3,7 +3,7 @@ require File.dirname(__FILE__) + '/../shared/adapters/adapter'
namespace :testbot do
def run_and_show_results(adapter, custom_path)
- Rake::Task["testbot:before_request"].invoke
+ 'testbot:before_request'.tap { |t| Rake::Task.task_defined?(t) && Rake::Task[t].invoke }
require File.expand_path(File.join(File.dirname(__FILE__), '..', 'requester', 'requester.rb'))
requester = Testbot::Requester::Requester.create_by_config("#{Rails.root}/config/testbot.yml")
View
5 test/requester/requester_test.rb
@@ -41,6 +41,7 @@ def build_with_result(results)
def setup
ENV['USE_JRUBY'] = nil
+ ENV['IN_TEST'] = 'true'
end
def mock_file_sizes
@@ -233,7 +234,7 @@ def fixture_path(local_path)
flexmock(HTTParty).should_receive(:get).once.with("http://192.168.1.100:#{Testbot::SERVER_PORT}/builds/5",
:format => :json).and_return({ "done" => true, "results" => "" })
- flexmock(requester).should_receive('system').with("rsync -az --delete -e ssh --exclude='.git' --exclude='tmp' . testbot@192.168.1.100:/path")
+ flexmock(requester).should_receive('system').with("rsync -az --delete --delete-excluded -e ssh --exclude='.git' --exclude='tmp' . testbot@192.168.1.100:/path")
mock_file_sizes
requester.run_tests(RspecAdapter, 'spec')
@@ -336,7 +337,7 @@ def fixture_path(local_path)
flexmock(requester).should_receive(:print)
flexmock(requester).should_receive(:puts)
- flexmock(requester).should_receive('system').with("rsync -az --delete -e ssh . cruise@somewhere:/tmp/testbot/foo")
+ flexmock(requester).should_receive('system').with("rsync -az --delete --delete-excluded -e ssh . cruise@somewhere:/tmp/testbot/foo")
mock_file_sizes
requester.run_tests(RspecAdapter, 'spec')
View
30 test/runner/runner_test.rb
@@ -12,10 +12,38 @@ class RunnerTest < Test::Unit::TestCase
flexmock(RubyEnv).should_receive(:bundler?).with("/path").returns(true)
runner = Runner.new({:max_instances => 1})
+ flexmock(File).should_receive(:exists?).with("/path/lib/tasks/testbot.rake").and_return(true)
+ flexmock(File).should_receive(:exists?).with("/path/config/testbot/before_run.rb").and_return(false)
flexmock(runner)
- flexmock(runner).should_receive(:system).with("export RAILS_ENV=test; export TEST_INSTANCES=1; cd /path; bundle; bundle exec rake testbot:before_run").once
+ flexmock(runner).should_receive(:system).with("cd /path; bundle").once
+ flexmock(runner).should_receive(:system).with("cd /path; RAILS_ENV=test TEST_INSTANCES=1 bundle exec rake testbot:before_run").once
runner.send(:before_run, job)
end
+
+ should "be able to use a plain ruby before_run file" do
+ job = flexmock(:job, :project => "/path")
+ flexmock(RubyEnv).should_receive(:bundler?).with("/path").returns(true)
+
+ runner = Runner.new({:max_instances => 1})
+ flexmock(File).should_receive(:exists?).with("/path/lib/tasks/testbot.rake").and_return(false)
+ flexmock(File).should_receive(:exists?).with("/path/config/testbot/before_run.rb").and_return(true)
+ flexmock(runner)
+ flexmock(runner).should_receive(:system).with("cd /path; bundle").once
+ flexmock(runner).should_receive(:system).with("cd /path; RAILS_ENV=test TEST_INSTANCES=1 bundle exec ruby config/testbot/before_run.rb").once
+ runner.send(:before_run, job)
+ end
+
+ should "be able to run without bundler" do
+ job = flexmock(:job, :project => "/path")
+ flexmock(RubyEnv).should_receive(:bundler?).with("/path").returns(false)
+
+ runner = Runner.new({:max_instances => 1})
+ flexmock(File).should_receive(:exists?).with("/path/lib/tasks/testbot.rake").and_return(false)
+ flexmock(File).should_receive(:exists?).with("/path/config/testbot/before_run.rb").and_return(true)
+ flexmock(runner)
+ flexmock(runner).should_receive(:system).with("cd /path; RAILS_ENV=test TEST_INSTANCES=1 ruby config/testbot/before_run.rb").once
+ runner.send(:before_run, job)
+ end
end
end
View
7 test/shared/adapters/helpers/ruby_env_test.rb
@@ -8,19 +8,19 @@ class RubyEnvTest < Test::Unit::TestCase
context "self.bundler?" do
should "return true if bundler is installed and there is a Gemfile" do
- flexmock(Gem).should_receive(:available?).with("bundler").once.and_return(true)
+ flexmock(Gem::Specification).should_receive(:find_by_name).with("bundler").once.and_return(true)
flexmock(File).should_receive(:exists?).with("path/to/project/Gemfile").once.and_return(true)
assert_equal true, RubyEnv.bundler?("path/to/project")
end
should "return false if bundler is installed but there is no Gemfile" do
- flexmock(Gem).should_receive(:available?).with("bundler").once.and_return(true)
+ flexmock(Gem::Specification).should_receive(:find_by_name).with("bundler").once.and_return(true)
flexmock(File).should_receive(:exists?).and_return(false)
assert_equal false, RubyEnv.bundler?("path/to/project")
end
should "return false if bundler is not installed" do
- flexmock(Gem).should_receive(:available?).with("bundler").once.and_return(false)
+ flexmock(Gem::Specification).should_receive(:find_by_name).with("bundler").once.and_return(false)
assert_equal false, RubyEnv.bundler?("path/to/project")
end
@@ -53,7 +53,6 @@ class RubyEnvTest < Test::Unit::TestCase
end
should "not look for a script when none is provided" do
- flexmock(File).should_receive(:exists?).once # Once for bundler check
assert_equal 'ruby -S rspec', RubyEnv.ruby_command("path/to/project", :bin => "rspec")
end
View
2  testbot.gemspec
@@ -13,6 +13,7 @@ Gem::Specification.new do |s|
s.executables = [ "testbot" ]
s.files = Dir.glob("lib/**/*") + Dir.glob("test/**/*") + %w(Gemfile .gemtest Rakefile testbot.gemspec CHANGELOG README.markdown bin/testbot) +
(File.exists?("DEV_VERSION") ? [ "DEV_VERSION" ] : [])
+
s.add_dependency('sinatra', '>=1.0.0')
s.add_dependency('httparty', '>= 0.6.1')
s.add_dependency('macaddr', '>= 1.0.0')
@@ -20,6 +21,7 @@ Gem::Specification.new do |s|
s.add_dependency('json_pure', '>= 1.4.6')
s.add_dependency('daemons', '>= 1.0.10')
s.add_dependency('acts_as_rails3_generator')
+ s.add_dependency('posix-spawn', '>= 0.3.6')
s.add_development_dependency("shoulda")
s.add_development_dependency("rack-test")

No commit comments for this range

Something went wrong with that request. Please try again.