flags are optional for splice and trysplice
[ruby_io_splice.git] / test / test_io_splice.rb
blob6b287ae9bff776673bd7846ee67e5c784dde216e
1 # -*- encoding: binary -*-
2 require 'test/unit'
3 require 'tempfile'
4 require 'socket'
5 require 'io/nonblock'
6 require 'timeout'
7 $-w = true
8 require 'io/splice'
10 class Test_IO_Splice < Test::Unit::TestCase
12   def test_splice
13     str = 'abcde'
14     size = 5
15     rd, wr = IO.pipe
16     tmp = Tempfile.new('ruby_io_splice')
18     assert_nothing_raised {
19       tmp.syswrite(str)
20       tmp.sysseek(0)
21     }
23     nr = IO.splice(tmp.fileno, nil, wr.fileno, nil, size, 0)
24     assert_equal size, nr
25     assert_equal str, rd.sysread(size)
26   end
28   def test_splice_io
29     str = 'abcde'
30     size = 5
31     rd, wr = IO.pipe
32     tmp = Tempfile.new('ruby_io_splice')
34     assert_nothing_raised {
35       tmp.syswrite(str)
36       tmp.sysseek(0)
37     }
39     nr = IO.splice(tmp, nil, wr, nil, size, 0)
40     assert_equal size, nr
41     assert_equal str, rd.sysread(size)
42   end
44   def test_splice_io_noflags
45     str = 'abcde'
46     size = 5
47     rd, wr = IO.pipe
48     tmp = Tempfile.new('ruby_io_splice')
50     assert_nothing_raised {
51       tmp.syswrite(str)
52       tmp.sysseek(0)
53     }
55     nr = IO.splice(tmp, nil, wr, nil, size)
56     assert_equal size, nr
57     assert_equal str, rd.sysread(size)
58   end
60   def test_trysplice_io_noflags
61     str = 'abcde'
62     size = 5
63     rd, wr = IO.pipe
64     tmp = Tempfile.new('ruby_io_splice')
66     assert_nothing_raised {
67       tmp.syswrite(str)
68       tmp.sysseek(0)
69     }
71     nr = IO.trysplice(tmp, nil, wr, nil, size)
72     assert_equal size, nr
73     assert_equal str, rd.sysread(size)
74   end
76   def test_splice_io_ish
77     str = 'abcde'
78     size = 5
79     rd, wr = IO.pipe
80     tmp = Tempfile.new('ruby_io_splice')
81     io_ish = [ tmp ]
82     def io_ish.to_io
83       first.to_io
84     end
86     assert_nothing_raised {
87       tmp.syswrite(str)
88       tmp.sysseek(0)
89     }
91     nr = IO.splice(io_ish, nil, wr, nil, size, 0)
92     assert_equal size, nr
93     assert_equal str, rd.sysread(size)
94   end
96   def test_splice_in_offset
97     str = 'abcde'
98     off = 3
99     len = 2
100     rd, wr = IO.pipe
101     tmp = Tempfile.new('ruby_io_splice')
103     assert_nothing_raised {
104       tmp.syswrite(str)
105       tmp.sysseek(0)
106     }
108     nr = IO.splice(tmp.fileno, off, wr.fileno, nil, len, 0)
109     assert_equal len, nr
110     assert_equal 'de', rd.sysread(len)
111   end
113   def test_splice_out_offset
114     str = 'abcde'
115     rd, wr = IO.pipe
116     tmp = Tempfile.new('ruby_io_splice')
118     assert_nothing_raised { wr.syswrite(str) }
119     nr = IO.splice(rd.fileno, nil, tmp.fileno, 3, str.size, 0)
120     assert_equal 5, nr
121     assert_nothing_raised { tmp.sysseek(0) }
122     assert_equal "\0\0\0abcde", tmp.sysread(9)
123   end
125   def test_splice_nonblock
126     rd, wr = IO.pipe
127     tmp = Tempfile.new('ruby_io_splice')
129     assert_raises(Errno::EAGAIN) {
130       IO.splice(rd.fileno, nil, tmp.fileno, 0, 5, IO::Splice::F_NONBLOCK)
131     }
132   end
134   def test_trysplice_nonblock
135     rd, wr = IO.pipe
136     tmp = Tempfile.new('ruby_io_splice')
137     assert_equal :EAGAIN,
138            IO.trysplice(rd, nil, tmp, 0, 5, IO::Splice::F_NONBLOCK)
139   end
141   def test_splice_eof
142     rd, wr = IO.pipe
143     tmp = Tempfile.new('ruby_io_splice')
144     wr.syswrite 'abc'
145     wr.close
147     nr = IO.splice(rd.fileno, nil, tmp.fileno, 0, 5, IO::Splice::F_NONBLOCK)
148     assert_equal 3, nr
149     assert_raises(EOFError) {
150       IO.splice(rd.fileno, nil, tmp.fileno, 0, 5, IO::Splice::F_NONBLOCK)
151     }
152   end
154   def test_trysplice_eof
155     rd, wr = IO.pipe
156     tmp = Tempfile.new('ruby_io_splice')
157     wr.syswrite 'abc'
158     wr.close
160     nr = IO.trysplice(rd, nil, tmp, 0, 5, IO::Splice::F_NONBLOCK)
161     assert_equal 3, nr
162     assert_nil IO.trysplice(rd, nil, tmp, 0, 5, IO::Splice::F_NONBLOCK)
163   end
165   def test_splice_nonblock_socket
166     server = TCPServer.new('127.0.0.1', 0)
167     port = server.addr[1]
168     rp, wp = IO.pipe
169     rs = TCPSocket.new('127.0.0.1', port)
170     rs.nonblock = true
171     assert_raises(Errno::EAGAIN) { IO.splice(rs, nil, wp, nil, 1024, 0) }
172     rs.close
173     server.close
174   end
176   def test_tee
177     str = 'abcde'
178     size = 5
179     rda, wra = IO.pipe
180     rdb, wrb = IO.pipe
182     assert_nothing_raised { wra.syswrite(str) }
183     nr = IO.tee(rda.fileno, wrb.fileno, size, 0)
184     assert_equal 5, nr
185     assert_equal str, rdb.sysread(5)
186     assert_equal str, rda.sysread(5)
187   end
189   def test_tee_eof
190     rda, wra = IO.pipe
191     rdb, wrb = IO.pipe
192     wra.close
193     assert_raises(EOFError) { IO.tee(rda.fileno, wrb.fileno, 4096, 0) }
194   end
196   def test_tee_nonblock
197     rda, wra = IO.pipe
198     rdb, wrb = IO.pipe
199     assert_raises(Errno::EAGAIN) {
200       IO.tee(rda.fileno, wrb.fileno, 4096, IO::Splice::F_NONBLOCK)
201     }
202   end
204   def test_tee_io
205     str = 'abcde'
206     size = 5
207     rda, wra = IO.pipe
208     rdb, wrb = IO.pipe
210     assert_nothing_raised { wra.syswrite(str) }
211     nr = IO.tee(rda, wrb, size, 0)
212     assert_equal 5, nr
213     assert_equal str, rdb.sysread(5)
214     assert_equal str, rda.sysread(5)
215   end
217   def test_vmsplice_array
218     data = %w(hello world how are you today)
219     r, w = IO.pipe
220     n = IO.vmsplice(w.fileno, data, 0)
221     assert_equal data.join('').size, n
222     assert_equal data.join(''), r.readpartial(16384)
223   end
225   def test_vmsplice_string
226     r, w = IO.pipe
227     assert_equal 5, IO.vmsplice(w, 'hello', 0)
228     assert_equal 'hello', r.read(5)
229   end
231   def test_vmsplice_array_io
232     data = %w(hello world how are you today)
233     r, w = IO.pipe
234     n = IO.vmsplice(w, data, 0)
235     assert_equal data.join('').size, n
236     assert_equal data.join(''), r.readpartial(16384)
237   end
239   def test_vmsplice_nonblock
240     data = %w(hello world how are you today)
241     r, w = IO.pipe
242     w.syswrite('.' * IO::Splice::PIPE_CAPA)
243     assert_raises(Errno::EAGAIN) {
244       IO.vmsplice(w.fileno, data, IO::Splice::F_NONBLOCK)
245     }
246   end
248   def test_vmsplice_in_full
249     empty = ""
251     # bs * count should be > PIPE_BUF
252     [ [ 512, 512 ], [ 131073, 3 ], [ 4098, 64 ] ].each do |(bs,count)|
253       rd, wr = IO.pipe
254       buf = File.open('/dev/urandom', 'rb') { |fp| fp.sysread(bs) }
256       vec = (1..count).map { buf }
257       pid = fork do
258         wr.close
259         tmp = []
260         begin
261           sleep 0.005
262           tmp << rd.readpartial(8192)
263         rescue EOFError
264           break
265         end while true
266         ok = (vec.join(empty) == tmp.join(empty))
267         exit! ok
268       end
269       assert_nothing_raised { rd.close }
270       assert_equal(bs * count, IO.vmsplice(wr.fileno, vec, 0))
271       assert_nothing_raised { wr.close }
272       _, status = Process.waitpid2(pid)
273       assert status.success?
274     end
275   end
277   def test_vmsplice_nil
278     data = %w(hello world how are you today)
279     assert_raises(TypeError) { IO.vmsplice(nil, data, 0) }
280   end
282   def test_constants
283     assert IO::Splice::PIPE_BUF > 0
284     %w(move nonblock more gift).each { |x|
285       assert Integer === IO::Splice.const_get("F_#{x.upcase}")
286     }
287     assert IO::Splice::PIPE_CAPA >= IO::Splice::PIPE_BUF
288   end
290   def test_splice_copy_stream_file_to_file_small
291     a, b = Tempfile.new('a'), Tempfile.new('b')
292     a.syswrite 'hello world'
293     a.sysseek(0)
294     IO::Splice.copy_stream(a, b)
295     b.rewind
296     assert_equal 'hello world', b.read
297   end
299   def test_splice_copy_stream_file_to_file_big
300     buf = ('ab' * IO::Splice::PIPE_CAPA) + 'hi'
301     a, b = Tempfile.new('a'), Tempfile.new('b')
302     a.syswrite buf
303     a.sysseek(0)
304     IO::Splice.copy_stream(a, b)
305     b.rewind
306     assert_equal buf, b.read
307   end
309   def test_splice_copy_stream_file_to_file_big_partial
310     nr = IO::Splice::PIPE_CAPA
311     buf = ('ab' * nr) + 'hi'
312     a, b = Tempfile.new('a'), Tempfile.new('b')
313     a.syswrite buf
314     a.sysseek(0)
315     assert_equal nr, IO::Splice.copy_stream(a, b, nr)
316     b.rewind
317     assert_equal('ab' * (nr/2), b.read)
318   end
320   def test_splice_copy_stream_file_to_file_len
321     a, b = Tempfile.new('a'), Tempfile.new('b')
322     a.syswrite 'hello world'
323     a.sysseek(0)
324     IO::Splice.copy_stream(a, b, 5)
325     b.rewind
326     assert_equal 'hello', b.read
327   end
329   def test_splice_copy_stream_pipe_to_file_len
330     a = Tempfile.new('a')
331     r, w = IO.pipe
332     w.syswrite 'hello world'
333     IO::Splice.copy_stream(r, a, 5)
334     a.rewind
335     assert_equal 'hello', a.read
336   end
338   def test_splice_copy_stream_paths
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)
343     assert_equal 'hello', b.read
344   end
346   def test_splice_copy_stream_src_offset
347     a = Tempfile.new('a')
348     b = Tempfile.new('a')
349     a.syswrite('hello world')
350     IO::Splice.copy_stream(a.path, b.path, 5, 6)
351     assert_equal 'world', b.read
352   end
354   def test_copy_stream_nonblock_src
355     server = TCPServer.new('127.0.0.1', 0)
356     port = server.addr[1]
357     rp, wp = IO.pipe
358     rs = TCPSocket.new('127.0.0.1', port)
359     rs.nonblock = true
360     nr = 0
361     assert_raises(Timeout::Error) do
362       timeout(0.05) { nr += IO::Splice.copy_stream(rs, wp, 5) }
363     end
364     assert_equal 0, nr
365     rs.close
366     server.close
367   end
369   def test_copy_stream_nonblock_dst
370     server = TCPServer.new('127.0.0.1', 0)
371     port = server.addr[1]
372     rp, wp = IO.pipe
373     rs = TCPSocket.new('127.0.0.1', port)
374     rs.nonblock = true
375     client = server.accept
376     buf = ' ' * IO::Splice::PIPE_CAPA
377     nr = 0
378     assert_raises(Timeout::Error) do
379       loop do
380         begin
381           wp.write_nonblock(buf)
382         rescue Errno::EAGAIN
383         end
384         timeout(0.05) do
385           nr += IO::Splice.copy_stream(rp, rs, IO::Splice::PIPE_CAPA)
386         end
387       end
388     end
389     assert_equal nr, client.read(nr).size
390     rs.close
391     server.close
392   end
394   def test_copy_stream_eof
395     r, w = IO.pipe
396     w.syswrite 'hello world'
397     w.close
398     a = Tempfile.new('a')
399     assert_equal 11, IO::Splice.copy_stream(r, a)
400     a.rewind
401     assert_equal 'hello world', a.read
402   end
404   def test_pipe_size
405     r, w = IO.pipe
406     assert_kind_of Integer, r.pipe_size
407     assert(r.pipe_size >= 512)
408     assert_nothing_raised { w.pipe_size = 8192 }
409     assert_equal 8192, r.pipe_size
411     w.write('*' * 4097)
412     assert_raises(Errno::EBUSY) { r.pipe_size = 4096 }
414     pipe_max_size = File.read("/proc/sys/fs/pipe-max-size").to_i
415     assert_nothing_raised { r.pipe_size = pipe_max_size }
416     assert_raises(Errno::EPERM) { r.pipe_size = pipe_max_size * 2 }
417   end if IO.method_defined?(:pipe_size)