HACKING: drop outdated information about pandoc
[unicorn.git] / test / test_helper.rb
blobd86f83b7e8444f7520be35bcd5aadc3e4f6b3d7a
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
17 # more complicated
18 DEFAULT_TRIES = 1000
19 DEFAULT_RES = 0.2
21 require 'test/unit'
22 require 'net/http'
23 require 'digest/sha1'
24 require 'uri'
25 require 'stringio'
26 require 'pathname'
27 require 'tempfile'
28 require 'fileutils'
29 require 'logger'
30 require 'unicorn'
31 require 'io/nonblock'
33 if ENV['DEBUG']
34   require 'ruby-debug'
35   Debugger.start
36 end
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}"
41 end
43 def redirect_test_io
44   orig_err = STDERR.dup
45   orig_out = STDOUT.dup
46   rdr_pid = $$
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
52     require 'shellwords'
53     cmd = tail.shellsplit
54     cmd << new_out.path
55     cmd << new_err.path
56     pid = Process.spawn(*cmd, { 1 => 2, :pgroup => true })
57     sleep 0.1 # wait for tail(1) to startup
58   end
59   STDERR.reopen(new_err)
60   STDOUT.reopen(new_out)
61   STDERR.sync = STDOUT.sync = true
63   at_exit do
64     if rdr_pid == $$
65       File.unlink(new_out.path) rescue nil
66       File.unlink(new_err.path) rescue nil
67     end
68   end
70   begin
71     yield
72   ensure
73     STDERR.reopen(orig_err)
74     STDOUT.reopen(orig_out)
75     Process.kill(:TERM, pid) if pid
76   end
77 end
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 :)
82 def which(bin)
83   ex = ENV['PATH'].split(/:/).detect do |x|
84     x << "/#{bin}"
85     File.executable?(x)
86   end or warn "`#{bin}' not found in PATH=#{ENV['PATH']}"
87   ex
88 end
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.)
92 def hit(uris)
93   results = []
94   uris.each do |u|
95     res = nil
97     if u.kind_of? String
98       u = 'http://127.0.0.1:8080/' if u == 'http://0.0.0.0:8080/'
99       res = Net::HTTP.get(URI.parse(u))
100     else
101       url = URI.parse(u[0])
102       res = Net::HTTP.new(url.host, url.port).start {|h| h.request(u[1]) }
103     end
105     assert res != nil, "Didn't get a response: #{u}"
106     results << res
107   end
109   return results
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')
122   retries = 100
123   base = 5000
124   port = sock = nil
125   begin
126     begin
127       port = base + rand(32768 - base)
128       while port == Unicorn::Const::DEFAULT_PORT
129         port = base + rand(32768 - base)
130       end
132       sock = Socket.new(Socket::AF_INET, Socket::SOCK_STREAM, 0)
133       sock.bind(Socket.pack_sockaddr_in(port, addr))
134       sock.listen(5)
135     rescue Errno::EADDRINUSE, Errno::EACCES
136       sock.close rescue nil
137       retry if (retries -= 1) >= 0
138     end
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 }
147   rescue Errno::EEXIST
148     sock.close rescue nil
149     retry
150   end
151   sock.close rescue nil
152   port
155 def try_require(lib)
156   begin
157     require lib
158     true
159   rescue LoadError
160     false
161   end
164 # sometimes the server may not come up right away
165 def retry_hit(uris = [])
166   tries = DEFAULT_TRIES
167   begin
168     hit(uris)
169   rescue Errno::EINVAL, Errno::ECONNREFUSED => err
170     if (tries -= 1) > 0
171       sleep DEFAULT_RES
172       retry
173     end
174     raise err
175   end
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
187   lines = []
188   while (tries -= 1) > 0
189     begin
190       lines = File.readlines(path).grep(/worker=\d+ ready/)
191       lines.size == nr_workers and return
192     rescue Errno::ENOENT
193     end
194     sleep DEFAULT_RES
195   end
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
203     begin
204       File.readlines(master_log).grep(/master process ready/)[0] and return
205     rescue Errno::ENOENT
206     end
207     sleep DEFAULT_RES
208   end
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
237   Process.kill(0, pid)
238   master_log = "#{@tmpdir}/test_stderr.#{pid}.log"
239   wait_master_ready(master_log)
240   File.truncate(master_log, 0)
241   nr = 50
242   kill_point = 2
243   nr.times do |i|
244     hit(["http://#{@addr}:#{@port}/#{i}"])
245     i == kill_point and Process.kill(:HUP, pid)
246   end
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)
258     sleep DEFAULT_RES
259   end
260   assert File.exist?(path), "path=#{path} exists #{caller.inspect}"
263 def xfork(&block)
264   fork do
265     ObjectSpace.each_object(Tempfile) do |tmp|
266       ObjectSpace.undefine_finalizer(tmp)
267     end
268     yield
269   end
272 # can't waitpid on detached processes
273 def wait_for_death(pid)
274   tries = DEFAULT_TRIES
275   while (tries -= 1) > 0
276     begin
277       Process.kill(0, pid)
278       begin
279         Process.waitpid(pid, Process::WNOHANG)
280       rescue Errno::ECHILD
281       end
282       sleep(DEFAULT_RES)
283     rescue Errno::ESRCH
284       return
285     end
286   end
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|
292     trap(sig, "DEFAULT")
293   end
296 def tcp_socket(*args)
297   sock = TCPSocket.new(*args)
298   sock.nonblock = false
299   sock
302 def unix_socket(*args)
303   sock = UNIXSocket.new(*args)
304   sock.nonblock = false
305   sock