simplify examples for 1.0.0 API
[ruby_io_splice.git] / test / test_io_splice.rb
blob2ae8f4a76bf117aae13fb46e8d625c9f97cb7a6d
1 # -*- encoding: binary -*-
2 require 'test/unit'
3 require 'tempfile'
4 require 'socket'
5 require 'io/nonblock'
6 $-w = true
7 require 'io/splice'
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')
18   retries = 100
19   base = 5000
20   port = sock = nil
21   begin
22     begin
23       port = base + rand(32768 - base)
24       while port == 8080
25         port = base + rand(32768 - base)
26       end
28       sock = Socket.new(Socket::AF_INET, Socket::SOCK_STREAM, 0)
29       sock.bind(Socket.pack_sockaddr_in(port, addr))
30       sock.listen(5)
31     rescue Errno::EADDRINUSE, Errno::EACCES
32       sock.close rescue nil
33       retry if (retries -= 1) >= 0
34     end
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 }
43   rescue Errno::EEXIST
44     sock.close rescue nil
45     retry
46   end
47   sock.close rescue nil
48   port
49 end
51 class Test_IO_Splice < Test::Unit::TestCase
53   def test_splice
54     str = 'abcde'
55     size = 5
56     rd, wr = IO.pipe
57     tmp = Tempfile.new('ruby_io_splice')
59     assert_nothing_raised {
60       tmp.syswrite(str)
61       tmp.sysseek(0)
62     }
64     nr = IO.splice(tmp.fileno, nil, wr.fileno, nil, size, 0)
65     assert_equal size, nr
66     assert_equal str, rd.sysread(size)
67   end
69   def test_splice_io
70     str = 'abcde'
71     size = 5
72     rd, wr = IO.pipe
73     tmp = Tempfile.new('ruby_io_splice')
75     assert_nothing_raised {
76       tmp.syswrite(str)
77       tmp.sysseek(0)
78     }
80     nr = IO.splice(tmp, nil, wr, nil, size, 0)
81     assert_equal size, nr
82     assert_equal str, rd.sysread(size)
83   end
85   def test_splice_io_ish
86     str = 'abcde'
87     size = 5
88     rd, wr = IO.pipe
89     tmp = Tempfile.new('ruby_io_splice')
90     io_ish = [ tmp ]
91     def io_ish.to_io
92       first.to_io
93     end
95     assert_nothing_raised {
96       tmp.syswrite(str)
97       tmp.sysseek(0)
98     }
100     nr = IO.splice(io_ish, nil, wr, nil, size, 0)
101     assert_equal size, nr
102     assert_equal str, rd.sysread(size)
103   end
105   def test_splice_in_offset
106     str = 'abcde'
107     off = 3
108     len = 2
109     rd, wr = IO.pipe
110     tmp = Tempfile.new('ruby_io_splice')
112     assert_nothing_raised {
113       tmp.syswrite(str)
114       tmp.sysseek(0)
115     }
117     nr = IO.splice(tmp.fileno, off, wr.fileno, nil, len, 0)
118     assert_equal len, nr
119     assert_equal 'de', rd.sysread(len)
120   end
122   def test_splice_out_offset
123     str = 'abcde'
124     rd, wr = IO.pipe
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)
129     assert_equal 5, nr
130     assert_nothing_raised { tmp.sysseek(0) }
131     assert_equal "\0\0\0abcde", tmp.sysread(9)
132   end
134   def test_splice_nonblock
135     rd, wr = IO.pipe
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)
140     }
141   end
143   def test_splice_eof
144     rd, wr = IO.pipe
145     tmp = Tempfile.new('ruby_io_splice')
146     wr.syswrite 'abc'
147     wr.close
149     nr = IO.splice(rd.fileno, nil, tmp.fileno, 0, 5, IO::Splice::F_NONBLOCK)
150     assert_equal 3, nr
151     assert_raises(EOFError) {
152       IO.splice(rd.fileno, nil, tmp.fileno, 0, 5, IO::Splice::F_NONBLOCK)
153     }
154   end
156   def test_splice_nonblock_socket
157     port = unused_port
158     server = TCPServer.new('127.0.0.1', port)
159     rp, wp = IO.pipe
160     rs = TCPSocket.new('127.0.0.1', port)
161     rs.nonblock = true
162     assert_raises(Errno::EAGAIN) { IO.splice(rs, nil, wp, nil, 1024, 0) }
163     rs.close
164     server.close
165   end
167   def test_tee
168     str = 'abcde'
169     size = 5
170     rda, wra = IO.pipe
171     rdb, wrb = IO.pipe
173     assert_nothing_raised { wra.syswrite(str) }
174     nr = IO.tee(rda.fileno, wrb.fileno, size, 0)
175     assert_equal 5, nr
176     assert_equal str, rdb.sysread(5)
177     assert_equal str, rda.sysread(5)
178   end
180   def test_tee_eof
181     rda, wra = IO.pipe
182     rdb, wrb = IO.pipe
183     wra.close
184     assert_raises(EOFError) { IO.tee(rda.fileno, wrb.fileno, 4096, 0) }
185   end
187   def test_tee_nonblock
188     rda, wra = IO.pipe
189     rdb, wrb = IO.pipe
190     assert_raises(Errno::EAGAIN) {
191       IO.tee(rda.fileno, wrb.fileno, 4096, IO::Splice::F_NONBLOCK)
192     }
193   end
195   def test_tee_io
196     str = 'abcde'
197     size = 5
198     rda, wra = IO.pipe
199     rdb, wrb = IO.pipe
201     assert_nothing_raised { wra.syswrite(str) }
202     nr = IO.tee(rda, wrb, size, 0)
203     assert_equal 5, nr
204     assert_equal str, rdb.sysread(5)
205     assert_equal str, rda.sysread(5)
206   end
208   def test_vmsplice_array
209     data = %w(hello world how are you today)
210     r, w = IO.pipe
211     n = IO.vmsplice(w.fileno, data, 0)
212     assert_equal data.join('').size, n
213     assert_equal data.join(''), r.readpartial(16384)
214   end
216   def test_vmsplice_array_io
217     data = %w(hello world how are you today)
218     r, w = IO.pipe
219     n = IO.vmsplice(w, data, 0)
220     assert_equal data.join('').size, n
221     assert_equal data.join(''), r.readpartial(16384)
222   end
224   def test_vmsplice_nonblock
225     data = %w(hello world how are you today)
226     r, w = IO.pipe
227     w.syswrite('.' * IO::Splice::PIPE_CAPA)
228     assert_raises(Errno::EAGAIN) {
229       IO.vmsplice(w.fileno, data, IO::Splice::F_NONBLOCK)
230     }
231   end
233   def test_vmsplice_in_full
234     empty = ""
236     # bs * count should be > PIPE_BUF
237     [ [ 512, 512 ], [ 131073, 3 ], [ 4098, 64 ] ].each do |(bs,count)|
238       rd, wr = IO.pipe
239       buf = File.open('/dev/urandom', 'rb') { |fp| fp.sysread(bs) }
241       vec = (1..count).map { buf }
242       pid = fork do
243         wr.close
244         tmp = []
245         begin
246           sleep 0.005
247           tmp << rd.readpartial(8192, buf)
248         rescue EOFError
249           break
250         end while true
251         ok = (vec.join(empty) == tmp.join(empty))
252         exit! ok
253       end
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?
259     end
260   end
262   def test_vmsplice_nil
263     data = %w(hello world how are you today)
264     assert_raises(TypeError) { IO.vmsplice(nil, data, 0) }
265   end
267   def test_constants
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}")
271     }
272     assert IO::Splice::PIPE_CAPA >= IO::Splice::PIPE_BUF
273   end
275   def test_splice_copy_stream_file_to_file_small
276     a, b = Tempfile.new('a'), Tempfile.new('b')
277     a.syswrite 'hello world'
278     a.sysseek(0)
279     IO::Splice.copy_stream(a, b)
280     b.rewind
281     assert_equal 'hello world', b.read
282   end
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')
287     a.syswrite buf
288     a.sysseek(0)
289     IO::Splice.copy_stream(a, b)
290     b.rewind
291     assert_equal buf, b.read
292   end
294   def test_splice_copy_stream_file_to_file_len
295     a, b = Tempfile.new('a'), Tempfile.new('b')
296     a.syswrite 'hello world'
297     a.sysseek(0)
298     IO::Splice.copy_stream(a, b, 5)
299     b.rewind
300     assert_equal 'hello', b.read
301   end
303   def test_splice_copy_stream_pipe_to_file_len
304     a = Tempfile.new('a')
305     r, w = IO.pipe
306     w.syswrite 'hello world'
307     IO::Splice.copy_stream(r, a, 5)
308     a.rewind
309     assert_equal 'hello', a.read
310   end