test_simulation.py 15KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484
  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 MyCycledSubjectBehaviour(MySubjectBehaviour):
  36. @property
  37. def cycle_frequency(self):
  38. return 2
  39. class MyCycledSimulationBehaviour(MySimulationBehaviour):
  40. @property
  41. def cycle_frequency(self):
  42. return 2
  43. class MyTimedSubjectBehaviour(MySubjectBehaviour):
  44. @property
  45. def seconds_frequency(self):
  46. return 1.0
  47. def run(self, data):
  48. self.last_execution_time = time.time()
  49. return super().run(data)
  50. class MyTimedSimulationBehaviour(MySimulationBehaviour):
  51. @property
  52. def seconds_frequency(self):
  53. return 1.0
  54. def run(self, data):
  55. self.last_execution_time = time.time()
  56. return super().run(data)
  57. class TestBehaviours(BaseTest):
  58. def test_subject_behaviour_produce_data(
  59. self,
  60. do_nothing_process_manager: ProcessManager,
  61. ):
  62. shared.reset()
  63. class MySubject(Subject):
  64. behaviours_classes = [MySubjectBehaviour]
  65. simulation = Simulation(config)
  66. my_subject = MySubject(config, simulation)
  67. subjects = Subjects(simulation=simulation)
  68. subjects.append(my_subject)
  69. simulation.subjects = subjects
  70. cycle_manager = CycleManager(
  71. config,
  72. logger,
  73. simulation=simulation,
  74. process_manager=do_nothing_process_manager,
  75. )
  76. results_by_subjects = cycle_manager._job_subjects(worker_id=0, process_count=1)
  77. assert results_by_subjects
  78. assert id(my_subject) in results_by_subjects
  79. assert MySubjectBehaviour in results_by_subjects[id(my_subject)]
  80. assert 'bar' in results_by_subjects[id(my_subject)][MySubjectBehaviour]
  81. assert 142 == results_by_subjects[id(my_subject)][MySubjectBehaviour]['bar']
  82. def test_simulation_behaviour_produce_data(
  83. self,
  84. do_nothing_process_manager: ProcessManager,
  85. ):
  86. shared.reset()
  87. simulation = MySimulation(config)
  88. subjects = Subjects(simulation=simulation)
  89. simulation.subjects = subjects
  90. cycle_manager = CycleManager(
  91. config,
  92. logger,
  93. simulation=simulation,
  94. process_manager=do_nothing_process_manager,
  95. )
  96. data = cycle_manager._job_simulation(worker_id=0, process_count=1)
  97. assert data
  98. assert MySimulationBehaviour in data
  99. assert 'bar' in data[MySimulationBehaviour]
  100. assert 142 == data[MySimulationBehaviour]['bar']
  101. def test_subject_behaviour_cycle_frequency(
  102. self,
  103. do_nothing_process_manager: ProcessManager,
  104. ):
  105. shared.reset()
  106. class MySubject(Subject):
  107. behaviours_classes = [MyCycledSubjectBehaviour]
  108. simulation = Simulation(config)
  109. my_subject = MySubject(config, simulation)
  110. subjects = Subjects(simulation=simulation)
  111. subjects.append(my_subject)
  112. simulation.subjects = subjects
  113. cycle_manager = CycleManager(
  114. config,
  115. logger,
  116. simulation=simulation,
  117. process_manager=do_nothing_process_manager,
  118. )
  119. # Cycle 0: behaviour NOT executed
  120. cycle_manager.current_cycle = 0
  121. results_by_subjects = cycle_manager._job_subjects(worker_id=0, process_count=1)
  122. assert results_by_subjects
  123. assert id(my_subject) in results_by_subjects
  124. assert not results_by_subjects[id(my_subject)]
  125. # Cycle 1: behaviour executed
  126. cycle_manager.current_cycle = 1
  127. results_by_subjects = cycle_manager._job_subjects(worker_id=0, process_count=1)
  128. assert results_by_subjects
  129. assert id(my_subject) in results_by_subjects
  130. assert results_by_subjects[id(my_subject)]
  131. def test_subject_behaviour_seconds_frequency(
  132. self,
  133. do_nothing_process_manager: ProcessManager,
  134. ):
  135. shared.reset()
  136. class MySubject(Subject):
  137. behaviours_classes = [MyTimedSubjectBehaviour]
  138. simulation = Simulation(config)
  139. my_subject = MySubject(config, simulation)
  140. subjects = Subjects(simulation=simulation)
  141. subjects.append(my_subject)
  142. simulation.subjects = subjects
  143. cycle_manager = CycleManager(
  144. config,
  145. logger,
  146. simulation=simulation,
  147. process_manager=do_nothing_process_manager,
  148. )
  149. # Thirst time, behaviour IS executed
  150. with freeze_time(datetime.datetime(2000, 12, 1, 0, 0, 0)):
  151. data = cycle_manager._job_subjects(worker_id=0, process_count=1)
  152. assert data
  153. assert id(my_subject) in data
  154. assert data[id(my_subject)]
  155. # Less second after: NOT executed
  156. with freeze_time(datetime.datetime(2000, 12, 1, 0, 0, 0, 500000)):
  157. data = cycle_manager._job_subjects(worker_id=0, process_count=1)
  158. assert data
  159. assert id(my_subject) in data
  160. assert not data[id(my_subject)]
  161. # Less second after: NOT executed
  162. with freeze_time(datetime.datetime(2000, 12, 1, 0, 0, 0, 700000)):
  163. data = cycle_manager._job_subjects(worker_id=0, process_count=1)
  164. assert data
  165. assert id(my_subject) in data
  166. assert not data[id(my_subject)]
  167. # Less second after: NOT executed
  168. with freeze_time(datetime.datetime(2000, 12, 1, 0, 0, 1, 500000)):
  169. data = cycle_manager._job_subjects(worker_id=0, process_count=1)
  170. assert data
  171. assert id(my_subject) in data
  172. assert data[id(my_subject)]
  173. def test_simulation_behaviour_cycle_frequency(
  174. self,
  175. do_nothing_process_manager: ProcessManager,
  176. ):
  177. shared.reset()
  178. class MyCycledSimulation(Simulation):
  179. behaviours_classes = [MyCycledSimulationBehaviour]
  180. simulation = MyCycledSimulation(config)
  181. subjects = Subjects(simulation=simulation)
  182. simulation.subjects = subjects
  183. cycle_manager = CycleManager(
  184. config,
  185. logger,
  186. simulation=simulation,
  187. process_manager=do_nothing_process_manager,
  188. )
  189. # Cycle 0: behaviour NOT executed
  190. cycle_manager.current_cycle = 0
  191. data = cycle_manager._job_simulation(worker_id=0, process_count=1)
  192. assert not data
  193. # Cycle 1: behaviour executed
  194. cycle_manager.current_cycle = 1
  195. data = cycle_manager._job_simulation(worker_id=0, process_count=1)
  196. assert data
  197. def test_simulation_behaviour_seconds_frequency(
  198. self,
  199. do_nothing_process_manager: ProcessManager,
  200. ):
  201. shared.reset()
  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