test_submission.py 37 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933
  1. # Copyright 2021-2024 Avaiga Private Limited
  2. #
  3. # Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
  4. # the License. You may obtain a copy of the License at
  5. #
  6. # http://www.apache.org/licenses/LICENSE-2.0
  7. #
  8. # Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
  9. # an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
  10. # specific language governing permissions and limitations under the License.
  11. from datetime import datetime
  12. import pytest
  13. from taipy.core import TaskId
  14. from taipy.core.job._job_manager_factory import _JobManagerFactory
  15. from taipy.core.job.job import Job
  16. from taipy.core.job.status import Status
  17. from taipy.core.submission._submission_manager_factory import _SubmissionManagerFactory
  18. from taipy.core.submission.submission import Submission
  19. from taipy.core.submission.submission_status import SubmissionStatus
  20. from taipy.core.task._task_manager_factory import _TaskManagerFactory
  21. from taipy.core.task.task import Task
  22. def test_submission_equals(submission):
  23. submission_manager = _SubmissionManagerFactory()._build_manager()
  24. submission_id = submission.id
  25. submission_manager._set(submission)
  26. # To test if instance is same type
  27. task = Task("task", {}, print, [], [], submission_id)
  28. submission_2 = submission_manager._get(submission_id)
  29. assert submission == submission_2
  30. assert submission != submission_id
  31. assert submission != task
  32. def test_create_submission(scenario, job, current_datetime):
  33. submission_1 = Submission(scenario.id, scenario._ID_PREFIX, scenario.config_id)
  34. assert submission_1.id is not None
  35. assert submission_1.entity_id == scenario.id
  36. assert submission_1.entity_type == scenario._ID_PREFIX
  37. assert submission_1.entity_config_id == scenario.config_id
  38. assert submission_1.jobs == []
  39. assert isinstance(submission_1.creation_date, datetime)
  40. assert submission_1._submission_status == SubmissionStatus.SUBMITTED
  41. assert submission_1._version is not None
  42. submission_2 = Submission(
  43. scenario.id,
  44. scenario._ID_PREFIX,
  45. scenario.config_id,
  46. "submission_id",
  47. [job],
  48. {"debug": True, "log": "log_file", "retry_note": 5},
  49. current_datetime,
  50. SubmissionStatus.COMPLETED,
  51. "version_id",
  52. )
  53. assert submission_2.id == "submission_id"
  54. assert submission_2.entity_id == scenario.id
  55. assert submission_2.entity_type == scenario._ID_PREFIX
  56. assert submission_2.entity_config_id == scenario.config_id
  57. assert submission_2._jobs == [job]
  58. assert submission_2._properties == {"debug": True, "log": "log_file", "retry_note": 5}
  59. assert submission_2.creation_date == current_datetime
  60. assert submission_2._submission_status == SubmissionStatus.COMPLETED
  61. assert submission_2._version == "version_id"
  62. class MockJob:
  63. def __init__(self, id: str, status):
  64. self.status = status
  65. self.id = id
  66. def is_failed(self):
  67. return self.status == Status.FAILED
  68. def is_canceled(self):
  69. return self.status == Status.CANCELED
  70. def is_blocked(self):
  71. return self.status == Status.BLOCKED
  72. def is_pending(self):
  73. return self.status == Status.PENDING
  74. def is_running(self):
  75. return self.status == Status.RUNNING
  76. def is_completed(self):
  77. return self.status == Status.COMPLETED
  78. def is_skipped(self):
  79. return self.status == Status.SKIPPED
  80. def is_abandoned(self):
  81. return self.status == Status.ABANDONED
  82. def is_submitted(self):
  83. return self.status == Status.SUBMITTED
  84. def __test_update_submission_status(job_ids, expected_submission_status):
  85. jobs = {
  86. "job0_submitted": MockJob("job0_submitted", Status.SUBMITTED),
  87. "job1_failed": MockJob("job1_failed", Status.FAILED),
  88. "job2_canceled": MockJob("job2_canceled", Status.CANCELED),
  89. "job3_blocked": MockJob("job3_blocked", Status.BLOCKED),
  90. "job4_pending": MockJob("job4_pending", Status.PENDING),
  91. "job5_running": MockJob("job5_running", Status.RUNNING),
  92. "job6_completed": MockJob("job6_completed", Status.COMPLETED),
  93. "job7_skipped": MockJob("job7_skipped", Status.SKIPPED),
  94. "job8_abandoned": MockJob("job8_abandoned", Status.ABANDONED),
  95. }
  96. submission = Submission("submission_id", "ENTITY_TYPE", "entity_config_id")
  97. submission.jobs = [jobs[job_id] for job_id in job_ids]
  98. for job_id in job_ids:
  99. job = jobs[job_id]
  100. _SubmissionManagerFactory._build_manager()._update_submission_status(submission, job)
  101. assert submission.submission_status == expected_submission_status
  102. @pytest.mark.parametrize(
  103. "job_ids, expected_submission_status",
  104. [
  105. (["job1_failed"], SubmissionStatus.FAILED),
  106. (["job2_canceled"], SubmissionStatus.CANCELED),
  107. (["job3_blocked"], SubmissionStatus.BLOCKED),
  108. (["job4_pending"], SubmissionStatus.PENDING),
  109. (["job5_running"], SubmissionStatus.RUNNING),
  110. (["job6_completed"], SubmissionStatus.COMPLETED),
  111. (["job7_skipped"], SubmissionStatus.COMPLETED),
  112. (["job8_abandoned"], SubmissionStatus.UNDEFINED),
  113. ],
  114. )
  115. def test_update_single_submission_status(job_ids, expected_submission_status):
  116. __test_update_submission_status(job_ids, expected_submission_status)
  117. @pytest.mark.parametrize(
  118. "job_ids, expected_submission_status",
  119. [
  120. (["job1_failed", "job1_failed"], SubmissionStatus.FAILED),
  121. (["job1_failed", "job2_canceled"], SubmissionStatus.FAILED),
  122. (["job1_failed", "job3_blocked"], SubmissionStatus.FAILED),
  123. (["job1_failed", "job4_pending"], SubmissionStatus.FAILED),
  124. (["job1_failed", "job5_running"], SubmissionStatus.FAILED),
  125. (["job1_failed", "job6_completed"], SubmissionStatus.FAILED),
  126. (["job1_failed", "job7_skipped"], SubmissionStatus.FAILED),
  127. (["job1_failed", "job8_abandoned"], SubmissionStatus.FAILED),
  128. (["job2_canceled", "job1_failed"], SubmissionStatus.FAILED),
  129. (["job3_blocked", "job1_failed"], SubmissionStatus.FAILED),
  130. (["job4_pending", "job1_failed"], SubmissionStatus.FAILED),
  131. (["job5_running", "job1_failed"], SubmissionStatus.FAILED),
  132. (["job6_completed", "job1_failed"], SubmissionStatus.FAILED),
  133. (["job7_skipped", "job1_failed"], SubmissionStatus.FAILED),
  134. (["job8_abandoned", "job1_failed"], SubmissionStatus.FAILED),
  135. ],
  136. )
  137. def test_update_submission_status_with_one_failed_job_in_jobs(job_ids, expected_submission_status):
  138. __test_update_submission_status(job_ids, expected_submission_status)
  139. @pytest.mark.parametrize(
  140. "job_ids, expected_submission_status",
  141. [
  142. (["job2_canceled", "job2_canceled"], SubmissionStatus.CANCELED),
  143. (["job2_canceled", "job3_blocked"], SubmissionStatus.CANCELED),
  144. (["job2_canceled", "job4_pending"], SubmissionStatus.CANCELED),
  145. (["job2_canceled", "job5_running"], SubmissionStatus.CANCELED),
  146. (["job2_canceled", "job6_completed"], SubmissionStatus.CANCELED),
  147. (["job2_canceled", "job7_skipped"], SubmissionStatus.CANCELED),
  148. (["job2_canceled", "job8_abandoned"], SubmissionStatus.CANCELED),
  149. (["job3_blocked", "job2_canceled"], SubmissionStatus.CANCELED),
  150. (["job4_pending", "job2_canceled"], SubmissionStatus.CANCELED),
  151. (["job5_running", "job2_canceled"], SubmissionStatus.CANCELED),
  152. (["job6_completed", "job2_canceled"], SubmissionStatus.CANCELED),
  153. (["job7_skipped", "job2_canceled"], SubmissionStatus.CANCELED),
  154. (["job8_abandoned", "job2_canceled"], SubmissionStatus.CANCELED),
  155. ],
  156. )
  157. def test_update_submission_status_with_one_canceled_job_in_jobs(job_ids, expected_submission_status):
  158. __test_update_submission_status(job_ids, expected_submission_status)
  159. @pytest.mark.parametrize(
  160. "job_ids, expected_submission_status",
  161. [
  162. (["job4_pending", "job3_blocked"], SubmissionStatus.PENDING),
  163. (["job4_pending", "job4_pending"], SubmissionStatus.PENDING),
  164. (["job4_pending", "job6_completed"], SubmissionStatus.PENDING),
  165. (["job4_pending", "job7_skipped"], SubmissionStatus.PENDING),
  166. (["job3_blocked", "job4_pending"], SubmissionStatus.PENDING),
  167. (["job6_completed", "job4_pending"], SubmissionStatus.PENDING),
  168. (["job7_skipped", "job4_pending"], SubmissionStatus.PENDING),
  169. ],
  170. )
  171. def test_update_submission_status_with_no_failed_or_cancel_one_pending_in_jobs(job_ids, expected_submission_status):
  172. __test_update_submission_status(job_ids, expected_submission_status)
  173. @pytest.mark.parametrize(
  174. "job_ids, expected_submission_status",
  175. [
  176. (["job5_running", "job3_blocked"], SubmissionStatus.RUNNING),
  177. (["job5_running", "job4_pending"], SubmissionStatus.RUNNING),
  178. (["job5_running", "job5_running"], SubmissionStatus.RUNNING),
  179. (["job5_running", "job6_completed"], SubmissionStatus.RUNNING),
  180. (["job5_running", "job7_skipped"], SubmissionStatus.RUNNING),
  181. (["job3_blocked", "job5_running"], SubmissionStatus.RUNNING),
  182. (["job4_pending", "job5_running"], SubmissionStatus.RUNNING),
  183. (["job6_completed", "job5_running"], SubmissionStatus.RUNNING),
  184. (["job7_skipped", "job5_running"], SubmissionStatus.RUNNING),
  185. ],
  186. )
  187. def test_update_submission_status_with_no_failed_cancel_nor_pending_one_running_in_jobs(
  188. job_ids, expected_submission_status
  189. ):
  190. __test_update_submission_status(job_ids, expected_submission_status)
  191. @pytest.mark.parametrize(
  192. "job_ids, expected_submission_status",
  193. [
  194. (["job3_blocked", "job3_blocked"], SubmissionStatus.BLOCKED),
  195. (["job3_blocked", "job6_completed"], SubmissionStatus.BLOCKED),
  196. (["job3_blocked", "job7_skipped"], SubmissionStatus.BLOCKED),
  197. (["job6_completed", "job3_blocked"], SubmissionStatus.BLOCKED),
  198. (["job7_skipped", "job3_blocked"], SubmissionStatus.BLOCKED),
  199. ],
  200. )
  201. def test_update_submission_status_with_no_failed_cancel_pending_nor_running_one_blocked_in_jobs(
  202. job_ids, expected_submission_status
  203. ):
  204. __test_update_submission_status(job_ids, expected_submission_status)
  205. @pytest.mark.parametrize(
  206. "job_ids, expected_submission_status",
  207. [
  208. (["job6_completed", "job6_completed"], SubmissionStatus.COMPLETED),
  209. (["job6_completed", "job7_skipped"], SubmissionStatus.COMPLETED),
  210. (["job7_skipped", "job6_completed"], SubmissionStatus.COMPLETED),
  211. (["job7_skipped", "job7_skipped"], SubmissionStatus.COMPLETED),
  212. ],
  213. )
  214. def test_update_submission_status_with_only_completed_or_skipped_in_jobs(job_ids, expected_submission_status):
  215. __test_update_submission_status(job_ids, expected_submission_status)
  216. @pytest.mark.parametrize(
  217. "job_ids, expected_submission_status",
  218. [
  219. (["job3_blocked", "job8_abandoned"], SubmissionStatus.UNDEFINED),
  220. (["job4_pending", "job8_abandoned"], SubmissionStatus.UNDEFINED),
  221. (["job5_running", "job8_abandoned"], SubmissionStatus.UNDEFINED),
  222. (["job6_completed", "job8_abandoned"], SubmissionStatus.UNDEFINED),
  223. (["job7_skipped", "job8_abandoned"], SubmissionStatus.UNDEFINED),
  224. (["job8_abandoned", "job8_abandoned"], SubmissionStatus.UNDEFINED),
  225. (["job8_abandoned", "job3_blocked"], SubmissionStatus.UNDEFINED),
  226. (["job8_abandoned", "job4_pending"], SubmissionStatus.UNDEFINED),
  227. (["job8_abandoned", "job5_running"], SubmissionStatus.UNDEFINED),
  228. (["job8_abandoned", "job6_completed"], SubmissionStatus.UNDEFINED),
  229. (["job8_abandoned", "job7_skipped"], SubmissionStatus.UNDEFINED),
  230. ],
  231. )
  232. def test_update_submission_status_with_wrong_case_abandoned_without_cancel_or_failed_in_jobs(
  233. job_ids, expected_submission_status
  234. ):
  235. __test_update_submission_status(job_ids, expected_submission_status)
  236. def test_auto_set_and_reload():
  237. task = Task(config_id="name_1", properties={}, function=print, id=TaskId("task_1"))
  238. submission_1 = Submission(task.id, task._ID_PREFIX, task.config_id, properties={})
  239. job_1 = Job("job_1", task, submission_1.id, submission_1.entity_id)
  240. job_2 = Job("job_2", task, submission_1.id, submission_1.entity_id)
  241. _TaskManagerFactory._build_manager()._set(task)
  242. _SubmissionManagerFactory._build_manager()._set(submission_1)
  243. _JobManagerFactory._build_manager()._set(job_1)
  244. _JobManagerFactory._build_manager()._set(job_2)
  245. submission_2 = _SubmissionManagerFactory._build_manager()._get(submission_1)
  246. assert submission_1.id == submission_2.id
  247. assert submission_1.entity_id == submission_2.entity_id
  248. assert submission_1.creation_date == submission_2.creation_date
  249. assert submission_1.submission_status == submission_2.submission_status
  250. # auto set & reload on jobs attribute
  251. assert submission_1.jobs == []
  252. assert submission_2.jobs == []
  253. submission_1.jobs = [job_1]
  254. assert submission_1.jobs == [job_1]
  255. assert submission_2.jobs == [job_1]
  256. submission_2.jobs = [job_2]
  257. assert submission_1.jobs == [job_2]
  258. assert submission_2.jobs == [job_2]
  259. submission_1.jobs = [job_1, job_2]
  260. assert submission_1.jobs == [job_1, job_2]
  261. assert submission_2.jobs == [job_1, job_2]
  262. submission_2.jobs = [job_2, job_1]
  263. assert submission_1.jobs == [job_2, job_1]
  264. assert submission_2.jobs == [job_2, job_1]
  265. # auto set & reload on is_canceled attribute
  266. assert not submission_1.is_canceled
  267. assert not submission_2.is_canceled
  268. submission_1.is_canceled = True
  269. assert submission_1.is_canceled
  270. assert submission_2.is_canceled
  271. submission_2.is_canceled = False
  272. assert not submission_1.is_canceled
  273. assert not submission_2.is_canceled
  274. # auto set & reload on is_completed attribute
  275. assert not submission_1.is_completed
  276. assert not submission_2.is_completed
  277. submission_1.is_completed = True
  278. assert submission_1.is_completed
  279. assert submission_2.is_completed
  280. submission_2.is_completed = False
  281. assert not submission_1.is_completed
  282. assert not submission_2.is_completed
  283. # auto set & reload on is_abandoned attribute
  284. assert not submission_1.is_abandoned
  285. assert not submission_2.is_abandoned
  286. submission_1.is_abandoned = True
  287. assert submission_1.is_abandoned
  288. assert submission_2.is_abandoned
  289. submission_2.is_abandoned = False
  290. assert not submission_1.is_abandoned
  291. assert not submission_2.is_abandoned
  292. # auto set & reload on submission_status attribute
  293. assert submission_1.submission_status == SubmissionStatus.SUBMITTED
  294. assert submission_2.submission_status == SubmissionStatus.SUBMITTED
  295. submission_1.submission_status = SubmissionStatus.BLOCKED
  296. assert submission_1.submission_status == SubmissionStatus.BLOCKED
  297. assert submission_2.submission_status == SubmissionStatus.BLOCKED
  298. submission_2.submission_status = SubmissionStatus.COMPLETED
  299. assert submission_1.submission_status == SubmissionStatus.COMPLETED
  300. assert submission_2.submission_status == SubmissionStatus.COMPLETED
  301. with submission_1 as submission:
  302. assert submission.jobs == [job_2, job_1]
  303. assert submission.submission_status == SubmissionStatus.COMPLETED
  304. submission.jobs = [job_1]
  305. submission.submission_status = SubmissionStatus.PENDING
  306. assert submission.jobs == [job_2, job_1]
  307. assert submission.submission_status == SubmissionStatus.COMPLETED
  308. assert submission_1.jobs == [job_1]
  309. assert submission_1.submission_status == SubmissionStatus.PENDING
  310. assert submission_2.jobs == [job_1]
  311. assert submission_2.submission_status == SubmissionStatus.PENDING
  312. def test_auto_set_and_reload_properties():
  313. task = Task(config_id="name_1", properties={}, function=print, id=TaskId("task_1"))
  314. submission_1 = Submission(task.id, task._ID_PREFIX, task.config_id, properties={})
  315. _TaskManagerFactory._build_manager()._set(task)
  316. _SubmissionManagerFactory._build_manager()._set(submission_1)
  317. submission_2 = _SubmissionManagerFactory._build_manager()._get(submission_1)
  318. # auto set & reload on properties attribute
  319. assert submission_1.properties == {}
  320. assert submission_2.properties == {}
  321. submission_1._properties["qux"] = 4
  322. assert submission_1.properties["qux"] == 4
  323. assert submission_2.properties["qux"] == 4
  324. assert submission_1.properties == {"qux": 4}
  325. assert submission_2.properties == {"qux": 4}
  326. submission_2._properties["qux"] = 5
  327. assert submission_1.properties["qux"] == 5
  328. assert submission_2.properties["qux"] == 5
  329. submission_1.properties["temp_key_1"] = "temp_value_1"
  330. submission_1.properties["temp_key_2"] = "temp_value_2"
  331. assert submission_1.properties == {"qux": 5, "temp_key_1": "temp_value_1", "temp_key_2": "temp_value_2"}
  332. assert submission_2.properties == {"qux": 5, "temp_key_1": "temp_value_1", "temp_key_2": "temp_value_2"}
  333. submission_1.properties.pop("temp_key_1")
  334. assert "temp_key_1" not in submission_1.properties.keys()
  335. assert "temp_key_1" not in submission_1.properties.keys()
  336. assert submission_1.properties == {"qux": 5, "temp_key_2": "temp_value_2"}
  337. assert submission_2.properties == {"qux": 5, "temp_key_2": "temp_value_2"}
  338. submission_2.properties.pop("temp_key_2")
  339. assert submission_1.properties == {"qux": 5}
  340. assert submission_2.properties == {"qux": 5}
  341. assert "temp_key_2" not in submission_1.properties.keys()
  342. assert "temp_key_2" not in submission_2.properties.keys()
  343. submission_1.properties["temp_key_3"] = 0
  344. assert submission_1.properties == {"qux": 5, "temp_key_3": 0}
  345. assert submission_2.properties == {"qux": 5, "temp_key_3": 0}
  346. submission_1.properties.update({"temp_key_3": 1})
  347. assert submission_1.properties == {"qux": 5, "temp_key_3": 1}
  348. assert submission_2.properties == {"qux": 5, "temp_key_3": 1}
  349. submission_1.properties.update({})
  350. assert submission_1.properties == {"qux": 5, "temp_key_3": 1}
  351. assert submission_2.properties == {"qux": 5, "temp_key_3": 1}
  352. submission_1.properties["temp_key_4"] = 0
  353. submission_1.properties["temp_key_5"] = 0
  354. with submission_1 as submission:
  355. assert submission.properties["qux"] == 5
  356. assert submission.properties["temp_key_3"] == 1
  357. assert submission.properties["temp_key_4"] == 0
  358. assert submission.properties["temp_key_5"] == 0
  359. submission.properties["qux"] = 9
  360. submission.properties.pop("temp_key_3")
  361. submission.properties.pop("temp_key_4")
  362. submission.properties.update({"temp_key_4": 1})
  363. submission.properties.update({"temp_key_5": 2})
  364. submission.properties.pop("temp_key_5")
  365. submission.properties.update({})
  366. assert submission.properties["qux"] == 5
  367. assert submission.properties["temp_key_3"] == 1
  368. assert submission.properties["temp_key_4"] == 0
  369. assert submission.properties["temp_key_5"] == 0
  370. assert submission_1.properties["qux"] == 9
  371. assert "temp_key_3" not in submission_1.properties.keys()
  372. assert submission_1.properties["temp_key_4"] == 1
  373. assert "temp_key_5" not in submission_1.properties.keys()
  374. @pytest.mark.parametrize(
  375. "job_statuses, expected_submission_statuses",
  376. [
  377. (
  378. [Status.SUBMITTED, Status.PENDING, Status.RUNNING, Status.COMPLETED],
  379. [SubmissionStatus.PENDING, SubmissionStatus.PENDING, SubmissionStatus.RUNNING, SubmissionStatus.COMPLETED],
  380. ),
  381. (
  382. [Status.SUBMITTED, Status.PENDING, Status.RUNNING, Status.SKIPPED],
  383. [SubmissionStatus.PENDING, SubmissionStatus.PENDING, SubmissionStatus.RUNNING, SubmissionStatus.COMPLETED],
  384. ),
  385. (
  386. [Status.SUBMITTED, Status.PENDING, Status.RUNNING, Status.FAILED],
  387. [SubmissionStatus.PENDING, SubmissionStatus.PENDING, SubmissionStatus.RUNNING, SubmissionStatus.FAILED],
  388. ),
  389. (
  390. [Status.SUBMITTED, Status.PENDING, Status.CANCELED],
  391. [SubmissionStatus.PENDING, SubmissionStatus.PENDING, SubmissionStatus.CANCELED],
  392. ),
  393. (
  394. [Status.SUBMITTED, Status.PENDING, Status.RUNNING, Status.CANCELED],
  395. [SubmissionStatus.PENDING, SubmissionStatus.PENDING, SubmissionStatus.RUNNING, SubmissionStatus.CANCELED],
  396. ),
  397. ([Status.SUBMITTED, Status.BLOCKED], [SubmissionStatus.PENDING, SubmissionStatus.BLOCKED]),
  398. ([Status.SUBMITTED, Status.SKIPPED], [SubmissionStatus.PENDING, SubmissionStatus.COMPLETED]),
  399. ],
  400. )
  401. def test_update_submission_status_with_single_job_completed(job_statuses, expected_submission_statuses):
  402. submission_manager = _SubmissionManagerFactory._build_manager()
  403. job = MockJob("job_id", Status.SUBMITTED)
  404. submission = Submission("submission_id", "ENTITY_TYPE", "entity_config_id")
  405. submission_manager._set(submission)
  406. assert submission.submission_status == SubmissionStatus.SUBMITTED
  407. for job_status, submission_status in zip(job_statuses, expected_submission_statuses):
  408. job.status = job_status
  409. submission_manager._update_submission_status(submission, job)
  410. assert submission.submission_status == submission_status
  411. def __test_update_submission_status_with_two_jobs(job_ids, job_statuses, expected_submission_statuses):
  412. submission_manager = _SubmissionManagerFactory._build_manager()
  413. jobs = {job_id: MockJob(job_id, Status.SUBMITTED) for job_id in job_ids}
  414. submission = Submission("submission_id", "ENTITY_TYPE", "entity_config_id")
  415. submission_manager._set(submission)
  416. assert submission.submission_status == SubmissionStatus.SUBMITTED
  417. for (job_id, job_status), submission_status in zip(job_statuses, expected_submission_statuses):
  418. job = jobs[job_id]
  419. job.status = job_status
  420. submission_manager._update_submission_status(submission, job)
  421. assert submission.submission_status == submission_status
  422. @pytest.mark.parametrize(
  423. "job_ids, job_statuses, expected_submission_statuses",
  424. [
  425. (
  426. ["job_1", "job_2"],
  427. [
  428. ("job_1", Status.SUBMITTED),
  429. ("job_2", Status.SUBMITTED),
  430. ("job_1", Status.PENDING),
  431. ("job_2", Status.PENDING),
  432. ("job_1", Status.RUNNING),
  433. ("job_2", Status.RUNNING),
  434. ("job_1", Status.COMPLETED),
  435. ("job_2", Status.COMPLETED),
  436. ],
  437. [
  438. SubmissionStatus.PENDING,
  439. SubmissionStatus.PENDING,
  440. SubmissionStatus.PENDING,
  441. SubmissionStatus.PENDING,
  442. SubmissionStatus.RUNNING,
  443. SubmissionStatus.RUNNING,
  444. SubmissionStatus.RUNNING,
  445. SubmissionStatus.COMPLETED,
  446. ],
  447. ),
  448. (
  449. ["job_1", "job_2"],
  450. [
  451. ("job_1", Status.SUBMITTED),
  452. ("job_2", Status.SUBMITTED),
  453. ("job_1", Status.PENDING),
  454. ("job_1", Status.RUNNING),
  455. ("job_2", Status.PENDING),
  456. ("job_2", Status.RUNNING),
  457. ("job_1", Status.COMPLETED),
  458. ("job_2", Status.COMPLETED),
  459. ],
  460. [
  461. SubmissionStatus.PENDING,
  462. SubmissionStatus.PENDING,
  463. SubmissionStatus.PENDING,
  464. SubmissionStatus.RUNNING,
  465. SubmissionStatus.RUNNING,
  466. SubmissionStatus.RUNNING,
  467. SubmissionStatus.RUNNING,
  468. SubmissionStatus.COMPLETED,
  469. ],
  470. ),
  471. (
  472. ["job_1", "job_2"],
  473. [
  474. ("job_1", Status.SUBMITTED),
  475. ("job_2", Status.SUBMITTED),
  476. ("job_1", Status.BLOCKED),
  477. ("job_2", Status.PENDING),
  478. ("job_2", Status.RUNNING),
  479. ("job_2", Status.COMPLETED),
  480. ("job_1", Status.PENDING),
  481. ("job_1", Status.RUNNING),
  482. ("job_1", Status.COMPLETED),
  483. ],
  484. [
  485. SubmissionStatus.PENDING,
  486. SubmissionStatus.PENDING,
  487. SubmissionStatus.PENDING,
  488. SubmissionStatus.PENDING,
  489. SubmissionStatus.RUNNING,
  490. SubmissionStatus.BLOCKED,
  491. SubmissionStatus.PENDING,
  492. SubmissionStatus.RUNNING,
  493. SubmissionStatus.COMPLETED,
  494. ],
  495. ),
  496. ],
  497. )
  498. def test_update_submission_status_with_two_jobs_completed(job_ids, job_statuses, expected_submission_statuses):
  499. __test_update_submission_status_with_two_jobs(job_ids, job_statuses, expected_submission_statuses)
  500. @pytest.mark.parametrize(
  501. "job_ids, job_statuses, expected_submission_statuses",
  502. [
  503. (
  504. ["job_1", "job_2"],
  505. [
  506. ("job_1", Status.SUBMITTED),
  507. ("job_2", Status.SUBMITTED),
  508. ("job_1", Status.PENDING),
  509. ("job_2", Status.PENDING),
  510. ("job_1", Status.RUNNING),
  511. ("job_2", Status.SKIPPED),
  512. ("job_1", Status.COMPLETED),
  513. ],
  514. [
  515. SubmissionStatus.PENDING,
  516. SubmissionStatus.PENDING,
  517. SubmissionStatus.PENDING,
  518. SubmissionStatus.PENDING,
  519. SubmissionStatus.RUNNING,
  520. SubmissionStatus.RUNNING,
  521. SubmissionStatus.COMPLETED,
  522. ],
  523. ),
  524. (
  525. ["job_1", "job_2"],
  526. [
  527. ("job_1", Status.SUBMITTED),
  528. ("job_2", Status.SUBMITTED),
  529. ("job_1", Status.PENDING),
  530. ("job_1", Status.RUNNING),
  531. ("job_2", Status.PENDING),
  532. ("job_2", Status.SKIPPED),
  533. ("job_1", Status.COMPLETED),
  534. ],
  535. [
  536. SubmissionStatus.PENDING,
  537. SubmissionStatus.PENDING,
  538. SubmissionStatus.PENDING,
  539. SubmissionStatus.RUNNING,
  540. SubmissionStatus.RUNNING,
  541. SubmissionStatus.RUNNING,
  542. SubmissionStatus.COMPLETED,
  543. ],
  544. ),
  545. (
  546. ["job_1", "job_2"],
  547. [
  548. ("job_1", Status.SUBMITTED),
  549. ("job_2", Status.SUBMITTED),
  550. ("job_1", Status.BLOCKED),
  551. ("job_2", Status.PENDING),
  552. ("job_2", Status.RUNNING),
  553. ("job_2", Status.COMPLETED),
  554. ("job_1", Status.PENDING),
  555. ("job_1", Status.SKIPPED),
  556. ],
  557. [
  558. SubmissionStatus.PENDING,
  559. SubmissionStatus.PENDING,
  560. SubmissionStatus.PENDING,
  561. SubmissionStatus.PENDING,
  562. SubmissionStatus.RUNNING,
  563. SubmissionStatus.BLOCKED,
  564. SubmissionStatus.PENDING,
  565. SubmissionStatus.COMPLETED,
  566. ],
  567. ),
  568. (
  569. ["job_1", "job_2"],
  570. [
  571. ("job_1", Status.SUBMITTED),
  572. ("job_2", Status.SUBMITTED),
  573. ("job_1", Status.PENDING),
  574. ("job_2", Status.PENDING),
  575. ("job_1", Status.SKIPPED),
  576. ("job_2", Status.SKIPPED),
  577. ],
  578. [
  579. SubmissionStatus.PENDING,
  580. SubmissionStatus.PENDING,
  581. SubmissionStatus.PENDING,
  582. SubmissionStatus.PENDING,
  583. SubmissionStatus.PENDING,
  584. SubmissionStatus.COMPLETED,
  585. ],
  586. ),
  587. (
  588. ["job_1", "job_2"],
  589. [
  590. ("job_1", Status.SUBMITTED),
  591. ("job_2", Status.SUBMITTED),
  592. ("job_1", Status.PENDING),
  593. ("job_1", Status.SKIPPED),
  594. ("job_2", Status.PENDING),
  595. ("job_2", Status.SKIPPED),
  596. ],
  597. [
  598. SubmissionStatus.PENDING,
  599. SubmissionStatus.PENDING,
  600. SubmissionStatus.PENDING,
  601. SubmissionStatus.PENDING,
  602. SubmissionStatus.PENDING,
  603. SubmissionStatus.COMPLETED,
  604. ],
  605. ),
  606. (
  607. ["job_1", "job_2"],
  608. [
  609. ("job_1", Status.SUBMITTED),
  610. ("job_2", Status.SUBMITTED),
  611. ("job_1", Status.BLOCKED),
  612. ("job_2", Status.PENDING),
  613. ("job_2", Status.SKIPPED),
  614. ("job_1", Status.PENDING),
  615. ("job_1", Status.SKIPPED),
  616. ],
  617. [
  618. SubmissionStatus.PENDING,
  619. SubmissionStatus.PENDING,
  620. SubmissionStatus.PENDING,
  621. SubmissionStatus.PENDING,
  622. SubmissionStatus.BLOCKED,
  623. SubmissionStatus.PENDING,
  624. SubmissionStatus.COMPLETED,
  625. ],
  626. ),
  627. ],
  628. )
  629. def test_update_submission_status_with_two_jobs_skipped(job_ids, job_statuses, expected_submission_statuses):
  630. __test_update_submission_status_with_two_jobs(job_ids, job_statuses, expected_submission_statuses)
  631. @pytest.mark.parametrize(
  632. "job_ids, job_statuses, expected_submission_statuses",
  633. [
  634. (
  635. ["job_1", "job_2"],
  636. [
  637. ("job_1", Status.SUBMITTED),
  638. ("job_2", Status.SUBMITTED),
  639. ("job_1", Status.PENDING),
  640. ("job_2", Status.PENDING),
  641. ("job_1", Status.RUNNING),
  642. ("job_2", Status.RUNNING),
  643. ("job_1", Status.FAILED),
  644. ("job_2", Status.COMPLETED),
  645. ],
  646. [
  647. SubmissionStatus.PENDING,
  648. SubmissionStatus.PENDING,
  649. SubmissionStatus.PENDING,
  650. SubmissionStatus.PENDING,
  651. SubmissionStatus.RUNNING,
  652. SubmissionStatus.RUNNING,
  653. SubmissionStatus.FAILED,
  654. SubmissionStatus.FAILED,
  655. ],
  656. ),
  657. (
  658. ["job_1", "job_2"],
  659. [
  660. ("job_1", Status.SUBMITTED),
  661. ("job_2", Status.SUBMITTED),
  662. ("job_1", Status.PENDING),
  663. ("job_1", Status.RUNNING),
  664. ("job_2", Status.PENDING),
  665. ("job_2", Status.RUNNING),
  666. ("job_1", Status.COMPLETED),
  667. ("job_2", Status.FAILED),
  668. ],
  669. [
  670. SubmissionStatus.PENDING,
  671. SubmissionStatus.PENDING,
  672. SubmissionStatus.PENDING,
  673. SubmissionStatus.RUNNING,
  674. SubmissionStatus.RUNNING,
  675. SubmissionStatus.RUNNING,
  676. SubmissionStatus.RUNNING,
  677. SubmissionStatus.FAILED,
  678. ],
  679. ),
  680. (
  681. ["job_1", "job_2"],
  682. [
  683. ("job_1", Status.SUBMITTED),
  684. ("job_2", Status.SUBMITTED),
  685. ("job_1", Status.BLOCKED),
  686. ("job_2", Status.PENDING),
  687. ("job_2", Status.RUNNING),
  688. ("job_2", Status.FAILED),
  689. ("job_1", Status.ABANDONED),
  690. ],
  691. [
  692. SubmissionStatus.PENDING,
  693. SubmissionStatus.PENDING,
  694. SubmissionStatus.PENDING,
  695. SubmissionStatus.PENDING,
  696. SubmissionStatus.RUNNING,
  697. SubmissionStatus.FAILED,
  698. SubmissionStatus.FAILED,
  699. ],
  700. ),
  701. ],
  702. )
  703. def test_update_submission_status_with_two_jobs_failed(job_ids, job_statuses, expected_submission_statuses):
  704. __test_update_submission_status_with_two_jobs(job_ids, job_statuses, expected_submission_statuses)
  705. @pytest.mark.parametrize(
  706. "job_ids, job_statuses, expected_submission_statuses",
  707. [
  708. (
  709. ["job_1", "job_2"],
  710. [
  711. ("job_1", Status.SUBMITTED),
  712. ("job_2", Status.SUBMITTED),
  713. ("job_1", Status.PENDING),
  714. ("job_2", Status.PENDING),
  715. ("job_1", Status.RUNNING),
  716. ("job_2", Status.RUNNING),
  717. ("job_1", Status.CANCELED),
  718. ("job_2", Status.COMPLETED),
  719. ],
  720. [
  721. SubmissionStatus.PENDING,
  722. SubmissionStatus.PENDING,
  723. SubmissionStatus.PENDING,
  724. SubmissionStatus.PENDING,
  725. SubmissionStatus.RUNNING,
  726. SubmissionStatus.RUNNING,
  727. SubmissionStatus.CANCELED,
  728. SubmissionStatus.CANCELED,
  729. ],
  730. ),
  731. (
  732. ["job_1", "job_2"],
  733. [
  734. ("job_1", Status.SUBMITTED),
  735. ("job_2", Status.SUBMITTED),
  736. ("job_1", Status.PENDING),
  737. ("job_1", Status.RUNNING),
  738. ("job_2", Status.PENDING),
  739. ("job_2", Status.RUNNING),
  740. ("job_1", Status.COMPLETED),
  741. ("job_2", Status.CANCELED),
  742. ],
  743. [
  744. SubmissionStatus.PENDING,
  745. SubmissionStatus.PENDING,
  746. SubmissionStatus.PENDING,
  747. SubmissionStatus.RUNNING,
  748. SubmissionStatus.RUNNING,
  749. SubmissionStatus.RUNNING,
  750. SubmissionStatus.RUNNING,
  751. SubmissionStatus.CANCELED,
  752. ],
  753. ),
  754. (
  755. ["job_1", "job_2"],
  756. [
  757. ("job_1", Status.SUBMITTED),
  758. ("job_2", Status.SUBMITTED),
  759. ("job_1", Status.BLOCKED),
  760. ("job_2", Status.PENDING),
  761. ("job_2", Status.RUNNING),
  762. ("job_2", Status.CANCELED),
  763. ("job_1", Status.ABANDONED),
  764. ],
  765. [
  766. SubmissionStatus.PENDING,
  767. SubmissionStatus.PENDING,
  768. SubmissionStatus.PENDING,
  769. SubmissionStatus.PENDING,
  770. SubmissionStatus.RUNNING,
  771. SubmissionStatus.CANCELED,
  772. SubmissionStatus.CANCELED,
  773. ],
  774. ),
  775. ],
  776. )
  777. def test_update_submission_status_with_two_jobs_canceled(job_ids, job_statuses, expected_submission_statuses):
  778. __test_update_submission_status_with_two_jobs(job_ids, job_statuses, expected_submission_statuses)
  779. def test_is_finished():
  780. submission_manager = _SubmissionManagerFactory._build_manager()
  781. submission = Submission("entity_id", "entity_type", "entity_config_id", "submission_id")
  782. submission_manager._set(submission)
  783. assert len(submission_manager._get_all()) == 1
  784. assert submission._submission_status == SubmissionStatus.SUBMITTED
  785. assert not submission.is_finished()
  786. submission.submission_status = SubmissionStatus.UNDEFINED
  787. assert submission.submission_status == SubmissionStatus.UNDEFINED
  788. assert not submission.is_finished()
  789. submission.submission_status = SubmissionStatus.CANCELED
  790. assert submission.submission_status == SubmissionStatus.CANCELED
  791. assert submission.is_finished()
  792. submission.submission_status = SubmissionStatus.FAILED
  793. assert submission.submission_status == SubmissionStatus.FAILED
  794. assert submission.is_finished()
  795. submission.submission_status = SubmissionStatus.BLOCKED
  796. assert submission.submission_status == SubmissionStatus.BLOCKED
  797. assert not submission.is_finished()
  798. submission.submission_status = SubmissionStatus.RUNNING
  799. assert submission.submission_status == SubmissionStatus.RUNNING
  800. assert not submission.is_finished()
  801. submission.submission_status = SubmissionStatus.PENDING
  802. assert submission.submission_status == SubmissionStatus.PENDING
  803. assert not submission.is_finished()
  804. submission.submission_status = SubmissionStatus.COMPLETED
  805. assert submission.submission_status == SubmissionStatus.COMPLETED
  806. assert submission.is_finished()
  807. def test_execution_duration():
  808. task = Task(config_id="task_1", properties={}, function=print, id=TaskId("task_1"))
  809. submission = Submission(task.id, task._ID_PREFIX, task.config_id, properties={})
  810. job_1 = Job("job_1", task, submission.id, submission.entity_id)
  811. job_2 = Job("job_2", task, submission.id, submission.entity_id)
  812. _TaskManagerFactory._build_manager()._set(task)
  813. _SubmissionManagerFactory._build_manager()._set(submission)
  814. _JobManagerFactory._build_manager()._set(job_1)
  815. _JobManagerFactory._build_manager()._set(job_2)
  816. submission.jobs = [job_1, job_2]
  817. _SubmissionManagerFactory._build_manager()._set(submission)
  818. job_1.execution_started_at = datetime(2024, 1, 1, 0, 0, 0)
  819. job_1.execution_ended_at = datetime(2024, 1, 1, 0, 0, 10)
  820. job_2.execution_started_at = datetime(2024, 1, 1, 0, 1, 0)
  821. job_2.execution_ended_at = datetime(2024, 1, 1, 0, 2, 30)
  822. assert submission.execution_started_at == job_1.execution_started_at
  823. assert submission.execution_ended_at == job_2.execution_ended_at
  824. assert submission.execution_duration == 150
  825. job_2.execution_ended_at = None # job_2 is still running
  826. assert submission.execution_started_at == job_1.execution_started_at
  827. assert submission.execution_ended_at is None
  828. assert submission.execution_duration is None