import unittest
-from future import Future
+from future import All, Future, Race
+from mock_function import MockFunction
class FutureTest(unittest.TestCase):
assert_raises_full_stack(future, FunkyException)
assert_raises_full_stack(future, FunkyException)
+ def testAll(self):
+ def callback_with_value(value):
+ return MockFunction(lambda: value)
+
+ # Test a single value.
+ callback = callback_with_value(42)
+ future = All((Future(callback=callback),))
+ self.assertTrue(*callback.CheckAndReset(0))
+ self.assertEqual([42], future.Get())
+ self.assertTrue(*callback.CheckAndReset(1))
+
+ # Test multiple callbacks.
+ callbacks = (callback_with_value(1),
+ callback_with_value(2),
+ callback_with_value(3))
+ future = All(Future(callback=callback) for callback in callbacks)
+ for callback in callbacks:
+ self.assertTrue(*callback.CheckAndReset(0))
+ self.assertEqual([1, 2, 3], future.Get())
+ for callback in callbacks:
+ self.assertTrue(*callback.CheckAndReset(1))
+
+ # Test throwing an error.
+ def throws_error():
+ raise ValueError()
+ callbacks = (callback_with_value(1),
+ callback_with_value(2),
+ MockFunction(throws_error))
+
+ future = All(Future(callback=callback) for callback in callbacks)
+ for callback in callbacks:
+ self.assertTrue(*callback.CheckAndReset(0))
+ self.assertRaises(ValueError, future.Get)
+ for callback in callbacks:
+ # Can't check that the callbacks were actually run because in theory the
+ # Futures can be resolved in any order.
+ callback.CheckAndReset(0)
+
+ # Test throwing an error with except_pass.
+ future = All((Future(callback=callback) for callback in callbacks),
+ except_pass=ValueError)
+ for callback in callbacks:
+ self.assertTrue(*callback.CheckAndReset(0))
+ self.assertEqual([1, 2, None], future.Get())
+
+ def testRaceSuccess(self):
+ callback = MockFunction(lambda: 42)
+
+ # Test a single value.
+ race = Race((Future(callback=callback),))
+ self.assertTrue(*callback.CheckAndReset(0))
+ self.assertEqual(42, race.Get())
+ self.assertTrue(*callback.CheckAndReset(1))
+
+ # Test multiple success values. Note that we could test different values
+ # and check that the first returned, but this is just an implementation
+ # detail of Race. When we have parallel Futures this might not always hold.
+ race = Race((Future(callback=callback),
+ Future(callback=callback),
+ Future(callback=callback)))
+ self.assertTrue(*callback.CheckAndReset(0))
+ self.assertEqual(42, race.Get())
+ # Can't assert the actual count here for the same reason as above.
+ callback.CheckAndReset(99)
+
+ # Test values with except_pass.
+ def throws_error():
+ raise ValueError()
+ race = Race((Future(callback=callback),
+ Future(callback=throws_error)),
+ except_pass=(ValueError,))
+ self.assertTrue(*callback.CheckAndReset(0))
+ self.assertEqual(42, race.Get())
+ self.assertTrue(*callback.CheckAndReset(1))
+
+ def testRaceErrors(self):
+ def throws_error():
+ raise ValueError()
+
+ # Test a single error.
+ race = Race((Future(callback=throws_error),))
+ self.assertRaises(ValueError, race.Get)
+
+ # Test multiple errors. Can't use different error types for the same reason
+ # as described in testRaceSuccess.
+ race = Race((Future(callback=throws_error),
+ Future(callback=throws_error),
+ Future(callback=throws_error)))
+ self.assertRaises(ValueError, race.Get)
+
+ # Test values with except_pass.
+ def throws_except_error():
+ raise NotImplementedError()
+ race = Race((Future(callback=throws_error),
+ Future(callback=throws_except_error)),
+ except_pass=(NotImplementedError,))
+ self.assertRaises(ValueError, race.Get)
+
+ race = Race((Future(callback=throws_error),
+ Future(callback=throws_error)),
+ except_pass=(ValueError,))
+ self.assertRaises(ValueError, race.Get)
+
+ # Test except_pass with default values.
+ race = Race((Future(callback=throws_error),
+ Future(callback=throws_except_error)),
+ except_pass=(NotImplementedError,),
+ default=42)
+ self.assertRaises(ValueError, race.Get)
+
+ race = Race((Future(callback=throws_error),
+ Future(callback=throws_error)),
+ except_pass=(ValueError,),
+ default=42)
+ self.assertEqual(42, race.Get())
+
+ def testThen(self):
+ def assertIs42(val):
+ self.assertEqual(val, 42)
+ return val
+
+ then = Future(value=42).Then(assertIs42)
+ # Shouldn't raise an error.
+ self.assertEqual(42, then.Get())
+
+ # Test raising an error.
+ then = Future(value=41).Then(assertIs42)
+ self.assertRaises(AssertionError, then.Get)
+
+ # Test setting up an error handler.
+ def handle(error):
+ if isinstance(error, ValueError):
+ return 'Caught'
+ raise error
+
+ def raiseValueError():
+ raise ValueError
+
+ def raiseException():
+ raise Exception
+
+ then = Future(callback=raiseValueError).Then(assertIs42, handle)
+ self.assertEqual('Caught', then.Get())
+ then = Future(callback=raiseException).Then(assertIs42, handle)
+ self.assertRaises(Exception, then.Get)
+
+ # Test chains of thens.
+ addOne = lambda val: val + 1
+ then = Future(value=40).Then(addOne).Then(addOne).Then(assertIs42)
+ # Shouldn't raise an error.
+ self.assertEqual(42, then.Get())
+
+ # Test error in chain.
+ then = Future(value=40).Then(addOne).Then(assertIs42).Then(addOne)
+ self.assertRaises(AssertionError, then.Get)
+
+ # Test handle error in chain.
+ def raiseValueErrorWithVal(val):
+ raise ValueError
+
+ then = Future(value=40).Then(addOne).Then(raiseValueErrorWithVal).Then(
+ addOne, handle).Then(lambda val: val + ' me')
+ self.assertEquals(then.Get(), 'Caught me')
+
+ # Test multiple handlers.
+ def myHandle(error):
+ if isinstance(error, AssertionError):
+ return 10
+ raise error
+
+ then = Future(value=40).Then(assertIs42).Then(addOne, handle).Then(addOne,
+ myHandle)
+ self.assertEquals(then.Get(), 10)
+
+ def testThenResolvesReturnedFutures(self):
+ def returnsFortyTwo():
+ return Future(value=42)
+ def inc(x):
+ return x + 1
+ def incFuture(x):
+ return Future(value=x + 1)
+
+ self.assertEqual(43, returnsFortyTwo().Then(inc).Get())
+ self.assertEqual(43, returnsFortyTwo().Then(incFuture).Get())
+ self.assertEqual(44, returnsFortyTwo().Then(inc).Then(inc).Get())
+ self.assertEqual(44, returnsFortyTwo().Then(inc).Then(incFuture).Get())
+ self.assertEqual(44, returnsFortyTwo().Then(incFuture).Then(inc).Get())
+ self.assertEqual(
+ 44, returnsFortyTwo().Then(incFuture).Then(incFuture).Get())
+
+ # The same behaviour should apply to error handlers.
+ def raisesSomething():
+ def boom(): raise ValueError
+ return Future(callback=boom)
+ def shouldNotHappen(_):
+ raise AssertionError()
+ def oops(error):
+ return 'oops'
+ def oopsFuture(error):
+ return Future(value='oops')
+
+ self.assertEqual(
+ 'oops', raisesSomething().Then(shouldNotHappen, oops).Get())
+ self.assertEqual(
+ 'oops', raisesSomething().Then(shouldNotHappen, oopsFuture).Get())
+ self.assertEqual(
+ 'oops',
+ raisesSomething().Then(shouldNotHappen, raisesSomething)
+ .Then(shouldNotHappen, oops).Get())
+ self.assertEqual(
+ 'oops',
+ raisesSomething().Then(shouldNotHappen, raisesSomething)
+ .Then(shouldNotHappen, oopsFuture).Get())
+
if __name__ == '__main__':
unittest.main()