This commit is contained in:
@@ -0,0 +1,31 @@
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIFRDCCAywCAQEwDQYJKoZIhvcNAQEFBQAwaDELMAkGA1UEBhMCRU0xFTATBgNV
|
||||
BAgTDEV2ZW50TWFjaGluZTEVMBMGA1UEChMMRXZlbnRNYWNoaW5lMRQwEgYDVQQL
|
||||
EwtEZXZlbG9wbWVudDEVMBMGA1UEAxMMRXZlbnRNYWNoaW5lMB4XDTA5MDMyOTAy
|
||||
MzE0NloXDTEwMDMyOTAyMzE0NlowaDELMAkGA1UEBhMCRU0xFTATBgNVBAgTDEV2
|
||||
ZW50TWFjaGluZTEVMBMGA1UEChMMRXZlbnRNYWNoaW5lMRQwEgYDVQQLEwtEZXZl
|
||||
bG9wbWVudDEVMBMGA1UEAxMMRXZlbnRNYWNoaW5lMIICIjANBgkqhkiG9w0BAQEF
|
||||
AAOCAg8AMIICCgKCAgEAv1FSOIX1z7CQtVBFlrB0A3/V29T+22STKKmiRWYkKL5b
|
||||
+hkrp9IZ5J4phZHgUVM2VDPOO2Oc2PU6dlGGZISg+UPERunTogxQKezCV0vcE9cK
|
||||
OwzxCFDRvv5rK8aKMscfBLbNKocAXywuRRQmdxPiVRzbyPrl+qCr/EDLXAX3D77l
|
||||
S8n2AwDg19VyI+IgFUE+Dy5e1eLoY6nV+Mq+vNXdn3ttF3t+ngac5pj5Q9h+pD5p
|
||||
67baDHSnf/7cy2fa/LKrLolVHQR9G2K6cEfeM99NtcsMbkoPs4iI3FA05OVTQHXg
|
||||
C8C8cRxrb9APl95I/ep65OIaCJgcdYxJ3QD3qOtQo6/NQsGnjbyiUxaEpjfqyT1N
|
||||
uzWD81Q8uXGNS8yD6dDynt/lseBjyp2nfC3uQ5fY18VdIcu0MJ9pezBUKrNuhlsy
|
||||
XXEZ2DXj4sY8QOvIcBqSB/zmS1nGEK55xrtkaiaNrY8fe8wRVpcPLxy+P225NFw+
|
||||
B69FJRA0Lj6Jt9BM4hV/3MSIEWwTVhuw4E02ywDYTzz1wq3ITf0tsbIPn0hXQMxD
|
||||
ohhAoKioM6u+yHtqsxD0eYaAWmHTVn5oDvOSGpvCpBfWHyA7FP5UQak0fKABEAgK
|
||||
iQYEnb294AXwXymJttfGTIV/Ne4tLN5dIpNma8UO8rlThlcr6xnTQDbR3gkTDRsC
|
||||
AwEAATANBgkqhkiG9w0BAQUFAAOCAgEAj7J8fy1LUWoVWnrXDAC9jwJ1nI/YjoSU
|
||||
6ywke3o04+nZC5S+dPnuVy+HAwsU940CoNvP6RStI/bH6JL+NIqEFmwM3M8xIEWV
|
||||
MYVPkfvQUxxGvDnaY7vv93u+6Q77HV3qlhAQBHChyuXyO7TG3+WzsiT9AnBNtAP0
|
||||
4jClt5kCAQXLO/p0SFEZQ8Ru9SM8d1i73Z0VDVzs8jYWlBhiherSgbw1xK4wBOpJ
|
||||
43XmjZsBSrDpiAXd07Ak3UL2GjfT7eStgebL3UIe39ThE/s/+l43bh0M6WbOBvyQ
|
||||
i/rZ50kd1GvN0xnZhtv07hIJWO85FGWi7Oet8AzdUZJ17v1Md/f2vdhPVTFN9q+w
|
||||
mQ6LxjackqCvaJaQfBEbqsn2Tklxk4tZuDioiQbOElT2e6vljQVJWIfNx38Ny2LM
|
||||
aiXQPQu+4CI7meAh5gXM5nyJGbZvRPsxj89CqYzyHCYs5HBP3AsviBvn26ziOF+c
|
||||
544VmHd9HkIv8UTC29hh+R64RlgMQQQdaXFaUrFPTs/do0k8n/c2bPc0iTdfi5Q2
|
||||
gq6Vi8q6Ay5wGgTtRRbn/mWKuCFjEh94z6pF9Xr06NX0PuEOdf+Ls9vI5vz6G0w6
|
||||
0Li7devEN7EKBY+7Mcjg918yq9i5tEiMkUgT68788t3fTC+4iUQ5fDtdrHsaOlIR
|
||||
8bs/XQVNE/s=
|
||||
-----END CERTIFICATE-----
|
||||
@@ -0,0 +1,51 @@
|
||||
-----BEGIN RSA PRIVATE KEY-----
|
||||
MIIJKAIBAAKCAgEAv1FSOIX1z7CQtVBFlrB0A3/V29T+22STKKmiRWYkKL5b+hkr
|
||||
p9IZ5J4phZHgUVM2VDPOO2Oc2PU6dlGGZISg+UPERunTogxQKezCV0vcE9cKOwzx
|
||||
CFDRvv5rK8aKMscfBLbNKocAXywuRRQmdxPiVRzbyPrl+qCr/EDLXAX3D77lS8n2
|
||||
AwDg19VyI+IgFUE+Dy5e1eLoY6nV+Mq+vNXdn3ttF3t+ngac5pj5Q9h+pD5p67ba
|
||||
DHSnf/7cy2fa/LKrLolVHQR9G2K6cEfeM99NtcsMbkoPs4iI3FA05OVTQHXgC8C8
|
||||
cRxrb9APl95I/ep65OIaCJgcdYxJ3QD3qOtQo6/NQsGnjbyiUxaEpjfqyT1NuzWD
|
||||
81Q8uXGNS8yD6dDynt/lseBjyp2nfC3uQ5fY18VdIcu0MJ9pezBUKrNuhlsyXXEZ
|
||||
2DXj4sY8QOvIcBqSB/zmS1nGEK55xrtkaiaNrY8fe8wRVpcPLxy+P225NFw+B69F
|
||||
JRA0Lj6Jt9BM4hV/3MSIEWwTVhuw4E02ywDYTzz1wq3ITf0tsbIPn0hXQMxDohhA
|
||||
oKioM6u+yHtqsxD0eYaAWmHTVn5oDvOSGpvCpBfWHyA7FP5UQak0fKABEAgKiQYE
|
||||
nb294AXwXymJttfGTIV/Ne4tLN5dIpNma8UO8rlThlcr6xnTQDbR3gkTDRsCAwEA
|
||||
AQKCAgB495RDRQB9x6hX3F+DviI8rDGug+h5FAiwJ0IBG2o1kNdbNVsTC5dvpEmg
|
||||
uPHaugCaEP+PMZbU34mNklKlb+7QbPbH18UGqz5so9TlmYOXz9oaKD6nAWL9nqRo
|
||||
02pCXQDR3DuxbhbgFnFTIECJ/jqXkl2toGaVp83W+6kZkHP8srkMyLASihWgosc+
|
||||
xRWAGvaAZtNz7br+eT5fxuH/SEKPOl1qAZ23kXrXm1XQfizk8MnMTptkUMYv+hfl
|
||||
TM98BASUsiTs6g+opy43HFn09naOQcqkWZO/8s6Gbvhi2lVfZqi5Ba6g3lVYJ3gU
|
||||
kGoako4N9qB7WqJz+LYjVR9C4TbkkJ9OD6ArwGAx5IIzC3XKSxCyY/pUn4YumPhY
|
||||
fjvY/km54TBtx/isS1TAgjSgDUxbzrfbkh7afOXSOniy9bWJMgNqHF61dqxWxmUg
|
||||
F5Tch9zH3qFFVkXpYzDU/R8ZV+CRouCvhn0eZYDh8IqIAwjH0VjkxjPyQtrdrMd3
|
||||
gDKMVKoY31EOMLZzv8a0prjpr15A+uw30tT336qb3fofks4pZKUJw8ru9jJVir2p
|
||||
+RML6iUHCmIeceF7/N1meooSMLPJe0xgKeMb9M4Wtd/et2UNVtP8nCDG622rf2a0
|
||||
F/EudXuFgc3FB8nXRw9TCkw9xKQff38edG5xPFUEgqObbVl5YQKCAQEA5DDKGOmp
|
||||
EO5Zuf/kZfG6/AMMYwAuv1HrYTV2w/HnI3tyQ34Xkeqo+I/OqmRk68Ztxw4Kx1So
|
||||
SRavkotrlWhhDpl2+Yn1BjkHktSoOdf9gJ9z9llkLmbOkBjmupig1NUB7fq/4y2k
|
||||
MdqJXDy3uVKHJ97gxdIheMTyHiKuMJPnuT5lZtlT210Ig82P7sLQb/sgCfKVFTr0
|
||||
Z3haQ5/tBNKjq+igT4nMBWupOTD1q2GeZLIZACnmnUIhvu+3/bm0l+wiCB0DqF0T
|
||||
Wy9tlL3fqQSCqzevL7/k5Lg6tJTaP/XYePB73TsOtAXgIaoltXgRBsBUeE1eaODx
|
||||
kMT6E1PPtn7EqQKCAQEA1qImmTWGqhKICrwje40awPufFtZ/qXKVCN/V+zYsrJV1
|
||||
EnZpUDM+zfitlQCugnrQVHSpgfekI6mmVkmogO3fkNjUFTq+neg7IHOUHnqotx+3
|
||||
NMqIsyFInGstu9mfPd26fzZjUtx5wKF38LDTIJJAEJ83U3UpPBfpwKmiOGDXOa54
|
||||
2i4em/bb/hrQR6JySruZYLi0fXnGI5ZOfpkHgC/KOFkKNKAg2oh4B9qo7ACyiSNk
|
||||
yojb2mmn6g1OLPxi7wGUSrkS1HQq4an6RZ+eUO0HXVWag0QStdQ91M9IrIHgSBBG
|
||||
0e86Ar6jtD579gqsbz4ySpI/FqEI9obTC+E1/b0aIwKCAQAGz334qGCnZLXA22ZR
|
||||
tJlEFEM2YTcD9snzqMjWqE2hvXl3kjfZ3wsUABbG9yAb+VwlaMHhmSE8rTSoRwj6
|
||||
+JaM/P+UCw4JFYKoWzh6IXwrbpbjb1+SEvdvTY71WsDSGVlpZOZ9PUt9QWyAGD/T
|
||||
hCcMhZZn0RG2rQoc5CQWxxNPcBFOtIXQMkKizGvTUHUwImqeYWMZsxzASdNH2WoV
|
||||
jsPbyaGfPhmcv83ZKyDp8IvtrXMZkiaT4vlm3Xi8VeKR9jY9z7/gMob1XcEDg3c9
|
||||
cCkGOy87WZrXSLhX02mAJzJCycqom66gqNw7pPxjIiY/8VWUEZsTvkL3cymTkhjM
|
||||
9ZOhAoIBAGUaNqJe01NTrV+ZJgGyAxM6s8LXQYV5IvjuL2bJKxwUvvP2cT9FFGWD
|
||||
qYiRrKJr5ayS07IUC+58oIzu33/0DSa27JgfduD9HrT3nKMK1mSEfRFSAjiXChQc
|
||||
bIubRGapBoub/AdxMazqoovvT1R9b84kobQfcVAMV6DYh0CVZWyXYfgsV2DSVOiK
|
||||
iufjfoDzg5lLCEI+1XW3/LunrB/W4yPN1X/amf8234ublYyt+2ucD4NUGnP05xLa
|
||||
N6P7M0MwdEEKkvMe0YBBSFH5kWK/dIOjqkgBDes20fVnuuz/tL1dZW7IiIP4dzaV
|
||||
ZGEOwBEatCfqYetv6b/u3IUxDfS7Wg8CggEBALoOwkn5LGdQg+bpdZAKJspGnJWL
|
||||
Kyr9Al2tvgc69rxfpZqS5eDLkYYCzWPpspSt0Axm1O7xOUDQDt42luaLNGJzHZ2Q
|
||||
Hn0ZNMhyHpe8d8mIQngRjD+nuLI/uFUglPzabDOCOln2aycjg1mA6ecXP1XMEVbu
|
||||
0RB/0IE36XTMfZ+u9+TRjkBLpmUaX1FdIQQWfwUou/LfaXotoQlhSGAcprLrncuJ
|
||||
T44UATYEgO/q9pMM33bdE3eBYZHoT9mSvqoLCN4s0LuwOYItIxLKUj0GulL0VQOI
|
||||
SZi+0A1c8cVDXgApkBrWPDQIR9JS4de0gW4hnDoUvHtUc2TYPRnz6N9MtFY=
|
||||
-----END RSA PRIVATE KEY-----
|
||||
@@ -0,0 +1,13 @@
|
||||
-----BEGIN DH PARAMETERS-----
|
||||
MIICCAKCAgEAikiatXa5aAteOtd6hOO33npjCvJByD3dwuM8rWzz0DFZdUH9nFJi
|
||||
b0VvTVweVECb6XZBsrDNLqGQykCrm43swSk5D9XQCGJLxFERD6yk3b90xaeCm3/a
|
||||
b0Ek5ZVvV73Cc/YbVmpBiOHoTFpUFJLZ7pLMQUSn8y3qUlNcY9/88HuwFi1s1lRM
|
||||
ovihSRyZMYAuYWOD4yuOuIcroKVjD6gWFrsW9XrALWny6vUXQrhk8Q3rj+wM6ZtE
|
||||
5afcB0b6ZJtphrDfk3dFjOVG/zVT37VWgrY8GABrpo2ey0W0WIQJ7rDKLaPaI4kc
|
||||
voOgC2K8Z3kSARZK+jULnwmBeYECz4EH/FF6FEp3GOKtkL4mqEkvh1n5EAesDOGl
|
||||
iiX+RZXcUrZliSeifSXBTMJWWFVC0fkGIMb9PTZfZHyAC54lpuxzVki0HIyQG9Fs
|
||||
41zBJ5e8eEoXXlfUYtduUC35YGy2IxSzYLAJE76rctAZSWghha9xLOCDFoLjMr8h
|
||||
FosKeHKJcBQ0bc8ymOpRIfrYLWhc0Pz2zkpJ/4eYw9t7NYg7S+jP19IE0gUnuM9v
|
||||
SpoYMtS28tP9nEdokdwuBKD0D3bJEBBefDlHgfXoMgvy9Hivc9PBGGNTNpyFPpwF
|
||||
sWVAkfhoNMJMC5V7LZsze+lftiDtzVoLSPDa9bO4BK7b/MgwCxfOhGsCAQI=
|
||||
-----END DH PARAMETERS-----
|
||||
@@ -0,0 +1,154 @@
|
||||
require 'em/pure_ruby' if ENV['EM_PURE_RUBY']
|
||||
require 'eventmachine'
|
||||
require 'test/unit'
|
||||
require 'rbconfig'
|
||||
require 'socket'
|
||||
|
||||
puts "EM Library Type: #{EM.library_type}"
|
||||
|
||||
class Test::Unit::TestCase
|
||||
class EMTestTimeout < StandardError ; end
|
||||
|
||||
def setup_timeout(timeout = TIMEOUT_INTERVAL)
|
||||
EM.schedule {
|
||||
EM.add_timer(timeout) {
|
||||
raise EMTestTimeout, "Test was cancelled after #{timeout} seconds."
|
||||
}
|
||||
}
|
||||
end
|
||||
|
||||
def port_in_use?(port, host="127.0.0.1")
|
||||
s = TCPSocket.new(host, port)
|
||||
s.close
|
||||
s
|
||||
rescue Errno::ECONNREFUSED
|
||||
false
|
||||
end
|
||||
|
||||
def next_port
|
||||
@@port ||= 9000
|
||||
begin
|
||||
@@port += 1
|
||||
end while port_in_use?(@@port)
|
||||
|
||||
@@port
|
||||
end
|
||||
|
||||
# Returns true if the host have a localhost 127.0.0.1 IPv4.
|
||||
def self.local_ipv4?
|
||||
return @@has_local_ipv4 if defined?(@@has_local_ipv4)
|
||||
begin
|
||||
get_my_ipv4_address "127.0.0.1"
|
||||
@@has_local_ipv4 = true
|
||||
rescue
|
||||
@@has_local_ipv4 = false
|
||||
end
|
||||
end
|
||||
|
||||
# Returns true if the host have a public IPv4 and stores it in
|
||||
# @@public_ipv4.
|
||||
def self.public_ipv4?
|
||||
return @@has_public_ipv4 if defined?(@@has_public_ipv4)
|
||||
begin
|
||||
@@public_ipv4 = get_my_ipv4_address "1.2.3.4"
|
||||
@@has_public_ipv4 = true
|
||||
rescue
|
||||
@@has_public_ipv4 = false
|
||||
end
|
||||
end
|
||||
|
||||
# Returns true if the host have a localhost ::1 IPv6.
|
||||
def self.local_ipv6?
|
||||
return @@has_local_ipv6 if defined?(@@has_local_ipv6)
|
||||
begin
|
||||
get_my_ipv6_address "::1"
|
||||
@@has_local_ipv6 = true
|
||||
rescue
|
||||
@@has_local_ipv6 = false
|
||||
end
|
||||
end
|
||||
|
||||
# Returns true if the host have a public IPv6 and stores it in
|
||||
# @@public_ipv6.
|
||||
def self.public_ipv6?
|
||||
return @@has_public_ipv6 if defined?(@@has_public_ipv6)
|
||||
begin
|
||||
@@public_ipv6 = get_my_ipv6_address "2001::1"
|
||||
@@has_public_ipv6 = true
|
||||
rescue
|
||||
@@has_public_ipv6 = false
|
||||
end
|
||||
end
|
||||
|
||||
# Returns an array with the localhost addresses (IPv4 and/or IPv6).
|
||||
def local_ips
|
||||
return @@local_ips if defined?(@@local_ips)
|
||||
@@local_ips = []
|
||||
@@local_ips << "127.0.0.1" if self.class.local_ipv4?
|
||||
@@local_ips << "::1" if self.class.local_ipv6?
|
||||
@@local_ips
|
||||
end
|
||||
|
||||
def exception_class
|
||||
jruby? ? NativeException : RuntimeError
|
||||
end
|
||||
|
||||
module PlatformHelper
|
||||
# http://blog.emptyway.com/2009/11/03/proper-way-to-detect-windows-platform-in-ruby/
|
||||
def windows?
|
||||
RbConfig::CONFIG['host_os'] =~ /mswin|mingw/
|
||||
end
|
||||
|
||||
def solaris?
|
||||
RUBY_PLATFORM =~ /solaris/
|
||||
end
|
||||
|
||||
# http://stackoverflow.com/questions/1342535/how-can-i-tell-if-im-running-from-jruby-vs-ruby/1685970#1685970
|
||||
def jruby?
|
||||
defined? JRUBY_VERSION
|
||||
end
|
||||
|
||||
def rbx?
|
||||
defined?(RUBY_ENGINE) && RUBY_ENGINE == 'rbx'
|
||||
end
|
||||
end
|
||||
|
||||
include PlatformHelper
|
||||
extend PlatformHelper
|
||||
|
||||
# Tests run significantly slower on windows. YMMV
|
||||
TIMEOUT_INTERVAL = windows? ? 1 : 0.25
|
||||
|
||||
def silent
|
||||
backup, $VERBOSE = $VERBOSE, nil
|
||||
begin
|
||||
yield
|
||||
ensure
|
||||
$VERBOSE = backup
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
private
|
||||
|
||||
def self.get_my_ipv4_address ip
|
||||
orig, Socket.do_not_reverse_lookup = Socket.do_not_reverse_lookup, true # turn off reverse DNS resolution temporarily
|
||||
UDPSocket.open(Socket::AF_INET) do |s|
|
||||
s.connect ip, 1
|
||||
s.addr.last
|
||||
end
|
||||
ensure
|
||||
Socket.do_not_reverse_lookup = orig
|
||||
end
|
||||
|
||||
def self.get_my_ipv6_address ip
|
||||
orig, Socket.do_not_reverse_lookup = Socket.do_not_reverse_lookup, true # turn off reverse DNS resolution temporarily
|
||||
UDPSocket.open(Socket::AF_INET6) do |s|
|
||||
s.connect ip, 1
|
||||
s.addr.last
|
||||
end
|
||||
ensure
|
||||
Socket.do_not_reverse_lookup = orig
|
||||
end
|
||||
|
||||
end
|
||||
@@ -0,0 +1,151 @@
|
||||
require 'em_test_helper'
|
||||
require 'socket'
|
||||
|
||||
class TestAttach < Test::Unit::TestCase
|
||||
class EchoServer < EM::Connection
|
||||
def receive_data data
|
||||
$received_data << data
|
||||
send_data data
|
||||
end
|
||||
end
|
||||
|
||||
class EchoClient < EM::Connection
|
||||
def initialize socket
|
||||
self.notify_readable = true
|
||||
@socket = socket
|
||||
@socket.write("abc\n")
|
||||
end
|
||||
|
||||
def notify_readable
|
||||
$read = @socket.readline
|
||||
$fd = detach
|
||||
end
|
||||
|
||||
def unbind
|
||||
EM.next_tick do
|
||||
@socket.write("def\n")
|
||||
EM.add_timer(0.1) { EM.stop }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def setup
|
||||
@port = next_port
|
||||
$read, $r, $w, $fd = nil
|
||||
$received_data = ""
|
||||
end
|
||||
|
||||
def teardown
|
||||
[$r, $w].each do |io|
|
||||
io.close rescue nil
|
||||
end
|
||||
$received_data = nil
|
||||
end
|
||||
|
||||
def test_attach
|
||||
socket = nil
|
||||
|
||||
EM.run {
|
||||
EM.start_server "127.0.0.1", @port, EchoServer
|
||||
socket = TCPSocket.new "127.0.0.1", @port
|
||||
EM.watch socket, EchoClient, socket
|
||||
}
|
||||
|
||||
assert_equal $read, "abc\n"
|
||||
unless jruby? # jruby filenos are not real
|
||||
assert_equal $fd, socket.fileno
|
||||
end
|
||||
assert_equal false, socket.closed?
|
||||
assert_equal socket.readline, "def\n"
|
||||
end
|
||||
|
||||
module PipeWatch
|
||||
def notify_readable
|
||||
$read = $r.readline
|
||||
EM.stop
|
||||
end
|
||||
end
|
||||
|
||||
def test_attach_server
|
||||
omit_if(jruby?)
|
||||
$before = TCPServer.new("127.0.0.1", @port)
|
||||
sig = nil
|
||||
EM.run {
|
||||
sig = EM.attach_server $before, EchoServer
|
||||
|
||||
handler = Class.new(EM::Connection) do
|
||||
def initialize
|
||||
send_data "hello world"
|
||||
close_connection_after_writing
|
||||
EM.add_timer(0.1) { EM.stop }
|
||||
end
|
||||
end
|
||||
EM.connect("127.0.0.1", @port, handler)
|
||||
}
|
||||
|
||||
assert_equal false, $before.closed?
|
||||
assert_equal "hello world", $received_data
|
||||
assert sig.is_a?(Integer)
|
||||
end
|
||||
|
||||
def test_attach_pipe
|
||||
EM.run{
|
||||
$r, $w = IO.pipe
|
||||
EM.watch $r, PipeWatch do |c|
|
||||
c.notify_readable = true
|
||||
end
|
||||
$w.write("ghi\n")
|
||||
}
|
||||
|
||||
assert_equal $read, "ghi\n"
|
||||
end
|
||||
|
||||
def test_set_readable
|
||||
before, after = nil
|
||||
|
||||
EM.run{
|
||||
$r, $w = IO.pipe
|
||||
c = EM.watch $r, PipeWatch do |con|
|
||||
con.notify_readable = false
|
||||
end
|
||||
|
||||
EM.next_tick{
|
||||
before = c.notify_readable?
|
||||
c.notify_readable = true
|
||||
after = c.notify_readable?
|
||||
}
|
||||
|
||||
$w.write("jkl\n")
|
||||
}
|
||||
|
||||
assert !before
|
||||
assert after
|
||||
assert_equal $read, "jkl\n"
|
||||
end
|
||||
|
||||
def test_read_write_pipe
|
||||
result = nil
|
||||
|
||||
pipe_reader = Module.new do
|
||||
define_method :receive_data do |data|
|
||||
result = data
|
||||
EM.stop
|
||||
end
|
||||
end
|
||||
|
||||
r,w = IO.pipe
|
||||
|
||||
EM.run {
|
||||
EM.attach r, pipe_reader
|
||||
writer = EM.attach(w)
|
||||
writer.send_data 'ghi'
|
||||
|
||||
# XXX: Process will hang in Windows without this line
|
||||
writer.close_connection_after_writing
|
||||
}
|
||||
|
||||
assert_equal "ghi", result
|
||||
ensure
|
||||
[r,w].each {|io| io.close rescue nil }
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,321 @@
|
||||
require 'em_test_helper'
|
||||
require 'socket'
|
||||
|
||||
class TestBasic < Test::Unit::TestCase
|
||||
def setup
|
||||
@port = next_port
|
||||
end
|
||||
|
||||
def test_connection_class_cache
|
||||
mod = Module.new
|
||||
a, b = nil, nil
|
||||
EM.run {
|
||||
EM.start_server '127.0.0.1', @port, mod
|
||||
a = EM.connect '127.0.0.1', @port, mod
|
||||
b = EM.connect '127.0.0.1', @port, mod
|
||||
EM.stop
|
||||
}
|
||||
assert_equal a.class, b.class
|
||||
assert_kind_of EM::Connection, a
|
||||
end
|
||||
|
||||
#-------------------------------------
|
||||
|
||||
|
||||
def test_em
|
||||
assert_nothing_raised do
|
||||
EM.run {
|
||||
setup_timeout
|
||||
EM.add_timer 0 do
|
||||
EM.stop
|
||||
end
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
#-------------------------------------
|
||||
|
||||
def test_timer
|
||||
assert_nothing_raised do
|
||||
EM.run {
|
||||
setup_timeout
|
||||
n = 0
|
||||
EM.add_periodic_timer(0.1) {
|
||||
n += 1
|
||||
EM.stop if n == 2
|
||||
}
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
#-------------------------------------
|
||||
|
||||
# This test once threw an already-running exception.
|
||||
module Trivial
|
||||
def post_init
|
||||
EM.stop
|
||||
end
|
||||
end
|
||||
|
||||
def test_server
|
||||
assert_nothing_raised do
|
||||
EM.run {
|
||||
setup_timeout
|
||||
EM.start_server "127.0.0.1", @port, Trivial
|
||||
EM.connect "127.0.0.1", @port
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
#--------------------------------------
|
||||
|
||||
# EM#run_block starts the reactor loop, runs the supplied block, and then STOPS
|
||||
# the loop automatically. Contrast with EM#run, which keeps running the reactor
|
||||
# even after the supplied block completes.
|
||||
def test_run_block
|
||||
assert !EM.reactor_running?
|
||||
a = nil
|
||||
EM.run_block { a = "Worked" }
|
||||
assert a
|
||||
assert !EM.reactor_running?
|
||||
end
|
||||
|
||||
class UnbindError < EM::Connection
|
||||
ERR = Class.new(StandardError)
|
||||
def initialize *args
|
||||
super
|
||||
end
|
||||
def connection_completed
|
||||
close_connection_after_writing
|
||||
end
|
||||
def unbind
|
||||
raise ERR
|
||||
end
|
||||
end
|
||||
|
||||
def test_unbind_error_during_stop
|
||||
assert_raises( UnbindError::ERR ) {
|
||||
EM.run {
|
||||
EM.start_server "127.0.0.1", @port
|
||||
EM.connect "127.0.0.1", @port, UnbindError do
|
||||
EM.stop
|
||||
end
|
||||
}
|
||||
}
|
||||
end
|
||||
|
||||
def test_unbind_error
|
||||
EM.run {
|
||||
EM.error_handler do |e|
|
||||
assert(e.is_a?(UnbindError::ERR))
|
||||
EM.stop
|
||||
end
|
||||
EM.start_server "127.0.0.1", @port
|
||||
EM.connect "127.0.0.1", @port, UnbindError
|
||||
}
|
||||
|
||||
# Remove the error handler before the next test
|
||||
EM.error_handler(nil)
|
||||
end
|
||||
|
||||
module BrsTestSrv
|
||||
def receive_data data
|
||||
$received << data
|
||||
end
|
||||
def unbind
|
||||
EM.stop
|
||||
end
|
||||
end
|
||||
module BrsTestCli
|
||||
def post_init
|
||||
send_data $sent
|
||||
close_connection_after_writing
|
||||
end
|
||||
end
|
||||
|
||||
# From ticket #50
|
||||
def test_byte_range_send
|
||||
$received = ''
|
||||
$sent = (0..255).to_a.pack('C*')
|
||||
EM::run {
|
||||
EM::start_server "127.0.0.1", @port, BrsTestSrv
|
||||
EM::connect "127.0.0.1", @port, BrsTestCli
|
||||
|
||||
setup_timeout
|
||||
}
|
||||
assert_equal($sent, $received)
|
||||
end
|
||||
|
||||
def test_bind_connect
|
||||
pend('FIXME: this test is broken on Windows') if windows?
|
||||
|
||||
local_ip = UDPSocket.open {|s| s.connect('localhost', 80); s.addr.last }
|
||||
|
||||
bind_port = next_port
|
||||
|
||||
port, ip = nil
|
||||
bound_server = Module.new do
|
||||
define_method :post_init do
|
||||
begin
|
||||
port, ip = Socket.unpack_sockaddr_in(get_peername)
|
||||
ensure
|
||||
EM.stop
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
EM.run do
|
||||
setup_timeout
|
||||
EM.start_server "127.0.0.1", @port, bound_server
|
||||
EM.bind_connect local_ip, bind_port, "127.0.0.1", @port
|
||||
end
|
||||
|
||||
assert_equal bind_port, port
|
||||
assert_equal local_ip, ip
|
||||
end
|
||||
|
||||
def test_invalid_address_bind_connect_dst
|
||||
e = nil
|
||||
EM.run do
|
||||
begin
|
||||
EM.bind_connect('localhost', nil, 'invalid.invalid', 80)
|
||||
rescue Exception => e
|
||||
# capture the exception
|
||||
ensure
|
||||
EM.stop
|
||||
end
|
||||
end
|
||||
|
||||
assert_kind_of(EventMachine::ConnectionError, e)
|
||||
assert_match(/unable to resolve address:.*not known/, e.message)
|
||||
end
|
||||
|
||||
def test_invalid_address_bind_connect_src
|
||||
e = nil
|
||||
EM.run do
|
||||
begin
|
||||
EM.bind_connect('invalid.invalid', nil, 'localhost', 80)
|
||||
rescue Exception => e
|
||||
# capture the exception
|
||||
ensure
|
||||
EM.stop
|
||||
end
|
||||
end
|
||||
|
||||
assert_kind_of(EventMachine::ConnectionError, e)
|
||||
assert_match(/invalid bind address:.*not known/, e.message)
|
||||
end
|
||||
|
||||
def test_reactor_thread?
|
||||
assert !EM.reactor_thread?
|
||||
EM.run { assert EM.reactor_thread?; EM.stop }
|
||||
assert !EM.reactor_thread?
|
||||
end
|
||||
|
||||
def test_schedule_on_reactor_thread
|
||||
x = false
|
||||
EM.run do
|
||||
EM.schedule { x = true }
|
||||
EM.stop
|
||||
end
|
||||
assert x
|
||||
end
|
||||
|
||||
def test_schedule_from_thread
|
||||
x = false
|
||||
EM.run do
|
||||
Thread.new { EM.schedule { x = true; EM.stop } }.join
|
||||
end
|
||||
assert x
|
||||
end
|
||||
|
||||
def test_set_heartbeat_interval
|
||||
omit_if(jruby?)
|
||||
interval = 0.5
|
||||
EM.run {
|
||||
EM.set_heartbeat_interval interval
|
||||
$interval = EM.get_heartbeat_interval
|
||||
EM.stop
|
||||
}
|
||||
assert_equal(interval, $interval)
|
||||
end
|
||||
|
||||
module PostInitRaiser
|
||||
ERR = Class.new(StandardError)
|
||||
def post_init
|
||||
raise ERR
|
||||
end
|
||||
end
|
||||
|
||||
def test_bubble_errors_from_post_init
|
||||
assert_raises(PostInitRaiser::ERR) do
|
||||
EM.run do
|
||||
EM.start_server "127.0.0.1", @port
|
||||
EM.connect "127.0.0.1", @port, PostInitRaiser
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
module InitializeRaiser
|
||||
ERR = Class.new(StandardError)
|
||||
def initialize
|
||||
raise ERR
|
||||
end
|
||||
end
|
||||
|
||||
def test_bubble_errors_from_initialize
|
||||
assert_raises(InitializeRaiser::ERR) do
|
||||
EM.run do
|
||||
EM.start_server "127.0.0.1", @port
|
||||
EM.connect "127.0.0.1", @port, InitializeRaiser
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def test_schedule_close
|
||||
omit_if(jruby?)
|
||||
localhost, port = '127.0.0.1', 9000
|
||||
timer_ran = false
|
||||
num_close_scheduled = nil
|
||||
EM.run do
|
||||
assert_equal 0, EM.num_close_scheduled
|
||||
EM.add_timer(1) { timer_ran = true; EM.stop }
|
||||
EM.start_server localhost, port do |s|
|
||||
s.close_connection
|
||||
num_close_scheduled = EM.num_close_scheduled
|
||||
end
|
||||
EM.connect localhost, port do |c|
|
||||
def c.unbind
|
||||
EM.stop
|
||||
end
|
||||
end
|
||||
end
|
||||
assert !timer_ran
|
||||
assert_equal 1, num_close_scheduled
|
||||
end
|
||||
|
||||
def test_error_handler_idempotent # issue 185
|
||||
errors = []
|
||||
ticks = []
|
||||
EM.error_handler do |e|
|
||||
errors << e
|
||||
end
|
||||
|
||||
EM.run do
|
||||
EM.next_tick do
|
||||
ticks << :first
|
||||
raise
|
||||
end
|
||||
EM.next_tick do
|
||||
ticks << :second
|
||||
end
|
||||
EM.add_timer(0.001) { EM.stop }
|
||||
end
|
||||
|
||||
# Remove the error handler before the next test
|
||||
EM.error_handler(nil)
|
||||
|
||||
assert_equal 1, errors.size
|
||||
assert_equal [:first, :second], ticks
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,75 @@
|
||||
require 'em_test_helper'
|
||||
|
||||
class TestEMChannel < Test::Unit::TestCase
|
||||
def test_channel_subscribe
|
||||
s = 0
|
||||
EM.run do
|
||||
c = EM::Channel.new
|
||||
c.subscribe { |v| s = v; EM.stop }
|
||||
c << 1
|
||||
end
|
||||
assert_equal 1, s
|
||||
end
|
||||
|
||||
def test_channel_unsubscribe
|
||||
s = 0
|
||||
EM.run do
|
||||
c = EM::Channel.new
|
||||
subscription = c.subscribe { |v| s = v }
|
||||
c.unsubscribe(subscription)
|
||||
c << 1
|
||||
EM.next_tick { EM.stop }
|
||||
end
|
||||
assert_not_equal 1, s
|
||||
end
|
||||
|
||||
def test_channel_pop
|
||||
s = 0
|
||||
EM.run do
|
||||
c = EM::Channel.new
|
||||
c.pop{ |v| s = v }
|
||||
c.push(1,2,3)
|
||||
c << 4
|
||||
c << 5
|
||||
EM.next_tick { EM.stop }
|
||||
end
|
||||
assert_equal 1, s
|
||||
end
|
||||
|
||||
def test_channel_reactor_thread_push
|
||||
out = []
|
||||
c = EM::Channel.new
|
||||
c.subscribe { |v| out << v }
|
||||
Thread.new { c.push(1,2,3) }.join
|
||||
assert out.empty?
|
||||
|
||||
EM.run { EM.next_tick { EM.stop } }
|
||||
|
||||
assert_equal [1,2,3], out
|
||||
end
|
||||
|
||||
def test_channel_reactor_thread_callback
|
||||
out = []
|
||||
c = EM::Channel.new
|
||||
Thread.new { c.subscribe { |v| out << v } }.join
|
||||
c.push(1,2,3)
|
||||
assert out.empty?
|
||||
|
||||
EM.run { EM.next_tick { EM.stop } }
|
||||
|
||||
assert_equal [1,2,3], out
|
||||
end
|
||||
|
||||
def test_channel_num_subscribers
|
||||
subs = 0
|
||||
EM.run do
|
||||
c = EM::Channel.new
|
||||
c.subscribe { |v| s = v }
|
||||
c.subscribe { |v| s = v }
|
||||
EM.next_tick { EM.stop }
|
||||
subs = c.num_subscribers
|
||||
end
|
||||
|
||||
assert_equal subs, 2
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,178 @@
|
||||
require 'em_test_helper'
|
||||
require 'em/completion'
|
||||
|
||||
class TestCompletion < Test::Unit::TestCase
|
||||
def completion
|
||||
@completion ||= EM::Completion.new
|
||||
end
|
||||
|
||||
def crank
|
||||
# This is a slow solution, but this just executes the next tick queue
|
||||
# once. It's the easiest way for now.
|
||||
EM.run { EM.stop }
|
||||
end
|
||||
|
||||
def results
|
||||
@results ||= []
|
||||
end
|
||||
|
||||
def test_state
|
||||
assert_equal :unknown, completion.state
|
||||
end
|
||||
|
||||
def test_succeed
|
||||
completion.callback { |val| results << val }
|
||||
completion.succeed :object
|
||||
crank
|
||||
assert_equal :succeeded, completion.state
|
||||
assert_equal [:object], results
|
||||
end
|
||||
|
||||
def test_fail
|
||||
completion.errback { |val| results << val }
|
||||
completion.fail :object
|
||||
crank
|
||||
assert_equal :failed, completion.state
|
||||
assert_equal [:object], results
|
||||
end
|
||||
|
||||
def test_callback
|
||||
completion.callback { results << :callback }
|
||||
completion.errback { results << :errback }
|
||||
completion.succeed
|
||||
crank
|
||||
assert_equal [:callback], results
|
||||
end
|
||||
|
||||
def test_errback
|
||||
completion.callback { results << :callback }
|
||||
completion.errback { results << :errback }
|
||||
completion.fail
|
||||
crank
|
||||
assert_equal [:errback], results
|
||||
end
|
||||
|
||||
def test_stateback
|
||||
completion.stateback(:magic) { results << :stateback }
|
||||
completion.change_state(:magic)
|
||||
crank
|
||||
assert_equal [:stateback], results
|
||||
end
|
||||
|
||||
def test_does_not_enqueue_when_completed
|
||||
completion.callback { results << :callback }
|
||||
completion.succeed
|
||||
completion.errback { results << :errback }
|
||||
completion.fail
|
||||
crank
|
||||
assert_equal [:callback], results
|
||||
end
|
||||
|
||||
def test_completed
|
||||
assert_equal false, completion.completed?
|
||||
completion.succeed
|
||||
assert_equal true, completion.completed?
|
||||
completion.fail
|
||||
assert_equal true, completion.completed?
|
||||
completion.change_state :magic
|
||||
assert_equal false, completion.completed?
|
||||
end
|
||||
|
||||
def test_recursive_callbacks
|
||||
completion.callback do |val|
|
||||
results << val
|
||||
completion.succeed :two
|
||||
end
|
||||
completion.callback do |val|
|
||||
results << val
|
||||
completion.succeed :three
|
||||
end
|
||||
completion.callback do |val|
|
||||
results << val
|
||||
end
|
||||
completion.succeed :one
|
||||
crank
|
||||
assert_equal [:one, :two, :three], results
|
||||
end
|
||||
|
||||
def test_late_defined_callbacks
|
||||
completion.callback { results << :one }
|
||||
completion.succeed
|
||||
crank
|
||||
assert_equal [:one], results
|
||||
completion.callback { results << :two }
|
||||
crank
|
||||
assert_equal [:one, :two], results
|
||||
end
|
||||
|
||||
def test_cleared_completions
|
||||
completion.callback { results << :callback }
|
||||
completion.errback { results << :errback }
|
||||
|
||||
completion.succeed
|
||||
crank
|
||||
completion.fail
|
||||
crank
|
||||
completion.succeed
|
||||
crank
|
||||
|
||||
assert_equal [:callback], results
|
||||
end
|
||||
|
||||
def test_skip_completed_callbacks
|
||||
completion.callback { results << :callback }
|
||||
completion.succeed
|
||||
crank
|
||||
|
||||
completion.errback { results << :errback }
|
||||
completion.fail
|
||||
crank
|
||||
|
||||
assert_equal [:callback], results
|
||||
end
|
||||
|
||||
def test_completions
|
||||
completion.completion { results << :completion }
|
||||
completion.succeed
|
||||
crank
|
||||
assert_equal [:completion], results
|
||||
|
||||
completion.change_state(:unknown)
|
||||
results.clear
|
||||
|
||||
completion.completion { results << :completion }
|
||||
completion.fail
|
||||
crank
|
||||
assert_equal [:completion], results
|
||||
end
|
||||
|
||||
def test_latent_completion
|
||||
completion.completion { results << :completion }
|
||||
completion.succeed
|
||||
crank
|
||||
completion.completion { results << :completion }
|
||||
crank
|
||||
assert_equal [:completion, :completion], results
|
||||
end
|
||||
|
||||
def test_timeout
|
||||
args = [1, 2, 3]
|
||||
EM.run do
|
||||
completion.timeout(0.0001, *args)
|
||||
completion.errback { |*errargs| results << errargs }
|
||||
completion.completion { EM.stop }
|
||||
EM.add_timer(0.1) { flunk 'test timed out' }
|
||||
end
|
||||
assert_equal [[1,2,3]], results
|
||||
end
|
||||
|
||||
def test_timeout_gets_cancelled
|
||||
EM.run do
|
||||
completion.timeout(0.0001, :timeout)
|
||||
completion.errback { results << :errback }
|
||||
completion.succeed
|
||||
EM.add_timer(0.0002) { EM.stop }
|
||||
end
|
||||
assert_equal [], results
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,83 @@
|
||||
require 'em_test_helper'
|
||||
|
||||
class TestConnectionCount < Test::Unit::TestCase
|
||||
def teardown
|
||||
EM.epoll = false
|
||||
EM.kqueue = false
|
||||
end
|
||||
|
||||
def test_idle_connection_count
|
||||
count = nil
|
||||
EM.run {
|
||||
count = EM.connection_count
|
||||
EM.stop_event_loop
|
||||
}
|
||||
assert_equal(0, count)
|
||||
end
|
||||
|
||||
# Run this again with epoll enabled (if available)
|
||||
def test_idle_connection_count_epoll
|
||||
EM.epoll if EM.epoll?
|
||||
|
||||
count = nil
|
||||
EM.run {
|
||||
count = EM.connection_count
|
||||
EM.stop_event_loop
|
||||
}
|
||||
assert_equal(0, count)
|
||||
end
|
||||
|
||||
# Run this again with kqueue enabled (if available)
|
||||
def test_idle_connection_count_kqueue
|
||||
EM.kqueue if EM.kqueue?
|
||||
|
||||
count = nil
|
||||
EM.run {
|
||||
count = EM.connection_count
|
||||
EM.stop_event_loop
|
||||
}
|
||||
assert_equal(0, count)
|
||||
end
|
||||
|
||||
module Client
|
||||
def connection_completed
|
||||
$client_conns += 1
|
||||
EM.stop if $client_conns == 3
|
||||
end
|
||||
end
|
||||
|
||||
def test_with_some_connections
|
||||
EM.run {
|
||||
$client_conns = 0
|
||||
$initial_conns = EM.connection_count
|
||||
EM.start_server("127.0.0.1", 9999)
|
||||
$server_conns = EM.connection_count
|
||||
3.times { EM.connect("127.0.0.1", 9999, Client) }
|
||||
}
|
||||
|
||||
assert_equal(0, $initial_conns)
|
||||
assert_equal(1, $server_conns)
|
||||
assert_equal(4, $client_conns + $server_conns)
|
||||
end
|
||||
|
||||
module DoubleCloseClient
|
||||
def unbind
|
||||
close_connection
|
||||
$num_close_scheduled_1 = EM.num_close_scheduled
|
||||
EM.next_tick do
|
||||
$num_close_scheduled_2 = EM.num_close_scheduled
|
||||
EM.stop
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def test_num_close_scheduled
|
||||
omit_if(jruby?)
|
||||
EM.run {
|
||||
assert_equal(0, EM.num_close_scheduled)
|
||||
EM.connect("127.0.0.1", 9999, DoubleCloseClient) # nothing listening on 9999
|
||||
}
|
||||
assert_equal(1, $num_close_scheduled_1)
|
||||
assert_equal(0, $num_close_scheduled_2)
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,35 @@
|
||||
require 'em_test_helper'
|
||||
|
||||
class TestConnectionWrite < Test::Unit::TestCase
|
||||
|
||||
# This test takes advantage of the fact that EM::_RunSelectOnce iterates over the connections twice:
|
||||
# - once to determine which ones to call Write() on
|
||||
# - and once to call Write() on each of them.
|
||||
#
|
||||
# But state may change in the meantime before Write() is finally called.
|
||||
# And that is what we try to exploit to get Write() to be called when bWatchOnly is true, and bNotifyWritable is false,
|
||||
# to cause an assertion failure.
|
||||
|
||||
module SimpleClient
|
||||
def notify_writable
|
||||
$conn2.notify_writable = false # Being naughty in callback
|
||||
# If this doesn't crash anything, the test passed!
|
||||
end
|
||||
end
|
||||
|
||||
def test_with_naughty_callback
|
||||
EM.run do
|
||||
r1, _ = IO.pipe
|
||||
r2, _ = IO.pipe
|
||||
|
||||
# Adding EM.watches
|
||||
$conn1 = EM.watch(r1, SimpleClient)
|
||||
$conn2 = EM.watch(r2, SimpleClient)
|
||||
|
||||
$conn1.notify_writable = true
|
||||
$conn2.notify_writable = true
|
||||
|
||||
EM.stop
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,35 @@
|
||||
require 'em_test_helper'
|
||||
|
||||
class TestDefer < Test::Unit::TestCase
|
||||
|
||||
def test_defers
|
||||
n = 0
|
||||
n_times = 20
|
||||
EM.run {
|
||||
n_times.times {
|
||||
work_proc = proc { n += 1 }
|
||||
callback = proc { EM.stop if n == n_times }
|
||||
EM.defer work_proc, callback
|
||||
}
|
||||
}
|
||||
assert_equal( n, n_times )
|
||||
end
|
||||
|
||||
def test_errbacks
|
||||
iterations = 20
|
||||
callback_parameter = rand(100)
|
||||
callback_parameters = []
|
||||
callback_op = proc { callback_parameter }
|
||||
callback = proc { |result| callback_parameters << result }
|
||||
errback_parameter = Exception.new
|
||||
errback_parameters = []
|
||||
errback_op = proc { raise errback_parameter }
|
||||
errback = proc { |error| errback_parameters << error }
|
||||
EventMachine.run do
|
||||
(1..iterations).each { |index| EventMachine.defer(index.even? ? callback_op : errback_op, callback, errback) }
|
||||
EventMachine.add_periodic_timer(0.1) { EventMachine.stop if EventMachine.defers_finished? }
|
||||
end
|
||||
assert_equal(callback_parameters.select { |parameter| parameter == callback_parameter }.length, iterations * 0.5)
|
||||
assert_equal(errback_parameters.select{ |parameter| parameter == errback_parameter }.length, iterations * 0.5)
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,35 @@
|
||||
require 'em_test_helper'
|
||||
|
||||
class TestDeferrable < Test::Unit::TestCase
|
||||
class Later
|
||||
include EM::Deferrable
|
||||
end
|
||||
|
||||
def test_timeout_without_args
|
||||
assert_nothing_raised do
|
||||
EM.run {
|
||||
df = Later.new
|
||||
df.timeout(0)
|
||||
df.errback { EM.stop }
|
||||
EM.add_timer(0.01) { flunk "Deferrable was not timed out." }
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
def test_timeout_with_args
|
||||
args = nil
|
||||
|
||||
EM.run {
|
||||
df = Later.new
|
||||
df.timeout(0, :timeout, :foo)
|
||||
df.errback do |type, name|
|
||||
args = [type, name]
|
||||
EM.stop
|
||||
end
|
||||
|
||||
EM.add_timer(0.01) { flunk "Deferrable was not timed out." }
|
||||
}
|
||||
|
||||
assert_equal [:timeout, :foo], args
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,142 @@
|
||||
require 'em_test_helper'
|
||||
|
||||
|
||||
class TestEpoll < Test::Unit::TestCase
|
||||
|
||||
module TestEchoServer
|
||||
def receive_data data
|
||||
send_data data
|
||||
close_connection_after_writing
|
||||
end
|
||||
end
|
||||
|
||||
module TestEchoClient
|
||||
def connection_completed
|
||||
send_data "ABCDE"
|
||||
$max += 1
|
||||
end
|
||||
def receive_data data
|
||||
raise "bad response" unless data == "ABCDE"
|
||||
end
|
||||
def unbind
|
||||
$n -= 1
|
||||
EM.stop if $n == 0
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
# We can set the rlimit/nofile of a process but we can only set it
|
||||
# higher if we're running as root.
|
||||
# On most systems, the default value is 1024.
|
||||
def test_rlimit
|
||||
omit_if(windows? || jruby?)
|
||||
unless EM.set_descriptor_table_size >= 1024
|
||||
a = EM.set_descriptor_table_size
|
||||
assert( a <= 1024 )
|
||||
a = EM.set_descriptor_table_size( 1024 )
|
||||
assert( a == 1024 )
|
||||
end
|
||||
end
|
||||
|
||||
# Run a high-volume version of this test by kicking the number of connections
|
||||
# up past 512. (Each connection uses two sockets, a client and a server.)
|
||||
# (Will require running the test as root)
|
||||
# This test exercises TCP clients and servers.
|
||||
#
|
||||
# XXX this test causes all sort of weird issues on OSX (when run as part of the suite)
|
||||
def _test_descriptors
|
||||
EM.epoll
|
||||
EM.set_descriptor_table_size 60000
|
||||
EM.run {
|
||||
EM.start_server "127.0.0.1", 9800, TestEchoServer
|
||||
$n = 0
|
||||
$max = 0
|
||||
100.times {
|
||||
EM.connect("127.0.0.1", 9800, TestEchoClient) {$n += 1}
|
||||
}
|
||||
}
|
||||
assert_equal(0, $n)
|
||||
assert_equal(100, $max)
|
||||
end
|
||||
|
||||
def setup
|
||||
@port = next_port
|
||||
end
|
||||
|
||||
module TestDatagramServer
|
||||
def receive_data dgm
|
||||
$in = dgm
|
||||
send_data "abcdefghij"
|
||||
end
|
||||
end
|
||||
module TestDatagramClient
|
||||
def initialize port
|
||||
@port = port
|
||||
end
|
||||
|
||||
def post_init
|
||||
send_datagram "1234567890", "127.0.0.1", @port
|
||||
end
|
||||
|
||||
def receive_data dgm
|
||||
$out = dgm
|
||||
EM.stop
|
||||
end
|
||||
end
|
||||
|
||||
def test_datagrams
|
||||
$in = $out = ""
|
||||
EM.run {
|
||||
EM.open_datagram_socket "127.0.0.1", @port, TestDatagramServer
|
||||
EM.open_datagram_socket "127.0.0.1", 0, TestDatagramClient, @port
|
||||
}
|
||||
assert_equal( "1234567890", $in )
|
||||
assert_equal( "abcdefghij", $out )
|
||||
end
|
||||
|
||||
# XXX this test fails randomly...
|
||||
def _test_unix_domain
|
||||
fn = "/tmp/xxx.chain"
|
||||
EM.epoll
|
||||
EM.set_descriptor_table_size 60000
|
||||
EM.run {
|
||||
# The pure-Ruby version won't let us open the socket if the node already exists.
|
||||
# Not sure, that actually may be correct and the compiled version is wrong.
|
||||
# Pure Ruby also oddly won't let us make that many connections. This test used
|
||||
# to run 100 times. Not sure where that lower connection-limit is coming from in
|
||||
# pure Ruby.
|
||||
# Let's not sweat the Unix-ness of the filename, since this test can't possibly
|
||||
# work on Windows anyway.
|
||||
#
|
||||
File.unlink(fn) if File.exist?(fn)
|
||||
EM.start_unix_domain_server fn, TestEchoServer
|
||||
$n = 0
|
||||
$max = 0
|
||||
50.times {
|
||||
EM.connect_unix_domain(fn, TestEchoClient) {$n += 1}
|
||||
}
|
||||
EM::add_timer(1) { $stderr.puts("test_unix_domain timed out!"); EM::stop }
|
||||
}
|
||||
assert_equal(0, $n)
|
||||
assert_equal(50, $max)
|
||||
ensure
|
||||
File.unlink(fn) if File.exist?(fn)
|
||||
end
|
||||
|
||||
def test_attach_detach
|
||||
EM.epoll
|
||||
EM.run {
|
||||
EM.add_timer(0.01) { EM.stop }
|
||||
|
||||
r, _ = IO.pipe
|
||||
|
||||
# This tests a regression where detach in the same tick as attach crashes EM
|
||||
EM.watch(r) do |connection|
|
||||
connection.detach
|
||||
end
|
||||
}
|
||||
|
||||
assert true
|
||||
end
|
||||
end
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
require 'em_test_helper'
|
||||
|
||||
class TestErrorHandler < Test::Unit::TestCase
|
||||
def setup
|
||||
@exception = Class.new(StandardError)
|
||||
end
|
||||
|
||||
def test_error_handler
|
||||
error = nil
|
||||
|
||||
EM.error_handler{ |e|
|
||||
error = e
|
||||
EM.error_handler(nil)
|
||||
EM.stop
|
||||
}
|
||||
|
||||
assert_nothing_raised do
|
||||
EM.run{
|
||||
EM.add_timer(0){
|
||||
raise @exception, 'test'
|
||||
}
|
||||
}
|
||||
end
|
||||
|
||||
assert_equal error.class, @exception
|
||||
assert_equal error.message, 'test'
|
||||
end
|
||||
|
||||
def test_without_error_handler
|
||||
assert_raise @exception do
|
||||
EM.run{
|
||||
EM.add_timer(0){
|
||||
raise @exception, 'test'
|
||||
}
|
||||
}
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,43 @@
|
||||
require 'em_test_helper'
|
||||
|
||||
class TestSomeExceptions < Test::Unit::TestCase
|
||||
class DoomedConnectionError < StandardError
|
||||
end
|
||||
class DoomedConnection < EventMachine::Connection
|
||||
def unbind
|
||||
raise DoomedConnectionError
|
||||
end
|
||||
end
|
||||
|
||||
# Read the commentary in EM#run.
|
||||
# This test exercises the ensure block in #run that makes sure
|
||||
# EM#release_machine gets called even if an exception is
|
||||
# thrown within the user code. Without the ensured call to release_machine,
|
||||
# the second call to EM#run will fail with a C++ exception
|
||||
# because the machine wasn't cleaned up properly.
|
||||
|
||||
def test_a
|
||||
assert_raises(RuntimeError) {
|
||||
EM.run {
|
||||
raise "some exception"
|
||||
}
|
||||
}
|
||||
end
|
||||
|
||||
def test_b
|
||||
assert_raises(RuntimeError) {
|
||||
EM.run {
|
||||
raise "some exception"
|
||||
}
|
||||
}
|
||||
end
|
||||
|
||||
def test_exception_on_unbind
|
||||
assert_raises(DoomedConnectionError) {
|
||||
EM.run {
|
||||
EM.connect("localhost", 8888, DoomedConnection)
|
||||
}
|
||||
}
|
||||
end
|
||||
|
||||
end
|
||||
@@ -0,0 +1,85 @@
|
||||
require 'em_test_helper'
|
||||
require 'tempfile'
|
||||
|
||||
class TestFileWatch < Test::Unit::TestCase
|
||||
if windows?
|
||||
def test_watch_file_raises_unsupported_error
|
||||
assert_raises(EM::Unsupported) do
|
||||
EM.run do
|
||||
file = Tempfile.new("fake_file")
|
||||
EM.watch_file(file.path)
|
||||
end
|
||||
end
|
||||
end
|
||||
elsif EM.respond_to? :watch_filename
|
||||
module FileWatcher
|
||||
def file_modified
|
||||
$modified = true
|
||||
end
|
||||
def file_deleted
|
||||
$deleted = true
|
||||
end
|
||||
def unbind
|
||||
$unbind = true
|
||||
EM.stop
|
||||
end
|
||||
end
|
||||
|
||||
def setup
|
||||
EM.kqueue = true if EM.kqueue?
|
||||
end
|
||||
|
||||
def teardown
|
||||
EM.kqueue = false if EM.kqueue?
|
||||
end
|
||||
|
||||
def test_events
|
||||
omit_if(solaris?)
|
||||
EM.run{
|
||||
file = Tempfile.new('em-watch')
|
||||
$tmp_path = file.path
|
||||
|
||||
# watch it
|
||||
watch = EM.watch_file(file.path, FileWatcher)
|
||||
$path = watch.path
|
||||
|
||||
# modify it
|
||||
File.open(file.path, 'w'){ |f| f.puts 'hi' }
|
||||
|
||||
# delete it
|
||||
EM.add_timer(0.01){ file.close; file.delete }
|
||||
}
|
||||
|
||||
assert_equal($path, $tmp_path)
|
||||
assert($modified)
|
||||
assert($deleted)
|
||||
assert($unbind)
|
||||
end
|
||||
|
||||
# Refer: https://github.com/eventmachine/eventmachine/issues/512
|
||||
def test_invalid_signature
|
||||
# This works fine with kqueue, only fails with linux inotify.
|
||||
omit_if(EM.kqueue?)
|
||||
|
||||
EM.run {
|
||||
file = Tempfile.new('foo')
|
||||
|
||||
w1 = EventMachine.watch_file(file.path)
|
||||
w2 = EventMachine.watch_file(file.path)
|
||||
|
||||
assert_raise EventMachine::InvalidSignature do
|
||||
w2.stop_watching
|
||||
end
|
||||
|
||||
EM.stop
|
||||
}
|
||||
end
|
||||
else
|
||||
warn "EM.watch_file not implemented, skipping tests in #{__FILE__}"
|
||||
|
||||
# Because some rubies will complain if a TestCase class has no tests
|
||||
def test_em_watch_file_unsupported
|
||||
assert true
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,75 @@
|
||||
require 'em_test_helper'
|
||||
|
||||
class TestFork < Test::Unit::TestCase
|
||||
|
||||
def test_fork_safe
|
||||
omit_if(jruby?)
|
||||
omit_if(windows?)
|
||||
|
||||
fork_pid = nil
|
||||
read, write = IO.pipe
|
||||
EM.run do
|
||||
fork_pid = fork do
|
||||
write.puts "forked"
|
||||
EM.run do
|
||||
EM.next_tick do
|
||||
write.puts "EM ran"
|
||||
EM.stop
|
||||
end
|
||||
end
|
||||
end
|
||||
EM.stop
|
||||
end
|
||||
|
||||
sleep 0.1
|
||||
begin
|
||||
Timeout::timeout 1 do
|
||||
assert_equal "forked\n", read.readline
|
||||
assert_equal "EM ran\n", read.readline
|
||||
end
|
||||
rescue Timeout::Error
|
||||
Process.kill 'TERM', fork_pid
|
||||
flunk "Timeout waiting for next_tick in new fork reactor"
|
||||
end
|
||||
ensure
|
||||
read.close rescue nil
|
||||
write.close rescue nil
|
||||
end
|
||||
|
||||
def test_fork_reactor
|
||||
omit_if(jruby?)
|
||||
omit_if(windows?)
|
||||
|
||||
fork_pid = nil
|
||||
read, write = IO.pipe
|
||||
EM.run do
|
||||
EM.defer do
|
||||
write.puts Process.pid
|
||||
EM.defer do
|
||||
EM.stop
|
||||
end
|
||||
end
|
||||
fork_pid = EM.fork_reactor do
|
||||
EM.defer do
|
||||
write.puts Process.pid
|
||||
EM.stop
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
sleep 0.1
|
||||
begin
|
||||
Timeout::timeout 1 do
|
||||
assert_equal Process.pid.to_s, read.readline.chomp
|
||||
assert_equal fork_pid.to_s, read.readline.chomp
|
||||
end
|
||||
rescue Timeout::Error
|
||||
Process.kill 'TERM', fork_pid
|
||||
flunk "Timeout waiting for deferred block in fork_reactor"
|
||||
end
|
||||
ensure
|
||||
read.close rescue nil
|
||||
write.close rescue nil
|
||||
end
|
||||
|
||||
end
|
||||
@@ -0,0 +1,170 @@
|
||||
require 'em_test_helper'
|
||||
|
||||
class TestFutures < Test::Unit::TestCase
|
||||
|
||||
def setup
|
||||
end
|
||||
|
||||
def teardown
|
||||
end
|
||||
|
||||
def test_future
|
||||
assert_equal(100, EM::Deferrable.future(100) )
|
||||
|
||||
p1 = proc { 100 + 1 }
|
||||
assert_equal(101, EM::Deferrable.future(p1) )
|
||||
end
|
||||
|
||||
class MyFuture
|
||||
include EM::Deferrable
|
||||
def initialize *args
|
||||
super
|
||||
set_deferred_status :succeeded, 40
|
||||
end
|
||||
end
|
||||
|
||||
class MyErrorFuture
|
||||
include EM::Deferrable
|
||||
def initialize *args
|
||||
super
|
||||
set_deferred_status :failed, 41
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
def test_future_1
|
||||
# Call future with one additional argument and it will be treated as a callback.
|
||||
def my_future
|
||||
MyFuture.new
|
||||
end
|
||||
|
||||
value = nil
|
||||
EM::Deferrable.future my_future, proc {|v| value=v}
|
||||
assert_equal( 40, value )
|
||||
end
|
||||
|
||||
|
||||
def test_future_2
|
||||
# Call future with two additional arguments and they will be treated as a callback
|
||||
# and an errback.
|
||||
value = nil
|
||||
EM::Deferrable.future MyErrorFuture.new, nil, proc {|v| value=v}
|
||||
assert_equal( 41, value )
|
||||
end
|
||||
|
||||
|
||||
def test_future_3
|
||||
# Call future with no additional arguments but with a block, and the block will be
|
||||
# treated as a callback.
|
||||
value = nil
|
||||
EM::Deferrable.future MyFuture.new do |v|
|
||||
value=v
|
||||
end
|
||||
assert_equal( 40, value )
|
||||
end
|
||||
|
||||
|
||||
class RecursiveCallback
|
||||
include EM::Deferrable
|
||||
end
|
||||
|
||||
# A Deferrable callback can call #set_deferred_status to change the values
|
||||
# passed to subsequent callbacks.
|
||||
#
|
||||
def test_recursive_callbacks
|
||||
n = 0 # counter assures that all the tests actually run.
|
||||
rc = RecursiveCallback.new
|
||||
rc.callback {|a|
|
||||
assert_equal(100, a)
|
||||
n += 1
|
||||
rc.set_deferred_status :succeeded, 101, 101
|
||||
}
|
||||
rc.callback {|a,b|
|
||||
assert_equal(101, a)
|
||||
assert_equal(101, b)
|
||||
n += 1
|
||||
rc.set_deferred_status :succeeded, 102, 102, 102
|
||||
}
|
||||
rc.callback {|a,b,c|
|
||||
assert_equal(102, a)
|
||||
assert_equal(102, b)
|
||||
assert_equal(102, c)
|
||||
n += 1
|
||||
}
|
||||
rc.set_deferred_status :succeeded, 100
|
||||
assert_equal(3, n)
|
||||
end
|
||||
|
||||
def test_syntactic_sugar
|
||||
rc = RecursiveCallback.new
|
||||
rc.set_deferred_success 100
|
||||
rc.set_deferred_failure 200
|
||||
end
|
||||
|
||||
# It doesn't raise an error to set deferred status more than once.
|
||||
# In fact, this is a desired and useful idiom when it happens INSIDE
|
||||
# a callback or errback.
|
||||
# However, it's less useful otherwise, and in fact would generally be
|
||||
# indicative of a programming error. However, we would like to be resistant
|
||||
# to such errors. So whenever we set deferred status, we also clear BOTH
|
||||
# stacks of handlers.
|
||||
#
|
||||
def test_double_calls
|
||||
s = 0
|
||||
e = 0
|
||||
|
||||
d = EM::DefaultDeferrable.new
|
||||
d.callback {s += 1}
|
||||
d.errback {e += 1}
|
||||
|
||||
d.succeed # We expect the callback to be called, and the errback to be DISCARDED.
|
||||
d.fail # Presumably an error. We expect the errback NOT to be called.
|
||||
d.succeed # We expect the callback to have been discarded and NOT to be called again.
|
||||
|
||||
assert_equal(1, s)
|
||||
assert_equal(0, e)
|
||||
end
|
||||
|
||||
# Adding a callback to a Deferrable that is already in a success state executes the callback
|
||||
# immediately. The same applies to a an errback added to an already-failed Deferrable.
|
||||
# HOWEVER, we expect NOT to be able to add errbacks to succeeded Deferrables, or callbacks
|
||||
# to failed ones.
|
||||
#
|
||||
# We illustrate this with a rather contrived test. The test calls #fail after #succeed,
|
||||
# which ordinarily would not happen in a real program.
|
||||
#
|
||||
# What we're NOT attempting to specify is what happens if a Deferrable is succeeded and then
|
||||
# failed (or vice-versa). Should we then be able to add callbacks/errbacks of the appropriate
|
||||
# type for immediate execution? For now at least, the official answer is "don't do that."
|
||||
#
|
||||
def test_delayed_callbacks
|
||||
s1 = 0
|
||||
s2 = 0
|
||||
e = 0
|
||||
|
||||
d = EM::DefaultDeferrable.new
|
||||
d.callback {s1 += 1}
|
||||
|
||||
d.succeed # Triggers and discards the callback.
|
||||
|
||||
d.callback {s2 += 1} # This callback is executed immediately and discarded.
|
||||
|
||||
d.errback {e += 1} # This errback should be DISCARDED and never execute.
|
||||
d.fail # To prove it, fail and assert e is 0
|
||||
|
||||
assert_equal( [1,1], [s1,s2] )
|
||||
assert_equal( 0, e )
|
||||
end
|
||||
|
||||
def test_timeout
|
||||
n = 0
|
||||
EM.run {
|
||||
d = EM::DefaultDeferrable.new
|
||||
d.callback {n = 1; EM.stop}
|
||||
d.errback {n = 2; EM.stop}
|
||||
d.timeout(0.01)
|
||||
}
|
||||
assert_equal( 2, n )
|
||||
end
|
||||
|
||||
end
|
||||
@@ -0,0 +1,35 @@
|
||||
require 'em_test_helper'
|
||||
|
||||
class TestHandlerCheck < Test::Unit::TestCase
|
||||
|
||||
class Foo < EM::Connection; end;
|
||||
module TestModule; end;
|
||||
|
||||
def test_with_correct_class
|
||||
assert_nothing_raised do
|
||||
EM.run {
|
||||
EM.connect("127.0.0.1", 80, Foo)
|
||||
EM.stop_event_loop
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
def test_with_incorrect_class
|
||||
assert_raise(ArgumentError) do
|
||||
EM.run {
|
||||
EM.connect("127.0.0.1", 80, String)
|
||||
EM.stop_event_loop
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
def test_with_module
|
||||
assert_nothing_raised do
|
||||
EM.run {
|
||||
EM.connect("127.0.0.1", 80, TestModule)
|
||||
EM.stop_event_loop
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
@@ -0,0 +1,155 @@
|
||||
require 'em_test_helper'
|
||||
|
||||
class TestHeaderAndContentProtocol < Test::Unit::TestCase
|
||||
|
||||
class SimpleTest < EM::P::HeaderAndContentProtocol
|
||||
attr_reader :first_header, :my_headers, :request
|
||||
|
||||
def receive_first_header_line hdr
|
||||
@first_header ||= []
|
||||
@first_header << hdr
|
||||
end
|
||||
def receive_headers hdrs
|
||||
@my_headers ||= []
|
||||
@my_headers << hdrs
|
||||
end
|
||||
def receive_request hdrs, content
|
||||
@request ||= []
|
||||
@request << [hdrs, content]
|
||||
end
|
||||
end
|
||||
|
||||
class StopOnUnbind < EM::Connection
|
||||
def unbind
|
||||
EM.add_timer(0.01) { EM.stop }
|
||||
end
|
||||
end
|
||||
|
||||
def setup
|
||||
@port = next_port
|
||||
end
|
||||
|
||||
def test_no_content
|
||||
the_connection = nil
|
||||
EM.run {
|
||||
EM.start_server( "127.0.0.1", @port, SimpleTest ) do |conn|
|
||||
the_connection = conn
|
||||
end
|
||||
setup_timeout
|
||||
|
||||
EM.connect "127.0.0.1", @port, StopOnUnbind do |c|
|
||||
c.send_data [ "aaa\n", "bbb\r\n", "ccc\n", "\n" ].join
|
||||
c.close_connection_after_writing
|
||||
end
|
||||
}
|
||||
assert_equal( ["aaa"], the_connection.first_header )
|
||||
assert_equal( [%w(aaa bbb ccc)], the_connection.my_headers )
|
||||
assert_equal( [[%w(aaa bbb ccc), ""]], the_connection.request )
|
||||
end
|
||||
|
||||
def test_content
|
||||
the_connection = nil
|
||||
content = "A" * 50
|
||||
headers = ["aaa", "bbb", "Content-length: #{content.length}", "ccc"]
|
||||
EM.run {
|
||||
EM.start_server( "127.0.0.1", @port, SimpleTest ) do |conn|
|
||||
the_connection = conn
|
||||
end
|
||||
setup_timeout
|
||||
|
||||
EM.connect "127.0.0.1", @port, StopOnUnbind do |c|
|
||||
headers.each { |h| c.send_data "#{h}\r\n" }
|
||||
c.send_data "\n"
|
||||
c.send_data content
|
||||
c.close_connection_after_writing
|
||||
end
|
||||
}
|
||||
assert_equal( ["aaa"], the_connection.first_header )
|
||||
assert_equal( [headers], the_connection.my_headers )
|
||||
assert_equal( [[headers, content]], the_connection.request )
|
||||
end
|
||||
|
||||
def test_several_requests
|
||||
the_connection = nil
|
||||
content = "A" * 50
|
||||
headers = ["aaa", "bbb", "Content-length: #{content.length}", "ccc"]
|
||||
EM.run {
|
||||
EM.start_server( "127.0.0.1", @port, SimpleTest ) do |conn|
|
||||
the_connection = conn
|
||||
end
|
||||
setup_timeout
|
||||
|
||||
EM.connect( "127.0.0.1", @port, StopOnUnbind ) do |c|
|
||||
5.times do
|
||||
headers.each { |h| c.send_data "#{h}\r\n" }
|
||||
c.send_data "\n"
|
||||
c.send_data content
|
||||
end
|
||||
c.close_connection_after_writing
|
||||
end
|
||||
}
|
||||
assert_equal( ["aaa"] * 5, the_connection.first_header )
|
||||
assert_equal( [headers] * 5, the_connection.my_headers )
|
||||
assert_equal( [[headers, content]] * 5, the_connection.request )
|
||||
end
|
||||
|
||||
|
||||
# def x_test_multiple_content_length_headers
|
||||
# # This is supposed to throw a RuntimeError but it throws a C++ exception instead.
|
||||
# the_connection = nil
|
||||
# content = "A" * 50
|
||||
# headers = ["aaa", "bbb", ["Content-length: #{content.length}"]*2, "ccc"].flatten
|
||||
# EM.run {
|
||||
# EM.start_server( "127.0.0.1", @port, SimpleTest ) do |conn|
|
||||
# the_connection = conn
|
||||
# end
|
||||
# EM.add_timer(4) {raise "test timed out"}
|
||||
# test_proc = proc {
|
||||
# t = TCPSocket.new "127.0.0.1", @port
|
||||
# headers.each {|h| t.write "#{h}\r\n" }
|
||||
# t.write "\n"
|
||||
# t.write content
|
||||
# t.close
|
||||
# }
|
||||
# EM.defer test_proc, proc {
|
||||
# EM.stop
|
||||
# }
|
||||
# }
|
||||
# end
|
||||
|
||||
def test_interpret_headers
|
||||
the_connection = nil
|
||||
content = "A" * 50
|
||||
headers = [
|
||||
"GET / HTTP/1.0",
|
||||
"Accept: aaa",
|
||||
"User-Agent: bbb",
|
||||
"Host: ccc",
|
||||
"x-tempest-header:ddd"
|
||||
]
|
||||
|
||||
EM.run {
|
||||
EM.start_server( "127.0.0.1", @port, SimpleTest ) do |conn|
|
||||
the_connection = conn
|
||||
end
|
||||
setup_timeout
|
||||
|
||||
EM.connect( "127.0.0.1", @port, StopOnUnbind ) do |c|
|
||||
headers.each { |h| c.send_data "#{h}\r\n" }
|
||||
c.send_data "\n"
|
||||
c.send_data content
|
||||
c.close_connection_after_writing
|
||||
end
|
||||
}
|
||||
|
||||
hsh = the_connection.headers_2_hash( the_connection.my_headers.shift )
|
||||
expect = {
|
||||
:accept => "aaa",
|
||||
:user_agent => "bbb",
|
||||
:host => "ccc",
|
||||
:x_tempest_header => "ddd"
|
||||
}
|
||||
assert_equal(expect, hsh)
|
||||
end
|
||||
|
||||
end
|
||||
@@ -0,0 +1,238 @@
|
||||
require 'em_test_helper'
|
||||
|
||||
class TestHttpClient < Test::Unit::TestCase
|
||||
|
||||
def setup
|
||||
@port = next_port
|
||||
end
|
||||
|
||||
#-------------------------------------
|
||||
|
||||
def test_http_client
|
||||
ok = false
|
||||
EM.run {
|
||||
c = silent { EM::P::HttpClient.send :request, :host => "www.google.com", :port => 80 }
|
||||
c.callback {
|
||||
ok = true
|
||||
c.close_connection
|
||||
EM.stop
|
||||
}
|
||||
c.errback {EM.stop} # necessary, otherwise a failure blocks the test suite forever.
|
||||
}
|
||||
assert ok
|
||||
end
|
||||
|
||||
#-------------------------------------
|
||||
|
||||
def test_http_client_1
|
||||
ok = false
|
||||
EM.run {
|
||||
c = silent { EM::P::HttpClient.send :request, :host => "www.google.com", :port => 80 }
|
||||
c.callback {
|
||||
ok = true
|
||||
c.close_connection
|
||||
EM.stop
|
||||
}
|
||||
c.errback {EM.stop}
|
||||
}
|
||||
assert ok
|
||||
end
|
||||
|
||||
#-------------------------------------
|
||||
|
||||
def test_http_client_2
|
||||
ok = false
|
||||
EM.run {
|
||||
c = silent { EM::P::HttpClient.send :request, :host => "www.google.com", :port => 80 }
|
||||
c.callback {
|
||||
ok = true
|
||||
c.close_connection
|
||||
EM.stop
|
||||
}
|
||||
c.errback {EM.stop}
|
||||
}
|
||||
assert ok
|
||||
end
|
||||
|
||||
|
||||
#-----------------------------------------
|
||||
|
||||
# Test a server that returns a page with a zero content-length.
|
||||
# This caused an early version of the HTTP client not to generate a response,
|
||||
# causing this test to hang. Observe, there was no problem with responses
|
||||
# lacking a content-length, just when the content-length was zero.
|
||||
#
|
||||
class EmptyContent < EM::Connection
|
||||
def initialize *args
|
||||
super
|
||||
end
|
||||
def receive_data data
|
||||
send_data "HTTP/1.0 404 ...\r\nContent-length: 0\r\n\r\n"
|
||||
close_connection_after_writing
|
||||
end
|
||||
end
|
||||
|
||||
def test_http_empty_content
|
||||
ok = false
|
||||
EM.run {
|
||||
EM.start_server "127.0.0.1", @port, EmptyContent
|
||||
c = silent { EM::P::HttpClient.send :request, :host => "127.0.0.1", :port => @port }
|
||||
c.callback {
|
||||
ok = true
|
||||
c.close_connection
|
||||
EM.stop
|
||||
}
|
||||
}
|
||||
assert ok
|
||||
end
|
||||
|
||||
|
||||
#---------------------------------------
|
||||
|
||||
class PostContent < EM::P::LineAndTextProtocol
|
||||
def initialize *args
|
||||
super
|
||||
@lines = []
|
||||
end
|
||||
def receive_line line
|
||||
if line.length > 0
|
||||
@lines << line
|
||||
else
|
||||
process_headers
|
||||
end
|
||||
end
|
||||
def receive_binary_data data
|
||||
@post_content = data
|
||||
send_response
|
||||
end
|
||||
def process_headers
|
||||
if @lines.first =~ /\APOST ([^\s]+) HTTP\/1.1\Z/
|
||||
@uri = $1.dup
|
||||
else
|
||||
raise "bad request"
|
||||
end
|
||||
|
||||
@lines.each {|line|
|
||||
if line =~ /\AContent-length:\s*(\d+)\Z/i
|
||||
@content_length = $1.dup.to_i
|
||||
elsif line =~ /\AContent-type:\s*(\d+)\Z/i
|
||||
@content_type = $1.dup
|
||||
end
|
||||
}
|
||||
|
||||
raise "invalid content length" unless @content_length
|
||||
set_binary_mode @content_length
|
||||
end
|
||||
def send_response
|
||||
send_data "HTTP/1.1 200 ...\r\nConnection: close\r\nContent-length: 10\r\nContent-type: text/html\r\n\r\n0123456789"
|
||||
close_connection_after_writing
|
||||
end
|
||||
end
|
||||
|
||||
# TODO, this is WRONG. The handler is asserting an HTTP 1.1 request, but the client
|
||||
# is sending a 1.0 request. Gotta fix the client
|
||||
def test_post
|
||||
response = nil
|
||||
EM.run {
|
||||
EM.start_server '127.0.0.1', @port, PostContent
|
||||
setup_timeout(2)
|
||||
c = silent { EM::P::HttpClient.request(
|
||||
:host => '127.0.0.1',
|
||||
:port => @port,
|
||||
:method => :post,
|
||||
:request => "/aaa",
|
||||
:content => "XYZ",
|
||||
:content_type => "text/plain"
|
||||
)}
|
||||
c.callback {|r|
|
||||
response = r
|
||||
EM.stop
|
||||
}
|
||||
}
|
||||
|
||||
assert_equal( 200, response[:status] )
|
||||
assert_equal( "0123456789", response[:content] )
|
||||
end
|
||||
|
||||
|
||||
# TODO, need a more intelligent cookie tester.
|
||||
# In fact, this whole test-harness needs a beefier server implementation.
|
||||
def test_cookie
|
||||
ok = false
|
||||
EM.run {
|
||||
c = silent { EM::Protocols::HttpClient.send :request, :host => "www.google.com", :port => 80, :cookie=>"aaa=bbb" }
|
||||
c.callback {
|
||||
ok = true
|
||||
c.close_connection
|
||||
EM.stop
|
||||
}
|
||||
c.errback {EM.stop}
|
||||
}
|
||||
assert ok
|
||||
end
|
||||
|
||||
# We can tell the client to send an HTTP/1.0 request (default is 1.1).
|
||||
# This is useful for suppressing chunked responses until those are working.
|
||||
def test_version_1_0
|
||||
ok = false
|
||||
EM.run {
|
||||
c = silent { EM::P::HttpClient.request(
|
||||
:host => "www.google.com",
|
||||
:port => 80,
|
||||
:version => "1.0"
|
||||
)}
|
||||
c.callback {
|
||||
ok = true
|
||||
c.close_connection
|
||||
EM.stop
|
||||
}
|
||||
c.errback {EM.stop}
|
||||
}
|
||||
assert ok
|
||||
end
|
||||
|
||||
#-----------------------------------------
|
||||
|
||||
# Test a server that returns chunked encoding
|
||||
#
|
||||
class ChunkedEncodingContent < EventMachine::Connection
|
||||
def initialize *args
|
||||
super
|
||||
end
|
||||
def receive_data data
|
||||
send_data ["HTTP/1.1 200 OK",
|
||||
"Server: nginx/0.7.67",
|
||||
"Date: Sat, 23 Oct 2010 16:41:32 GMT",
|
||||
"Content-Type: application/json",
|
||||
"Transfer-Encoding: chunked",
|
||||
"Connection: keep-alive",
|
||||
"",
|
||||
"1800",
|
||||
"chunk1" * 1024,
|
||||
"5a",
|
||||
"chunk2" * 15,
|
||||
"0",
|
||||
""].join("\r\n")
|
||||
close_connection_after_writing
|
||||
end
|
||||
end
|
||||
|
||||
def test_http_chunked_encoding_content
|
||||
ok = false
|
||||
EM.run {
|
||||
EM.start_server "127.0.0.1", @port, ChunkedEncodingContent
|
||||
c = silent { EM::P::HttpClient.send :request, :host => "127.0.0.1", :port => @port }
|
||||
c.callback { |result|
|
||||
if result[:content] == "chunk1" * 1024 + "chunk2" * 15
|
||||
ok = true
|
||||
end
|
||||
c.close_connection
|
||||
EM.stop
|
||||
}
|
||||
}
|
||||
assert ok
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
|
||||
@@ -0,0 +1,128 @@
|
||||
require 'em_test_helper'
|
||||
|
||||
class TestHttpClient2 < Test::Unit::TestCase
|
||||
class TestServer < EM::Connection
|
||||
end
|
||||
|
||||
def setup
|
||||
@port = next_port
|
||||
end
|
||||
|
||||
# #connect returns an object which has made a connection to an HTTP server
|
||||
# and exposes methods for making HTTP requests on that connection.
|
||||
# #connect can take either a pair of parameters (a host and a port),
|
||||
# or a single parameter which is a Hash.
|
||||
#
|
||||
def test_connect
|
||||
EM.run {
|
||||
setup_timeout(1)
|
||||
EM.start_server '127.0.0.1', @port, TestServer
|
||||
silent do
|
||||
EM::P::HttpClient2.connect '127.0.0.1', @port
|
||||
EM::P::HttpClient2.connect( :host=>'127.0.0.1', :port=>@port )
|
||||
end
|
||||
EM.stop
|
||||
}
|
||||
end
|
||||
|
||||
def test_bad_port
|
||||
EM.run {
|
||||
setup_timeout(1)
|
||||
EM.start_server '127.0.0.1', @port, TestServer
|
||||
assert_raises( ArgumentError ) {
|
||||
silent { EM::P::HttpClient2.connect '127.0.0.1', "xxx" }
|
||||
}
|
||||
EM.stop
|
||||
}
|
||||
end
|
||||
|
||||
def test_bad_server
|
||||
err = nil
|
||||
EM.run {
|
||||
setup_timeout(1)
|
||||
http = silent { EM::P::HttpClient2.connect '127.0.0.1', 9999 }
|
||||
d = http.get "/"
|
||||
d.errback { err = true; d.internal_error; EM.stop }
|
||||
}
|
||||
assert(err)
|
||||
end
|
||||
|
||||
def test_get
|
||||
content = nil
|
||||
EM.run {
|
||||
setup_timeout(1)
|
||||
http = silent { EM::P::HttpClient2.connect :host => "google.com", :port => 80, :version => '1.0' }
|
||||
d = http.get "/"
|
||||
d.callback {
|
||||
content = d.content
|
||||
EM.stop
|
||||
}
|
||||
}
|
||||
assert(content)
|
||||
end
|
||||
|
||||
# Not a pipelined request because we wait for one response before we request the next.
|
||||
# XXX this test is broken because it sends the second request to the first connection
|
||||
# XXX right before the connection closes
|
||||
def _test_get_multiple
|
||||
content = nil
|
||||
EM.run {
|
||||
setup_timeout(1)
|
||||
http = silent { EM::P::HttpClient2.connect "google.com", :version => '1.0' }
|
||||
d = http.get "/"
|
||||
d.callback {
|
||||
e = http.get "/"
|
||||
e.callback {
|
||||
content = e.content
|
||||
EM.stop
|
||||
}
|
||||
}
|
||||
}
|
||||
assert(content)
|
||||
end
|
||||
|
||||
def test_get_pipeline
|
||||
headers, headers2 = nil, nil
|
||||
EM.run {
|
||||
setup_timeout(1)
|
||||
http = silent { EM::P::HttpClient2.connect "google.com", 80 }
|
||||
d = http.get("/")
|
||||
d.callback {
|
||||
headers = d.headers
|
||||
}
|
||||
e = http.get("/")
|
||||
e.callback {
|
||||
headers2 = e.headers
|
||||
}
|
||||
EM.tick_loop { EM.stop if headers && headers2 }
|
||||
EM.add_timer(1) { EM.stop }
|
||||
}
|
||||
assert(headers)
|
||||
assert(headers2)
|
||||
end
|
||||
|
||||
def test_authheader
|
||||
EM.run {
|
||||
setup_timeout(1)
|
||||
EM.start_server '127.0.0.1', @port, TestServer
|
||||
http = silent { EM::P::HttpClient2.connect '127.0.0.1', 18842 }
|
||||
d = http.get :url=>"/", :authorization=>"Basic xxx"
|
||||
d.callback {EM.stop}
|
||||
d.errback {EM.stop}
|
||||
}
|
||||
end
|
||||
|
||||
def test_https_get
|
||||
omit_unless(EM.ssl?)
|
||||
d = nil
|
||||
EM.run {
|
||||
setup_timeout(1)
|
||||
http = silent { EM::P::HttpClient2.connect :host => 'www.google.com', :port => 443, :ssl => true, :version => '1.0' }
|
||||
d = http.get "/"
|
||||
d.callback {EM.stop}
|
||||
d.errback {EM.stop}
|
||||
}
|
||||
assert_equal(200, d.status)
|
||||
end
|
||||
|
||||
end
|
||||
@@ -0,0 +1,31 @@
|
||||
require 'em_test_helper'
|
||||
|
||||
class TestIdleConnection < Test::Unit::TestCase
|
||||
def setup
|
||||
@port = next_port
|
||||
end
|
||||
|
||||
def test_idle_time
|
||||
omit_if(!EM.respond_to?(:get_idle_time))
|
||||
|
||||
a, b = nil, nil
|
||||
EM.run do
|
||||
EM.start_server '127.0.0.1', @port, Module.new
|
||||
conn = EM.connect '127.0.0.1', @port
|
||||
EM.add_timer(0.3) do
|
||||
a = conn.get_idle_time
|
||||
conn.send_data 'a'
|
||||
EM.next_tick do
|
||||
EM.next_tick do
|
||||
b = conn.get_idle_time
|
||||
conn.close_connection
|
||||
EM.stop
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
assert_in_delta 0.3, a, 0.1
|
||||
assert_in_delta 0, b, 0.1
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,54 @@
|
||||
require 'em_test_helper'
|
||||
|
||||
class TestInactivityTimeout < Test::Unit::TestCase
|
||||
|
||||
if EM.respond_to? :get_comm_inactivity_timeout
|
||||
def test_default
|
||||
EM.run {
|
||||
c = EM.connect("127.0.0.1", 54321)
|
||||
assert_equal 0.0, c.comm_inactivity_timeout
|
||||
EM.stop
|
||||
}
|
||||
end
|
||||
|
||||
def test_set_and_get
|
||||
EM.run {
|
||||
c = EM.connect("127.0.0.1", 54321)
|
||||
c.comm_inactivity_timeout = 2.5
|
||||
assert_equal 2.5, c.comm_inactivity_timeout
|
||||
EM.stop
|
||||
}
|
||||
end
|
||||
|
||||
def test_for_real
|
||||
start, finish = nil
|
||||
|
||||
timeout_handler = Module.new do
|
||||
define_method :unbind do
|
||||
finish = Time.now
|
||||
EM.stop
|
||||
end
|
||||
end
|
||||
|
||||
EM.run {
|
||||
setup_timeout
|
||||
EM.heartbeat_interval = 0.01
|
||||
EM.start_server("127.0.0.1", 12345)
|
||||
EM.add_timer(0.01) {
|
||||
start = Time.now
|
||||
c = EM.connect("127.0.0.1", 12345, timeout_handler)
|
||||
c.comm_inactivity_timeout = 0.02
|
||||
}
|
||||
}
|
||||
|
||||
assert_in_delta(0.02, (finish - start), 0.02)
|
||||
end
|
||||
else
|
||||
warn "EM.comm_inactivity_timeout not implemented, skipping tests in #{__FILE__}"
|
||||
|
||||
# Because some rubies will complain if a TestCase class has no tests
|
||||
def test_em_comm_inactivity_timeout_not_implemented
|
||||
assert true
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,95 @@
|
||||
require 'em_test_helper'
|
||||
|
||||
class TestIPv4 < Test::Unit::TestCase
|
||||
# Runs a TCP server in the local IPv4 address, connects to it and sends a specific data.
|
||||
# Timeout in 2 seconds.
|
||||
def test_ipv4_tcp_local_server
|
||||
omit_if(!Test::Unit::TestCase.public_ipv4?)
|
||||
|
||||
@@received_data = nil
|
||||
@local_port = next_port
|
||||
setup_timeout(2)
|
||||
|
||||
EM.run do
|
||||
EM::start_server(@@public_ipv4, @local_port) do |s|
|
||||
def s.receive_data data
|
||||
@@received_data = data
|
||||
EM.stop
|
||||
end
|
||||
end
|
||||
|
||||
EM::connect(@@public_ipv4, @local_port) do |c|
|
||||
c.send_data "ipv4/tcp"
|
||||
end
|
||||
end
|
||||
|
||||
assert_equal "ipv4/tcp", @@received_data
|
||||
end
|
||||
|
||||
# Runs a UDP server in the local IPv4 address, connects to it and sends a specific data.
|
||||
# Timeout in 2 seconds.
|
||||
def test_ipv4_udp_local_server
|
||||
omit_if(!Test::Unit::TestCase.public_ipv4?)
|
||||
|
||||
@@received_data = nil
|
||||
@local_port = next_port
|
||||
setup_timeout(2)
|
||||
|
||||
EM.run do
|
||||
EM::open_datagram_socket(@@public_ipv4, @local_port) do |s|
|
||||
def s.receive_data data
|
||||
@@received_data = data
|
||||
EM.stop
|
||||
end
|
||||
end
|
||||
|
||||
EM::open_datagram_socket(@@public_ipv4, next_port) do |c|
|
||||
c.send_datagram "ipv4/udp", @@public_ipv4, @local_port
|
||||
end
|
||||
end
|
||||
|
||||
assert_equal "ipv4/udp", @@received_data
|
||||
end
|
||||
|
||||
# Try to connect via TCP to an invalid IPv4. EM.connect should raise
|
||||
# EM::ConnectionError.
|
||||
def test_tcp_connect_to_invalid_ipv4
|
||||
omit_if(!Test::Unit::TestCase.public_ipv4?)
|
||||
|
||||
invalid_ipv4 = "9.9:9"
|
||||
|
||||
EM.run do
|
||||
begin
|
||||
error = nil
|
||||
EM.connect(invalid_ipv4, 1234)
|
||||
rescue => e
|
||||
error = e
|
||||
ensure
|
||||
EM.stop
|
||||
assert_equal EM::ConnectionError, (error && error.class)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Try to send a UDP datagram to an invalid IPv4. EM.send_datagram should raise
|
||||
# EM::ConnectionError.
|
||||
def test_udp_send_datagram_to_invalid_ipv4
|
||||
omit_if(!Test::Unit::TestCase.public_ipv4?)
|
||||
|
||||
invalid_ipv4 = "9.9:9"
|
||||
|
||||
EM.run do
|
||||
begin
|
||||
error = nil
|
||||
EM.open_datagram_socket(@@public_ipv4, next_port) do |c|
|
||||
c.send_datagram "hello", invalid_ipv4, 1234
|
||||
end
|
||||
rescue => e
|
||||
error = e
|
||||
ensure
|
||||
EM.stop
|
||||
assert_equal EM::ConnectionError, (error && error.class)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,107 @@
|
||||
require 'em_test_helper'
|
||||
|
||||
class TestIPv6 < Test::Unit::TestCase
|
||||
|
||||
if Test::Unit::TestCase.public_ipv6?
|
||||
|
||||
# Runs a TCP server in the local IPv6 address, connects to it and sends a specific data.
|
||||
# Timeout in 2 seconds.
|
||||
def test_ipv6_tcp_local_server
|
||||
@@received_data = nil
|
||||
@local_port = next_port
|
||||
setup_timeout(2)
|
||||
|
||||
EM.run do
|
||||
EM.start_server(@@public_ipv6, @local_port) do |s|
|
||||
def s.receive_data data
|
||||
@@received_data = data
|
||||
EM.stop
|
||||
end
|
||||
end
|
||||
|
||||
EM::connect(@@public_ipv6, @local_port) do |c|
|
||||
def c.unbind(reason)
|
||||
warn "unbind: #{reason.inspect}" if reason # XXX at least find out why it failed
|
||||
end
|
||||
c.send_data "ipv6/tcp"
|
||||
end
|
||||
end
|
||||
|
||||
assert_equal "ipv6/tcp", @@received_data
|
||||
end
|
||||
|
||||
# Runs a UDP server in the local IPv6 address, connects to it and sends a specific data.
|
||||
# Timeout in 2 seconds.
|
||||
def test_ipv6_udp_local_server
|
||||
@@received_data = nil
|
||||
@local_port = next_port
|
||||
@@remote_ip = nil
|
||||
setup_timeout(2)
|
||||
|
||||
EM.run do
|
||||
EM.open_datagram_socket(@@public_ipv6, @local_port) do |s|
|
||||
def s.receive_data data
|
||||
_port, @@remote_ip = Socket.unpack_sockaddr_in(get_peername)
|
||||
@@received_data = data
|
||||
EM.stop
|
||||
end
|
||||
end
|
||||
|
||||
EM.open_datagram_socket(@@public_ipv6, next_port) do |c|
|
||||
c.send_datagram "ipv6/udp", @@public_ipv6, @local_port
|
||||
end
|
||||
end
|
||||
assert_equal @@remote_ip, @@public_ipv6
|
||||
assert_equal "ipv6/udp", @@received_data
|
||||
end
|
||||
|
||||
# Try to connect via TCP to an invalid IPv6. EM.connect should raise
|
||||
# EM::ConnectionError.
|
||||
def test_tcp_connect_to_invalid_ipv6
|
||||
invalid_ipv6 = "1:A"
|
||||
|
||||
EM.run do
|
||||
begin
|
||||
error = nil
|
||||
EM.connect(invalid_ipv6, 1234)
|
||||
rescue => e
|
||||
error = e
|
||||
ensure
|
||||
EM.stop
|
||||
assert_equal EM::ConnectionError, (error && error.class)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Try to send a UDP datagram to an invalid IPv6. EM.send_datagram should raise
|
||||
# EM::ConnectionError.
|
||||
def test_udp_send_datagram_to_invalid_ipv6
|
||||
invalid_ipv6 = "1:A"
|
||||
|
||||
EM.run do
|
||||
begin
|
||||
error = nil
|
||||
EM.open_datagram_socket(@@public_ipv6, next_port) do |c|
|
||||
c.send_datagram "hello", invalid_ipv6, 1234
|
||||
end
|
||||
rescue => e
|
||||
error = e
|
||||
ensure
|
||||
EM.stop
|
||||
assert_equal EM::ConnectionError, (error && error.class)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
else
|
||||
warn "no IPv6 in this host, skipping tests in #{__FILE__}"
|
||||
|
||||
# Because some rubies will complain if a TestCase class has no tests.
|
||||
def test_ipv6_unavailable
|
||||
assert true
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
@@ -0,0 +1,118 @@
|
||||
require 'em_test_helper'
|
||||
|
||||
class TestIterator < Test::Unit::TestCase
|
||||
|
||||
# By default, format the time with tenths-of-seconds.
|
||||
# Some tests should ask for extra decimal places to ensure
|
||||
# that delays between iterations will receive a changed time.
|
||||
def get_time(n=1)
|
||||
time = EM.current_time
|
||||
time.strftime('%H:%M:%S.') + time.tv_usec.to_s[0, n]
|
||||
end
|
||||
|
||||
def test_default_concurrency
|
||||
items = {}
|
||||
list = 1..10
|
||||
EM.run {
|
||||
EM::Iterator.new(list).each( proc {|num,iter|
|
||||
time = get_time(3)
|
||||
items[time] ||= []
|
||||
items[time] << num
|
||||
EM::Timer.new(0.02) {iter.next}
|
||||
}, proc {EM.stop})
|
||||
}
|
||||
assert_equal(10, items.keys.size)
|
||||
assert_equal(list.to_a.sort, items.values.flatten.sort)
|
||||
end
|
||||
|
||||
def test_default_concurrency_with_a_proc
|
||||
items = {}
|
||||
list = (1..10).to_a
|
||||
original_list = list.dup
|
||||
EM.run {
|
||||
EM::Iterator.new(proc{list.pop || EM::Iterator::Stop}).each( proc {|num,iter|
|
||||
time = get_time(3)
|
||||
items[time] ||= []
|
||||
items[time] << num
|
||||
EM::Timer.new(0.02) {iter.next}
|
||||
}, proc {EM.stop})
|
||||
}
|
||||
assert_equal(10, items.keys.size)
|
||||
assert_equal(original_list.to_a.sort, items.values.flatten.sort)
|
||||
end
|
||||
|
||||
def test_concurrency_bigger_than_list_size
|
||||
items = {}
|
||||
list = [1,2,3]
|
||||
EM.run {
|
||||
EM::Iterator.new(list,10).each(proc {|num,iter|
|
||||
time = get_time
|
||||
items[time] ||= []
|
||||
items[time] << num
|
||||
EM::Timer.new(1) {iter.next}
|
||||
}, proc {EM.stop})
|
||||
}
|
||||
assert_equal(1, items.keys.size)
|
||||
assert_equal(list.to_a.sort, items.values.flatten.sort)
|
||||
end
|
||||
|
||||
def test_changing_concurrency_affects_active_iteration
|
||||
items = {}
|
||||
list = 1..25
|
||||
seen = 0
|
||||
EM.run {
|
||||
i = EM::Iterator.new(list,1)
|
||||
i.each(proc {|num,iter|
|
||||
time = get_time
|
||||
items[time] ||= []
|
||||
items[time] << num
|
||||
if (seen += 1) == 5
|
||||
# The first 5 items will be distinct times
|
||||
# The next 20 items will happen in 2 bursts
|
||||
i.concurrency = 10
|
||||
end
|
||||
EM::Timer.new(0.2) {iter.next}
|
||||
}, proc {EM.stop})
|
||||
}
|
||||
assert_in_delta(7, items.keys.size, 1)
|
||||
assert_equal(list.to_a.sort, items.values.flatten.sort)
|
||||
end
|
||||
|
||||
def test_map
|
||||
list = 100..150
|
||||
EM.run {
|
||||
EM::Iterator.new(list).map(proc{ |num,iter|
|
||||
EM.add_timer(0.01){ iter.return(num) }
|
||||
}, proc{ |results|
|
||||
assert_equal(list.to_a.size, results.size)
|
||||
EM.stop
|
||||
})
|
||||
}
|
||||
end
|
||||
|
||||
def test_inject
|
||||
omit_if(windows?)
|
||||
|
||||
list = %w[ pwd uptime uname date ]
|
||||
EM.run {
|
||||
EM::Iterator.new(list, 2).inject({}, proc{ |hash,cmd,iter|
|
||||
EM.system(cmd){ |output,status|
|
||||
hash[cmd] = status.exitstatus == 0 ? output.strip : nil
|
||||
iter.return(hash)
|
||||
}
|
||||
}, proc{ |results|
|
||||
assert_equal(results.keys.sort, list.sort)
|
||||
EM.stop
|
||||
})
|
||||
}
|
||||
end
|
||||
|
||||
def test_concurrency_is_0
|
||||
EM.run {
|
||||
assert_raise ArgumentError do
|
||||
EM::Iterator.new(1..5,0)
|
||||
end
|
||||
EM.stop
|
||||
}
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,28 @@
|
||||
require 'em_test_helper'
|
||||
|
||||
class TestKeyboardEvents < Test::Unit::TestCase
|
||||
|
||||
module KbHandler
|
||||
include EM::Protocols::LineText2
|
||||
def receive_line d
|
||||
EM::stop if d == "STOP"
|
||||
end
|
||||
end
|
||||
|
||||
# This test doesn't actually do anything useful but is here to
|
||||
# illustrate the usage. If you removed the timer and ran this test
|
||||
# by itself on a console, and then typed into the console, it would
|
||||
# work.
|
||||
# I don't know how to get the test harness to simulate actual keystrokes.
|
||||
# When someone figures that out, then we can make this a real test.
|
||||
#
|
||||
def test_kb
|
||||
omit_if(jruby?)
|
||||
omit_if(!$stdout.tty?) # don't run the test unless it stands a chance of validity.
|
||||
EM.run do
|
||||
EM.open_keyboard KbHandler
|
||||
EM::Timer.new(1) { EM.stop }
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
@@ -0,0 +1,33 @@
|
||||
require 'em_test_helper'
|
||||
|
||||
class TestLineProtocol < Test::Unit::TestCase
|
||||
class LineProtocolTestClass
|
||||
include EM::Protocols::LineProtocol
|
||||
|
||||
def lines
|
||||
@lines ||= []
|
||||
end
|
||||
|
||||
def receive_line(line)
|
||||
lines << line
|
||||
end
|
||||
end
|
||||
|
||||
def setup
|
||||
@proto = LineProtocolTestClass.new
|
||||
end
|
||||
|
||||
def test_simple_split_line
|
||||
@proto.receive_data("this is")
|
||||
assert_equal([], @proto.lines)
|
||||
|
||||
@proto.receive_data(" a test\n")
|
||||
assert_equal(["this is a test"], @proto.lines)
|
||||
end
|
||||
|
||||
def test_simple_lines
|
||||
@proto.receive_data("aaa\nbbb\r\nccc\nddd")
|
||||
assert_equal(%w(aaa bbb ccc), @proto.lines)
|
||||
end
|
||||
|
||||
end
|
||||
@@ -0,0 +1,155 @@
|
||||
require 'em_test_helper'
|
||||
|
||||
class TestLineAndTextProtocol < Test::Unit::TestCase
|
||||
|
||||
class TLP_LineBuffer < EM::P::LineAndTextProtocol
|
||||
attr_reader :line_buffer
|
||||
|
||||
def initialize
|
||||
super
|
||||
@line_buffer = []
|
||||
end
|
||||
|
||||
def receive_line line
|
||||
@line_buffer << line
|
||||
end
|
||||
end
|
||||
|
||||
module StopClient
|
||||
def set_receive_data(&blk)
|
||||
@rdb = blk
|
||||
end
|
||||
|
||||
def receive_data data
|
||||
@rdb.call(data) if @rdb
|
||||
end
|
||||
|
||||
def unbind
|
||||
EM.add_timer(0.1) { EM.stop }
|
||||
end
|
||||
end
|
||||
|
||||
def setup
|
||||
@port = next_port
|
||||
end
|
||||
|
||||
def test_simple_lines
|
||||
conn = nil
|
||||
EM.run {
|
||||
EM.start_server( "127.0.0.1", @port, TLP_LineBuffer ) do |c|
|
||||
conn = c
|
||||
end
|
||||
setup_timeout
|
||||
|
||||
EM.connect "127.0.0.1", @port, StopClient do |c|
|
||||
c.send_data "aaa\nbbb\r\nccc\n"
|
||||
c.close_connection_after_writing
|
||||
end
|
||||
}
|
||||
assert_equal( %w(aaa bbb ccc), conn.line_buffer)
|
||||
end
|
||||
|
||||
#--------------------------------------------------------------------
|
||||
|
||||
class TLP_ErrorMessage < EM::P::LineAndTextProtocol
|
||||
attr_reader :error_message
|
||||
|
||||
def initialize
|
||||
super
|
||||
@error_message = []
|
||||
end
|
||||
|
||||
def receive_line text
|
||||
raise
|
||||
end
|
||||
|
||||
def receive_error text
|
||||
@error_message << text
|
||||
end
|
||||
end
|
||||
|
||||
def test_overlength_lines
|
||||
conn = nil
|
||||
EM.run {
|
||||
EM.start_server( "127.0.0.1", @port, TLP_ErrorMessage ) do |c|
|
||||
conn = c
|
||||
end
|
||||
setup_timeout
|
||||
EM.connect "127.0.0.1", @port, StopClient do |c|
|
||||
c.send_data "a" * (16*1024 + 1)
|
||||
c.send_data "\n"
|
||||
c.close_connection_after_writing
|
||||
end
|
||||
|
||||
}
|
||||
assert_equal( ["overlength line"], conn.error_message )
|
||||
end
|
||||
|
||||
|
||||
#--------------------------------------------------------------------
|
||||
|
||||
class LineAndTextTest < EM::P::LineAndTextProtocol
|
||||
def receive_line line
|
||||
if line =~ /content-length:\s*(\d+)/i
|
||||
@content_length = $1.to_i
|
||||
elsif line.length == 0
|
||||
set_binary_mode @content_length
|
||||
end
|
||||
end
|
||||
def receive_binary_data text
|
||||
send_data "received #{text.length} bytes"
|
||||
close_connection_after_writing
|
||||
end
|
||||
end
|
||||
|
||||
def test_lines_and_text
|
||||
output = ''
|
||||
EM.run {
|
||||
EM.start_server( "127.0.0.1", @port, LineAndTextTest )
|
||||
setup_timeout
|
||||
|
||||
EM.connect "127.0.0.1", @port, StopClient do |c|
|
||||
c.set_receive_data { |data| output << data }
|
||||
c.send_data "Content-length: 400\n"
|
||||
c.send_data "\n"
|
||||
c.send_data "A" * 400
|
||||
EM.add_timer(0.1) { c.close_connection_after_writing }
|
||||
end
|
||||
}
|
||||
assert_equal( "received 400 bytes", output )
|
||||
end
|
||||
|
||||
#--------------------------------------------------------------------
|
||||
|
||||
|
||||
class BinaryTextTest < EM::P::LineAndTextProtocol
|
||||
def receive_line line
|
||||
if line =~ /content-length:\s*(\d+)/i
|
||||
set_binary_mode $1.to_i
|
||||
else
|
||||
raise "protocol error"
|
||||
end
|
||||
end
|
||||
def receive_binary_data text
|
||||
send_data "received #{text.length} bytes"
|
||||
close_connection_after_writing
|
||||
end
|
||||
end
|
||||
|
||||
def test_binary_text
|
||||
output = ''
|
||||
EM.run {
|
||||
EM.start_server( "127.0.0.1", @port, BinaryTextTest )
|
||||
setup_timeout
|
||||
|
||||
EM.connect "127.0.0.1", @port, StopClient do |c|
|
||||
c.set_receive_data { |data| output << data }
|
||||
c.send_data "Content-length: 10000\n"
|
||||
c.send_data "A" * 10000
|
||||
EM.add_timer(0.1) { c.close_connection_after_writing }
|
||||
end
|
||||
}
|
||||
assert_equal( "received 10000 bytes", output )
|
||||
end
|
||||
|
||||
end
|
||||
@@ -0,0 +1,332 @@
|
||||
require 'em_test_helper'
|
||||
|
||||
# TODO!!! Need tests for overlength headers and text bodies.
|
||||
|
||||
class TestLineText2 < Test::Unit::TestCase
|
||||
|
||||
# Run each of these tests two ways: passing in the whole test-dataset in one chunk,
|
||||
# and passing it in one character at a time.
|
||||
|
||||
class Basic
|
||||
include EM::Protocols::LineText2
|
||||
attr_reader :lines
|
||||
def receive_line line
|
||||
(@lines ||= []) << line
|
||||
end
|
||||
end
|
||||
def test_basic
|
||||
testdata = "Line 1\nLine 2\r\nLine 3\n"
|
||||
|
||||
a = Basic.new
|
||||
a.receive_data testdata
|
||||
assert_equal( ["Line 1", "Line 2", "Line 3"], a.lines )
|
||||
|
||||
a = Basic.new
|
||||
testdata.length.times {|i| a.receive_data( testdata[i...i+1] ) }
|
||||
assert_equal( ["Line 1", "Line 2", "Line 3"], a.lines )
|
||||
end
|
||||
|
||||
# The basic test above shows that extra newlines are chomped
|
||||
# This test shows that newlines are preserved if the delimiter isn't \n
|
||||
class PreserveNewlines
|
||||
include EM::Protocols::LineText2
|
||||
attr_reader :lines
|
||||
def initialize *args
|
||||
super
|
||||
@delim = "|"
|
||||
set_delimiter @delim
|
||||
end
|
||||
def receive_line line
|
||||
(@lines ||= []) << line
|
||||
end
|
||||
end
|
||||
def test_preserve_newlines
|
||||
a = PreserveNewlines.new
|
||||
a.receive_data "aaa|bbb|ccc|\n|\r\n| \t ||"
|
||||
assert_equal( ["aaa", "bbb", "ccc", "\n", "\r\n", " \t ", ""], a.lines )
|
||||
end
|
||||
|
||||
class ChangeDelimiter
|
||||
include EM::Protocols::LineText2
|
||||
attr_reader :lines
|
||||
def initialize *args
|
||||
super
|
||||
@delim = "A"
|
||||
set_delimiter @delim
|
||||
end
|
||||
def receive_line line
|
||||
(@lines ||= []) << line
|
||||
set_delimiter( @delim.succ! )
|
||||
end
|
||||
end
|
||||
|
||||
def test_change_delimiter
|
||||
testdata = %Q(LineaALinebBLinecCLinedD)
|
||||
|
||||
a = ChangeDelimiter.new
|
||||
a.receive_data testdata
|
||||
assert_equal( ["Linea", "Lineb", "Linec", "Lined"], a.lines )
|
||||
|
||||
a = ChangeDelimiter.new
|
||||
testdata.length.times {|i| a.receive_data( testdata[i...i+1] ) }
|
||||
assert_equal( ["Linea", "Lineb", "Linec", "Lined"], a.lines )
|
||||
end
|
||||
|
||||
class RegexDelimiter
|
||||
include EM::Protocols::LineText2
|
||||
attr_reader :lines
|
||||
def initialize *args
|
||||
super
|
||||
@delim = /[A-D]/
|
||||
set_delimiter @delim
|
||||
end
|
||||
def receive_line line
|
||||
(@lines ||= []) << line
|
||||
end
|
||||
end
|
||||
|
||||
def test_regex_delimiter
|
||||
testdata = %Q(LineaALinebBLinecCLinedD)
|
||||
|
||||
a = RegexDelimiter.new
|
||||
a.receive_data testdata
|
||||
assert_equal( ["Linea", "Lineb", "Linec", "Lined"], a.lines )
|
||||
|
||||
a = RegexDelimiter.new
|
||||
testdata.length.times {|i| a.receive_data( testdata[i...i+1] ) }
|
||||
assert_equal( ["Linea", "Lineb", "Linec", "Lined"], a.lines )
|
||||
end
|
||||
|
||||
#--
|
||||
# Test two lines followed by an empty line, ten bytes of binary data, then
|
||||
# two more lines.
|
||||
|
||||
class Binary
|
||||
include EM::Protocols::LineText2
|
||||
attr_reader :lines, :body
|
||||
def initialize *args
|
||||
super
|
||||
@lines = []
|
||||
@body = nil
|
||||
end
|
||||
def receive_line ln
|
||||
if ln == ""
|
||||
set_text_mode 10
|
||||
else
|
||||
@lines << ln
|
||||
end
|
||||
end
|
||||
def receive_binary_data data
|
||||
@body = data
|
||||
end
|
||||
end
|
||||
|
||||
def test_binary
|
||||
testdata = %Q(Line 1
|
||||
Line 2
|
||||
|
||||
0000000000Line 3
|
||||
Line 4
|
||||
)
|
||||
|
||||
a = Binary.new
|
||||
a.receive_data testdata
|
||||
assert_equal( ["Line 1", "Line 2", "Line 3", "Line 4"], a.lines)
|
||||
assert_equal( "0000000000", a.body )
|
||||
|
||||
a = Binary.new
|
||||
testdata.length.times {|i| a.receive_data( testdata[i...i+1] ) }
|
||||
assert_equal( ["Line 1", "Line 2", "Line 3", "Line 4"], a.lines)
|
||||
assert_equal( "0000000000", a.body )
|
||||
end
|
||||
|
||||
|
||||
# Test unsized binary data. The expectation is that each chunk of it
|
||||
# will be passed to us as it it received.
|
||||
class UnsizedBinary
|
||||
include EM::Protocols::LineText2
|
||||
attr_reader :n_calls, :body
|
||||
def initialize *args
|
||||
super
|
||||
set_text_mode
|
||||
end
|
||||
def receive_binary_data data
|
||||
@n_calls ||= 0
|
||||
@n_calls += 1
|
||||
(@body ||= "") << data
|
||||
end
|
||||
end
|
||||
|
||||
def test_unsized_binary
|
||||
testdata = "X\0" * 1000
|
||||
|
||||
a = UnsizedBinary.new
|
||||
a.receive_data testdata
|
||||
assert_equal( 1, a.n_calls )
|
||||
assert_equal( testdata, a.body )
|
||||
|
||||
a = UnsizedBinary.new
|
||||
testdata.length.times {|i| a.receive_data( testdata[i...i+1] ) }
|
||||
assert_equal( 2000, a.n_calls )
|
||||
assert_equal( testdata, a.body )
|
||||
end
|
||||
|
||||
|
||||
# Test binary data with a "throw back" into line-mode.
|
||||
class ThrowBack
|
||||
include EM::Protocols::LineText2
|
||||
attr_reader :headers
|
||||
def initialize *args
|
||||
super
|
||||
@headers = []
|
||||
@n_bytes = 0
|
||||
set_text_mode
|
||||
end
|
||||
def receive_binary_data data
|
||||
wanted = 25 - @n_bytes
|
||||
will_take = if data.length > wanted
|
||||
data.length - wanted
|
||||
else
|
||||
data.length
|
||||
end
|
||||
@n_bytes += will_take
|
||||
|
||||
if @n_bytes == 25
|
||||
set_line_mode( data[will_take..-1] )
|
||||
end
|
||||
end
|
||||
def receive_line ln
|
||||
@headers << ln
|
||||
end
|
||||
end
|
||||
def test_throw_back
|
||||
testdata = "Line\n" * 10
|
||||
|
||||
a = ThrowBack.new
|
||||
a.receive_data testdata
|
||||
assert_equal( ["Line"] * 5, a.headers )
|
||||
|
||||
a = ThrowBack.new
|
||||
testdata.length.times {|i| a.receive_data( testdata[i...i+1] ) }
|
||||
assert_equal( ["Line"] * 5, a.headers )
|
||||
end
|
||||
|
||||
# Test multi-character line delimiters.
|
||||
# Also note that the test data has a "tail" with no delimiter, that will be
|
||||
# discarded, but cf. the BinaryTail test.
|
||||
# TODO!!! This test doesn't work in the byte-by-byte case.
|
||||
class Multichar
|
||||
include EM::Protocols::LineText2
|
||||
attr_reader :lines
|
||||
def initialize *args
|
||||
super
|
||||
@lines = []
|
||||
set_delimiter "012"
|
||||
end
|
||||
def receive_line ln
|
||||
@lines << ln
|
||||
end
|
||||
end
|
||||
def test_multichar
|
||||
testdata = "Line012Line012Line012Line"
|
||||
|
||||
a = Multichar.new
|
||||
a.receive_data testdata
|
||||
assert_equal( ["Line"]*3, a.lines )
|
||||
|
||||
a = Multichar.new
|
||||
testdata.length.times {|i| a.receive_data( testdata[i...i+1] ) }
|
||||
# DOESN'T WORK in this case. Multi-character delimiters are broken.
|
||||
#assert_equal( ["Line"]*3, a.lines )
|
||||
end
|
||||
|
||||
# Test a binary "tail," when a sized binary transfer doesn't complete because
|
||||
# of an unbind. We get a partial result.
|
||||
class BinaryTail
|
||||
include EM::Protocols::LineText2
|
||||
attr_reader :data
|
||||
def initialize *args
|
||||
super
|
||||
@data = ""
|
||||
set_text_mode 1000
|
||||
end
|
||||
def receive_binary_data data
|
||||
# we expect to get all the data in one chunk, even in the byte-by-byte case,
|
||||
# because sized transfers by definition give us exactly one call to
|
||||
# #receive_binary_data.
|
||||
@data = data
|
||||
end
|
||||
end
|
||||
def test_binary_tail
|
||||
testdata = "0" * 500
|
||||
|
||||
a = BinaryTail.new
|
||||
a.receive_data testdata
|
||||
a.unbind
|
||||
assert_equal( "0" * 500, a.data )
|
||||
|
||||
a = BinaryTail.new
|
||||
testdata.length.times {|i| a.receive_data( testdata[i...i+1] ) }
|
||||
a.unbind
|
||||
assert_equal( "0" * 500, a.data )
|
||||
end
|
||||
|
||||
|
||||
# Test an end-of-binary call. Arrange to receive binary data but don't bother counting it
|
||||
# as it comes. Rely on getting receive_end_of_binary_data to signal the transition back to
|
||||
# line mode.
|
||||
# At the present time, this isn't strictly necessary with sized binary chunks because by
|
||||
# definition we accumulate them and make exactly one call to receive_binary_data, but
|
||||
# we may want to support a mode in the future that would break up large chunks into multiple
|
||||
# calls.
|
||||
class LazyBinary
|
||||
include EM::Protocols::LineText2
|
||||
attr_reader :data, :end
|
||||
def initialize *args
|
||||
super
|
||||
@data = ""
|
||||
set_text_mode 1000
|
||||
end
|
||||
def receive_binary_data data
|
||||
# we expect to get all the data in one chunk, even in the byte-by-byte case,
|
||||
# because sized transfers by definition give us exactly one call to
|
||||
# #receive_binary_data.
|
||||
@data = data
|
||||
end
|
||||
def receive_end_of_binary_data
|
||||
@end = true
|
||||
end
|
||||
end
|
||||
def test_receive_end_of_binary_data
|
||||
testdata = "_" * 1000
|
||||
a = LazyBinary.new
|
||||
testdata.length.times {|i| a.receive_data( testdata[i...i+1] ) }
|
||||
assert_equal( "_" * 1000, a.data )
|
||||
assert( a.end )
|
||||
end
|
||||
|
||||
|
||||
# This tests a bug fix in which calling set_text_mode failed when called
|
||||
# inside receive_binary_data.
|
||||
#
|
||||
class BinaryPair
|
||||
include EM::Protocols::LineText2
|
||||
attr_reader :sizes
|
||||
def initialize *args
|
||||
super
|
||||
set_text_mode 1
|
||||
@sizes = []
|
||||
end
|
||||
def receive_binary_data dt
|
||||
@sizes << dt.length
|
||||
set_text_mode( (dt.length == 1) ? 2 : 1 )
|
||||
end
|
||||
end
|
||||
def test_binary_pairs
|
||||
test_data = "123" * 5
|
||||
a = BinaryPair.new
|
||||
a.receive_data test_data
|
||||
assert_equal( [1,2,1,2,1,2,1,2,1,2], a.sizes )
|
||||
end
|
||||
|
||||
end
|
||||
@@ -0,0 +1,22 @@
|
||||
require 'em_test_helper'
|
||||
require 'socket'
|
||||
|
||||
class TestManyFDs < Test::Unit::TestCase
|
||||
def setup
|
||||
@port = next_port
|
||||
end
|
||||
|
||||
def test_connection_class_cache
|
||||
mod = Module.new
|
||||
a = nil
|
||||
Process.setrlimit(Process::RLIMIT_NOFILE, 4096) rescue nil
|
||||
EM.run {
|
||||
EM.start_server '127.0.0.1', @port, mod
|
||||
1100.times do
|
||||
a = EM.connect '127.0.0.1', @port, mod
|
||||
assert_kind_of EM::Connection, a
|
||||
end
|
||||
EM.stop
|
||||
}
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,104 @@
|
||||
require 'em_test_helper'
|
||||
|
||||
class TestNextTick < Test::Unit::TestCase
|
||||
|
||||
def test_tick_arg
|
||||
pr = proc {EM.stop}
|
||||
EM.run {
|
||||
EM.next_tick pr
|
||||
}
|
||||
assert true
|
||||
end
|
||||
|
||||
def test_tick_block
|
||||
EM.run {
|
||||
EM.next_tick {EM.stop}
|
||||
}
|
||||
assert true
|
||||
end
|
||||
|
||||
# This illustrates the solution to a long-standing problem.
|
||||
# It's now possible to correctly nest calls to EM#run.
|
||||
# See the source code commentary for EM#run for more info.
|
||||
#
|
||||
def test_run_run
|
||||
EM.run {
|
||||
EM.run {
|
||||
EM.next_tick {EM.stop}
|
||||
}
|
||||
}
|
||||
end
|
||||
|
||||
def test_pre_run_queue
|
||||
x = false
|
||||
EM.next_tick { EM.stop; x = true }
|
||||
EM.run { EM.add_timer(0.01) { EM.stop } }
|
||||
assert x
|
||||
end
|
||||
|
||||
def test_cleanup_after_stop
|
||||
x = true
|
||||
EM.run{
|
||||
EM.next_tick{
|
||||
EM.stop
|
||||
EM.next_tick{ x=false }
|
||||
}
|
||||
}
|
||||
EM.run{
|
||||
EM.next_tick{ EM.stop }
|
||||
}
|
||||
assert x
|
||||
end
|
||||
|
||||
# We now support an additional parameter for EM#run.
|
||||
# You can pass two procs to EM#run now. The first is executed as the normal
|
||||
# run block. The second (if given) is scheduled for execution after the
|
||||
# reactor loop completes.
|
||||
# The reason for supporting this is subtle. There has always been an expectation
|
||||
# that EM#run doesn't return until after the reactor loop ends. But now it's
|
||||
# possible to nest calls to EM#run, which means that a nested call WILL
|
||||
# RETURN. In order to write code that will run correctly either way, it's
|
||||
# recommended to put any code which must execute after the reactor completes
|
||||
# in the second parameter.
|
||||
#
|
||||
def test_run_run_2
|
||||
a = proc {EM.stop}
|
||||
b = proc {assert true}
|
||||
EM.run a, b
|
||||
end
|
||||
|
||||
|
||||
# This illustrates that EM#run returns when it's called nested.
|
||||
# This isn't a feature, rather it's something to be wary of when writing code
|
||||
# that must run correctly even if EM#run is called while a reactor is already
|
||||
# running.
|
||||
def test_run_run_3
|
||||
a = []
|
||||
EM.run {
|
||||
EM.run proc {EM.stop}, proc {a << 2}
|
||||
a << 1
|
||||
}
|
||||
assert_equal( [1,2], a )
|
||||
end
|
||||
|
||||
|
||||
def test_schedule_on_reactor_thread
|
||||
x = false
|
||||
EM.run do
|
||||
EM.schedule { x = true }
|
||||
EM.stop
|
||||
end
|
||||
assert x
|
||||
end
|
||||
|
||||
def test_schedule_from_thread
|
||||
x = false
|
||||
EM.run do
|
||||
Thread.new { EM.schedule { x = true } }.join
|
||||
assert !x
|
||||
EM.next_tick { EM.stop }
|
||||
end
|
||||
assert x
|
||||
end
|
||||
|
||||
end
|
||||
@@ -0,0 +1,36 @@
|
||||
require 'em_test_helper'
|
||||
|
||||
class TestObjectProtocol < Test::Unit::TestCase
|
||||
module Server
|
||||
include EM::P::ObjectProtocol
|
||||
def post_init
|
||||
send_object :hello=>'world'
|
||||
end
|
||||
def receive_object obj
|
||||
$server = obj
|
||||
EM.stop
|
||||
end
|
||||
end
|
||||
|
||||
module Client
|
||||
include EM::P::ObjectProtocol
|
||||
def receive_object obj
|
||||
$client = obj
|
||||
send_object 'you_said'=>obj
|
||||
end
|
||||
end
|
||||
|
||||
def setup
|
||||
@port = next_port
|
||||
end
|
||||
|
||||
def test_send_receive
|
||||
EM.run{
|
||||
EM.start_server "127.0.0.1", @port, Server
|
||||
EM.connect "127.0.0.1", @port, Client
|
||||
}
|
||||
|
||||
assert($client == {:hello=>'world'})
|
||||
assert($server == {'you_said'=>{:hello=>'world'}})
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,107 @@
|
||||
require 'em_test_helper'
|
||||
|
||||
class TestPause < Test::Unit::TestCase
|
||||
if EM.respond_to? :pause_connection
|
||||
def setup
|
||||
@port = next_port
|
||||
end
|
||||
|
||||
def teardown
|
||||
assert(!EM.reactor_running?)
|
||||
end
|
||||
|
||||
def test_pause_resume
|
||||
server = nil
|
||||
|
||||
s_rx = c_rx = 0
|
||||
|
||||
test_server = Module.new do
|
||||
define_method :post_init do
|
||||
server = self
|
||||
end
|
||||
|
||||
define_method :receive_data do |data|
|
||||
s_rx += 1
|
||||
|
||||
EM.add_periodic_timer(0.01) { send_data 'hi' }
|
||||
send_data 'hi'
|
||||
|
||||
# pause server, now no outgoing data will actually
|
||||
# be sent and no more incoming data will be received
|
||||
pause
|
||||
end
|
||||
end
|
||||
|
||||
test_client = Module.new do
|
||||
def post_init
|
||||
EM.add_periodic_timer(0.01) do
|
||||
send_data 'hello'
|
||||
end
|
||||
end
|
||||
|
||||
define_method :receive_data do |data|
|
||||
c_rx += 1
|
||||
end
|
||||
end
|
||||
|
||||
EM.run do
|
||||
EM.start_server "127.0.0.1", @port, test_server
|
||||
EM.connect "127.0.0.1", @port, test_client
|
||||
|
||||
EM.add_timer(0.05) do
|
||||
assert_equal 1, s_rx
|
||||
assert_equal 0, c_rx
|
||||
assert server.paused?
|
||||
|
||||
# resume server, queued outgoing and incoming data will be flushed
|
||||
server.resume
|
||||
|
||||
assert !server.paused?
|
||||
|
||||
EM.add_timer(0.05) do
|
||||
assert server.paused?
|
||||
assert s_rx > 1
|
||||
assert c_rx > 0
|
||||
EM.stop
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def test_pause_in_receive_data
|
||||
incoming = []
|
||||
|
||||
test_server = Module.new do
|
||||
define_method(:receive_data) do |data|
|
||||
incoming << data
|
||||
pause
|
||||
EM.add_timer(0.5){ close_connection }
|
||||
end
|
||||
define_method(:unbind) do
|
||||
EM.stop
|
||||
end
|
||||
end
|
||||
|
||||
buf = 'a' * 1024
|
||||
|
||||
EM.run do
|
||||
EM.start_server "127.0.0.1", @port, test_server
|
||||
cli = EM.connect "127.0.0.1", @port
|
||||
128.times do
|
||||
cli.send_data buf
|
||||
end
|
||||
end
|
||||
|
||||
assert_equal 1, incoming.size
|
||||
assert incoming[0].bytesize > buf.bytesize
|
||||
assert incoming[0].bytesize < buf.bytesize * 128
|
||||
end
|
||||
else
|
||||
warn "EM.pause_connection not implemented, skipping tests in #{__FILE__}"
|
||||
|
||||
# Because some rubies will complain if a TestCase class has no tests
|
||||
def test_em_pause_connection_not_implemented
|
||||
assert true
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,52 @@
|
||||
require 'em_test_helper'
|
||||
|
||||
class TestPendingConnectTimeout < Test::Unit::TestCase
|
||||
|
||||
if EM.respond_to? :get_pending_connect_timeout
|
||||
def test_default
|
||||
EM.run {
|
||||
c = EM.connect("127.0.0.1", 54321)
|
||||
assert_equal 20.0, c.pending_connect_timeout
|
||||
EM.stop
|
||||
}
|
||||
end
|
||||
|
||||
def test_set_and_get
|
||||
EM.run {
|
||||
c = EM.connect("127.0.0.1", 54321)
|
||||
c.pending_connect_timeout = 2.5
|
||||
assert_equal 2.5, c.pending_connect_timeout
|
||||
EM.stop
|
||||
}
|
||||
end
|
||||
|
||||
def test_for_real
|
||||
start, finish = nil
|
||||
|
||||
timeout_handler = Module.new do
|
||||
define_method :unbind do
|
||||
finish = EM.current_time
|
||||
EM.stop
|
||||
end
|
||||
end
|
||||
|
||||
EM.run {
|
||||
setup_timeout
|
||||
EM.heartbeat_interval = 0.1
|
||||
start = EM.current_time
|
||||
c = EM.connect('192.0.2.0', 54321, timeout_handler)
|
||||
c.pending_connect_timeout = 0.2
|
||||
}
|
||||
|
||||
assert_in_delta(0.2, (finish - start), 0.1)
|
||||
end
|
||||
else
|
||||
warn "EM.pending_connect_timeout not implemented, skipping tests in #{__FILE__}"
|
||||
|
||||
# Because some rubies will complain if a TestCase class has no tests
|
||||
def test_em_pending_connect_timeout_not_implemented
|
||||
assert true
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
@@ -0,0 +1,196 @@
|
||||
require 'em_test_helper'
|
||||
|
||||
class TestPool < Test::Unit::TestCase
|
||||
def pool
|
||||
@pool ||= EM::Pool.new
|
||||
end
|
||||
|
||||
def go
|
||||
EM.run { yield }
|
||||
end
|
||||
|
||||
def stop
|
||||
EM.stop
|
||||
end
|
||||
|
||||
def deferrable
|
||||
@deferrable ||= EM::DefaultDeferrable.new
|
||||
end
|
||||
|
||||
def test_supports_more_work_than_resources
|
||||
ran = false
|
||||
go do
|
||||
pool.perform do
|
||||
ran = true
|
||||
deferrable
|
||||
end
|
||||
stop
|
||||
end
|
||||
assert_equal false, ran
|
||||
go do
|
||||
pool.add :resource
|
||||
stop
|
||||
end
|
||||
assert_equal true, ran
|
||||
end
|
||||
|
||||
def test_reques_resources_on_error
|
||||
pooled_res, pooled_res2 = nil
|
||||
pool.add :res
|
||||
go do
|
||||
pool.perform do |res|
|
||||
pooled_res = res
|
||||
deferrable
|
||||
end
|
||||
stop
|
||||
end
|
||||
deferrable.fail
|
||||
go do
|
||||
pool.perform do |res|
|
||||
pooled_res2 = res
|
||||
deferrable
|
||||
end
|
||||
stop
|
||||
end
|
||||
assert_equal :res, pooled_res
|
||||
assert_equal pooled_res, pooled_res2
|
||||
end
|
||||
|
||||
def test_supports_custom_on_error
|
||||
eres = nil
|
||||
pool.on_error do |res|
|
||||
eres = res
|
||||
end
|
||||
performs = []
|
||||
pool.add :res
|
||||
go do
|
||||
pool.perform do |res|
|
||||
performs << res
|
||||
deferrable
|
||||
end
|
||||
pool.perform do |res|
|
||||
performs << res
|
||||
deferrable
|
||||
end
|
||||
deferrable.fail
|
||||
stop
|
||||
end
|
||||
assert_equal :res, eres
|
||||
# manual requeues required when error handler is installed:
|
||||
assert_equal 1, performs.size
|
||||
assert_equal :res, performs.first
|
||||
end
|
||||
|
||||
def test_catches_successful_deferrables
|
||||
performs = []
|
||||
pool.add :res
|
||||
go do
|
||||
pool.perform { |res| performs << res; deferrable }
|
||||
pool.perform { |res| performs << res; deferrable }
|
||||
stop
|
||||
end
|
||||
assert_equal [:res], performs
|
||||
deferrable.succeed
|
||||
go { stop }
|
||||
assert_equal [:res, :res], performs
|
||||
end
|
||||
|
||||
def test_prunes_locked_and_removed_resources
|
||||
performs = []
|
||||
pool.add :res
|
||||
deferrable.succeed
|
||||
go do
|
||||
pool.perform { |res| performs << res; pool.remove res; deferrable }
|
||||
pool.perform { |res| performs << res; pool.remove res; deferrable }
|
||||
stop
|
||||
end
|
||||
assert_equal [:res], performs
|
||||
end
|
||||
|
||||
# Contents is only to be used for inspection of the pool!
|
||||
def test_contents
|
||||
pool.add :res
|
||||
assert_equal [:res], pool.contents
|
||||
# Assert that modifying the contents list does not affect the pools
|
||||
# contents.
|
||||
pool.contents.delete(:res)
|
||||
assert_equal [:res], pool.contents
|
||||
end
|
||||
|
||||
def test_contents_when_perform_errors_and_on_error_is_not_set
|
||||
pool.add :res
|
||||
assert_equal [:res], pool.contents
|
||||
|
||||
pool.perform do |r|
|
||||
d = EM::DefaultDeferrable.new
|
||||
d.fail
|
||||
d
|
||||
end
|
||||
|
||||
EM.run { EM.next_tick { EM.stop } }
|
||||
|
||||
assert_equal [:res], pool.contents
|
||||
end
|
||||
|
||||
def test_contents_when_perform_errors_and_on_error_is_set
|
||||
pool.add :res
|
||||
res = nil
|
||||
pool.on_error do |r|
|
||||
res = r
|
||||
end
|
||||
assert_equal [:res], pool.contents
|
||||
|
||||
pool.perform do |r|
|
||||
d = EM::DefaultDeferrable.new
|
||||
d.fail 'foo'
|
||||
d
|
||||
end
|
||||
|
||||
EM.run { EM.next_tick { EM.stop } }
|
||||
|
||||
assert_equal :res, res
|
||||
assert_equal [], pool.contents
|
||||
end
|
||||
|
||||
def test_num_waiting
|
||||
pool.add :res
|
||||
assert_equal 0, pool.num_waiting
|
||||
pool.perform { |r| EM::DefaultDeferrable.new }
|
||||
assert_equal 0, pool.num_waiting
|
||||
10.times { pool.perform { |r| EM::DefaultDeferrable.new } }
|
||||
EM.run { EM.next_tick { EM.stop } }
|
||||
assert_equal 10, pool.num_waiting
|
||||
end
|
||||
|
||||
def test_exceptions_in_the_work_block_bubble_up_raise_and_fail_the_resource
|
||||
pool.add :res
|
||||
|
||||
res = nil
|
||||
pool.on_error { |r| res = r }
|
||||
pool.perform { raise 'boom' }
|
||||
|
||||
assert_raises(RuntimeError) do
|
||||
EM.run { EM.next_tick { EM.stop } }
|
||||
end
|
||||
|
||||
assert_equal [], pool.contents
|
||||
assert_equal :res, res
|
||||
end
|
||||
|
||||
def test_removed_list_does_not_leak_on_errors
|
||||
pool.add :res
|
||||
|
||||
pool.on_error do |r|
|
||||
# This is actually the wrong thing to do, and not required, but some users
|
||||
# might do it. When they do, they would find that @removed would cause a
|
||||
# slow leak.
|
||||
pool.remove r
|
||||
end
|
||||
|
||||
pool.perform { d = EM::DefaultDeferrable.new; d.fail; d }
|
||||
|
||||
EM.run { EM.next_tick { EM.stop } }
|
||||
assert_equal [], pool.instance_variable_get(:@removed)
|
||||
end
|
||||
|
||||
end
|
||||
@@ -0,0 +1,50 @@
|
||||
require 'em_test_helper'
|
||||
|
||||
if EM.kqueue?
|
||||
class TestProcessWatch < Test::Unit::TestCase
|
||||
module ParentProcessWatcher
|
||||
def process_forked
|
||||
$forked = true
|
||||
end
|
||||
end
|
||||
|
||||
module ChildProcessWatcher
|
||||
def process_exited
|
||||
$exited = true
|
||||
end
|
||||
def unbind
|
||||
$unbind = true
|
||||
EM.stop
|
||||
end
|
||||
end
|
||||
|
||||
def setup
|
||||
EM.kqueue = true
|
||||
end
|
||||
|
||||
def teardown
|
||||
EM.kqueue = false
|
||||
end
|
||||
|
||||
def test_events
|
||||
omit_if(rbx?)
|
||||
omit_if(jruby?)
|
||||
EM.run{
|
||||
# watch ourselves for a fork notification
|
||||
EM.watch_process(Process.pid, ParentProcessWatcher)
|
||||
$fork_pid = fork{ sleep }
|
||||
child = EM.watch_process($fork_pid, ChildProcessWatcher)
|
||||
$pid = child.pid
|
||||
|
||||
EM.add_timer(0.2){
|
||||
Process.kill('TERM', $fork_pid)
|
||||
}
|
||||
}
|
||||
|
||||
assert_equal($pid, $fork_pid)
|
||||
assert($forked)
|
||||
assert($exited)
|
||||
assert($unbind)
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,128 @@
|
||||
require 'em_test_helper'
|
||||
|
||||
class TestProcesses < Test::Unit::TestCase
|
||||
|
||||
if !windows? && !jruby?
|
||||
|
||||
# EM::DeferrableChildProcess is a sugaring of a common use-case
|
||||
# involving EM::popen.
|
||||
# Call the #open method on EM::DeferrableChildProcess, passing
|
||||
# a command-string. #open immediately returns an EM::Deferrable
|
||||
# object. It also schedules the forking of a child process, which
|
||||
# will execute the command passed to #open.
|
||||
# When the forked child terminates, the Deferrable will be signalled
|
||||
# and execute its callbacks, passing the data that the child process
|
||||
# wrote to stdout.
|
||||
#
|
||||
def test_deferrable_child_process
|
||||
ls = ""
|
||||
EM.run {
|
||||
d = EM::DeferrableChildProcess.open( "ls -ltr" )
|
||||
d.callback {|data_from_child|
|
||||
ls = data_from_child
|
||||
EM.stop
|
||||
}
|
||||
}
|
||||
assert( ls.length > 0)
|
||||
end
|
||||
|
||||
def setup
|
||||
$out = nil
|
||||
$status = nil
|
||||
end
|
||||
|
||||
def test_em_system
|
||||
EM.run{
|
||||
EM.system('ls'){ |out,status| $out, $status = out, status; EM.stop }
|
||||
}
|
||||
|
||||
assert( $out.length > 0 )
|
||||
assert_equal(0, $status.exitstatus)
|
||||
assert_kind_of(Process::Status, $status)
|
||||
end
|
||||
|
||||
def test_em_system_pid
|
||||
$pids = []
|
||||
|
||||
EM.run{
|
||||
$pids << EM.system('echo hi', proc{ |out,status|$pids << status.pid; EM.stop })
|
||||
}
|
||||
|
||||
assert_equal $pids[0], $pids[1]
|
||||
end
|
||||
|
||||
def test_em_system_with_proc
|
||||
EM.run{
|
||||
EM.system('ls', proc{ |out,status| $out, $status = out, status; EM.stop })
|
||||
}
|
||||
|
||||
assert( $out.length > 0 )
|
||||
assert_equal(0, $status.exitstatus)
|
||||
assert_kind_of(Process::Status, $status)
|
||||
end
|
||||
|
||||
def test_em_system_with_two_procs
|
||||
EM.run{
|
||||
EM.system('sh', proc{ |process|
|
||||
process.send_data("echo hello\n")
|
||||
process.send_data("exit\n")
|
||||
}, proc{ |out,status|
|
||||
$out = out
|
||||
$status = status
|
||||
EM.stop
|
||||
})
|
||||
}
|
||||
|
||||
assert_equal("hello\n", $out)
|
||||
end
|
||||
|
||||
def test_em_system_cmd_arguments
|
||||
EM.run{
|
||||
EM.system('echo', '1', '2', 'version', proc{ |process|
|
||||
}, proc{ |out,status|
|
||||
$out = out
|
||||
$status = status
|
||||
EM.stop
|
||||
})
|
||||
}
|
||||
|
||||
assert_match(/1 2 version/i, $out)
|
||||
end
|
||||
|
||||
def test_em_system_spaced_arguments
|
||||
EM.run{
|
||||
EM.system('ruby', '-e', 'puts "hello"', proc{ |out,status|
|
||||
$out = out
|
||||
EM.stop
|
||||
})
|
||||
}
|
||||
|
||||
assert_equal("hello\n", $out)
|
||||
end
|
||||
|
||||
def test_em_popen_pause_resume
|
||||
c_rx = 0
|
||||
|
||||
test_client = Module.new do
|
||||
define_method :receive_data do |data|
|
||||
c_rx += 1
|
||||
pause
|
||||
EM.add_timer(0.5) { EM.stop }
|
||||
end
|
||||
end
|
||||
|
||||
EM.run do
|
||||
EM.popen('echo 1', test_client)
|
||||
end
|
||||
|
||||
assert_equal 1, c_rx
|
||||
end
|
||||
else
|
||||
warn "EM.popen not implemented, skipping tests in #{__FILE__}"
|
||||
|
||||
# Because some rubies will complain if a TestCase class has no tests
|
||||
def test_em_popen_unsupported
|
||||
assert true
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,180 @@
|
||||
require 'em_test_helper'
|
||||
|
||||
class TestProxyConnection < Test::Unit::TestCase
|
||||
|
||||
if EM.respond_to?(:start_proxy)
|
||||
module ProxyConnection
|
||||
def initialize(client, request)
|
||||
@client, @request = client, request
|
||||
end
|
||||
|
||||
def post_init
|
||||
EM::enable_proxy(self, @client)
|
||||
end
|
||||
|
||||
def connection_completed
|
||||
EM.next_tick {
|
||||
send_data @request
|
||||
}
|
||||
end
|
||||
|
||||
def proxy_target_unbound
|
||||
$unbound_early = true
|
||||
EM.stop
|
||||
end
|
||||
|
||||
def unbind
|
||||
$proxied_bytes = self.get_proxied_bytes
|
||||
@client.close_connection_after_writing
|
||||
end
|
||||
end
|
||||
|
||||
module PartialProxyConnection
|
||||
def initialize(client, request, length)
|
||||
@client, @request, @length = client, request, length
|
||||
end
|
||||
|
||||
def post_init
|
||||
EM::enable_proxy(self, @client, 0, @length)
|
||||
end
|
||||
|
||||
def receive_data(data)
|
||||
$unproxied_data = data
|
||||
@client.send_data(data)
|
||||
end
|
||||
|
||||
def connection_completed
|
||||
EM.next_tick {
|
||||
send_data @request
|
||||
}
|
||||
end
|
||||
|
||||
def proxy_target_unbound
|
||||
$unbound_early = true
|
||||
EM.stop
|
||||
end
|
||||
|
||||
def proxy_completed
|
||||
$proxy_completed = true
|
||||
end
|
||||
|
||||
def unbind
|
||||
@client.close_connection_after_writing
|
||||
end
|
||||
end
|
||||
|
||||
module Client
|
||||
def connection_completed
|
||||
send_data "EM rocks!"
|
||||
end
|
||||
|
||||
def receive_data(data)
|
||||
$client_data = data
|
||||
end
|
||||
|
||||
def unbind
|
||||
EM.stop
|
||||
end
|
||||
end
|
||||
|
||||
module Client2
|
||||
include Client
|
||||
def unbind; end
|
||||
end
|
||||
|
||||
module Server
|
||||
def receive_data(data)
|
||||
send_data "I know!" if data == "EM rocks!"
|
||||
close_connection_after_writing
|
||||
end
|
||||
end
|
||||
|
||||
module ProxyServer
|
||||
def initialize port
|
||||
@port = port
|
||||
end
|
||||
|
||||
def receive_data(data)
|
||||
@proxy = EM.connect("127.0.0.1", @port, ProxyConnection, self, data)
|
||||
end
|
||||
end
|
||||
|
||||
module PartialProxyServer
|
||||
def initialize port
|
||||
@port = port
|
||||
end
|
||||
|
||||
def receive_data(data)
|
||||
EM.connect("127.0.0.1", @port, PartialProxyConnection, self, data, 1)
|
||||
end
|
||||
end
|
||||
|
||||
module EarlyClosingProxy
|
||||
def initialize port
|
||||
@port = port
|
||||
end
|
||||
|
||||
def receive_data(data)
|
||||
EM.connect("127.0.0.1", @port, ProxyConnection, self, data)
|
||||
close_connection
|
||||
end
|
||||
end
|
||||
|
||||
def setup
|
||||
@port = next_port
|
||||
@proxy_port = next_port
|
||||
end
|
||||
|
||||
def test_proxy_connection
|
||||
EM.run {
|
||||
EM.start_server("127.0.0.1", @port, Server)
|
||||
EM.start_server("127.0.0.1", @proxy_port, ProxyServer, @port)
|
||||
EM.connect("127.0.0.1", @proxy_port, Client)
|
||||
}
|
||||
|
||||
assert_equal("I know!", $client_data)
|
||||
end
|
||||
|
||||
def test_proxied_bytes
|
||||
EM.run {
|
||||
EM.start_server("127.0.0.1", @port, Server)
|
||||
EM.start_server("127.0.0.1", @proxy_port, ProxyServer, @port)
|
||||
EM.connect("127.0.0.1", @proxy_port, Client)
|
||||
}
|
||||
|
||||
assert_equal("I know!", $client_data)
|
||||
assert_equal("I know!".bytesize, $proxied_bytes)
|
||||
end
|
||||
|
||||
def test_partial_proxy_connection
|
||||
EM.run {
|
||||
EM.start_server("127.0.0.1", @port, Server)
|
||||
EM.start_server("127.0.0.1", @proxy_port, PartialProxyServer, @port)
|
||||
EM.connect("127.0.0.1", @proxy_port, Client)
|
||||
}
|
||||
|
||||
assert_equal("I know!", $client_data)
|
||||
assert_equal(" know!", $unproxied_data)
|
||||
assert($proxy_completed)
|
||||
end
|
||||
|
||||
def test_early_close
|
||||
$client_data = nil
|
||||
EM.run {
|
||||
EM.start_server("127.0.0.1", @port, Server)
|
||||
EM.start_server("127.0.0.1", @proxy_port, EarlyClosingProxy, @port)
|
||||
EM.connect("127.0.0.1", @proxy_port, Client2)
|
||||
}
|
||||
|
||||
assert($unbound_early)
|
||||
end
|
||||
else
|
||||
warn "EM.start_proxy not implemented, skipping tests in #{__FILE__}"
|
||||
|
||||
# Because some rubies will complain if a TestCase class has no tests
|
||||
def test_em_start_proxy_not_implemented
|
||||
assert !EM.respond_to?(:start_proxy)
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
@@ -0,0 +1,139 @@
|
||||
require 'em_test_helper'
|
||||
|
||||
class TestPure < Test::Unit::TestCase
|
||||
|
||||
def setup
|
||||
@port = next_port
|
||||
end
|
||||
|
||||
# These tests are intended to exercise problems that come up in the
|
||||
# pure-Ruby implementation. However, we DON'T constrain them such that
|
||||
# they only run in pure-Ruby. These tests need to work identically in
|
||||
# any implementation.
|
||||
|
||||
#-------------------------------------
|
||||
|
||||
# The EM reactor needs to run down open connections and release other resources
|
||||
# when it stops running. Make sure this happens even if user code throws a Ruby
|
||||
# exception.
|
||||
# If exception handling is incorrect, the second test will fail with a no-bind error
|
||||
# because the TCP server opened in the first test will not have been closed.
|
||||
|
||||
def test_exception_handling_releases_resources
|
||||
exception = Class.new(StandardError)
|
||||
|
||||
2.times do
|
||||
assert_raises(exception) do
|
||||
EM.run do
|
||||
EM.start_server "127.0.0.1", @port
|
||||
raise exception
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Under some circumstances, the pure Ruby library would emit an Errno::ECONNREFUSED
|
||||
# exception on certain kinds of TCP connect-errors.
|
||||
# It's always been something of an open question whether EM should throw an exception
|
||||
# in these cases but the defined answer has always been to catch it the unbind method.
|
||||
# With a connect failure, the latter will always fire, but connection_completed will
|
||||
# never fire. So even though the point is arguable, it's incorrect for the pure Ruby
|
||||
# version to throw an exception.
|
||||
module TestConnrefused
|
||||
def unbind
|
||||
EM.stop
|
||||
end
|
||||
def connection_completed
|
||||
raise "should never get here"
|
||||
end
|
||||
end
|
||||
|
||||
def test_connrefused
|
||||
assert_nothing_raised do
|
||||
EM.run {
|
||||
setup_timeout(2)
|
||||
EM.connect "127.0.0.1", @port, TestConnrefused
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
# Make sure connection_completed gets called as expected with TCP clients. This is the
|
||||
# opposite of test_connrefused.
|
||||
# If the test fails, it will hang because EM.stop never gets called.
|
||||
#
|
||||
module TestConnaccepted
|
||||
def connection_completed
|
||||
EM.stop
|
||||
end
|
||||
end
|
||||
def test_connaccepted
|
||||
assert_nothing_raised do
|
||||
EM.run {
|
||||
EM.start_server "127.0.0.1", @port
|
||||
EM.connect "127.0.0.1", @port, TestConnaccepted
|
||||
setup_timeout(1)
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
def test_reactor_running
|
||||
a = false
|
||||
EM.run {
|
||||
a = EM.reactor_running?
|
||||
EM.next_tick {EM.stop}
|
||||
}
|
||||
assert a
|
||||
end
|
||||
|
||||
module TLSServer
|
||||
def post_init
|
||||
start_tls
|
||||
end
|
||||
|
||||
def ssl_handshake_completed
|
||||
$server_handshake_completed = true
|
||||
end
|
||||
|
||||
def receive_data(data)
|
||||
$server_received_data = data
|
||||
send_data(data)
|
||||
end
|
||||
end
|
||||
|
||||
module TLSClient
|
||||
def post_init
|
||||
start_tls
|
||||
end
|
||||
|
||||
def ssl_handshake_completed
|
||||
$client_handshake_completed = true
|
||||
end
|
||||
|
||||
def connection_completed
|
||||
send_data('Hello World!')
|
||||
end
|
||||
|
||||
def receive_data(data)
|
||||
$client_received_data = data
|
||||
close_connection
|
||||
end
|
||||
|
||||
def unbind
|
||||
EM.stop_event_loop
|
||||
end
|
||||
end
|
||||
|
||||
def test_start_tls
|
||||
$client_handshake_completed, $server_handshake_completed = false, false
|
||||
$client_received_data, $server_received_data = nil, nil
|
||||
EM.run do
|
||||
EM.start_server("127.0.0.1", 16789, TLSServer)
|
||||
EM.connect("127.0.0.1", 16789, TLSClient)
|
||||
end
|
||||
|
||||
assert($client_handshake_completed)
|
||||
assert($server_handshake_completed)
|
||||
assert($client_received_data == "Hello World!")
|
||||
assert($server_received_data == "Hello World!")
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,64 @@
|
||||
require 'em_test_helper'
|
||||
|
||||
class TestEMQueue < Test::Unit::TestCase
|
||||
def test_queue_push
|
||||
s = 0
|
||||
EM.run do
|
||||
q = EM::Queue.new
|
||||
q.push(1)
|
||||
EM.next_tick { s = q.size; EM.stop }
|
||||
end
|
||||
assert_equal 1, s
|
||||
end
|
||||
|
||||
def test_queue_pop
|
||||
x,y,z = nil
|
||||
EM.run do
|
||||
q = EM::Queue.new
|
||||
q.push(1,2,3)
|
||||
q.pop { |v| x = v }
|
||||
q.pop { |v| y = v }
|
||||
q.pop { |v| z = v; EM.stop }
|
||||
end
|
||||
assert_equal 1, x
|
||||
assert_equal 2, y
|
||||
assert_equal 3, z
|
||||
end
|
||||
|
||||
def test_queue_reactor_thread
|
||||
q = EM::Queue.new
|
||||
|
||||
Thread.new { q.push(1,2,3) }.join
|
||||
assert q.empty?
|
||||
EM.run { EM.next_tick { EM.stop } }
|
||||
assert_equal 3, q.size
|
||||
|
||||
x = nil
|
||||
Thread.new { q.pop { |v| x = v } }.join
|
||||
assert_equal nil, x
|
||||
EM.run { EM.next_tick { EM.stop } }
|
||||
assert_equal 1, x
|
||||
end
|
||||
|
||||
def test_num_waiting
|
||||
q = EM::Queue.new
|
||||
many = 3
|
||||
many.times { q.pop {} }
|
||||
EM.run { EM.next_tick { EM.stop } }
|
||||
assert_equal many, q.num_waiting
|
||||
end
|
||||
|
||||
def test_big_queue
|
||||
EM.run do
|
||||
q = EM::Queue.new
|
||||
2000.times do |i|
|
||||
q.push(*0..1000)
|
||||
q.pop { |v| assert_equal v, i % 1001 }
|
||||
end
|
||||
q.pop do
|
||||
assert_equal 1_999_999, q.size
|
||||
EM.stop
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,105 @@
|
||||
require 'em_test_helper'
|
||||
|
||||
class TestResolver < Test::Unit::TestCase
|
||||
def test_nameserver
|
||||
assert_kind_of(String, EM::DNS::Resolver.nameserver)
|
||||
end
|
||||
|
||||
def test_nameservers
|
||||
assert_kind_of(Array, EM::DNS::Resolver.nameservers)
|
||||
end
|
||||
|
||||
def test_hosts
|
||||
assert_kind_of(Hash, EM::DNS::Resolver.hosts)
|
||||
|
||||
# Make sure that blank or comment lines are skipped
|
||||
refute(EM::DNS::Resolver.hosts.include? nil)
|
||||
end
|
||||
|
||||
def test_a
|
||||
pend('FIXME: this test is broken on Windows') if windows?
|
||||
|
||||
EM.run {
|
||||
d = EM::DNS::Resolver.resolve "example.com"
|
||||
d.errback { assert false }
|
||||
d.callback { |r|
|
||||
assert r
|
||||
EM.stop
|
||||
}
|
||||
}
|
||||
end
|
||||
|
||||
def test_bad_host
|
||||
EM.run {
|
||||
d = EM::DNS::Resolver.resolve "asdfasasdf"
|
||||
d.callback { assert false }
|
||||
d.errback { assert true; EM.stop }
|
||||
}
|
||||
end
|
||||
|
||||
def test_garbage
|
||||
assert_raises( ArgumentError ) {
|
||||
EM.run {
|
||||
EM::DNS::Resolver.resolve 123
|
||||
}
|
||||
}
|
||||
end
|
||||
|
||||
# There isn't a public DNS entry like 'example.com' with an A rrset
|
||||
def test_a_pair
|
||||
pend('FIXME: this test is broken on Windows') if windows?
|
||||
|
||||
EM.run {
|
||||
d = EM::DNS::Resolver.resolve "yahoo.com"
|
||||
d.errback { |err| assert false, "failed to resolve yahoo.com: #{err}" }
|
||||
d.callback { |r|
|
||||
assert_kind_of(Array, r)
|
||||
assert r.size > 1, "returned #{r.size} results: #{r.inspect}"
|
||||
EM.stop
|
||||
}
|
||||
}
|
||||
end
|
||||
|
||||
def test_localhost
|
||||
pend('FIXME: this test is broken on Windows') if windows?
|
||||
|
||||
EM.run {
|
||||
d = EM::DNS::Resolver.resolve "localhost"
|
||||
d.errback { assert false }
|
||||
d.callback { |r|
|
||||
assert_include(["127.0.0.1", "::1"], r.first)
|
||||
assert_kind_of(Array, r)
|
||||
|
||||
EM.stop
|
||||
}
|
||||
}
|
||||
end
|
||||
|
||||
def test_timer_cleanup
|
||||
pend('FIXME: this test is broken on Windows') if windows?
|
||||
|
||||
EM.run {
|
||||
d = EM::DNS::Resolver.resolve "example.com"
|
||||
d.errback { |err| assert false, "failed to resolve example.com: #{err}" }
|
||||
d.callback { |r|
|
||||
# This isn't a great test, but it's hard to get more canonical
|
||||
# confirmation that the timer is cancelled
|
||||
assert_nil(EM::DNS::Resolver.socket.instance_variable_get(:@timer))
|
||||
|
||||
EM.stop
|
||||
}
|
||||
}
|
||||
end
|
||||
|
||||
def test_failure_timer_cleanup
|
||||
EM.run {
|
||||
d = EM::DNS::Resolver.resolve "asdfasdf"
|
||||
d.callback { assert false }
|
||||
d.errback {
|
||||
assert_nil(EM::DNS::Resolver.socket.instance_variable_get(:@timer))
|
||||
|
||||
EM.stop
|
||||
}
|
||||
}
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,14 @@
|
||||
require 'em_test_helper'
|
||||
|
||||
class TestRunning < Test::Unit::TestCase
|
||||
def test_running
|
||||
assert_equal( false, EM::reactor_running? )
|
||||
r = false
|
||||
EM.run {
|
||||
r = EM::reactor_running?
|
||||
EM.stop
|
||||
}
|
||||
assert_equal( true, r )
|
||||
end
|
||||
end
|
||||
|
||||
@@ -0,0 +1,47 @@
|
||||
require 'em_test_helper'
|
||||
|
||||
|
||||
class TestSASL < Test::Unit::TestCase
|
||||
|
||||
# SASL authentication is usually done with UNIX-domain sockets, but
|
||||
# we'll use TCP so this test will work on Windows. As far as the
|
||||
# protocol handlers are concerned, there's no difference.
|
||||
|
||||
TestUser,TestPsw = "someone", "password"
|
||||
|
||||
class SaslServer < EM::Connection
|
||||
include EM::Protocols::SASLauth
|
||||
def validate usr, psw, sys, realm
|
||||
usr == TestUser and psw == TestPsw
|
||||
end
|
||||
end
|
||||
|
||||
class SaslClient < EM::Connection
|
||||
include EM::Protocols::SASLauthclient
|
||||
end
|
||||
|
||||
def setup
|
||||
@port = next_port
|
||||
end
|
||||
|
||||
def test_sasl
|
||||
resp = nil
|
||||
EM.run {
|
||||
EM.start_server( "127.0.0.1", @port, SaslServer )
|
||||
|
||||
c = EM.connect( "127.0.0.1", @port, SaslClient )
|
||||
d = c.validate?( TestUser, TestPsw )
|
||||
d.timeout 1
|
||||
d.callback {
|
||||
resp = true
|
||||
EM.stop
|
||||
}
|
||||
d.errback {
|
||||
resp = false
|
||||
EM.stop
|
||||
}
|
||||
}
|
||||
assert_equal( true, resp )
|
||||
end
|
||||
|
||||
end
|
||||
@@ -0,0 +1,217 @@
|
||||
require 'em_test_helper'
|
||||
require 'tempfile'
|
||||
|
||||
class TestSendFile < Test::Unit::TestCase
|
||||
|
||||
if EM.respond_to?(:send_file_data)
|
||||
module TestModule
|
||||
def initialize filename
|
||||
@filename = filename
|
||||
end
|
||||
|
||||
def post_init
|
||||
send_file_data @filename
|
||||
close_connection_after_writing
|
||||
end
|
||||
end
|
||||
|
||||
module TestClient
|
||||
def data_to(&blk)
|
||||
@data_to = blk
|
||||
end
|
||||
|
||||
def receive_data(data)
|
||||
@data_to.call(data) if @data_to
|
||||
end
|
||||
|
||||
def unbind
|
||||
EM.stop
|
||||
end
|
||||
end
|
||||
|
||||
def setup
|
||||
@file = Tempfile.new("em_test_file")
|
||||
@filename = @file.path
|
||||
@port = next_port
|
||||
end
|
||||
|
||||
def test_send_file
|
||||
File.open( @filename, "w" ) {|f|
|
||||
f << ("A" * 5000)
|
||||
}
|
||||
|
||||
data = ''
|
||||
|
||||
EM.run {
|
||||
EM.start_server "127.0.0.1", @port, TestModule, @filename
|
||||
setup_timeout
|
||||
|
||||
EM.connect "127.0.0.1", @port, TestClient do |c|
|
||||
c.data_to { |d| data << d }
|
||||
end
|
||||
}
|
||||
|
||||
assert_equal( "A" * 5000, data )
|
||||
end
|
||||
|
||||
# EM::Connection#send_file_data has a strict upper limit on the filesize it will work with.
|
||||
def test_send_large_file
|
||||
File.open( @filename, "w" ) {|f|
|
||||
f << ("A" * 1000000)
|
||||
}
|
||||
|
||||
data = ''
|
||||
|
||||
assert_raises(RuntimeError) {
|
||||
EM.run {
|
||||
EM.start_server "127.0.0.1", @port, TestModule, @filename
|
||||
setup_timeout
|
||||
EM.connect "127.0.0.1", @port, TestClient do |c|
|
||||
c.data_to { |d| data << d }
|
||||
end
|
||||
}
|
||||
}
|
||||
end
|
||||
|
||||
module StreamTestModule
|
||||
def initialize filename
|
||||
@filename = filename
|
||||
end
|
||||
|
||||
def post_init
|
||||
EM::Deferrable.future( stream_file_data(@filename)) {
|
||||
close_connection_after_writing
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
module ChunkStreamTestModule
|
||||
def initialize filename
|
||||
@filename = filename
|
||||
end
|
||||
|
||||
def post_init
|
||||
EM::Deferrable.future( stream_file_data(@filename, :http_chunks=>true)) {
|
||||
close_connection_after_writing
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
def test_stream_file_data
|
||||
File.open( @filename, "w" ) {|f|
|
||||
f << ("A" * 1000)
|
||||
}
|
||||
|
||||
data = ''
|
||||
|
||||
EM.run {
|
||||
EM.start_server "127.0.0.1", @port, StreamTestModule, @filename
|
||||
setup_timeout
|
||||
EM.connect "127.0.0.1", @port, TestClient do |c|
|
||||
c.data_to { |d| data << d }
|
||||
end
|
||||
}
|
||||
|
||||
assert_equal( "A" * 1000, data )
|
||||
end
|
||||
|
||||
def test_stream_chunked_file_data
|
||||
File.open( @filename, "w" ) {|f|
|
||||
f << ("A" * 1000)
|
||||
}
|
||||
|
||||
data = ''
|
||||
|
||||
EM.run {
|
||||
EM.start_server "127.0.0.1", @port, ChunkStreamTestModule, @filename
|
||||
setup_timeout
|
||||
EM.connect "127.0.0.1", @port, TestClient do |c|
|
||||
c.data_to { |d| data << d }
|
||||
end
|
||||
}
|
||||
|
||||
assert_equal( "3e8\r\n#{"A" * 1000}\r\n0\r\n\r\n", data )
|
||||
end
|
||||
|
||||
module BadFileTestModule
|
||||
def initialize filename
|
||||
@filename = filename
|
||||
end
|
||||
|
||||
def post_init
|
||||
de = stream_file_data( @filename+".wrong" )
|
||||
de.errback {|msg|
|
||||
send_data msg
|
||||
close_connection_after_writing
|
||||
}
|
||||
end
|
||||
end
|
||||
def test_stream_bad_file
|
||||
data = ''
|
||||
EM.run {
|
||||
EM.start_server "127.0.0.1", @port, BadFileTestModule, @filename
|
||||
setup_timeout(5)
|
||||
EM.connect "127.0.0.1", @port, TestClient do |c|
|
||||
c.data_to { |d| data << d }
|
||||
end
|
||||
}
|
||||
|
||||
assert_equal( "file not found", data )
|
||||
end
|
||||
else
|
||||
warn "EM.send_file_data not implemented, skipping tests in #{__FILE__}"
|
||||
|
||||
# Because some rubies will complain if a TestCase class has no tests
|
||||
def test_em_send_file_data_not_implemented
|
||||
assert !EM.respond_to?(:send_file_data)
|
||||
end
|
||||
end
|
||||
|
||||
begin
|
||||
require 'fastfilereaderext'
|
||||
|
||||
def test_stream_large_file_data
|
||||
File.open( @filename, "w" ) {|f|
|
||||
f << ("A" * 10000)
|
||||
}
|
||||
|
||||
data = ''
|
||||
|
||||
EM.run {
|
||||
EM.start_server "127.0.0.1", @port, StreamTestModule, @filename
|
||||
setup_timeout
|
||||
EM.connect "127.0.0.1", @port, TestClient do |c|
|
||||
c.data_to { |d| data << d }
|
||||
end
|
||||
}
|
||||
|
||||
assert_equal( "A" * 10000, data )
|
||||
end
|
||||
|
||||
def test_stream_large_chunked_file_data
|
||||
File.open( @filename, "w" ) {|f|
|
||||
f << ("A" * 100000)
|
||||
}
|
||||
|
||||
data = ''
|
||||
|
||||
EM.run {
|
||||
EM.start_server "127.0.0.1", @port, ChunkStreamTestModule, @filename
|
||||
setup_timeout
|
||||
EM.connect "127.0.0.1", @port, TestClient do |c|
|
||||
c.data_to { |d| data << d }
|
||||
end
|
||||
}
|
||||
|
||||
expected = [
|
||||
"4000\r\n#{"A" * 16384}\r\n" * 6,
|
||||
"6a0\r\n#{"A" * 0x6a0}\r\n",
|
||||
"0\r\n\r\n"
|
||||
].join
|
||||
assert_equal( expected, data )
|
||||
end
|
||||
rescue LoadError
|
||||
warn "require 'fastfilereaderext' failed, skipping tests in #{__FILE__}"
|
||||
end
|
||||
|
||||
end
|
||||
@@ -0,0 +1,33 @@
|
||||
require 'em_test_helper'
|
||||
require 'socket'
|
||||
|
||||
class TestServers < Test::Unit::TestCase
|
||||
|
||||
def setup
|
||||
@port = next_port
|
||||
end
|
||||
|
||||
def server_alive?
|
||||
port_in_use?(@port)
|
||||
end
|
||||
|
||||
def run_test_stop_server
|
||||
EM.run {
|
||||
sig = EM.start_server("127.0.0.1", @port)
|
||||
assert server_alive?, "Server didn't start"
|
||||
EM.stop_server sig
|
||||
# Give the server some time to shutdown.
|
||||
EM.add_timer(0.1) {
|
||||
assert !server_alive?, "Server didn't stop"
|
||||
EM.stop
|
||||
}
|
||||
}
|
||||
end
|
||||
|
||||
def test_stop_server
|
||||
assert !server_alive?, "Port already in use"
|
||||
2.times { run_test_stop_server }
|
||||
assert !server_alive?, "Servers didn't stop"
|
||||
end
|
||||
|
||||
end
|
||||
@@ -0,0 +1,23 @@
|
||||
require 'em_test_helper'
|
||||
|
||||
class TestShutdownHooks < Test::Unit::TestCase
|
||||
def test_shutdown_hooks
|
||||
r = false
|
||||
EM.run {
|
||||
EM.add_shutdown_hook { r = true }
|
||||
EM.stop
|
||||
}
|
||||
assert_equal( true, r )
|
||||
end
|
||||
|
||||
def test_hook_order
|
||||
r = []
|
||||
EM.run {
|
||||
EM.add_shutdown_hook { r << 2 }
|
||||
EM.add_shutdown_hook { r << 1 }
|
||||
EM.stop
|
||||
}
|
||||
assert_equal( [1, 2], r )
|
||||
end
|
||||
end
|
||||
|
||||
@@ -0,0 +1,75 @@
|
||||
require 'em_test_helper'
|
||||
|
||||
class TestSmtpClient < Test::Unit::TestCase
|
||||
|
||||
Localhost = "127.0.0.1"
|
||||
Localport = 9801
|
||||
|
||||
def setup
|
||||
end
|
||||
|
||||
def teardown
|
||||
end
|
||||
|
||||
def test_a
|
||||
# No real tests until we have a server implementation to test against.
|
||||
# This is what the call looks like, though:
|
||||
err = nil
|
||||
EM.run {
|
||||
d = EM::Protocols::SmtpClient.send :domain=>"example.com",
|
||||
:host=>Localhost,
|
||||
:port=>Localport, # optional, defaults 25
|
||||
:starttls=>true,
|
||||
:from=>"sender@example.com",
|
||||
:to=> ["to_1@example.com", "to_2@example.com"],
|
||||
:header=> {"Subject" => "This is a subject line"},
|
||||
:body=> "This is the body of the email",
|
||||
:verbose=>true
|
||||
d.errback {|e|
|
||||
err = e
|
||||
EM.stop
|
||||
}
|
||||
}
|
||||
assert(err)
|
||||
end
|
||||
|
||||
def test_content
|
||||
err = nil
|
||||
EM.run {
|
||||
d = EM::Protocols::SmtpClient.send :domain=>"example.com",
|
||||
:host=>Localhost,
|
||||
:port=>Localport, # optional, defaults 25
|
||||
:starttls=>true,
|
||||
:from=>"sender@example.com",
|
||||
:to=> ["to_1@example.com", "to_2@example.com"],
|
||||
:content => ["Subject: xxx\r\n\r\ndata\r\n.\r\n"],
|
||||
:verbose=>true
|
||||
d.errback {|e|
|
||||
err = e
|
||||
EM.stop
|
||||
}
|
||||
}
|
||||
assert(err)
|
||||
end
|
||||
|
||||
|
||||
EM::Protocols::SmtpClient.__send__(:public, :escape_leading_dots)
|
||||
|
||||
def test_escaping
|
||||
smtp = EM::Protocols::SmtpClient.new :domain => "example.com"
|
||||
|
||||
expectations = {
|
||||
"Hello\r\n" => "Hello\r\n",
|
||||
"\r\n.whatever\r\n" => "\r\n..whatever\r\n",
|
||||
"\r\n.\r\n" => "\r\n..\r\n",
|
||||
"\r\n.\r\n." => "\r\n..\r\n..",
|
||||
".\r\n.\r\n" => "..\r\n..\r\n",
|
||||
"..\r\n" => "...\r\n"
|
||||
}
|
||||
|
||||
expectations.each do |input, output|
|
||||
assert_equal output, smtp.escape_leading_dots(input)
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
@@ -0,0 +1,57 @@
|
||||
require 'em_test_helper'
|
||||
|
||||
class TestSmtpServer < Test::Unit::TestCase
|
||||
|
||||
# Don't test on port 25. It requires superuser and there's probably
|
||||
# a mail server already running there anyway.
|
||||
Localhost = "127.0.0.1"
|
||||
Localport = 25001
|
||||
|
||||
# This class is an example of what you need to write in order
|
||||
# to implement a mail server. You override the methods you are
|
||||
# interested in. Some, but not all, of these are illustrated here.
|
||||
#
|
||||
class Mailserver < EM::Protocols::SmtpServer
|
||||
|
||||
attr_reader :my_msg_body, :my_sender, :my_recipients
|
||||
|
||||
def initialize *args
|
||||
super
|
||||
end
|
||||
def receive_sender sender
|
||||
@my_sender = sender
|
||||
#p sender
|
||||
true
|
||||
end
|
||||
def receive_recipient rcpt
|
||||
@my_recipients ||= []
|
||||
@my_recipients << rcpt
|
||||
true
|
||||
end
|
||||
def receive_data_chunk c
|
||||
@my_msg_body = c.last
|
||||
end
|
||||
def connection_ended
|
||||
EM.stop
|
||||
end
|
||||
end
|
||||
|
||||
def test_mail
|
||||
c = nil
|
||||
EM.run {
|
||||
EM.start_server( Localhost, Localport, Mailserver ) {|conn| c = conn}
|
||||
EM::Timer.new(2) {EM.stop} # prevent hanging the test suite in case of error
|
||||
EM::Protocols::SmtpClient.send :host=>Localhost,
|
||||
:port=>Localport,
|
||||
:domain=>"bogus",
|
||||
:from=>"me@example.com",
|
||||
:to=>"you@example.com",
|
||||
:header=> {"Subject"=>"Email subject line", "Reply-to"=>"me@example.com"},
|
||||
:body=>"Not much of interest here."
|
||||
|
||||
}
|
||||
assert_equal( "Not much of interest here.", c.my_msg_body )
|
||||
assert_equal( "<me@example.com>", c.my_sender )
|
||||
assert_equal( ["<you@example.com>"], c.my_recipients )
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,54 @@
|
||||
require 'em_test_helper'
|
||||
require 'socket'
|
||||
|
||||
class TestSockOpt < Test::Unit::TestCase
|
||||
def setup
|
||||
assert(!EM.reactor_running?)
|
||||
@port = next_port
|
||||
end
|
||||
|
||||
def teardown
|
||||
assert(!EM.reactor_running?)
|
||||
end
|
||||
|
||||
def test_set_sock_opt
|
||||
omit_if(windows?)
|
||||
omit_if(!EM.respond_to?(:set_sock_opt))
|
||||
|
||||
val = nil
|
||||
test_module = Module.new do
|
||||
define_method :post_init do
|
||||
val = set_sock_opt Socket::SOL_SOCKET, Socket::SO_BROADCAST, true
|
||||
EM.stop
|
||||
end
|
||||
end
|
||||
|
||||
EM.run do
|
||||
EM.start_server '127.0.0.1', @port
|
||||
EM.connect '127.0.0.1', @port, test_module
|
||||
end
|
||||
|
||||
assert_equal 0, val
|
||||
end
|
||||
|
||||
def test_get_sock_opt
|
||||
omit_if(windows?)
|
||||
omit_if(!EM.respond_to?(:set_sock_opt))
|
||||
|
||||
val = nil
|
||||
test_module = Module.new do
|
||||
define_method :connection_completed do
|
||||
val = get_sock_opt Socket::SOL_SOCKET, Socket::SO_ERROR
|
||||
EM.stop
|
||||
end
|
||||
end
|
||||
|
||||
EM.run do
|
||||
EM.start_server '127.0.0.1', @port
|
||||
EM.connect '127.0.0.1', @port, test_module
|
||||
end
|
||||
|
||||
assert_equal "\0\0\0\0", val
|
||||
end
|
||||
|
||||
end
|
||||
@@ -0,0 +1,293 @@
|
||||
|
||||
require 'em_test_helper'
|
||||
|
||||
|
||||
|
||||
class TestSpawn < Test::Unit::TestCase
|
||||
|
||||
# Spawn a process that simply stops the reactor.
|
||||
# Assert that the notification runs after the block that calls it.
|
||||
#
|
||||
def test_stop
|
||||
x = nil
|
||||
EM.run {
|
||||
s = EM.spawn {EM.stop}
|
||||
s.notify
|
||||
x = true
|
||||
}
|
||||
assert x
|
||||
end
|
||||
|
||||
|
||||
# Pass a parameter to a spawned process.
|
||||
#
|
||||
def test_parms
|
||||
val = 5
|
||||
EM.run {
|
||||
s = EM.spawn {|v| val *= v; EM.stop}
|
||||
s.notify 3
|
||||
}
|
||||
assert_equal( 15, val )
|
||||
end
|
||||
|
||||
# Pass multiple parameters to a spawned process.
|
||||
#
|
||||
def test_multiparms
|
||||
val = 5
|
||||
EM.run {
|
||||
s = EM.spawn {|v1,v2| val *= (v1 + v2); EM.stop}
|
||||
s.notify 3,4
|
||||
}
|
||||
assert_equal( 35, val )
|
||||
end
|
||||
|
||||
|
||||
# This test demonstrates that a notification does not happen immediately,
|
||||
# but rather is scheduled sometime after the current code path completes.
|
||||
#
|
||||
def test_race
|
||||
x = 0
|
||||
EM.run {
|
||||
s = EM.spawn {x *= 2; EM.stop}
|
||||
s.notify
|
||||
x = 2
|
||||
}
|
||||
assert_equal( 4, x)
|
||||
end
|
||||
|
||||
|
||||
# Spawn a process and notify it 25 times to run fibonacci
|
||||
# on a pair of global variables.
|
||||
#
|
||||
def test_fibonacci
|
||||
x = 1
|
||||
y = 1
|
||||
EM.run {
|
||||
s = EM.spawn {x,y = y,x+y}
|
||||
25.times {s.notify}
|
||||
|
||||
t = EM.spawn {EM.stop}
|
||||
t.notify
|
||||
}
|
||||
assert_equal( 121393, x)
|
||||
assert_equal( 196418, y)
|
||||
end
|
||||
|
||||
# This one spawns 25 distinct processes, and notifies each one once,
|
||||
# rather than notifying a single process 25 times.
|
||||
#
|
||||
def test_another_fibonacci
|
||||
x = 1
|
||||
y = 1
|
||||
EM.run {
|
||||
25.times {
|
||||
s = EM.spawn {x,y = y,x+y}
|
||||
s.notify
|
||||
}
|
||||
|
||||
t = EM.spawn {EM.stop}
|
||||
t.notify
|
||||
}
|
||||
assert_equal( 121393, x)
|
||||
assert_equal( 196418, y)
|
||||
end
|
||||
|
||||
|
||||
# Make a chain of processes that notify each other in turn
|
||||
# with intermediate fibonacci results. The final process in
|
||||
# the chain stops the loop and returns the result.
|
||||
#
|
||||
def test_fibonacci_chain
|
||||
a,b = nil
|
||||
|
||||
EM.run {
|
||||
nextpid = EM.spawn {|x,y|
|
||||
a,b = x,y
|
||||
EM.stop
|
||||
}
|
||||
|
||||
25.times {
|
||||
n = nextpid
|
||||
nextpid = EM.spawn {|x,y| n.notify( y, x+y )}
|
||||
}
|
||||
|
||||
nextpid.notify( 1, 1 )
|
||||
}
|
||||
|
||||
assert_equal( 121393, a)
|
||||
assert_equal( 196418, b)
|
||||
end
|
||||
|
||||
|
||||
# EM#yield gives a spawed process to yield control to other processes
|
||||
# (in other words, to stop running), and to specify a different code block
|
||||
# that will run on its next notification.
|
||||
#
|
||||
def test_yield
|
||||
a = 0
|
||||
EM.run {
|
||||
n = EM.spawn {
|
||||
a += 10
|
||||
EM.yield {
|
||||
a += 20
|
||||
EM.yield {
|
||||
a += 30
|
||||
EM.stop
|
||||
}
|
||||
}
|
||||
}
|
||||
n.notify
|
||||
n.notify
|
||||
n.notify
|
||||
}
|
||||
assert_equal( 60, a )
|
||||
end
|
||||
|
||||
# EM#yield_and_notify behaves like EM#yield, except that it also notifies the
|
||||
# yielding process. This may sound trivial, since the yield block will run very
|
||||
# shortly after with no action by the program, but this actually can be very useful,
|
||||
# because it causes the reactor core to execute once before the yielding process
|
||||
# gets control back. So it can be used to allow heavily-used network connections
|
||||
# to clear buffers, or allow other processes to process their notifications.
|
||||
#
|
||||
# Notice in this test code that only a simple notify is needed at the bottom
|
||||
# of the initial block. Even so, all of the yielded blocks will execute.
|
||||
#
|
||||
def test_yield_and_notify
|
||||
a = 0
|
||||
EM.run {
|
||||
n = EM.spawn {
|
||||
a += 10
|
||||
EM.yield_and_notify {
|
||||
a += 20
|
||||
EM.yield_and_notify {
|
||||
a += 30
|
||||
EM.stop
|
||||
}
|
||||
}
|
||||
}
|
||||
n.notify
|
||||
}
|
||||
assert_equal( 60, a )
|
||||
end
|
||||
|
||||
# resume is an alias for notify.
|
||||
#
|
||||
def test_resume
|
||||
EM.run {
|
||||
n = EM.spawn {EM.stop}
|
||||
n.resume
|
||||
}
|
||||
assert true
|
||||
end
|
||||
|
||||
# run is an idiomatic alias for notify.
|
||||
#
|
||||
def test_run
|
||||
EM.run {
|
||||
(EM.spawn {EM.stop}).run
|
||||
}
|
||||
assert true
|
||||
end
|
||||
|
||||
|
||||
# Clones the ping-pong example from the Erlang tutorial, in much less code.
|
||||
# Illustrates that a spawned block executes in the context of a SpawnableObject.
|
||||
# (Meaning, we can pass self as a parameter to another process that can then
|
||||
# notify us.)
|
||||
#
|
||||
def test_ping_pong
|
||||
n_pongs = 0
|
||||
EM.run {
|
||||
pong = EM.spawn {|x, ping|
|
||||
n_pongs += 1
|
||||
ping.notify( x-1 )
|
||||
}
|
||||
ping = EM.spawn {|x|
|
||||
if x > 0
|
||||
pong.notify x, self
|
||||
else
|
||||
EM.stop
|
||||
end
|
||||
}
|
||||
ping.notify 3
|
||||
}
|
||||
assert_equal( 3, n_pongs )
|
||||
end
|
||||
|
||||
# Illustrates that you can call notify inside a notification, and it will cause
|
||||
# the currently-executing process to be re-notified. Of course, the new notification
|
||||
# won't run until sometime after the current one completes.
|
||||
#
|
||||
def test_self_notify
|
||||
n = 0
|
||||
EM.run {
|
||||
pid = EM.spawn {|x|
|
||||
if x > 0
|
||||
n += x
|
||||
notify( x-1 )
|
||||
else
|
||||
EM.stop
|
||||
end
|
||||
}
|
||||
pid.notify 3
|
||||
}
|
||||
assert_equal( 6, n )
|
||||
end
|
||||
|
||||
|
||||
# Illustrates that the block passed to #spawn executes in the context of a
|
||||
# SpawnedProcess object, NOT in the local context. This can often be deceptive.
|
||||
#
|
||||
class BlockScopeTest
|
||||
attr_reader :var
|
||||
def run
|
||||
# The following line correctly raises a NameError.
|
||||
# The problem is that the programmer expected the spawned block to
|
||||
# execute in the local context, but it doesn't.
|
||||
#
|
||||
# (EM.spawn { do_something }).notify ### NO! BAD!
|
||||
|
||||
|
||||
|
||||
# The following line correctly passes self as a parameter to the
|
||||
# notified process.
|
||||
#
|
||||
(EM.spawn {|obj| obj.do_something }).notify(self)
|
||||
|
||||
|
||||
|
||||
# Here's another way to do it. This works because "myself" is bound
|
||||
# in the local scope, unlike "self," so the spawned block sees it.
|
||||
#
|
||||
myself = self
|
||||
(EM.spawn { myself.do_something }).notify
|
||||
|
||||
|
||||
|
||||
# And we end the loop.
|
||||
# This is a tangential point, but observe that #notify never blocks.
|
||||
# It merely appends a message to the internal queue of a spawned process
|
||||
# and returns. As it turns out, the reactor processes notifications for ALL
|
||||
# spawned processes in the order that #notify is called. So there is a
|
||||
# reasonable expectation that the process which stops the reactor will
|
||||
# execute after the previous ones in this method. HOWEVER, this is NOT
|
||||
# a documented behavior and is subject to change.
|
||||
#
|
||||
(EM.spawn {EM.stop}).notify
|
||||
end
|
||||
def do_something
|
||||
@var ||= 0
|
||||
@var += 100
|
||||
end
|
||||
end
|
||||
|
||||
def test_block_scope
|
||||
bs = BlockScopeTest.new
|
||||
EM.run {
|
||||
bs.run
|
||||
}
|
||||
assert_equal( 200, bs.var )
|
||||
end
|
||||
|
||||
end
|
||||
@@ -0,0 +1,78 @@
|
||||
require "test/unit"
|
||||
require 'tempfile'
|
||||
|
||||
require 'em_test_helper'
|
||||
|
||||
module EM
|
||||
def self._set_mocks
|
||||
class <<self
|
||||
alias set_tls_parms_old set_tls_parms
|
||||
alias start_tls_old start_tls
|
||||
begin
|
||||
old, $VERBOSE = $VERBOSE, nil
|
||||
def set_tls_parms *args; end
|
||||
def start_tls *args; end
|
||||
ensure
|
||||
$VERBOSE = old
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def self._clear_mocks
|
||||
class <<self
|
||||
begin
|
||||
old, $VERBOSE = $VERBOSE, nil
|
||||
alias set_tls_parms set_tls_parms_old
|
||||
alias start_tls start_tls_old
|
||||
ensure
|
||||
$VERBOSE = old
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
|
||||
class TestSslArgs < Test::Unit::TestCase
|
||||
def setup
|
||||
EM._set_mocks
|
||||
end
|
||||
|
||||
def teardown
|
||||
EM._clear_mocks
|
||||
end
|
||||
|
||||
def test_tls_params_file_doesnt_exist
|
||||
priv_file, cert_file = 'foo_priv_key', 'bar_cert_file'
|
||||
[priv_file, cert_file].all? do |f|
|
||||
assert(!File.exist?(f), "Cert file #{f} seems to exist, and should not for the tests")
|
||||
end
|
||||
|
||||
# associate_callback_target is a pain! (build!)
|
||||
conn = EM::Connection.new('foo')
|
||||
|
||||
assert_raises(EM::FileNotFoundException) do
|
||||
conn.start_tls(:private_key_file => priv_file)
|
||||
end
|
||||
assert_raises(EM::FileNotFoundException) do
|
||||
conn.start_tls(:cert_chain_file => cert_file)
|
||||
end
|
||||
assert_raises(EM::FileNotFoundException) do
|
||||
conn.start_tls(:private_key_file => priv_file, :cert_chain_file => cert_file)
|
||||
end
|
||||
end
|
||||
|
||||
def test_tls_params_file_does_exist
|
||||
priv_file = Tempfile.new('em_test')
|
||||
cert_file = Tempfile.new('em_test')
|
||||
priv_file_path = priv_file.path
|
||||
cert_file_path = cert_file.path
|
||||
conn = EM::Connection.new('foo')
|
||||
params = {:private_key_file => priv_file_path, :cert_chain_file => cert_file_path}
|
||||
begin
|
||||
conn.start_tls params
|
||||
rescue Object
|
||||
assert(false, 'should not have raised an exception')
|
||||
end
|
||||
end
|
||||
end if EM.ssl?
|
||||
@@ -0,0 +1,84 @@
|
||||
require 'em_test_helper'
|
||||
|
||||
class TestSslDhParam < Test::Unit::TestCase
|
||||
def setup
|
||||
$dir = File.dirname(File.expand_path(__FILE__)) + '/'
|
||||
$dhparam_file = File.join($dir, 'dhparam.pem')
|
||||
end
|
||||
|
||||
module Client
|
||||
def post_init
|
||||
start_tls
|
||||
end
|
||||
|
||||
def ssl_handshake_completed
|
||||
$client_handshake_completed = true
|
||||
$client_cipher_name = get_cipher_name
|
||||
close_connection
|
||||
end
|
||||
|
||||
def unbind
|
||||
EM.stop_event_loop
|
||||
end
|
||||
end
|
||||
|
||||
module Server
|
||||
def post_init
|
||||
start_tls(:dhparam => $dhparam_file, :cipher_list => "DHE,EDH")
|
||||
end
|
||||
|
||||
def ssl_handshake_completed
|
||||
$server_handshake_completed = true
|
||||
$server_cipher_name = get_cipher_name
|
||||
end
|
||||
end
|
||||
|
||||
module NoDhServer
|
||||
def post_init
|
||||
start_tls(:cipher_list => "DHE,EDH")
|
||||
end
|
||||
|
||||
def ssl_handshake_completed
|
||||
$server_handshake_completed = true
|
||||
$server_cipher_name = get_cipher_name
|
||||
end
|
||||
end
|
||||
|
||||
def test_no_dhparam
|
||||
omit_unless(EM.ssl?)
|
||||
omit_if(EM.library_type == :pure_ruby) # DH will work with defaults
|
||||
omit_if(rbx?)
|
||||
|
||||
$client_handshake_completed, $server_handshake_completed = false, false
|
||||
$server_cipher_name, $client_cipher_name = nil, nil
|
||||
|
||||
EM.run {
|
||||
EM.start_server("127.0.0.1", 16784, NoDhServer)
|
||||
EM.connect("127.0.0.1", 16784, Client)
|
||||
}
|
||||
|
||||
assert(!$client_handshake_completed)
|
||||
assert(!$server_handshake_completed)
|
||||
end
|
||||
|
||||
def test_dhparam
|
||||
omit_unless(EM.ssl?)
|
||||
omit_if(rbx?)
|
||||
|
||||
$client_handshake_completed, $server_handshake_completed = false, false
|
||||
$server_cipher_name, $client_cipher_name = nil, nil
|
||||
|
||||
EM.run {
|
||||
EM.start_server("127.0.0.1", 16784, Server)
|
||||
EM.connect("127.0.0.1", 16784, Client)
|
||||
}
|
||||
|
||||
assert($client_handshake_completed)
|
||||
assert($server_handshake_completed)
|
||||
|
||||
assert($client_cipher_name.length > 0)
|
||||
assert_equal($client_cipher_name, $server_cipher_name)
|
||||
|
||||
assert_match(/^(DHE|EDH)/, $client_cipher_name)
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,80 @@
|
||||
require 'em_test_helper'
|
||||
|
||||
class TestSslEcdhCurve < Test::Unit::TestCase
|
||||
module Client
|
||||
def post_init
|
||||
start_tls
|
||||
end
|
||||
|
||||
def ssl_handshake_completed
|
||||
$client_handshake_completed = true
|
||||
$client_cipher_name = get_cipher_name
|
||||
close_connection
|
||||
end
|
||||
|
||||
def unbind
|
||||
EM.stop_event_loop
|
||||
end
|
||||
end
|
||||
|
||||
module Server
|
||||
def post_init
|
||||
start_tls(:ecdh_curve => "prime256v1", :cipher_list => "ECDH")
|
||||
end
|
||||
|
||||
def ssl_handshake_completed
|
||||
$server_handshake_completed = true
|
||||
$server_cipher_name = get_cipher_name
|
||||
end
|
||||
end
|
||||
|
||||
module NoCurveServer
|
||||
def post_init
|
||||
start_tls(:cipher_list => "ECDH")
|
||||
end
|
||||
|
||||
def ssl_handshake_completed
|
||||
$server_handshake_completed = true
|
||||
$server_cipher_name = get_cipher_name
|
||||
end
|
||||
end
|
||||
|
||||
def test_no_ecdh_curve
|
||||
omit_unless(EM.ssl?)
|
||||
omit_if(rbx?)
|
||||
|
||||
$client_handshake_completed, $server_handshake_completed = false, false
|
||||
|
||||
EM.run {
|
||||
EM.start_server("127.0.0.1", 16784, NoCurveServer)
|
||||
EM.connect("127.0.0.1", 16784, Client)
|
||||
}
|
||||
|
||||
assert(!$client_handshake_completed)
|
||||
assert(!$server_handshake_completed)
|
||||
end
|
||||
|
||||
def test_ecdh_curve
|
||||
omit_unless(EM.ssl?)
|
||||
omit_if(EM.library_type == :pure_ruby && RUBY_VERSION < "2.3.0")
|
||||
omit_if(rbx?)
|
||||
|
||||
$client_handshake_completed, $server_handshake_completed = false, false
|
||||
$server_cipher_name, $client_cipher_name = nil, nil
|
||||
|
||||
EM.run {
|
||||
EM.start_server("127.0.0.1", 16784, Server)
|
||||
EM.connect("127.0.0.1", 16784, Client)
|
||||
}
|
||||
|
||||
assert($client_handshake_completed)
|
||||
assert($server_handshake_completed)
|
||||
|
||||
assert($client_cipher_name.length > 0)
|
||||
assert_equal($client_cipher_name, $server_cipher_name)
|
||||
|
||||
assert_match(/^(AECDH|ECDHE)/, $client_cipher_name)
|
||||
end
|
||||
|
||||
|
||||
end
|
||||
@@ -0,0 +1,49 @@
|
||||
require 'em_test_helper'
|
||||
|
||||
require 'socket'
|
||||
require 'openssl'
|
||||
|
||||
if EM.ssl?
|
||||
class TestSslExtensions < Test::Unit::TestCase
|
||||
|
||||
module Client
|
||||
def ssl_handshake_completed
|
||||
$client_handshake_completed = true
|
||||
close_connection
|
||||
end
|
||||
|
||||
def unbind
|
||||
EM.stop_event_loop
|
||||
end
|
||||
|
||||
def post_init
|
||||
start_tls(:ssl_version => :tlsv1, :sni_hostname => 'example.com')
|
||||
end
|
||||
end
|
||||
|
||||
module Server
|
||||
def ssl_handshake_completed
|
||||
$server_handshake_completed = true
|
||||
$server_sni_hostname = get_sni_hostname
|
||||
end
|
||||
|
||||
def post_init
|
||||
start_tls(:ssl_version => :TLSv1)
|
||||
end
|
||||
end
|
||||
|
||||
def test_tlsext_sni_hostname
|
||||
$server_handshake_completed = false
|
||||
|
||||
EM.run do
|
||||
EM.start_server("127.0.0.1", 16784, Server)
|
||||
EM.connect("127.0.0.1", 16784, Client)
|
||||
end
|
||||
|
||||
assert($server_handshake_completed)
|
||||
assert_equal('example.com', $server_sni_hostname)
|
||||
end
|
||||
end
|
||||
else
|
||||
warn "EM built without SSL support, skipping tests in #{__FILE__}"
|
||||
end
|
||||
@@ -0,0 +1,65 @@
|
||||
require 'em_test_helper'
|
||||
|
||||
class TestSSLMethods < Test::Unit::TestCase
|
||||
|
||||
module ServerHandler
|
||||
def post_init
|
||||
start_tls
|
||||
end
|
||||
|
||||
def ssl_handshake_completed
|
||||
$server_called_back = true
|
||||
$server_cert_value = get_peer_cert
|
||||
$server_cipher_bits = get_cipher_bits
|
||||
$server_cipher_name = get_cipher_name
|
||||
$server_cipher_protocol = get_cipher_protocol
|
||||
end
|
||||
end
|
||||
|
||||
module ClientHandler
|
||||
def post_init
|
||||
start_tls
|
||||
end
|
||||
|
||||
def ssl_handshake_completed
|
||||
$client_called_back = true
|
||||
$client_cert_value = get_peer_cert
|
||||
$client_cipher_bits = get_cipher_bits
|
||||
$client_cipher_name = get_cipher_name
|
||||
$client_cipher_protocol = get_cipher_protocol
|
||||
EM.stop_event_loop
|
||||
end
|
||||
end
|
||||
|
||||
def test_ssl_methods
|
||||
omit_unless(EM.ssl?)
|
||||
omit_if(rbx?)
|
||||
$server_called_back, $client_called_back = false, false
|
||||
$server_cert_value, $client_cert_value = nil, nil
|
||||
$server_cipher_bits, $client_cipher_bits = nil, nil
|
||||
$server_cipher_name, $client_cipher_name = nil, nil
|
||||
$server_cipher_protocol, $client_cipher_protocol = nil, nil
|
||||
|
||||
EM.run {
|
||||
EM.start_server("127.0.0.1", 9999, ServerHandler)
|
||||
EM.connect("127.0.0.1", 9999, ClientHandler)
|
||||
}
|
||||
|
||||
assert($server_called_back)
|
||||
assert($client_called_back)
|
||||
|
||||
assert($server_cert_value.is_a?(NilClass))
|
||||
assert($client_cert_value.is_a?(String))
|
||||
|
||||
assert($client_cipher_bits > 0)
|
||||
assert_equal($client_cipher_bits, $server_cipher_bits)
|
||||
|
||||
assert($client_cipher_name.length > 0)
|
||||
assert_match(/AES/, $client_cipher_name)
|
||||
assert_equal($client_cipher_name, $server_cipher_name)
|
||||
|
||||
assert_match(/TLS/, $client_cipher_protocol)
|
||||
assert_equal($client_cipher_protocol, $server_cipher_protocol)
|
||||
end
|
||||
|
||||
end
|
||||
@@ -0,0 +1,246 @@
|
||||
require 'em_test_helper'
|
||||
|
||||
require 'socket'
|
||||
require 'openssl'
|
||||
|
||||
if EM.ssl?
|
||||
class TestSslProtocols < Test::Unit::TestCase
|
||||
|
||||
module Client
|
||||
def ssl_handshake_completed
|
||||
$client_handshake_completed = true
|
||||
close_connection
|
||||
end
|
||||
|
||||
def unbind
|
||||
EM.stop_event_loop
|
||||
end
|
||||
end
|
||||
|
||||
module Server
|
||||
def ssl_handshake_completed
|
||||
$server_handshake_completed = true
|
||||
end
|
||||
end
|
||||
|
||||
module ClientAny
|
||||
include Client
|
||||
def post_init
|
||||
start_tls(:ssl_version => %w(sslv2 sslv3 tlsv1 tlsv1_1 tlsv1_2))
|
||||
end
|
||||
end
|
||||
|
||||
module ClientDefault
|
||||
include Client
|
||||
def post_init
|
||||
start_tls
|
||||
end
|
||||
end
|
||||
|
||||
module ClientSSLv3
|
||||
include Client
|
||||
def post_init
|
||||
start_tls(:ssl_version => %w(SSLv3))
|
||||
end
|
||||
end
|
||||
|
||||
module ServerSSLv3
|
||||
include Server
|
||||
def post_init
|
||||
start_tls(:ssl_version => %w(SSLv3))
|
||||
end
|
||||
end
|
||||
|
||||
module ServerTLSv1CaseInsensitive
|
||||
include Server
|
||||
def post_init
|
||||
start_tls(:ssl_version => %w(tlsv1))
|
||||
end
|
||||
end
|
||||
|
||||
module ServerAny
|
||||
include Server
|
||||
def post_init
|
||||
start_tls(:ssl_version => %w(sslv2 sslv3 tlsv1 tlsv1_1 tlsv1_2))
|
||||
end
|
||||
end
|
||||
|
||||
module ServerDefault
|
||||
include Server
|
||||
def post_init
|
||||
start_tls
|
||||
end
|
||||
end
|
||||
|
||||
module InvalidProtocol
|
||||
include Client
|
||||
def post_init
|
||||
start_tls(:ssl_version => %w(tlsv1 badinput))
|
||||
end
|
||||
end
|
||||
|
||||
def test_invalid_ssl_version
|
||||
assert_raises(RuntimeError, "Unrecognized SSL/TLS Version: badinput") do
|
||||
EM.run do
|
||||
EM.start_server("127.0.0.1", 16784, InvalidProtocol)
|
||||
EM.connect("127.0.0.1", 16784, InvalidProtocol)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def test_any_to_v3
|
||||
$client_handshake_completed, $server_handshake_completed = false, false
|
||||
EM.run do
|
||||
EM.start_server("127.0.0.1", 16784, ServerSSLv3)
|
||||
EM.connect("127.0.0.1", 16784, ClientAny)
|
||||
end
|
||||
|
||||
assert($client_handshake_completed)
|
||||
assert($server_handshake_completed)
|
||||
end
|
||||
|
||||
def test_case_insensitivity
|
||||
$client_handshake_completed, $server_handshake_completed = false, false
|
||||
EM.run do
|
||||
EM.start_server("127.0.0.1", 16784, ServerTLSv1CaseInsensitive)
|
||||
EM.connect("127.0.0.1", 16784, ClientAny)
|
||||
end
|
||||
|
||||
assert($client_handshake_completed)
|
||||
assert($server_handshake_completed)
|
||||
end
|
||||
|
||||
def test_v3_to_any
|
||||
$client_handshake_completed, $server_handshake_completed = false, false
|
||||
EM.run do
|
||||
EM.start_server("127.0.0.1", 16784, ServerAny)
|
||||
EM.connect("127.0.0.1", 16784, ClientSSLv3)
|
||||
end
|
||||
|
||||
assert($client_handshake_completed)
|
||||
assert($server_handshake_completed)
|
||||
end
|
||||
|
||||
def test_v3_to_v3
|
||||
$client_handshake_completed, $server_handshake_completed = false, false
|
||||
EM.run do
|
||||
EM.start_server("127.0.0.1", 16784, ServerSSLv3)
|
||||
EM.connect("127.0.0.1", 16784, ClientSSLv3)
|
||||
end
|
||||
|
||||
assert($client_handshake_completed)
|
||||
assert($server_handshake_completed)
|
||||
end
|
||||
|
||||
def test_any_to_any
|
||||
$client_handshake_completed, $server_handshake_completed = false, false
|
||||
EM.run do
|
||||
EM.start_server("127.0.0.1", 16784, ServerAny)
|
||||
EM.connect("127.0.0.1", 16784, ClientAny)
|
||||
end
|
||||
|
||||
assert($client_handshake_completed)
|
||||
assert($server_handshake_completed)
|
||||
end
|
||||
|
||||
def test_default_to_default
|
||||
$client_handshake_completed, $server_handshake_completed = false, false
|
||||
EM.run do
|
||||
EM.start_server("127.0.0.1", 16784, ServerDefault)
|
||||
EM.connect("127.0.0.1", 16784, ClientDefault)
|
||||
end
|
||||
|
||||
assert($client_handshake_completed)
|
||||
assert($server_handshake_completed)
|
||||
end
|
||||
|
||||
module ServerV3StopAfterHandshake
|
||||
def post_init
|
||||
start_tls(:ssl_version => %w(SSLv3))
|
||||
end
|
||||
|
||||
def ssl_handshake_completed
|
||||
$server_handshake_completed = true
|
||||
EM.stop_event_loop
|
||||
end
|
||||
end
|
||||
|
||||
module ServerTLSv1StopAfterHandshake
|
||||
def post_init
|
||||
start_tls(:ssl_version => %w(TLSv1))
|
||||
end
|
||||
|
||||
def ssl_handshake_completed
|
||||
$server_handshake_completed = true
|
||||
EM.stop_event_loop
|
||||
end
|
||||
end
|
||||
|
||||
def test_v3_with_external_client
|
||||
$server_handshake_completed = false
|
||||
EM.run do
|
||||
setup_timeout(2)
|
||||
EM.start_server("127.0.0.1", 16784, ServerV3StopAfterHandshake)
|
||||
EM.defer do
|
||||
sock = TCPSocket.new("127.0.0.1", 16784)
|
||||
ctx = OpenSSL::SSL::SSLContext.new
|
||||
ctx.ssl_version = :SSLv3_client
|
||||
ssl = OpenSSL::SSL::SSLSocket.new(sock, ctx)
|
||||
ssl.connect
|
||||
ssl.close rescue nil
|
||||
sock.close rescue nil
|
||||
end
|
||||
end
|
||||
|
||||
assert($server_handshake_completed)
|
||||
end
|
||||
|
||||
def test_tlsv1_with_external_client
|
||||
$server_handshake_completed = false
|
||||
EM.run do
|
||||
setup_timeout(2)
|
||||
EM.start_server("127.0.0.1", 16784, ServerTLSv1StopAfterHandshake)
|
||||
EM.defer do
|
||||
sock = TCPSocket.new("127.0.0.1", 16784)
|
||||
ctx = OpenSSL::SSL::SSLContext.new
|
||||
ctx.ssl_version = :TLSv1_client
|
||||
ssl = OpenSSL::SSL::SSLSocket.new(sock, ctx)
|
||||
ssl.connect
|
||||
ssl.close rescue nil
|
||||
sock.close rescue nil
|
||||
end
|
||||
end
|
||||
|
||||
assert($server_handshake_completed)
|
||||
end
|
||||
|
||||
def test_tlsv1_required_with_external_client
|
||||
$server_handshake_completed = false
|
||||
|
||||
EM.run do
|
||||
n = 0
|
||||
EM.add_periodic_timer(0.5) do
|
||||
n += 1
|
||||
(EM.stop rescue nil) if n == 2
|
||||
end
|
||||
EM.start_server("127.0.0.1", 16784, ServerTLSv1StopAfterHandshake)
|
||||
EM.defer do
|
||||
sock = TCPSocket.new("127.0.0.1", 16784)
|
||||
ctx = OpenSSL::SSL::SSLContext.new
|
||||
ctx.ssl_version = :SSLv3_client
|
||||
ssl = OpenSSL::SSL::SSLSocket.new(sock, ctx)
|
||||
assert_raise OpenSSL::SSL::SSLError do
|
||||
ssl.connect
|
||||
end
|
||||
ssl.close rescue nil
|
||||
sock.close rescue nil
|
||||
EM.stop rescue nil
|
||||
end
|
||||
end
|
||||
|
||||
assert(!$server_handshake_completed)
|
||||
end
|
||||
end
|
||||
else
|
||||
warn "EM built without SSL support, skipping tests in #{__FILE__}"
|
||||
end
|
||||
@@ -0,0 +1,128 @@
|
||||
require 'em_test_helper'
|
||||
|
||||
class TestSslVerify < Test::Unit::TestCase
|
||||
def setup
|
||||
$dir = File.dirname(File.expand_path(__FILE__)) + '/'
|
||||
$cert_from_file = File.read($dir+'client.crt')
|
||||
end
|
||||
|
||||
module ClientNoCert
|
||||
def connection_completed
|
||||
start_tls()
|
||||
end
|
||||
|
||||
def ssl_handshake_completed
|
||||
$client_handshake_completed = true
|
||||
close_connection
|
||||
end
|
||||
|
||||
def unbind
|
||||
EM.stop_event_loop
|
||||
end
|
||||
end
|
||||
|
||||
module Client
|
||||
def connection_completed
|
||||
start_tls(:private_key_file => $dir+'client.key', :cert_chain_file => $dir+'client.crt')
|
||||
end
|
||||
|
||||
def ssl_handshake_completed
|
||||
$client_handshake_completed = true
|
||||
close_connection
|
||||
end
|
||||
|
||||
def unbind
|
||||
EM.stop_event_loop
|
||||
end
|
||||
end
|
||||
|
||||
module AcceptServer
|
||||
def post_init
|
||||
start_tls(:verify_peer => true)
|
||||
end
|
||||
|
||||
def ssl_verify_peer(cert)
|
||||
$cert_from_server = cert
|
||||
true
|
||||
end
|
||||
|
||||
def ssl_handshake_completed
|
||||
$server_handshake_completed = true
|
||||
end
|
||||
end
|
||||
|
||||
module DenyServer
|
||||
def post_init
|
||||
start_tls(:verify_peer => true)
|
||||
end
|
||||
|
||||
def ssl_verify_peer(cert)
|
||||
$cert_from_server = cert
|
||||
# Do not accept the peer. This should now cause the connection to shut down without the SSL handshake being completed.
|
||||
false
|
||||
end
|
||||
|
||||
def ssl_handshake_completed
|
||||
$server_handshake_completed = true
|
||||
end
|
||||
end
|
||||
|
||||
module FailServerNoPeerCert
|
||||
def post_init
|
||||
start_tls(:verify_peer => true, :fail_if_no_peer_cert => true)
|
||||
end
|
||||
|
||||
def ssl_verify_peer(cert)
|
||||
raise "Verify peer should not get called for a client without a certificate"
|
||||
end
|
||||
|
||||
def ssl_handshake_completed
|
||||
$server_handshake_completed = true
|
||||
end
|
||||
end
|
||||
|
||||
def test_fail_no_peer_cert
|
||||
omit_unless(EM.ssl?)
|
||||
omit_if(rbx?)
|
||||
|
||||
$client_handshake_completed, $server_handshake_completed = false, false
|
||||
|
||||
EM.run {
|
||||
EM.start_server("127.0.0.1", 16784, FailServerNoPeerCert)
|
||||
EM.connect("127.0.0.1", 16784, ClientNoCert)
|
||||
}
|
||||
|
||||
assert(!$client_handshake_completed)
|
||||
assert(!$server_handshake_completed)
|
||||
end
|
||||
|
||||
def test_accept_server
|
||||
omit_unless(EM.ssl?)
|
||||
omit_if(EM.library_type == :pure_ruby) # Server has a default cert chain
|
||||
omit_if(rbx?)
|
||||
$client_handshake_completed, $server_handshake_completed = false, false
|
||||
EM.run {
|
||||
EM.start_server("127.0.0.1", 16784, AcceptServer)
|
||||
EM.connect("127.0.0.1", 16784, Client).instance_variable_get("@signature")
|
||||
}
|
||||
|
||||
assert_equal($cert_from_file, $cert_from_server)
|
||||
assert($client_handshake_completed)
|
||||
assert($server_handshake_completed)
|
||||
end
|
||||
|
||||
def test_deny_server
|
||||
omit_unless(EM.ssl?)
|
||||
omit_if(EM.library_type == :pure_ruby) # Server has a default cert chain
|
||||
omit_if(rbx?)
|
||||
$client_handshake_completed, $server_handshake_completed = false, false
|
||||
EM.run {
|
||||
EM.start_server("127.0.0.1", 16784, DenyServer)
|
||||
EM.connect("127.0.0.1", 16784, Client)
|
||||
}
|
||||
|
||||
assert_equal($cert_from_file, $cert_from_server)
|
||||
assert(!$client_handshake_completed)
|
||||
assert(!$server_handshake_completed)
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,38 @@
|
||||
require 'em_test_helper'
|
||||
|
||||
class TestStomp < Test::Unit::TestCase
|
||||
CONTENT_LENGTH_REGEX = /^content-length: (\d+)$/
|
||||
|
||||
def bytesize(str)
|
||||
str = str.to_s
|
||||
size = str.bytesize if str.respond_to?(:bytesize) # bytesize added in 1.9
|
||||
size || str.size
|
||||
end
|
||||
|
||||
class TStomp
|
||||
include EM::P::Stomp
|
||||
|
||||
def last_sent_content_length
|
||||
@sent && Integer(@sent[CONTENT_LENGTH_REGEX, 1])
|
||||
end
|
||||
|
||||
def send_data(string)
|
||||
@sent = string
|
||||
end
|
||||
end
|
||||
|
||||
def test_content_length_in_bytes
|
||||
connection = TStomp.new
|
||||
|
||||
queue = "queue"
|
||||
failure_message = "header content-length is not the byte size of last sent body"
|
||||
|
||||
body = "test"
|
||||
connection.send queue, body
|
||||
assert_equal bytesize(body), connection.last_sent_content_length, failure_message
|
||||
|
||||
body = "test\u221A"
|
||||
connection.send queue, body
|
||||
assert_equal bytesize(body), connection.last_sent_content_length, failure_message
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,46 @@
|
||||
# coding: utf-8
|
||||
require 'em_test_helper'
|
||||
|
||||
class TestSystem < Test::Unit::TestCase
|
||||
def setup
|
||||
@filename = File.expand_path("../я манал dump.txt", __FILE__)
|
||||
@test_data = 'a' * 100
|
||||
File.open(@filename, 'w'){|f| f.write(@test_data)}
|
||||
end
|
||||
|
||||
def test_system
|
||||
omit_if(windows?)
|
||||
|
||||
result = nil
|
||||
status = nil
|
||||
EM.run {
|
||||
EM.system('cat', @filename){|out, state|
|
||||
result = out
|
||||
status = state.exitstatus
|
||||
EM.stop
|
||||
}
|
||||
}
|
||||
assert_equal(0, status)
|
||||
assert_equal(@test_data, result)
|
||||
end
|
||||
|
||||
def test_system_with_string
|
||||
omit_if(windows?)
|
||||
|
||||
result = nil
|
||||
status = nil
|
||||
EM.run {
|
||||
EM.system("cat '#@filename'"){|out, state|
|
||||
result = out
|
||||
status = state.exitstatus
|
||||
EM.stop
|
||||
}
|
||||
}
|
||||
assert_equal(0, status)
|
||||
assert_equal(@test_data, result)
|
||||
end
|
||||
|
||||
def teardown
|
||||
File.unlink(@filename)
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,63 @@
|
||||
require 'em_test_helper'
|
||||
|
||||
class TestThreadedResource < Test::Unit::TestCase
|
||||
def object
|
||||
@object ||= {}
|
||||
end
|
||||
|
||||
def resource
|
||||
@resource = EM::ThreadedResource.new do
|
||||
object
|
||||
end
|
||||
end
|
||||
|
||||
def teardown
|
||||
resource.shutdown
|
||||
end
|
||||
|
||||
def test_dispatch_completion
|
||||
EM.run do
|
||||
EM.add_timer(3) do
|
||||
EM.stop
|
||||
fail 'Resource dispatch timed out'
|
||||
end
|
||||
completion = resource.dispatch do |o|
|
||||
o[:foo] = :bar
|
||||
:foo
|
||||
end
|
||||
completion.callback do |result|
|
||||
assert_equal :foo, result
|
||||
EM.stop
|
||||
end
|
||||
completion.errback do |error|
|
||||
EM.stop
|
||||
fail "Unexpected error: #{error.message}"
|
||||
end
|
||||
end
|
||||
assert_equal :bar, object[:foo]
|
||||
end
|
||||
|
||||
def test_dispatch_failure
|
||||
completion = resource.dispatch do |o|
|
||||
raise 'boom'
|
||||
end
|
||||
completion.errback do |error|
|
||||
assert_kind_of RuntimeError, error
|
||||
assert_equal 'boom', error.message
|
||||
end
|
||||
end
|
||||
|
||||
def test_dispatch_threading
|
||||
main = Thread.current
|
||||
resource.dispatch do |o|
|
||||
o[:dispatch_thread] = Thread.current
|
||||
end
|
||||
assert_not_equal main, object[:dispatch_thread]
|
||||
end
|
||||
|
||||
def test_shutdown
|
||||
# This test should get improved sometime. The method returning thread is
|
||||
# NOT an api that will be maintained.
|
||||
assert !resource.shutdown.alive?
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,59 @@
|
||||
require "test/unit"
|
||||
require 'em_test_helper'
|
||||
|
||||
class TestEmTickLoop < Test::Unit::TestCase
|
||||
def test_em_tick_loop
|
||||
i = 0
|
||||
EM.tick_loop { i += 1; EM.stop if i == 10 }
|
||||
EM.run { EM.add_timer(1) { EM.stop } }
|
||||
assert_equal i, 10
|
||||
end
|
||||
|
||||
def test_tick_loop_on_stop
|
||||
t = nil
|
||||
tick_loop = EM.tick_loop { :stop }
|
||||
tick_loop.on_stop { t = true }
|
||||
EM.run { EM.next_tick { EM.stop } }
|
||||
assert t
|
||||
end
|
||||
|
||||
def test_start_twice
|
||||
i = 0
|
||||
s = 0
|
||||
tick_loop = EM.tick_loop { i += 1; :stop }
|
||||
tick_loop.on_stop { s += 1; EM.stop }
|
||||
EM.run { EM.next_tick { EM.stop } }
|
||||
assert_equal 1, i
|
||||
assert_equal 1, s
|
||||
tick_loop.start
|
||||
EM.run { EM.next_tick { EM.stop } }
|
||||
assert_equal 2, i
|
||||
assert_equal 1, s # stop callbacks are only called once
|
||||
end
|
||||
|
||||
def test_stop
|
||||
i, s = 0, 0
|
||||
tick_loop = EM.tick_loop { i += 1 }
|
||||
tick_loop.on_stop { s += 1 }
|
||||
EM.run { EM.next_tick { tick_loop.stop; EM.next_tick { EM.stop } } }
|
||||
assert tick_loop.stopped?
|
||||
assert_equal 1, i
|
||||
assert_equal 1, s
|
||||
end
|
||||
|
||||
def test_immediate_stops
|
||||
s = 0
|
||||
tick_loop = EM::TickLoop.new { }
|
||||
tick_loop.on_stop { s += 1 }
|
||||
tick_loop.on_stop { s += 1 }
|
||||
assert_equal 2, s
|
||||
end
|
||||
|
||||
def test_stopped
|
||||
tick_loop = EM::TickLoop.new { }
|
||||
assert tick_loop.stopped?
|
||||
tick_loop.start
|
||||
assert !tick_loop.stopped?
|
||||
end
|
||||
|
||||
end
|
||||
@@ -0,0 +1,130 @@
|
||||
require 'em_test_helper'
|
||||
|
||||
class TestTimers < Test::Unit::TestCase
|
||||
|
||||
def test_timer_with_block
|
||||
x = false
|
||||
EM.run {
|
||||
EM::Timer.new(0) {
|
||||
x = true
|
||||
EM.stop
|
||||
}
|
||||
}
|
||||
assert x
|
||||
end
|
||||
|
||||
def test_timer_with_proc
|
||||
x = false
|
||||
EM.run {
|
||||
EM::Timer.new(0, proc {
|
||||
x = true
|
||||
EM.stop
|
||||
})
|
||||
}
|
||||
assert x
|
||||
end
|
||||
|
||||
def test_timer_cancel
|
||||
assert_nothing_raised do
|
||||
EM.run {
|
||||
timer = EM::Timer.new(0.01) { flunk "Timer was not cancelled." }
|
||||
timer.cancel
|
||||
|
||||
EM.add_timer(0.02) { EM.stop }
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
def test_periodic_timer
|
||||
x = 0
|
||||
EM.run {
|
||||
EM::PeriodicTimer.new(0.01) do
|
||||
x += 1
|
||||
EM.stop if x == 4
|
||||
end
|
||||
}
|
||||
|
||||
assert_equal 4, x
|
||||
end
|
||||
|
||||
def test_add_periodic_timer
|
||||
x = 0
|
||||
EM.run {
|
||||
t = EM.add_periodic_timer(0.01) do
|
||||
x += 1
|
||||
EM.stop if x == 4
|
||||
end
|
||||
assert t.respond_to?(:cancel)
|
||||
}
|
||||
assert_equal 4, x
|
||||
end
|
||||
|
||||
def test_periodic_timer_cancel
|
||||
x = 0
|
||||
EM.run {
|
||||
pt = EM::PeriodicTimer.new(0.01) { x += 1 }
|
||||
pt.cancel
|
||||
EM::Timer.new(0.02) { EM.stop }
|
||||
}
|
||||
assert_equal 0, x
|
||||
end
|
||||
|
||||
def test_add_periodic_timer_cancel
|
||||
x = 0
|
||||
EM.run {
|
||||
pt = EM.add_periodic_timer(0.01) { x += 1 }
|
||||
EM.cancel_timer(pt)
|
||||
EM.add_timer(0.02) { EM.stop }
|
||||
}
|
||||
assert_equal 0, x
|
||||
end
|
||||
|
||||
def test_periodic_timer_self_cancel
|
||||
x = 0
|
||||
EM.run {
|
||||
pt = EM::PeriodicTimer.new(0) {
|
||||
x += 1
|
||||
if x == 4
|
||||
pt.cancel
|
||||
EM.stop
|
||||
end
|
||||
}
|
||||
}
|
||||
assert_equal 4, x
|
||||
end
|
||||
|
||||
def test_oneshot_timer_large_future_value
|
||||
large_value = 11948602000
|
||||
EM.run {
|
||||
EM.add_timer(large_value) { EM.stop }
|
||||
EM.add_timer(0.02) { EM.stop }
|
||||
}
|
||||
end
|
||||
|
||||
# This test is only applicable to compiled versions of the reactor.
|
||||
# Pure ruby and java versions have no built-in limit on the number of outstanding timers.
|
||||
unless [:pure_ruby, :java].include? EM.library_type
|
||||
def test_timer_change_max_outstanding
|
||||
defaults = EM.get_max_timers
|
||||
EM.set_max_timers(100)
|
||||
|
||||
one_hundred_one_timers = lambda do
|
||||
101.times { EM.add_timer(0.01) {} }
|
||||
EM.stop
|
||||
end
|
||||
|
||||
assert_raises(RuntimeError) do
|
||||
EM.run( &one_hundred_one_timers )
|
||||
end
|
||||
|
||||
EM.set_max_timers( 101 )
|
||||
|
||||
assert_nothing_raised do
|
||||
EM.run( &one_hundred_one_timers )
|
||||
end
|
||||
ensure
|
||||
EM.set_max_timers(defaults)
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
@@ -0,0 +1,8 @@
|
||||
require 'em_test_helper'
|
||||
|
||||
class TestUserDefinedEvents < Test::Unit::TestCase
|
||||
|
||||
def test_a
|
||||
end
|
||||
|
||||
end
|
||||
@@ -0,0 +1,40 @@
|
||||
require 'em_test_helper'
|
||||
|
||||
class TestUnbindReason < Test::Unit::TestCase
|
||||
|
||||
class StubConnection < EM::Connection
|
||||
attr_reader :error
|
||||
def unbind(reason = nil)
|
||||
@error = reason
|
||||
EM.stop
|
||||
end
|
||||
end
|
||||
|
||||
# RFC 5737 Address Blocks Reserved for Documentation
|
||||
def test_connect_timeout
|
||||
conn = nil
|
||||
EM.run do
|
||||
conn = EM.connect '192.0.2.0', 80, StubConnection
|
||||
conn.pending_connect_timeout = 1
|
||||
end
|
||||
assert_equal Errno::ETIMEDOUT, conn.error
|
||||
end
|
||||
|
||||
def test_connect_refused
|
||||
pend('FIXME: this test is broken on Windows') if windows?
|
||||
conn = nil
|
||||
EM.run do
|
||||
conn = EM.connect '127.0.0.1', 12388, StubConnection
|
||||
end
|
||||
assert_equal Errno::ECONNREFUSED, conn.error
|
||||
end
|
||||
|
||||
def test_optional_argument
|
||||
pend('FIXME: this test is broken on Windows') if windows?
|
||||
conn = nil
|
||||
EM.run do
|
||||
conn = EM.connect '127.0.0.1', 12388, StubConnection
|
||||
end
|
||||
assert_equal Errno::ECONNREFUSED, conn.error
|
||||
end
|
||||
end
|
||||
Reference in New Issue
Block a user