1 # -*- encoding: binary -*-
3 # Copyright (c) 2009 Eric Wong
4 # You can redistribute it and/or modify it under the same terms as Ruby 1.8 or
7 # Ensure we stay sane in the face of signals being sent to us
9 require 'test/test_helper'
14 def initialize(bs, count)
20 @count.times { yield @buf }
24 class SignalsTest < Test::Unit::TestCase
30 @sock = Tempfile.new('unicorn.sock')
31 @tmp = Tempfile.new('unicorn.write')
33 File.unlink(@sock.path)
34 File.unlink(@tmp.path)
36 :listeners => [ "127.0.0.1:#@port", @sock.path ],
37 :after_fork => lambda { |server,worker|
38 trap(:HUP) { @tmp.syswrite('.') }
48 def test_worker_dies_on_dead_master
50 app = lambda { |env| [ 200, {'X-Pid' => "#$$" }, [] ] }
51 opts = @server_opts.merge(:timeout => 3)
52 redirect_test_io { HttpServer.new(app, opts).start.join }
54 child = sock = buf = t0 = nil
55 assert_nothing_raised do
56 wait_workers_ready("test_stderr.#{pid}.log", 1)
57 sock = TCPSocket.new('127.0.0.1', @port)
58 sock.syswrite("GET / HTTP/1.0\r\n\r\n")
59 buf = sock.readpartial(4096)
61 buf =~ /\bX-Pid: (\d+)\b/ or raise Exception
63 wait_master_ready("test_stderr.#{pid}.log")
64 wait_workers_ready("test_stderr.#{pid}.log", 1)
65 Process.kill(:KILL, pid)
67 File.unlink("test_stderr.#{pid}.log", "test_stdout.#{pid}.log")
72 assert_raises(Errno::ESRCH) { loop { Process.kill(0, child); sleep 0.2 } }
73 assert((Time.now - t0) < 60)
80 app = lambda { |env| wr.syswrite('.'); sleep; [ 200, {}, [] ] }
81 redirect_test_io { HttpServer.new(app, @server_opts).start.join }
85 assert_nothing_raised do
86 wait_workers_ready("test_stderr.#{pid}.log", 1)
87 sock = TCPSocket.new('127.0.0.1', @port)
88 sock.syswrite("GET / HTTP/1.0\r\n\r\n")
89 buf = rd.readpartial(1)
90 wait_master_ready("test_stderr.#{pid}.log")
91 Process.kill(:INT, pid)
96 assert_raises(EOFError,Errno::ECONNRESET,Errno::EPIPE,Errno::EINVAL,
98 buf = sock.sysread(4096)
104 def test_timeout_slow_response
106 app = lambda { |env| sleep }
107 opts = @server_opts.merge(:timeout => 3)
108 redirect_test_io { HttpServer.new(app, opts).start.join }
112 assert_nothing_raised do
113 wait_workers_ready("test_stderr.#{pid}.log", 1)
114 sock = TCPSocket.new('127.0.0.1', @port)
115 sock.syswrite("GET / HTTP/1.0\r\n\r\n")
119 assert_raises(EOFError,Errno::ECONNRESET,Errno::EPIPE,Errno::EINVAL,
121 buf = sock.sysread(4096)
125 assert diff > 1.0, "diff was #{diff.inspect}"
128 Process.kill(:TERM, pid) rescue nil
131 def test_response_write
133 [ 200, { 'Content-Type' => 'text/plain', 'X-Pid' => Process.pid.to_s },
134 Dd.new(@bs, @count) ]
136 redirect_test_io { @server = HttpServer.new(app, @server_opts).start }
138 assert_nothing_raised do
139 wait_workers_ready("test_stderr.#{$$}.log", 1)
140 sock = TCPSocket.new('127.0.0.1', @port)
141 sock.syswrite("GET / HTTP/1.0\r\n\r\n")
144 header_len = pid = nil
145 assert_nothing_raised do
146 buf = sock.sysread(16384, buf)
147 pid = buf[/\r\nX-Pid: (\d+)\r\n/, 1].to_i
148 header_len = buf[/\A(.+?\r\n\r\n)/m, 1].size
150 assert pid > 0, "pid not positive: #{pid.inspect}"
152 size_before = @tmp.stat.size
153 assert_raises(EOFError,Errno::ECONNRESET,Errno::EPIPE,Errno::EINVAL,
156 3.times { Process.kill(:HUP, pid) }
157 sock.sysread(16384, buf)
159 3.times { Process.kill(:HUP, pid) }
163 redirect_test_io { @server.stop(true) }
164 # can't check for == since pending signals get merged
165 assert size_before < @tmp.stat.size
166 got = read - header_len
167 expect = @bs * @count
168 assert_equal(expect, got, "expect=#{expect} got=#{got}")
169 assert_nothing_raised { sock.close }
172 def test_request_read
174 while env['rack.input'].read(4096)
176 [ 200, {'Content-Type'=>'text/plain', 'X-Pid'=>Process.pid.to_s}, [] ]
178 redirect_test_io { @server = HttpServer.new(app, @server_opts).start }
181 assert_nothing_raised do
182 wait_workers_ready("test_stderr.#{$$}.log", 1)
183 sock = TCPSocket.new('127.0.0.1', @port)
184 sock.syswrite("GET / HTTP/1.0\r\n\r\n")
185 pid = sock.sysread(4096)[/\r\nX-Pid: (\d+)\r\n/, 1].to_i
189 assert pid > 0, "pid not positive: #{pid.inspect}"
190 sock = TCPSocket.new('127.0.0.1', @port)
191 sock.syswrite("PUT / HTTP/1.0\r\n")
192 sock.syswrite("Content-Length: #{@bs * @count}\r\n\r\n")
193 1000.times { Process.kill(:HUP, pid) }
194 size_before = @tmp.stat.size
195 killer = fork { loop { Process.kill(:HUP, pid); sleep(0.0001) } }
197 @count.times { sock.syswrite(buf) }
198 Process.kill(:KILL, killer)
199 Process.waitpid2(killer)
200 redirect_test_io { @server.stop(true) }
201 # can't check for == since pending signals get merged
202 assert size_before < @tmp.stat.size
203 assert_equal pid, sock.sysread(4096)[/\r\nX-Pid: (\d+)\r\n/, 1].to_i