1 # -*- encoding: binary -*-
10 # unused_port provides an unused port on +addr+ usable for TCP that is
11 # guaranteed to be unused across all unicorn builds on that system. It
12 # prevents race conditions by using a lock file other unicorn builds
13 # will see. This is required if you perform several builds in parallel
14 # with a continuous integration system or run tests in parallel via
15 # gmake. This is NOT guaranteed to be race-free if you run other
16 # processes that bind to random ports for testing (but the window
17 # for a race condition is very small).
18 def unused_port(addr = '127.0.0.1')
24 port = base + rand(32768 - base)
26 port = base + rand(32768 - base)
29 sock = Socket.new(Socket::AF_INET, Socket::SOCK_STREAM, 0)
30 sock.bind(Socket.pack_sockaddr_in(port, addr))
32 rescue Errno::EADDRINUSE, Errno::EACCES
34 retry if (retries -= 1) >= 0
37 # since we'll end up closing the random port we just got, there's a race
38 # condition could allow the random port we just chose to reselect itself
39 # when running tests in parallel with gmake. Create a lock file while
40 # we have the port here to ensure that does not happen .
41 lock_path = "#{Dir::tmpdir}/unicorn_test.#{addr}:#{port}.lock"
42 lock = File.open(lock_path, File::WRONLY|File::CREAT|File::EXCL, 0600)
43 at_exit { File.unlink(lock_path) rescue nil }
52 class Test_IO_Splice < Test::Unit::TestCase
58 tmp = Tempfile.new('ruby_io_splice')
60 assert_nothing_raised {
65 nr = IO.splice(tmp.fileno, nil, wr.fileno, nil, size, 0)
67 assert_equal str, rd.sysread(size)
74 tmp = Tempfile.new('ruby_io_splice')
76 assert_nothing_raised {
81 nr = IO.splice(tmp, nil, wr, nil, size, 0)
83 assert_equal str, rd.sysread(size)
86 def test_splice_io_ish
90 tmp = Tempfile.new('ruby_io_splice')
96 assert_nothing_raised {
101 nr = IO.splice(io_ish, nil, wr, nil, size, 0)
102 assert_equal size, nr
103 assert_equal str, rd.sysread(size)
106 def test_splice_in_offset
111 tmp = Tempfile.new('ruby_io_splice')
113 assert_nothing_raised {
118 nr = IO.splice(tmp.fileno, off, wr.fileno, nil, len, 0)
120 assert_equal 'de', rd.sysread(len)
123 def test_splice_out_offset
126 tmp = Tempfile.new('ruby_io_splice')
128 assert_nothing_raised { wr.syswrite(str) }
129 nr = IO.splice(rd.fileno, nil, tmp.fileno, 3, str.size, 0)
131 assert_nothing_raised { tmp.sysseek(0) }
132 assert_equal "\0\0\0abcde", tmp.sysread(9)
135 def test_splice_nonblock
137 tmp = Tempfile.new('ruby_io_splice')
139 assert_raises(Errno::EAGAIN) {
140 IO.splice(rd.fileno, nil, tmp.fileno, 0, 5, IO::Splice::F_NONBLOCK)
146 tmp = Tempfile.new('ruby_io_splice')
150 nr = IO.splice(rd.fileno, nil, tmp.fileno, 0, 5, IO::Splice::F_NONBLOCK)
152 assert_raises(EOFError) {
153 IO.splice(rd.fileno, nil, tmp.fileno, 0, 5, IO::Splice::F_NONBLOCK)
157 def test_splice_nonblock_socket
159 server = TCPServer.new('127.0.0.1', port)
161 rs = TCPSocket.new('127.0.0.1', port)
163 assert_raises(Errno::EAGAIN) { IO.splice(rs, nil, wp, nil, 1024, 0) }
174 assert_nothing_raised { wra.syswrite(str) }
175 nr = IO.tee(rda.fileno, wrb.fileno, size, 0)
177 assert_equal str, rdb.sysread(5)
178 assert_equal str, rda.sysread(5)
185 assert_raises(EOFError) { IO.tee(rda.fileno, wrb.fileno, 4096, 0) }
188 def test_tee_nonblock
191 assert_raises(Errno::EAGAIN) {
192 IO.tee(rda.fileno, wrb.fileno, 4096, IO::Splice::F_NONBLOCK)
202 assert_nothing_raised { wra.syswrite(str) }
203 nr = IO.tee(rda, wrb, size, 0)
205 assert_equal str, rdb.sysread(5)
206 assert_equal str, rda.sysread(5)
209 def test_vmsplice_array
210 data = %w(hello world how are you today)
212 n = IO.vmsplice(w.fileno, data, 0)
213 assert_equal data.join('').size, n
214 assert_equal data.join(''), r.readpartial(16384)
217 def test_vmsplice_string
219 assert_equal 5, IO.vmsplice(w, 'hello', 0)
220 assert_equal 'hello', r.read(5)
223 def test_vmsplice_array_io
224 data = %w(hello world how are you today)
226 n = IO.vmsplice(w, data, 0)
227 assert_equal data.join('').size, n
228 assert_equal data.join(''), r.readpartial(16384)
231 def test_vmsplice_nonblock
232 data = %w(hello world how are you today)
234 w.syswrite('.' * IO::Splice::PIPE_CAPA)
235 assert_raises(Errno::EAGAIN) {
236 IO.vmsplice(w.fileno, data, IO::Splice::F_NONBLOCK)
240 def test_vmsplice_in_full
243 # bs * count should be > PIPE_BUF
244 [ [ 512, 512 ], [ 131073, 3 ], [ 4098, 64 ] ].each do |(bs,count)|
246 buf = File.open('/dev/urandom', 'rb') { |fp| fp.sysread(bs) }
248 vec = (1..count).map { buf }
254 tmp << rd.readpartial(8192)
258 ok = (vec.join(empty) == tmp.join(empty))
261 assert_nothing_raised { rd.close }
262 assert_equal(bs * count, IO.vmsplice(wr.fileno, vec, 0))
263 assert_nothing_raised { wr.close }
264 _, status = Process.waitpid2(pid)
265 assert status.success?
269 def test_vmsplice_nil
270 data = %w(hello world how are you today)
271 assert_raises(TypeError) { IO.vmsplice(nil, data, 0) }
275 assert IO::Splice::PIPE_BUF > 0
276 %w(move nonblock more gift).each { |x|
277 assert Integer === IO::Splice.const_get("F_#{x.upcase}")
279 assert IO::Splice::PIPE_CAPA >= IO::Splice::PIPE_BUF
282 def test_splice_copy_stream_file_to_file_small
283 a, b = Tempfile.new('a'), Tempfile.new('b')
284 a.syswrite 'hello world'
286 IO::Splice.copy_stream(a, b)
288 assert_equal 'hello world', b.read
291 def test_splice_copy_stream_file_to_file_big
292 buf = ('ab' * IO::Splice::PIPE_CAPA) + 'hi'
293 a, b = Tempfile.new('a'), Tempfile.new('b')
296 IO::Splice.copy_stream(a, b)
298 assert_equal buf, b.read
301 def test_splice_copy_stream_file_to_file_big_partial
302 nr = IO::Splice::PIPE_CAPA
303 buf = ('ab' * nr) + 'hi'
304 a, b = Tempfile.new('a'), Tempfile.new('b')
307 assert_equal nr, IO::Splice.copy_stream(a, b, nr)
309 assert_equal('ab' * (nr/2), b.read)
312 def test_splice_copy_stream_file_to_file_len
313 a, b = Tempfile.new('a'), Tempfile.new('b')
314 a.syswrite 'hello world'
316 IO::Splice.copy_stream(a, b, 5)
318 assert_equal 'hello', b.read
321 def test_splice_copy_stream_pipe_to_file_len
322 a = Tempfile.new('a')
324 w.syswrite 'hello world'
325 IO::Splice.copy_stream(r, a, 5)
327 assert_equal 'hello', a.read
330 def test_splice_copy_stream_paths
331 a = Tempfile.new('a')
332 b = Tempfile.new('a')
333 a.syswrite('hello world')
334 IO::Splice.copy_stream(a.path, b.path, 5)
335 assert_equal 'hello', b.read
338 def test_splice_copy_stream_src_offset
339 a = Tempfile.new('a')
340 b = Tempfile.new('a')
341 a.syswrite('hello world')
342 IO::Splice.copy_stream(a.path, b.path, 5, 6)
343 assert_equal 'world', b.read
346 def test_copy_stream_nonblock_src
348 server = TCPServer.new('127.0.0.1', port)
350 rs = TCPSocket.new('127.0.0.1', port)
353 assert_raises(Timeout::Error) do
354 timeout(0.05) { nr += IO::Splice.copy_stream(rs, wp, 5) }
361 def test_copy_stream_nonblock_dst
363 server = TCPServer.new('127.0.0.1', port)
365 rs = TCPSocket.new('127.0.0.1', port)
367 client = server.accept
368 buf = ' ' * IO::Splice::PIPE_CAPA
370 assert_raises(Timeout::Error) do
373 wp.write_nonblock(buf)
377 nr += IO::Splice.copy_stream(rp, rs, IO::Splice::PIPE_CAPA)
381 assert_equal nr, client.read(nr).size
386 def test_copy_stream_eof
388 w.syswrite 'hello world'
390 a = Tempfile.new('a')
391 assert_equal 11, IO::Splice.copy_stream(r, a)
393 assert_equal 'hello world', a.read
398 assert Integer, r.pipe_size
399 assert(r.pipe_size >= 512)
400 assert_nothing_raised { w.pipe_size = 8192 }
401 assert 8192, r.pipe_size
404 assert_raises(Errno::EBUSY) { r.pipe_size = 4096 }
406 pipe_max_size = File.read("/proc/sys/fs/pipe-max-size").to_i
407 assert_nothing_raised { r.pipe_size = pipe_max_size }
408 assert_raises(Errno::EPERM) { r.pipe_size = pipe_max_size * 2 }
409 end if IO.method_defined?(:pipe_size)