const: remove unused constants
[unicorn.git] / test / exec / test_exec.rb
blobcf7ef28af696a0dd831b7b102f2f5233bfc8c0b0
1 # Copyright (c) 2009 Eric Wong
2 require 'test/test_helper'
4 do_test = true
5 $unicorn_bin = ENV['UNICORN_TEST_BIN'] || "unicorn"
6 redirect_test_io do
7   do_test = system($unicorn_bin, '-v')
8 end
10 unless do_test
11   warn "#{$unicorn_bin} not found in PATH=#{ENV['PATH']}, " \
12        "skipping this test"
13 end
15 unless try_require('rack')
16   warn "Unable to load Rack, skipping this test"
17   do_test = false
18 end
20 class ExecTest < Test::Unit::TestCase
21   trap(:QUIT, 'IGNORE')
23   HI = <<-EOS
24 use Rack::ContentLength
25 run proc { |env| [ 200, { 'Content-Type' => 'text/plain' }, [ "HI\\n" ] ] }
26   EOS
28   HELLO = <<-EOS
29 class Hello
30   def call(env)
31     [ 200, { 'Content-Type' => 'text/plain' }, [ "HI\\n" ] ]
32   end
33 end
34   EOS
36   COMMON_TMP = Tempfile.new('unicorn_tmp') unless defined?(COMMON_TMP)
38   HEAVY_CFG = <<-EOS
39 worker_processes 4
40 timeout 30
41 logger Logger.new('#{COMMON_TMP.path}')
42 before_fork do |server, worker|
43   server.logger.info "before_fork: worker=\#{worker.nr}"
44 end
45   EOS
47   def setup
48     @pwd = Dir.pwd
49     @tmpfile = Tempfile.new('unicorn_exec_test')
50     @tmpdir = @tmpfile.path
51     @tmpfile.close!
52     Dir.mkdir(@tmpdir)
53     Dir.chdir(@tmpdir)
54     @addr = ENV['UNICORN_TEST_ADDR'] || '127.0.0.1'
55     @port = unused_port(@addr)
56     @sockets = []
57     @start_pid = $$
58   end
60   def teardown
61     return if @start_pid != $$
62     Dir.chdir(@pwd)
63     FileUtils.rmtree(@tmpdir)
64     @sockets.each { |path| File.unlink(path) rescue nil }
65     loop do
66       Process.kill('-QUIT', 0)
67       begin
68         Process.waitpid(-1, Process::WNOHANG) or break
69       rescue Errno::ECHILD
70         break
71       end
72     end
73   end
75   def test_exit_signals
76     %w(INT TERM QUIT).each do |sig|
77       File.open("config.ru", "wb") { |fp| fp.syswrite(HI) }
78       pid = xfork { redirect_test_io { exec($unicorn_bin, "-l#@addr:#@port") } }
79       wait_master_ready("test_stderr.#{pid}.log")
80       wait_workers_ready("test_stderr.#{pid}.log", 1)
81       status = nil
82       assert_nothing_raised do
83         Process.kill(sig, pid)
84         pid, status = Process.waitpid2(pid)
85       end
86       reaped = File.readlines("test_stderr.#{pid}.log").grep(/reaped/)
87       assert_equal 1, reaped.size
88       assert status.exited?
89     end
90   end
92   def test_basic
93     File.open("config.ru", "wb") { |fp| fp.syswrite(HI) }
94     pid = fork do
95       redirect_test_io { exec($unicorn_bin, "-l", "#{@addr}:#{@port}") }
96     end
97     results = retry_hit(["http://#{@addr}:#{@port}/"])
98     assert_equal String, results[0].class
99     assert_shutdown(pid)
100   end
102   def test_ttin_ttou
103     File.open("config.ru", "wb") { |fp| fp.syswrite(HI) }
104     pid = fork { redirect_test_io { exec($unicorn_bin, "-l#@addr:#@port") } }
105     log = "test_stderr.#{pid}.log"
106     wait_master_ready(log)
107     [ 2, 3].each { |i|
108       assert_nothing_raised { Process.kill(:TTIN, pid) }
109       wait_workers_ready(log, i)
110     }
111     File.truncate(log, 0)
112     reaped = nil
113     [ 2, 1, 0].each { |i|
114       assert_nothing_raised { Process.kill(:TTOU, pid) }
115       DEFAULT_TRIES.times {
116         sleep DEFAULT_RES
117         reaped = File.readlines(log).grep(/reaped.*\s*worker=#{i}$/)
118         break if reaped.size == 1
119       }
120       assert_equal 1, reaped.size
121     }
122   end
124   def test_help
125     redirect_test_io do
126       assert(system($unicorn_bin, "-h"), "help text returns true")
127     end
128     assert_equal 0, File.stat("test_stderr.#$$.log").size
129     assert_not_equal 0, File.stat("test_stdout.#$$.log").size
130     lines = File.readlines("test_stdout.#$$.log")
132     # Be considerate of the on-call technician working from their
133     # mobile phone or netbook on a slow connection :)
134     assert lines.size <= 24, "help height fits in an ANSI terminal window"
135     lines.each do |line|
136       assert line.size <= 80, "help width fits in an ANSI terminal window"
137     end
138   end
140   def test_broken_reexec_config
141     File.open("config.ru", "wb") { |fp| fp.syswrite(HI) }
142     pid_file = "#{@tmpdir}/test.pid"
143     old_file = "#{pid_file}.oldbin"
144     ucfg = Tempfile.new('unicorn_test_config')
145     ucfg.syswrite("listen %(#@addr:#@port)\n")
146     ucfg.syswrite("pid %(#{pid_file})\n")
147     ucfg.syswrite("logger Logger.new(%(#{@tmpdir}/log))\n")
148     pid = xfork do
149       redirect_test_io do
150         exec($unicorn_bin, "-D", "-l#{@addr}:#{@port}", "-c#{ucfg.path}")
151       end
152     end
153     results = retry_hit(["http://#{@addr}:#{@port}/"])
154     assert_equal String, results[0].class
156     wait_for_file(pid_file)
157     Process.waitpid(pid)
158     Process.kill(:USR2, File.read(pid_file).to_i)
159     wait_for_file(old_file)
160     wait_for_file(pid_file)
161     old_pid = File.read(old_file).to_i
162     Process.kill(:QUIT, old_pid)
163     wait_for_death(old_pid)
165     ucfg.syswrite("timeout %(#{pid_file})\n") # introduce a bug
166     current_pid = File.read(pid_file).to_i
167     Process.kill(:USR2, current_pid)
169     # wait for pid_file to restore itself
170     tries = DEFAULT_TRIES
171     begin
172       while current_pid != File.read(pid_file).to_i
173         sleep(DEFAULT_RES) and (tries -= 1) > 0
174       end
175     rescue Errno::ENOENT
176       (sleep(DEFAULT_RES) and (tries -= 1) > 0) and retry
177     end
178     assert_equal current_pid, File.read(pid_file).to_i
180     tries = DEFAULT_TRIES
181     while File.exist?(old_file)
182       (sleep(DEFAULT_RES) and (tries -= 1) > 0) or break
183     end
184     assert ! File.exist?(old_file), "oldbin=#{old_file} gone"
185     port2 = unused_port(@addr)
187     # fix the bug
188     ucfg.sysseek(0)
189     ucfg.truncate(0)
190     ucfg.syswrite("listen %(#@addr:#@port)\n")
191     ucfg.syswrite("listen %(#@addr:#{port2})\n")
192     ucfg.syswrite("pid %(#{pid_file})\n")
193     assert_nothing_raised { Process.kill(:USR2, current_pid) }
195     wait_for_file(old_file)
196     wait_for_file(pid_file)
197     new_pid = File.read(pid_file).to_i
198     assert_not_equal current_pid, new_pid
199     assert_equal current_pid, File.read(old_file).to_i
200     results = retry_hit(["http://#{@addr}:#{@port}/",
201                          "http://#{@addr}:#{port2}/"])
202     assert_equal String, results[0].class
203     assert_equal String, results[1].class
205     assert_nothing_raised do
206       Process.kill(:QUIT, current_pid)
207       Process.kill(:QUIT, new_pid)
208     end
209   end
211   def test_broken_reexec_ru
212     File.open("config.ru", "wb") { |fp| fp.syswrite(HI) }
213     pid_file = "#{@tmpdir}/test.pid"
214     old_file = "#{pid_file}.oldbin"
215     ucfg = Tempfile.new('unicorn_test_config')
216     ucfg.syswrite("pid %(#{pid_file})\n")
217     ucfg.syswrite("logger Logger.new(%(#{@tmpdir}/log))\n")
218     pid = xfork do
219       redirect_test_io do
220         exec($unicorn_bin, "-D", "-l#{@addr}:#{@port}", "-c#{ucfg.path}")
221       end
222     end
223     results = retry_hit(["http://#{@addr}:#{@port}/"])
224     assert_equal String, results[0].class
226     wait_for_file(pid_file)
227     Process.waitpid(pid)
228     Process.kill(:USR2, File.read(pid_file).to_i)
229     wait_for_file(old_file)
230     wait_for_file(pid_file)
231     old_pid = File.read(old_file).to_i
232     Process.kill(:QUIT, old_pid)
233     wait_for_death(old_pid)
235     File.unlink("config.ru") # break reloading
236     current_pid = File.read(pid_file).to_i
237     Process.kill(:USR2, current_pid)
239     # wait for pid_file to restore itself
240     tries = DEFAULT_TRIES
241     begin
242       while current_pid != File.read(pid_file).to_i
243         sleep(DEFAULT_RES) and (tries -= 1) > 0
244       end
245     rescue Errno::ENOENT
246       (sleep(DEFAULT_RES) and (tries -= 1) > 0) and retry
247     end
249     tries = DEFAULT_TRIES
250     while File.exist?(old_file)
251       (sleep(DEFAULT_RES) and (tries -= 1) > 0) or break
252     end
253     assert ! File.exist?(old_file), "oldbin=#{old_file} gone"
254     assert_equal current_pid, File.read(pid_file).to_i
256     # fix the bug
257     File.open("config.ru", "wb") { |fp| fp.syswrite(HI) }
258     assert_nothing_raised { Process.kill(:USR2, current_pid) }
259     wait_for_file(old_file)
260     wait_for_file(pid_file)
261     new_pid = File.read(pid_file).to_i
262     assert_not_equal current_pid, new_pid
263     assert_equal current_pid, File.read(old_file).to_i
264     results = retry_hit(["http://#{@addr}:#{@port}/"])
265     assert_equal String, results[0].class
267     assert_nothing_raised do
268       Process.kill(:QUIT, current_pid)
269       Process.kill(:QUIT, new_pid)
270     end
271   end
273   def test_unicorn_config_listener_swap
274     port_cli = unused_port
275     File.open("config.ru", "wb") { |fp| fp.syswrite(HI) }
276     ucfg = Tempfile.new('unicorn_test_config')
277     ucfg.syswrite("listen '#@addr:#@port'\n")
278     pid = xfork do
279       redirect_test_io do
280         exec($unicorn_bin, "-c#{ucfg.path}", "-l#@addr:#{port_cli}")
281       end
282     end
283     results = retry_hit(["http://#@addr:#{port_cli}/"])
284     assert_equal String, results[0].class
285     results = retry_hit(["http://#@addr:#@port/"])
286     assert_equal String, results[0].class
288     port2 = unused_port(@addr)
289     ucfg.sysseek(0)
290     ucfg.truncate(0)
291     ucfg.syswrite("listen '#@addr:#{port2}'\n")
292     Process.kill(:HUP, pid)
294     results = retry_hit(["http://#@addr:#{port2}/"])
295     assert_equal String, results[0].class
296     results = retry_hit(["http://#@addr:#{port_cli}/"])
297     assert_equal String, results[0].class
298     assert_nothing_raised do
299       reuse = TCPServer.new(@addr, @port)
300       reuse.close
301     end
302     assert_shutdown(pid)
303   end
305   def test_unicorn_config_listen_with_options
306     File.open("config.ru", "wb") { |fp| fp.syswrite(HI) }
307     ucfg = Tempfile.new('unicorn_test_config')
308     ucfg.syswrite("listen '#{@addr}:#{@port}', :backlog => 512,\n")
309     ucfg.syswrite("                            :rcvbuf => 4096,\n")
310     ucfg.syswrite("                            :sndbuf => 4096\n")
311     pid = xfork do
312       redirect_test_io { exec($unicorn_bin, "-c#{ucfg.path}") }
313     end
314     results = retry_hit(["http://#{@addr}:#{@port}/"])
315     assert_equal String, results[0].class
316     assert_shutdown(pid)
317   end
319   def test_unicorn_config_per_worker_listen
320     port2 = unused_port
321     pid_spit = 'use Rack::ContentLength;' \
322       'run proc { |e| [ 200, {"Content-Type"=>"text/plain"}, ["#$$\\n"] ] }'
323     File.open("config.ru", "wb") { |fp| fp.syswrite(pid_spit) }
324     tmp = Tempfile.new('test.socket')
325     File.unlink(tmp.path)
326     ucfg = Tempfile.new('unicorn_test_config')
327     ucfg.syswrite("listen '#@addr:#@port'\n")
328     ucfg.syswrite("before_fork { |s,w|\n")
329     ucfg.syswrite("  s.listen('#{tmp.path}', :backlog => 5, :sndbuf => 8192)\n")
330     ucfg.syswrite("  s.listen('#@addr:#{port2}', :rcvbuf => 8192)\n")
331     ucfg.syswrite("\n}\n")
332     pid = xfork do
333       redirect_test_io { exec($unicorn_bin, "-c#{ucfg.path}") }
334     end
335     results = retry_hit(["http://#{@addr}:#{@port}/"])
336     assert_equal String, results[0].class
337     worker_pid = results[0].to_i
338     assert_not_equal pid, worker_pid
339     s = UNIXSocket.new(tmp.path)
340     s.syswrite("GET / HTTP/1.0\r\n\r\n")
341     results = ''
342     loop { results << s.sysread(4096) } rescue nil
343     assert_nothing_raised { s.close }
344     assert_equal worker_pid, results.split(/\r\n/).last.to_i
345     results = hit(["http://#@addr:#{port2}/"])
346     assert_equal String, results[0].class
347     assert_equal worker_pid, results[0].to_i
348     assert_shutdown(pid)
349   end
351   def test_unicorn_config_listen_augments_cli
352     port2 = unused_port(@addr)
353     File.open("config.ru", "wb") { |fp| fp.syswrite(HI) }
354     ucfg = Tempfile.new('unicorn_test_config')
355     ucfg.syswrite("listen '#{@addr}:#{@port}'\n")
356     pid = xfork do
357       redirect_test_io do
358         exec($unicorn_bin, "-c#{ucfg.path}", "-l#{@addr}:#{port2}")
359       end
360     end
361     uris = [@port, port2].map { |i| "http://#{@addr}:#{i}/" }
362     results = retry_hit(uris)
363     assert_equal results.size, uris.size
364     assert_equal String, results[0].class
365     assert_equal String, results[1].class
366     assert_shutdown(pid)
367   end
369   def test_weird_config_settings
370     File.open("config.ru", "wb") { |fp| fp.syswrite(HI) }
371     ucfg = Tempfile.new('unicorn_test_config')
372     ucfg.syswrite(HEAVY_CFG)
373     pid = xfork do
374       redirect_test_io do
375         exec($unicorn_bin, "-c#{ucfg.path}", "-l#{@addr}:#{@port}")
376       end
377     end
379     results = retry_hit(["http://#{@addr}:#{@port}/"])
380     assert_equal String, results[0].class
381     wait_master_ready(COMMON_TMP.path)
382     wait_workers_ready(COMMON_TMP.path, 4)
383     bf = File.readlines(COMMON_TMP.path).grep(/\bbefore_fork: worker=/)
384     assert_equal 4, bf.size
385     rotate = Tempfile.new('unicorn_rotate')
386     assert_nothing_raised do
387       File.rename(COMMON_TMP.path, rotate.path)
388       Process.kill(:USR1, pid)
389     end
390     wait_for_file(COMMON_TMP.path)
391     assert File.exist?(COMMON_TMP.path), "#{COMMON_TMP.path} exists"
392     # USR1 should've been passed to all workers
393     tries = DEFAULT_TRIES
394     log = File.readlines(rotate.path)
395     while (tries -= 1) > 0 &&
396           log.grep(/reopening logs\.\.\./).size < 5
397       sleep DEFAULT_RES
398       log = File.readlines(rotate.path)
399     end
400     assert_equal 5, log.grep(/reopening logs\.\.\./).size
401     assert_equal 0, log.grep(/done reopening logs/).size
403     tries = DEFAULT_TRIES
404     log = File.readlines(COMMON_TMP.path)
405     while (tries -= 1) > 0 && log.grep(/done reopening logs/).size < 5
406       sleep DEFAULT_RES
407       log = File.readlines(COMMON_TMP.path)
408     end
409     assert_equal 5, log.grep(/done reopening logs/).size
410     assert_equal 0, log.grep(/reopening logs\.\.\./).size
411     assert_nothing_raised { Process.kill(:QUIT, pid) }
412     status = nil
413     assert_nothing_raised { pid, status = Process.waitpid2(pid) }
414     assert status.success?, "exited successfully"
415   end
417   def test_read_embedded_cli_switches
418     File.open("config.ru", "wb") do |fp|
419       fp.syswrite("#\\ -p #{@port} -o #{@addr}\n")
420       fp.syswrite(HI)
421     end
422     pid = fork { redirect_test_io { exec($unicorn_bin) } }
423     results = retry_hit(["http://#{@addr}:#{@port}/"])
424     assert_equal String, results[0].class
425     assert_shutdown(pid)
426   end
428   def test_config_ru_alt_path
429     config_path = "#{@tmpdir}/foo.ru"
430     File.open(config_path, "wb") { |fp| fp.syswrite(HI) }
431     pid = fork do
432       redirect_test_io do
433         Dir.chdir("/")
434         exec($unicorn_bin, "-l#{@addr}:#{@port}", config_path)
435       end
436     end
437     results = retry_hit(["http://#{@addr}:#{@port}/"])
438     assert_equal String, results[0].class
439     assert_shutdown(pid)
440   end
442   def test_load_module
443     libdir = "#{@tmpdir}/lib"
444     FileUtils.mkpath([ libdir ])
445     config_path = "#{libdir}/hello.rb"
446     File.open(config_path, "wb") { |fp| fp.syswrite(HELLO) }
447     pid = fork do
448       redirect_test_io do
449         Dir.chdir("/")
450         exec($unicorn_bin, "-l#{@addr}:#{@port}", config_path)
451       end
452     end
453     results = retry_hit(["http://#{@addr}:#{@port}/"])
454     assert_equal String, results[0].class
455     assert_shutdown(pid)
456   end
458   def test_reexec
459     File.open("config.ru", "wb") { |fp| fp.syswrite(HI) }
460     pid_file = "#{@tmpdir}/test.pid"
461     pid = fork do
462       redirect_test_io do
463         exec($unicorn_bin, "-l#{@addr}:#{@port}", "-P#{pid_file}")
464       end
465     end
466     reexec_basic_test(pid, pid_file)
467   end
469   def test_reexec_alt_config
470     config_file = "#{@tmpdir}/foo.ru"
471     File.open(config_file, "wb") { |fp| fp.syswrite(HI) }
472     pid_file = "#{@tmpdir}/test.pid"
473     pid = fork do
474       redirect_test_io do
475         exec($unicorn_bin, "-l#{@addr}:#{@port}", "-P#{pid_file}", config_file)
476       end
477     end
478     reexec_basic_test(pid, pid_file)
479   end
481   def test_socket_unlinked_restore
482     results = nil
483     sock = Tempfile.new('unicorn_test_sock')
484     sock_path = sock.path
485     @sockets << sock_path
486     sock.close!
487     ucfg = Tempfile.new('unicorn_test_config')
488     ucfg.syswrite("listen \"#{sock_path}\"\n")
490     File.open("config.ru", "wb") { |fp| fp.syswrite(HI) }
491     pid = xfork { redirect_test_io { exec($unicorn_bin, "-c#{ucfg.path}") } }
492     wait_for_file(sock_path)
493     assert File.socket?(sock_path)
494     assert_nothing_raised do
495       sock = UNIXSocket.new(sock_path)
496       sock.syswrite("GET / HTTP/1.0\r\n\r\n")
497       results = sock.sysread(4096)
498     end
499     assert_equal String, results.class
500     assert_nothing_raised do
501       File.unlink(sock_path)
502       Process.kill(:HUP, pid)
503     end
504     wait_for_file(sock_path)
505     assert File.socket?(sock_path)
506     assert_nothing_raised do
507       sock = UNIXSocket.new(sock_path)
508       sock.syswrite("GET / HTTP/1.0\r\n\r\n")
509       results = sock.sysread(4096)
510     end
511     assert_equal String, results.class
512   end
514   def test_unicorn_config_file
515     pid_file = "#{@tmpdir}/test.pid"
516     sock = Tempfile.new('unicorn_test_sock')
517     sock_path = sock.path
518     sock.close!
519     @sockets << sock_path
521     log = Tempfile.new('unicorn_test_log')
522     ucfg = Tempfile.new('unicorn_test_config')
523     ucfg.syswrite("listen \"#{sock_path}\"\n")
524     ucfg.syswrite("pid \"#{pid_file}\"\n")
525     ucfg.syswrite("logger Logger.new('#{log.path}')\n")
526     ucfg.close
528     File.open("config.ru", "wb") { |fp| fp.syswrite(HI) }
529     pid = xfork do
530       redirect_test_io do
531         exec($unicorn_bin, "-l#{@addr}:#{@port}",
532              "-P#{pid_file}", "-c#{ucfg.path}")
533       end
534     end
535     results = retry_hit(["http://#{@addr}:#{@port}/"])
536     assert_equal String, results[0].class
537     wait_master_ready(log.path)
538     assert File.exist?(pid_file), "pid_file created"
539     assert_equal pid, File.read(pid_file).to_i
540     assert File.socket?(sock_path), "socket created"
541     assert_nothing_raised do
542       sock = UNIXSocket.new(sock_path)
543       sock.syswrite("GET / HTTP/1.0\r\n\r\n")
544       results = sock.sysread(4096)
545     end
546     assert_equal String, results.class
548     # try reloading the config
549     sock = Tempfile.new('new_test_sock')
550     new_sock_path = sock.path
551     @sockets << new_sock_path
552     sock.close!
553     new_log = Tempfile.new('unicorn_test_log')
554     new_log.sync = true
555     assert_equal 0, new_log.size
557     assert_nothing_raised do
558       ucfg = File.open(ucfg.path, "wb")
559       ucfg.syswrite("listen \"#{sock_path}\"\n")
560       ucfg.syswrite("listen \"#{new_sock_path}\"\n")
561       ucfg.syswrite("pid \"#{pid_file}\"\n")
562       ucfg.syswrite("logger Logger.new('#{new_log.path}')\n")
563       ucfg.close
564       Process.kill(:HUP, pid)
565     end
567     wait_for_file(new_sock_path)
568     assert File.socket?(new_sock_path), "socket exists"
569     @sockets.each do |path|
570       assert_nothing_raised do
571         sock = UNIXSocket.new(path)
572         sock.syswrite("GET / HTTP/1.0\r\n\r\n")
573         results = sock.sysread(4096)
574       end
575       assert_equal String, results.class
576     end
578     assert_not_equal 0, new_log.size
579     reexec_usr2_quit_test(pid, pid_file)
580   end
582   def test_daemonize_reexec
583     pid_file = "#{@tmpdir}/test.pid"
584     log = Tempfile.new('unicorn_test_log')
585     ucfg = Tempfile.new('unicorn_test_config')
586     ucfg.syswrite("pid \"#{pid_file}\"\n")
587     ucfg.syswrite("logger Logger.new('#{log.path}')\n")
588     ucfg.close
590     File.open("config.ru", "wb") { |fp| fp.syswrite(HI) }
591     pid = xfork do
592       redirect_test_io do
593         exec($unicorn_bin, "-D", "-l#{@addr}:#{@port}", "-c#{ucfg.path}")
594       end
595     end
596     results = retry_hit(["http://#{@addr}:#{@port}/"])
597     assert_equal String, results[0].class
598     wait_for_file(pid_file)
599     new_pid = File.read(pid_file).to_i
600     assert_not_equal pid, new_pid
601     pid, status = Process.waitpid2(pid)
602     assert status.success?, "original process exited successfully"
603     assert_nothing_raised { Process.kill(0, new_pid) }
604     reexec_usr2_quit_test(new_pid, pid_file)
605   end
607   def test_reexec_fd_leak
608     unless RUBY_PLATFORM =~ /linux/ # Solaris may work, too, but I forget...
609       warn "FD leak test only works on Linux at the moment"
610       return
611     end
612     pid_file = "#{@tmpdir}/test.pid"
613     log = Tempfile.new('unicorn_test_log')
614     log.sync = true
615     ucfg = Tempfile.new('unicorn_test_config')
616     ucfg.syswrite("pid \"#{pid_file}\"\n")
617     ucfg.syswrite("logger Logger.new('#{log.path}')\n")
618     ucfg.syswrite("stderr_path '#{log.path}'\n")
619     ucfg.syswrite("stdout_path '#{log.path}'\n")
620     ucfg.close
622     File.open("config.ru", "wb") { |fp| fp.syswrite(HI) }
623     pid = xfork do
624       redirect_test_io do
625         exec($unicorn_bin, "-D", "-l#{@addr}:#{@port}", "-c#{ucfg.path}")
626       end
627     end
629     wait_master_ready(log.path)
630     wait_workers_ready(log.path, 1)
631     File.truncate(log.path, 0)
632     wait_for_file(pid_file)
633     orig_pid = pid = File.read(pid_file).to_i
634     orig_fds = `ls -l /proc/#{pid}/fd`.split(/\n/)
635     assert $?.success?
636     expect_size = orig_fds.size
638     assert_nothing_raised do
639       Process.kill(:USR2, pid)
640       wait_for_file("#{pid_file}.oldbin")
641       Process.kill(:QUIT, pid)
642     end
643     wait_for_death(pid)
645     wait_master_ready(log.path)
646     wait_workers_ready(log.path, 1)
647     File.truncate(log.path, 0)
648     wait_for_file(pid_file)
649     pid = File.read(pid_file).to_i
650     assert_not_equal orig_pid, pid
651     curr_fds = `ls -l /proc/#{pid}/fd`.split(/\n/)
652     assert $?.success?
654     # we could've inherited descriptors the first time around
655     assert expect_size >= curr_fds.size, curr_fds.inspect
656     expect_size = curr_fds.size
658     assert_nothing_raised do
659       Process.kill(:USR2, pid)
660       wait_for_file("#{pid_file}.oldbin")
661       Process.kill(:QUIT, pid)
662     end
663     wait_for_death(pid)
665     wait_master_ready(log.path)
666     wait_workers_ready(log.path, 1)
667     File.truncate(log.path, 0)
668     wait_for_file(pid_file)
669     pid = File.read(pid_file).to_i
670     curr_fds = `ls -l /proc/#{pid}/fd`.split(/\n/)
671     assert $?.success?
672     assert_equal expect_size, curr_fds.size, curr_fds.inspect
674     Process.kill(:QUIT, pid)
675     wait_for_death(pid)
676   end
678 end if do_test