Add bin and edit workflow
Gitea Actions Demo / Explore-Gitea-Actions (push) Failing after 9s

This commit is contained in:
2026-09-16 13:11:16 -06:00
parent c8ac4fcae5
commit 4cee170d66
17576 changed files with 895740 additions and 2 deletions
@@ -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