From 71c8ce651166b49178676f1c37723aa34c4ef9e5 Mon Sep 17 00:00:00 2001 From: Eric Wong Date: Tue, 31 Mar 2009 19:15:50 -0700 Subject: test: factor out exec helpers into common code for Rails tests --- test/exec/test_exec.rb | 148 ++-------------------------------------------- test/rails/test_rails.rb | 10 +--- test/test_helper.rb | 151 ++++++++++++++++++++++++++++++++++++++++++++++- 3 files changed, 155 insertions(+), 154 deletions(-) (limited to 'test') diff --git a/test/exec/test_exec.rb b/test/exec/test_exec.rb index 2d0528f..d7c8206 100644 --- a/test/exec/test_exec.rb +++ b/test/exec/test_exec.rb @@ -1,28 +1,19 @@ # Copyright (c) 2009 Eric Wong -STDIN.sync = STDOUT.sync = STDERR.sync = true require 'test/test_helper' -require 'pathname' -require 'tempfile' -require 'fileutils' do_test = true -DEFAULT_TRIES = 1000 -DEFAULT_RES = 0.2 - $unicorn_bin = ENV['UNICORN_TEST_BIN'] || "unicorn" redirect_test_io do do_test = system($unicorn_bin, '-v') end unless do_test - STDERR.puts "#{$unicorn_bin} not found in PATH=#{ENV['PATH']}, " \ - "skipping this test" + warn "#{$unicorn_bin} not found in PATH=#{ENV['PATH']}, " \ + "skipping this test" end -begin - require 'rack' -rescue LoadError - STDERR.puts "Unable to load Rack, skipping this test" +unless try_require('rack') + warn "Unable to load Rack, skipping this test" do_test = false end @@ -476,135 +467,4 @@ end reexec_usr2_quit_test(new_pid, pid_file) end - private - - # sometimes the server may not come up right away - def retry_hit(uris = []) - tries = DEFAULT_TRIES - begin - hit(uris) - rescue Errno::ECONNREFUSED => err - if (tries -= 1) > 0 - sleep DEFAULT_RES - retry - end - raise err - end - end - - def assert_shutdown(pid) - wait_master_ready("#{@tmpdir}/test_stderr.#{pid}.log") - assert_nothing_raised { Process.kill(:QUIT, pid) } - status = nil - assert_nothing_raised { pid, status = Process.waitpid2(pid) } - assert status.success?, "exited successfully" - end - - def wait_workers_ready(path, nr_workers) - tries = DEFAULT_TRIES - lines = [] - while (tries -= 1) > 0 - begin - lines = File.readlines(path).grep(/worker=\d+ ready/) - lines.size == nr_workers and return - rescue Errno::ENOENT - end - sleep DEFAULT_RES - end - raise "#{nr_workers} workers never became ready:" \ - "\n\t#{lines.join("\n\t")}\n" - end - - def wait_master_ready(master_log) - tries = DEFAULT_TRIES - while (tries -= 1) > 0 - begin - File.readlines(master_log).grep(/master process ready/)[0] and return - rescue Errno::ENOENT - end - sleep DEFAULT_RES - end - raise "master process never became ready" - end - - def reexec_usr2_quit_test(pid, pid_file) - assert File.exist?(pid_file), "pid file OK" - assert ! File.exist?("#{pid_file}.oldbin"), "oldbin pid file" - assert_nothing_raised { Process.kill(:USR2, pid) } - assert_nothing_raised { retry_hit(["http://#{@addr}:#{@port}/"]) } - wait_for_file("#{pid_file}.oldbin") - wait_for_file(pid_file) - - old_pid = File.read("#{pid_file}.oldbin").to_i - new_pid = File.read(pid_file).to_i - - # kill old master process - assert_not_equal pid, new_pid - assert_equal pid, old_pid - assert_nothing_raised { Process.kill(:QUIT, old_pid) } - assert_nothing_raised { retry_hit(["http://#{@addr}:#{@port}/"]) } - wait_for_death(old_pid) - assert_equal new_pid, File.read(pid_file).to_i - assert_nothing_raised { retry_hit(["http://#{@addr}:#{@port}/"]) } - assert_nothing_raised { Process.kill(:QUIT, new_pid) } - end - - def reexec_basic_test(pid, pid_file) - results = retry_hit(["http://#{@addr}:#{@port}/"]) - assert_equal String, results[0].class - assert_nothing_raised { Process.kill(0, pid) } - master_log = "#{@tmpdir}/test_stderr.#{pid}.log" - wait_master_ready(master_log) - File.truncate(master_log, 0) - nr = 50 - kill_point = 2 - assert_nothing_raised do - nr.times do |i| - hit(["http://#{@addr}:#{@port}/#{i}"]) - i == kill_point and Process.kill(:HUP, pid) - end - end - wait_master_ready(master_log) - assert File.exist?(pid_file), "pid=#{pid_file} exists" - new_pid = File.read(pid_file).to_i - assert_not_equal pid, new_pid - assert_nothing_raised { Process.kill(0, new_pid) } - assert_nothing_raised { Process.kill(:QUIT, new_pid) } - end - - def wait_for_file(path) - tries = DEFAULT_TRIES - while (tries -= 1) > 0 && ! File.exist?(path) - sleep DEFAULT_RES - end - assert File.exist?(path), "path=#{path} exists #{caller.inspect}" - end - - def xfork(&block) - fork do - ObjectSpace.each_object(Tempfile) do |tmp| - ObjectSpace.undefine_finalizer(tmp) - end - yield - end - end - - # can't waitpid on detached processes - def wait_for_death(pid) - tries = DEFAULT_TRIES - while (tries -= 1) > 0 - begin - Process.kill(0, pid) - begin - Process.waitpid(pid, Process::WNOHANG) - rescue Errno::ECHILD - end - sleep(DEFAULT_RES) - rescue Errno::ESRCH - return - end - end - raise "PID:#{pid} never died!" - end - end if do_test diff --git a/test/rails/test_rails.rb b/test/rails/test_rails.rb index aa0d300..2815734 100644 --- a/test/rails/test_rails.rb +++ b/test/rails/test_rails.rb @@ -1,14 +1,8 @@ # Copyright (c) 2009 Eric Wong -STDIN.sync = STDOUT.sync = STDERR.sync = true require 'test/test_helper' -require 'pathname' -require 'tempfile' -require 'fileutils' # don't call exit(0) since it may be run under rake (but gmake is recommended) do_test = true -DEFAULT_TRIES = 1000 -DEFAULT_RES = 0.2 $unicorn_rails_bin = ENV['UNICORN_RAILS_TEST_BIN'] || "unicorn_rails" redirect_test_io { do_test = system($unicorn_rails_bin, '-v') } @@ -92,7 +86,7 @@ logger Logger.new('#{COMMON_TMP.path}') redirect_test_io do pid = fork { exec 'unicorn_rails', "-l#@addr:#@port" } end - sleep 1 # HACK + wait_master_ready("test_stderr.#$$.log") tmp_dirs.each { |dir| assert(File.directory?("tmp/#{dir}")) } res = Net::HTTP.get_response(URI.parse("http://#@addr:#@port/foo")) assert_equal "FOO\n", res.body @@ -111,7 +105,7 @@ logger Logger.new('#{COMMON_TMP.path}') redirect_test_io do pid = fork { exec 'unicorn_rails', "-l#@addr:#@port", '-P/poo' } end - sleep 1 # HACK + wait_master_ready("test_stderr.#$$.log") res = Net::HTTP.get_response(URI.parse("http://#@addr:#@port/poo/foo")) # p res # p res.body diff --git a/test/test_helper.rb b/test/test_helper.rb index 4243606..37625ec 100644 --- a/test/test_helper.rb +++ b/test/test_helper.rb @@ -4,9 +4,16 @@ # Additional work donated by contributors. See http://mongrel.rubyforge.org/attributions.html # for more information. +STDIN.sync = STDOUT.sync = STDERR.sync = true # buffering makes debugging hard + +# Some tests watch a log file or a pid file to spring up to check state +# Can't rely on inotify on non-Linux and logging to a pipe makes things +# more complicated +DEFAULT_TRIES = 1000 +DEFAULT_RES = 0.2 HERE = File.dirname(__FILE__) unless defined?(HERE) -%w(lib ext bin test).each do |dir| +%w(lib ext).each do |dir| $LOAD_PATH.unshift "#{HERE}/../#{dir}" end @@ -15,8 +22,10 @@ require 'net/http' require 'digest/sha1' require 'uri' require 'stringio' +require 'pathname' +require 'tempfile' +require 'fileutils' require 'unicorn' -require 'tmpdir' if ENV['DEBUG'] require 'ruby-debug' @@ -112,3 +121,141 @@ def unused_port(addr = '127.0.0.1') sock.close rescue nil port end + +def try_require(lib) + begin + require lib + true + rescue LoadError + false + end +end + +# sometimes the server may not come up right away +def retry_hit(uris = []) + tries = DEFAULT_TRIES + begin + hit(uris) + rescue Errno::ECONNREFUSED => err + if (tries -= 1) > 0 + sleep DEFAULT_RES + retry + end + raise err + end +end + +def assert_shutdown(pid) + wait_master_ready("test_stderr.#{pid}.log") + assert_nothing_raised { Process.kill(:QUIT, pid) } + status = nil + assert_nothing_raised { pid, status = Process.waitpid2(pid) } + assert status.success?, "exited successfully" +end + +def wait_workers_ready(path, nr_workers) + tries = DEFAULT_TRIES + lines = [] + while (tries -= 1) > 0 + begin + lines = File.readlines(path).grep(/worker=\d+ ready/) + lines.size == nr_workers and return + rescue Errno::ENOENT + end + sleep DEFAULT_RES + end + raise "#{nr_workers} workers never became ready:" \ + "\n\t#{lines.join("\n\t")}\n" +end + +def wait_master_ready(master_log) + tries = DEFAULT_TRIES + while (tries -= 1) > 0 + begin + File.readlines(master_log).grep(/master process ready/)[0] and return + rescue Errno::ENOENT + end + sleep DEFAULT_RES + end + raise "master process never became ready" +end + +def reexec_usr2_quit_test(pid, pid_file) + assert File.exist?(pid_file), "pid file OK" + assert ! File.exist?("#{pid_file}.oldbin"), "oldbin pid file" + assert_nothing_raised { Process.kill(:USR2, pid) } + assert_nothing_raised { retry_hit(["http://#{@addr}:#{@port}/"]) } + wait_for_file("#{pid_file}.oldbin") + wait_for_file(pid_file) + + old_pid = File.read("#{pid_file}.oldbin").to_i + new_pid = File.read(pid_file).to_i + + # kill old master process + assert_not_equal pid, new_pid + assert_equal pid, old_pid + assert_nothing_raised { Process.kill(:QUIT, old_pid) } + assert_nothing_raised { retry_hit(["http://#{@addr}:#{@port}/"]) } + wait_for_death(old_pid) + assert_equal new_pid, File.read(pid_file).to_i + assert_nothing_raised { retry_hit(["http://#{@addr}:#{@port}/"]) } + assert_nothing_raised { Process.kill(:QUIT, new_pid) } +end + +def reexec_basic_test(pid, pid_file) + results = retry_hit(["http://#{@addr}:#{@port}/"]) + assert_equal String, results[0].class + assert_nothing_raised { Process.kill(0, pid) } + master_log = "#{@tmpdir}/test_stderr.#{pid}.log" + wait_master_ready(master_log) + File.truncate(master_log, 0) + nr = 50 + kill_point = 2 + assert_nothing_raised do + nr.times do |i| + hit(["http://#{@addr}:#{@port}/#{i}"]) + i == kill_point and Process.kill(:HUP, pid) + end + end + wait_master_ready(master_log) + assert File.exist?(pid_file), "pid=#{pid_file} exists" + new_pid = File.read(pid_file).to_i + assert_not_equal pid, new_pid + assert_nothing_raised { Process.kill(0, new_pid) } + assert_nothing_raised { Process.kill(:QUIT, new_pid) } +end + +def wait_for_file(path) + tries = DEFAULT_TRIES + while (tries -= 1) > 0 && ! File.exist?(path) + sleep DEFAULT_RES + end + assert File.exist?(path), "path=#{path} exists #{caller.inspect}" +end + +def xfork(&block) + fork do + ObjectSpace.each_object(Tempfile) do |tmp| + ObjectSpace.undefine_finalizer(tmp) + end + yield + end +end + +# can't waitpid on detached processes +def wait_for_death(pid) + tries = DEFAULT_TRIES + while (tries -= 1) > 0 + begin + Process.kill(0, pid) + begin + Process.waitpid(pid, Process::WNOHANG) + rescue Errno::ECHILD + end + sleep(DEFAULT_RES) + rescue Errno::ESRCH + return + end + end + raise "PID:#{pid} never died!" +end -- cgit v1.2.3-24-ge0c7