POSIX_MQ#notify only works on GNU/Linux for now
[ruby_posix_mq.git] / test / test_posix_mq.rb
blob1fa029be86be67f80f4912d486053093c337ddf4
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   HAVE_TO_IO = if POSIX_MQ.instance_methods.grep(/\Ato_io\z/).empty?
11     warn "POSIX_MQ#to_io not supported on this platform: #{RUBY_PLATFORM}"
12     false
13   else
14     true
15   end
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_timed_receive
31     interval = 0.01
32     @mq = POSIX_MQ.new(@path, :rw)
33     assert ! @mq.nonblock?
34     t0 = Time.now
35     assert_raises(Errno::ETIMEDOUT) { @mq.receive "", interval }
36     elapsed = Time.now - t0
37     assert elapsed > interval
38   end
40   def test_timed_send
41     interval = 0.01
42     @mq = POSIX_MQ.new(@path, :rw, 0666, POSIX_MQ::Attr[0, 1, 1, 0])
43     assert ! @mq.nonblock?
44     assert_nothing_raised { @mq.send "A", 1, interval }
45     t0 = Time.now
46     assert_raises(Errno::ETIMEDOUT) { @mq.send "B", 1, interval }
47     elapsed = Time.now - t0
48     assert elapsed > interval
49   end
51   def test_open
52     POSIX_MQ.open(@path, IO::CREAT|IO::WRONLY, 0666) do |mq|
53       @mq = mq
54       assert mq.kind_of?(POSIX_MQ)
55       assert_equal @path, mq.name
56       assert_nil mq.send("HI", 0)
57       assert_equal 1, mq.attr.curmsgs
58       assert_nil mq.close
59       assert_raises(IOError) { mq.close }
60     end
61     assert @mq.closed?
62     @mq = nil
63     POSIX_MQ.unlink(@path)
64   end
66   def test_name
67     path = "" << @path.dup
68     path.freeze
69     @mq = POSIX_MQ.new @path, IO::CREAT|IO::WRONLY, 0666
70     assert_equal path, @mq.name
71   end
73   def test_new_readonly
74     @mq = POSIX_MQ.new @path, IO::CREAT|IO::WRONLY, 0666
75     rd = POSIX_MQ.new @path, IO::RDONLY
76     assert_equal @mq.name, rd.name
77     assert_nil rd.close
78   end
80   def test_send0_receive
81     @mq = POSIX_MQ.new @path, IO::CREAT|IO::RDWR, 0666
82     assert_equal(@mq, @mq << "hello")
83     assert_equal [ "hello", 0 ], @mq.receive
84   end
86   def test_send0_chain
87     @mq = POSIX_MQ.new @path, IO::CREAT|IO::RDWR, 0666
88     @mq << "hello" << "world"
89     assert_equal [ "hello", 0 ], @mq.receive
90     assert_equal [ "world", 0 ], @mq.receive
91   end
93   def test_send_receive
94     @mq = POSIX_MQ.new @path, IO::CREAT|IO::RDWR, 0666
95     assert_nil @mq.send("hello", 0)
96     assert_equal [ "hello", 0 ], @mq.receive
97   end
99   def test_send_receive_buf
100     buf = ""
101     @mq = POSIX_MQ.new @path, IO::CREAT|IO::RDWR, 0666
102     assert_nil @mq.send("hello", 0)
103     assert_equal [ "hello", 0 ], @mq.receive(buf)
104     assert_equal "hello", buf
105   end
107   def test_send_receive_prio
108     @mq = POSIX_MQ.new @path, IO::CREAT|IO::RDWR, 0666
109     assert_nil @mq.send("hello", 2)
110     assert_equal [ "hello", 2 ], @mq.receive
111   end
113   def test_getattr
114     @mq = POSIX_MQ.new @path, IO::CREAT|IO::WRONLY, 0666
115     mq_attr = @mq.attr
116     assert_equal POSIX_MQ::Attr, mq_attr.class
117     assert mq_attr.flags.kind_of?(Integer)
118     assert mq_attr.maxmsg.kind_of?(Integer)
119     assert mq_attr.msgsize.kind_of?(Integer)
120     assert mq_attr.curmsgs.kind_of?(Integer)
121   end
123   def test_to_io
124     @mq = POSIX_MQ.new @path, IO::CREAT|IO::RDWR, 0666
125     assert @mq.to_io.kind_of?(IO)
126     assert_nothing_raised { IO.select([@mq], nil, nil, 0) }
127   end if HAVE_TO_IO
129   def test_notify
130     rd, wr = IO.pipe
131     orig = trap(:USR1) { wr.syswrite('.') }
132     @mq = POSIX_MQ.new @path, IO::CREAT|IO::RDWR, 0666
133     assert_nothing_raised { @mq.notify = :SIGUSR1 }
134     assert_nothing_raised { @mq.send("hello", 0) }
135     assert_equal [[rd], [], []], IO.select([rd], nil, nil, 10)
136     assert_equal '.', rd.sysread(1)
137     assert_nil(@mq.notify = nil)
138     assert_nothing_raised { @mq.send("hello", 0) }
139     assert_nil IO.select([rd], nil, nil, 0.1)
140     ensure
141       trap(:USR1, orig)
142   end
144   def test_notify_none
145     @mq = POSIX_MQ.new @path, IO::CREAT|IO::RDWR, 0666
146     assert_nothing_raised { @mq.notify = false }
147     pid = fork do
148       begin
149         @mq.notify = :USR1
150       rescue Errno::EBUSY
151         exit 0
152       rescue => e
153         p e
154       end
155       exit! 1
156     end
157     _, status = Process.waitpid2(pid)
158     assert status.success?, status.inspect
159   end
161   def test_setattr
162     @mq = POSIX_MQ.new @path, IO::CREAT|IO::WRONLY, 0666
163     mq_attr = POSIX_MQ::Attr.new(IO::NONBLOCK)
164     @mq.attr = mq_attr
165     assert_equal IO::NONBLOCK, @mq.attr.flags
166     assert mq_attr.flags.kind_of?(Integer)
168     mq_attr.flags = 0
169     @mq.attr = mq_attr
170     assert_equal 0, @mq.attr.flags
171   end
173   def test_new_nonblocking
174     @mq = POSIX_MQ.new @path, IO::CREAT|IO::WRONLY|IO::NONBLOCK, 0666
175     assert @mq.nonblock?
176   end
178   def test_new_blocking
179     @mq = POSIX_MQ.new @path, IO::CREAT|IO::WRONLY, 0666
180     assert ! @mq.nonblock?
181   end
183   def test_nonblock_toggle
184     @mq = POSIX_MQ.new @path, IO::CREAT|IO::WRONLY, 0666
185     assert ! @mq.nonblock?
186     @mq.nonblock = true
187     assert @mq.nonblock?
188     @mq.nonblock = false
189     assert ! @mq.nonblock?
190     assert_raises(ArgumentError) { @mq.nonblock = nil }
191   end
193   def test_new_sym_w
194     @mq = POSIX_MQ.new @path, :w
195     assert_equal IO::WRONLY, @mq.to_io.fcntl(Fcntl::F_GETFL)
196   end if HAVE_TO_IO
198   def test_new_sym_r
199     @mq = POSIX_MQ.new @path, :w
200     mq = nil
201     assert_nothing_raised { mq = POSIX_MQ.new @path, :r }
202     assert_equal IO::RDONLY, mq.to_io.fcntl(Fcntl::F_GETFL)
203     assert_nil mq.close
204   end if HAVE_TO_IO
206   def test_new_path_only
207     @mq = POSIX_MQ.new @path, :w
208     mq = nil
209     assert_nothing_raised { mq = POSIX_MQ.new @path }
210     assert_equal IO::RDONLY, mq.to_io.fcntl(Fcntl::F_GETFL)
211     assert_nil mq.close
212   end if HAVE_TO_IO
214   def test_new_sym_wr
215     @mq = POSIX_MQ.new @path, :rw
216     assert_equal IO::RDWR, @mq.to_io.fcntl(Fcntl::F_GETFL)
217   end if HAVE_TO_IO
219   def test_new_attr
220     mq_attr = POSIX_MQ::Attr.new(IO::NONBLOCK, 1, 1, 0)
221     @mq = POSIX_MQ.new @path, IO::CREAT|IO::RDWR, 0666, mq_attr
222     assert @mq.nonblock?
223     assert_equal mq_attr, @mq.attr
225     assert_raises(Errno::EAGAIN) { @mq.receive }
226     assert_raises(Errno::EMSGSIZE) { @mq << '..' }
227     assert_nothing_raised { @mq << '.' }
228     assert_equal [ '.', 0 ], @mq.receive
229     assert_nothing_raised { @mq << '.' }
230     assert_raises(Errno::EAGAIN) { @mq << '.' }
231   end
233   def test_prio_max
234     min_posix_mq_prio_max = 31 # defined by POSIX
235     assert POSIX_MQ::PRIO_MAX >= min_posix_mq_prio_max
236   end
238   def test_open_max
239     assert POSIX_MQ::OPEN_MAX.kind_of?(Integer)
240   end
242   def test_notify_block_replace
243     q = Queue.new
244     @mq = POSIX_MQ.new(@path, :rw)
245     assert_nothing_raised { @mq.notify { |mq| q << mq } }
246     @mq << "hi"
247     assert_equal POSIX_MQ, q.pop.class
248     assert_equal "hi", @mq.receive.first
249     assert_nothing_raised { @mq.notify { |mq| q << "hi" } }
250     @mq << "bye"
251     assert_equal "hi", q.pop
252   end if POSIX_MQ.respond_to?(:notify)
254   def test_notify_thread
255     q = Queue.new
256     @mq = POSIX_MQ.new(@path, :rw)
257     @mq.notify_thread = thr = Thread.new { sleep }
258     assert thr.alive?
259     @mq.notify { |mq| q << Thread.current }
260     @mq << "."
261     x = q.pop
262     assert x.instance_of?(Thread)
263     assert Thread.current != x
264     assert ! thr.alive?
265   end if POSIX_MQ.respond_to?(:notify)