test_exec: cleanup stale socket on exit
[unicorn.git] / test / exec / test_exec.rb
blobb0462dcf0d634edc9030e2a38c5e9f255c5a47f6
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       status = nil
81       assert_nothing_raised do
82         Process.kill(sig, pid)
83         pid, status = Process.waitpid2(pid)
84       end
85       reaped = File.readlines("test_stderr.#{pid}.log").grep(/reaped/)
86       assert_equal 1, reaped.size
87       assert status.exited?
88     end
89   end
91   def test_basic
92     File.open("config.ru", "wb") { |fp| fp.syswrite(HI) }
93     pid = fork do
94       redirect_test_io { exec($unicorn_bin, "-l", "#{@addr}:#{@port}") }
95     end
96     results = retry_hit(["http://#{@addr}:#{@port}/"])
97     assert_equal String, results[0].class
98     assert_shutdown(pid)
99   end
101   def test_help
102     redirect_test_io do
103       assert(system($unicorn_bin, "-h"), "help text returns true")
104     end
105     assert_equal 0, File.stat("test_stderr.#$$.log").size
106     assert_not_equal 0, File.stat("test_stdout.#$$.log").size
107     lines = File.readlines("test_stdout.#$$.log")
109     # Be considerate of the on-call technician working from their
110     # mobile phone or netbook on a slow connection :)
111     assert lines.size <= 24, "help height fits in an ANSI terminal window"
112     lines.each do |line|
113       assert line.size <= 80, "help width fits in an ANSI terminal window"
114     end
115   end
117   def test_broken_reexec_config
118     File.open("config.ru", "wb") { |fp| fp.syswrite(HI) }
119     pid_file = "#{@tmpdir}/test.pid"
120     old_file = "#{pid_file}.oldbin"
121     ucfg = Tempfile.new('unicorn_test_config')
122     ucfg.syswrite("listen %(#@addr:#@port)\n")
123     ucfg.syswrite("pid %(#{pid_file})\n")
124     ucfg.syswrite("logger Logger.new(%(#{@tmpdir}/log))\n")
125     pid = xfork do
126       redirect_test_io do
127         exec($unicorn_bin, "-D", "-l#{@addr}:#{@port}", "-c#{ucfg.path}")
128       end
129     end
130     results = retry_hit(["http://#{@addr}:#{@port}/"])
131     assert_equal String, results[0].class
133     wait_for_file(pid_file)
134     Process.waitpid(pid)
135     Process.kill(:USR2, File.read(pid_file).to_i)
136     wait_for_file(old_file)
137     wait_for_file(pid_file)
138     old_pid = File.read(old_file).to_i
139     Process.kill(:QUIT, old_pid)
140     wait_for_death(old_pid)
142     ucfg.syswrite("timeout %(#{pid_file})\n") # introduce a bug
143     current_pid = File.read(pid_file).to_i
144     Process.kill(:USR2, current_pid)
146     # wait for pid_file to restore itself
147     tries = DEFAULT_TRIES
148     begin
149       while current_pid != File.read(pid_file).to_i
150         sleep(DEFAULT_RES) and (tries -= 1) > 0
151       end
152     rescue Errno::ENOENT
153       (sleep(DEFAULT_RES) and (tries -= 1) > 0) and retry
154     end
155     assert_equal current_pid, File.read(pid_file).to_i
157     tries = DEFAULT_TRIES
158     while File.exist?(old_file)
159       (sleep(DEFAULT_RES) and (tries -= 1) > 0) or break
160     end
161     assert ! File.exist?(old_file), "oldbin=#{old_file} gone"
162     port2 = unused_port(@addr)
164     # fix the bug
165     ucfg.sysseek(0)
166     ucfg.truncate(0)
167     ucfg.syswrite("listen %(#@addr:#@port)\n")
168     ucfg.syswrite("listen %(#@addr:#{port2})\n")
169     ucfg.syswrite("pid %(#{pid_file})\n")
170     assert_nothing_raised { Process.kill(:USR2, current_pid) }
172     wait_for_file(old_file)
173     wait_for_file(pid_file)
174     new_pid = File.read(pid_file).to_i
175     assert_not_equal current_pid, new_pid
176     assert_equal current_pid, File.read(old_file).to_i
177     results = retry_hit(["http://#{@addr}:#{@port}/",
178                          "http://#{@addr}:#{port2}/"])
179     assert_equal String, results[0].class
180     assert_equal String, results[1].class
182     assert_nothing_raised do
183       Process.kill(:QUIT, current_pid)
184       Process.kill(:QUIT, new_pid)
185     end
186   end
188   def test_broken_reexec_ru
189     File.open("config.ru", "wb") { |fp| fp.syswrite(HI) }
190     pid_file = "#{@tmpdir}/test.pid"
191     old_file = "#{pid_file}.oldbin"
192     ucfg = Tempfile.new('unicorn_test_config')
193     ucfg.syswrite("pid %(#{pid_file})\n")
194     ucfg.syswrite("logger Logger.new(%(#{@tmpdir}/log))\n")
195     pid = xfork do
196       redirect_test_io do
197         exec($unicorn_bin, "-D", "-l#{@addr}:#{@port}", "-c#{ucfg.path}")
198       end
199     end
200     results = retry_hit(["http://#{@addr}:#{@port}/"])
201     assert_equal String, results[0].class
203     wait_for_file(pid_file)
204     Process.waitpid(pid)
205     Process.kill(:USR2, File.read(pid_file).to_i)
206     wait_for_file(old_file)
207     wait_for_file(pid_file)
208     old_pid = File.read(old_file).to_i
209     Process.kill(:QUIT, old_pid)
210     wait_for_death(old_pid)
212     File.unlink("config.ru") # break reloading
213     current_pid = File.read(pid_file).to_i
214     Process.kill(:USR2, current_pid)
216     # wait for pid_file to restore itself
217     tries = DEFAULT_TRIES
218     begin
219       while current_pid != File.read(pid_file).to_i
220         sleep(DEFAULT_RES) and (tries -= 1) > 0
221       end
222     rescue Errno::ENOENT
223       (sleep(DEFAULT_RES) and (tries -= 1) > 0) and retry
224     end
226     tries = DEFAULT_TRIES
227     while File.exist?(old_file)
228       (sleep(DEFAULT_RES) and (tries -= 1) > 0) or break
229     end
230     assert ! File.exist?(old_file), "oldbin=#{old_file} gone"
231     assert_equal current_pid, File.read(pid_file).to_i
233     # fix the bug
234     File.open("config.ru", "wb") { |fp| fp.syswrite(HI) }
235     assert_nothing_raised { Process.kill(:USR2, current_pid) }
236     wait_for_file(old_file)
237     wait_for_file(pid_file)
238     new_pid = File.read(pid_file).to_i
239     assert_not_equal current_pid, new_pid
240     assert_equal current_pid, File.read(old_file).to_i
241     results = retry_hit(["http://#{@addr}:#{@port}/"])
242     assert_equal String, results[0].class
244     assert_nothing_raised do
245       Process.kill(:QUIT, current_pid)
246       Process.kill(:QUIT, new_pid)
247     end
248   end
250   def test_unicorn_config_listener_swap
251     port_cli = unused_port
252     File.open("config.ru", "wb") { |fp| fp.syswrite(HI) }
253     ucfg = Tempfile.new('unicorn_test_config')
254     ucfg.syswrite("listen '#@addr:#@port'\n")
255     pid = xfork do
256       redirect_test_io do
257         exec($unicorn_bin, "-c#{ucfg.path}", "-l#@addr:#{port_cli}")
258       end
259     end
260     results = retry_hit(["http://#@addr:#{port_cli}/"])
261     assert_equal String, results[0].class
262     results = retry_hit(["http://#@addr:#@port/"])
263     assert_equal String, results[0].class
265     port2 = unused_port(@addr)
266     ucfg.sysseek(0)
267     ucfg.truncate(0)
268     ucfg.syswrite("listen '#@addr:#{port2}'\n")
269     Process.kill(:HUP, pid)
271     results = retry_hit(["http://#@addr:#{port2}/"])
272     assert_equal String, results[0].class
273     results = retry_hit(["http://#@addr:#{port_cli}/"])
274     assert_equal String, results[0].class
275     assert_nothing_raised do
276       reuse = TCPServer.new(@addr, @port)
277       reuse.close
278     end
279     assert_shutdown(pid)
280   end
282   def test_unicorn_config_listen_with_options
283     File.open("config.ru", "wb") { |fp| fp.syswrite(HI) }
284     ucfg = Tempfile.new('unicorn_test_config')
285     ucfg.syswrite("listen '#{@addr}:#{@port}', :backlog => 512,\n")
286     ucfg.syswrite("                            :rcvbuf => 4096,\n")
287     ucfg.syswrite("                            :sndbuf => 4096\n")
288     pid = xfork do
289       redirect_test_io { exec($unicorn_bin, "-c#{ucfg.path}") }
290     end
291     results = retry_hit(["http://#{@addr}:#{@port}/"])
292     assert_equal String, results[0].class
293     assert_shutdown(pid)
294   end
296   def test_unicorn_config_per_worker_listen
297     port2 = unused_port
298     pid_spit = 'use Rack::ContentLength;' \
299       'run proc { |e| [ 200, {"Content-Type"=>"text/plain"}, ["#$$\\n"] ] }'
300     File.open("config.ru", "wb") { |fp| fp.syswrite(pid_spit) }
301     tmp = Tempfile.new('test.socket')
302     File.unlink(tmp.path)
303     ucfg = Tempfile.new('unicorn_test_config')
304     ucfg.syswrite("listen '#@addr:#@port'\n")
305     ucfg.syswrite("before_fork { |s,w|\n")
306     ucfg.syswrite("  s.listen('#{tmp.path}', :backlog => 5, :sndbuf => 8192)\n")
307     ucfg.syswrite("  s.listen('#@addr:#{port2}', :rcvbuf => 8192)\n")
308     ucfg.syswrite("\n}\n")
309     pid = xfork do
310       redirect_test_io { exec($unicorn_bin, "-c#{ucfg.path}") }
311     end
312     results = retry_hit(["http://#{@addr}:#{@port}/"])
313     assert_equal String, results[0].class
314     worker_pid = results[0].to_i
315     assert_not_equal pid, worker_pid
316     s = UNIXSocket.new(tmp.path)
317     s.syswrite("GET / HTTP/1.0\r\n\r\n")
318     results = ''
319     loop { results << s.sysread(4096) } rescue nil
320     assert_nothing_raised { s.close }
321     assert_equal worker_pid, results.split(/\r\n/).last.to_i
322     results = hit(["http://#@addr:#{port2}/"])
323     assert_equal String, results[0].class
324     assert_equal worker_pid, results[0].to_i
325     assert_shutdown(pid)
326   end
328   def test_unicorn_config_listen_augments_cli
329     port2 = unused_port(@addr)
330     File.open("config.ru", "wb") { |fp| fp.syswrite(HI) }
331     ucfg = Tempfile.new('unicorn_test_config')
332     ucfg.syswrite("listen '#{@addr}:#{@port}'\n")
333     pid = xfork do
334       redirect_test_io do
335         exec($unicorn_bin, "-c#{ucfg.path}", "-l#{@addr}:#{port2}")
336       end
337     end
338     uris = [@port, port2].map { |i| "http://#{@addr}:#{i}/" }
339     results = retry_hit(uris)
340     assert_equal results.size, uris.size
341     assert_equal String, results[0].class
342     assert_equal String, results[1].class
343     assert_shutdown(pid)
344   end
346   def test_weird_config_settings
347     File.open("config.ru", "wb") { |fp| fp.syswrite(HI) }
348     ucfg = Tempfile.new('unicorn_test_config')
349     ucfg.syswrite(HEAVY_CFG)
350     pid = xfork do
351       redirect_test_io do
352         exec($unicorn_bin, "-c#{ucfg.path}", "-l#{@addr}:#{@port}")
353       end
354     end
356     results = retry_hit(["http://#{@addr}:#{@port}/"])
357     assert_equal String, results[0].class
358     wait_master_ready(COMMON_TMP.path)
359     wait_workers_ready(COMMON_TMP.path, 4)
360     bf = File.readlines(COMMON_TMP.path).grep(/\bbefore_fork: worker=/)
361     assert_equal 4, bf.size
362     rotate = Tempfile.new('unicorn_rotate')
363     assert_nothing_raised do
364       File.rename(COMMON_TMP.path, rotate.path)
365       Process.kill(:USR1, pid)
366     end
367     wait_for_file(COMMON_TMP.path)
368     assert File.exist?(COMMON_TMP.path), "#{COMMON_TMP.path} exists"
369     # USR1 should've been passed to all workers
370     tries = DEFAULT_TRIES
371     log = File.readlines(rotate.path)
372     while (tries -= 1) > 0 &&
373           log.grep(/reopening logs\.\.\./).size < 5
374       sleep DEFAULT_RES
375       log = File.readlines(rotate.path)
376     end
377     assert_equal 5, log.grep(/reopening logs\.\.\./).size
378     assert_equal 0, log.grep(/done reopening logs/).size
380     tries = DEFAULT_TRIES
381     log = File.readlines(COMMON_TMP.path)
382     while (tries -= 1) > 0 && log.grep(/done reopening logs/).size < 5
383       sleep DEFAULT_RES
384       log = File.readlines(COMMON_TMP.path)
385     end
386     assert_equal 5, log.grep(/done reopening logs/).size
387     assert_equal 0, log.grep(/reopening logs\.\.\./).size
388     assert_nothing_raised { Process.kill(:QUIT, pid) }
389     status = nil
390     assert_nothing_raised { pid, status = Process.waitpid2(pid) }
391     assert status.success?, "exited successfully"
392   end
394   def test_read_embedded_cli_switches
395     File.open("config.ru", "wb") do |fp|
396       fp.syswrite("#\\ -p #{@port} -o #{@addr}\n")
397       fp.syswrite(HI)
398     end
399     pid = fork { redirect_test_io { exec($unicorn_bin) } }
400     results = retry_hit(["http://#{@addr}:#{@port}/"])
401     assert_equal String, results[0].class
402     assert_shutdown(pid)
403   end
405   def test_config_ru_alt_path
406     config_path = "#{@tmpdir}/foo.ru"
407     File.open(config_path, "wb") { |fp| fp.syswrite(HI) }
408     pid = fork do
409       redirect_test_io do
410         Dir.chdir("/")
411         exec($unicorn_bin, "-l#{@addr}:#{@port}", config_path)
412       end
413     end
414     results = retry_hit(["http://#{@addr}:#{@port}/"])
415     assert_equal String, results[0].class
416     assert_shutdown(pid)
417   end
419   def test_load_module
420     libdir = "#{@tmpdir}/lib"
421     FileUtils.mkpath([ libdir ])
422     config_path = "#{libdir}/hello.rb"
423     File.open(config_path, "wb") { |fp| fp.syswrite(HELLO) }
424     pid = fork do
425       redirect_test_io do
426         Dir.chdir("/")
427         exec($unicorn_bin, "-l#{@addr}:#{@port}", config_path)
428       end
429     end
430     results = retry_hit(["http://#{@addr}:#{@port}/"])
431     assert_equal String, results[0].class
432     assert_shutdown(pid)
433   end
435   def test_reexec
436     File.open("config.ru", "wb") { |fp| fp.syswrite(HI) }
437     pid_file = "#{@tmpdir}/test.pid"
438     pid = fork do
439       redirect_test_io do
440         exec($unicorn_bin, "-l#{@addr}:#{@port}", "-P#{pid_file}")
441       end
442     end
443     reexec_basic_test(pid, pid_file)
444   end
446   def test_reexec_alt_config
447     config_file = "#{@tmpdir}/foo.ru"
448     File.open(config_file, "wb") { |fp| fp.syswrite(HI) }
449     pid_file = "#{@tmpdir}/test.pid"
450     pid = fork do
451       redirect_test_io do
452         exec($unicorn_bin, "-l#{@addr}:#{@port}", "-P#{pid_file}", config_file)
453       end
454     end
455     reexec_basic_test(pid, pid_file)
456   end
458   def test_socket_unlinked_restore
459     results = nil
460     sock = Tempfile.new('unicorn_test_sock')
461     sock_path = sock.path
462     @sockets << sock_path
463     sock.close!
464     ucfg = Tempfile.new('unicorn_test_config')
465     ucfg.syswrite("listen \"#{sock_path}\"\n")
467     File.open("config.ru", "wb") { |fp| fp.syswrite(HI) }
468     pid = xfork { redirect_test_io { exec($unicorn_bin, "-c#{ucfg.path}") } }
469     wait_for_file(sock_path)
470     assert File.socket?(sock_path)
471     assert_nothing_raised do
472       sock = UNIXSocket.new(sock_path)
473       sock.syswrite("GET / HTTP/1.0\r\n\r\n")
474       results = sock.sysread(4096)
475     end
476     assert_equal String, results.class
477     assert_nothing_raised do
478       File.unlink(sock_path)
479       Process.kill(:HUP, pid)
480     end
481     wait_for_file(sock_path)
482     assert File.socket?(sock_path)
483     assert_nothing_raised do
484       sock = UNIXSocket.new(sock_path)
485       sock.syswrite("GET / HTTP/1.0\r\n\r\n")
486       results = sock.sysread(4096)
487     end
488     assert_equal String, results.class
489   end
491   def test_unicorn_config_file
492     pid_file = "#{@tmpdir}/test.pid"
493     sock = Tempfile.new('unicorn_test_sock')
494     sock_path = sock.path
495     sock.close!
496     @sockets << sock_path
498     log = Tempfile.new('unicorn_test_log')
499     ucfg = Tempfile.new('unicorn_test_config')
500     ucfg.syswrite("listen \"#{sock_path}\"\n")
501     ucfg.syswrite("pid \"#{pid_file}\"\n")
502     ucfg.syswrite("logger Logger.new('#{log.path}')\n")
503     ucfg.close
505     File.open("config.ru", "wb") { |fp| fp.syswrite(HI) }
506     pid = xfork do
507       redirect_test_io do
508         exec($unicorn_bin, "-l#{@addr}:#{@port}",
509              "-P#{pid_file}", "-c#{ucfg.path}")
510       end
511     end
512     results = retry_hit(["http://#{@addr}:#{@port}/"])
513     assert_equal String, results[0].class
514     wait_master_ready(log.path)
515     assert File.exist?(pid_file), "pid_file created"
516     assert_equal pid, File.read(pid_file).to_i
517     assert File.socket?(sock_path), "socket created"
518     assert_nothing_raised do
519       sock = UNIXSocket.new(sock_path)
520       sock.syswrite("GET / HTTP/1.0\r\n\r\n")
521       results = sock.sysread(4096)
522     end
523     assert_equal String, results.class
525     # try reloading the config
526     sock = Tempfile.new('new_test_sock')
527     new_sock_path = sock.path
528     @sockets << new_sock_path
529     sock.close!
530     new_log = Tempfile.new('unicorn_test_log')
531     new_log.sync = true
532     assert_equal 0, new_log.size
534     assert_nothing_raised do
535       ucfg = File.open(ucfg.path, "wb")
536       ucfg.syswrite("listen \"#{sock_path}\"\n")
537       ucfg.syswrite("listen \"#{new_sock_path}\"\n")
538       ucfg.syswrite("pid \"#{pid_file}\"\n")
539       ucfg.syswrite("logger Logger.new('#{new_log.path}')\n")
540       ucfg.close
541       Process.kill(:HUP, pid)
542     end
544     wait_for_file(new_sock_path)
545     assert File.socket?(new_sock_path), "socket exists"
546     @sockets.each do |path|
547       assert_nothing_raised do
548         sock = UNIXSocket.new(path)
549         sock.syswrite("GET / HTTP/1.0\r\n\r\n")
550         results = sock.sysread(4096)
551       end
552       assert_equal String, results.class
553     end
555     assert_not_equal 0, new_log.size
556     reexec_usr2_quit_test(pid, pid_file)
557   end
559   def test_daemonize_reexec
560     pid_file = "#{@tmpdir}/test.pid"
561     log = Tempfile.new('unicorn_test_log')
562     ucfg = Tempfile.new('unicorn_test_config')
563     ucfg.syswrite("pid \"#{pid_file}\"\n")
564     ucfg.syswrite("logger Logger.new('#{log.path}')\n")
565     ucfg.close
567     File.open("config.ru", "wb") { |fp| fp.syswrite(HI) }
568     pid = xfork do
569       redirect_test_io do
570         exec($unicorn_bin, "-D", "-l#{@addr}:#{@port}", "-c#{ucfg.path}")
571       end
572     end
573     results = retry_hit(["http://#{@addr}:#{@port}/"])
574     assert_equal String, results[0].class
575     wait_for_file(pid_file)
576     new_pid = File.read(pid_file).to_i
577     assert_not_equal pid, new_pid
578     pid, status = Process.waitpid2(pid)
579     assert status.success?, "original process exited successfully"
580     assert_nothing_raised { Process.kill(0, new_pid) }
581     reexec_usr2_quit_test(new_pid, pid_file)
582   end
584   def test_reexec_fd_leak
585     unless RUBY_PLATFORM =~ /linux/ # Solaris may work, too, but I forget...
586       warn "FD leak test only works on Linux at the moment"
587       return
588     end
589     pid_file = "#{@tmpdir}/test.pid"
590     log = Tempfile.new('unicorn_test_log')
591     log.sync = true
592     ucfg = Tempfile.new('unicorn_test_config')
593     ucfg.syswrite("pid \"#{pid_file}\"\n")
594     ucfg.syswrite("logger Logger.new('#{log.path}')\n")
595     ucfg.syswrite("stderr_path '#{log.path}'\n")
596     ucfg.syswrite("stdout_path '#{log.path}'\n")
597     ucfg.close
599     File.open("config.ru", "wb") { |fp| fp.syswrite(HI) }
600     pid = xfork do
601       redirect_test_io do
602         exec($unicorn_bin, "-D", "-l#{@addr}:#{@port}", "-c#{ucfg.path}")
603       end
604     end
606     wait_master_ready(log.path)
607     File.truncate(log.path, 0)
608     wait_for_file(pid_file)
609     orig_pid = pid = File.read(pid_file).to_i
610     orig_fds = `ls -l /proc/#{pid}/fd`.split(/\n/)
611     assert $?.success?
612     expect_size = orig_fds.size
614     assert_nothing_raised do
615       Process.kill(:USR2, pid)
616       wait_for_file("#{pid_file}.oldbin")
617       Process.kill(:QUIT, pid)
618     end
619     wait_for_death(pid)
621     wait_master_ready(log.path)
622     File.truncate(log.path, 0)
623     wait_for_file(pid_file)
624     pid = File.read(pid_file).to_i
625     assert_not_equal orig_pid, pid
626     curr_fds = `ls -l /proc/#{pid}/fd`.split(/\n/)
627     assert $?.success?
629     # we could've inherited descriptors the first time around
630     assert expect_size >= curr_fds.size, curr_fds.inspect
631     expect_size = curr_fds.size
633     assert_nothing_raised do
634       Process.kill(:USR2, pid)
635       wait_for_file("#{pid_file}.oldbin")
636       Process.kill(:QUIT, pid)
637     end
638     wait_for_death(pid)
640     wait_master_ready(log.path)
641     File.truncate(log.path, 0)
642     wait_for_file(pid_file)
643     pid = File.read(pid_file).to_i
644     curr_fds = `ls -l /proc/#{pid}/fd`.split(/\n/)
645     assert $?.success?
646     assert_equal expect_size, curr_fds.size, curr_fds.inspect
648     Process.kill(:QUIT, pid)
649     wait_for_death(pid)
650   end
652 end if do_test