root/version_0/tests/test_futures.rb
| Revision 648, 5.2 kB (checked in by blackhedd, 1 year ago) | |
|---|---|
| |
| Line | |
|---|---|
| 1 | # $Id$ |
| 2 | # |
| 3 | # Author:: Francis Cianfrocca (gmail: blackhedd) |
| 4 | # Homepage:: http://rubyeventmachine.com |
| 5 | # Date:: 8 April 2006 |
| 6 | # |
| 7 | # See EventMachine and EventMachine::Connection for documentation and |
| 8 | # usage examples. |
| 9 | # |
| 10 | #---------------------------------------------------------------------------- |
| 11 | # |
| 12 | # Copyright (C) 2006-07 by Francis Cianfrocca. All Rights Reserved. |
| 13 | # Gmail: blackhedd |
| 14 | # |
| 15 | # This program is free software; you can redistribute it and/or modify |
| 16 | # it under the terms of either: 1) the GNU General Public License |
| 17 | # as published by the Free Software Foundation; either version 2 of the |
| 18 | # License, or (at your option) any later version; or 2) Ruby's License. |
| 19 | # |
| 20 | # See the file COPYING for complete licensing information. |
| 21 | # |
| 22 | #--------------------------------------------------------------------------- |
| 23 | # |
| 24 | # |
| 25 | # |
| 26 | |
| 27 | $:.unshift "../lib" |
| 28 | require 'eventmachine' |
| 29 | require 'test/unit' |
| 30 | |
| 31 | |
| 32 | |
| 33 | class TestFutures < Test::Unit::TestCase |
| 34 | |
| 35 | def setup |
| 36 | end |
| 37 | |
| 38 | def teardown |
| 39 | end |
| 40 | |
| 41 | def test_future |
| 42 | assert_equal(100, EventMachine::Deferrable.future(100) ) |
| 43 | |
| 44 | p1 = proc { 100 + 1 } |
| 45 | assert_equal(101, EventMachine::Deferrable.future(p1) ) |
| 46 | end |
| 47 | |
| 48 | |
| 49 | |
| 50 | class MyFuture |
| 51 | include EventMachine::Deferrable |
| 52 | def initialize *args |
| 53 | super |
| 54 | set_deferred_status :succeeded, 40 |
| 55 | end |
| 56 | end |
| 57 | |
| 58 | class MyErrorFuture |
| 59 | include EventMachine::Deferrable |
| 60 | def initialize *args |
| 61 | super |
| 62 | set_deferred_status :failed, 41 |
| 63 | end |
| 64 | end |
| 65 | |
| 66 | |
| 67 | def test_future_1 |
| 68 | # Call future with one additional argument and it will be treated as a callback. |
| 69 | def my_future |
| 70 | MyFuture.new |
| 71 | end |
| 72 | |
| 73 | value = nil |
| 74 | EventMachine::Deferrable.future my_future, proc {|v| value=v} |
| 75 | assert_equal( 40, value ) |
| 76 | end |
| 77 | |
| 78 | |
| 79 | def test_future_2 |
| 80 | # Call future with two additional arguments and they will be treated as a callback |
| 81 | # and an errback. |
| 82 | value = nil |
| 83 | EventMachine::Deferrable.future MyErrorFuture.new, nil, proc {|v| value=v} |
| 84 | assert_equal( 41, value ) |
| 85 | end |
| 86 | |
| 87 | |
| 88 | def test_future_3 |
| 89 | # Call future with no additional arguments but with a block, and the block will be |
| 90 | # treated as a callback. |
| 91 | value = nil |
| 92 | EventMachine::Deferrable.future MyFuture.new do |v| |
| 93 | value=v |
| 94 | end |
| 95 | assert_equal( 40, value ) |
| 96 | end |
| 97 | |
| 98 | |
| 99 | class RecursiveCallback |
| 100 | include EventMachine::Deferrable |
| 101 | end |
| 102 | |
| 103 | # A Deferrable callback can call #set_deferred_status to change the values |
| 104 | # passed to subsequent callbacks. |
| 105 | # |
| 106 | def test_recursive_callbacks |
| 107 | n = 0 # counter assures that all the tests actually run. |
| 108 | rc = RecursiveCallback.new |
| 109 | rc.callback {|a| |
| 110 | assert_equal(100, a) |
| 111 | n += 1 |
| 112 | rc.set_deferred_status :succeeded, 101, 101 |
| 113 | } |
| 114 | rc.callback {|a,b| |
| 115 | assert_equal(101, a) |
| 116 | assert_equal(101, b) |
| 117 | n += 1 |
| 118 | rc.set_deferred_status :succeeded, 102, 102, 102 |
| 119 | } |
| 120 | rc.callback {|a,b,c| |
| 121 | assert_equal(102, a) |
| 122 | assert_equal(102, b) |
| 123 | assert_equal(102, c) |
| 124 | n += 1 |
| 125 | } |
| 126 | rc.set_deferred_status :succeeded, 100 |
| 127 | assert_equal(3, n) |
| 128 | end |
| 129 | |
| 130 | |
| 131 | |
| 132 | def test_syntactic_sugar |
| 133 | rc = RecursiveCallback.new |
| 134 | rc.set_deferred_success 100 |
| 135 | rc.set_deferred_failure 200 |
| 136 | end |
| 137 | |
| 138 | |
| 139 | |
| 140 | # It doesn't raise an error to set deferred status more than once. |
| 141 | # In fact, this is a desired and useful idiom when it happens INSIDE |
| 142 | # a callback or errback. |
| 143 | # However, it's less useful otherwise, and in fact would generally be |
| 144 | # indicative of a programming error. However, we would like to be resistant |
| 145 | # to such errors. So whenever we set deferred status, we also clear BOTH |
| 146 | # stacks of handlers. |
| 147 | # |
| 148 | def test_double_calls |
| 149 | s = 0 |
| 150 | e = 0 |
| 151 | |
| 152 | d = EM::DefaultDeferrable.new |
| 153 | d.callback {s += 1} |
| 154 | d.errback {e += 1} |
| 155 | |
| 156 | d.succeed # We expect the callback to be called, and the errback to be DISCARDED. |
| 157 | d.fail # Presumably an error. We expect the errback NOT to be called. |
| 158 | d.succeed # We expect the callback to have been discarded and NOT to be called again. |
| 159 | |
| 160 | assert_equal(1, s) |
| 161 | assert_equal(0, e) |
| 162 | end |
| 163 | |
| 164 | |
| 165 | # Adding a callback to a Deferrable that is already in a success state executes the callback |
| 166 | # immediately. The same applies to a an errback added to an already-failed Deferrable. |
| 167 | # HOWEVER, we expect NOT to be able to add errbacks to succeeded Deferrables, or callbacks |
| 168 | # to failed ones. |
| 169 | # |
| 170 | # We illustrate this with a rather contrived test. The test calls #fail after #succeed, |
| 171 | # which ordinarily would not happen in a real program. |
| 172 | # |
| 173 | # What we're NOT attempting to specify is what happens if a Deferrable is succeeded and then |
| 174 | # failed (or vice-versa). Should we then be able to add callbacks/errbacks of the appropriate |
| 175 | # type for immediate execution? For now at least, the official answer is "don't do that." |
| 176 | # |
| 177 | def test_delayed_callbacks |
| 178 | s1 = 0 |
| 179 | s2 = 0 |
| 180 | e = 0 |
| 181 | |
| 182 | d = EM::DefaultDeferrable.new |
| 183 | d.callback {s1 += 1} |
| 184 | |
| 185 | d.succeed # Triggers and discards the callback. |
| 186 | |
| 187 | d.callback {s2 += 1} # This callback is executed immediately and discarded. |
| 188 | |
| 189 | d.errback {e += 1} # This errback should be DISCARDED and never execute. |
| 190 | d.fail # To prove it, let's |
| 191 | |
| 192 | assert_equal( [1,1], [s1,s2] ) |
| 193 | assert_equal( 0, e ) |
| 194 | end |
| 195 | |
| 196 | |
| 197 | |
| 198 | |
| 199 | # |
| 200 | # |
| 201 | # |
| 202 | def test_timeout |
| 203 | n = 0 |
| 204 | EM.run { |
| 205 | d = EM::DefaultDeferrable.new |
| 206 | d.callback {n = 1; EM.stop} |
| 207 | d.errback {n = 2; EM.stop} |
| 208 | d.timeout(1) |
| 209 | } |
| 210 | assert_equal( 2, n ) |
| 211 | end |
| 212 | |
| 213 | end |
| 214 |
Note: See TracBrowser for help on using the browser.
