diff options
author | Eric Wong <normalperson@yhbt.net> | 2009-02-13 00:08:32 -0800 |
---|---|---|
committer | Eric Wong <normalperson@yhbt.net> | 2009-02-13 00:08:32 -0800 |
commit | 0bd2bf7c438eb4394b7a66ca0999ab896beccf95 (patch) | |
tree | 3523688db5a7e1b9c48e89e083a0665506b8131d | |
parent | 3be825a50f0d9f7aa443e277a1b6c6bb667c1fb1 (diff) | |
download | unicorn-0bd2bf7c438eb4394b7a66ca0999ab896beccf95.tar.gz |
unused_port is more reliable as it actually tries to bind a port and retries if it fails. This is also safe across parallel unicorn tests running in different directories.
-rw-r--r-- | test/test_helper.rb | 44 | ||||
-rw-r--r-- | test/unit/test_server.rb | 2 |
2 files changed, 39 insertions, 7 deletions
diff --git a/test/test_helper.rb b/test/test_helper.rb index ae4f178..46fe2d1 100644 --- a/test/test_helper.rb +++ b/test/test_helper.rb @@ -21,6 +21,7 @@ require 'uri' require 'stringio' require 'pp' require 'unicorn' +require 'tmpdir' if ENV['DEBUG'] require 'ruby-debug' @@ -67,10 +68,41 @@ def hit(uris) return results end -# process_based_port provides a port number, usable for TCP and UDP -# connections based on $$ and with a 5000 as base. -# this is required if you perform several builds of unicorn in parallel -# (like continuous integration systems) -def process_based_port - 5000 + $$ % 1000 +# unused_port provides an unused port on +addr+ usable for TCP that is +# guaranteed to be unused across all unicorn builds on that system. It +# prevents race conditions by using a lock file other unicorn builds +# will see. This is required if you perform several builds in parallel +# with a continuous integration system or run tests in parallel via +# gmake. This is NOT guaranteed to be race-free if you run other +# processes that bind to random ports for testing (but the window +# for a race condition is very small). You may also set UNICORN_TEST_ADDR +# to override the default test address (127.0.0.1). +def unused_port(addr = '127.0.0.1') + retries = 100 + base = 5000 + port = sock = nil + begin + begin + port = base + rand(32768 - base) + sock = Socket.new(Socket::AF_INET, Socket::SOCK_STREAM, 0) + sock.bind(Socket.pack_sockaddr_in(port, addr)) + sock.listen(5) + rescue Errno::EADDRINUSE, Errno::EACCES + sock.close rescue nil + retry if (retries -= 1) >= 0 + end + + # since we'll end up closing the random port we just got, there's a race + # condition could allow the random port we just chose to reselect itself + # when running tests in parallel with gmake. Create a lock file while + # we have the port here to ensure that does not happen . + lock_path = "#{Dir::tmpdir}/unicorn_test.#{addr}:#{port}.lock" + lock = File.open(lock_path, File::WRONLY|File::CREAT|File::EXCL, 0600) + at_exit { File.unlink(lock_path) rescue nil } + rescue Errno::EEXIST + sock.close rescue nil + retry + end + sock.close rescue nil + port end diff --git a/test/unit/test_server.rb b/test/unit/test_server.rb index e9bd487..d19064c 100644 --- a/test/unit/test_server.rb +++ b/test/unit/test_server.rb @@ -21,7 +21,7 @@ class WebServerTest < Test::Unit::TestCase def setup @valid_request = "GET / HTTP/1.1\r\nHost: www.zedshaw.com\r\nContent-Type: text/plain\r\n\r\n" - @port = process_based_port + @port = unused_port @tester = TestHandler.new redirect_test_io do @server = HttpServer.new(@tester, :listeners => [ "127.0.0.1:#{@port}" ] ) |