1 # -*- encoding: binary -*-
2 # frozen_string_literal: true
6 $stderr.sync = $stdout.sync = true
10 class Test_POSIX_MQ < Test::Unit::TestCase
12 POSIX_MQ.method_defined?(:to_io) or
13 warn "POSIX_MQ#to_io not supported on this platform: #{RUBY_PLATFORM}"
14 POSIX_MQ.method_defined?(:notify) or
15 warn "POSIX_MQ#notify not supported on this platform: #{RUBY_PLATFORM}"
16 POSIX_MQ.respond_to?(:for_fd) or
17 warn "POSIX_MQ::for_fd not supported on this platform: #{RUBY_PLATFORM}"
21 @path = "/posix_mq.rb.#{Time.now.to_i}.#$$.#{rand}"
26 assert_equal @mq, @mq.unlink
32 def test_open_with_null_byte
33 assert_raises(ArgumentError) { POSIX_MQ.open("/hello\0world", :rw) }
36 def test_unlink_with_null_byte
37 assert_raises(ArgumentError) { POSIX_MQ.open("/hello\0world", :rw) }
41 assert_nothing_raised do
42 2025.times { POSIX_MQ.new(@path, :rw) }
44 @mq = POSIX_MQ.new(@path, :rw)
45 @mq.to_io if @mq.respond_to?(:to_io)
48 end unless defined?RUBY_ENGINE && RUBY_ENGINE == "rbx"
50 def test_name_clobber_proof
51 @mq = POSIX_MQ.new(@path, :rw)
54 assert_nothing_raised { @mq.name.gsub!(/\A/, "foo") }
55 assert_equal tmp, @mq.name
56 assert tmp.object_id != @mq.name.object_id
60 @mq = POSIX_MQ.new(@path, :rw)
62 assert_equal @mq.object_id, dup.object_id
64 assert_equal @mq.object_id, clone.object_id
67 def test_timed_receive_float
69 @mq = POSIX_MQ.new(@path, :rw)
70 assert ! @mq.nonblock?
72 maybe_timeout { @mq.receive nil, interval } or return
73 elapsed = Time.now - t0
74 assert_operator elapsed, :>, interval, elapsed.inspect
75 assert_operator elapsed, :<, 0.04, elapsed.inspect
78 def test_timed_receive_divmod
80 def interval.divmod(num)
81 num == 1 ? [ 0, 0.01 ] : nil
83 @mq = POSIX_MQ.new(@path, :rw)
84 assert ! @mq.nonblock?
86 maybe_timeout { @mq.receive nil, interval } or return
87 elapsed = Time.now - t0
88 assert_operator elapsed, :>=, 0.01, elapsed.inspect
89 assert_operator elapsed, :<=, 0.04, elapsed.inspect
92 def test_timed_receive_fixnum
94 @mq = POSIX_MQ.new(@path, :rw)
95 assert ! @mq.nonblock?
97 maybe_timeout { @mq.receive nil, interval } or return
98 elapsed = Time.now - t0
99 assert elapsed >= interval, elapsed.inspect
100 assert elapsed < 1.10, elapsed.inspect
104 alarm = lambda do |x|
105 Thread.new(x) do |time|
107 Process.kill(:USR1, $$)
113 Thread.new { @mq.send("HI") }
117 @mq = POSIX_MQ.new(@path, :rw)
118 assert ! @mq.nonblock?
121 elapsed = Time.now - t0
122 assert_equal(["HI", 0], a)
123 assert elapsed >= interval, elapsed.inspect
124 assert elapsed < 1.10, elapsed.inspect
125 assert_equal 1, alarms
127 trap(:USR1, sig) if sig
132 @mq = POSIX_MQ.new(@path, :rw, 0666, POSIX_MQ::Attr[0, 1, 1, 0])
133 assert ! @mq.nonblock?
134 assert_nothing_raised {
136 @mq.send "A", 1, interval
137 rescue NotImplementedError
142 maybe_timeout { @mq.send "B", 1, interval } or return
143 elapsed = Time.now - t0
144 assert elapsed > interval
148 POSIX_MQ.open(@path, IO::CREAT|IO::WRONLY, 0666) do |mq|
150 assert mq.kind_of?(POSIX_MQ)
151 assert_equal @path, mq.name
152 assert_equal true, mq.send("HI", 0)
153 assert_equal 1, mq.attr.curmsgs
159 idempotent_close = begin
166 2.times { assert_nil mq.close }
168 assert_raises(IOError) { mq.close }
173 POSIX_MQ.unlink(@path)
179 @mq = POSIX_MQ.new @path, IO::CREAT|IO::WRONLY, 0666
180 assert_equal path, @mq.name
183 def test_new_readonly
184 @mq = POSIX_MQ.new @path, IO::CREAT|IO::WRONLY, 0666
185 rd = POSIX_MQ.new @path, IO::RDONLY
186 assert_equal @mq.name, rd.name
190 def test_send0_receive
191 @mq = POSIX_MQ.new @path, IO::CREAT|IO::RDWR, 0666
192 assert_equal(@mq, @mq << "hello")
193 assert_equal [ "hello", 0 ], @mq.receive
197 @mq = POSIX_MQ.new @path, IO::CREAT|IO::RDWR, 0666
198 @mq << "hello" << "world"
199 assert_equal [ "hello", 0 ], @mq.receive
200 assert_equal [ "world", 0 ], @mq.receive
204 @mq = POSIX_MQ.new @path, IO::CREAT|IO::RDWR, 0666
206 assert_equal "hello", @mq.shift
211 @mq = POSIX_MQ.new @path, IO::CREAT|IO::RDWR, 0666
213 assert_equal "hello", @mq.shift(buf)
214 assert_equal "hello", buf
217 def test_send_receive
218 @mq = POSIX_MQ.new @path, IO::CREAT|IO::RDWR, 0666
219 assert_equal true, @mq.send("hello", 0)
220 assert_equal [ "hello", 0 ], @mq.receive
223 def test_send_receive_buf
225 @mq = POSIX_MQ.new @path, IO::CREAT|IO::RDWR, 0666
226 assert_equal true, @mq.send("hello", 0)
227 assert_equal [ "hello", 0 ], @mq.receive(buf)
228 assert_equal "hello", buf
231 def test_send_receive_prio
232 @mq = POSIX_MQ.new @path, IO::CREAT|IO::RDWR, 0666
233 assert_equal true, @mq.send("hello", 2)
234 assert_equal [ "hello", 2 ], @mq.receive
238 @mq = POSIX_MQ.new @path, IO::CREAT|IO::WRONLY, 0666
240 assert_equal POSIX_MQ::Attr, mq_attr.class
241 assert mq_attr.flags.kind_of?(Integer)
242 assert mq_attr.maxmsg.kind_of?(Integer)
243 assert mq_attr.msgsize.kind_of?(Integer)
244 assert mq_attr.curmsgs.kind_of?(Integer)
248 @mq = POSIX_MQ.new @path, IO::CREAT|IO::RDWR, 0666
249 assert @mq.to_io.kind_of?(IO)
250 assert_nothing_raised { IO.select([@mq], nil, nil, 0) }
251 end if POSIX_MQ.method_defined?(:to_io)
255 @mq = POSIX_MQ.new @path, IO::CREAT|IO::RDWR, 0666
256 @alt = POSIX_MQ.for_fd(@mq.to_io.to_i)
257 assert_equal true, @mq.send("hello", 0)
258 assert_equal [ "hello", 0 ], @alt.receive(buf)
259 assert_equal "hello", buf
260 assert_equal @mq.to_io.to_i, @alt.to_io.to_i
261 assert_raises(ArgumentError) { @alt.name }
262 assert_raises(Errno::EBADF) { POSIX_MQ.for_fd(1) }
263 @alt.autoclose = false
264 assert_equal false, @alt.autoclose?
266 # iterate a bunch and hope GC kicks in
267 fd = @mq.to_io.fileno
269 mq = POSIX_MQ.for_fd(fd)
270 assert_equal true, mq.autoclose?
272 assert_equal false, mq.autoclose?
274 end if POSIX_MQ.respond_to?(:for_fd) && POSIX_MQ.method_defined?(:to_io)
276 def test_autoclose_propagates_to_io
277 @mq = POSIX_MQ.new @path, IO::CREAT|IO::RDWR, 0666
278 @mq.autoclose = false
279 assert_equal false, @mq.to_io.autoclose?
281 assert_equal true, @mq.to_io.autoclose?
282 end if POSIX_MQ.method_defined?(:to_io)
286 orig = trap(:USR1) { wr.syswrite('.') }
287 @mq = POSIX_MQ.new @path, IO::CREAT|IO::RDWR, 0666
288 assert_nothing_raised { @mq.notify = :SIGUSR1 }
289 assert_nothing_raised { @mq.send("hello", 0) }
290 assert_equal [[rd], [], []], IO.select([rd], nil, nil, 10)
291 assert_equal '.', rd.sysread(1)
292 assert_nil(@mq.notify = nil)
293 assert_nothing_raised { @mq.send("hello", 0) }
294 assert_nil IO.select([rd], nil, nil, 0.1)
297 end if POSIX_MQ.method_defined?(:to_io)
300 @mq = POSIX_MQ.new @path, IO::CREAT|IO::RDWR, 0666
301 assert_nothing_raised { @mq.notify = false }
308 exit!(0) if Errno::EBADF === e && RUBY_PLATFORM =~ /freebsd/
309 warn "#{e.message} (#{e.class})\n"
313 _, status = Process.waitpid2(pid)
314 assert status.success?, status.inspect
318 @mq = POSIX_MQ.new @path, IO::CREAT|IO::WRONLY, 0666
319 mq_attr = POSIX_MQ::Attr.new(IO::NONBLOCK)
321 assert_equal IO::NONBLOCK, @mq.attr.flags
322 assert mq_attr.flags.kind_of?(Integer)
326 assert_equal 0, @mq.attr.flags
329 def test_setattr_fork
330 return if RUBY_PLATFORM !~ /freebsd/
331 @mq = POSIX_MQ.new @path, IO::CREAT|IO::WRONLY, 0666
332 mq_attr = POSIX_MQ::Attr.new(IO::NONBLOCK)
340 exit!(2) if Errno::EBADF === e && RUBY_PLATFORM =~ /freebsd/
341 warn "#{e.message} (#{e.class})\n"
346 _, status = Process.waitpid2(pid)
348 assert ! @mq.nonblock?
350 assert_equal 2, status.exitstatus
354 def test_new_nonblocking
355 @mq = POSIX_MQ.new @path, IO::CREAT|IO::WRONLY|IO::NONBLOCK, 0666
359 def test_new_blocking
360 @mq = POSIX_MQ.new @path, IO::CREAT|IO::WRONLY, 0666
361 assert ! @mq.nonblock?
364 def test_nonblock_toggle
365 @mq = POSIX_MQ.new @path, IO::CREAT|IO::WRONLY, 0666
366 assert ! @mq.nonblock?
370 assert ! @mq.nonblock?
371 assert_raises(ArgumentError) { @mq.nonblock = nil }
375 @mq = POSIX_MQ.new @path, :w
376 assert_equal IO::WRONLY, @mq.to_io.fcntl(Fcntl::F_GETFL)
377 end if POSIX_MQ.method_defined?(:to_io)
380 @mq = POSIX_MQ.new @path, :w
382 assert_nothing_raised { mq = POSIX_MQ.new @path, :r }
383 assert_equal IO::RDONLY, mq.to_io.fcntl(Fcntl::F_GETFL)
385 end if POSIX_MQ.method_defined?(:to_io)
387 def test_new_path_only
388 @mq = POSIX_MQ.new @path, :w
390 assert_nothing_raised { mq = POSIX_MQ.new @path }
391 assert_equal IO::RDONLY, mq.to_io.fcntl(Fcntl::F_GETFL)
393 end if POSIX_MQ.method_defined?(:to_io)
396 @mq = POSIX_MQ.new @path, :rw
397 assert_equal IO::RDWR, @mq.to_io.fcntl(Fcntl::F_GETFL)
398 end if POSIX_MQ.method_defined?(:to_io)
401 mq_attr = POSIX_MQ::Attr.new(IO::NONBLOCK, 1, 1, 0)
402 @mq = POSIX_MQ.new @path, IO::CREAT|IO::RDWR, 0666, mq_attr
404 assert_equal mq_attr, @mq.attr
406 assert_raises(Errno::EAGAIN) { @mq.receive }
407 assert_raises(Errno::EMSGSIZE) { @mq << '..' }
408 assert_nothing_raised { @mq << '.' }
409 assert_equal [ '.', 0 ], @mq.receive
410 assert_nothing_raised { @mq << '.' }
411 assert_raises(Errno::EAGAIN) { @mq << '.' }
415 mq_attr = POSIX_MQ::Attr.new(IO::NONBLOCK, 1, 1, 0)
416 @mq = POSIX_MQ.new @path, IO::CREAT|IO::RDWR, 0666, mq_attr
418 assert_nil @mq.tryreceive
419 assert_nil @mq.tryshift
420 assert_equal true, @mq.trysend("a")
421 assert_equal [ "a", 0 ], @mq.tryreceive
422 assert_equal true, @mq.trysend("b")
423 assert_equal "b", @mq.tryshift
424 assert_equal true, @mq.trysend("c")
425 assert_equal false, @mq.trysend("d")
429 min_posix_mq_prio_max = 31 # defined by POSIX
430 assert POSIX_MQ::PRIO_MAX >= min_posix_mq_prio_max
434 assert POSIX_MQ::OPEN_MAX.kind_of?(Integer)
437 def test_notify_block_replace
439 @mq = POSIX_MQ.new(@path, :rw)
440 assert_nothing_raised { @mq.notify { |mq| q << mq } }
441 assert_nothing_raised { Process.waitpid2(fork { @mq << "hi" }) }
442 assert_equal @mq.object_id, q.pop.object_id
443 assert_equal "hi", @mq.receive.first
444 assert_nothing_raised { @mq.notify { |mq| q << "hi" } }
445 assert_nothing_raised { Process.waitpid2(fork { @mq << "bye" }) }
446 assert_equal "hi", q.pop
447 end if POSIX_MQ.method_defined?(:notify)
449 def test_notify_thread
451 @mq = POSIX_MQ.new(@path, :rw)
452 @mq.notify { |mq| q << Thread.current }
455 assert x.instance_of?(Thread)
456 assert Thread.current != x
457 end if POSIX_MQ.method_defined?(:notify)
459 def test_bad_open_mode
460 assert_raises(ArgumentError) { POSIX_MQ.new(@path, "rw") }
463 def test_bad_open_attr
464 assert_raises(TypeError) { POSIX_MQ.new(@path, :rw, 0666, [0, 1, 1, 0]) }
468 @mq = POSIX_MQ.new @path, IO::CREAT|IO::WRONLY, 0666
469 assert_raises(TypeError) { @mq.attr = {} }
470 assert_raises(TypeError) { @mq.attr = Struct.new(:a,:b,:c,:d).new }
475 assert_raises(exc) { } # FAIL
477 rescue Errno::ETIMEDOUT => e
479 rescue NotImplementedError => e