# -*- encoding: binary -*- require 'test/unit' require 'io/nonblock' require 'digest/sha1' require 'fileutils' $-w = true require 'kgio' module LibReadWriteTest RANDOM_BLOB = File.open("/dev/urandom") do |fp| nr = 31 buf = fp.read(nr) # get roughly a 20MB block of random data (buf * (20 * 1024 * 1024 / nr)) + (buf * rand(123)) end def teardown @rd.close if defined?(@rd) && ! @rd.closed? @wr.close if defined?(@wr) && ! @wr.closed? FileUtils.remove_entry_secure(@tmpdir) if defined?(@tmpdir) end def test_write_empty assert_nil @wr.kgio_write("") end def test_trywrite_empty assert_nil @wr.kgio_trywrite("") end def test_writev_empty assert_nil @wr.kgio_writev([]) end def test_trywritev_empty assert_nil @wr.kgio_trywritev([]) end def test_read_zero assert_equal "", @rd.kgio_read(0) buf = "foo" assert_equal buf.object_id, @rd.kgio_read(0, buf).object_id assert_equal "", buf end def test_read_shared a = "." * 0x1000 b = a.dup @wr.syswrite "a" assert_equal "a", @rd.kgio_read(0x1000, a) assert_equal "a", a assert_equal "." * 0x1000, b end def test_read_shared_2 a = "." * 0x1000 b = a.dup @wr.syswrite "a" assert_equal "a", @rd.kgio_read(0x1000, b) assert_equal "a", b assert_equal "." * 0x1000, a end def test_tryread_zero assert_equal "", @rd.kgio_tryread(0) buf = "foo" assert_equal buf.object_id, @rd.kgio_tryread(0, buf).object_id assert_equal "", buf end def test_tryread_shared a = "." * 0x1000 b = a.dup @wr.syswrite("a") IO.select([@rd]) # this seems needed on FreeBSD 9.0 assert_equal "a", @rd.kgio_tryread(0x1000, b) assert_equal "a", b assert_equal "." * 0x1000, a end def test_tryread_shared_2 a = "." * 0x1000 b = a.dup @wr.syswrite("a") IO.select([@rd]) # this seems needed on FreeBSD 9.0 assert_equal "a", @rd.kgio_tryread(0x1000, a) assert_equal "a", a assert_equal "." * 0x1000, b end def test_read_eof @wr.close assert_nil @rd.kgio_read(5) end def test_read_bang_eof @wr.close begin @rd.kgio_read!(5) assert false, "should never get here (line:#{__LINE__})" rescue EOFError => e assert_equal [], e.backtrace end end def test_tryread_eof @wr.close IO.select([@rd]) # this seems needed on FreeBSD 9.0 assert_nil @rd.kgio_tryread(5) end def test_write_closed @rd.close begin loop { @wr.kgio_write "HI" } rescue Errno::EPIPE, Errno::ECONNRESET => e assert_equal [], e.backtrace return end assert false, "should never get here (line:#{__LINE__})" end def test_trywrite_closed @rd.close begin loop { @wr.kgio_trywrite "HI" } rescue Errno::EPIPE, Errno::ECONNRESET => e assert_equal [], e.backtrace return end assert false, "should never get here (line:#{__LINE__})" end def test_writev_closed @rd.close begin loop { @wr.kgio_writev ["HI"] } rescue Errno::EPIPE, Errno::ECONNRESET => e assert_equal [], e.backtrace return end assert false, "should never get here (line:#{__LINE__})" end def test_trywritev_closed @rd.close begin loop { @wr.kgio_trywritev ["HI"] } rescue Errno::EPIPE, Errno::ECONNRESET => e assert_equal [], e.backtrace return end assert false, "should never get here (line:#{__LINE__})" end def test_trywrite_full buf = "\302\251" * 1024 * 1024 buf2 = "" dig = Digest::SHA1.new t = Thread.new do sleep 1 nr = 0 begin dig.update(@rd.readpartial(4096, buf2)) nr += buf2.size rescue EOFError break rescue => e end while true dig.hexdigest end 50.times do wr = buf begin rv = @wr.kgio_trywrite(wr) case rv when String wr = rv when :wait_readable assert false, "should never get here line=#{__LINE__}" when :wait_writable IO.select(nil, [ @wr ]) else wr = false end end while wr end @wr.close t.join assert_equal '8ff79d8115f9fe38d18be858c66aa08a1cc27a66', t.value end def test_trywritev_full buf = ["\302\251" * 128] * 8 * 1024 buf2 = "" dig = Digest::SHA1.new t = Thread.new do sleep 1 nr = 0 begin dig.update(@rd.readpartial(4096, buf2)) nr += buf2.size rescue EOFError break rescue => e end while true dig.hexdigest end 50.times do wr = buf begin rv = @wr.kgio_trywritev(wr) case rv when Array wr = rv when :wait_readable assert false, "should never get here line=#{__LINE__}" when :wait_writable IO.select(nil, [ @wr ]) else wr = false end end while wr end @wr.close t.join assert_equal '8ff79d8115f9fe38d18be858c66aa08a1cc27a66', t.value end def test_write_conv assert_equal nil, @wr.kgio_write(10) assert_equal "10", @rd.kgio_read(2) end def test_trywrite_conv assert_equal nil, @wr.kgio_trywrite(10) IO.select([@rd]) # this seems needed on FreeBSD 9.0 assert_equal "10", @rd.kgio_tryread(2) end def test_tryread_empty assert_equal :wait_readable, @rd.kgio_tryread(1) end def test_read_too_much assert_equal nil, @wr.kgio_write("hi") assert_equal "hi", @rd.kgio_read(4) end def test_tryread_too_much assert_equal nil, @wr.kgio_trywrite("hi") assert_equal @rd, @rd.kgio_wait_readable assert_equal "hi", @rd.kgio_tryread(4) end def test_read_short assert_equal nil, @wr.kgio_write("hi") assert_equal "h", @rd.kgio_read(1) assert_equal "i", @rd.kgio_read(1) end def test_tryread_short assert_equal nil, @wr.kgio_trywrite("hi") IO.select([@rd]) # this seems needed on FreeBSD 9.0 assert_equal "h", @rd.kgio_tryread(1) assert_equal "i", @rd.kgio_tryread(1) end def test_read_extra_buf tmp = "" tmp_object_id = tmp.object_id assert_equal nil, @wr.kgio_write("hi") rv = @rd.kgio_read(2, tmp) assert_equal "hi", rv assert_equal rv.object_id, tmp.object_id assert_equal tmp_object_id, rv.object_id end def test_trywrite_return_wait_writable tmp = [] tmp << @wr.kgio_trywrite("HI") until tmp[-1] == :wait_writable assert :wait_writable === tmp[-1] assert(!(:wait_readable === tmp[-1])) assert_equal :wait_writable, tmp.pop assert tmp.size > 0 penultimate = tmp.pop assert(penultimate == "I" || penultimate == nil) assert tmp.size > 0 tmp.each { |count| assert_equal nil, count } end def test_trywritev_return_wait_writable tmp = [] tmp << @wr.kgio_trywritev(["HI"]) until tmp[-1] == :wait_writable assert :wait_writable === tmp[-1] assert(!(:wait_readable === tmp[-1])) assert_equal :wait_writable, tmp.pop assert tmp.size > 0 penultimate = tmp.pop assert(penultimate == ["I"] || penultimate == nil, "penultimate is #{penultimate.inspect}") assert tmp.size > 0 tmp.each { |count| assert_equal nil, count } end def test_tryread_extra_buf_eagain_clears_buffer tmp = "hello world" rv = @rd.kgio_tryread(2, tmp) assert_equal :wait_readable, rv assert_equal "", tmp end def test_tryread_extra_buf_eof_clears_buffer tmp = "hello world" @wr.close IO.select([@rd]) # this seems needed on FreeBSD 9.0 assert_nil @rd.kgio_tryread(2, tmp) assert_equal "", tmp end def test_monster_trywrite buf = RANDOM_BLOB.dup rv = @wr.kgio_trywrite(buf) assert_kind_of String, rv assert rv.size < buf.size @rd.nonblock = false assert_equal(buf, @rd.read(buf.size - rv.size) + rv) end def test_monster_write buf = RANDOM_BLOB.dup thr = Thread.new { @wr.kgio_write(buf) } @rd.nonblock = false readed = @rd.read(buf.size) thr.join assert_nil thr.value assert_equal buf, readed end def test_monster_trywritev buf, start = [], 0 while start < RANDOM_BLOB.size s = RANDOM_BLOB[start, 1000] start += s.size buf << s end rv = @wr.kgio_trywritev(buf) assert_kind_of Array, rv rv = rv.join assert rv.size < RANDOM_BLOB.size @rd.nonblock = false assert_equal(RANDOM_BLOB, @rd.read(RANDOM_BLOB.size - rv.size) + rv) end def test_monster_writev buf, start = [], 0 while start < RANDOM_BLOB.size s = RANDOM_BLOB[start, 10000] start += s.size buf << s end thr = Thread.new { @wr.kgio_writev(buf) } @rd.nonblock = false readed = @rd.read(RANDOM_BLOB.size) thr.join assert_nil thr.value e = (RANDOM_BLOB == readed) assert e end def test_monster_write_wait_writable @wr.instance_variable_set :@nr, 0 def @wr.kgio_wait_writable @nr += 1 IO.select(nil, [self]) end buf = RANDOM_BLOB thr = Thread.new { @wr.kgio_write(buf) } Thread.pass until thr.stop? readed = @rd.read(buf.size) thr.join assert_nil thr.value assert_equal buf, readed assert @wr.instance_variable_get(:@nr) > 0 end def test_monster_writev_wait_writable @wr.instance_variable_set :@nr, 0 def @wr.kgio_wait_writable @nr += 1 IO.select(nil, [self]) end buf = [ RANDOM_BLOB, RANDOM_BLOB ] buf_size = buf.inject(0){|c, s| c + s.size} thr = Thread.new { @wr.kgio_writev(buf) } Thread.pass until thr.stop? readed = @rd.read(buf_size) thr.join assert_nil thr.value e = (buf.join == readed) assert e assert @wr.instance_variable_get(:@nr) > 0 end def test_wait_readable_ruby_default elapsed = 0 foo = nil t0 = Time.now thr = Thread.new { sleep 1; @wr.write "HELLO" } foo = @rd.kgio_read(5) elapsed = Time.now - t0 assert elapsed >= 1.0, "elapsed: #{elapsed}" assert_equal "HELLO", foo thr.join assert_equal 5, thr.value end def test_wait_writable_ruby_default buf = "." * 512 nr = 0 begin nr += @wr.write_nonblock(buf) rescue Errno::EAGAIN break end while true elapsed = 0 foo = nil t0 = Time.now thr = Thread.new { sleep 1; @rd.read(nr) } foo = @wr.kgio_write("HELLO") elapsed = Time.now - t0 assert_nil foo if @wr.stat.pipe? assert elapsed >= 1.0, "elapsed: #{elapsed}" end assert(String === foo || foo == nil) assert_kind_of String, thr.value end def test_wait_readable_method def @rd.kgio_wait_readable defined?(@z) ? raise(RuntimeError, "Hello") : @z = "HI" end foo = nil begin foo = @rd.kgio_read(5) assert false rescue RuntimeError => e assert_equal("Hello", e.message) end assert_equal "HI", @rd.instance_variable_get(:@z) assert_nil foo end def test_tryread_wait_readable_method def @rd.kgio_wait_readable raise "Hello" end assert_equal :wait_readable, @rd.kgio_tryread(5) end def test_trywrite_wait_readable_method def @wr.kgio_wait_writable raise "Hello" end buf = "." * 4096 rv = nil until rv == :wait_writable rv = @wr.kgio_trywrite(buf) end assert_equal :wait_writable, rv end def test_wait_writable_method def @wr.kgio_wait_writable defined?(@z) ? raise(RuntimeError, "Hello") : @z = "HI" end n = [] begin loop { n << @wr.kgio_write("HIHIHIHIHIHI") } assert false rescue RuntimeError => e assert_equal("Hello", e.message) end assert n.size > 0 assert_equal "HI", @wr.instance_variable_get(:@z) end end