6 require 'sleepy_penguin'
8 class TestEpoll < Test::Unit::TestCase
10 RBX = defined?(RUBY_ENGINE) && (RUBY_ENGINE == 'rbx')
18 Epoll.constants.each do |const|
19 next if const.to_sym == :IO
20 nr = Epoll.const_get(const)
21 assert nr <= 0xffffffff, "#{const}=#{nr}"
27 Thread.new { sleep 0.100; @ep.add(@wr, Epoll::OUT) }
29 @ep.wait { |flags,obj| tmp << [ flags, obj ] }
30 elapsed = Time.now - t0
31 assert elapsed >= 0.100
32 assert_equal [[Epoll::OUT, @wr]], tmp
37 @ep.add @rd, Epoll::IN
39 @ep.wait(nil, 100) { |flags,obj| tmp << [ flags, obj ] }
43 _, status = Process.waitpid2(pid)
44 assert status.success?
45 @ep.wait(nil, 0) { |flags,obj| tmp << [ flags, obj ] }
46 assert_equal [[Epoll::IN, @rd]], tmp
49 def test_after_fork_usability
50 fork { @ep.add(@rd, Epoll::IN); exit!(0) }
51 fork { @ep.set(@rd, Epoll::IN); exit!(0) }
52 fork { @ep.to_io; exit!(0) }
53 fork { @ep.dup; exit!(0) }
54 fork { @ep.clone; exit!(0) }
55 fork { @ep.close; exit!(0) }
56 fork { @ep.closed?; exit!(0) }
66 res.each { |(_,status)| assert status.success? }
69 def test_tcp_connect_nonblock_edge
70 epflags = Epoll::OUT | Epoll::ET
72 srv = TCPServer.new(host, 0)
74 addr = Socket.pack_sockaddr_in(port, host)
75 sock = Socket.new(Socket::AF_INET, Socket::SOCK_STREAM, 0)
76 assert_raises(Errno::EINPROGRESS) { sock.connect_nonblock(addr) }
77 IO.select(nil, [ sock ], [sock ])
78 @ep.add(sock, epflags)
80 @ep.wait(1) { |flags, obj| tmp << [ flags, obj ] }
81 assert_equal [ [Epoll::OUT, sock] ], tmp
84 def test_tcp_connect_edge
85 epflags = Epoll::OUT | Epoll::ET
87 srv = TCPServer.new(host, 0)
89 sock = TCPSocket.new(host, port)
90 @ep.add(sock, epflags)
92 @ep.wait(1) { |flags, obj| tmp << [ flags, obj ] }
93 assert_equal [ [Epoll::OUT, sock] ], tmp
97 assert_nothing_raised do
98 @rd.close unless @rd.closed?
99 @wr.close unless @wr.closed?
100 @ep.close unless @ep.closed?
104 def test_max_events_big
105 @ep.add @rd, Epoll::IN
107 thr = Thread.new { @ep.wait(1024) { |flags, obj| tmp << [ flags, obj ] } }
112 assert_equal([[Epoll::IN, @rd]], tmp)
114 thr = Thread.new { @ep.wait { |flags, obj| tmp << [ flags, obj ] } }
116 assert_equal([[Epoll::IN, @rd]], tmp)
119 def test_max_events_small
120 @ep.add @rd, Epoll::IN | Epoll::ET
121 @ep.add @wr, Epoll::OUT | Epoll::ET
124 @ep.wait(1) { |flags, obj| tmp << [ flags, obj ] }
125 assert_equal 1, tmp.size
126 @ep.wait(1) { |flags, obj| tmp << [ flags, obj ] }
127 assert_equal 2, tmp.size
130 def test_signal_safe_wait_forever
133 time[:USR1] = Time.now
137 @ep.add @rd, Epoll::IN
140 sleep 0.5 # slightly racy :<
141 Process.kill(:USR1, Process.ppid)
144 time[:START_WAIT] = Time.now
145 assert_nothing_raised do
146 @ep.wait do |flags, obj|
147 tmp << [ flags, obj ]
151 assert_equal([[Epoll::IN, @rd]], tmp)
152 _, status = Process.waitpid2(pid)
153 assert status.success?, status.inspect
154 usr1_delay = time[:USR1] - time[:START_WAIT]
155 assert_in_delta(0.5, usr1_delay, 0.1, "usr1_delay=#{usr1_delay}")
156 ep_delay = time[:EP] - time[:USR1]
157 assert_in_delta(0.5, ep_delay, 0.1, "ep1_delay=#{ep_delay}")
159 trap(:USR1, 'DEFAULT')
163 @ep.add @rd, Epoll::IN
165 thr = Thread.new { @ep.wait { |flags, obj| tmp << [ flags, obj ] } }
168 assert_nil thr.join(0.01)
175 defined?(Epoll::RDHUP) or
176 return warn "skipping test, EPOLLRDHUP not available"
177 rd, wr = UNIXSocket.pair
178 @ep.add rd, Epoll::RDHUP
180 thr = Thread.new { @ep.wait { |flags, obj| tmp << [ flags, obj ] } }
181 wr.shutdown Socket::SHUT_WR
183 assert_equal([[ Epoll::RDHUP, rd ]], tmp)
187 @ep.add @rd, Epoll::IN
189 thr = Thread.new { @ep.wait { |flags, obj| tmp << [ flags, obj ] } }
192 assert_equal([[ Epoll::HUP, @rd ]], tmp)
197 assert_nothing_raised do
199 @ep.add @rd, Epoll::IN
200 @ep.add w, Epoll::OUT
201 @ep.add @wr, Epoll::OUT
204 @ep.wait { |flags, obj| tmp << [ flags, obj ] }
205 assert_equal 2, tmp.size
206 assert_equal [ Epoll::OUT ], tmp.map { |flags, obj| flags }.uniq
207 ios = tmp.map { |flags, obj| obj }
208 assert ios.include?(@wr)
209 assert ios.include?(w)
213 assert_nothing_raised { 4096.times { Epoll.new } }
218 assert_nothing_raised do
221 assert_kind_of IO, ep.to_io
230 assert @ep.to_io.fileno != clone.to_io.fileno
231 clone.add @wr, Epoll::OUT
232 @ep.wait(nil, 0) { |flags, obj| tmp << [ flags, obj ] }
233 assert_equal([[Epoll::OUT, @wr]], tmp)
234 assert_nothing_raised { clone.close }
240 assert @ep.to_io.fileno != clone.to_io.fileno
241 clone.add @wr, Epoll::OUT
242 @ep.wait(nil, 0) { |flags, obj| tmp << [ flags, obj ] }
243 assert_equal([[Epoll::OUT, @wr]], tmp)
244 assert_nothing_raised { clone.close }
247 def test_set_idempotency
248 assert_nothing_raised do
249 @ep.set @rd, Epoll::IN
250 @ep.set @rd, Epoll::IN
251 @ep.set @wr, Epoll::OUT
252 @ep.set @wr, Epoll::OUT
256 def test_wait_timeout
258 assert_equal 0, @ep.wait(nil, 100) { |flags,obj| assert false }
260 assert(diff >= 0.075, "#{diff} < 0.100s")
264 assert_raises(Errno::ENOENT) { @ep.del(@rd) }
265 assert_nothing_raised do
266 @ep.add(@rd, Epoll::IN)
272 @ep.add(@rd, Epoll::IN)
273 assert_equal 0, @ep.wait(nil, 0) { |flags,obj| assert false }
276 nr = @ep.wait(nil, 0) do |flags,obj|
277 assert_equal Epoll::IN, flags
278 assert_equal obj, @rd
286 @ep.add(@wr, Epoll::OUT | Epoll::IN)
288 nr = @ep.wait(nil, 0) do |flags, obj|
289 assert_equal Epoll::OUT, flags
290 assert_equal obj, @wr
297 def test_wait_write_blocked
299 @wr.write_nonblock('.' * 65536)
303 @ep.add(@wr, Epoll::OUT | Epoll::IN)
304 assert_equal 0, @ep.wait(nil, 0) { |flags,event| assert false }
309 @ep.add @rd, Epoll::IN
310 thr = Thread.new { tmp = IO.select([ @ep ]) }
315 assert_equal([[@ep],[],[]], tmp)
318 def test_new_no_cloexec
320 io = Epoll.new(0).to_io
321 assert((io.fcntl(Fcntl::F_GETFD) & Fcntl::FD_CLOEXEC) == 0)
326 io = Epoll.new(Epoll::CLOEXEC).to_io
327 assert((io.fcntl(Fcntl::F_GETFD) & Fcntl::FD_CLOEXEC) == Fcntl::FD_CLOEXEC)
333 assert_equal 0, io.fcntl(Fcntl::F_GETFD)
337 assert_nil @ep.delete(@rd)
338 assert_nil @ep.delete(@wr)
339 assert_nothing_raised { @ep.add @rd, Epoll::IN }
340 assert_equal @rd, @ep.delete(@rd)
341 assert_nil @ep.delete(@rd)
345 @ep.add @rd, Epoll::IN
346 assert_equal @rd, @ep.io_for(@rd.fileno)
347 assert_equal @rd, @ep.io_for(@rd)
349 assert_nil @ep.io_for(@rd.fileno)
350 assert_nil @ep.io_for(@rd)
354 @ep.add @rd, Epoll::IN
355 assert_equal Epoll::IN, @ep.flags_for(@rd.fileno)
356 assert_equal Epoll::IN, @ep.flags_for(@rd)
359 assert_nil @ep.flags_for(@rd.fileno)
360 assert_nil @ep.flags_for(@rd)
363 def test_flags_for_sym
365 assert_equal Epoll::IN, @ep.flags_for(@rd.fileno)
366 assert_equal Epoll::IN, @ep.flags_for(@rd)
369 assert_nil @ep.flags_for(@rd.fileno)
370 assert_nil @ep.flags_for(@rd)
373 def test_flags_for_sym_ary
374 @ep.add @rd, [:IN, :ET]
375 expect = Epoll::IN | Epoll::ET
376 assert_equal expect, @ep.flags_for(@rd.fileno)
377 assert_equal expect, @ep.flags_for(@rd)
380 assert_nil @ep.flags_for(@rd.fileno)
381 assert_nil @ep.flags_for(@rd)
385 assert ! @ep.include?(@rd)
386 @ep.add @rd, Epoll::IN
387 assert @ep.include?(@rd)
388 assert @ep.include?(@rd.fileno)
389 assert ! @ep.include?(@wr)
390 assert ! @ep.include?(@wr.fileno)
393 def test_cross_thread_close
395 thr = Thread.new { sleep(1); @ep.close }
396 assert_raises(IOError) do
397 @ep.wait { |flags, obj| tmp << [ flags, obj ] }
400 end if RUBY_VERSION == "1.9.3"
402 def test_epoll_wait_signal_torture
406 @ep.add(@rd, Epoll::IN)
408 trap(:USR1) { usr1 += 1 }
410 trap(:USR1, "DEFAULT")
413 nr.times { Process.kill(:USR1, ppid); sleep 0.01 }
418 assert_nothing_raised { @ep.wait(nil, 100) { |flags,obj| tmp << obj } }
421 _, status = Process.waitpid2(pid)
422 assert status.success?, status.inspect
423 assert usr1 > 0, "usr1: #{usr1}"
425 trap(:USR1, "DEFAULT")