test_futures.py 8.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381
  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_result():
  119. # Cancelled
  120. fut = core.AsyncFuture()
  121. assert not fut.done()
  122. fut.cancel()
  123. with pytest.raises(Exception):
  124. fut.result()
  125. # None
  126. fut = core.AsyncFuture()
  127. fut.set_result(None)
  128. assert fut.done()
  129. assert fut.result() is None
  130. # Store int
  131. fut = core.AsyncFuture()
  132. fut.set_result(123)
  133. assert fut.result() == 123
  134. # Store string
  135. fut = core.AsyncFuture()
  136. fut.set_result("test\000\u1234")
  137. assert fut.result() == "test\000\u1234"
  138. # Store TypedWritableReferenceCount
  139. tex = core.Texture()
  140. rc = tex.get_ref_count()
  141. fut = core.AsyncFuture()
  142. fut.set_result(tex)
  143. assert tex.get_ref_count() == rc + 1
  144. assert fut.result() == tex
  145. assert tex.get_ref_count() == rc + 1
  146. assert fut.result() == tex
  147. assert tex.get_ref_count() == rc + 1
  148. fut = None
  149. assert tex.get_ref_count() == rc
  150. # Store EventParameter (gets unwrapped)
  151. ep = core.EventParameter(0.5)
  152. fut = core.AsyncFuture()
  153. fut.set_result(ep)
  154. assert fut.result() == 0.5
  155. assert fut.result() == 0.5
  156. # Store TypedObject
  157. dg = core.Datagram(b"test")
  158. fut = core.AsyncFuture()
  159. fut.set_result(dg)
  160. assert fut.result() == dg
  161. assert fut.result() == dg
  162. # Store arbitrary Python object
  163. obj = object()
  164. rc = sys.getrefcount(obj)
  165. fut = core.AsyncFuture()
  166. fut.set_result(obj)
  167. assert sys.getrefcount(obj) == rc + 1
  168. assert fut.result() is obj
  169. assert sys.getrefcount(obj) == rc + 1
  170. assert fut.result() is obj
  171. assert sys.getrefcount(obj) == rc + 1
  172. fut = None
  173. assert sys.getrefcount(obj) == rc
  174. def test_future_gather():
  175. fut1 = core.AsyncFuture()
  176. fut2 = core.AsyncFuture()
  177. # 0 and 1 arguments are special
  178. assert core.AsyncFuture.gather().done()
  179. assert core.AsyncFuture.gather(fut1) == fut1
  180. # Gathering not-done futures
  181. gather = core.AsyncFuture.gather(fut1, fut2)
  182. assert not gather.done()
  183. # One future done
  184. fut1.set_result(1)
  185. assert not gather.done()
  186. # Two futures done
  187. fut2.set_result(2)
  188. assert gather.done()
  189. assert not gather.cancelled()
  190. assert tuple(gather.result()) == (1, 2)
  191. def test_future_gather_cancel_inner():
  192. fut1 = core.AsyncFuture()
  193. fut2 = core.AsyncFuture()
  194. # Gathering not-done futures
  195. gather = core.AsyncFuture.gather(fut1, fut2)
  196. assert not gather.done()
  197. # One future cancelled
  198. fut1.cancel()
  199. assert not gather.done()
  200. # Two futures cancelled
  201. fut2.set_result(2)
  202. assert gather.done()
  203. assert not gather.cancelled()
  204. with pytest.raises(CancelledError):
  205. assert gather.result()
  206. def test_future_gather_cancel_outer():
  207. fut1 = core.AsyncFuture()
  208. fut2 = core.AsyncFuture()
  209. # Gathering not-done futures
  210. gather = core.AsyncFuture.gather(fut1, fut2)
  211. assert not gather.done()
  212. assert gather.cancel()
  213. assert gather.done()
  214. assert gather.cancelled()
  215. with pytest.raises(CancelledError):
  216. assert gather.result()
  217. def test_future_done_callback():
  218. fut = core.AsyncFuture()
  219. # Use the list hack since Python 2 doesn't have the "nonlocal" keyword.
  220. called = [False]
  221. def on_done(arg):
  222. assert arg == fut
  223. called[0] = True
  224. fut.add_done_callback(on_done)
  225. fut.cancel()
  226. assert fut.done()
  227. task_mgr = core.AsyncTaskManager.get_global_ptr()
  228. task_mgr.poll()
  229. assert called[0]
  230. def test_future_done_callback_already_done():
  231. # Same as above, but with the future already done when add_done_callback
  232. # is called.
  233. fut = core.AsyncFuture()
  234. fut.cancel()
  235. assert fut.done()
  236. # Use the list hack since Python 2 doesn't have the "nonlocal" keyword.
  237. called = [False]
  238. def on_done(arg):
  239. assert arg == fut
  240. called[0] = True
  241. fut.add_done_callback(on_done)
  242. task_mgr = core.AsyncTaskManager.get_global_ptr()
  243. task_mgr.poll()
  244. assert called[0]
  245. def test_event_future():
  246. queue = core.EventQueue()
  247. handler = core.EventHandler(queue)
  248. fut = handler.get_future("test")
  249. # If we ask again, we should get the same one.
  250. assert handler.get_future("test") == fut
  251. event = core.Event("test")
  252. handler.dispatch_event(event)
  253. assert fut.done()
  254. assert not fut.cancelled()
  255. assert fut.result() == event
  256. def test_event_future_cancel():
  257. # This is a very strange thing to do, but it's possible, so let's make
  258. # sure it gives defined behavior.
  259. queue = core.EventQueue()
  260. handler = core.EventHandler(queue)
  261. fut = handler.get_future("test")
  262. fut.cancel()
  263. assert fut.done()
  264. assert fut.cancelled()
  265. event = core.Event("test")
  266. handler.dispatch_event(event)
  267. assert fut.done()
  268. assert fut.cancelled()
  269. def test_event_future_cancel2():
  270. queue = core.EventQueue()
  271. handler = core.EventHandler(queue)
  272. # Make sure we get a new future if we cancelled the first one.
  273. fut = handler.get_future("test")
  274. fut.cancel()
  275. fut2 = handler.get_future("test")
  276. assert fut != fut2
  277. assert fut.done()
  278. assert fut.cancelled()
  279. assert not fut2.done()
  280. assert not fut2.cancelled()