remove support for errno clobbering in 1.9.1
[socket_dontwait.git] / test / test_socket_dontwait.rb
blob097ec961865c9a69ea69646cc7c1e508106e5ed5
1 require 'test/unit'
2 require 'io/nonblock'
3 $-w = true
5 CALLED = {}
6 class IO
7   %w(read read_nonblock readpartial).each do |m|
8     eval "
9       alias #{m}_orig #{m}
10       undef_method :#{m}
11       def #{m}(*args)
12         CALLED[:#{m}] = args
13         #{m}_orig(*args)
14       end
15     "
16   end
17 end
19 ORIGINAL_SOCKET = ENV['ORIGINAL_SOCKET'].to_i != 0
20 require(ORIGINAL_SOCKET ? 'socket' : 'socket_dontwait')
22 class TestSocketDontwait < Test::Unit::TestCase
23   def setup
24     @rd, @wr = UNIXSocket.pair
25   end
27   def teardown
28     CALLED.clear
29     @rd.close unless @rd.closed?
30     @wr.close unless @wr.closed?
31   end
33   def test_write_read_nonblock_str
34     assert_equal 3, @wr.write_nonblock('abc')
35     assert_equal 'abc', @rd.read_nonblock(3)
36     assert CALLED.empty? unless ORIGINAL_SOCKET
37   end
39   def test_write_read_nonblock_integer
40     assert_equal 3, @wr.write_nonblock(123)
41     assert_equal '123', @rd.read_nonblock(3)
42     assert CALLED.empty? unless ORIGINAL_SOCKET
43   end
45   def test_invalid_read_length
46     assert_raise(ArgumentError) { @rd.read(-1) }
47     assert_raise(ArgumentError) { @rd.read(-1, "") }
48     assert_raise(ArgumentError) { @rd.read_nonblock(-1) }
49     assert_raise(ArgumentError) { @rd.read_nonblock(-1, "") }
50     assert_raise(ArgumentError) { @rd.readpartial(-1) }
51     assert_raise(ArgumentError) { @rd.readpartial(-1, "") }
52     assert CALLED.empty? unless ORIGINAL_SOCKET
53   end
55   def test_read
56     buf = '.' * 4093
57     t = Thread.new { @rd.read 4096 }
58     assert_equal 4093, @wr.write(buf)
59     t.join(0)
60     assert t.alive?
61     assert_equal 4, @wr.write('....')
62     t.join
63     assert_equal('.' * 4096, t.value)
64     assert_equal('.', @rd.read(1))
65     @wr.close
66     assert_nil @rd.read(1)
67     assert CALLED.empty? unless ORIGINAL_SOCKET
68   end
70   def test_read_empty
71     @wr.close
72     assert_equal '', @rd.read
73     str = ''
74     str_object_id = str.object_id
75     assert_equal str_object_id, @rd.read(nil, str).object_id
76     assert CALLED.empty? unless ORIGINAL_SOCKET
77   end
79   def test_big_write_read_full_even
80     tmp = IO.read('/dev/urandom', 16427) * 32
81     tmp << '.' until ((tmp.bytesize % 16384) == 0)
82     return
83     pid = fork do
84       nr = @wr.write(tmp)
85       exit!(nr == tmp.bytesize)
86     end
87     @wr.close
88     assert_equal tmp, @rd.read
89     _, status = Process.waitpid2(pid)
90     assert status.success?
91     assert CALLED.empty? unless ORIGINAL_SOCKET
92   end
94   def test_big_write_read_full_odd
95     n = @wr.getsockopt(Socket::SOL_SOCKET, Socket::SO_SNDBUF).unpack('i')[0]
96     tmp = IO.read('/dev/urandom', 16427) * 32
97     tmp << 'aasdfzf' until ((tmp.bytesize % 16384) != 0)
98     pid = fork do
99       nr = @wr.write(tmp)
100       exit!(nr == tmp.bytesize)
101     end
102     @wr.close
103     assert_equal tmp, @rd.read
104     _, status = Process.waitpid2(pid)
105     assert status.success?
106     assert CALLED.empty? unless ORIGINAL_SOCKET
107   end
109   def test_zero_reads
110     assert_equal '', @rd.read(0)
111     assert_equal '', @rd.readpartial(0)
112     assert_equal '', @rd.read_nonblock(0)
114     buf = 'hello'
115     assert_equal buf.object_id, @rd.read(0, buf).object_id
116     assert_equal '', buf
117     buf = 'hello'
118     assert_equal buf.object_id, @rd.readpartial(0, buf).object_id
119     assert_equal '', buf
120     buf = 'hello'
121     assert_equal buf.object_id, @rd.read_nonblock(0, buf).object_id
122     assert_equal '', buf
123   end
125   def test_readpartial_signal
126     r, w = IO.pipe
127     pid = fork do
128       r.close
129       trap(:USR1) { w.syswrite "USR1" }
130       w.syswrite "READY"
131       rv = @rd.readpartial(16384)
132       exit!(rv == "SIGNAL SAFE" && (ORIGINAL_SOCKET || CALLED.empty?))
133     end
134     w.close
135     assert_equal "READY", r.read(5)
136     assert_nothing_raised do
137       sleep 0.5 # ugh, still potentially racy :<
138       Process.kill(:USR1, pid)
139     end
140     assert_equal 'USR1', r.readpartial(4)
141     assert_equal 11, @wr.write("SIGNAL SAFE")
142     _, status = Process.waitpid2(pid)
143     assert status.success?
144   end
146   def test_read_signal
147     r, w = IO.pipe
148     pid = fork do
149       r.close
150       trap(:USR1) { w.syswrite "USR1" }
151       w.syswrite "READY"
152       rv = @rd.read(11)
153       exit!(rv == "SIGNAL SAFE" && (ORIGINAL_SOCKET || CALLED.empty?))
154     end
155     w.close
156     assert_equal "READY", r.read(5)
157     assert_nothing_raised do
158       sleep 0.5 # ugh, still potentially racy :<
159       Process.kill(:USR1, pid)
160     end
161     assert_equal 'USR1', r.readpartial(4)
162     assert_equal 11, @wr.write("SIGNAL SAFE")
163     _, status = Process.waitpid2(pid)
164     assert status.success?
165   end
167   def test_closed_read
168     @rd.close
169     assert_raises(IOError) { @rd.read }
170     assert CALLED.empty? unless ORIGINAL_SOCKET
171   end
173   def test_closed_write
174     @wr.close
175     assert_raises(IOError) { @wr.write 'hello' }
176     assert_raises(IOError) { @wr.write_nonblock 'hello' }
177     assert CALLED.empty? unless ORIGINAL_SOCKET
178   end
180   def test_closed_writer_read
181     @wr.close
182     assert_raises(EOFError) { @rd.readpartial 5 }
183     assert_raises(EOFError) { @rd.read_nonblock 5 }
184     assert_nil @rd.read(5)
185     assert CALLED.empty? unless ORIGINAL_SOCKET
186   end
188   def test_closed_writer_read_with_buffer
189     @wr.close
190     buf = 'hello'
191     assert_raises(EOFError) { @rd.readpartial 5, buf }
192     assert_equal '', buf
194     buf = 'hello'
195     assert_raises(EOFError) { @rd.read_nonblock 5, buf }
196     assert_equal '', buf
198     buf = 'hello'
199     assert_nil @rd.read(5, buf)
200     assert_equal '', buf
201     assert CALLED.empty? unless ORIGINAL_SOCKET
202   end
204   def test_closed_reader_write
205     @rd.close
206     assert_raises(Errno::EPIPE) { @wr.write 'hello' }
207     assert_raises(Errno::EPIPE) { @wr.write_nonblock 'hello' }
208     assert CALLED.empty? unless ORIGINAL_SOCKET
209   end
211   def test_readpartial_lock
212     s = ""
213     t = Thread.new { @rd.readpartial(5, s) }
214     Thread.pass until s.size == 5
215     assert_raise(RuntimeError) { s.clear }
216     @wr.write "foobarbaz"
217     @wr.close
218     assert_equal("fooba", t.value)
219     assert CALLED.empty? unless ORIGINAL_SOCKET
220   end
222   def test_read_lock
223     s = ""
224     t = Thread.new { @rd.read(5, s) }
225     Thread.pass until s.size == 5
226     assert_raise(RuntimeError) { s.clear }
227     @wr.write "foobarbaz"
228     @wr.close
229     assert_equal("fooba", t.value)
230     assert CALLED.empty? unless ORIGINAL_SOCKET
231   end
233   def test_close_write
234     @wr.write "foobarbaz"
235     @wr.close_write
236     assert_equal("foobarbaz", @rd.read)
237     assert CALLED.empty? unless ORIGINAL_SOCKET
238   end
240   def test_read_on_nonblock_socket
241     s = ''
242     @rd.nonblock = true
243     assert @rd.nonblock?
244     t = Thread.new { @rd.read(6, s) }
245     Thread.pass until s.size == 6
246     @wr.write 'abcdef'
247     t.join
248     assert_equal 'abcdef', t.value
249     assert ! @rd.nonblock? unless ORIGINAL_SOCKET
250     assert CALLED.empty? unless ORIGINAL_SOCKET
251   end
253   def test_write_nonblock_raises_eagain
254     buf = "HELLO" * 1000
255     assert_raises(Errno::EAGAIN) { loop { @wr.write_nonblock buf } }
256   end
258   def test_read_nonblock_raises_eagain
259     assert_raises(Errno::EAGAIN) { @rd.read_nonblock 1 }
260     assert CALLED.empty? unless ORIGINAL_SOCKET
261   end
263   def test_gets_fallback_to_super_readpartial_buffers
264     @wr.write "hi"
265     t = Thread.new { @rd.gets }
266     t.join(0.1)
267     assert t.alive?
268     @wr.write "\nbye\n"
269     t.join
270     assert "hi\n", t.value
271     assert_equal "bye\n", @rd.readpartial(60)
272     assert_equal({ :readpartial => [ 60 ]}, CALLED)
273   end
275   def test_gets_fallback_to_super_read_buffers
276     @wr.write "hi"
277     t = Thread.new { @rd.gets }
278     t.join(0.1)
279     assert t.alive?
280     @wr.write "\nbye\n"
281     t.join
282     assert "hi\n", t.value
283     assert_equal "bye\n", @rd.read(4)
284     assert_equal({ :read => [ 4 ]}, CALLED)
285   end
287   def test_gets_fallback_to_super_read_nonblock_buffers
288     @wr.write "hi"
289     t = Thread.new { @rd.gets }
290     t.join(0.1)
291     assert t.alive?
292     @wr.write "\nbye\n"
293     t.join
294     assert "hi\n", t.value
295     assert_equal "bye\n", @rd.read_nonblock(40)
296     assert_equal({ :read_nonblock => [ 40 ]}, CALLED)
297   end
299   def test_set_sync
300     assert @wr.sync
301     assert_nothing_raised { @wr.sync = false }
302     assert @wr.sync
303   end unless ORIGINAL_SOCKET