IO.vmsplice flags argument defaults to zero
[ruby_io_splice.git] / test / test_io_splice.rb
blobeb4faebdce18496315377b18cacb4e091ce7804f
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_trysplice_nonblock_noargs
142     rd, wr = IO.pipe
143     tmp = Tempfile.new('ruby_io_splice')
144     assert_equal :EAGAIN, IO.trysplice(rd, nil, tmp, 0, 5)
145     assert_equal :EAGAIN, IO.trysplice(rd, nil, tmp, 0, 5, IO::Splice::F_MORE)
146   end
148   def test_splice_eof
149     rd, wr = IO.pipe
150     tmp = Tempfile.new('ruby_io_splice')
151     wr.syswrite 'abc'
152     wr.close
154     nr = IO.splice(rd.fileno, nil, tmp.fileno, 0, 5, IO::Splice::F_NONBLOCK)
155     assert_equal 3, nr
156     assert_raises(EOFError) {
157       IO.splice(rd.fileno, nil, tmp.fileno, 0, 5, IO::Splice::F_NONBLOCK)
158     }
159   end
161   def test_trysplice_eof
162     rd, wr = IO.pipe
163     tmp = Tempfile.new('ruby_io_splice')
164     wr.syswrite 'abc'
165     wr.close
167     nr = IO.trysplice(rd, nil, tmp, 0, 5, IO::Splice::F_NONBLOCK)
168     assert_equal 3, nr
169     assert_nil IO.trysplice(rd, nil, tmp, 0, 5, IO::Splice::F_NONBLOCK)
170   end
172   def test_splice_nonblock_socket
173     server = TCPServer.new('127.0.0.1', 0)
174     port = server.addr[1]
175     rp, wp = IO.pipe
176     rs = TCPSocket.new('127.0.0.1', port)
177     rs.nonblock = true
178     assert_raises(Errno::EAGAIN) { IO.splice(rs, nil, wp, nil, 1024, 0) }
179     rs.close
180     server.close
181   end
183   def test_tee
184     str = 'abcde'
185     size = 5
186     rda, wra = IO.pipe
187     rdb, wrb = IO.pipe
189     assert_nothing_raised { wra.syswrite(str) }
190     nr = IO.tee(rda.fileno, wrb.fileno, size, 0)
191     assert_equal 5, nr
192     assert_equal str, rdb.sysread(5)
193     assert_equal str, rda.sysread(5)
194   end
196   def test_trytee
197     str = 'abcde'
198     size = 5
199     rda, wra = IO.pipe
200     rdb, wrb = IO.pipe
202     assert_nothing_raised { wra.syswrite(str) }
203     nr = IO.trytee(rda, wrb, size, 0)
204     assert_equal 5, nr
205     assert_equal str, rdb.sysread(5)
206     assert_equal str, rda.sysread(5)
207   end
209   def test_tee_eof
210     rda, wra = IO.pipe
211     rdb, wrb = IO.pipe
212     wra.close
213     assert_raises(EOFError) { IO.tee(rda.fileno, wrb.fileno, 4096, 0) }
214   end
216   def test_trytee_eof
217     rda, wra = IO.pipe
218     rdb, wrb = IO.pipe
219     wra.close
220     assert_nil IO.trytee(rda, wrb, 4096)
221   end
223   def test_tee_nonblock
224     rda, wra = IO.pipe
225     rdb, wrb = IO.pipe
226     assert_raises(Errno::EAGAIN) {
227       IO.tee(rda.fileno, wrb.fileno, 4096, IO::Splice::F_NONBLOCK)
228     }
229   end
231   def test_trytee_nonblock
232     rda, wra = IO.pipe
233     rdb, wrb = IO.pipe
234     assert_equal :EAGAIN, IO.trytee(rda, wrb, 4096)
235   end
237   def test_tee_io
238     str = 'abcde'
239     size = 5
240     rda, wra = IO.pipe
241     rdb, wrb = IO.pipe
243     assert_nothing_raised { wra.syswrite(str) }
244     nr = IO.tee(rda, wrb, size, 0)
245     assert_equal 5, nr
246     assert_equal str, rdb.sysread(5)
247     assert_equal str, rda.sysread(5)
248   end
250   def test_vmsplice_array
251     data = %w(hello world how are you today)
252     r, w = IO.pipe
253     n = IO.vmsplice(w.fileno, data, 0)
254     assert_equal data.join('').size, n
255     assert_equal data.join(''), r.readpartial(16384)
256   end
258   def test_vmsplice_noflags
259     data = %w(hello world how are you today)
260     r, w = IO.pipe
261     n = IO.vmsplice(w, data)
262     assert_equal data.join('').size, n
263     assert_equal data.join(''), r.readpartial(16384)
264   end
266   def test_vmsplice_string
267     r, w = IO.pipe
268     assert_equal 5, IO.vmsplice(w, 'hello', 0)
269     assert_equal 'hello', r.read(5)
270   end
272   def test_vmsplice_array_io
273     data = %w(hello world how are you today)
274     r, w = IO.pipe
275     n = IO.vmsplice(w, data, 0)
276     assert_equal data.join('').size, n
277     assert_equal data.join(''), r.readpartial(16384)
278   end
280   def test_vmsplice_nonblock
281     data = %w(hello world how are you today)
282     r, w = IO.pipe
283     w.syswrite('.' * IO::Splice::PIPE_CAPA)
284     assert_raises(Errno::EAGAIN) {
285       IO.vmsplice(w.fileno, data, IO::Splice::F_NONBLOCK)
286     }
287   end
289   def test_vmsplice_in_full
290     empty = ""
292     # bs * count should be > PIPE_BUF
293     [ [ 512, 512 ], [ 131073, 3 ], [ 4098, 64 ] ].each do |(bs,count)|
294       rd, wr = IO.pipe
295       buf = File.open('/dev/urandom', 'rb') { |fp| fp.sysread(bs) }
297       vec = (1..count).map { buf }
298       pid = fork do
299         wr.close
300         tmp = []
301         begin
302           sleep 0.005
303           tmp << rd.readpartial(8192)
304         rescue EOFError
305           break
306         end while true
307         ok = (vec.join(empty) == tmp.join(empty))
308         exit! ok
309       end
310       assert_nothing_raised { rd.close }
311       assert_equal(bs * count, IO.vmsplice(wr.fileno, vec, 0))
312       assert_nothing_raised { wr.close }
313       _, status = Process.waitpid2(pid)
314       assert status.success?
315     end
316   end
318   def test_vmsplice_nil
319     data = %w(hello world how are you today)
320     assert_raises(TypeError) { IO.vmsplice(nil, data, 0) }
321   end
323   def test_constants
324     assert IO::Splice::PIPE_BUF > 0
325     %w(move nonblock more gift).each { |x|
326       assert Integer === IO::Splice.const_get("F_#{x.upcase}")
327     }
328     assert IO::Splice::PIPE_CAPA >= IO::Splice::PIPE_BUF
329   end
331   def test_splice_copy_stream_file_to_file_small
332     a, b = Tempfile.new('a'), Tempfile.new('b')
333     a.syswrite 'hello world'
334     a.sysseek(0)
335     IO::Splice.copy_stream(a, b)
336     b.rewind
337     assert_equal 'hello world', b.read
338   end
340   def test_splice_copy_stream_file_to_file_big
341     buf = ('ab' * IO::Splice::PIPE_CAPA) + 'hi'
342     a, b = Tempfile.new('a'), Tempfile.new('b')
343     a.syswrite buf
344     a.sysseek(0)
345     IO::Splice.copy_stream(a, b)
346     b.rewind
347     assert_equal buf, b.read
348   end
350   def test_splice_copy_stream_file_to_file_big_partial
351     nr = IO::Splice::PIPE_CAPA
352     buf = ('ab' * nr) + 'hi'
353     a, b = Tempfile.new('a'), Tempfile.new('b')
354     a.syswrite buf
355     a.sysseek(0)
356     assert_equal nr, IO::Splice.copy_stream(a, b, nr)
357     b.rewind
358     assert_equal('ab' * (nr/2), b.read)
359   end
361   def test_splice_copy_stream_file_to_file_len
362     a, b = Tempfile.new('a'), Tempfile.new('b')
363     a.syswrite 'hello world'
364     a.sysseek(0)
365     IO::Splice.copy_stream(a, b, 5)
366     b.rewind
367     assert_equal 'hello', b.read
368   end
370   def test_splice_copy_stream_pipe_to_file_len
371     a = Tempfile.new('a')
372     r, w = IO.pipe
373     w.syswrite 'hello world'
374     IO::Splice.copy_stream(r, a, 5)
375     a.rewind
376     assert_equal 'hello', a.read
377   end
379   def test_splice_copy_stream_paths
380     a = Tempfile.new('a')
381     b = Tempfile.new('a')
382     a.syswrite('hello world')
383     IO::Splice.copy_stream(a.path, b.path, 5)
384     assert_equal 'hello', b.read
385   end
387   def test_splice_copy_stream_src_offset
388     a = Tempfile.new('a')
389     b = Tempfile.new('a')
390     a.syswrite('hello world')
391     IO::Splice.copy_stream(a.path, b.path, 5, 6)
392     assert_equal 'world', b.read
393   end
395   def test_copy_stream_nonblock_src
396     server = TCPServer.new('127.0.0.1', 0)
397     port = server.addr[1]
398     rp, wp = IO.pipe
399     rs = TCPSocket.new('127.0.0.1', port)
400     rs.nonblock = true
401     nr = 0
402     assert_raises(Timeout::Error) do
403       timeout(0.05) { nr += IO::Splice.copy_stream(rs, wp, 5) }
404     end
405     assert_equal 0, nr
406     rs.close
407     server.close
408   end
410   def test_copy_stream_nonblock_dst
411     server = TCPServer.new('127.0.0.1', 0)
412     port = server.addr[1]
413     rp, wp = IO.pipe
414     rs = TCPSocket.new('127.0.0.1', port)
415     rs.nonblock = true
416     client = server.accept
417     buf = ' ' * IO::Splice::PIPE_CAPA
418     nr = 0
419     assert_raises(Timeout::Error) do
420       loop do
421         begin
422           wp.write_nonblock(buf)
423         rescue Errno::EAGAIN
424         end
425         timeout(0.05) do
426           nr += IO::Splice.copy_stream(rp, rs, IO::Splice::PIPE_CAPA)
427         end
428       end
429     end
430     assert_equal nr, client.read(nr).size
431     rs.close
432     server.close
433   end
435   def test_copy_stream_eof
436     r, w = IO.pipe
437     w.syswrite 'hello world'
438     w.close
439     a = Tempfile.new('a')
440     assert_equal 11, IO::Splice.copy_stream(r, a)
441     a.rewind
442     assert_equal 'hello world', a.read
443   end
445   def test_pipe_size
446     r, w = IO.pipe
447     assert_kind_of Integer, r.pipe_size
448     assert(r.pipe_size >= 512)
449     assert_nothing_raised { w.pipe_size = 8192 }
450     assert_equal 8192, r.pipe_size
452     w.write('*' * 4097)
453     assert_raises(Errno::EBUSY) { r.pipe_size = 4096 }
455     pipe_max_size = File.read("/proc/sys/fs/pipe-max-size").to_i
456     assert_nothing_raised { r.pipe_size = pipe_max_size }
457     assert_raises(Errno::EPERM) { r.pipe_size = pipe_max_size * 2 }
458   end if IO.method_defined?(:pipe_size)