copy_stream: enough to get this working under MRI 1.8
[ruby_io_splice.git] / test / test_io_splice.rb
blob26f2e00d55ba1f3125264c1892b8b18f581d8645
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 self.mri?
13     (defined?(RUBY_ENGINE) && RUBY_ENGINE == "ruby") || !defined?(RUBY_ENGINE)
14   end
16   def test_splice
17     str = 'abcde'
18     size = 5
19     rd, wr = IO.pipe
20     tmp = Tempfile.new('ruby_io_splice')
22     assert_nothing_raised {
23       tmp.syswrite(str)
24       tmp.sysseek(0)
25     }
27     nr = IO.splice(tmp.fileno, nil, wr.fileno, nil, size, 0)
28     assert_equal size, nr
29     assert_equal str, rd.sysread(size)
30   end
32   def test_splice_io
33     str = 'abcde'
34     size = 5
35     rd, wr = IO.pipe
36     tmp = Tempfile.new('ruby_io_splice')
38     assert_nothing_raised {
39       tmp.syswrite(str)
40       tmp.sysseek(0)
41     }
43     nr = IO.splice(tmp, nil, wr, nil, size, 0)
44     assert_equal size, nr
45     assert_equal str, rd.sysread(size)
46   end
48   def test_splice_io_noflags
49     str = 'abcde'
50     size = 5
51     rd, wr = IO.pipe
52     tmp = Tempfile.new('ruby_io_splice')
54     assert_nothing_raised {
55       tmp.syswrite(str)
56       tmp.sysseek(0)
57     }
59     nr = IO.splice(tmp, nil, wr, nil, size)
60     assert_equal size, nr
61     assert_equal str, rd.sysread(size)
62   end
64   def test_trysplice_io_noflags
65     str = 'abcde'
66     size = 5
67     rd, wr = IO.pipe
68     tmp = Tempfile.new('ruby_io_splice')
70     assert_nothing_raised {
71       tmp.syswrite(str)
72       tmp.sysseek(0)
73     }
75     nr = IO.trysplice(tmp, nil, wr, nil, size)
76     assert_equal size, nr
77     assert_equal str, rd.sysread(size)
78   end
80   def test_splice_io_ish
81     str = 'abcde'
82     size = 5
83     rd, wr = IO.pipe
84     tmp = Tempfile.new('ruby_io_splice')
85     io_ish = [ tmp ]
86     def io_ish.to_io
87       first.to_io
88     end
90     assert_nothing_raised {
91       tmp.syswrite(str)
92       tmp.sysseek(0)
93     }
95     nr = IO.splice(io_ish, nil, wr, nil, size, 0)
96     assert_equal size, nr
97     assert_equal str, rd.sysread(size)
98   end
100   def test_splice_in_offset
101     str = 'abcde'
102     off = 3
103     len = 2
104     rd, wr = IO.pipe
105     tmp = Tempfile.new('ruby_io_splice')
107     assert_nothing_raised {
108       tmp.syswrite(str)
109       tmp.sysseek(0)
110     }
112     nr = IO.splice(tmp.fileno, off, wr.fileno, nil, len, 0)
113     assert_equal len, nr
114     assert_equal 'de', rd.sysread(len)
115   end
117   def test_splice_out_offset
118     str = 'abcde'
119     rd, wr = IO.pipe
120     tmp = Tempfile.new('ruby_io_splice')
122     assert_nothing_raised { wr.syswrite(str) }
123     nr = IO.splice(rd.fileno, nil, tmp.fileno, 3, str.size, 0)
124     assert_equal 5, nr
125     assert_nothing_raised { tmp.sysseek(0) }
126     assert_equal "\0\0\0abcde", tmp.sysread(9)
127   end
129   def test_splice_nonblock
130     rd, wr = IO.pipe
131     tmp = Tempfile.new('ruby_io_splice')
133     assert_raises(Errno::EAGAIN) {
134       IO.splice(rd.fileno, nil, tmp.fileno, 0, 5, IO::Splice::F_NONBLOCK)
135     }
136   end
138   def test_trysplice_nonblock
139     rd, wr = IO.pipe
140     tmp = Tempfile.new('ruby_io_splice')
141     assert_equal :EAGAIN,
142            IO.trysplice(rd, nil, tmp, 0, 5, IO::Splice::F_NONBLOCK)
143   end
145   def test_trysplice_nonblock_noargs
146     rd, wr = IO.pipe
147     tmp = Tempfile.new('ruby_io_splice')
148     assert_equal :EAGAIN, IO.trysplice(rd, nil, tmp, 0, 5)
149     assert_equal :EAGAIN, IO.trysplice(rd, nil, tmp, 0, 5, IO::Splice::F_MORE)
150   end
152   def test_splice_eof
153     rd, wr = IO.pipe
154     tmp = Tempfile.new('ruby_io_splice')
155     wr.syswrite 'abc'
156     wr.close
158     nr = IO.splice(rd.fileno, nil, tmp.fileno, 0, 5, IO::Splice::F_NONBLOCK)
159     assert_equal 3, nr
160     assert_raises(EOFError) {
161       IO.splice(rd.fileno, nil, tmp.fileno, 0, 5, IO::Splice::F_NONBLOCK)
162     }
163   end
165   def test_trysplice_eof
166     rd, wr = IO.pipe
167     tmp = Tempfile.new('ruby_io_splice')
168     wr.syswrite 'abc'
169     wr.close
171     nr = IO.trysplice(rd, nil, tmp, 0, 5, IO::Splice::F_NONBLOCK)
172     assert_equal 3, nr
173     assert_nil IO.trysplice(rd, nil, tmp, 0, 5, IO::Splice::F_NONBLOCK)
174   end
176   def test_splice_nonblock_socket
177     server = TCPServer.new('127.0.0.1', 0)
178     port = server.addr[1]
179     rp, wp = IO.pipe
180     rs = TCPSocket.new('127.0.0.1', port)
181     rs.nonblock = true
182     assert_raises(Errno::EAGAIN) { IO.splice(rs, nil, wp, nil, 1024, 0) }
183     rs.close
184     server.close
185   end
187   def test_tee
188     str = 'abcde'
189     size = 5
190     rda, wra = IO.pipe
191     rdb, wrb = IO.pipe
193     assert_nothing_raised { wra.syswrite(str) }
194     nr = IO.tee(rda.fileno, wrb.fileno, size, 0)
195     assert_equal 5, nr
196     assert_equal str, rdb.sysread(5)
197     assert_equal str, rda.sysread(5)
198   end
200   def test_trytee
201     str = 'abcde'
202     size = 5
203     rda, wra = IO.pipe
204     rdb, wrb = IO.pipe
206     assert_nothing_raised { wra.syswrite(str) }
207     nr = IO.trytee(rda, wrb, size, 0)
208     assert_equal 5, nr
209     assert_equal str, rdb.sysread(5)
210     assert_equal str, rda.sysread(5)
211   end
213   def test_tee_eof
214     rda, wra = IO.pipe
215     rdb, wrb = IO.pipe
216     wra.close
217     assert_raises(EOFError) { IO.tee(rda.fileno, wrb.fileno, 4096, 0) }
218   end
220   def test_trytee_eof
221     rda, wra = IO.pipe
222     rdb, wrb = IO.pipe
223     wra.close
224     assert_nil IO.trytee(rda, wrb, 4096)
225   end
227   def test_tee_nonblock
228     rda, wra = IO.pipe
229     rdb, wrb = IO.pipe
230     assert_raises(Errno::EAGAIN) {
231       IO.tee(rda.fileno, wrb.fileno, 4096, IO::Splice::F_NONBLOCK)
232     }
233   end
235   def test_trytee_nonblock
236     rda, wra = IO.pipe
237     rdb, wrb = IO.pipe
238     assert_equal :EAGAIN, IO.trytee(rda, wrb, 4096)
239   end
241   def test_tee_io
242     str = 'abcde'
243     size = 5
244     rda, wra = IO.pipe
245     rdb, wrb = IO.pipe
247     assert_nothing_raised { wra.syswrite(str) }
248     nr = IO.tee(rda, wrb, size, 0)
249     assert_equal 5, nr
250     assert_equal str, rdb.sysread(5)
251     assert_equal str, rda.sysread(5)
252   end
254   def test_vmsplice_array
255     data = %w(hello world how are you today)
256     r, w = IO.pipe
257     n = IO.vmsplice(w.fileno, data, 0)
258     assert_equal data.join('').size, n
259     assert_equal data.join(''), r.readpartial(16384)
260   end
262   def test_vmsplice_noflags
263     data = %w(hello world how are you today)
264     r, w = IO.pipe
265     n = IO.vmsplice(w, data)
266     assert_equal data.join('').size, n
267     assert_equal data.join(''), r.readpartial(16384)
268   end
270   def test_vmsplice_string
271     r, w = IO.pipe
272     assert_equal 5, IO.vmsplice(w, 'hello', 0)
273     assert_equal 'hello', r.read(5)
274   end
276   def test_vmsplice_array_io
277     data = %w(hello world how are you today)
278     r, w = IO.pipe
279     n = IO.vmsplice(w, data, 0)
280     assert_equal data.join('').size, n
281     assert_equal data.join(''), r.readpartial(16384)
282   end
284   def test_vmsplice_nonblock
285     data = %w(hello world how are you today)
286     r, w = IO.pipe
287     w.syswrite('.' * IO::Splice::PIPE_CAPA)
288     assert_raises(Errno::EAGAIN) {
289       IO.vmsplice(w.fileno, data, IO::Splice::F_NONBLOCK)
290     }
291   end
293   def test_vmsplice_in_full
294     empty = ""
296     # bs * count should be > PIPE_BUF
297     [ [ 512, 512 ], [ 131073, 3 ], [ 4098, 64 ] ].each do |(bs,count)|
298       rd, wr = IO.pipe
299       buf = File.open('/dev/urandom', 'rb') { |fp| fp.sysread(bs) }
301       vec = (1..count).map { buf }
302       pid = fork do
303         wr.close
304         tmp = []
305         begin
306           sleep 0.005
307           tmp << rd.readpartial(8192)
308         rescue EOFError
309           break
310         end while true
311         ok = (vec.join(empty) == tmp.join(empty))
312         exit! ok
313       end
314       assert_nothing_raised { rd.close }
315       assert_equal(bs * count, IO.vmsplice(wr.fileno, vec, 0))
316       assert_nothing_raised { wr.close }
317       _, status = Process.waitpid2(pid)
318       assert status.success?
319     end
320   end
322   def test_vmsplice_nil
323     data = %w(hello world how are you today)
324     assert_raises(TypeError) { IO.vmsplice(nil, data, 0) }
325   end
327   def test_constants
328     assert IO::Splice::PIPE_BUF > 0
329     %w(move nonblock more gift).each { |x|
330       assert Integer === IO::Splice.const_get("F_#{x.upcase}")
331     }
332     assert IO::Splice::PIPE_CAPA >= IO::Splice::PIPE_BUF
333   end
335   def test_splice_copy_stream_file_to_file_small
336     a, b = Tempfile.new('a'), Tempfile.new('b')
337     a.syswrite 'hello world'
338     a.sysseek(0)
339     IO::Splice.copy_stream(a, b)
340     b.rewind
341     assert_equal 'hello world', b.read
342   end
344   def test_splice_copy_stream_file_to_file_big
345     buf = ('ab' * IO::Splice::PIPE_CAPA) + 'hi'
346     a, b = Tempfile.new('a'), Tempfile.new('b')
347     a.syswrite buf
348     a.sysseek(0)
349     IO::Splice.copy_stream(a, b)
350     b.rewind
351     assert_equal buf, b.read
352   end
354   def test_splice_copy_stream_file_to_file_big_partial
355     nr = IO::Splice::PIPE_CAPA
356     buf = ('ab' * nr) + 'hi'
357     a, b = Tempfile.new('a'), Tempfile.new('b')
358     a.syswrite buf
359     a.sysseek(0)
360     assert_equal nr, IO::Splice.copy_stream(a, b, nr)
361     b.rewind
362     assert_equal('ab' * (nr/2), b.read)
363   end
365   def test_splice_copy_stream_file_to_file_len
366     a, b = Tempfile.new('a'), Tempfile.new('b')
367     a.syswrite 'hello world'
368     a.sysseek(0)
369     IO::Splice.copy_stream(a, b, 5)
370     b.rewind
371     assert_equal 'hello', b.read
372   end
374   def test_splice_copy_stream_pipe_to_file_len
375     a = Tempfile.new('a')
376     r, w = IO.pipe
377     w.syswrite 'hello world'
378     IO::Splice.copy_stream(r, a, 5)
379     a.rewind
380     assert_equal 'hello', a.read
381   end
383   def test_splice_copy_stream_paths
384     a = Tempfile.new('a')
385     b = Tempfile.new('a')
386     a.syswrite('hello world')
387     IO::Splice.copy_stream(a.path, b.path, 5)
388     assert_equal 'hello', b.read
389   end
391   def test_splice_copy_stream_src_offset
392     a = Tempfile.new('a')
393     b = Tempfile.new('a')
394     a.syswrite('hello world')
395     IO::Splice.copy_stream(a.path, b.path, 5, 6)
396     assert_equal 'world', b.read
397   end
399   def test_splice_copy_stream_src_offset_unchanged
400     a = Tempfile.new('a')
401     b = Tempfile.new('a')
402     a.syswrite('hello world')
403     assert_equal 0, a.sysseek(0, IO::SEEK_SET)
404     IO::Splice.copy_stream(a, b.path, 5, 6)
405     assert_equal 'world', b.read
406     assert_equal 0, a.sysseek(0, IO::SEEK_CUR)
407   end
409   def test_copy_stream_nonblock_src
410     server = TCPServer.new('127.0.0.1', 0)
411     port = server.addr[1]
412     rp, wp = IO.pipe
413     rs = TCPSocket.new('127.0.0.1', port)
414     rs.nonblock = true
415     nr = 0
416     assert_raises(Timeout::Error) do
417       timeout(0.05) { nr += IO::Splice.copy_stream(rs, wp, 5) }
418     end
419     assert_equal 0, nr
420     rs.close
421     server.close
422   end if mri?
424   def test_copy_stream_nonblock_dst
425     server = TCPServer.new('127.0.0.1', 0)
426     port = server.addr[1]
427     rp, wp = IO.pipe
428     rs = TCPSocket.new('127.0.0.1', port)
429     rs.nonblock = true
430     client = server.accept
431     buf = ' ' * IO::Splice::PIPE_CAPA
432     nr = 0
433     assert_raises(Timeout::Error) do
434       loop do
435         begin
436           wp.write_nonblock(buf)
437         rescue Errno::EAGAIN
438         end
439         timeout(0.05) do
440           nr += IO::Splice.copy_stream(rp, rs, IO::Splice::PIPE_CAPA)
441         end
442       end
443     end
444     assert_equal nr, client.read(nr).size
445     rs.close
446     server.close
447   end if mri?
449   def test_copy_stream_eof
450     r, w = IO.pipe
451     w.syswrite 'hello world'
452     w.close
453     a = Tempfile.new('a')
454     assert_equal 11, IO::Splice.copy_stream(r, a)
455     a.rewind
456     assert_equal 'hello world', a.read
457   end
459   def test_pipe_size
460     r, w = IO.pipe
461     assert_kind_of Integer, r.pipe_size
462     assert(r.pipe_size >= 512)
463     assert_nothing_raised { w.pipe_size = 8192 }
464     assert_equal 8192, r.pipe_size
466     w.write('*' * 4097)
467     assert_raises(Errno::EBUSY) { r.pipe_size = 4096 }
469     pipe_max_size = File.read("/proc/sys/fs/pipe-max-size").to_i
470     assert_nothing_raised { r.pipe_size = pipe_max_size }
471     assert_raises(Errno::EPERM) { r.pipe_size = pipe_max_size * 2 }
472   end if IO.method_defined?(:pipe_size)