test_simulation.py 17KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544
  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 IS 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. # Cycle 1: behaviour IS NOT executed
  124. cycle_manager.current_cycle = 1
  125. results_by_subjects = cycle_manager._job_subjects(worker_id=0, process_count=1)
  126. assert not results_by_subjects
  127. def test_subject_behaviour_seconds_frequency(
  128. self,
  129. do_nothing_process_manager: ProcessManager,
  130. ):
  131. shared.reset()
  132. class MySubject(Subject):
  133. behaviours_classes = [MyTimedSubjectBehaviour]
  134. simulation = Simulation(config)
  135. my_subject = MySubject(config, simulation)
  136. subjects = Subjects(simulation=simulation)
  137. subjects.append(my_subject)
  138. simulation.subjects = subjects
  139. cycle_manager = CycleManager(
  140. config,
  141. logger,
  142. simulation=simulation,
  143. process_manager=do_nothing_process_manager,
  144. )
  145. # Thirst time, behaviour IS executed
  146. with freeze_time(datetime.datetime(2000, 12, 1, 0, 0, 0)):
  147. data = cycle_manager._job_subjects(worker_id=0, process_count=1)
  148. assert data
  149. assert id(my_subject) in data
  150. assert data[id(my_subject)]
  151. # Less second after: NOT executed
  152. with freeze_time(datetime.datetime(2000, 12, 1, 0, 0, 0, 500000)):
  153. data = cycle_manager._job_subjects(worker_id=0, process_count=1)
  154. assert not data
  155. # Less second after: NOT executed
  156. with freeze_time(datetime.datetime(2000, 12, 1, 0, 0, 0, 700000)):
  157. data = cycle_manager._job_subjects(worker_id=0, process_count=1)
  158. assert not data
  159. # Less second after: IS executed
  160. with freeze_time(datetime.datetime(2000, 12, 1, 0, 0, 1, 500000)):
  161. data = cycle_manager._job_subjects(worker_id=0, process_count=1)
  162. assert data
  163. def test_simulation_behaviour_cycle_frequency(
  164. self,
  165. do_nothing_process_manager: ProcessManager,
  166. ):
  167. shared.reset()
  168. class MyCycledSimulation(Simulation):
  169. behaviours_classes = [MyCycledSimulationBehaviour]
  170. simulation = MyCycledSimulation(config)
  171. subjects = Subjects(simulation=simulation)
  172. simulation.subjects = subjects
  173. cycle_manager = CycleManager(
  174. config,
  175. logger,
  176. simulation=simulation,
  177. process_manager=do_nothing_process_manager,
  178. )
  179. # Cycle 0: behaviour IS executed
  180. cycle_manager.current_cycle = 0
  181. data = cycle_manager._job_simulation(worker_id=0, process_count=1)
  182. assert data
  183. # Cycle 1: behaviour IS NOT executed
  184. cycle_manager.current_cycle = 1
  185. data = cycle_manager._job_simulation(worker_id=0, process_count=1)
  186. assert not data
  187. def test_simulation_behaviour_seconds_frequency(
  188. self,
  189. do_nothing_process_manager: ProcessManager,
  190. ):
  191. shared.reset()
  192. class MyTimedSimulation(Simulation):
  193. behaviours_classes = [MyTimedSimulationBehaviour]
  194. simulation = MyTimedSimulation(config)
  195. subjects = Subjects(simulation=simulation)
  196. simulation.subjects = subjects
  197. cycle_manager = CycleManager(
  198. config,
  199. logger,
  200. simulation=simulation,
  201. process_manager=do_nothing_process_manager,
  202. )
  203. # Thirst time, behaviour IS executed
  204. with freeze_time(datetime.datetime(2000, 12, 1, 0, 0, 0)):
  205. data = cycle_manager._job_simulation(worker_id=0, process_count=1)
  206. assert data
  207. # Less second after: NOT executed
  208. with freeze_time(datetime.datetime(2000, 12, 1, 0, 0, 0, 500000)):
  209. data = cycle_manager._job_simulation(worker_id=0, process_count=1)
  210. assert not data
  211. # Less second after: NOT executed
  212. with freeze_time(datetime.datetime(2000, 12, 1, 0, 0, 0, 700000)):
  213. data = cycle_manager._job_simulation(worker_id=0, process_count=1)
  214. assert not data
  215. # More second after: IS executed
  216. with freeze_time(datetime.datetime(2000, 12, 1, 0, 0, 1, 500000)):
  217. data = cycle_manager._job_simulation(worker_id=0, process_count=1)
  218. assert data
  219. def test_subject_behavior_not_called_if_no_more_subjects(
  220. self,
  221. do_nothing_process_manager: ProcessManager,
  222. ):
  223. shared.reset()
  224. class MySubject(Subject):
  225. behaviours_classes = [MySubjectBehaviour]
  226. simulation = Simulation(config)
  227. my_subject = MySubject(config, simulation)
  228. subjects = Subjects(simulation=simulation)
  229. subjects.append(my_subject)
  230. simulation.subjects = subjects
  231. cycle_manager = CycleManager(
  232. config,
  233. logger,
  234. simulation=simulation,
  235. process_manager=do_nothing_process_manager,
  236. )
  237. results_by_subjects = cycle_manager._job_subjects(worker_id=0, process_count=1)
  238. assert results_by_subjects
  239. assert id(my_subject) in results_by_subjects
  240. assert results_by_subjects[id(my_subject)]
  241. # If we remove subject, no more data generated
  242. subjects.remove(my_subject)
  243. results_by_subjects = cycle_manager._job_subjects(worker_id=0, process_count=1)
  244. assert not results_by_subjects
  245. class TestMechanisms(BaseTest):
  246. def test_mechanism_called_once_for_multiple_subject_behaviors(
  247. self,
  248. do_nothing_process_manager: ProcessManager,
  249. ):
  250. shared.reset()
  251. called = 0
  252. global called
  253. class MySubjectMechanism(SubjectMechanism):
  254. def run(self):
  255. global called
  256. called += 1
  257. return {'foo': 42}
  258. class MySubjectBehaviour1(SubjectBehaviour):
  259. use = [MySubjectMechanism]
  260. def run(self, data):
  261. return {'bar': data[MySubjectMechanism]['foo'] + 100}
  262. class MySubjectBehaviour2(SubjectBehaviour):
  263. use = [MySubjectMechanism]
  264. def run(self, data):
  265. return {'bar': data[MySubjectMechanism]['foo'] + 100}
  266. class MySubject(Subject):
  267. behaviours_classes = [MySubjectBehaviour1, MySubjectBehaviour2]
  268. simulation = Simulation(config)
  269. my_subject = MySubject(config, simulation)
  270. subjects = Subjects(simulation=simulation)
  271. subjects.append(my_subject)
  272. simulation.subjects = subjects
  273. cycle_manager = CycleManager(
  274. config,
  275. logger,
  276. simulation=simulation,
  277. process_manager=do_nothing_process_manager,
  278. )
  279. cycle_manager._job_subjects(worker_id=0, process_count=1)
  280. assert called == 1
  281. def test_mechanism_called_once_for_multiple_simulation_behaviors(
  282. self,
  283. do_nothing_process_manager: ProcessManager,
  284. ):
  285. shared.reset()
  286. called = 0
  287. global called
  288. class MySimulationMechanism(SimulationMechanism):
  289. def run(self, process_number: int = None, process_count: int = None):
  290. global called
  291. called += 1
  292. return {'foo': 42}
  293. class MySimulationBehaviour1(SimulationBehaviour):
  294. use = [MySimulationMechanism]
  295. def run(self, data):
  296. return {'bar': data[MySimulationMechanism]['foo'] + 100}
  297. class MySimulationBehaviour2(SimulationBehaviour):
  298. use = [MySimulationMechanism]
  299. def run(self, data):
  300. return {'bar': data[MySimulationMechanism]['foo'] + 100}
  301. class MySimulation(Simulation):
  302. behaviours_classes = [MySimulationBehaviour1, MySimulationBehaviour2]
  303. simulation = MySimulation(config)
  304. subjects = Subjects(simulation=simulation)
  305. simulation.subjects = subjects
  306. cycle_manager = CycleManager(
  307. config,
  308. logger,
  309. simulation=simulation,
  310. process_manager=do_nothing_process_manager,
  311. )
  312. cycle_manager._job_simulation(worker_id=0, process_count=1)
  313. assert called == 1
  314. def test_mechanism_not_called_if_no_subject_behavior(
  315. self,
  316. do_nothing_process_manager: ProcessManager,
  317. ):
  318. shared.reset()
  319. called = 0
  320. global called
  321. class MySubjectMechanism(SubjectMechanism):
  322. def run(self):
  323. global called
  324. called += 1
  325. return {'foo': 42}
  326. class MySubject(Subject):
  327. behaviours_classes = []
  328. simulation = Simulation(config)
  329. my_subject = MySubject(config, simulation)
  330. subjects = Subjects(simulation=simulation)
  331. subjects.append(my_subject)
  332. simulation.subjects = subjects
  333. cycle_manager = CycleManager(
  334. config,
  335. logger,
  336. simulation=simulation,
  337. process_manager=do_nothing_process_manager,
  338. )
  339. cycle_manager._job_subjects(worker_id=0, process_count=1)
  340. assert called == 0
  341. def test_mechanism_not_called_if_no_simulation_behavior(
  342. self,
  343. do_nothing_process_manager: ProcessManager,
  344. ):
  345. shared.reset()
  346. called = 0
  347. global called
  348. class MySimulationMechanism(SimulationMechanism):
  349. def run(self, process_number: int = None, process_count: int = None):
  350. global called
  351. called += 1
  352. return {'foo': 42}
  353. class MySimulation(Simulation):
  354. pass
  355. simulation = MySimulation(config)
  356. subjects = Subjects(simulation=simulation)
  357. simulation.subjects = subjects
  358. cycle_manager = CycleManager(
  359. config,
  360. logger,
  361. simulation=simulation,
  362. process_manager=do_nothing_process_manager,
  363. )
  364. cycle_manager._job_simulation(worker_id=0, process_count=1)
  365. assert called == 0
  366. def test_mechanism_not_called_if_subject_behavior_cycled_not_active_yet(
  367. self,
  368. do_nothing_process_manager: ProcessManager,
  369. ):
  370. shared.reset()
  371. called = 0
  372. global called
  373. class MySubjectMechanism(SubjectMechanism):
  374. def run(self):
  375. global called
  376. called += 1
  377. return {'foo': 42}
  378. class MySubjectBehaviour1(SubjectBehaviour):
  379. use = [MySubjectMechanism]
  380. @property
  381. def cycle_frequency(self):
  382. return 2
  383. def run(self, data):
  384. return {'bar': data[MySubjectMechanism]['foo'] + 100}
  385. class MySubject(Subject):
  386. behaviours_classes = [MySubjectBehaviour1]
  387. simulation = Simulation(config)
  388. my_subject = MySubject(config, simulation)
  389. subjects = Subjects(simulation=simulation)
  390. subjects.append(my_subject)
  391. simulation.subjects = subjects
  392. cycle_manager = CycleManager(
  393. config,
  394. logger,
  395. simulation=simulation,
  396. process_manager=do_nothing_process_manager,
  397. )
  398. cycle_manager.current_cycle = 0
  399. cycle_manager._job_subjects(worker_id=0, process_count=1)
  400. assert called == 1
  401. cycle_manager.current_cycle = 1
  402. cycle_manager._job_subjects(worker_id=0, process_count=1)
  403. assert called == 1
  404. cycle_manager.current_cycle = 2
  405. cycle_manager._job_subjects(worker_id=0, process_count=1)
  406. assert called == 2
  407. cycle_manager.current_cycle = 3
  408. cycle_manager._job_subjects(worker_id=0, process_count=1)
  409. assert called == 2
  410. def test_mechanism_not_called_if_simulation_behavior_cycled_not_active_yet(
  411. self,
  412. do_nothing_process_manager: ProcessManager,
  413. ):
  414. shared.reset()
  415. pass
  416. def test_mechanism_not_called_if_subject_behavior_timebase_not_active_yet(
  417. self,
  418. do_nothing_process_manager: ProcessManager,
  419. ):
  420. shared.reset()
  421. pass
  422. def test_mechanism_not_called_if_simulation_behavior_timebase_not_active_yet(
  423. self,
  424. do_nothing_process_manager: ProcessManager,
  425. ):
  426. shared.reset()
  427. pass
  428. # TODO: Test Simulation mechanism parralelisation
  429. # TODO: Test behaviour actions generation