clobber errno before syscalls for BasicSocket#read
[socket_dontwait.git] / test / test_socket_dontwait.rb
blob82d8f79bc3d296038bee1445f6858d24dc81f9d1
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   BS = ENV['bs'] ? ENV['bs'].to_i : 16384
24   COUNT = ENV['count'] ? ENV['count'].to_i : 100000
26   def setup
27     @rd, @wr = UNIXSocket.pair
28   end
30   def teardown
31     CALLED.clear
32     @rd.close unless @rd.closed?
33     @wr.close unless @wr.closed?
34   end
36   def test_write_read_nonblock_str
37     assert_equal 3, @wr.write_nonblock('abc')
38     assert_equal 'abc', @rd.read_nonblock(3)
39     assert CALLED.empty? unless ORIGINAL_SOCKET
40   end
42   def test_write_read_nonblock_integer
43     assert_equal 3, @wr.write_nonblock(123)
44     assert_equal '123', @rd.read_nonblock(3)
45     assert CALLED.empty? unless ORIGINAL_SOCKET
46   end
48   def test_invalid_read_length
49     assert_raise(ArgumentError) { @rd.read(-1) }
50     assert_raise(ArgumentError) { @rd.read(-1, "") }
51     assert_raise(ArgumentError) { @rd.read_nonblock(-1) }
52     assert_raise(ArgumentError) { @rd.read_nonblock(-1, "") }
53     assert_raise(ArgumentError) { @rd.readpartial(-1) }
54     assert_raise(ArgumentError) { @rd.readpartial(-1, "") }
55     assert CALLED.empty? unless ORIGINAL_SOCKET
56   end
58   def test_read
59     buf = '.' * 4093
60     t = Thread.new { @rd.read 4096 }
61     assert_equal 4093, @wr.write(buf)
62     t.join(0)
63     assert t.alive?
64     assert_equal 4, @wr.write('....')
65     t.join
66     assert_equal('.' * 4096, t.value)
67     assert_equal('.', @rd.read(1))
68     @wr.close
69     assert_nil @rd.read(1)
70     assert CALLED.empty? unless ORIGINAL_SOCKET
71   end
73   def test_read_empty
74     @wr.close
75     assert_equal '', @rd.read
76     str = ''
77     str_object_id = str.object_id
78     assert_equal str_object_id, @rd.read(nil, str).object_id
79     assert CALLED.empty? unless ORIGINAL_SOCKET
80   end
82   def test_big_write_read_full_even
83     tmp = IO.read('/dev/urandom', 16427) * 32
84     tmp << '.' until ((tmp.bytesize % 16384) == 0)
85     return
86     pid = fork do
87       nr = @wr.write(tmp)
88       exit!(nr == tmp.bytesize)
89     end
90     @wr.close
91     assert_equal tmp, @rd.read
92     _, status = Process.waitpid2(pid)
93     assert status.success?
94     assert CALLED.empty? unless ORIGINAL_SOCKET
95   end
97   def test_big_write_read_full_odd
98     n = @wr.getsockopt(Socket::SOL_SOCKET, Socket::SO_SNDBUF).unpack('i')[0]
99     tmp = IO.read('/dev/urandom', 16427) * 32
100     tmp << 'aasdfzf' until ((tmp.bytesize % 16384) != 0)
101     pid = fork do
102       nr = @wr.write(tmp)
103       exit!(nr == tmp.bytesize)
104     end
105     @wr.close
106     assert_equal tmp, @rd.read
107     _, status = Process.waitpid2(pid)
108     assert status.success?
109     assert CALLED.empty? unless ORIGINAL_SOCKET
110   end
112   def test_zero_reads
113     assert_equal '', @rd.read(0)
114     assert_equal '', @rd.readpartial(0)
115     assert_equal '', @rd.read_nonblock(0)
117     buf = 'hello'
118     assert_equal buf.object_id, @rd.read(0, buf).object_id
119     assert_equal '', buf
120     buf = 'hello'
121     assert_equal buf.object_id, @rd.readpartial(0, buf).object_id
122     assert_equal '', buf
123     buf = 'hello'
124     assert_equal buf.object_id, @rd.read_nonblock(0, buf).object_id
125     assert_equal '', buf
126   end
128   def test_readpartial_signal
129     r, w = IO.pipe
130     pid = fork do
131       r.close
132       trap(:USR1) { w.syswrite "USR1" }
133       w.syswrite "READY"
134       rv = @rd.readpartial(16384)
135       exit!(rv == "SIGNAL SAFE" && (ORIGINAL_SOCKET || CALLED.empty?))
136     end
137     w.close
138     assert_equal "READY", r.read(5)
139     assert_nothing_raised do
140       sleep 0.5 # ugh, still potentially racy :<
141       Process.kill(:USR1, pid)
142     end
143     assert_equal 'USR1', r.readpartial(4)
144     assert_equal 11, @wr.write("SIGNAL SAFE")
145     _, status = Process.waitpid2(pid)
146     assert status.success?
147   end
149   def test_read_signal
150     r, w = IO.pipe
151     pid = fork do
152       r.close
153       trap(:USR1) { w.syswrite "USR1" }
154       w.syswrite "READY"
155       rv = @rd.read(11)
156       exit!(rv == "SIGNAL SAFE" && (ORIGINAL_SOCKET || CALLED.empty?))
157     end
158     w.close
159     assert_equal "READY", r.read(5)
160     assert_nothing_raised do
161       sleep 0.5 # ugh, still potentially racy :<
162       Process.kill(:USR1, pid)
163     end
164     assert_equal 'USR1', r.readpartial(4)
165     assert_equal 11, @wr.write("SIGNAL SAFE")
166     _, status = Process.waitpid2(pid)
167     assert status.success?
168   end
170   def test_closed_read
171     @rd.close
172     assert_raises(IOError) { @rd.read }
173     assert CALLED.empty? unless ORIGINAL_SOCKET
174   end
176   def test_closed_write
177     @wr.close
178     assert_raises(IOError) { @wr.write 'hello' }
179     assert_raises(IOError) { @wr.write_nonblock 'hello' }
180     assert CALLED.empty? unless ORIGINAL_SOCKET
181   end
183   def test_closed_writer_read
184     @wr.close
185     assert_raises(EOFError) { @rd.readpartial 5 }
186     assert_raises(EOFError) { @rd.read_nonblock 5 }
187     assert_nil @rd.read(5)
188     assert CALLED.empty? unless ORIGINAL_SOCKET
189   end
191   def test_closed_writer_read_with_buffer
192     @wr.close
193     buf = 'hello'
194     assert_raises(EOFError) { @rd.readpartial 5, buf }
195     assert_equal '', buf
197     buf = 'hello'
198     assert_raises(EOFError) { @rd.read_nonblock 5, buf }
199     assert_equal '', buf
201     buf = 'hello'
202     assert_nil @rd.read(5, buf)
203     assert_equal '', buf
204     assert CALLED.empty? unless ORIGINAL_SOCKET
205   end
207   def test_closed_reader_write
208     @rd.close
209     assert_raises(Errno::EPIPE) { @wr.write 'hello' }
210     assert_raises(Errno::EPIPE) { @wr.write_nonblock 'hello' }
211     assert CALLED.empty? unless ORIGINAL_SOCKET
212   end
214   def test_readpartial_lock
215     s = ""
216     t = Thread.new { @rd.readpartial(5, s) }
217     Thread.pass until s.size == 5
218     assert_raise(RuntimeError) { s.clear }
219     @wr.write "foobarbaz"
220     @wr.close
221     assert_equal("fooba", t.value)
222     assert CALLED.empty? unless ORIGINAL_SOCKET
223   end
225   def test_read_lock
226     s = ""
227     t = Thread.new { @rd.read(5, s) }
228     Thread.pass until s.size == 5
229     assert_raise(RuntimeError) { s.clear }
230     @wr.write "foobarbaz"
231     @wr.close
232     assert_equal("fooba", t.value)
233     assert CALLED.empty? unless ORIGINAL_SOCKET
234   end
236   def test_close_write
237     @wr.write "foobarbaz"
238     @wr.close_write
239     assert_equal("foobarbaz", @rd.read)
240     assert CALLED.empty? unless ORIGINAL_SOCKET
241   end
243   def test_read_on_nonblock_socket
244     s = ''
245     @rd.nonblock = true
246     assert @rd.nonblock?
247     t = Thread.new { @rd.read(6, s) }
248     Thread.pass until s.size == 6
249     @wr.write 'abcdef'
250     t.join
251     assert_equal 'abcdef', t.value
252     assert ! @rd.nonblock? unless ORIGINAL_SOCKET
253     assert CALLED.empty? unless ORIGINAL_SOCKET
254   end
256   def test_write_nonblock_raises_eagain
257     buf = "HELLO" * 1000
258     assert_raises(Errno::EAGAIN) { loop { @wr.write_nonblock buf } }
259   end
261   def test_read_nonblock_raises_eagain
262     assert_raises(Errno::EAGAIN) { @rd.read_nonblock 1 }
263     assert CALLED.empty? unless ORIGINAL_SOCKET
264   end
266   def test_set_sync
267     assert @wr.sync
268     assert_nothing_raised { @wr.sync = false }
269     assert @wr.sync
270   end unless ORIGINAL_SOCKET
272   def read_loop(io)
273     rbuf = ""
274     bs = BS
275     n = bs * COUNT
276     while io.read(bs, rbuf)
277       n -= rbuf.size
278     end
279     0 == n
280   end
282   def write_loop(io)
283     buf = " " * BS
284     n = 0
285     COUNT.times do
286       n += io.write buf
287     end
288     io.close
289     n == COUNT * BS
290   end
292   def test_read_write_loop_fork
293     pid = fork do
294       @wr.close
295       exit read_loop(@rd)
296     end
297     @rd.close
298     rv = write_loop(@wr)
299     assert rv
300     _, status = Process.waitpid2(pid)
301     assert status.success?
302   end