1 # -*- encoding: binary -*-
3 # Copyright (c) 2009 Eric Wong
4 # You can redistribute it and/or modify it under the same terms as Ruby.
6 # Ensure we stay sane in the face of signals being sent to us
8 require 'test/test_helper'
13 def initialize(bs, count)
19 @count.times { yield @buf }
23 class SignalsTest < Test::Unit::TestCase
29 @sock = Tempfile.new('unicorn.sock')
30 @tmp = Tempfile.new('unicorn.write')
32 File.unlink(@sock.path)
33 File.unlink(@tmp.path)
35 :listeners => [ "127.0.0.1:#@port", @sock.path ],
36 :after_fork => lambda { |server,worker|
37 trap(:HUP) { @tmp.syswrite('.') }
47 def test_worker_dies_on_dead_master
49 app = lambda { |env| [ 200, {'X-Pid' => "#$$" }, [] ] }
50 opts = @server_opts.merge(:timeout => 3)
51 redirect_test_io { HttpServer.new(app, opts).start.join }
53 child = sock = buf = t0 = nil
54 assert_nothing_raised do
55 wait_workers_ready("test_stderr.#{pid}.log", 1)
56 sock = TCPSocket.new('127.0.0.1', @port)
57 sock.syswrite("GET / HTTP/1.0\r\n\r\n")
58 buf = sock.readpartial(4096)
60 buf =~ /\bX-Pid: (\d+)\b/ or raise Exception
62 wait_master_ready("test_stderr.#{pid}.log")
63 wait_workers_ready("test_stderr.#{pid}.log", 1)
64 Process.kill(:KILL, pid)
66 File.unlink("test_stderr.#{pid}.log", "test_stdout.#{pid}.log")
71 assert_raises(Errno::ESRCH) { loop { Process.kill(0, child); sleep 0.2 } }
72 assert((Time.now - t0) < 60)
79 app = lambda { |env| wr.syswrite('.'); sleep; [ 200, {}, [] ] }
80 redirect_test_io { HttpServer.new(app, @server_opts).start.join }
84 assert_nothing_raised do
85 wait_workers_ready("test_stderr.#{pid}.log", 1)
86 sock = TCPSocket.new('127.0.0.1', @port)
87 sock.syswrite("GET / HTTP/1.0\r\n\r\n")
88 buf = rd.readpartial(1)
89 wait_master_ready("test_stderr.#{pid}.log")
90 Process.kill(:INT, pid)
95 assert_raises(EOFError,Errno::ECONNRESET,Errno::EPIPE,Errno::EINVAL,
97 buf = sock.sysread(4096)
103 def test_timeout_slow_response
105 app = lambda { |env| sleep }
106 opts = @server_opts.merge(:timeout => 3)
107 redirect_test_io { HttpServer.new(app, opts).start.join }
111 assert_nothing_raised do
112 wait_workers_ready("test_stderr.#{pid}.log", 1)
113 sock = TCPSocket.new('127.0.0.1', @port)
114 sock.syswrite("GET / HTTP/1.0\r\n\r\n")
118 assert_raises(EOFError,Errno::ECONNRESET,Errno::EPIPE,Errno::EINVAL,
120 buf = sock.sysread(4096)
124 assert diff > 1.0, "diff was #{diff.inspect}"
127 Process.kill(:TERM, pid) rescue nil
130 def test_response_write
132 [ 200, { 'Content-Type' => 'text/plain', 'X-Pid' => Process.pid.to_s },
133 Dd.new(@bs, @count) ]
135 redirect_test_io { @server = HttpServer.new(app, @server_opts).start }
137 assert_nothing_raised do
138 wait_workers_ready("test_stderr.#{$$}.log", 1)
139 sock = TCPSocket.new('127.0.0.1', @port)
140 sock.syswrite("GET / HTTP/1.0\r\n\r\n")
143 header_len = pid = nil
144 assert_nothing_raised do
145 buf = sock.sysread(16384, buf)
146 pid = buf[/\r\nX-Pid: (\d+)\r\n/, 1].to_i
147 header_len = buf[/\A(.+?\r\n\r\n)/m, 1].size
149 assert pid > 0, "pid not positive: #{pid.inspect}"
151 size_before = @tmp.stat.size
152 assert_raises(EOFError,Errno::ECONNRESET,Errno::EPIPE,Errno::EINVAL,
155 3.times { Process.kill(:HUP, pid) }
156 sock.sysread(16384, buf)
158 3.times { Process.kill(:HUP, pid) }
162 redirect_test_io { @server.stop(true) }
163 # can't check for == since pending signals get merged
164 assert size_before < @tmp.stat.size
165 got = read - header_len
166 expect = @bs * @count
167 assert_equal(expect, got, "expect=#{expect} got=#{got}")
168 assert_nothing_raised { sock.close }
171 def test_request_read
173 while env['rack.input'].read(4096)
175 [ 200, {'Content-Type'=>'text/plain', 'X-Pid'=>Process.pid.to_s}, [] ]
177 redirect_test_io { @server = HttpServer.new(app, @server_opts).start }
180 assert_nothing_raised do
181 wait_workers_ready("test_stderr.#{$$}.log", 1)
182 sock = TCPSocket.new('127.0.0.1', @port)
183 sock.syswrite("GET / HTTP/1.0\r\n\r\n")
184 pid = sock.sysread(4096)[/\r\nX-Pid: (\d+)\r\n/, 1].to_i
188 assert pid > 0, "pid not positive: #{pid.inspect}"
189 sock = TCPSocket.new('127.0.0.1', @port)
190 sock.syswrite("PUT / HTTP/1.0\r\n")
191 sock.syswrite("Content-Length: #{@bs * @count}\r\n\r\n")
192 1000.times { Process.kill(:HUP, pid) }
193 size_before = @tmp.stat.size
194 killer = fork { loop { Process.kill(:HUP, pid); sleep(0.0001) } }
196 @count.times { sock.syswrite(buf) }
197 Process.kill(:KILL, killer)
198 Process.waitpid2(killer)
199 redirect_test_io { @server.stop(true) }
200 # can't check for == since pending signals get merged
201 assert size_before < @tmp.stat.size
202 assert_equal pid, sock.sysread(4096)[/\r\nX-Pid: (\d+)\r\n/, 1].to_i