raindrops 0.4.1 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.document +2 -1
- data/.gitignore +4 -0
- data/.wrongdoc.yml +4 -0
- data/GIT-VERSION-GEN +1 -1
- data/GNUmakefile +2 -196
- data/Gemfile +7 -0
- data/LICENSE +1 -1
- data/README +17 -47
- data/Rakefile +0 -104
- data/examples/linux-listener-stats.rb +123 -0
- data/examples/{config.ru → middleware.ru} +1 -1
- data/examples/watcher.ru +4 -0
- data/examples/watcher_demo.ru +13 -0
- data/examples/zbatery.conf.rb +13 -0
- data/ext/raindrops/extconf.rb +5 -0
- data/ext/raindrops/linux_inet_diag.c +449 -151
- data/ext/raindrops/linux_tcp_info.c +170 -0
- data/ext/raindrops/my_fileno.h +36 -0
- data/ext/raindrops/raindrops.c +232 -20
- data/lib/raindrops.rb +20 -7
- data/lib/raindrops/aggregate.rb +8 -0
- data/lib/raindrops/aggregate/last_data_recv.rb +86 -0
- data/lib/raindrops/aggregate/pmq.rb +239 -0
- data/lib/raindrops/last_data_recv.rb +100 -0
- data/lib/raindrops/linux.rb +26 -16
- data/lib/raindrops/middleware.rb +112 -41
- data/lib/raindrops/middleware/proxy.rb +34 -0
- data/lib/raindrops/struct.rb +15 -0
- data/lib/raindrops/watcher.rb +362 -0
- data/pkg.mk +171 -0
- data/raindrops.gemspec +10 -20
- data/test/ipv6_enabled.rb +10 -0
- data/test/rack_unicorn.rb +12 -0
- data/test/test_aggregate_pmq.rb +65 -0
- data/test/test_inet_diag_socket.rb +13 -0
- data/test/test_last_data_recv_unicorn.rb +69 -0
- data/test/test_linux.rb +55 -57
- data/test/test_linux_all_tcp_listen_stats.rb +66 -0
- data/test/test_linux_all_tcp_listen_stats_leak.rb +43 -0
- data/test/test_linux_ipv6.rb +158 -0
- data/test/test_linux_tcp_info.rb +61 -0
- data/test/test_middleware.rb +15 -2
- data/test/test_middleware_unicorn.rb +37 -0
- data/test/test_middleware_unicorn_ipv6.rb +37 -0
- data/test/test_raindrops.rb +65 -1
- data/test/test_raindrops_gc.rb +23 -1
- data/test/test_watcher.rb +85 -0
- metadata +69 -22
- data/examples/linux-tcp-listener-stats.rb +0 -44
@@ -0,0 +1,66 @@
|
|
1
|
+
# -*- encoding: binary -*-
|
2
|
+
require 'test/unit'
|
3
|
+
require 'socket'
|
4
|
+
require 'raindrops'
|
5
|
+
require 'pp'
|
6
|
+
$stderr.sync = $stdout.sync = true
|
7
|
+
|
8
|
+
class TestLinuxAllTcpListenStats < Test::Unit::TestCase
|
9
|
+
include Raindrops::Linux
|
10
|
+
TEST_ADDR = ENV['UNICORN_TEST_ADDR'] || '127.0.0.1'
|
11
|
+
|
12
|
+
def test_print_all
|
13
|
+
puts "EVERYTHING"
|
14
|
+
pp Raindrops::Linux.tcp_listener_stats
|
15
|
+
puts("-" * 72)
|
16
|
+
end if $stdout.tty?
|
17
|
+
|
18
|
+
def setup
|
19
|
+
@socks = []
|
20
|
+
end
|
21
|
+
|
22
|
+
def teardown
|
23
|
+
@socks.each { |io| io.closed? or io.close }
|
24
|
+
end
|
25
|
+
|
26
|
+
def new_server
|
27
|
+
s = TCPServer.new TEST_ADDR, 0
|
28
|
+
@socks << s
|
29
|
+
[ s, s.addr[1] ]
|
30
|
+
end
|
31
|
+
|
32
|
+
def new_client(port)
|
33
|
+
s = TCPSocket.new("127.0.0.1", port)
|
34
|
+
@socks << s
|
35
|
+
s
|
36
|
+
end
|
37
|
+
|
38
|
+
def new_accept(srv)
|
39
|
+
c = srv.accept
|
40
|
+
@socks << c
|
41
|
+
c
|
42
|
+
end
|
43
|
+
|
44
|
+
def test_all_ports
|
45
|
+
srv, port = new_server
|
46
|
+
addr = "#{TEST_ADDR}:#{port}"
|
47
|
+
all = Raindrops::Linux.tcp_listener_stats
|
48
|
+
assert_equal [0,0], all[addr].to_a
|
49
|
+
|
50
|
+
new_client(port)
|
51
|
+
all = Raindrops::Linux.tcp_listener_stats
|
52
|
+
assert_equal [0,1], all[addr].to_a
|
53
|
+
|
54
|
+
new_client(port)
|
55
|
+
all = Raindrops::Linux.tcp_listener_stats
|
56
|
+
assert_equal [0,2], all[addr].to_a
|
57
|
+
|
58
|
+
new_accept(srv)
|
59
|
+
all = Raindrops::Linux.tcp_listener_stats
|
60
|
+
assert_equal [1,1], all[addr].to_a
|
61
|
+
|
62
|
+
new_accept(srv)
|
63
|
+
all = Raindrops::Linux.tcp_listener_stats
|
64
|
+
assert_equal [2,0], all[addr].to_a
|
65
|
+
end
|
66
|
+
end if RUBY_PLATFORM =~ /linux/
|
@@ -0,0 +1,43 @@
|
|
1
|
+
# -*- encoding: binary -*-
|
2
|
+
require 'test/unit'
|
3
|
+
require 'raindrops'
|
4
|
+
require 'socket'
|
5
|
+
require 'benchmark'
|
6
|
+
$stderr.sync = $stdout.sync = true
|
7
|
+
|
8
|
+
class TestLinuxAllTcpListenStatsLeak < Test::Unit::TestCase
|
9
|
+
|
10
|
+
TEST_ADDR = ENV['UNICORN_TEST_ADDR'] || '127.0.0.1'
|
11
|
+
|
12
|
+
|
13
|
+
def rss_kb
|
14
|
+
File.readlines("/proc/#$$/status").grep(/VmRSS:/)[0].split(/\s+/)[1].to_i
|
15
|
+
end
|
16
|
+
def test_leak
|
17
|
+
s = TCPServer.new(TEST_ADDR, 0)
|
18
|
+
start_kb = rss_kb
|
19
|
+
p [ :start_kb, start_kb ]
|
20
|
+
assert_nothing_raised do
|
21
|
+
p(Benchmark.measure {
|
22
|
+
1000.times { Raindrops::Linux.all_tcp_listener_stats }
|
23
|
+
})
|
24
|
+
end
|
25
|
+
cur_kb = rss_kb
|
26
|
+
p [ :cur_kb, cur_kb ]
|
27
|
+
now = Time.now.to_i
|
28
|
+
fin = now + 60
|
29
|
+
assert_nothing_raised do
|
30
|
+
1000000000.times { |i|
|
31
|
+
if (i % 1024) == 0
|
32
|
+
now = Time.now.to_i
|
33
|
+
break if now > fin
|
34
|
+
end
|
35
|
+
Raindrops::Linux.all_tcp_listener_stats
|
36
|
+
}
|
37
|
+
end
|
38
|
+
cur_kb = rss_kb
|
39
|
+
p [ :cur_kb, cur_kb ]
|
40
|
+
ensure
|
41
|
+
s.close
|
42
|
+
end
|
43
|
+
end if ENV["STRESS"].to_i != 0
|
@@ -0,0 +1,158 @@
|
|
1
|
+
# -*- encoding: binary -*-
|
2
|
+
require 'test/unit'
|
3
|
+
require 'tempfile'
|
4
|
+
require 'raindrops'
|
5
|
+
require 'socket'
|
6
|
+
require 'pp'
|
7
|
+
require "./test/ipv6_enabled"
|
8
|
+
$stderr.sync = $stdout.sync = true
|
9
|
+
|
10
|
+
class TestLinuxIPv6 < Test::Unit::TestCase
|
11
|
+
include Raindrops::Linux
|
12
|
+
|
13
|
+
TEST_ADDR = ENV["TEST_HOST6"] || "::1"
|
14
|
+
|
15
|
+
def test_tcp
|
16
|
+
s = TCPServer.new(TEST_ADDR, 0)
|
17
|
+
port = s.addr[1]
|
18
|
+
addr = "[#{TEST_ADDR}]:#{port}"
|
19
|
+
addrs = [ addr ]
|
20
|
+
stats = tcp_listener_stats(addrs)
|
21
|
+
assert_equal 1, stats.size
|
22
|
+
assert_equal 0, stats[addr].queued
|
23
|
+
assert_equal 0, stats[addr].active
|
24
|
+
|
25
|
+
c = TCPSocket.new(TEST_ADDR, port)
|
26
|
+
stats = tcp_listener_stats(addrs)
|
27
|
+
assert_equal 1, stats.size
|
28
|
+
assert_equal 1, stats[addr].queued
|
29
|
+
assert_equal 0, stats[addr].active
|
30
|
+
|
31
|
+
sc = s.accept
|
32
|
+
stats = tcp_listener_stats(addrs)
|
33
|
+
assert_equal 1, stats.size
|
34
|
+
assert_equal 0, stats[addr].queued
|
35
|
+
assert_equal 1, stats[addr].active
|
36
|
+
end
|
37
|
+
|
38
|
+
def test_tcp_multi
|
39
|
+
s1 = TCPServer.new(TEST_ADDR, 0)
|
40
|
+
s2 = TCPServer.new(TEST_ADDR, 0)
|
41
|
+
port1, port2 = s1.addr[1], s2.addr[1]
|
42
|
+
addr1, addr2 = "[#{TEST_ADDR}]:#{port1}", "[#{TEST_ADDR}]:#{port2}"
|
43
|
+
addrs = [ addr1, addr2 ]
|
44
|
+
stats = tcp_listener_stats(addrs)
|
45
|
+
assert_equal 2, stats.size
|
46
|
+
assert_equal 0, stats[addr1].queued
|
47
|
+
assert_equal 0, stats[addr1].active
|
48
|
+
assert_equal 0, stats[addr2].queued
|
49
|
+
assert_equal 0, stats[addr2].active
|
50
|
+
|
51
|
+
c1 = TCPSocket.new(TEST_ADDR, port1)
|
52
|
+
stats = tcp_listener_stats(addrs)
|
53
|
+
assert_equal 2, stats.size
|
54
|
+
assert_equal 1, stats[addr1].queued
|
55
|
+
assert_equal 0, stats[addr1].active
|
56
|
+
assert_equal 0, stats[addr2].queued
|
57
|
+
assert_equal 0, stats[addr2].active
|
58
|
+
|
59
|
+
sc1 = s1.accept
|
60
|
+
stats = tcp_listener_stats(addrs)
|
61
|
+
assert_equal 2, stats.size
|
62
|
+
assert_equal 0, stats[addr1].queued
|
63
|
+
assert_equal 1, stats[addr1].active
|
64
|
+
assert_equal 0, stats[addr2].queued
|
65
|
+
assert_equal 0, stats[addr2].active
|
66
|
+
|
67
|
+
c2 = TCPSocket.new(TEST_ADDR, port2)
|
68
|
+
stats = tcp_listener_stats(addrs)
|
69
|
+
assert_equal 2, stats.size
|
70
|
+
assert_equal 0, stats[addr1].queued
|
71
|
+
assert_equal 1, stats[addr1].active
|
72
|
+
assert_equal 1, stats[addr2].queued
|
73
|
+
assert_equal 0, stats[addr2].active
|
74
|
+
|
75
|
+
c3 = TCPSocket.new(TEST_ADDR, port2)
|
76
|
+
stats = tcp_listener_stats(addrs)
|
77
|
+
assert_equal 2, stats.size
|
78
|
+
assert_equal 0, stats[addr1].queued
|
79
|
+
assert_equal 1, stats[addr1].active
|
80
|
+
assert_equal 2, stats[addr2].queued
|
81
|
+
assert_equal 0, stats[addr2].active
|
82
|
+
|
83
|
+
sc2 = s2.accept
|
84
|
+
stats = tcp_listener_stats(addrs)
|
85
|
+
assert_equal 2, stats.size
|
86
|
+
assert_equal 0, stats[addr1].queued
|
87
|
+
assert_equal 1, stats[addr1].active
|
88
|
+
assert_equal 1, stats[addr2].queued
|
89
|
+
assert_equal 1, stats[addr2].active
|
90
|
+
|
91
|
+
sc1.close
|
92
|
+
stats = tcp_listener_stats(addrs)
|
93
|
+
assert_equal 0, stats[addr1].queued
|
94
|
+
assert_equal 0, stats[addr1].active
|
95
|
+
assert_equal 1, stats[addr2].queued
|
96
|
+
assert_equal 1, stats[addr2].active
|
97
|
+
end
|
98
|
+
|
99
|
+
def test_invalid_addresses
|
100
|
+
assert_raises(ArgumentError) { tcp_listener_stats(%w([1:::5)) }
|
101
|
+
assert_raises(ArgumentError) { tcp_listener_stats(%w([1:::]5)) }
|
102
|
+
end
|
103
|
+
|
104
|
+
# tries to overflow buffers
|
105
|
+
def test_tcp_stress_test
|
106
|
+
nr_proc = 32
|
107
|
+
nr_sock = 500
|
108
|
+
s = TCPServer.new(TEST_ADDR, 0)
|
109
|
+
port = s.addr[1]
|
110
|
+
addr = "[#{TEST_ADDR}]:#{port}"
|
111
|
+
addrs = [ addr ]
|
112
|
+
rda, wra = IO.pipe
|
113
|
+
rdb, wrb = IO.pipe
|
114
|
+
|
115
|
+
nr_proc.times do
|
116
|
+
fork do
|
117
|
+
rda.close
|
118
|
+
wrb.close
|
119
|
+
socks = (1..nr_sock).map { s.accept }
|
120
|
+
wra.syswrite('.')
|
121
|
+
wra.close
|
122
|
+
rdb.sysread(1) # wait for parent to nuke us
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
nr_proc.times do
|
127
|
+
fork do
|
128
|
+
rda.close
|
129
|
+
wrb.close
|
130
|
+
socks = (1..nr_sock).map { TCPSocket.new(TEST_ADDR, port) }
|
131
|
+
wra.syswrite('.')
|
132
|
+
wra.close
|
133
|
+
rdb.sysread(1) # wait for parent to nuke us
|
134
|
+
end
|
135
|
+
end
|
136
|
+
|
137
|
+
assert_equal('.' * (nr_proc * 2), rda.read(nr_proc * 2))
|
138
|
+
|
139
|
+
rda.close
|
140
|
+
stats = tcp_listener_stats(addrs)
|
141
|
+
expect = { addr => Raindrops::ListenStats[nr_sock * nr_proc, 0] }
|
142
|
+
assert_equal expect, stats
|
143
|
+
|
144
|
+
uno_mas = TCPSocket.new(TEST_ADDR, port)
|
145
|
+
stats = tcp_listener_stats(addrs)
|
146
|
+
expect = { addr => Raindrops::ListenStats[nr_sock * nr_proc, 1] }
|
147
|
+
assert_equal expect, stats
|
148
|
+
|
149
|
+
if ENV["BENCHMARK"].to_i != 0
|
150
|
+
require 'benchmark'
|
151
|
+
puts(Benchmark.measure{1000.times { tcp_listener_stats(addrs) }})
|
152
|
+
end
|
153
|
+
|
154
|
+
wrb.syswrite('.' * (nr_proc * 2)) # broadcast a wakeup
|
155
|
+
statuses = Process.waitall
|
156
|
+
statuses.each { |(pid,status)| assert status.success?, status.inspect }
|
157
|
+
end if ENV["STRESS"].to_i != 0
|
158
|
+
end if RUBY_PLATFORM =~ /linux/ && ipv6_enabled?
|
@@ -0,0 +1,61 @@
|
|
1
|
+
# -*- encoding: binary -*-
|
2
|
+
require 'test/unit'
|
3
|
+
require 'tempfile'
|
4
|
+
require 'raindrops'
|
5
|
+
require 'socket'
|
6
|
+
require 'pp'
|
7
|
+
$stderr.sync = $stdout.sync = true
|
8
|
+
class TestLinuxTCP_Info < Test::Unit::TestCase
|
9
|
+
|
10
|
+
TEST_ADDR = ENV['UNICORN_TEST_ADDR'] || '127.0.0.1'
|
11
|
+
|
12
|
+
# Linux kernel commit 5ee3afba88f5a79d0bff07ddd87af45919259f91
|
13
|
+
TCP_INFO_useful_listenq = `uname -r`.strip >= '2.6.24'
|
14
|
+
|
15
|
+
def test_tcp_server
|
16
|
+
s = TCPServer.new(TEST_ADDR, 0)
|
17
|
+
rv = Raindrops::TCP_Info.new s
|
18
|
+
c = TCPSocket.new TEST_ADDR, s.addr[1]
|
19
|
+
tmp = Raindrops::TCP_Info.new s
|
20
|
+
TCP_INFO_useful_listenq and assert_equal 1, tmp.unacked
|
21
|
+
|
22
|
+
assert_equal 0, rv.unacked
|
23
|
+
a = s.accept
|
24
|
+
tmp = Raindrops::TCP_Info.new s
|
25
|
+
assert_equal 0, tmp.unacked
|
26
|
+
ensure
|
27
|
+
c.close if c
|
28
|
+
a.close if a
|
29
|
+
s.close
|
30
|
+
end
|
31
|
+
|
32
|
+
def test_accessors
|
33
|
+
s = TCPServer.new TEST_ADDR, 0
|
34
|
+
tmp = Raindrops::TCP_Info.new s
|
35
|
+
tcp_info_methods = tmp.methods - Object.new.methods
|
36
|
+
assert tcp_info_methods.size >= 32
|
37
|
+
tcp_info_methods.each do |m|
|
38
|
+
val = tmp.__send__ m
|
39
|
+
assert_kind_of Integer, val
|
40
|
+
assert val >= 0
|
41
|
+
end
|
42
|
+
ensure
|
43
|
+
s.close
|
44
|
+
end
|
45
|
+
|
46
|
+
def test_tcp_server_delayed
|
47
|
+
delay = 0.010
|
48
|
+
delay_ms = (delay * 1000).to_i
|
49
|
+
s = TCPServer.new(TEST_ADDR, 0)
|
50
|
+
c = TCPSocket.new TEST_ADDR, s.addr[1]
|
51
|
+
c.syswrite "."
|
52
|
+
sleep delay
|
53
|
+
a = s.accept
|
54
|
+
i = Raindrops::TCP_Info.new(a)
|
55
|
+
assert i.last_data_recv >= delay_ms, "#{i.last_data_recv} < #{delay_ms}"
|
56
|
+
ensure
|
57
|
+
c.close if c
|
58
|
+
a.close if a
|
59
|
+
s.close
|
60
|
+
end
|
61
|
+
end
|
data/test/test_middleware.rb
CHANGED
@@ -14,7 +14,7 @@ class TestMiddleware < Test::Unit::TestCase
|
|
14
14
|
app = Raindrops::Middleware.new(@app)
|
15
15
|
response = app.call({})
|
16
16
|
assert_equal @response[0,2], response[0,2]
|
17
|
-
assert response.last.kind_of?(Raindrops::Middleware)
|
17
|
+
assert response.last.kind_of?(Raindrops::Middleware::Proxy)
|
18
18
|
assert response.last.object_id != app.object_id
|
19
19
|
tmp = []
|
20
20
|
response.last.each { |y| tmp << y }
|
@@ -35,7 +35,7 @@ class TestMiddleware < Test::Unit::TestCase
|
|
35
35
|
assert_equal 0, stats.calling
|
36
36
|
assert_equal 1, stats.writing
|
37
37
|
assert_equal 200, response[0]
|
38
|
-
assert response.last.kind_of?(Raindrops::Middleware)
|
38
|
+
assert response.last.kind_of?(Raindrops::Middleware::Proxy)
|
39
39
|
tmp = []
|
40
40
|
response.last.each do |y|
|
41
41
|
assert_equal 1, stats.writing
|
@@ -108,4 +108,17 @@ class TestMiddleware < Test::Unit::TestCase
|
|
108
108
|
assert_equal expect, response
|
109
109
|
end
|
110
110
|
|
111
|
+
def test_middleware_proxy_to_path_missing
|
112
|
+
app = Raindrops::Middleware.new(@app)
|
113
|
+
response = app.call({})
|
114
|
+
body = response[2]
|
115
|
+
assert_kind_of Raindrops::Middleware::Proxy, body
|
116
|
+
assert ! body.respond_to?(:to_path)
|
117
|
+
assert body.respond_to?(:close)
|
118
|
+
orig_body = @response[2]
|
119
|
+
|
120
|
+
def orig_body.to_path; "/dev/null"; end
|
121
|
+
assert body.respond_to?(:to_path)
|
122
|
+
assert_equal "/dev/null", body.to_path
|
123
|
+
end
|
111
124
|
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
# -*- encoding: binary -*-
|
2
|
+
require "./test/rack_unicorn"
|
3
|
+
$stderr.sync = $stdout.sync = true
|
4
|
+
|
5
|
+
class TestMiddlewareUnicorn < Test::Unit::TestCase
|
6
|
+
|
7
|
+
def setup
|
8
|
+
@host = ENV["UNICORN_TEST_ADDR"] || "127.0.0.1"
|
9
|
+
@sock = TCPServer.new @host, 0
|
10
|
+
@port = @sock.addr[1]
|
11
|
+
ENV["UNICORN_FD"] = @sock.fileno.to_s
|
12
|
+
@host_with_port = "#@host:#@port"
|
13
|
+
@opts = { :listeners => [ @host_with_port ] }
|
14
|
+
@addr_regexp = Regexp.escape @host_with_port
|
15
|
+
end
|
16
|
+
|
17
|
+
def test_auto_listener
|
18
|
+
@app = Rack::Builder.new do
|
19
|
+
use Raindrops::Middleware
|
20
|
+
run Rack::Lobster.new
|
21
|
+
end
|
22
|
+
@srv = fork { Unicorn.run(@app, @opts) }
|
23
|
+
|
24
|
+
s = TCPSocket.new @host, @port
|
25
|
+
s.write "GET /_raindrops HTTP/1.0\r\n\r\n"
|
26
|
+
resp = s.read
|
27
|
+
head, body = resp.split /\r\n\r\n/, 2
|
28
|
+
assert_match %r{^#@addr_regexp active: 1$}, body
|
29
|
+
assert_match %r{^#@addr_regexp queued: 0$}, body
|
30
|
+
end
|
31
|
+
|
32
|
+
def teardown
|
33
|
+
Process.kill :QUIT, @srv
|
34
|
+
_, status = Process.waitpid2 @srv
|
35
|
+
assert status.success?
|
36
|
+
end
|
37
|
+
end if defined?(Unicorn) && RUBY_PLATFORM =~ /linux/
|
@@ -0,0 +1,37 @@
|
|
1
|
+
# -*- encoding: binary -*-
|
2
|
+
require "./test/rack_unicorn"
|
3
|
+
require "./test/ipv6_enabled"
|
4
|
+
$stderr.sync = $stdout.sync = true
|
5
|
+
|
6
|
+
class TestMiddlewareUnicornIPv6 < Test::Unit::TestCase
|
7
|
+
|
8
|
+
def setup
|
9
|
+
@host = ENV["TEST_HOST6"] || "::1"
|
10
|
+
sock = TCPServer.new @host, 0
|
11
|
+
@port = sock.addr[1]
|
12
|
+
ENV["UNICORN_FD"] = sock.fileno.to_s
|
13
|
+
@host_with_port = "[#@host]:#@port"
|
14
|
+
@opts = { :listeners => [ @host_with_port ] }
|
15
|
+
@addr_regexp = Regexp.escape @host_with_port
|
16
|
+
end
|
17
|
+
|
18
|
+
def test_auto_listener
|
19
|
+
@app = Rack::Builder.new do
|
20
|
+
use Raindrops::Middleware
|
21
|
+
run Rack::Lobster.new
|
22
|
+
end
|
23
|
+
@srv = fork { Unicorn.run(@app, @opts) }
|
24
|
+
s = TCPSocket.new @host, @port
|
25
|
+
s.write "GET /_raindrops HTTP/1.0\r\n\r\n"
|
26
|
+
resp = s.read
|
27
|
+
head, body = resp.split /\r\n\r\n/, 2
|
28
|
+
assert_match %r{^#@addr_regexp active: 1$}, body
|
29
|
+
assert_match %r{^#@addr_regexp queued: 0$}, body
|
30
|
+
end
|
31
|
+
|
32
|
+
def teardown
|
33
|
+
Process.kill :QUIT, @srv
|
34
|
+
_, status = Process.waitpid2 @srv
|
35
|
+
assert status.success?
|
36
|
+
end
|
37
|
+
end if defined?(Unicorn) && RUBY_PLATFORM =~ /linux/ && ipv6_enabled?
|
data/test/test_raindrops.rb
CHANGED
@@ -4,15 +4,27 @@ require 'raindrops'
|
|
4
4
|
|
5
5
|
class TestRaindrops < Test::Unit::TestCase
|
6
6
|
|
7
|
+
def test_raindrop_counter_max
|
8
|
+
assert_kind_of Integer, Raindrops::MAX
|
9
|
+
assert Raindrops::MAX > 0
|
10
|
+
printf "Raindrops::MAX = 0x%x\n", Raindrops::MAX
|
11
|
+
end
|
12
|
+
|
7
13
|
def test_raindrop_size
|
8
14
|
assert_kind_of Integer, Raindrops::SIZE
|
9
15
|
assert Raindrops::SIZE > 0
|
10
16
|
puts "Raindrops::SIZE = #{Raindrops::SIZE}"
|
11
17
|
end
|
12
18
|
|
13
|
-
def
|
19
|
+
def test_page_size
|
20
|
+
assert_kind_of Integer, Raindrops::PAGE_SIZE
|
21
|
+
assert Raindrops::PAGE_SIZE > Raindrops::SIZE
|
22
|
+
end
|
23
|
+
|
24
|
+
def test_size_and_capa
|
14
25
|
rd = Raindrops.new(4)
|
15
26
|
assert_equal 4, rd.size
|
27
|
+
assert rd.capa >= rd.size
|
16
28
|
end
|
17
29
|
|
18
30
|
def test_ary
|
@@ -98,4 +110,56 @@ class TestRaindrops < Test::Unit::TestCase
|
|
98
110
|
assert_equal expect, rd.to_ary
|
99
111
|
end
|
100
112
|
|
113
|
+
def test_resize
|
114
|
+
rd = Raindrops.new(4)
|
115
|
+
assert_equal 4, rd.size
|
116
|
+
assert_equal rd.capa, rd.size = rd.capa
|
117
|
+
assert_equal rd.capa, rd.to_ary.size
|
118
|
+
assert_equal 0, rd[rd.capa - 1]
|
119
|
+
assert_equal 1, rd.incr(rd.capa - 1)
|
120
|
+
assert_raises(ArgumentError) { rd[rd.capa] }
|
121
|
+
end
|
122
|
+
|
123
|
+
def test_resize_mremap
|
124
|
+
rd = Raindrops.new(4)
|
125
|
+
assert_equal 4, rd.size
|
126
|
+
old_capa = rd.capa
|
127
|
+
rd.size = rd.capa + 1
|
128
|
+
assert_equal old_capa * 2, rd.capa
|
129
|
+
|
130
|
+
# mremap() is currently broken with MAP_SHARED
|
131
|
+
# https://bugzilla.kernel.org/show_bug.cgi?id=8691
|
132
|
+
assert_equal 0, rd[old_capa]
|
133
|
+
assert_equal rd.capa, rd.to_ary.size
|
134
|
+
assert_equal 0, rd[rd.capa - 1]
|
135
|
+
assert_equal 1, rd.incr(rd.capa - 1)
|
136
|
+
assert_raises(ArgumentError) { rd[rd.capa] }
|
137
|
+
rescue RangeError
|
138
|
+
end # if RUBY_PLATFORM =~ /linux/
|
139
|
+
|
140
|
+
def test_evaporate
|
141
|
+
rd = Raindrops.new 1
|
142
|
+
assert_nil rd.evaporate!
|
143
|
+
assert_raises(StandardError) { rd.evaporate! }
|
144
|
+
end
|
145
|
+
|
146
|
+
def test_evaporate_with_fork
|
147
|
+
tmp = Raindrops.new 2
|
148
|
+
pid = fork do
|
149
|
+
tmp.incr 0
|
150
|
+
exit(tmp.evaporate! == nil)
|
151
|
+
end
|
152
|
+
_, status = Process.waitpid2(pid)
|
153
|
+
assert status.success?
|
154
|
+
assert_equal [ 1, 0 ], tmp.to_ary
|
155
|
+
tmp.incr 1
|
156
|
+
assert_equal [ 1, 1 ], tmp.to_ary
|
157
|
+
pid = fork do
|
158
|
+
tmp.incr 1
|
159
|
+
exit([ 1, 2 ] == tmp.to_ary)
|
160
|
+
end
|
161
|
+
_, status = Process.waitpid2(pid)
|
162
|
+
assert status.success?
|
163
|
+
assert_equal [ 1, 2 ], tmp.to_ary
|
164
|
+
end
|
101
165
|
end
|