pack rw_args struct
[ruby_posix_mq.git] / test / test_posix_mq.rb
bloba4fc407f6f9bd7ae993da62e597b78706e9feb14
1 # -*- encoding: binary -*-
2 require 'test/unit'
3 require 'thread'
4 require 'fcntl'
5 $stderr.sync = $stdout.sync = true
6 $-w = true
7 require 'posix_mq'
9 class Test_POSIX_MQ < Test::Unit::TestCase
11   POSIX_MQ.method_defined?(:to_io) or
12     warn "POSIX_MQ#to_io not supported on this platform: #{RUBY_PLATFORM}"
13   POSIX_MQ.method_defined?(:notify) or
14     warn "POSIX_MQ#notify not supported on this platform: #{RUBY_PLATFORM}"
15   POSIX_MQ.respond_to?(:for_fd) or
16     warn "POSIX_MQ::for_fd not supported on this platform: #{RUBY_PLATFORM}"
18   def setup
19     @mq = nil
20     @path = "/posix_mq.rb.#{Time.now.to_i}.#$$.#{rand}"
21   end
23   def teardown
24     @mq or return
25     assert_equal @mq, @mq.unlink
26     assert ! @mq.closed?
27     assert_nil @mq.close
28     assert @mq.closed?
29   end
31   def test_open_with_null_byte
32     assert_raises(ArgumentError) { POSIX_MQ.open("/hello\0world", :rw) }
33   end
35   def test_unlink_with_null_byte
36     assert_raises(ArgumentError) { POSIX_MQ.open("/hello\0world", :rw) }
37   end
39   def test_gc
40     assert_nothing_raised do
41       2025.times { POSIX_MQ.new(@path, :rw) }
42       2025.times {
43         @mq = POSIX_MQ.new(@path, :rw)
44         @mq.to_io if @mq.respond_to?(:to_io)
45       }
46     end
47   end unless defined?RUBY_ENGINE && RUBY_ENGINE == "rbx"
49   def test_name_clobber_proof
50     @mq = POSIX_MQ.new(@path, :rw)
51     tmp = @mq.name
52     tmp.freeze
53     assert_nothing_raised { @mq.name.gsub!(/\A/, "foo") }
54     assert_equal tmp, @mq.name
55     assert tmp.object_id != @mq.name.object_id
56   end
58   def test_dup_clone
59     @mq = POSIX_MQ.new(@path, :rw)
60     dup = @mq.dup
61     assert_equal @mq.object_id, dup.object_id
62     clone = @mq.clone
63     assert_equal @mq.object_id, clone.object_id
64   end
66   def test_timed_receive_float
67     interval = 0.01
68     @mq = POSIX_MQ.new(@path, :rw)
69     assert ! @mq.nonblock?
70     t0 = Time.now
71     maybe_timeout { @mq.receive "", interval } or return
72     elapsed = Time.now - t0
73     assert_operator elapsed, :>, interval, elapsed.inspect
74     assert_operator elapsed, :<, 0.04, elapsed.inspect
75   end
77   def test_timed_receive_divmod
78     interval = Object.new
79     def interval.divmod(num)
80       num == 1 ? [ 0, 0.01 ] : nil
81     end
82     @mq = POSIX_MQ.new(@path, :rw)
83     assert ! @mq.nonblock?
84     t0 = Time.now
85     maybe_timeout { @mq.receive "", interval } or return
86     elapsed = Time.now - t0
87     assert_operator elapsed, :>=, 0.01, elapsed.inspect
88     assert_operator elapsed, :<=, 0.04, elapsed.inspect
89   end
91   def test_timed_receive_fixnum
92     interval = 1
93     @mq = POSIX_MQ.new(@path, :rw)
94     assert ! @mq.nonblock?
95     t0 = Time.now
96     maybe_timeout { @mq.receive "", interval } or return
97     elapsed = Time.now - t0
98     assert elapsed >= interval, elapsed.inspect
99     assert elapsed < 1.10, elapsed.inspect
100   end
102   def test_signal_safe
103     alarm = lambda do |x|
104       Thread.new(x) do |time|
105         sleep(time)
106         Process.kill(:USR1, $$)
107       end
108     end
109     alarms = 0
110     sig = trap(:USR1) do
111       alarms += 1
112       Thread.new { @mq.send("HI") }
113     end
114     interval = 1
115     alarm.call interval
116     @mq = POSIX_MQ.new(@path, :rw)
117     assert ! @mq.nonblock?
118     t0 = Time.now
119     a = @mq.receive
120     elapsed = Time.now - t0
121     assert_equal(["HI", 0], a)
122     assert elapsed >= interval, elapsed.inspect
123     assert elapsed < 1.10, elapsed.inspect
124     assert_equal 1, alarms
125   ensure
126     trap(:USR1, sig) if sig
127   end
129   def test_timed_send
130     interval = 0.01
131     @mq = POSIX_MQ.new(@path, :rw, 0666, POSIX_MQ::Attr[0, 1, 1, 0])
132     assert ! @mq.nonblock?
133     assert_nothing_raised {
134       begin
135         @mq.send "A", 1, interval
136       rescue NotImplementedError
137         return
138       end
139     }
140     t0 = Time.now
141     maybe_timeout { @mq.send "B", 1, interval } or return
142     elapsed = Time.now - t0
143     assert elapsed > interval
144   end
146   def test_open
147     POSIX_MQ.open(@path, IO::CREAT|IO::WRONLY, 0666) do |mq|
148       @mq = mq
149       assert mq.kind_of?(POSIX_MQ)
150       assert_equal @path, mq.name
151       assert_equal true, mq.send("HI", 0)
152       assert_equal 1, mq.attr.curmsgs
153       assert_nil mq.close
154       assert_raises(IOError) { mq.close }
155     end
156     assert @mq.closed?
157     @mq = nil
158     POSIX_MQ.unlink(@path)
159   end
161   def test_name
162     path = "" << @path.dup
163     path.freeze
164     @mq = POSIX_MQ.new @path, IO::CREAT|IO::WRONLY, 0666
165     assert_equal path, @mq.name
166   end
168   def test_new_readonly
169     @mq = POSIX_MQ.new @path, IO::CREAT|IO::WRONLY, 0666
170     rd = POSIX_MQ.new @path, IO::RDONLY
171     assert_equal @mq.name, rd.name
172     assert_nil rd.close
173   end
175   def test_send0_receive
176     @mq = POSIX_MQ.new @path, IO::CREAT|IO::RDWR, 0666
177     assert_equal(@mq, @mq << "hello")
178     assert_equal [ "hello", 0 ], @mq.receive
179   end
181   def test_send0_chain
182     @mq = POSIX_MQ.new @path, IO::CREAT|IO::RDWR, 0666
183     @mq << "hello" << "world"
184     assert_equal [ "hello", 0 ], @mq.receive
185     assert_equal [ "world", 0 ], @mq.receive
186   end
188   def test_shift
189     @mq = POSIX_MQ.new @path, IO::CREAT|IO::RDWR, 0666
190     @mq << "hello"
191     assert_equal "hello", @mq.shift
192   end
194   def test_shift_buf
195     buf = ""
196     @mq = POSIX_MQ.new @path, IO::CREAT|IO::RDWR, 0666
197     @mq << "hello"
198     assert_equal "hello", @mq.shift(buf)
199     assert_equal "hello", buf
200   end
202   def test_send_receive
203     @mq = POSIX_MQ.new @path, IO::CREAT|IO::RDWR, 0666
204     assert_equal true, @mq.send("hello", 0)
205     assert_equal [ "hello", 0 ], @mq.receive
206   end
208   def test_send_receive_buf
209     buf = ""
210     @mq = POSIX_MQ.new @path, IO::CREAT|IO::RDWR, 0666
211     assert_equal true, @mq.send("hello", 0)
212     assert_equal [ "hello", 0 ], @mq.receive(buf)
213     assert_equal "hello", buf
214   end
216   def test_send_receive_prio
217     @mq = POSIX_MQ.new @path, IO::CREAT|IO::RDWR, 0666
218     assert_equal true, @mq.send("hello", 2)
219     assert_equal [ "hello", 2 ], @mq.receive
220   end
222   def test_getattr
223     @mq = POSIX_MQ.new @path, IO::CREAT|IO::WRONLY, 0666
224     mq_attr = @mq.attr
225     assert_equal POSIX_MQ::Attr, mq_attr.class
226     assert mq_attr.flags.kind_of?(Integer)
227     assert mq_attr.maxmsg.kind_of?(Integer)
228     assert mq_attr.msgsize.kind_of?(Integer)
229     assert mq_attr.curmsgs.kind_of?(Integer)
230   end
232   def test_to_io
233     @mq = POSIX_MQ.new @path, IO::CREAT|IO::RDWR, 0666
234     assert @mq.to_io.kind_of?(IO)
235     assert_nothing_raised { IO.select([@mq], nil, nil, 0) }
236   end if POSIX_MQ.method_defined?(:to_io)
238   def test_for_fd
239     buf = ""
240     @mq = POSIX_MQ.new @path, IO::CREAT|IO::RDWR, 0666
241     @alt = POSIX_MQ.for_fd(@mq.to_io.to_i)
242     assert_equal true, @mq.send("hello", 0)
243     assert_equal [ "hello", 0 ], @alt.receive(buf)
244     assert_equal "hello", buf
245     assert_equal @mq.to_io.to_i, @alt.to_io.to_i
246     assert_raises(ArgumentError) { @alt.name }
247     assert_raises(Errno::EBADF) { POSIX_MQ.for_fd(1) }
248     @alt.autoclose = false
249     assert_equal false, @alt.autoclose?
251     # iterate a bunch and hope GC kicks in
252     fd = @mq.to_io.fileno
253     10_000.times do
254       mq = POSIX_MQ.for_fd(fd)
255       assert_equal true, mq.autoclose?
256       mq.autoclose = false
257       assert_equal false, mq.autoclose?
258     end
259   end if POSIX_MQ.respond_to?(:for_fd) && POSIX_MQ.method_defined?(:to_io)
261   def test_notify
262     rd, wr = IO.pipe
263     orig = trap(:USR1) { wr.syswrite('.') }
264     @mq = POSIX_MQ.new @path, IO::CREAT|IO::RDWR, 0666
265     assert_nothing_raised { @mq.notify = :SIGUSR1 }
266     assert_nothing_raised { @mq.send("hello", 0) }
267     assert_equal [[rd], [], []], IO.select([rd], nil, nil, 10)
268     assert_equal '.', rd.sysread(1)
269     assert_nil(@mq.notify = nil)
270     assert_nothing_raised { @mq.send("hello", 0) }
271     assert_nil IO.select([rd], nil, nil, 0.1)
272     ensure
273       trap(:USR1, orig)
274   end if POSIX_MQ.method_defined?(:to_io)
276   def test_notify_none
277     @mq = POSIX_MQ.new @path, IO::CREAT|IO::RDWR, 0666
278     assert_nothing_raised { @mq.notify = false }
279     pid = fork do
280       begin
281         @mq.notify = :USR1
282       rescue Errno::EBUSY
283         exit 0
284       rescue => e
285         p e
286       end
287       exit! 1
288     end
289     _, status = Process.waitpid2(pid)
290     assert status.success?, status.inspect
291   end
293   def test_setattr
294     @mq = POSIX_MQ.new @path, IO::CREAT|IO::WRONLY, 0666
295     mq_attr = POSIX_MQ::Attr.new(IO::NONBLOCK)
296     @mq.attr = mq_attr
297     assert_equal IO::NONBLOCK, @mq.attr.flags
298     assert mq_attr.flags.kind_of?(Integer)
300     mq_attr.flags = 0
301     @mq.attr = mq_attr
302     assert_equal 0, @mq.attr.flags
303   end
305   def test_setattr_fork
306     @mq = POSIX_MQ.new @path, IO::CREAT|IO::WRONLY, 0666
307     mq_attr = POSIX_MQ::Attr.new(IO::NONBLOCK)
308     @mq.attr = mq_attr
309     assert @mq.nonblock?
311     pid = fork { @mq.nonblock = false }
312     assert Process.waitpid2(pid)[1].success?
313     assert ! @mq.nonblock?
314   end
316   def test_new_nonblocking
317     @mq = POSIX_MQ.new @path, IO::CREAT|IO::WRONLY|IO::NONBLOCK, 0666
318     assert @mq.nonblock?
319   end
321   def test_new_blocking
322     @mq = POSIX_MQ.new @path, IO::CREAT|IO::WRONLY, 0666
323     assert ! @mq.nonblock?
324   end
326   def test_nonblock_toggle
327     @mq = POSIX_MQ.new @path, IO::CREAT|IO::WRONLY, 0666
328     assert ! @mq.nonblock?
329     @mq.nonblock = true
330     assert @mq.nonblock?
331     @mq.nonblock = false
332     assert ! @mq.nonblock?
333     assert_raises(ArgumentError) { @mq.nonblock = nil }
334   end
336   def test_new_sym_w
337     @mq = POSIX_MQ.new @path, :w
338     assert_equal IO::WRONLY, @mq.to_io.fcntl(Fcntl::F_GETFL)
339   end if POSIX_MQ.method_defined?(:to_io)
341   def test_new_sym_r
342     @mq = POSIX_MQ.new @path, :w
343     mq = nil
344     assert_nothing_raised { mq = POSIX_MQ.new @path, :r }
345     assert_equal IO::RDONLY, mq.to_io.fcntl(Fcntl::F_GETFL)
346     assert_nil mq.close
347   end if POSIX_MQ.method_defined?(:to_io)
349   def test_new_path_only
350     @mq = POSIX_MQ.new @path, :w
351     mq = nil
352     assert_nothing_raised { mq = POSIX_MQ.new @path }
353     assert_equal IO::RDONLY, mq.to_io.fcntl(Fcntl::F_GETFL)
354     assert_nil mq.close
355   end if POSIX_MQ.method_defined?(:to_io)
357   def test_new_sym_wr
358     @mq = POSIX_MQ.new @path, :rw
359     assert_equal IO::RDWR, @mq.to_io.fcntl(Fcntl::F_GETFL)
360   end if POSIX_MQ.method_defined?(:to_io)
362   def test_new_attr
363     mq_attr = POSIX_MQ::Attr.new(IO::NONBLOCK, 1, 1, 0)
364     @mq = POSIX_MQ.new @path, IO::CREAT|IO::RDWR, 0666, mq_attr
365     assert @mq.nonblock?
366     assert_equal mq_attr, @mq.attr
368     assert_raises(Errno::EAGAIN) { @mq.receive }
369     assert_raises(Errno::EMSGSIZE) { @mq << '..' }
370     assert_nothing_raised { @mq << '.' }
371     assert_equal [ '.', 0 ], @mq.receive
372     assert_nothing_raised { @mq << '.' }
373     assert_raises(Errno::EAGAIN) { @mq << '.' }
374   end
376   def test_try
377     mq_attr = POSIX_MQ::Attr.new(IO::NONBLOCK, 1, 1, 0)
378     @mq = POSIX_MQ.new @path, IO::CREAT|IO::RDWR, 0666, mq_attr
380     assert_nil @mq.tryreceive
381     assert_nil @mq.tryshift
382     assert_equal true, @mq.trysend("a")
383     assert_equal [ "a", 0 ], @mq.tryreceive
384     assert_equal true, @mq.trysend("b")
385     assert_equal "b", @mq.tryshift
386     assert_equal true, @mq.trysend("c")
387     assert_equal false, @mq.trysend("d")
388   end
390   def test_prio_max
391     min_posix_mq_prio_max = 31 # defined by POSIX
392     assert POSIX_MQ::PRIO_MAX >= min_posix_mq_prio_max
393   end
395   def test_open_max
396     assert POSIX_MQ::OPEN_MAX.kind_of?(Integer)
397   end
399   def test_notify_block_replace
400     q = Queue.new
401     @mq = POSIX_MQ.new(@path, :rw)
402     assert_nothing_raised { @mq.notify { |mq| q << mq } }
403     assert_nothing_raised { Process.waitpid2(fork { @mq << "hi" }) }
404     assert_equal @mq.object_id, q.pop.object_id
405     assert_equal "hi", @mq.receive.first
406     assert_nothing_raised { @mq.notify { |mq| q << "hi" } }
407     assert_nothing_raised { Process.waitpid2(fork { @mq << "bye" }) }
408     assert_equal "hi", q.pop
409   end if POSIX_MQ.method_defined?(:notify)
411   def test_notify_thread
412     q = Queue.new
413     @mq = POSIX_MQ.new(@path, :rw)
414     @mq.notify { |mq| q << Thread.current }
415     @mq << "."
416     x = q.pop
417     assert x.instance_of?(Thread)
418     assert Thread.current != x
419   end if POSIX_MQ.method_defined?(:notify)
421   def test_bad_open_mode
422     assert_raises(ArgumentError) { POSIX_MQ.new(@path, "rw") }
423   end
425   def test_bad_open_attr
426     assert_raises(TypeError) { POSIX_MQ.new(@path, :rw, 0666, [0, 1, 1, 0]) }
427   end
429   def test_bad_setattr
430     @mq = POSIX_MQ.new @path, IO::CREAT|IO::WRONLY, 0666
431     assert_raises(TypeError) { @mq.attr = {} }
432     assert_raises(TypeError) { @mq.attr = Struct.new(:a,:b,:c,:d).new }
433   end
435   def maybe_timeout
436     yield
437     assert_raises(exc) { } # FAIL
438     return true
439     rescue Errno::ETIMEDOUT => e
440       return true
441     rescue NotImplementedError => e
442       warn "E: #{e}"
443       return false
444   end