kgio 1.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.document +9 -0
- data/.gitignore +20 -0
- data/COPYING +165 -0
- data/GIT-VERSION-GEN +40 -0
- data/GNUmakefile +191 -0
- data/HACKING +66 -0
- data/ISSUES +36 -0
- data/LICENSE +18 -0
- data/README +68 -0
- data/Rakefile +169 -0
- data/TODO +1 -0
- data/ext/kgio/extconf.rb +21 -0
- data/ext/kgio/kgio_ext.c +1066 -0
- data/ext/kgio/missing/accept4.h +54 -0
- data/ext/kgio/missing/ancient_ruby.h +19 -0
- data/ext/kgio/my_fileno.h +36 -0
- data/ext/kgio/nonblock.h +15 -0
- data/ext/kgio/sock_for_fd.h +66 -0
- data/kgio.gemspec +36 -0
- data/lib/kgio.rb +18 -0
- data/setup.rb +1586 -0
- data/test/lib_read_write.rb +240 -0
- data/test/lib_server_accept.rb +70 -0
- data/test/test_connect_fd_leak.rb +23 -0
- data/test/test_pipe_popen.rb +14 -0
- data/test/test_pipe_read_write.rb +9 -0
- data/test/test_socketpair_read_write.rb +9 -0
- data/test/test_tcp_client_read_server_write.rb +13 -0
- data/test/test_tcp_connect.rb +73 -0
- data/test/test_tcp_server.rb +16 -0
- data/test/test_tcp_server_read_client_write.rb +13 -0
- data/test/test_unix_client_read_server_write.rb +17 -0
- data/test/test_unix_connect.rb +75 -0
- data/test/test_unix_server.rb +20 -0
- data/test/test_unix_server_read_client_write.rb +17 -0
- metadata +130 -0
@@ -0,0 +1,240 @@
|
|
1
|
+
require 'test/unit'
|
2
|
+
require 'io/nonblock'
|
3
|
+
$-w = true
|
4
|
+
require 'kgio'
|
5
|
+
|
6
|
+
module LibReadWriteTest
|
7
|
+
RANDOM_BLOB = File.open("/dev/urandom") { |fp| fp.read(10 * 1024 * 1024) }
|
8
|
+
|
9
|
+
def teardown
|
10
|
+
assert_nothing_raised do
|
11
|
+
@rd.close unless @rd.closed?
|
12
|
+
@wr.close unless @wr.closed?
|
13
|
+
end
|
14
|
+
assert_nothing_raised do
|
15
|
+
Kgio.wait_readable = Kgio.wait_writable = nil
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def test_read_eof
|
20
|
+
@wr.close
|
21
|
+
assert_nil @rd.kgio_read(5)
|
22
|
+
end
|
23
|
+
|
24
|
+
def test_tryread_eof
|
25
|
+
@wr.close
|
26
|
+
assert_nil @rd.kgio_tryread(5)
|
27
|
+
end
|
28
|
+
|
29
|
+
def test_write_closed
|
30
|
+
@rd.close
|
31
|
+
assert_raises(Errno::EPIPE, Errno::ECONNRESET) {
|
32
|
+
loop { @wr.kgio_write "HI" }
|
33
|
+
}
|
34
|
+
end
|
35
|
+
|
36
|
+
def test_trywrite_closed
|
37
|
+
@rd.close
|
38
|
+
assert_raises(Errno::EPIPE, Errno::ECONNRESET) {
|
39
|
+
loop { @wr.kgio_trywrite "HI" }
|
40
|
+
}
|
41
|
+
end
|
42
|
+
|
43
|
+
def test_write_conv
|
44
|
+
assert_equal nil, @wr.kgio_write(10)
|
45
|
+
assert_equal "10", @rd.kgio_read(2)
|
46
|
+
end
|
47
|
+
|
48
|
+
def test_trywrite_conv
|
49
|
+
assert_equal nil, @wr.kgio_trywrite(10)
|
50
|
+
assert_equal "10", @rd.kgio_tryread(2)
|
51
|
+
end
|
52
|
+
|
53
|
+
def test_tryread_empty
|
54
|
+
assert_equal Kgio::WaitReadable, @rd.kgio_tryread(1)
|
55
|
+
end
|
56
|
+
|
57
|
+
def test_read_too_much
|
58
|
+
assert_equal nil, @wr.kgio_write("hi")
|
59
|
+
assert_equal "hi", @rd.kgio_read(4)
|
60
|
+
end
|
61
|
+
|
62
|
+
def test_tryread_too_much
|
63
|
+
assert_equal nil, @wr.kgio_trywrite("hi")
|
64
|
+
assert_equal "hi", @rd.kgio_tryread(4)
|
65
|
+
end
|
66
|
+
|
67
|
+
def test_read_short
|
68
|
+
assert_equal nil, @wr.kgio_write("hi")
|
69
|
+
assert_equal "h", @rd.kgio_read(1)
|
70
|
+
assert_equal "i", @rd.kgio_read(1)
|
71
|
+
end
|
72
|
+
|
73
|
+
def test_tryread_short
|
74
|
+
assert_equal nil, @wr.kgio_trywrite("hi")
|
75
|
+
assert_equal "h", @rd.kgio_tryread(1)
|
76
|
+
assert_equal "i", @rd.kgio_tryread(1)
|
77
|
+
end
|
78
|
+
|
79
|
+
def test_read_extra_buf
|
80
|
+
tmp = ""
|
81
|
+
tmp_object_id = tmp.object_id
|
82
|
+
assert_equal nil, @wr.kgio_write("hi")
|
83
|
+
rv = @rd.kgio_read(2, tmp)
|
84
|
+
assert_equal "hi", rv
|
85
|
+
assert_equal rv.object_id, tmp.object_id
|
86
|
+
assert_equal tmp_object_id, rv.object_id
|
87
|
+
end
|
88
|
+
|
89
|
+
def test_trywrite_return_wait_writable
|
90
|
+
tmp = []
|
91
|
+
tmp << @wr.kgio_trywrite("HI") until tmp[-1] == Kgio::WaitWritable
|
92
|
+
assert_equal Kgio::WaitWritable, tmp.pop
|
93
|
+
assert tmp.size > 0
|
94
|
+
penultimate = tmp.pop
|
95
|
+
assert(penultimate == "I" || penultimate == nil)
|
96
|
+
assert tmp.size > 0
|
97
|
+
tmp.each { |count| assert_equal nil, count }
|
98
|
+
end
|
99
|
+
|
100
|
+
def test_tryread_extra_buf_eagain_clears_buffer
|
101
|
+
tmp = "hello world"
|
102
|
+
rv = @rd.kgio_tryread(2, tmp)
|
103
|
+
assert_equal Kgio::WaitReadable, rv
|
104
|
+
assert_equal "", tmp
|
105
|
+
end
|
106
|
+
|
107
|
+
def test_tryread_extra_buf_eof_clears_buffer
|
108
|
+
tmp = "hello world"
|
109
|
+
@wr.close
|
110
|
+
assert_nil @rd.kgio_tryread(2, tmp)
|
111
|
+
assert_equal "", tmp
|
112
|
+
end
|
113
|
+
|
114
|
+
def test_monster_trywrite
|
115
|
+
buf = RANDOM_BLOB.dup
|
116
|
+
rv = @wr.kgio_trywrite(buf)
|
117
|
+
assert_kind_of String, rv
|
118
|
+
assert rv.size < buf.size
|
119
|
+
@rd.nonblock = false
|
120
|
+
assert_equal(buf, @rd.read(buf.size - rv.size) + rv)
|
121
|
+
end
|
122
|
+
|
123
|
+
def test_monster_write
|
124
|
+
buf = RANDOM_BLOB.dup
|
125
|
+
thr = Thread.new { @wr.kgio_write(buf) }
|
126
|
+
@rd.nonblock = false
|
127
|
+
readed = @rd.read(buf.size)
|
128
|
+
thr.join
|
129
|
+
assert_nil thr.value
|
130
|
+
assert_equal buf, readed
|
131
|
+
end
|
132
|
+
|
133
|
+
def test_monster_write_wait_writable
|
134
|
+
@wr.instance_variable_set :@nr, 0
|
135
|
+
def @wr.wait_writable
|
136
|
+
@nr += 1
|
137
|
+
IO.select(nil, [self])
|
138
|
+
end
|
139
|
+
Kgio.wait_writable = :wait_writable
|
140
|
+
buf = "." * 1024 * 1024 * 10
|
141
|
+
thr = Thread.new { @wr.kgio_write(buf) }
|
142
|
+
readed = @rd.read(buf.size)
|
143
|
+
thr.join
|
144
|
+
assert_nil thr.value
|
145
|
+
assert_equal buf, readed
|
146
|
+
assert @wr.instance_variable_get(:@nr) > 0
|
147
|
+
end
|
148
|
+
|
149
|
+
def test_wait_readable_ruby_default
|
150
|
+
assert_nothing_raised { Kgio.wait_readable = nil }
|
151
|
+
elapsed = 0
|
152
|
+
foo = nil
|
153
|
+
t0 = Time.now
|
154
|
+
thr = Thread.new { sleep 1; @wr.write "HELLO" }
|
155
|
+
assert_nothing_raised do
|
156
|
+
foo = @rd.kgio_read(5)
|
157
|
+
elapsed = Time.now - t0
|
158
|
+
end
|
159
|
+
assert elapsed >= 1.0, "elapsed: #{elapsed}"
|
160
|
+
assert_equal "HELLO", foo
|
161
|
+
thr.join
|
162
|
+
assert_equal 5, thr.value
|
163
|
+
end
|
164
|
+
|
165
|
+
def test_wait_writable_ruby_default
|
166
|
+
buf = "." * 512
|
167
|
+
nr = 0
|
168
|
+
begin
|
169
|
+
nr += @wr.write_nonblock(buf)
|
170
|
+
rescue Errno::EAGAIN
|
171
|
+
break
|
172
|
+
end while true
|
173
|
+
assert_nothing_raised { Kgio.wait_writable = nil }
|
174
|
+
elapsed = 0
|
175
|
+
foo = nil
|
176
|
+
t0 = Time.now
|
177
|
+
thr = Thread.new { sleep 1; @rd.readpartial(nr) }
|
178
|
+
assert_nothing_raised do
|
179
|
+
foo = @wr.kgio_write("HELLO")
|
180
|
+
elapsed = Time.now - t0
|
181
|
+
end
|
182
|
+
assert_nil foo
|
183
|
+
if @wr.stat.pipe?
|
184
|
+
assert elapsed >= 1.0, "elapsed: #{elapsed}"
|
185
|
+
end
|
186
|
+
assert(String === foo || foo == nil)
|
187
|
+
assert_kind_of String, thr.value
|
188
|
+
end
|
189
|
+
|
190
|
+
def test_wait_readable_method
|
191
|
+
def @rd.moo
|
192
|
+
defined?(@z) ? raise(RuntimeError, "Hello") : @z = "HI"
|
193
|
+
end
|
194
|
+
assert_nothing_raised { Kgio.wait_readable = :moo }
|
195
|
+
foo = nil
|
196
|
+
begin
|
197
|
+
foo = @rd.kgio_read(5)
|
198
|
+
assert false
|
199
|
+
rescue RuntimeError => e
|
200
|
+
assert_equal("Hello", e.message)
|
201
|
+
end
|
202
|
+
assert_equal "HI", @rd.instance_variable_get(:@z)
|
203
|
+
assert_nil foo
|
204
|
+
end
|
205
|
+
|
206
|
+
def test_tryread_wait_readable_method
|
207
|
+
def @rd.moo
|
208
|
+
raise "Hello"
|
209
|
+
end
|
210
|
+
assert_nothing_raised { Kgio.wait_readable = :moo }
|
211
|
+
assert_equal Kgio::WaitReadable, @rd.kgio_tryread(5)
|
212
|
+
end
|
213
|
+
|
214
|
+
def test_trywrite_wait_readable_method
|
215
|
+
def @wr.moo
|
216
|
+
raise "Hello"
|
217
|
+
end
|
218
|
+
assert_nothing_raised { Kgio.wait_writable = :moo }
|
219
|
+
tmp = []
|
220
|
+
buf = "." * 1024
|
221
|
+
10000.times { tmp << @wr.kgio_trywrite(buf) }
|
222
|
+
assert_equal Kgio::WaitWritable, tmp.pop
|
223
|
+
end
|
224
|
+
|
225
|
+
def test_wait_writable_method
|
226
|
+
def @wr.moo
|
227
|
+
defined?(@z) ? raise(RuntimeError, "Hello") : @z = "HI"
|
228
|
+
end
|
229
|
+
assert_nothing_raised { Kgio.wait_writable = :moo }
|
230
|
+
n = []
|
231
|
+
begin
|
232
|
+
loop { n << @wr.kgio_write("HIHIHIHIHIHI") }
|
233
|
+
assert false
|
234
|
+
rescue RuntimeError => e
|
235
|
+
assert_equal("Hello", e.message)
|
236
|
+
end
|
237
|
+
assert n.size > 0
|
238
|
+
assert_equal "HI", @wr.instance_variable_get(:@z)
|
239
|
+
end
|
240
|
+
end
|
@@ -0,0 +1,70 @@
|
|
1
|
+
require 'test/unit'
|
2
|
+
require 'io/nonblock'
|
3
|
+
$-w = true
|
4
|
+
require 'kgio'
|
5
|
+
|
6
|
+
module LibServerAccept
|
7
|
+
|
8
|
+
def teardown
|
9
|
+
@srv.close unless @srv.closed?
|
10
|
+
Kgio.accept_cloexec = true
|
11
|
+
Kgio.accept_nonblock = false
|
12
|
+
end
|
13
|
+
|
14
|
+
def test_tryaccept_success
|
15
|
+
a = client_connect
|
16
|
+
IO.select([@srv])
|
17
|
+
b = @srv.kgio_tryaccept
|
18
|
+
assert_kind_of Kgio::Socket, b
|
19
|
+
assert_equal @host, b.kgio_addr
|
20
|
+
end
|
21
|
+
|
22
|
+
def test_tryaccept_fail
|
23
|
+
assert_equal nil, @srv.kgio_tryaccept
|
24
|
+
end
|
25
|
+
|
26
|
+
def test_blocking_accept
|
27
|
+
t0 = Time.now
|
28
|
+
pid = fork { sleep 1; a = client_connect; sleep }
|
29
|
+
b = @srv.kgio_accept
|
30
|
+
elapsed = Time.now - t0
|
31
|
+
assert_kind_of Kgio::Socket, b
|
32
|
+
assert_equal @host, b.kgio_addr
|
33
|
+
Process.kill(:TERM, pid)
|
34
|
+
Process.waitpid(pid)
|
35
|
+
assert elapsed >= 1, "elapsed: #{elapsed}"
|
36
|
+
end
|
37
|
+
|
38
|
+
def test_blocking_accept_with_nonblock_socket
|
39
|
+
@srv.nonblock = true
|
40
|
+
t0 = Time.now
|
41
|
+
pid = fork { sleep 1; a = client_connect; sleep }
|
42
|
+
b = @srv.kgio_accept
|
43
|
+
elapsed = Time.now - t0
|
44
|
+
assert_kind_of Kgio::Socket, b
|
45
|
+
assert_equal @host, b.kgio_addr
|
46
|
+
Process.kill(:TERM, pid)
|
47
|
+
Process.waitpid(pid)
|
48
|
+
assert elapsed >= 1, "elapsed: #{elapsed}"
|
49
|
+
|
50
|
+
t0 = Time.now
|
51
|
+
pid = fork { sleep 6; a = client_connect; sleep }
|
52
|
+
b = @srv.kgio_accept
|
53
|
+
elapsed = Time.now - t0
|
54
|
+
assert_kind_of Kgio::Socket, b
|
55
|
+
assert_equal @host, b.kgio_addr
|
56
|
+
Process.kill(:TERM, pid)
|
57
|
+
Process.waitpid(pid)
|
58
|
+
assert elapsed >= 6, "elapsed: #{elapsed}"
|
59
|
+
|
60
|
+
t0 = Time.now
|
61
|
+
pid = fork { sleep 1; a = client_connect; sleep }
|
62
|
+
b = @srv.kgio_accept
|
63
|
+
elapsed = Time.now - t0
|
64
|
+
assert_kind_of Kgio::Socket, b
|
65
|
+
assert_equal @host, b.kgio_addr
|
66
|
+
Process.kill(:TERM, pid)
|
67
|
+
Process.waitpid(pid)
|
68
|
+
assert elapsed >= 1, "elapsed: #{elapsed}"
|
69
|
+
end
|
70
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
require 'test/unit'
|
2
|
+
require 'io/nonblock'
|
3
|
+
$-w = true
|
4
|
+
require 'kgio'
|
5
|
+
|
6
|
+
class TestConnectFDLeak < Test::Unit::TestCase
|
7
|
+
|
8
|
+
def teardown
|
9
|
+
Kgio.wait_readable = Kgio.wait_writable = nil
|
10
|
+
end
|
11
|
+
|
12
|
+
def test_unix_socket
|
13
|
+
nr = 0
|
14
|
+
path = "/non/existent/path"
|
15
|
+
assert(! File.exist?(path), "#{path} should not exist")
|
16
|
+
assert_nothing_raised do
|
17
|
+
begin
|
18
|
+
sock = Kgio::UNIXSocket.new(path)
|
19
|
+
rescue Errno::ENOENT
|
20
|
+
end while (nr += 1) < 10000
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
require 'test/unit'
|
2
|
+
require 'io/nonblock'
|
3
|
+
$-w = true
|
4
|
+
require 'kgio'
|
5
|
+
|
6
|
+
class TestPipePopen < Test::Unit::TestCase
|
7
|
+
def test_popen
|
8
|
+
io = Kgio::Pipe.popen("sleep 1 && echo HI")
|
9
|
+
assert_equal Kgio::WaitReadable, io.kgio_tryread(2)
|
10
|
+
sleep 1.5
|
11
|
+
assert_equal "HI\n", io.kgio_read(3)
|
12
|
+
assert_nil io.kgio_read(5)
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
require './test/lib_read_write'
|
2
|
+
|
3
|
+
class TesTcpClientReadServerWrite < Test::Unit::TestCase
|
4
|
+
def setup
|
5
|
+
@host = ENV["TEST_HOST"] || '127.0.0.1'
|
6
|
+
@srv = Kgio::TCPServer.new(@host, 0)
|
7
|
+
@port = @srv.addr[1]
|
8
|
+
@wr = Kgio::TCPSocket.new(@host, @port)
|
9
|
+
@rd = @srv.kgio_accept
|
10
|
+
end
|
11
|
+
|
12
|
+
include LibReadWriteTest
|
13
|
+
end
|
@@ -0,0 +1,73 @@
|
|
1
|
+
require 'test/unit'
|
2
|
+
require 'io/nonblock'
|
3
|
+
$-w = true
|
4
|
+
require 'kgio'
|
5
|
+
|
6
|
+
class SubSocket < Kgio::Socket
|
7
|
+
attr_accessor :foo
|
8
|
+
def wait_writable
|
9
|
+
@foo = "waited"
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
class TestKgioTcpConnect < Test::Unit::TestCase
|
14
|
+
|
15
|
+
def setup
|
16
|
+
@host = ENV["TEST_HOST"] || '127.0.0.1'
|
17
|
+
@srv = Kgio::TCPServer.new(@host, 0)
|
18
|
+
@port = @srv.addr[1]
|
19
|
+
@addr = Socket.pack_sockaddr_in(@port, @host)
|
20
|
+
end
|
21
|
+
|
22
|
+
def teardown
|
23
|
+
@srv.close unless @srv.closed?
|
24
|
+
Kgio.accept_cloexec = true
|
25
|
+
Kgio.accept_nonblock = false
|
26
|
+
Kgio.wait_readable = Kgio.wait_writable = nil
|
27
|
+
end
|
28
|
+
|
29
|
+
def test_new
|
30
|
+
sock = Kgio::Socket.new(@addr)
|
31
|
+
assert_kind_of Kgio::Socket, sock
|
32
|
+
ready = IO.select(nil, [ sock ])
|
33
|
+
assert_equal sock, ready[1][0]
|
34
|
+
assert_equal nil, sock.kgio_write("HELLO")
|
35
|
+
end
|
36
|
+
|
37
|
+
def test_start
|
38
|
+
sock = Kgio::Socket.start(@addr)
|
39
|
+
assert_kind_of Kgio::Socket, sock
|
40
|
+
ready = IO.select(nil, [ sock ])
|
41
|
+
assert_equal sock, ready[1][0]
|
42
|
+
assert_equal nil, sock.kgio_write("HELLO")
|
43
|
+
end
|
44
|
+
|
45
|
+
def test_tcp_socket_new_invalid
|
46
|
+
assert_raises(ArgumentError) { Kgio::TCPSocket.new('example.com', 80) }
|
47
|
+
assert_raises(ArgumentError) { Kgio::TCPSocket.new('999.999.999.999', 80) }
|
48
|
+
end
|
49
|
+
|
50
|
+
def test_tcp_socket_new
|
51
|
+
sock = Kgio::TCPSocket.new(@host, @port)
|
52
|
+
assert_instance_of Kgio::TCPSocket, sock
|
53
|
+
ready = IO.select(nil, [ sock ])
|
54
|
+
assert_equal sock, ready[1][0]
|
55
|
+
assert_equal nil, sock.kgio_write("HELLO")
|
56
|
+
end
|
57
|
+
|
58
|
+
def test_socket_start
|
59
|
+
Kgio::wait_writable = :wait_writable
|
60
|
+
sock = SubSocket.start(@addr)
|
61
|
+
assert_nil sock.foo
|
62
|
+
ready = IO.select(nil, [ sock ])
|
63
|
+
assert_equal sock, ready[1][0]
|
64
|
+
assert_equal nil, sock.kgio_write("HELLO")
|
65
|
+
end
|
66
|
+
|
67
|
+
def test_wait_writable_set
|
68
|
+
Kgio::wait_writable = :wait_writable
|
69
|
+
sock = SubSocket.new(@addr)
|
70
|
+
assert_equal "waited", sock.foo
|
71
|
+
assert_equal nil, sock.kgio_write("HELLO")
|
72
|
+
end
|
73
|
+
end
|