File: test_futures.rb

package info (click to toggle)
ruby-eventmachine 1.0.3-6%2Bdeb8u1
  • links: PTS, VCS
  • area: main
  • in suites: jessie
  • size: 2,000 kB
  • ctags: 3,178
  • sloc: ruby: 8,641; cpp: 5,217; java: 827; makefile: 5
file content (170 lines) | stat: -rw-r--r-- 4,564 bytes parent folder | download | duplicates (4)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
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