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 wait_workers_ready("test_stderr.#{pid}.log", 1)
55 sock = TCPSocket.new('127.0.0.1', @port)
56 sock.syswrite("GET / HTTP/1.0\r\n\r\n")
57 buf = sock.readpartial(4096)
59 buf =~ /\bX-Pid: (\d+)\b/ or raise Exception
61 wait_master_ready("test_stderr.#{pid}.log")
62 wait_workers_ready("test_stderr.#{pid}.log", 1)
63 Process.kill(:KILL, pid)
65 File.unlink("test_stderr.#{pid}.log", "test_stdout.#{pid}.log")
69 assert_raises(Errno::ESRCH) { loop { Process.kill(0, child); sleep 0.2 } }
70 assert((Time.now - t0) < 60)
77 app = lambda { |env| wr.syswrite('.'); sleep; [ 200, {}, [] ] }
78 redirect_test_io { HttpServer.new(app, @server_opts).start.join }
81 wait_workers_ready("test_stderr.#{pid}.log", 1)
82 sock = TCPSocket.new('127.0.0.1', @port)
83 sock.syswrite("GET / HTTP/1.0\r\n\r\n")
84 buf = rd.readpartial(1)
85 wait_master_ready("test_stderr.#{pid}.log")
86 Process.kill(:INT, pid)
90 assert_raises(EOFError,Errno::ECONNRESET,Errno::EPIPE,Errno::EINVAL,
92 buf = sock.sysread(4096)
97 def test_timeout_slow_response
99 app = lambda { |env| sleep }
100 opts = @server_opts.merge(:timeout => 3)
101 redirect_test_io { HttpServer.new(app, opts).start.join }
104 wait_workers_ready("test_stderr.#{pid}.log", 1)
105 sock = TCPSocket.new('127.0.0.1', @port)
106 sock.syswrite("GET / HTTP/1.0\r\n\r\n")
109 assert_raises(EOFError,Errno::ECONNRESET,Errno::EPIPE,Errno::EINVAL,
111 buf = sock.sysread(4096)
115 assert diff > 1.0, "diff was #{diff.inspect}"
118 Process.kill(:TERM, pid) rescue nil
121 def test_response_write
123 [ 200, { 'Content-Type' => 'text/plain', 'X-Pid' => Process.pid.to_s },
124 Dd.new(@bs, @count) ]
126 redirect_test_io { @server = HttpServer.new(app, @server_opts).start }
127 wait_workers_ready("test_stderr.#{$$}.log", 1)
128 sock = TCPSocket.new('127.0.0.1', @port)
129 sock.syswrite("GET / HTTP/1.0\r\n\r\n")
131 header_len = pid = nil
132 buf = sock.sysread(16384, buf)
133 pid = buf[/\r\nX-Pid: (\d+)\r\n/, 1].to_i
134 header_len = buf[/\A(.+?\r\n\r\n)/m, 1].size
135 assert pid > 0, "pid not positive: #{pid.inspect}"
137 size_before = @tmp.stat.size
138 assert_raises(EOFError,Errno::ECONNRESET,Errno::EPIPE,Errno::EINVAL,
141 3.times { Process.kill(:HUP, pid) }
142 sock.sysread(16384, buf)
144 3.times { Process.kill(:HUP, pid) }
148 redirect_test_io { @server.stop(true) }
149 # can't check for == since pending signals get merged
150 assert size_before < @tmp.stat.size
151 got = read - header_len
152 expect = @bs * @count
153 assert_equal(expect, got, "expect=#{expect} got=#{got}")
154 assert_nil sock.close
157 def test_request_read
159 while env['rack.input'].read(4096)
161 [ 200, {'Content-Type'=>'text/plain', 'X-Pid'=>Process.pid.to_s}, [] ]
163 redirect_test_io { @server = HttpServer.new(app, @server_opts).start }
165 wait_workers_ready("test_stderr.#{$$}.log", 1)
166 sock = TCPSocket.new('127.0.0.1', @port)
167 sock.syswrite("GET / HTTP/1.0\r\n\r\n")
168 pid = sock.sysread(4096)[/\r\nX-Pid: (\d+)\r\n/, 1].to_i
169 assert_nil sock.close
171 assert pid > 0, "pid not positive: #{pid.inspect}"
172 sock = TCPSocket.new('127.0.0.1', @port)
173 sock.syswrite("PUT / HTTP/1.0\r\n")
174 sock.syswrite("Content-Length: #{@bs * @count}\r\n\r\n")
175 1000.times { Process.kill(:HUP, pid) }
176 size_before = @tmp.stat.size
177 killer = fork { loop { Process.kill(:HUP, pid); sleep(0.01) } }
179 @count.times { sock.syswrite(buf) }
180 Process.kill(:KILL, killer)
181 Process.waitpid2(killer)
182 redirect_test_io { @server.stop(true) }
183 # can't check for == since pending signals get merged
184 assert size_before < @tmp.stat.size
185 assert_equal pid, sock.sysread(4096)[/\r\nX-Pid: (\d+)\r\n/, 1].to_i
186 assert_nil sock.close