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