future_test.py revision 5f1c94371a64b3196d4be9466099bb892df9b88e
1#!/usr/bin/env python
2# Copyright 2013 The Chromium Authors. All rights reserved.
3# Use of this source code is governed by a BSD-style license that can be
4# found in the LICENSE file.
5
6import traceback
7import unittest
8
9
10from future import All, Future, Race
11from mock_function import MockFunction
12
13
14class FutureTest(unittest.TestCase):
15  def testNoValueOrDelegate(self):
16    self.assertRaises(ValueError, Future)
17
18  def testValue(self):
19    future = Future(value=42)
20    self.assertEqual(42, future.Get())
21    self.assertEqual(42, future.Get())
22
23  def testDelegateValue(self):
24    called = [False,]
25    def callback():
26      self.assertFalse(called[0])
27      called[0] = True
28      return 42
29    future = Future(callback=callback)
30    self.assertEqual(42, future.Get())
31    self.assertEqual(42, future.Get())
32
33  def testErrorThrowingDelegate(self):
34    class FunkyException(Exception):
35      pass
36
37    # Set up a chain of functions to test the stack trace.
38    def qux():
39      raise FunkyException()
40    def baz():
41      return qux()
42    def bar():
43      return baz()
44    def foo():
45      return bar()
46    chain = [foo, bar, baz, qux]
47
48    called = [False,]
49    def callback():
50      self.assertFalse(called[0])
51      called[0] = True
52      return foo()
53
54    fail = self.fail
55    assertTrue = self.assertTrue
56    def assert_raises_full_stack(future, err):
57      try:
58        future.Get()
59        fail('Did not raise %s' % err)
60      except Exception as e:
61        assertTrue(isinstance(e, err))
62        stack = traceback.format_exc()
63        assertTrue(all(stack.find(fn.__name__) != -1 for fn in chain))
64
65    future = Future(callback=callback)
66    assert_raises_full_stack(future, FunkyException)
67    assert_raises_full_stack(future, FunkyException)
68
69  def testAll(self):
70    def callback_with_value(value):
71      return MockFunction(lambda: value)
72
73    # Test a single value.
74    callback = callback_with_value(42)
75    future = All((Future(callback=callback),))
76    self.assertTrue(*callback.CheckAndReset(0))
77    self.assertEqual([42], future.Get())
78    self.assertTrue(*callback.CheckAndReset(1))
79
80    # Test multiple callbacks.
81    callbacks = (callback_with_value(1),
82                 callback_with_value(2),
83                 callback_with_value(3))
84    future = All(Future(callback=callback) for callback in callbacks)
85    for callback in callbacks:
86      self.assertTrue(*callback.CheckAndReset(0))
87    self.assertEqual([1, 2, 3], future.Get())
88    for callback in callbacks:
89      self.assertTrue(*callback.CheckAndReset(1))
90
91    # Test throwing an error.
92    def throws_error():
93      raise ValueError()
94    callbacks = (callback_with_value(1),
95                 callback_with_value(2),
96                 MockFunction(throws_error))
97    future = All(Future(callback=callback) for callback in callbacks)
98    for callback in callbacks:
99      self.assertTrue(*callback.CheckAndReset(0))
100    # Can't check that the callbacks were actually run because in theory the
101    # Futures can be resolved in any order.
102    self.assertRaises(ValueError, future.Get)
103
104  def testRaceSuccess(self):
105    callback = MockFunction(lambda: 42)
106
107    # Test a single value.
108    race = Race((Future(callback=callback),))
109    self.assertTrue(*callback.CheckAndReset(0))
110    self.assertEqual(42, race.Get())
111    self.assertTrue(*callback.CheckAndReset(1))
112
113    # Test multiple success values. Note that we could test different values
114    # and check that the first returned, but this is just an implementation
115    # detail of Race. When we have parallel Futures this might not always hold.
116    race = Race((Future(callback=callback),
117                 Future(callback=callback),
118                 Future(callback=callback)))
119    self.assertTrue(*callback.CheckAndReset(0))
120    self.assertEqual(42, race.Get())
121    # Can't assert the actual count here for the same reason as above.
122    callback.CheckAndReset(99)
123
124    # Test values with except_pass.
125    def throws_error():
126      raise ValueError()
127    race = Race((Future(callback=callback),
128                 Future(callback=throws_error)),
129                 except_pass=(ValueError,))
130    self.assertTrue(*callback.CheckAndReset(0))
131    self.assertEqual(42, race.Get())
132    self.assertTrue(*callback.CheckAndReset(1))
133
134  def testRaceErrors(self):
135    def throws_error():
136      raise ValueError()
137
138    # Test a single error.
139    race = Race((Future(callback=throws_error),))
140    self.assertRaises(ValueError, race.Get)
141
142    # Test multiple errors. Can't use different error types for the same reason
143    # as described in testRaceSuccess.
144    race = Race((Future(callback=throws_error),
145                 Future(callback=throws_error),
146                 Future(callback=throws_error)))
147    self.assertRaises(ValueError, race.Get)
148
149    # Test values with except_pass.
150    def throws_except_error():
151      raise NotImplementedError()
152    race = Race((Future(callback=throws_error),
153                 Future(callback=throws_except_error)),
154                 except_pass=(NotImplementedError,))
155    self.assertRaises(ValueError, race.Get)
156
157    race = Race((Future(callback=throws_error),
158                 Future(callback=throws_error)),
159                 except_pass=(ValueError,))
160    self.assertRaises(ValueError, race.Get)
161
162  def testThen(self):
163    def assertIs42(val):
164      self.assertEqual(val, 42)
165      return val
166
167    then = Future(value=42).Then(assertIs42)
168    # Shouldn't raise an error.
169    self.assertEqual(42, then.Get())
170
171    # Test raising an error.
172    then = Future(value=41).Then(assertIs42)
173    self.assertRaises(AssertionError, then.Get)
174
175    # Test setting up an error handler.
176    def handle(error):
177      if isinstance(error, ValueError):
178        return 'Caught'
179      raise error
180
181    def raiseValueError():
182      raise ValueError
183
184    def raiseException():
185      raise Exception
186
187    then = Future(callback=raiseValueError).Then(assertIs42, handle)
188    self.assertEqual('Caught', then.Get())
189    then = Future(callback=raiseException).Then(assertIs42, handle)
190    self.assertRaises(Exception, then.Get)
191
192    # Test chains of thens.
193    addOne = lambda val: val + 1
194    then = Future(value=40).Then(addOne).Then(addOne).Then(assertIs42)
195    # Shouldn't raise an error.
196    self.assertEqual(42, then.Get())
197
198    # Test error in chain.
199    then = Future(value=40).Then(addOne).Then(assertIs42).Then(addOne)
200    self.assertRaises(AssertionError, then.Get)
201
202    # Test handle error in chain.
203    def raiseValueErrorWithVal(val):
204      raise ValueError
205
206    then = Future(value=40).Then(addOne).Then(raiseValueErrorWithVal).Then(
207        addOne, handle).Then(lambda val: val + ' me')
208    self.assertEquals(then.Get(), 'Caught me')
209
210    # Test multiple handlers.
211    def myHandle(error):
212      if isinstance(error, AssertionError):
213        return 10
214      raise error
215
216    then = Future(value=40).Then(assertIs42).Then(addOne, handle).Then(addOne,
217                                                                       myHandle)
218    self.assertEquals(then.Get(), 10)
219
220  def testThenResolvesReturnedFutures(self):
221    def returnsFortyTwo():
222      return Future(value=42)
223    def inc(x):
224      return x + 1
225    def incFuture(x):
226      return Future(value=x + 1)
227
228    self.assertEqual(43, returnsFortyTwo().Then(inc).Get())
229    self.assertEqual(43, returnsFortyTwo().Then(incFuture).Get())
230    self.assertEqual(44, returnsFortyTwo().Then(inc).Then(inc).Get())
231    self.assertEqual(44, returnsFortyTwo().Then(inc).Then(incFuture).Get())
232    self.assertEqual(44, returnsFortyTwo().Then(incFuture).Then(inc).Get())
233    self.assertEqual(
234        44, returnsFortyTwo().Then(incFuture).Then(incFuture).Get())
235
236    # The same behaviour should apply to error handlers.
237    def raisesSomething():
238      def boom(): raise ValueError
239      return Future(callback=boom)
240    def shouldNotHappen(_):
241      raise AssertionError()
242    def oops(error):
243      return 'oops'
244    def oopsFuture(error):
245      return Future(value='oops')
246
247    self.assertEqual(
248        'oops', raisesSomething().Then(shouldNotHappen, oops).Get())
249    self.assertEqual(
250        'oops', raisesSomething().Then(shouldNotHappen, oopsFuture).Get())
251    self.assertEqual(
252        'oops',
253        raisesSomething().Then(shouldNotHappen, raisesSomething)
254                         .Then(shouldNotHappen, oops).Get())
255    self.assertEqual(
256        'oops',
257        raisesSomething().Then(shouldNotHappen, raisesSomething)
258                         .Then(shouldNotHappen, oopsFuture).Get())
259
260
261if __name__ == '__main__':
262  unittest.main()
263