6 require 'sleepy_penguin'
8 class TestEpoll < Test::Unit::TestCase
10 RBX = defined?(RUBY_ENGINE) && (RUBY_ENGINE == 'rbx')
19 Thread.new { sleep 0.100; @ep.add(@wr, Epoll::OUT) }
21 @ep.wait { |flags,obj| tmp << [ flags, obj ] }
22 elapsed = Time.now - t0
23 assert elapsed >= 0.100
24 assert_equal [[Epoll::OUT, @wr]], tmp
29 @ep.add @rd, Epoll::IN
31 @ep.wait(nil, 100) { |flags,obj| tmp << [ flags, obj ] }
35 _, status = Process.waitpid2(pid)
36 assert status.success?
37 @ep.wait(nil, 0) { |flags,obj| tmp << [ flags, obj ] }
38 assert_equal [[Epoll::IN, @rd]], tmp
41 def test_after_fork_usability
42 fork { @ep.add(@rd, Epoll::IN); exit!(0) }
43 fork { @ep.set(@rd, Epoll::IN); exit!(0) }
44 fork { @ep.to_io; exit!(0) }
45 fork { @ep.dup; exit!(0) }
46 fork { @ep.clone; exit!(0) }
47 fork { @ep.close; exit!(0) }
48 fork { @ep.closed?; exit!(0) }
58 res.each { |(pid,status)| assert status.success? }
61 def test_tcp_connect_nonblock_edge
62 epflags = Epoll::OUT | Epoll::ET
64 srv = TCPServer.new(host, 0)
66 addr = Socket.pack_sockaddr_in(port, host)
67 sock = Socket.new(Socket::AF_INET, Socket::SOCK_STREAM, 0)
68 assert_raises(Errno::EINPROGRESS) { sock.connect_nonblock(addr) }
69 IO.select(nil, [ sock ], [sock ])
70 @ep.add(sock, epflags)
72 @ep.wait(1) { |flags, obj| tmp << [ flags, obj ] }
73 assert_equal [ [Epoll::OUT, sock] ], tmp
76 def test_tcp_connect_edge
77 epflags = Epoll::OUT | Epoll::ET
79 srv = TCPServer.new(host, 0)
81 sock = TCPSocket.new(host, port)
82 @ep.add(sock, epflags)
84 @ep.wait(1) { |flags, obj| tmp << [ flags, obj ] }
85 assert_equal [ [Epoll::OUT, sock] ], tmp
89 assert_nothing_raised do
90 @rd.close unless @rd.closed?
91 @wr.close unless @wr.closed?
92 @ep.close unless @ep.closed?
96 def test_max_events_big
97 @ep.add @rd, Epoll::IN
99 thr = Thread.new { @ep.wait(1024) { |flags, obj| tmp << [ flags, obj ] } }
104 assert_equal([[Epoll::IN, @rd]], tmp)
106 thr = Thread.new { @ep.wait { |flags, obj| tmp << [ flags, obj ] } }
108 assert_equal([[Epoll::IN, @rd]], tmp)
111 def test_max_events_small
112 @ep.add @rd, Epoll::IN | Epoll::ET
113 @ep.add @wr, Epoll::OUT | Epoll::ET
116 @ep.wait(1) { |flags, obj| tmp << [ flags, obj ] }
117 assert_equal 1, tmp.size
118 @ep.wait(1) { |flags, obj| tmp << [ flags, obj ] }
119 assert_equal 2, tmp.size
124 trap(:USR1) { time[:USR1] = Time.now; sleep 0.1; @wr.write '.' }
125 @ep.add @rd, Epoll::IN
128 sleep 0.1 # slightly racy :<
129 Process.kill(:USR1, Process.ppid)
131 time[:START_WAIT] = Time.now
132 @ep.wait { |flags, obj| tmp << [ flags, obj ]; time[:EP] = Time.now }
133 assert_equal([[Epoll::IN, @rd]], tmp)
134 _, status = Process.waitpid2(pid)
135 assert status.success?
136 assert((time[:USR1] - time[:START_WAIT]) >= 0.1)
137 assert((time[:USR1] - time[:START_WAIT]) < 0.15)
138 assert((time[:EP] - time[:USR1]) >= 0.1)
139 assert((time[:EP] - time[:USR1]) < 0.15)
141 trap(:USR1, 'DEFAULT')
145 @ep.add @rd, Epoll::IN
147 thr = Thread.new { @ep.wait { |flags, obj| tmp << [ flags, obj ] } }
150 assert_nil thr.join(0.01)
157 rd, wr = UNIXSocket.pair
158 @ep.add rd, Epoll::RDHUP
160 thr = Thread.new { @ep.wait { |flags, obj| tmp << [ flags, obj ] } }
161 wr.shutdown Socket::SHUT_WR
163 assert_equal([[ Epoll::RDHUP, rd ]], tmp)
167 @ep.add @rd, Epoll::IN
169 thr = Thread.new { @ep.wait { |flags, obj| tmp << [ flags, obj ] } }
172 assert_equal([[ Epoll::HUP, @rd ]], tmp)
177 assert_nothing_raised do
179 @ep.add @rd, Epoll::IN
180 @ep.add w, Epoll::OUT
181 @ep.add @wr, Epoll::OUT
184 @ep.wait { |flags, obj| tmp << [ flags, obj ] }
185 assert_equal 2, tmp.size
186 assert_equal [ Epoll::OUT ], tmp.map { |flags, obj| flags }.uniq
187 ios = tmp.map { |flags, obj| obj }
188 assert ios.include?(@wr)
189 assert ios.include?(w)
193 assert_nothing_raised { 4096.times { Epoll.new } }
198 assert_nothing_raised do
210 assert @ep.to_io.fileno != clone.to_io.fileno
211 clone.add @wr, Epoll::OUT
212 @ep.wait(nil, 0) { |flags, obj| tmp << [ flags, obj ] }
213 assert_equal([[Epoll::OUT, @wr]], tmp)
214 assert_nothing_raised { clone.close }
220 assert @ep.to_io.fileno != clone.to_io.fileno
221 clone.add @wr, Epoll::OUT
222 @ep.wait(nil, 0) { |flags, obj| tmp << [ flags, obj ] }
223 assert_equal([[Epoll::OUT, @wr]], tmp)
224 assert_nothing_raised { clone.close }
227 def test_set_idempotency
228 assert_nothing_raised do
229 @ep.set @rd, Epoll::IN
230 @ep.set @rd, Epoll::IN
231 @ep.set @wr, Epoll::OUT
232 @ep.set @wr, Epoll::OUT
236 def test_wait_timeout
238 assert_equal 0, @ep.wait(nil, 100) { |flags,obj| assert false }
240 assert(diff >= 0.075, "#{diff} < 0.100s")
244 assert_raises(Errno::ENOENT) { @ep.del(@rd) }
245 assert_nothing_raised do
246 @ep.add(@rd, Epoll::IN)
252 @ep.add(@rd, Epoll::IN)
253 assert_equal 0, @ep.wait(nil, 0) { |flags,obj| assert false }
256 nr = @ep.wait(nil, 0) do |flags,obj|
257 assert_equal Epoll::IN, flags
258 assert_equal obj, @rd
266 @ep.add(@wr, Epoll::OUT | Epoll::IN)
268 nr = @ep.wait(nil, 0) do |flags, obj|
269 assert_equal Epoll::OUT, flags
270 assert_equal obj, @wr
277 def test_wait_write_blocked
279 @wr.write_nonblock('.' * 65536)
283 @ep.add(@wr, Epoll::OUT | Epoll::IN)
285 assert_equal 0, @ep.wait(nil, 0) { |flags,event| assert false }
290 @ep.add @rd, Epoll::IN
291 thr = Thread.new { tmp = IO.select([ @ep ]) }
296 assert_equal([[@ep],[],[]], tmp)
299 def test_new_no_cloexec
301 io = Epoll.new(0).to_io
302 assert((io.fcntl(Fcntl::F_GETFD) & Fcntl::FD_CLOEXEC) == 0)
307 io = Epoll.new(Epoll::CLOEXEC).to_io
308 assert((io.fcntl(Fcntl::F_GETFD) & Fcntl::FD_CLOEXEC) == Fcntl::FD_CLOEXEC)
314 assert((io.fcntl(Fcntl::F_GETFD) & Fcntl::FD_CLOEXEC) == Fcntl::FD_CLOEXEC)
318 assert_nil @ep.delete(@rd)
319 assert_nil @ep.delete(@wr)
320 assert_nothing_raised { @ep.add @rd, Epoll::IN }
321 assert_equal 0, @ep.delete(@rd)
322 assert_nil @ep.delete(@rd)