open/notify: invoke GC if needed
[ruby_posix_mq.git] / test / test_posix_mq.rb
blob37ca66427f4402b472135aeadc79a6b980019308
1 # -*- encoding: binary -*-
2 require 'test/unit'
3 require 'posix_mq'
4 require 'thread'
5 require 'fcntl'
6 require 'set'
7 $stderr.sync = $stdout.sync = true
9 class Test_POSIX_MQ < Test::Unit::TestCase
10   METHODS = Set.new(POSIX_MQ.instance_methods.map { |x| x.to_sym })
12   METHODS.include?(:to_io) or
13     warn "POSIX_MQ#to_io not supported on this platform: #{RUBY_PLATFORM}"
14   METHODS.include?(:notify) or
15     warn "POSIX_MQ#notify not supported on this platform: #{RUBY_PLATFORM}"
17   def setup
18     @mq = nil
19     @path = "/posix_mq.rb.#{Time.now.to_i}.#$$.#{rand}"
20   end
22   def teardown
23     @mq or return
24     assert_equal @mq, @mq.unlink
25     assert ! @mq.closed?
26     assert_nil @mq.close
27     assert @mq.closed?
28   end
30   def test_gc
31     assert_nothing_raised do
32       2025.times { POSIX_MQ.new(@path, :rw) }
33     end
34   end
36   def test_name_clobber_proof
37     @mq = POSIX_MQ.new(@path, :rw)
38     tmp = @mq.name
39     tmp.freeze
40     assert_nothing_raised { @mq.name.gsub!(/\A/, "foo") }
41     assert_equal tmp, @mq.name
42     assert tmp.object_id != @mq.name.object_id
43   end
45   def test_dup_clone
46     @mq = POSIX_MQ.new(@path, :rw)
47     dup = @mq.dup
48     assert_equal @mq.object_id, dup.object_id
49     clone = @mq.clone
50     assert_equal @mq.object_id, clone.object_id
51   end
53   def test_timed_receive
54     interval = 0.01
55     @mq = POSIX_MQ.new(@path, :rw)
56     assert ! @mq.nonblock?
57     t0 = Time.now
58     assert_raises(Errno::ETIMEDOUT) { @mq.receive "", interval }
59     elapsed = Time.now - t0
60     assert elapsed > interval
61   end
63   def test_timed_send
64     interval = 0.01
65     @mq = POSIX_MQ.new(@path, :rw, 0666, POSIX_MQ::Attr[0, 1, 1, 0])
66     assert ! @mq.nonblock?
67     assert_nothing_raised { @mq.send "A", 1, interval }
68     t0 = Time.now
69     assert_raises(Errno::ETIMEDOUT) { @mq.send "B", 1, interval }
70     elapsed = Time.now - t0
71     assert elapsed > interval
72   end
74   def test_open
75     POSIX_MQ.open(@path, IO::CREAT|IO::WRONLY, 0666) do |mq|
76       @mq = mq
77       assert mq.kind_of?(POSIX_MQ)
78       assert_equal @path, mq.name
79       assert_nil mq.send("HI", 0)
80       assert_equal 1, mq.attr.curmsgs
81       assert_nil mq.close
82       assert_raises(IOError) { mq.close }
83     end
84     assert @mq.closed?
85     @mq = nil
86     POSIX_MQ.unlink(@path)
87   end
89   def test_name
90     path = "" << @path.dup
91     path.freeze
92     @mq = POSIX_MQ.new @path, IO::CREAT|IO::WRONLY, 0666
93     assert_equal path, @mq.name
94   end
96   def test_new_readonly
97     @mq = POSIX_MQ.new @path, IO::CREAT|IO::WRONLY, 0666
98     rd = POSIX_MQ.new @path, IO::RDONLY
99     assert_equal @mq.name, rd.name
100     assert_nil rd.close
101   end
103   def test_send0_receive
104     @mq = POSIX_MQ.new @path, IO::CREAT|IO::RDWR, 0666
105     assert_equal(@mq, @mq << "hello")
106     assert_equal [ "hello", 0 ], @mq.receive
107   end
109   def test_send0_chain
110     @mq = POSIX_MQ.new @path, IO::CREAT|IO::RDWR, 0666
111     @mq << "hello" << "world"
112     assert_equal [ "hello", 0 ], @mq.receive
113     assert_equal [ "world", 0 ], @mq.receive
114   end
116   def test_shift
117     @mq = POSIX_MQ.new @path, IO::CREAT|IO::RDWR, 0666
118     @mq << "hello"
119     assert_equal "hello", @mq.shift
120   end
122   def test_shift_buf
123     buf = ""
124     @mq = POSIX_MQ.new @path, IO::CREAT|IO::RDWR, 0666
125     @mq << "hello"
126     assert_equal "hello", @mq.shift(buf)
127     assert_equal "hello", buf
128   end
130   def test_send_receive
131     @mq = POSIX_MQ.new @path, IO::CREAT|IO::RDWR, 0666
132     assert_nil @mq.send("hello", 0)
133     assert_equal [ "hello", 0 ], @mq.receive
134   end
136   def test_send_receive_buf
137     buf = ""
138     @mq = POSIX_MQ.new @path, IO::CREAT|IO::RDWR, 0666
139     assert_nil @mq.send("hello", 0)
140     assert_equal [ "hello", 0 ], @mq.receive(buf)
141     assert_equal "hello", buf
142   end
144   def test_send_receive_prio
145     @mq = POSIX_MQ.new @path, IO::CREAT|IO::RDWR, 0666
146     assert_nil @mq.send("hello", 2)
147     assert_equal [ "hello", 2 ], @mq.receive
148   end
150   def test_getattr
151     @mq = POSIX_MQ.new @path, IO::CREAT|IO::WRONLY, 0666
152     mq_attr = @mq.attr
153     assert_equal POSIX_MQ::Attr, mq_attr.class
154     assert mq_attr.flags.kind_of?(Integer)
155     assert mq_attr.maxmsg.kind_of?(Integer)
156     assert mq_attr.msgsize.kind_of?(Integer)
157     assert mq_attr.curmsgs.kind_of?(Integer)
158   end
160   def test_to_io
161     @mq = POSIX_MQ.new @path, IO::CREAT|IO::RDWR, 0666
162     assert @mq.to_io.kind_of?(IO)
163     assert_nothing_raised { IO.select([@mq], nil, nil, 0) }
164   end if METHODS.include?(:to_io)
166   def test_notify
167     rd, wr = IO.pipe
168     orig = trap(:USR1) { wr.syswrite('.') }
169     @mq = POSIX_MQ.new @path, IO::CREAT|IO::RDWR, 0666
170     assert_nothing_raised { @mq.notify = :SIGUSR1 }
171     assert_nothing_raised { @mq.send("hello", 0) }
172     assert_equal [[rd], [], []], IO.select([rd], nil, nil, 10)
173     assert_equal '.', rd.sysread(1)
174     assert_nil(@mq.notify = nil)
175     assert_nothing_raised { @mq.send("hello", 0) }
176     assert_nil IO.select([rd], nil, nil, 0.1)
177     ensure
178       trap(:USR1, orig)
179   end
181   def test_notify_none
182     @mq = POSIX_MQ.new @path, IO::CREAT|IO::RDWR, 0666
183     assert_nothing_raised { @mq.notify = false }
184     pid = fork do
185       begin
186         @mq.notify = :USR1
187       rescue Errno::EBUSY
188         exit 0
189       rescue => e
190         p e
191       end
192       exit! 1
193     end
194     _, status = Process.waitpid2(pid)
195     assert status.success?, status.inspect
196   end
198   def test_setattr
199     @mq = POSIX_MQ.new @path, IO::CREAT|IO::WRONLY, 0666
200     mq_attr = POSIX_MQ::Attr.new(IO::NONBLOCK)
201     @mq.attr = mq_attr
202     assert_equal IO::NONBLOCK, @mq.attr.flags
203     assert mq_attr.flags.kind_of?(Integer)
205     mq_attr.flags = 0
206     @mq.attr = mq_attr
207     assert_equal 0, @mq.attr.flags
208   end
210   def test_new_nonblocking
211     @mq = POSIX_MQ.new @path, IO::CREAT|IO::WRONLY|IO::NONBLOCK, 0666
212     assert @mq.nonblock?
213   end
215   def test_new_blocking
216     @mq = POSIX_MQ.new @path, IO::CREAT|IO::WRONLY, 0666
217     assert ! @mq.nonblock?
218   end
220   def test_nonblock_toggle
221     @mq = POSIX_MQ.new @path, IO::CREAT|IO::WRONLY, 0666
222     assert ! @mq.nonblock?
223     @mq.nonblock = true
224     assert @mq.nonblock?
225     @mq.nonblock = false
226     assert ! @mq.nonblock?
227     assert_raises(ArgumentError) { @mq.nonblock = nil }
228   end
230   def test_new_sym_w
231     @mq = POSIX_MQ.new @path, :w
232     assert_equal IO::WRONLY, @mq.to_io.fcntl(Fcntl::F_GETFL)
233   end if METHODS.include?(:to_io)
235   def test_new_sym_r
236     @mq = POSIX_MQ.new @path, :w
237     mq = nil
238     assert_nothing_raised { mq = POSIX_MQ.new @path, :r }
239     assert_equal IO::RDONLY, mq.to_io.fcntl(Fcntl::F_GETFL)
240     assert_nil mq.close
241   end if METHODS.include?(:to_io)
243   def test_new_path_only
244     @mq = POSIX_MQ.new @path, :w
245     mq = nil
246     assert_nothing_raised { mq = POSIX_MQ.new @path }
247     assert_equal IO::RDONLY, mq.to_io.fcntl(Fcntl::F_GETFL)
248     assert_nil mq.close
249   end if METHODS.include?(:to_io)
251   def test_new_sym_wr
252     @mq = POSIX_MQ.new @path, :rw
253     assert_equal IO::RDWR, @mq.to_io.fcntl(Fcntl::F_GETFL)
254   end if METHODS.include?(:to_io)
256   def test_new_attr
257     mq_attr = POSIX_MQ::Attr.new(IO::NONBLOCK, 1, 1, 0)
258     @mq = POSIX_MQ.new @path, IO::CREAT|IO::RDWR, 0666, mq_attr
259     assert @mq.nonblock?
260     assert_equal mq_attr, @mq.attr
262     assert_raises(Errno::EAGAIN) { @mq.receive }
263     assert_raises(Errno::EMSGSIZE) { @mq << '..' }
264     assert_nothing_raised { @mq << '.' }
265     assert_equal [ '.', 0 ], @mq.receive
266     assert_nothing_raised { @mq << '.' }
267     assert_raises(Errno::EAGAIN) { @mq << '.' }
268   end
270   def test_prio_max
271     min_posix_mq_prio_max = 31 # defined by POSIX
272     assert POSIX_MQ::PRIO_MAX >= min_posix_mq_prio_max
273   end
275   def test_open_max
276     assert POSIX_MQ::OPEN_MAX.kind_of?(Integer)
277   end
279   def test_notify_block_replace
280     q = Queue.new
281     @mq = POSIX_MQ.new(@path, :rw)
282     assert_nothing_raised { @mq.notify { |mq| q << mq } }
283     assert_nothing_raised { Process.waitpid2(fork { @mq << "hi" }) }
284     assert_equal @mq.object_id, q.pop.object_id
285     assert_equal "hi", @mq.receive.first
286     assert_nothing_raised { @mq.notify { |mq| q << "hi" } }
287     assert_nothing_raised { Process.waitpid2(fork { @mq << "bye" }) }
288     assert_equal "hi", q.pop
289   end if METHODS.include?(:notify)
291   def test_notify_thread
292     q = Queue.new
293     @mq = POSIX_MQ.new(@path, :rw)
294     @mq.notify { |mq| q << Thread.current }
295     @mq << "."
296     x = q.pop
297     assert x.instance_of?(Thread)
298     assert Thread.current != x
299   end if METHODS.include?(:notify)