1 # -*- encoding: binary -*-
9 # unused_port provides an unused port on +addr+ usable for TCP that is
10 # guaranteed to be unused across all unicorn builds on that system. It
11 # prevents race conditions by using a lock file other unicorn builds
12 # will see. This is required if you perform several builds in parallel
13 # with a continuous integration system or run tests in parallel via
14 # gmake. This is NOT guaranteed to be race-free if you run other
15 # processes that bind to random ports for testing (but the window
16 # for a race condition is very small).
17 def unused_port(addr = '127.0.0.1')
23 port = base + rand(32768 - base)
25 port = base + rand(32768 - base)
28 sock = Socket.new(Socket::AF_INET, Socket::SOCK_STREAM, 0)
29 sock.bind(Socket.pack_sockaddr_in(port, addr))
31 rescue Errno::EADDRINUSE, Errno::EACCES
33 retry if (retries -= 1) >= 0
36 # since we'll end up closing the random port we just got, there's a race
37 # condition could allow the random port we just chose to reselect itself
38 # when running tests in parallel with gmake. Create a lock file while
39 # we have the port here to ensure that does not happen .
40 lock_path = "#{Dir::tmpdir}/unicorn_test.#{addr}:#{port}.lock"
41 lock = File.open(lock_path, File::WRONLY|File::CREAT|File::EXCL, 0600)
42 at_exit { File.unlink(lock_path) rescue nil }
51 class Test_IO_Splice < Test::Unit::TestCase
57 tmp = Tempfile.new('ruby_io_splice')
59 assert_nothing_raised {
64 nr = IO.splice(tmp.fileno, nil, wr.fileno, nil, size, 0)
66 assert_equal str, rd.sysread(size)
73 tmp = Tempfile.new('ruby_io_splice')
75 assert_nothing_raised {
80 nr = IO.splice(tmp, nil, wr, nil, size, 0)
82 assert_equal str, rd.sysread(size)
85 def test_splice_io_ish
89 tmp = Tempfile.new('ruby_io_splice')
95 assert_nothing_raised {
100 nr = IO.splice(io_ish, nil, wr, nil, size, 0)
101 assert_equal size, nr
102 assert_equal str, rd.sysread(size)
105 def test_splice_in_offset
110 tmp = Tempfile.new('ruby_io_splice')
112 assert_nothing_raised {
117 nr = IO.splice(tmp.fileno, off, wr.fileno, nil, len, 0)
119 assert_equal 'de', rd.sysread(len)
122 def test_splice_out_offset
125 tmp = Tempfile.new('ruby_io_splice')
127 assert_nothing_raised { wr.syswrite(str) }
128 nr = IO.splice(rd.fileno, nil, tmp.fileno, 3, str.size, 0)
130 assert_nothing_raised { tmp.sysseek(0) }
131 assert_equal "\0\0\0abcde", tmp.sysread(9)
134 def test_splice_nonblock
136 tmp = Tempfile.new('ruby_io_splice')
138 assert_raises(Errno::EAGAIN) {
139 IO.splice(rd.fileno, nil, tmp.fileno, 0, 5, IO::Splice::F_NONBLOCK)
145 tmp = Tempfile.new('ruby_io_splice')
149 nr = IO.splice(rd.fileno, nil, tmp.fileno, 0, 5, IO::Splice::F_NONBLOCK)
151 assert_raises(EOFError) {
152 IO.splice(rd.fileno, nil, tmp.fileno, 0, 5, IO::Splice::F_NONBLOCK)
156 def test_splice_nonblock_socket
158 server = TCPServer.new('127.0.0.1', port)
160 rs = TCPSocket.new('127.0.0.1', port)
162 assert_raises(Errno::EAGAIN) { IO.splice(rs, nil, wp, nil, 1024, 0) }
173 assert_nothing_raised { wra.syswrite(str) }
174 nr = IO.tee(rda.fileno, wrb.fileno, size, 0)
176 assert_equal str, rdb.sysread(5)
177 assert_equal str, rda.sysread(5)
184 assert_raises(EOFError) { IO.tee(rda.fileno, wrb.fileno, 4096, 0) }
187 def test_tee_nonblock
190 assert_raises(Errno::EAGAIN) {
191 IO.tee(rda.fileno, wrb.fileno, 4096, IO::Splice::F_NONBLOCK)
201 assert_nothing_raised { wra.syswrite(str) }
202 nr = IO.tee(rda, wrb, size, 0)
204 assert_equal str, rdb.sysread(5)
205 assert_equal str, rda.sysread(5)
208 def test_vmsplice_array
209 data = %w(hello world how are you today)
211 n = IO.vmsplice(w.fileno, data, 0)
212 assert_equal data.join('').size, n
213 assert_equal data.join(''), r.readpartial(16384)
216 def test_vmsplice_array_io
217 data = %w(hello world how are you today)
219 n = IO.vmsplice(w, data, 0)
220 assert_equal data.join('').size, n
221 assert_equal data.join(''), r.readpartial(16384)
224 def test_vmsplice_nonblock
225 data = %w(hello world how are you today)
227 w.syswrite('.' * IO::Splice::PIPE_CAPA)
228 assert_raises(Errno::EAGAIN) {
229 IO.vmsplice(w.fileno, data, IO::Splice::F_NONBLOCK)
233 def test_vmsplice_in_full
236 # bs * count should be > PIPE_BUF
237 [ [ 512, 512 ], [ 131073, 3 ], [ 4098, 64 ] ].each do |(bs,count)|
239 buf = File.open('/dev/urandom', 'rb') { |fp| fp.sysread(bs) }
241 vec = (1..count).map { buf }
247 tmp << rd.readpartial(8192, buf)
251 ok = (vec.join(empty) == tmp.join(empty))
254 assert_nothing_raised { rd.close }
255 assert_equal(bs * count, IO.vmsplice(wr.fileno, vec, 0))
256 assert_nothing_raised { wr.close }
257 _, status = Process.waitpid2(pid)
258 assert status.success?
262 def test_vmsplice_nil
263 data = %w(hello world how are you today)
264 assert_raises(TypeError) { IO.vmsplice(nil, data, 0) }
268 assert IO::Splice::PIPE_BUF > 0
269 %w(move nonblock more gift).each { |x|
270 assert Integer === IO::Splice.const_get("F_#{x.upcase}")
272 assert IO::Splice::PIPE_CAPA >= IO::Splice::PIPE_BUF
275 def test_splice_copy_stream_file_to_file_small
276 a, b = Tempfile.new('a'), Tempfile.new('b')
277 a.syswrite 'hello world'
279 IO::Splice.copy_stream(a, b)
281 assert_equal 'hello world', b.read
284 def test_splice_copy_stream_file_to_file_big
285 buf = ('ab' * IO::Splice::PIPE_CAPA) + 'hi'
286 a, b = Tempfile.new('a'), Tempfile.new('b')
289 IO::Splice.copy_stream(a, b)
291 assert_equal buf, b.read
294 def test_splice_copy_stream_file_to_file_len
295 a, b = Tempfile.new('a'), Tempfile.new('b')
296 a.syswrite 'hello world'
298 IO::Splice.copy_stream(a, b, 5)
300 assert_equal 'hello', b.read
303 def test_splice_copy_stream_pipe_to_file_len
304 a = Tempfile.new('a')
306 w.syswrite 'hello world'
307 IO::Splice.copy_stream(r, a, 5)
309 assert_equal 'hello', a.read