test_simulation.py 15KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480
  1. import datetime
  2. import time
  3. from synergine2.config import Config
  4. from synergine2.cycle import CycleManager
  5. from synergine2.log import SynergineLogger
  6. from synergine2.processing import ProcessManager
  7. from synergine2.share import shared
  8. from synergine2.simulation import Simulation
  9. from synergine2.simulation import Subjects
  10. from synergine2.simulation import SubjectBehaviour
  11. from synergine2.simulation import SubjectMechanism
  12. from synergine2.simulation import Subject
  13. from synergine2.simulation import SimulationMechanism
  14. from synergine2.simulation import SimulationBehaviour
  15. from tests import BaseTest
  16. from freezegun import freeze_time
  17. config = Config()
  18. logger = SynergineLogger('test')
  19. class MySubjectMechanism(SubjectMechanism):
  20. def run(self):
  21. return {'foo': 42}
  22. class MySimulationMechanism(SimulationMechanism):
  23. def run(self, process_number: int=None, process_count: int=None):
  24. return {'foo': 42}
  25. class MySubjectBehaviour(SubjectBehaviour):
  26. use = [MySubjectMechanism]
  27. def run(self, data):
  28. return {'bar': data[MySubjectMechanism]['foo'] + 100}
  29. class MySimulationBehaviour(SimulationBehaviour):
  30. use = [MySimulationMechanism]
  31. def run(self, data):
  32. return {'bar': data[MySimulationMechanism]['foo'] + 100}
  33. class MySimulation(Simulation):
  34. behaviours_classes = [MySimulationBehaviour]
  35. class TestBehaviours(BaseTest):
  36. def test_subject_behaviour_produce_data(
  37. self,
  38. do_nothing_process_manager: ProcessManager,
  39. ):
  40. shared.reset()
  41. class MySubject(Subject):
  42. behaviours_classes = [MySubjectBehaviour]
  43. simulation = Simulation(config)
  44. my_subject = MySubject(config, simulation)
  45. subjects = Subjects(simulation=simulation)
  46. subjects.append(my_subject)
  47. simulation.subjects = subjects
  48. cycle_manager = CycleManager(
  49. config,
  50. logger,
  51. simulation=simulation,
  52. process_manager=do_nothing_process_manager,
  53. )
  54. results_by_subjects = cycle_manager._job_subjects(worker_id=0, process_count=1)
  55. assert results_by_subjects
  56. assert id(my_subject) in results_by_subjects
  57. assert MySubjectBehaviour in results_by_subjects[id(my_subject)]
  58. assert 'bar' in results_by_subjects[id(my_subject)][MySubjectBehaviour]
  59. assert 142 == results_by_subjects[id(my_subject)][MySubjectBehaviour]['bar']
  60. def test_simulation_behaviour_produce_data(
  61. self,
  62. do_nothing_process_manager: ProcessManager,
  63. ):
  64. shared.reset()
  65. simulation = MySimulation(config)
  66. subjects = Subjects(simulation=simulation)
  67. simulation.subjects = subjects
  68. cycle_manager = CycleManager(
  69. config,
  70. logger,
  71. simulation=simulation,
  72. process_manager=do_nothing_process_manager,
  73. )
  74. data = cycle_manager._job_simulation(worker_id=0, process_count=1)
  75. assert data
  76. assert MySimulationBehaviour in data
  77. assert 'bar' in data[MySimulationBehaviour]
  78. assert 142 == data[MySimulationBehaviour]['bar']
  79. def test_subject_behaviour_cycle_frequency(
  80. self,
  81. do_nothing_process_manager: ProcessManager,
  82. ):
  83. shared.reset()
  84. class MyCycledSubjectBehaviour(MySubjectBehaviour):
  85. @property
  86. def cycle_frequency(self):
  87. return 2
  88. class MySubject(Subject):
  89. behaviours_classes = [MyCycledSubjectBehaviour]
  90. simulation = Simulation(config)
  91. my_subject = MySubject(config, simulation)
  92. subjects = Subjects(simulation=simulation)
  93. subjects.append(my_subject)
  94. simulation.subjects = subjects
  95. cycle_manager = CycleManager(
  96. config,
  97. logger,
  98. simulation=simulation,
  99. process_manager=do_nothing_process_manager,
  100. )
  101. # Cycle 0: behaviour NOT executed
  102. cycle_manager.current_cycle = 0
  103. results_by_subjects = cycle_manager._job_subjects(worker_id=0, process_count=1)
  104. assert results_by_subjects
  105. assert id(my_subject) in results_by_subjects
  106. assert not results_by_subjects[id(my_subject)]
  107. # Cycle 1: behaviour executed
  108. cycle_manager.current_cycle = 1
  109. results_by_subjects = cycle_manager._job_subjects(worker_id=0, process_count=1)
  110. assert results_by_subjects
  111. assert id(my_subject) in results_by_subjects
  112. assert results_by_subjects[id(my_subject)]
  113. def test_subject_behaviour_seconds_frequency(
  114. self,
  115. do_nothing_process_manager: ProcessManager,
  116. ):
  117. shared.reset()
  118. class MyTimedSubjectBehaviour(MySubjectBehaviour):
  119. @property
  120. def seconds_frequency(self):
  121. return 1.0
  122. def run(self, data):
  123. self.last_execution_time = time.time()
  124. return super().run(data)
  125. class MySubject(Subject):
  126. behaviours_classes = [MyTimedSubjectBehaviour]
  127. simulation = Simulation(config)
  128. my_subject = MySubject(config, simulation)
  129. subjects = Subjects(simulation=simulation)
  130. subjects.append(my_subject)
  131. simulation.subjects = subjects
  132. cycle_manager = CycleManager(
  133. config,
  134. logger,
  135. simulation=simulation,
  136. process_manager=do_nothing_process_manager,
  137. )
  138. # Thirst time, behaviour IS executed
  139. with freeze_time(datetime.datetime(2000, 12, 1, 0, 0, 0)):
  140. data = cycle_manager._job_subjects(worker_id=0, process_count=1)
  141. assert data
  142. assert id(my_subject) in data
  143. assert data[id(my_subject)]
  144. # Less second after: NOT executed
  145. with freeze_time(datetime.datetime(2000, 12, 1, 0, 0, 0, 500000)):
  146. data = cycle_manager._job_subjects(worker_id=0, process_count=1)
  147. assert data
  148. assert id(my_subject) in data
  149. assert not data[id(my_subject)]
  150. # Less second after: NOT executed
  151. with freeze_time(datetime.datetime(2000, 12, 1, 0, 0, 0, 700000)):
  152. data = cycle_manager._job_subjects(worker_id=0, process_count=1)
  153. assert data
  154. assert id(my_subject) in data
  155. assert not data[id(my_subject)]
  156. # Less second after: NOT executed
  157. with freeze_time(datetime.datetime(2000, 12, 1, 0, 0, 1, 500000)):
  158. data = cycle_manager._job_subjects(worker_id=0, process_count=1)
  159. assert data
  160. assert id(my_subject) in data
  161. assert data[id(my_subject)]
  162. def test_simulation_behaviour_cycle_frequency(
  163. self,
  164. do_nothing_process_manager: ProcessManager,
  165. ):
  166. shared.reset()
  167. class MyCycledSimulationBehaviour(MySimulationBehaviour):
  168. @property
  169. def cycle_frequency(self):
  170. return 2
  171. class MyCycledSimulation(Simulation):
  172. behaviours_classes = [MyCycledSimulationBehaviour]
  173. simulation = MyCycledSimulation(config)
  174. subjects = Subjects(simulation=simulation)
  175. simulation.subjects = subjects
  176. cycle_manager = CycleManager(
  177. config,
  178. logger,
  179. simulation=simulation,
  180. process_manager=do_nothing_process_manager,
  181. )
  182. # Cycle 0: behaviour NOT executed
  183. cycle_manager.current_cycle = 0
  184. data = cycle_manager._job_simulation(worker_id=0, process_count=1)
  185. assert not data
  186. # Cycle 1: behaviour executed
  187. cycle_manager.current_cycle = 1
  188. data = cycle_manager._job_simulation(worker_id=0, process_count=1)
  189. assert data
  190. def test_simulation_behaviour_seconds_frequency(
  191. self,
  192. do_nothing_process_manager: ProcessManager,
  193. ):
  194. shared.reset()
  195. class MyTimedSimulationBehaviour(MySimulationBehaviour):
  196. @property
  197. def seconds_frequency(self):
  198. return 1.0
  199. def run(self, data):
  200. self.last_execution_time = time.time()
  201. return super().run(data)
  202. class MyTimedSimulation(Simulation):
  203. behaviours_classes = [MyTimedSimulationBehaviour]
  204. simulation = MyTimedSimulation(config)
  205. subjects = Subjects(simulation=simulation)
  206. simulation.subjects = subjects
  207. cycle_manager = CycleManager(
  208. config,
  209. logger,
  210. simulation=simulation,
  211. process_manager=do_nothing_process_manager,
  212. )
  213. # Thirst time, behaviour IS executed
  214. with freeze_time(datetime.datetime(2000, 12, 1, 0, 0, 0)):
  215. data = cycle_manager._job_simulation(worker_id=0, process_count=1)
  216. assert data
  217. # Less second after: NOT executed
  218. with freeze_time(datetime.datetime(2000, 12, 1, 0, 0, 0, 500000)):
  219. data = cycle_manager._job_simulation(worker_id=0, process_count=1)
  220. assert not data
  221. # Less second after: NOT executed
  222. with freeze_time(datetime.datetime(2000, 12, 1, 0, 0, 0, 700000)):
  223. data = cycle_manager._job_simulation(worker_id=0, process_count=1)
  224. assert not data
  225. # More second after: IS executed
  226. with freeze_time(datetime.datetime(2000, 12, 1, 0, 0, 1, 500000)):
  227. data = cycle_manager._job_simulation(worker_id=0, process_count=1)
  228. assert data
  229. def test_subject_behavior_not_called_if_no_more_subjects(
  230. self,
  231. do_nothing_process_manager: ProcessManager,
  232. ):
  233. shared.reset()
  234. class MySubject(Subject):
  235. behaviours_classes = [MySubjectBehaviour]
  236. simulation = Simulation(config)
  237. my_subject = MySubject(config, simulation)
  238. subjects = Subjects(simulation=simulation)
  239. subjects.append(my_subject)
  240. simulation.subjects = subjects
  241. cycle_manager = CycleManager(
  242. config,
  243. logger,
  244. simulation=simulation,
  245. process_manager=do_nothing_process_manager,
  246. )
  247. results_by_subjects = cycle_manager._job_subjects(worker_id=0, process_count=1)
  248. assert results_by_subjects
  249. assert id(my_subject) in results_by_subjects
  250. assert results_by_subjects[id(my_subject)]
  251. # If we remove subject, no more data generated
  252. subjects.remove(my_subject)
  253. results_by_subjects = cycle_manager._job_subjects(worker_id=0, process_count=1)
  254. assert not results_by_subjects
  255. class TestMechanisms(BaseTest):
  256. def test_mechanism_called_once_for_multiple_subject_behaviors(
  257. self,
  258. do_nothing_process_manager: ProcessManager,
  259. ):
  260. shared.reset()
  261. called = 0
  262. global called
  263. class MySubjectMechanism(SubjectMechanism):
  264. def run(self):
  265. global called
  266. called += 1
  267. return {'foo': 42}
  268. class MySubjectBehaviour1(SubjectBehaviour):
  269. use = [MySubjectMechanism]
  270. def run(self, data):
  271. return {'bar': data[MySubjectMechanism]['foo'] + 100}
  272. class MySubjectBehaviour2(SubjectBehaviour):
  273. use = [MySubjectMechanism]
  274. def run(self, data):
  275. return {'bar': data[MySubjectMechanism]['foo'] + 100}
  276. class MySubject(Subject):
  277. behaviours_classes = [MySubjectBehaviour1, MySubjectBehaviour2]
  278. simulation = Simulation(config)
  279. my_subject = MySubject(config, simulation)
  280. subjects = Subjects(simulation=simulation)
  281. subjects.append(my_subject)
  282. simulation.subjects = subjects
  283. cycle_manager = CycleManager(
  284. config,
  285. logger,
  286. simulation=simulation,
  287. process_manager=do_nothing_process_manager,
  288. )
  289. cycle_manager._job_subjects(worker_id=0, process_count=1)
  290. assert called == 1
  291. def test_mechanism_called_once_for_multiple_simulation_behaviors(
  292. self,
  293. do_nothing_process_manager: ProcessManager,
  294. ):
  295. shared.reset()
  296. called = 0
  297. global called
  298. class MySimulationMechanism(SimulationMechanism):
  299. def run(self, process_number: int = None, process_count: int = None):
  300. global called
  301. called += 1
  302. return {'foo': 42}
  303. class MySimulationBehaviour1(SimulationBehaviour):
  304. use = [MySimulationMechanism]
  305. def run(self, data):
  306. return {'bar': data[MySimulationMechanism]['foo'] + 100}
  307. class MySimulationBehaviour2(SimulationBehaviour):
  308. use = [MySimulationMechanism]
  309. def run(self, data):
  310. return {'bar': data[MySimulationMechanism]['foo'] + 100}
  311. class MySimulation(Simulation):
  312. behaviours_classes = [MySimulationBehaviour1, MySimulationBehaviour2]
  313. simulation = MySimulation(config)
  314. subjects = Subjects(simulation=simulation)
  315. simulation.subjects = subjects
  316. cycle_manager = CycleManager(
  317. config,
  318. logger,
  319. simulation=simulation,
  320. process_manager=do_nothing_process_manager,
  321. )
  322. cycle_manager._job_simulation(worker_id=0, process_count=1)
  323. assert called == 1
  324. def test_mechanism_not_called_if_no_subject_behavior(
  325. self,
  326. do_nothing_process_manager: ProcessManager,
  327. ):
  328. shared.reset()
  329. called = 0
  330. global called
  331. class MySubjectMechanism(SubjectMechanism):
  332. def run(self):
  333. global called
  334. called += 1
  335. return {'foo': 42}
  336. class MySubject(Subject):
  337. behaviours_classes = []
  338. simulation = Simulation(config)
  339. my_subject = MySubject(config, simulation)
  340. subjects = Subjects(simulation=simulation)
  341. subjects.append(my_subject)
  342. simulation.subjects = subjects
  343. cycle_manager = CycleManager(
  344. config,
  345. logger,
  346. simulation=simulation,
  347. process_manager=do_nothing_process_manager,
  348. )
  349. cycle_manager._job_subjects(worker_id=0, process_count=1)
  350. assert called == 0
  351. def test_mechanism_not_called_if_no_simulation_behavior(
  352. self,
  353. do_nothing_process_manager: ProcessManager,
  354. ):
  355. shared.reset()
  356. called = 0
  357. global called
  358. class MySimulationMechanism(SimulationMechanism):
  359. def run(self, process_number: int = None, process_count: int = None):
  360. global called
  361. called += 1
  362. return {'foo': 42}
  363. class MySimulation(Simulation):
  364. pass
  365. simulation = MySimulation(config)
  366. subjects = Subjects(simulation=simulation)
  367. simulation.subjects = subjects
  368. cycle_manager = CycleManager(
  369. config,
  370. logger,
  371. simulation=simulation,
  372. process_manager=do_nothing_process_manager,
  373. )
  374. cycle_manager._job_simulation(worker_id=0, process_count=1)
  375. assert called == 0
  376. def test_mechanism_not_called_if_subject_behavior_timebase_not_active_yet(self):
  377. shared.reset()
  378. pass
  379. def test_mechanism_not_called_if_simulation_behavior_timebase_not_active_yet(self):
  380. shared.reset()
  381. pass
  382. # TODO: Test Simulation mechanism parralelisation
  383. # TODO: Test behaviour actions generation