1 # -*- encoding: binary -*-
7 $stderr.sync = $stdout.sync = true
9 class TestLinux < Test::Unit::TestCase
10 include Raindrops::Linux
12 TEST_ADDR = ENV['UNICORN_TEST_ADDR'] || '127.0.0.1'
15 tmp = Tempfile.new("\xde\xad\xbe\xef") # valid path, really :)
17 us = UNIXServer.new(tmp.path)
18 stats = unix_listener_stats([tmp.path])
19 assert_equal 1, stats.size
20 assert_equal 0, stats[tmp.path].active
21 assert_equal 0, stats[tmp.path].queued
23 uc0 = UNIXSocket.new(tmp.path)
24 stats = unix_listener_stats([tmp.path])
25 assert_equal 1, stats.size
26 assert_equal 0, stats[tmp.path].active
27 assert_equal 1, stats[tmp.path].queued
29 uc1 = UNIXSocket.new(tmp.path)
30 stats = unix_listener_stats([tmp.path])
31 assert_equal 1, stats.size
32 assert_equal 0, stats[tmp.path].active
33 assert_equal 2, stats[tmp.path].queued
36 stats = unix_listener_stats([tmp.path])
37 assert_equal 1, stats.size
38 assert_equal 1, stats[tmp.path].active
39 assert_equal 1, stats[tmp.path].queued
44 s = TCPServer.new(TEST_ADDR, port)
45 addr = "#{TEST_ADDR}:#{port}"
47 stats = tcp_listener_stats(addrs)
48 assert_equal 1, stats.size
49 assert_equal 0, stats[addr].queued
50 assert_equal 0, stats[addr].active
52 c = TCPSocket.new(TEST_ADDR, port)
53 stats = tcp_listener_stats(addrs)
54 assert_equal 1, stats.size
55 assert_equal 1, stats[addr].queued
56 assert_equal 0, stats[addr].active
59 stats = tcp_listener_stats(addrs)
60 assert_equal 1, stats.size
61 assert_equal 0, stats[addr].queued
62 assert_equal 1, stats[addr].active
66 port1, port2 = unused_port, unused_port
67 s1 = TCPServer.new(TEST_ADDR, port1)
68 s2 = TCPServer.new(TEST_ADDR, port2)
69 addr1, addr2 = "#{TEST_ADDR}:#{port1}", "#{TEST_ADDR}:#{port2}"
70 addrs = [ addr1, addr2 ]
71 stats = tcp_listener_stats(addrs)
72 assert_equal 2, stats.size
73 assert_equal 0, stats[addr1].queued
74 assert_equal 0, stats[addr1].active
75 assert_equal 0, stats[addr2].queued
76 assert_equal 0, stats[addr2].active
78 c1 = TCPSocket.new(TEST_ADDR, port1)
79 stats = tcp_listener_stats(addrs)
80 assert_equal 2, stats.size
81 assert_equal 1, stats[addr1].queued
82 assert_equal 0, stats[addr1].active
83 assert_equal 0, stats[addr2].queued
84 assert_equal 0, stats[addr2].active
87 stats = tcp_listener_stats(addrs)
88 assert_equal 2, stats.size
89 assert_equal 0, stats[addr1].queued
90 assert_equal 1, stats[addr1].active
91 assert_equal 0, stats[addr2].queued
92 assert_equal 0, stats[addr2].active
94 c2 = TCPSocket.new(TEST_ADDR, port2)
95 stats = tcp_listener_stats(addrs)
96 assert_equal 2, stats.size
97 assert_equal 0, stats[addr1].queued
98 assert_equal 1, stats[addr1].active
99 assert_equal 1, stats[addr2].queued
100 assert_equal 0, stats[addr2].active
102 c3 = TCPSocket.new(TEST_ADDR, port2)
103 stats = tcp_listener_stats(addrs)
104 assert_equal 2, stats.size
105 assert_equal 0, stats[addr1].queued
106 assert_equal 1, stats[addr1].active
107 assert_equal 2, stats[addr2].queued
108 assert_equal 0, stats[addr2].active
111 stats = tcp_listener_stats(addrs)
112 assert_equal 2, stats.size
113 assert_equal 0, stats[addr1].queued
114 assert_equal 1, stats[addr1].active
115 assert_equal 1, stats[addr2].queued
116 assert_equal 1, stats[addr2].active
119 stats = tcp_listener_stats(addrs)
120 assert_equal 0, stats[addr1].queued
121 assert_equal 0, stats[addr1].active
122 assert_equal 1, stats[addr2].queued
123 assert_equal 1, stats[addr2].active
126 # tries to overflow buffers
127 def test_tcp_stress_test
131 addr = "#{TEST_ADDR}:#{port}"
133 s = TCPServer.new(TEST_ADDR, port)
141 socks = nr_sock.times.map { s.accept }
144 rdb.sysread(1) # wait for parent to nuke us
152 socks = nr_sock.times.map { TCPSocket.new(TEST_ADDR, port) }
155 rdb.sysread(1) # wait for parent to nuke us
159 assert_equal('.' * (nr_proc * 2), rda.read(nr_proc * 2))
162 stats = tcp_listener_stats(addrs)
163 expect = { addr => Raindrops::ListenStats[nr_sock * nr_proc, 0] }
164 assert_equal expect, stats
166 uno_mas = TCPSocket.new(TEST_ADDR, port)
167 stats = tcp_listener_stats(addrs)
168 expect = { addr => Raindrops::ListenStats[nr_sock * nr_proc, 1] }
169 assert_equal expect, stats
171 if ENV["BENCHMARK"].to_i != 0
173 puts(Benchmark.measure{1000.times { tcp_listener_stats(addrs) }})
176 wrb.syswrite('.' * (nr_proc * 2)) # broadcast a wakeup
177 statuses = Process.waitall
178 statuses.each { |(pid,status)| assert status.success?, status.inspect }
179 end if ENV["STRESS"].to_i != 0
183 # Stolen from Unicorn, also a version of this is used by the Rainbows!
185 # unused_port provides an unused port on +addr+ usable for TCP that is
186 # guaranteed to be unused across all compatible tests on that system. It
187 # prevents race conditions by using a lock file other tests
188 # will see. This is required if you perform several builds in parallel
189 # with a continuous integration system or run tests in parallel via
190 # gmake. This is NOT guaranteed to be race-free if you run other
191 # systems that bind to random ports for testing (but the window
192 # for a race condition is very small). You may also set UNICORN_TEST_ADDR
193 # to override the default test address (127.0.0.1).
194 def unused_port(addr = TEST_ADDR)
200 port = base + rand(32768 - base)
202 port = base + rand(32768 - base)
205 sock = Socket.new(Socket::AF_INET, Socket::SOCK_STREAM, 0)
206 sock.bind(Socket.pack_sockaddr_in(port, addr))
208 rescue Errno::EADDRINUSE, Errno::EACCES
209 sock.close rescue nil
210 retry if (retries -= 1) >= 0
213 # since we'll end up closing the random port we just got, there's a race
214 # condition could allow the random port we just chose to reselect itself
215 # when running tests in parallel with gmake. Create a lock file while
216 # we have the port here to ensure that does not happen .
217 lock_path = "#{Dir::tmpdir}/unicorn_test.#{addr}:#{port}.lock"
218 lock = File.open(lock_path, File::WRONLY|File::CREAT|File::EXCL, 0600)
219 at_exit { File.unlink(lock_path) rescue nil }
221 sock.close rescue nil
224 sock.close rescue nil
228 end if RUBY_PLATFORM =~ /linux/