test_futures.py 7.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316
  1. from panda3d import core
  2. import pytest
  3. import time
  4. import sys
  5. if sys.version_info >= (3,):
  6. from concurrent.futures._base import TimeoutError, CancelledError
  7. else:
  8. TimeoutError = Exception
  9. CancelledError = Exception
  10. def test_future_cancelled():
  11. fut = core.AsyncFuture()
  12. assert not fut.done()
  13. assert not fut.cancelled()
  14. fut.cancel()
  15. assert fut.done()
  16. assert fut.cancelled()
  17. with pytest.raises(CancelledError):
  18. fut.result()
  19. # Works more than once
  20. with pytest.raises(CancelledError):
  21. fut.result()
  22. def test_future_timeout():
  23. fut = core.AsyncFuture()
  24. with pytest.raises(TimeoutError):
  25. fut.result(0.001)
  26. # Works more than once
  27. with pytest.raises(TimeoutError):
  28. fut.result(0.001)
  29. @pytest.mark.skipif(not core.Thread.is_threading_supported(),
  30. reason="Threading support disabled")
  31. def test_future_wait():
  32. threading = pytest.importorskip("direct.stdpy.threading")
  33. fut = core.AsyncFuture()
  34. # Launch a thread to set the result value.
  35. def thread_main():
  36. time.sleep(0.001)
  37. fut.set_result(None)
  38. thread = threading.Thread(target=thread_main)
  39. assert not fut.done()
  40. thread.start()
  41. assert fut.result() is None
  42. assert fut.done()
  43. assert not fut.cancelled()
  44. assert fut.result() is None
  45. @pytest.mark.skipif(not core.Thread.is_threading_supported(),
  46. reason="Threading support disabled")
  47. def test_future_wait_cancel():
  48. threading = pytest.importorskip("direct.stdpy.threading")
  49. fut = core.AsyncFuture()
  50. # Launch a thread to cancel the future.
  51. def thread_main():
  52. time.sleep(0.001)
  53. fut.cancel()
  54. thread = threading.Thread(target=thread_main)
  55. assert not fut.done()
  56. thread.start()
  57. with pytest.raises(CancelledError):
  58. fut.result()
  59. assert fut.done()
  60. assert fut.cancelled()
  61. with pytest.raises(CancelledError):
  62. fut.result()
  63. def test_task_cancel():
  64. task_mgr = core.AsyncTaskManager.get_global_ptr()
  65. task = core.PythonTask(lambda task: task.done)
  66. task_mgr.add(task)
  67. assert not task.done()
  68. task_mgr.remove(task)
  69. assert task.done()
  70. assert task.cancelled()
  71. with pytest.raises(CancelledError):
  72. task.result()
  73. def test_task_cancel_during_run():
  74. task_mgr = core.AsyncTaskManager.get_global_ptr()
  75. task_chain = task_mgr.make_task_chain("test_task_cancel_during_run")
  76. def task_main(task):
  77. task.remove()
  78. # It won't yet be marked done until after it returns.
  79. assert not task.done()
  80. return task.done
  81. task = core.PythonTask(task_main)
  82. task.set_task_chain(task_chain.name)
  83. task_mgr.add(task)
  84. task_chain.wait_for_tasks()
  85. assert task.done()
  86. assert task.cancelled()
  87. with pytest.raises(CancelledError):
  88. task.result()
  89. def test_task_result():
  90. task_mgr = core.AsyncTaskManager.get_global_ptr()
  91. task_chain = task_mgr.make_task_chain("test_task_result")
  92. def task_main(task):
  93. task.set_result(42)
  94. # It won't yet be marked done until after it returns.
  95. assert not task.done()
  96. return core.PythonTask.done
  97. task = core.PythonTask(task_main)
  98. task.set_task_chain(task_chain.name)
  99. task_mgr.add(task)
  100. task_chain.wait_for_tasks()
  101. assert task.done()
  102. assert not task.cancelled()
  103. assert task.result() == 42
  104. def test_coro_exception():
  105. task_mgr = core.AsyncTaskManager.get_global_ptr()
  106. task_chain = task_mgr.make_task_chain("test_coro_exception")
  107. def coro_main():
  108. raise RuntimeError
  109. yield None
  110. task = core.PythonTask(coro_main())
  111. task.set_task_chain(task_chain.name)
  112. task_mgr.add(task)
  113. task_chain.wait_for_tasks()
  114. assert task.done()
  115. assert not task.cancelled()
  116. with pytest.raises(RuntimeError):
  117. task.result()
  118. def test_future_gather():
  119. fut1 = core.AsyncFuture()
  120. fut2 = core.AsyncFuture()
  121. # 0 and 1 arguments are special
  122. assert core.AsyncFuture.gather().done()
  123. assert core.AsyncFuture.gather(fut1) == fut1
  124. # Gathering not-done futures
  125. gather = core.AsyncFuture.gather(fut1, fut2)
  126. assert not gather.done()
  127. # One future done
  128. fut1.set_result(1)
  129. assert not gather.done()
  130. # Two futures done
  131. fut2.set_result(2)
  132. assert gather.done()
  133. assert not gather.cancelled()
  134. assert tuple(gather.result()) == (1, 2)
  135. def test_future_gather_cancel_inner():
  136. fut1 = core.AsyncFuture()
  137. fut2 = core.AsyncFuture()
  138. # Gathering not-done futures
  139. gather = core.AsyncFuture.gather(fut1, fut2)
  140. assert not gather.done()
  141. # One future cancelled
  142. fut1.cancel()
  143. assert not gather.done()
  144. # Two futures cancelled
  145. fut2.set_result(2)
  146. assert gather.done()
  147. assert not gather.cancelled()
  148. with pytest.raises(CancelledError):
  149. assert gather.result()
  150. def test_future_gather_cancel_outer():
  151. fut1 = core.AsyncFuture()
  152. fut2 = core.AsyncFuture()
  153. # Gathering not-done futures
  154. gather = core.AsyncFuture.gather(fut1, fut2)
  155. assert not gather.done()
  156. assert gather.cancel()
  157. assert gather.done()
  158. assert gather.cancelled()
  159. with pytest.raises(CancelledError):
  160. assert gather.result()
  161. def test_future_done_callback():
  162. fut = core.AsyncFuture()
  163. # Use the list hack since Python 2 doesn't have the "nonlocal" keyword.
  164. called = [False]
  165. def on_done(arg):
  166. assert arg == fut
  167. called[0] = True
  168. fut.add_done_callback(on_done)
  169. fut.cancel()
  170. assert fut.done()
  171. task_mgr = core.AsyncTaskManager.get_global_ptr()
  172. task_mgr.poll()
  173. assert called[0]
  174. def test_future_done_callback_already_done():
  175. # Same as above, but with the future already done when add_done_callback
  176. # is called.
  177. fut = core.AsyncFuture()
  178. fut.cancel()
  179. assert fut.done()
  180. # Use the list hack since Python 2 doesn't have the "nonlocal" keyword.
  181. called = [False]
  182. def on_done(arg):
  183. assert arg == fut
  184. called[0] = True
  185. fut.add_done_callback(on_done)
  186. task_mgr = core.AsyncTaskManager.get_global_ptr()
  187. task_mgr.poll()
  188. assert called[0]
  189. def test_event_future():
  190. queue = core.EventQueue()
  191. handler = core.EventHandler(queue)
  192. fut = handler.get_future("test")
  193. # If we ask again, we should get the same one.
  194. assert handler.get_future("test") == fut
  195. event = core.Event("test")
  196. handler.dispatch_event(event)
  197. assert fut.done()
  198. assert not fut.cancelled()
  199. assert fut.result() == event
  200. def test_event_future_cancel():
  201. # This is a very strange thing to do, but it's possible, so let's make
  202. # sure it gives defined behavior.
  203. queue = core.EventQueue()
  204. handler = core.EventHandler(queue)
  205. fut = handler.get_future("test")
  206. fut.cancel()
  207. assert fut.done()
  208. assert fut.cancelled()
  209. event = core.Event("test")
  210. handler.dispatch_event(event)
  211. assert fut.done()
  212. assert fut.cancelled()
  213. def test_event_future_cancel2():
  214. queue = core.EventQueue()
  215. handler = core.EventHandler(queue)
  216. # Make sure we get a new future if we cancelled the first one.
  217. fut = handler.get_future("test")
  218. fut.cancel()
  219. fut2 = handler.get_future("test")
  220. assert fut != fut2
  221. assert fut.done()
  222. assert fut.cancelled()
  223. assert not fut2.done()
  224. assert not fut2.cancelled()