attempt to support systems w/o mq_timedsend/mq_timedreceive
[ruby_posix_mq.git] / test / test_posix_mq.rb
blob8febb0435748524d89729e591230c7514cf9ea85
1 # -*- encoding: binary -*-
2 require 'test/unit'
3 require 'posix_mq'
4 require 'thread'
5 require 'fcntl'
6 $stderr.sync = $stdout.sync = true
8 class Test_POSIX_MQ < Test::Unit::TestCase
10   POSIX_MQ.method_defined?(:to_io) or
11     warn "POSIX_MQ#to_io not supported on this platform: #{RUBY_PLATFORM}"
12   POSIX_MQ.method_defined?(:notify) or
13     warn "POSIX_MQ#notify not supported on this platform: #{RUBY_PLATFORM}"
15   def setup
16     @mq = nil
17     @path = "/posix_mq.rb.#{Time.now.to_i}.#$$.#{rand}"
18   end
20   def teardown
21     @mq or return
22     assert_equal @mq, @mq.unlink
23     assert ! @mq.closed?
24     assert_nil @mq.close
25     assert @mq.closed?
26   end
28   def test_open_with_null_byte
29     assert_raises(ArgumentError) { POSIX_MQ.open("/hello\0world", :rw) }
30   end
32   def test_unlink_with_null_byte
33     assert_raises(ArgumentError) { POSIX_MQ.open("/hello\0world", :rw) }
34   end
36   def test_gc
37     assert_nothing_raised do
38       2025.times { POSIX_MQ.new(@path, :rw) }
39       2025.times { @mq = POSIX_MQ.new(@path, :rw); @mq.to_io }
40     end
41   end unless defined?RUBY_ENGINE && RUBY_ENGINE == "rbx"
43   def test_name_clobber_proof
44     @mq = POSIX_MQ.new(@path, :rw)
45     tmp = @mq.name
46     tmp.freeze
47     assert_nothing_raised { @mq.name.gsub!(/\A/, "foo") }
48     assert_equal tmp, @mq.name
49     assert tmp.object_id != @mq.name.object_id
50   end
52   def test_dup_clone
53     @mq = POSIX_MQ.new(@path, :rw)
54     dup = @mq.dup
55     assert_equal @mq.object_id, dup.object_id
56     clone = @mq.clone
57     assert_equal @mq.object_id, clone.object_id
58   end
60   def test_timed_receive_float
61     interval = 0.01
62     @mq = POSIX_MQ.new(@path, :rw)
63     assert ! @mq.nonblock?
64     t0 = Time.now
65     maybe_timeout { @mq.receive "", interval } or return
66     elapsed = Time.now - t0
67     assert elapsed > interval, elapsed.inspect
68     assert elapsed < 0.02, elapsed.inspect
69   end
71   def test_timed_receive_divmod
72     interval = Object.new
73     def interval.divmod(num)
74       num == 1 ? [ 0, 0.01 ] : nil
75     end
76     @mq = POSIX_MQ.new(@path, :rw)
77     assert ! @mq.nonblock?
78     t0 = Time.now
79     maybe_timeout { @mq.receive "", interval } or return
80     elapsed = Time.now - t0
81     assert elapsed >= 0.01, elapsed.inspect
82     assert elapsed <= 0.02, elapsed.inspect
83   end
85   def test_timed_receive_fixnum
86     interval = 1
87     @mq = POSIX_MQ.new(@path, :rw)
88     assert ! @mq.nonblock?
89     t0 = Time.now
90     maybe_timeout { @mq.receive "", interval } or return
91     elapsed = Time.now - t0
92     assert elapsed >= interval, elapsed.inspect
93     assert elapsed < 1.10, elapsed.inspect
94   end
96   def test_timed_send
97     interval = 0.01
98     @mq = POSIX_MQ.new(@path, :rw, 0666, POSIX_MQ::Attr[0, 1, 1, 0])
99     assert ! @mq.nonblock?
100     assert_nothing_raised {
101       begin
102         @mq.send "A", 1, interval
103       rescue NotImplementedError
104         return
105       end
106     }
107     t0 = Time.now
108     maybe_timeout { @mq.send "B", 1, interval } or return
109     elapsed = Time.now - t0
110     assert elapsed > interval
111   end
113   def test_open
114     POSIX_MQ.open(@path, IO::CREAT|IO::WRONLY, 0666) do |mq|
115       @mq = mq
116       assert mq.kind_of?(POSIX_MQ)
117       assert_equal @path, mq.name
118       assert_nil mq.send("HI", 0)
119       assert_equal 1, mq.attr.curmsgs
120       assert_nil mq.close
121       assert_raises(IOError) { mq.close }
122     end
123     assert @mq.closed?
124     @mq = nil
125     POSIX_MQ.unlink(@path)
126   end
128   def test_name
129     path = "" << @path.dup
130     path.freeze
131     @mq = POSIX_MQ.new @path, IO::CREAT|IO::WRONLY, 0666
132     assert_equal path, @mq.name
133   end
135   def test_new_readonly
136     @mq = POSIX_MQ.new @path, IO::CREAT|IO::WRONLY, 0666
137     rd = POSIX_MQ.new @path, IO::RDONLY
138     assert_equal @mq.name, rd.name
139     assert_nil rd.close
140   end
142   def test_send0_receive
143     @mq = POSIX_MQ.new @path, IO::CREAT|IO::RDWR, 0666
144     assert_equal(@mq, @mq << "hello")
145     assert_equal [ "hello", 0 ], @mq.receive
146   end
148   def test_send0_chain
149     @mq = POSIX_MQ.new @path, IO::CREAT|IO::RDWR, 0666
150     @mq << "hello" << "world"
151     assert_equal [ "hello", 0 ], @mq.receive
152     assert_equal [ "world", 0 ], @mq.receive
153   end
155   def test_shift
156     @mq = POSIX_MQ.new @path, IO::CREAT|IO::RDWR, 0666
157     @mq << "hello"
158     assert_equal "hello", @mq.shift
159   end
161   def test_shift_buf
162     buf = ""
163     @mq = POSIX_MQ.new @path, IO::CREAT|IO::RDWR, 0666
164     @mq << "hello"
165     assert_equal "hello", @mq.shift(buf)
166     assert_equal "hello", buf
167   end
169   def test_send_receive
170     @mq = POSIX_MQ.new @path, IO::CREAT|IO::RDWR, 0666
171     assert_nil @mq.send("hello", 0)
172     assert_equal [ "hello", 0 ], @mq.receive
173   end
175   def test_send_receive_buf
176     buf = ""
177     @mq = POSIX_MQ.new @path, IO::CREAT|IO::RDWR, 0666
178     assert_nil @mq.send("hello", 0)
179     assert_equal [ "hello", 0 ], @mq.receive(buf)
180     assert_equal "hello", buf
181   end
183   def test_send_receive_prio
184     @mq = POSIX_MQ.new @path, IO::CREAT|IO::RDWR, 0666
185     assert_nil @mq.send("hello", 2)
186     assert_equal [ "hello", 2 ], @mq.receive
187   end
189   def test_getattr
190     @mq = POSIX_MQ.new @path, IO::CREAT|IO::WRONLY, 0666
191     mq_attr = @mq.attr
192     assert_equal POSIX_MQ::Attr, mq_attr.class
193     assert mq_attr.flags.kind_of?(Integer)
194     assert mq_attr.maxmsg.kind_of?(Integer)
195     assert mq_attr.msgsize.kind_of?(Integer)
196     assert mq_attr.curmsgs.kind_of?(Integer)
197   end
199   def test_to_io
200     @mq = POSIX_MQ.new @path, IO::CREAT|IO::RDWR, 0666
201     assert @mq.to_io.kind_of?(IO)
202     assert_nothing_raised { IO.select([@mq], nil, nil, 0) }
203   end if POSIX_MQ.method_defined?(:to_io)
205   def test_notify
206     rd, wr = IO.pipe
207     orig = trap(:USR1) { wr.syswrite('.') }
208     @mq = POSIX_MQ.new @path, IO::CREAT|IO::RDWR, 0666
209     assert_nothing_raised { @mq.notify = :SIGUSR1 }
210     assert_nothing_raised { @mq.send("hello", 0) }
211     assert_equal [[rd], [], []], IO.select([rd], nil, nil, 10)
212     assert_equal '.', rd.sysread(1)
213     assert_nil(@mq.notify = nil)
214     assert_nothing_raised { @mq.send("hello", 0) }
215     assert_nil IO.select([rd], nil, nil, 0.1)
216     ensure
217       trap(:USR1, orig)
218   end
220   def test_notify_none
221     @mq = POSIX_MQ.new @path, IO::CREAT|IO::RDWR, 0666
222     assert_nothing_raised { @mq.notify = false }
223     pid = fork do
224       begin
225         @mq.notify = :USR1
226       rescue Errno::EBUSY
227         exit 0
228       rescue => e
229         p e
230       end
231       exit! 1
232     end
233     _, status = Process.waitpid2(pid)
234     assert status.success?, status.inspect
235   end
237   def test_setattr
238     @mq = POSIX_MQ.new @path, IO::CREAT|IO::WRONLY, 0666
239     mq_attr = POSIX_MQ::Attr.new(IO::NONBLOCK)
240     @mq.attr = mq_attr
241     assert_equal IO::NONBLOCK, @mq.attr.flags
242     assert mq_attr.flags.kind_of?(Integer)
244     mq_attr.flags = 0
245     @mq.attr = mq_attr
246     assert_equal 0, @mq.attr.flags
247   end
249   def test_new_nonblocking
250     @mq = POSIX_MQ.new @path, IO::CREAT|IO::WRONLY|IO::NONBLOCK, 0666
251     assert @mq.nonblock?
252   end
254   def test_new_blocking
255     @mq = POSIX_MQ.new @path, IO::CREAT|IO::WRONLY, 0666
256     assert ! @mq.nonblock?
257   end
259   def test_nonblock_toggle
260     @mq = POSIX_MQ.new @path, IO::CREAT|IO::WRONLY, 0666
261     assert ! @mq.nonblock?
262     @mq.nonblock = true
263     assert @mq.nonblock?
264     @mq.nonblock = false
265     assert ! @mq.nonblock?
266     assert_raises(ArgumentError) { @mq.nonblock = nil }
267   end
269   def test_new_sym_w
270     @mq = POSIX_MQ.new @path, :w
271     assert_equal IO::WRONLY, @mq.to_io.fcntl(Fcntl::F_GETFL)
272   end if POSIX_MQ.method_defined?(:to_io)
274   def test_new_sym_r
275     @mq = POSIX_MQ.new @path, :w
276     mq = nil
277     assert_nothing_raised { mq = POSIX_MQ.new @path, :r }
278     assert_equal IO::RDONLY, mq.to_io.fcntl(Fcntl::F_GETFL)
279     assert_nil mq.close
280   end if POSIX_MQ.method_defined?(:to_io)
282   def test_new_path_only
283     @mq = POSIX_MQ.new @path, :w
284     mq = nil
285     assert_nothing_raised { mq = POSIX_MQ.new @path }
286     assert_equal IO::RDONLY, mq.to_io.fcntl(Fcntl::F_GETFL)
287     assert_nil mq.close
288   end if POSIX_MQ.method_defined?(:to_io)
290   def test_new_sym_wr
291     @mq = POSIX_MQ.new @path, :rw
292     assert_equal IO::RDWR, @mq.to_io.fcntl(Fcntl::F_GETFL)
293   end if POSIX_MQ.method_defined?(:to_io)
295   def test_new_attr
296     mq_attr = POSIX_MQ::Attr.new(IO::NONBLOCK, 1, 1, 0)
297     @mq = POSIX_MQ.new @path, IO::CREAT|IO::RDWR, 0666, mq_attr
298     assert @mq.nonblock?
299     assert_equal mq_attr, @mq.attr
301     assert_raises(Errno::EAGAIN) { @mq.receive }
302     assert_raises(Errno::EMSGSIZE) { @mq << '..' }
303     assert_nothing_raised { @mq << '.' }
304     assert_equal [ '.', 0 ], @mq.receive
305     assert_nothing_raised { @mq << '.' }
306     assert_raises(Errno::EAGAIN) { @mq << '.' }
307   end
309   def test_prio_max
310     min_posix_mq_prio_max = 31 # defined by POSIX
311     assert POSIX_MQ::PRIO_MAX >= min_posix_mq_prio_max
312   end
314   def test_open_max
315     assert POSIX_MQ::OPEN_MAX.kind_of?(Integer)
316   end
318   def test_notify_block_replace
319     q = Queue.new
320     @mq = POSIX_MQ.new(@path, :rw)
321     assert_nothing_raised { @mq.notify { |mq| q << mq } }
322     assert_nothing_raised { Process.waitpid2(fork { @mq << "hi" }) }
323     assert_equal @mq.object_id, q.pop.object_id
324     assert_equal "hi", @mq.receive.first
325     assert_nothing_raised { @mq.notify { |mq| q << "hi" } }
326     assert_nothing_raised { Process.waitpid2(fork { @mq << "bye" }) }
327     assert_equal "hi", q.pop
328   end if POSIX_MQ.method_defined?(:notify)
330   def test_notify_thread
331     q = Queue.new
332     @mq = POSIX_MQ.new(@path, :rw)
333     @mq.notify { |mq| q << Thread.current }
334     @mq << "."
335     x = q.pop
336     assert x.instance_of?(Thread)
337     assert Thread.current != x
338   end if POSIX_MQ.method_defined?(:notify)
340   def test_bad_open_mode
341     assert_raises(ArgumentError) { mq = POSIX_MQ.new(@path, "rw") }
342   end
344   def test_bad_open_attr
345     assert_raises(TypeError) { POSIX_MQ.new(@path, :rw, 0666, [0, 1, 1, 0]) }
346   end
348   def test_bad_setattr
349     @mq = POSIX_MQ.new @path, IO::CREAT|IO::WRONLY, 0666
350     assert_raises(TypeError) { @mq.attr = {} }
351     assert_raises(TypeError) { @mq.attr = Struct.new(:a,:b,:c,:d).new }
352   end
354   def maybe_timeout
355     yield
356     assert_raises(exc) { } # FAIL
357     return true
358     rescue Errno::ETIMEDOUT => e
359       return true
360     rescue NotImplementedError => e
361       warn "E: #{e}"
362       return false
363   end