test/lib_read_write: account for larger-than-normal pipes
[kgio.git] / test / lib_read_write.rb
blob5034db834e853e10ff1a99a2fa39712c7714dad6
1 # -*- encoding: binary -*-
2 require 'test/unit'
3 require 'io/nonblock'
4 require 'digest/sha1'
5 $-w = true
6 require 'kgio'
8 module LibReadWriteTest
9   RANDOM_BLOB = File.open("/dev/urandom") { |fp| fp.read(10 * 1024 * 1024) }
11   def teardown
12     @rd.close if defined?(@rd) && ! @rd.closed?
13     @wr.close if defined?(@wr) && ! @wr.closed?
14   end
16   def test_write_empty
17     assert_nil @wr.kgio_write("")
18   end
20   def test_trywrite_empty
21     assert_nil @wr.kgio_trywrite("")
22   end
24   def test_writev_empty
25     assert_nil @wr.kgio_writev([])
26   end
28   def test_trywritev_empty
29     assert_nil @wr.kgio_trywritev([])
30   end
32   def test_read_zero
33     assert_equal "", @rd.kgio_read(0)
34     buf = "foo"
35     assert_equal buf.object_id, @rd.kgio_read(0, buf).object_id
36     assert_equal "", buf
37   end
39   def test_read_shared
40     a = "." * 0x1000
41     b = a.dup
42     @wr.syswrite "a"
43     assert_equal "a", @rd.kgio_read(0x1000, a)
44     assert_equal "a", a
45     assert_equal "." * 0x1000, b
46   end
48   def test_read_shared_2
49     a = "." * 0x1000
50     b = a.dup
51     @wr.syswrite "a"
52     assert_equal "a", @rd.kgio_read(0x1000, b)
53     assert_equal "a", b
54     assert_equal "." * 0x1000, a
55   end
57   def test_tryread_zero
58     assert_equal "", @rd.kgio_tryread(0)
59     buf = "foo"
60     assert_equal buf.object_id, @rd.kgio_tryread(0, buf).object_id
61     assert_equal "", buf
62   end
64   def test_tryread_shared
65     a = "." * 0x1000
66     b = a.dup
67     @wr.syswrite("a")
68     IO.select([@rd]) # this seems needed on FreeBSD 9.0
69     assert_equal "a", @rd.kgio_tryread(0x1000, b)
70     assert_equal "a", b
71     assert_equal "." * 0x1000, a
72   end
74   def test_tryread_shared_2
75     a = "." * 0x1000
76     b = a.dup
77     @wr.syswrite("a")
78     IO.select([@rd]) # this seems needed on FreeBSD 9.0
79     assert_equal "a", @rd.kgio_tryread(0x1000, a)
80     assert_equal "a", a
81     assert_equal "." * 0x1000, b
82   end
84   def test_read_eof
85     @wr.close
86     assert_nil @rd.kgio_read(5)
87   end
89   def test_read_bang_eof
90     @wr.close
91     begin
92       @rd.kgio_read!(5)
93       assert false, "should never get here (line:#{__LINE__})"
94     rescue EOFError => e
95       assert_equal [], e.backtrace
96     end
97   end
99   def test_tryread_eof
100     @wr.close
101     IO.select([@rd]) # this seems needed on FreeBSD 9.0
102     assert_nil @rd.kgio_tryread(5)
103   end
105   def test_write_closed
106     @rd.close
107     begin
108       loop { @wr.kgio_write "HI" }
109     rescue Errno::EPIPE, Errno::ECONNRESET => e
110       assert_equal [], e.backtrace
111       return
112     end
113     assert false, "should never get here (line:#{__LINE__})"
114   end
116   def test_trywrite_closed
117     @rd.close
118     begin
119       loop { @wr.kgio_trywrite "HI" }
120     rescue Errno::EPIPE, Errno::ECONNRESET => e
121       assert_equal [], e.backtrace
122       return
123     end
124     assert false, "should never get here (line:#{__LINE__})"
125   end
127   def test_writev_closed
128     @rd.close
129     begin
130       loop { @wr.kgio_writev ["HI"] }
131     rescue Errno::EPIPE, Errno::ECONNRESET => e
132       assert_equal [], e.backtrace
133       return
134     end
135     assert false, "should never get here (line:#{__LINE__})"
136   end
138   def test_trywritev_closed
139     @rd.close
140     begin
141       loop { @wr.kgio_trywritev ["HI"] }
142     rescue Errno::EPIPE, Errno::ECONNRESET => e
143       assert_equal [], e.backtrace
144       return
145     end
146     assert false, "should never get here (line:#{__LINE__})"
147   end
149   def test_trywrite_full
150     buf = "\302\251" * 1024 * 1024
151     buf2 = ""
152     dig = Digest::SHA1.new
153     t = Thread.new do
154       sleep 1
155       nr = 0
156       begin
157         dig.update(@rd.readpartial(4096, buf2))
158         nr += buf2.size
159       rescue EOFError
160         break
161       rescue => e
162       end while true
163       dig.hexdigest
164     end
165     50.times do
166       wr = buf
167       begin
168         rv = @wr.kgio_trywrite(wr)
169         case rv
170         when String
171           wr = rv
172         when :wait_readable
173           assert false, "should never get here line=#{__LINE__}"
174         when :wait_writable
175           IO.select(nil, [ @wr ])
176         else
177           wr = false
178         end
179       end while wr
180     end
181     @wr.close
182     t.join
183     assert_equal '8ff79d8115f9fe38d18be858c66aa08a1cc27a66', t.value
184   end
186   def test_trywritev_full
187     buf = ["\302\251" * 128] * 8 * 1024
188     buf2 = ""
189     dig = Digest::SHA1.new
190     t = Thread.new do
191       sleep 1
192       nr = 0
193       begin
194         dig.update(@rd.readpartial(4096, buf2))
195         nr += buf2.size
196       rescue EOFError
197         break
198       rescue => e
199       end while true
200       dig.hexdigest
201     end
202     50.times do
203       wr = buf
204       begin
205         rv = @wr.kgio_trywritev(wr)
206         case rv
207         when Array
208           wr = rv
209         when :wait_readable
210           assert false, "should never get here line=#{__LINE__}"
211         when :wait_writable
212           IO.select(nil, [ @wr ])
213         else
214           wr = false
215         end
216       end while wr
217     end
218     @wr.close
219     t.join
220     assert_equal '8ff79d8115f9fe38d18be858c66aa08a1cc27a66', t.value
221   end
223   def test_write_conv
224     assert_equal nil, @wr.kgio_write(10)
225     assert_equal "10", @rd.kgio_read(2)
226   end
228   def test_trywrite_conv
229     assert_equal nil, @wr.kgio_trywrite(10)
230     IO.select([@rd]) # this seems needed on FreeBSD 9.0
231     assert_equal "10", @rd.kgio_tryread(2)
232   end
234   def test_tryread_empty
235     assert_equal :wait_readable, @rd.kgio_tryread(1)
236   end
238   def test_read_too_much
239     assert_equal nil, @wr.kgio_write("hi")
240     assert_equal "hi", @rd.kgio_read(4)
241   end
243   def test_tryread_too_much
244     assert_equal nil, @wr.kgio_trywrite("hi")
245     assert_equal @rd, @rd.kgio_wait_readable
246     assert_equal "hi", @rd.kgio_tryread(4)
247   end
249   def test_read_short
250     assert_equal nil, @wr.kgio_write("hi")
251     assert_equal "h", @rd.kgio_read(1)
252     assert_equal "i", @rd.kgio_read(1)
253   end
255   def test_tryread_short
256     assert_equal nil, @wr.kgio_trywrite("hi")
257     IO.select([@rd]) # this seems needed on FreeBSD 9.0
258     assert_equal "h", @rd.kgio_tryread(1)
259     assert_equal "i", @rd.kgio_tryread(1)
260   end
262   def test_read_extra_buf
263     tmp = ""
264     tmp_object_id = tmp.object_id
265     assert_equal nil, @wr.kgio_write("hi")
266     rv = @rd.kgio_read(2, tmp)
267     assert_equal "hi", rv
268     assert_equal rv.object_id, tmp.object_id
269     assert_equal tmp_object_id, rv.object_id
270   end
272   def test_trywrite_return_wait_writable
273     tmp = []
274     tmp << @wr.kgio_trywrite("HI") until tmp[-1] == :wait_writable
275     assert :wait_writable === tmp[-1]
276     assert(!(:wait_readable === tmp[-1]))
277     assert_equal :wait_writable, tmp.pop
278     assert tmp.size > 0
279     penultimate = tmp.pop
280     assert(penultimate == "I" || penultimate == nil)
281     assert tmp.size > 0
282     tmp.each { |count| assert_equal nil, count }
283   end
285   def test_trywritev_return_wait_writable
286     tmp = []
287     tmp << @wr.kgio_trywritev(["HI"]) until tmp[-1] == :wait_writable
288     assert :wait_writable === tmp[-1]
289     assert(!(:wait_readable === tmp[-1]))
290     assert_equal :wait_writable, tmp.pop
291     assert tmp.size > 0
292     penultimate = tmp.pop
293     assert(penultimate == "I" || penultimate == nil)
294     assert tmp.size > 0
295     tmp.each { |count| assert_equal nil, count }
296   end
298   def test_tryread_extra_buf_eagain_clears_buffer
299     tmp = "hello world"
300     rv = @rd.kgio_tryread(2, tmp)
301     assert_equal :wait_readable, rv
302     assert_equal "", tmp
303   end
305   def test_tryread_extra_buf_eof_clears_buffer
306     tmp = "hello world"
307     @wr.close
308     IO.select([@rd]) # this seems needed on FreeBSD 9.0
309     assert_nil @rd.kgio_tryread(2, tmp)
310     assert_equal "", tmp
311   end
313   def test_monster_trywrite
314     buf = RANDOM_BLOB.dup
315     rv = @wr.kgio_trywrite(buf)
316     assert_kind_of String, rv
317     assert rv.size < buf.size
318     @rd.nonblock = false
319     assert_equal(buf, @rd.read(buf.size - rv.size) + rv)
320   end
322   def test_monster_write
323     buf = RANDOM_BLOB.dup
324     thr = Thread.new { @wr.kgio_write(buf) }
325     @rd.nonblock = false
326     readed = @rd.read(buf.size)
327     thr.join
328     assert_nil thr.value
329     assert_equal buf, readed
330   end
332   def test_monster_trywritev
333     buf, start = [], 0
334     while start < RANDOM_BLOB.size
335       s = RANDOM_BLOB[start, 1000]
336       start += s.size
337       buf << s
338     end
339     rv = @wr.kgio_trywritev(buf)
340     assert_kind_of Array, rv
341     rv = rv.join
342     assert rv.size < RANDOM_BLOB.size
343     @rd.nonblock = false
344     assert_equal(RANDOM_BLOB, @rd.read(RANDOM_BLOB.size - rv.size) + rv)
345   end
347   def test_monster_writev
348     buf, start = [], 0
349     while start < RANDOM_BLOB.size
350       s = RANDOM_BLOB[start, 10000]
351       start += s.size
352       buf << s
353     end
354     thr = Thread.new { @wr.kgio_writev(buf) }
355     @rd.nonblock = false
356     readed = @rd.read(RANDOM_BLOB.size)
357     thr.join
358     assert_nil thr.value
359     e = (RANDOM_BLOB == readed)
360     assert e
361   end
363   def test_monster_write_wait_writable
364     @wr.instance_variable_set :@nr, 0
365     def @wr.kgio_wait_writable
366       @nr += 1
367       IO.select(nil, [self])
368     end
369     buf = "." * 1024 * 1024 * 10
370     thr = Thread.new { @wr.kgio_write(buf) }
371     Thread.pass until thr.stop?
372     readed = @rd.read(buf.size)
373     thr.join
374     assert_nil thr.value
375     assert_equal buf, readed
376     assert @wr.instance_variable_get(:@nr) > 0
377   end
379   def test_monster_writev_wait_writable
380     @wr.instance_variable_set :@nr, 0
381     def @wr.kgio_wait_writable
382       @nr += 1
383       IO.select(nil, [self])
384     end
385     buf = ["." * 1024] * 1024 * 10
386     buf_size = buf.inject(0){|c, s| c + s.size}
387     thr = Thread.new { @wr.kgio_writev(buf) }
388     Thread.pass until thr.stop?
389     readed = @rd.read(buf_size)
390     thr.join
391     assert_nil thr.value
392     e = (buf.join == readed)
393     assert e
394     assert @wr.instance_variable_get(:@nr) > 0
395   end
397   def test_wait_readable_ruby_default
398     elapsed = 0
399     foo = nil
400     t0 = Time.now
401     thr = Thread.new { sleep 1; @wr.write "HELLO" }
402     foo = @rd.kgio_read(5)
403     elapsed = Time.now - t0
404     assert elapsed >= 1.0, "elapsed: #{elapsed}"
405     assert_equal "HELLO", foo
406     thr.join
407     assert_equal 5, thr.value
408   end
410   def test_wait_writable_ruby_default
411     buf = "." * 512
412     nr = 0
413     begin
414       nr += @wr.write_nonblock(buf)
415     rescue Errno::EAGAIN
416       break
417     end while true
418     elapsed = 0
419     foo = nil
420     t0 = Time.now
421     thr = Thread.new { sleep 1; @rd.read(nr) }
422     foo = @wr.kgio_write("HELLO")
423     elapsed = Time.now - t0
425     assert_nil foo
426     if @wr.stat.pipe?
427       assert elapsed >= 1.0, "elapsed: #{elapsed}"
428     end
429     assert(String === foo || foo == nil)
430     assert_kind_of String, thr.value
431   end
433   def test_wait_readable_method
434     def @rd.kgio_wait_readable
435       defined?(@z) ? raise(RuntimeError, "Hello") : @z = "HI"
436     end
437     foo = nil
438     begin
439       foo = @rd.kgio_read(5)
440       assert false
441     rescue RuntimeError => e
442       assert_equal("Hello", e.message)
443     end
444     assert_equal "HI", @rd.instance_variable_get(:@z)
445     assert_nil foo
446   end
448   def test_tryread_wait_readable_method
449     def @rd.kgio_wait_readable
450       raise "Hello"
451     end
452     assert_equal :wait_readable, @rd.kgio_tryread(5)
453   end
455   def test_trywrite_wait_readable_method
456     def @wr.kgio_wait_writable
457       raise "Hello"
458     end
459     buf = "." * 4096
460     rv = nil
461     until rv == :wait_writable
462       rv = @wr.kgio_trywrite(buf)
463     end
464     assert_equal :wait_writable, rv
465   end
467   def test_wait_writable_method
468     def @wr.kgio_wait_writable
469       defined?(@z) ? raise(RuntimeError, "Hello") : @z = "HI"
470     end
471     n = []
472     begin
473       loop { n << @wr.kgio_write("HIHIHIHIHIHI") }
474       assert false
475     rescue RuntimeError => e
476       assert_equal("Hello", e.message)
477     end
478     assert n.size > 0
479     assert_equal "HI", @wr.instance_variable_get(:@z)
480   end