1 # -*- encoding: binary -*-
3 # Copyright (c) 2005 Zed A. Shaw
4 # You can redistribute it and/or modify it under the same terms as Ruby 1.8 or
5 # the GPLv2+ (GPLv3+ preferred)
7 # Additional work donated by contributors. See git history
8 # for more information.
10 STDIN.sync = STDOUT.sync = STDERR.sync = true # buffering makes debugging hard
12 # FIXME: move curl-dependent tests into t/
13 ENV['NO_PROXY'] ||= ENV['UNICORN_TEST_ADDR'] || '127.0.0.1'
15 # Some tests watch a log file or a pid file to spring up to check state
16 # Can't rely on inotify on non-Linux and logging to a pipe makes things
38 unless RUBY_VERSION < '3.1'
39 warn "Unicorn was only tested against MRI up to 3.0.\n" \
40 "It might not properly work with #{RUBY_VERSION}"
47 new_out = File.open("test_stdout.#$$.log", "a")
48 new_err = File.open("test_stderr.#$$.log", "a")
49 new_out.sync = new_err.sync = true
51 if tail = ENV['TAIL'] # "tail -F" if GNU, "tail -f" otherwise
56 pid = Process.spawn(*cmd, { 1 => 2, :pgroup => true })
57 sleep 0.1 # wait for tail(1) to startup
59 STDERR.reopen(new_err)
60 STDOUT.reopen(new_out)
61 STDERR.sync = STDOUT.sync = true
65 File.unlink(new_out.path) rescue nil
66 File.unlink(new_err.path) rescue nil
73 STDERR.reopen(orig_err)
74 STDOUT.reopen(orig_out)
75 Process.kill(:TERM, pid) if pid
79 # which(1) exit codes cannot be trusted on some systems
80 # We use UNIX shell utilities in some tests because we don't trust
81 # ourselves to write Ruby 100% correctly :)
83 ex = ENV['PATH'].split(/:/).detect do |x|
86 end or warn "`#{bin}' not found in PATH=#{ENV['PATH']}"
90 # Either takes a string to do a get request against, or a tuple of [URI, HTTP] where
91 # HTTP is some kind of Net::HTTP request object (POST, HEAD, etc.)
98 u = 'http://127.0.0.1:8080/' if u == 'http://0.0.0.0:8080/'
99 res = Net::HTTP.get(URI.parse(u))
101 url = URI.parse(u[0])
102 res = Net::HTTP.new(url.host, url.port).start {|h| h.request(u[1]) }
105 assert res != nil, "Didn't get a response: #{u}"
112 # unused_port provides an unused port on +addr+ usable for TCP that is
113 # guaranteed to be unused across all unicorn builds on that system. It
114 # prevents race conditions by using a lock file other unicorn builds
115 # will see. This is required if you perform several builds in parallel
116 # with a continuous integration system or run tests in parallel via
117 # gmake. This is NOT guaranteed to be race-free if you run other
118 # processes that bind to random ports for testing (but the window
119 # for a race condition is very small). You may also set UNICORN_TEST_ADDR
120 # to override the default test address (127.0.0.1).
121 def unused_port(addr = '127.0.0.1')
127 port = base + rand(32768 - base)
128 while port == Unicorn::Const::DEFAULT_PORT
129 port = base + rand(32768 - base)
132 sock = Socket.new(Socket::AF_INET, Socket::SOCK_STREAM, 0)
133 sock.bind(Socket.pack_sockaddr_in(port, addr))
135 rescue Errno::EADDRINUSE, Errno::EACCES
136 sock.close rescue nil
137 retry if (retries -= 1) >= 0
140 # since we'll end up closing the random port we just got, there's a race
141 # condition could allow the random port we just chose to reselect itself
142 # when running tests in parallel with gmake. Create a lock file while
143 # we have the port here to ensure that does not happen .
144 lock_path = "#{Dir::tmpdir}/unicorn_test.#{addr}:#{port}.lock"
145 File.open(lock_path, File::WRONLY|File::CREAT|File::EXCL, 0600).close
146 at_exit { File.unlink(lock_path) rescue nil }
148 sock.close rescue nil
151 sock.close rescue nil
164 # sometimes the server may not come up right away
165 def retry_hit(uris = [])
166 tries = DEFAULT_TRIES
169 rescue Errno::EINVAL, Errno::ECONNREFUSED => err
178 def assert_shutdown(pid)
179 wait_master_ready("test_stderr.#{pid}.log")
180 Process.kill(:QUIT, pid)
181 pid, status = Process.waitpid2(pid)
182 assert status.success?, "exited successfully"
185 def wait_workers_ready(path, nr_workers)
186 tries = DEFAULT_TRIES
188 while (tries -= 1) > 0
190 lines = File.readlines(path).grep(/worker=\d+ ready/)
191 lines.size == nr_workers and return
196 raise "#{nr_workers} workers never became ready:" \
197 "\n\t#{lines.join("\n\t")}\n"
200 def wait_master_ready(master_log)
201 tries = DEFAULT_TRIES
202 while (tries -= 1) > 0
204 File.readlines(master_log).grep(/master process ready/)[0] and return
209 raise "master process never became ready"
212 def reexec_usr2_quit_test(pid, pid_file)
213 assert File.exist?(pid_file), "pid file OK"
214 assert ! File.exist?("#{pid_file}.oldbin"), "oldbin pid file"
215 Process.kill(:USR2, pid)
216 retry_hit(["http://#{@addr}:#{@port}/"])
217 wait_for_file("#{pid_file}.oldbin")
218 wait_for_file(pid_file)
220 old_pid = File.read("#{pid_file}.oldbin").to_i
221 new_pid = File.read(pid_file).to_i
223 # kill old master process
224 assert_not_equal pid, new_pid
225 assert_equal pid, old_pid
226 Process.kill(:QUIT, old_pid)
227 retry_hit(["http://#{@addr}:#{@port}/"])
228 wait_for_death(old_pid)
229 assert_equal new_pid, File.read(pid_file).to_i
230 retry_hit(["http://#{@addr}:#{@port}/"])
231 Process.kill(:QUIT, new_pid)
234 def reexec_basic_test(pid, pid_file)
235 results = retry_hit(["http://#{@addr}:#{@port}/"])
236 assert_equal String, results[0].class
238 master_log = "#{@tmpdir}/test_stderr.#{pid}.log"
239 wait_master_ready(master_log)
240 File.truncate(master_log, 0)
244 hit(["http://#{@addr}:#{@port}/#{i}"])
245 i == kill_point and Process.kill(:HUP, pid)
247 wait_master_ready(master_log)
248 assert File.exist?(pid_file), "pid=#{pid_file} exists"
249 new_pid = File.read(pid_file).to_i
250 assert_not_equal pid, new_pid
251 Process.kill(0, new_pid)
252 Process.kill(:QUIT, new_pid)
255 def wait_for_file(path)
256 tries = DEFAULT_TRIES
257 while (tries -= 1) > 0 && ! File.exist?(path)
260 assert File.exist?(path), "path=#{path} exists #{caller.inspect}"
265 ObjectSpace.each_object(Tempfile) do |tmp|
266 ObjectSpace.undefine_finalizer(tmp)
272 # can't waitpid on detached processes
273 def wait_for_death(pid)
274 tries = DEFAULT_TRIES
275 while (tries -= 1) > 0
279 Process.waitpid(pid, Process::WNOHANG)
287 raise "PID:#{pid} never died!"
290 def reset_sig_handlers
291 %w(WINCH QUIT INT TERM USR1 USR2 HUP TTIN TTOU CHLD).each do |sig|
296 def tcp_socket(*args)
297 sock = TCPSocket.new(*args)
298 sock.nonblock = false
302 def unix_socket(*args)
303 sock = UNIXSocket.new(*args)
304 sock.nonblock = false