@@ -178,3 +178,48 @@ def test_execute_process_with_output_dictionary():
178178    ls  =  LaunchService ()
179179    ls .include_launch_description (ld )
180180    assert  0  ==  ls .run ()
181+ 
182+ 
183+ def  test_execute_process_with_shutdown_on_error ():
184+     """Test proper shutdown of children after exception during launch.""" 
185+     exited_processes  =  0 
186+ 
187+     def  on_exit (event , context ):
188+         nonlocal  exited_processes 
189+         print (event , context )
190+         exited_processes  +=  1 
191+ 
192+     executable_1  =  ExecuteLocal (
193+         process_description = Executable (
194+             cmd = [sys .executable , '-c' , 'while True: pass' ]
195+         ),
196+         output = {'stdout' : 'screen' , 'stderr' : 'screen' },
197+         on_exit = on_exit ,
198+     )
199+     executable_2  =  ExecuteLocal (
200+         process_description = Executable (
201+             cmd = [sys .executable , '-c' , 'while True: pass' ]
202+         ),
203+         output = {'stdout' : 'screen' , 'stderr' : 'screen' },
204+         on_exit = on_exit ,
205+     )
206+ 
207+     # It's slightly tricky to coerce the standard implementation to fail in 
208+     # this way. However, launch_ros's Node class can fail similar to this and 
209+     # this case therefore needs to be handled correctly. 
210+     class  ExecutableThatFails (ExecuteLocal ):
211+ 
212+         def  execute (self , context ):
213+             raise  Exception ('Execute Local failed' )
214+ 
215+     executable_invalid  =  ExecutableThatFails (
216+         process_description = Executable (
217+             cmd = ['fake_process_that_doesnt_exists' ]
218+         ),
219+         output = {'stdout' : 'screen' , 'stderr' : 'screen' },
220+     )
221+     ld  =  LaunchDescription ([executable_1 , executable_2 , executable_invalid ])
222+     ls  =  LaunchService ()
223+     ls .include_launch_description (ld )
224+     assert  ls .run () ==  1 
225+     assert  exited_processes  ==  2 
0 commit comments