@@ -337,4 +337,124 @@ public function it_should_run_single_task_successfully(): void {
337337 $ this ->assertTrue ( $ after_called , 'after callable should have been called ' );
338338 $ this ->assertTrue ( $ always_called , 'always callable should have been called ' );
339339 }
340+
341+ /**
342+ * @test
343+ */
344+ public function it_should_catch_exception_thrown_in_before_callable (): void {
345+ $ shepherd = shepherd ();
346+ $ prefix = tests_shepherd_get_hook_prefix ();
347+
348+ $ task = new Do_Action_Task ();
349+
350+ $ run_failed_count = did_action ( "shepherd_ {$ prefix }_tasks_run_failed " );
351+ $ captured_tasks = null ;
352+ $ captured_exception = null ;
353+
354+ add_action ( "shepherd_ {$ prefix }_tasks_run_failed " , function ( $ tasks , $ e ) use ( &$ captured_tasks , &$ captured_exception ) {
355+ $ captured_tasks = $ tasks ;
356+ $ captured_exception = $ e ;
357+ }, 10 , 2 );
358+
359+ $ shepherd ->run ( [ $ task ], [
360+ 'before ' => function () {
361+ throw new Exception ( 'Before callable failed ' );
362+ },
363+ ] );
364+
365+ $ this ->assertSame ( $ run_failed_count + 1 , did_action ( "shepherd_ {$ prefix }_tasks_run_failed " ), 'tasks_run_failed action should have fired ' );
366+ $ this ->assertIsArray ( $ captured_tasks );
367+ $ this ->assertCount ( 1 , $ captured_tasks );
368+ $ this ->assertInstanceOf ( Exception::class, $ captured_exception );
369+ $ this ->assertSame ( 'Before callable failed ' , $ captured_exception ->getMessage () );
370+ }
371+
372+ /**
373+ * @test
374+ */
375+ public function it_should_catch_exception_thrown_in_after_callable (): void {
376+ $ shepherd = shepherd ();
377+ $ prefix = tests_shepherd_get_hook_prefix ();
378+
379+ $ task = new Do_Action_Task ();
380+
381+ $ run_failed_count = did_action ( "shepherd_ {$ prefix }_tasks_run_failed " );
382+ $ captured_exception = null ;
383+
384+ add_action ( "shepherd_ {$ prefix }_tasks_run_failed " , function ( $ tasks , $ e ) use ( &$ captured_exception ) {
385+ $ captured_exception = $ e ;
386+ }, 10 , 2 );
387+
388+ $ shepherd ->run ( [ $ task ], [
389+ 'after ' => function () {
390+ throw new Exception ( 'After callable failed ' );
391+ },
392+ ] );
393+
394+ // Task should have run before the after callable threw
395+ $ this ->assertSame ( 1 , did_action ( $ task ->get_task_name () ), 'Task should have run before after callable failed ' );
396+ $ this ->assertSame ( $ run_failed_count + 1 , did_action ( "shepherd_ {$ prefix }_tasks_run_failed " ), 'tasks_run_failed action should have fired ' );
397+ $ this ->assertInstanceOf ( Exception::class, $ captured_exception );
398+ $ this ->assertSame ( 'After callable failed ' , $ captured_exception ->getMessage () );
399+ }
400+
401+ /**
402+ * @test
403+ */
404+ public function it_should_catch_exception_thrown_in_always_callable (): void {
405+ $ shepherd = shepherd ();
406+ $ prefix = tests_shepherd_get_hook_prefix ();
407+
408+ $ task = new Do_Action_Task ();
409+
410+ $ run_failed_count = did_action ( "shepherd_ {$ prefix }_tasks_run_failed " );
411+ $ tasks_finished_count = did_action ( "shepherd_ {$ prefix }_tasks_finished " );
412+ $ captured_exception = null ;
413+
414+ add_action ( "shepherd_ {$ prefix }_tasks_run_failed " , function ( $ tasks , $ e ) use ( &$ captured_exception ) {
415+ $ captured_exception = $ e ;
416+ }, 10 , 2 );
417+
418+ $ shepherd ->run ( [ $ task ], [
419+ 'always ' => function () {
420+ throw new Exception ( 'Always callable failed ' );
421+ },
422+ ] );
423+
424+ // Task should have run successfully
425+ $ this ->assertSame ( 1 , did_action ( $ task ->get_task_name () ), 'Task should have run ' );
426+ // tasks_finished should NOT have fired because always callable threw before it
427+ $ this ->assertSame ( $ tasks_finished_count , did_action ( "shepherd_ {$ prefix }_tasks_finished " ), 'tasks_finished should not have fired ' );
428+ // tasks_run_failed should have fired
429+ $ this ->assertSame ( $ run_failed_count + 1 , did_action ( "shepherd_ {$ prefix }_tasks_run_failed " ), 'tasks_run_failed action should have fired ' );
430+ $ this ->assertInstanceOf ( Exception::class, $ captured_exception );
431+ $ this ->assertSame ( 'Always callable failed ' , $ captured_exception ->getMessage () );
432+ }
433+
434+ /**
435+ * @test
436+ */
437+ public function it_should_catch_throwable_in_callable (): void {
438+ $ shepherd = shepherd ();
439+ $ prefix = tests_shepherd_get_hook_prefix ();
440+
441+ $ task = new Do_Action_Task ();
442+
443+ $ run_failed_count = did_action ( "shepherd_ {$ prefix }_tasks_run_failed " );
444+ $ captured_throwable = null ;
445+
446+ add_action ( "shepherd_ {$ prefix }_tasks_run_failed " , function ( $ tasks , $ e ) use ( &$ captured_throwable ) {
447+ $ captured_throwable = $ e ;
448+ }, 10 , 2 );
449+
450+ $ shepherd ->run ( [ $ task ], [
451+ 'before ' => function () {
452+ throw new \Error ( 'Type error in callable ' );
453+ },
454+ ] );
455+
456+ $ this ->assertSame ( $ run_failed_count + 1 , did_action ( "shepherd_ {$ prefix }_tasks_run_failed " ), 'tasks_run_failed action should have fired for Throwable ' );
457+ $ this ->assertInstanceOf ( \Throwable::class, $ captured_throwable );
458+ $ this ->assertSame ( 'Type error in callable ' , $ captured_throwable ->getMessage () );
459+ }
340460}
0 commit comments