@@ -1573,15 +1573,17 @@ def test_update_scheduler(
1573
1573
def test_exception (self ):
1574
1574
patch_signal = mock .patch (f"{ bastion .__name__ } .send_signal" )
1575
1575
with patch_signal , self ._patch_bastion () as mock_bastion :
1576
+ mock_command_proc = mock .Mock ()
1577
+ mock_cleanup_proc = mock .Mock ()
1576
1578
mock_job = Job (
1577
1579
spec = mock .Mock (),
1578
1580
state = mock .Mock (),
1579
- command_proc = mock . Mock () ,
1580
- cleanup_proc = mock . Mock () ,
1581
+ command_proc = mock_command_proc ,
1582
+ cleanup_proc = mock_cleanup_proc ,
1581
1583
)
1582
1584
1583
1585
def mock_execute ():
1584
- mock_bastion ._active_jobs ["test" ] = mock_job
1586
+ mock_bastion ._active_jobs [mock_job . spec . name ] = mock_job
1585
1587
raise ValueError ("Mock error" )
1586
1588
1587
1589
with mock .patch .multiple (
@@ -1591,14 +1593,19 @@ def mock_execute():
1591
1593
) as mock_methods :
1592
1594
with self .assertRaisesRegex (ValueError , "Mock error" ):
1593
1595
mock_bastion .execute ()
1594
- self .assertIn (
1595
- mock_job .command_proc , mock_methods ["_wait_and_close_proc" ].call_args_list [0 ][0 ]
1596
- )
1597
- self .assertIn (
1598
- mock_job .cleanup_proc , mock_methods ["_wait_and_close_proc" ].call_args_list [1 ][0 ]
1599
- )
1596
+ mock_wait_and_close_proc = mock_methods ["_wait_and_close_proc" ]
1597
+ self .assertIn (mock_command_proc , mock_wait_and_close_proc .call_args_list [0 ][0 ])
1598
+ self .assertIn (mock_cleanup_proc , mock_wait_and_close_proc .call_args_list [1 ][0 ])
1600
1599
1601
- def test_execute_with_exception_and_job_failure (self ):
1600
+ @parameterized .product (
1601
+ kill_job1_error = [None , Exception ("Cannot kill job1" )],
1602
+ kill_job2_error = [None , Exception ("Cannot kill job2" )],
1603
+ )
1604
+ def test_execute_with_exception_and_job_failure (
1605
+ self ,
1606
+ kill_job1_error : Optional [Exception ],
1607
+ kill_job2_error : Optional [Exception ],
1608
+ ):
1602
1609
job_1 = Job (
1603
1610
spec = mock .Mock (),
1604
1611
state = mock .Mock (),
@@ -1612,22 +1619,31 @@ def test_execute_with_exception_and_job_failure(self):
1612
1619
cleanup_proc = mock .Mock (),
1613
1620
)
1614
1621
active_jobs = {
1615
- "job1" : job_1 ,
1616
- "job2" : job_2 ,
1622
+ job_1 . spec . name : job_1 ,
1623
+ job_2 . spec . name : job_2 ,
1617
1624
}
1618
1625
1619
1626
with self ._patch_bastion () as mock_bastion :
1620
1627
mock_bastion ._execute = mock .Mock (side_effect = Exception ("Execution failed" ))
1621
- mock_bastion ._kill_job = mock .Mock (side_effect = [Exception ( "Cannot kill job" ), None ])
1622
-
1628
+ mock_bastion ._kill_job = mock .Mock (side_effect = [kill_job1_error , kill_job2_error ])
1629
+ mock_bastion . _remove_local_job = mock . Mock ( wraps = mock_bastion . _remove_local_job )
1623
1630
mock_bastion ._active_jobs = active_jobs
1624
1631
1625
1632
with self .assertRaises (Exception ):
1626
1633
mock_bastion .execute ()
1627
1634
1628
- self .assertEqual (mock_bastion ._kill_job .call_count , 2 )
1635
+ self .assertEqual (mock_bastion ._remove_local_job .call_count , 2 )
1629
1636
expected_calls = [mock .call (job_1 ), mock .call (job_2 )]
1630
- self .assertEqual (mock_bastion ._kill_job .call_args_list , expected_calls )
1637
+ self .assertEqual (mock_bastion ._remove_local_job .call_args_list , expected_calls )
1638
+ # A job remains if and only if there is exception during the clean up process.
1639
+ self .assertEqual (
1640
+ job_1 in mock_bastion ._active_jobs .values (),
1641
+ kill_job1_error is not None ,
1642
+ )
1643
+ self .assertEqual (
1644
+ job_2 in mock_bastion ._active_jobs .values (),
1645
+ kill_job2_error is not None ,
1646
+ )
1631
1647
1632
1648
def test_sync_jobs_for_valid_pending_to_sudden_invalid_jobs (self ):
1633
1649
"""Test behavior of state transition for pending invalid jobs."""
0 commit comments