This commit is contained in:
@@ -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
|
||||
Reference in New Issue
Block a user