@@ -127,7 +127,7 @@ describe(OperationExecutionManager.name, () => {
127127 quietMode : false ,
128128 debugMode : false ,
129129 parallelism : 1 ,
130- destination : mockWritable ,
130+ destinations : [ mockWritable ] ,
131131 abortController : new AbortController ( )
132132 } ;
133133 executionPassOptions = { } ;
@@ -210,7 +210,7 @@ describe(OperationExecutionManager.name, () => {
210210 quietMode : false ,
211211 debugMode : false ,
212212 parallelism : 1 ,
213- destination : mockWritable ,
213+ destinations : [ mockWritable ] ,
214214 abortController : new AbortController ( )
215215 }
216216 ) ;
@@ -257,7 +257,7 @@ describe(OperationExecutionManager.name, () => {
257257 quietMode : false ,
258258 debugMode : false ,
259259 parallelism : 1 ,
260- destination : mockWritable ,
260+ destinations : [ mockWritable ] ,
261261 abortController : new AbortController ( )
262262 }
263263 ) ;
@@ -277,7 +277,7 @@ describe(OperationExecutionManager.name, () => {
277277 quietMode : false ,
278278 debugMode : false ,
279279 parallelism : 1 ,
280- destination : mockWritable ,
280+ destinations : [ mockWritable ] ,
281281 abortController : new AbortController ( )
282282 } ;
283283 executionPassOptions = { } ;
@@ -348,7 +348,7 @@ describe(OperationExecutionManager.name, () => {
348348 quietMode : false ,
349349 debugMode : false ,
350350 parallelism : 1 ,
351- destination : mockWritable ,
351+ destinations : [ mockWritable ] ,
352352 abortController : new AbortController ( )
353353 } ;
354354 } ) ;
@@ -386,7 +386,7 @@ describe(OperationExecutionManager.name, () => {
386386 quietMode : false ,
387387 debugMode : false ,
388388 parallelism : 1 ,
389- destination : mockWritable ,
389+ destinations : [ mockWritable ] ,
390390 abortController : new AbortController ( )
391391 } ;
392392 } ) ;
@@ -560,7 +560,7 @@ describe(OperationExecutionManager.name, () => {
560560 quietMode : false ,
561561 debugMode : false ,
562562 parallelism : 1 ,
563- destination : mockWritable ,
563+ destinations : [ mockWritable ] ,
564564 abortController : new AbortController ( ) ,
565565 runNextPassBehavior : 'manual'
566566 } ;
@@ -610,7 +610,7 @@ describe(OperationExecutionManager.name, () => {
610610 quietMode : false ,
611611 debugMode : false ,
612612 parallelism : 1 ,
613- destination : mockWritable ,
613+ destinations : [ mockWritable ] ,
614614 abortController : new AbortController ( ) ,
615615 runNextPassBehavior : 'manual'
616616 } ;
@@ -641,6 +641,111 @@ describe(OperationExecutionManager.name, () => {
641641 expect ( manager . status ) . toBe ( OperationStatus . Ready ) ;
642642 } ) ;
643643 } ) ;
644+
645+ describe ( 'Terminal destination APIs' , ( ) => {
646+ beforeEach ( ( ) => {
647+ executionManagerOptions = {
648+ quietMode : false ,
649+ debugMode : false ,
650+ parallelism : 1 ,
651+ destinations : [ mockWritable ] ,
652+ abortController : new AbortController ( )
653+ } ;
654+ executionPassOptions = { } ;
655+ } ) ;
656+
657+ it ( 'addTerminalDestination causes new destination to receive output' , async ( ) => {
658+ const extraDest = new MockWritable ( ) ;
659+
660+ executionManager = createExecutionManager (
661+ executionManagerOptions ,
662+ new MockOperationRunner ( 'to-extra' , async ( terminal : CollatedTerminal ) => {
663+ terminal . writeStdoutLine ( 'Message for extra destination' ) ;
664+ return OperationStatus . Success ;
665+ } )
666+ ) ;
667+
668+ // Add destination before executing
669+ executionManager . addTerminalDestination ( extraDest ) ;
670+
671+ const result : IExecutionResult = await executionManager . executeAsync ( executionPassOptions ) ;
672+ expect ( result . status ) . toBe ( OperationStatus . Success ) ;
673+
674+ const allOutput : string = extraDest . getAllOutput ( ) ;
675+ expect ( allOutput ) . toContain ( 'Message for extra destination' ) ;
676+ } ) ;
677+
678+ it ( 'removeTerminalDestination closes destination by default and stops further output' , async ( ) => {
679+ const extraDest = new MockWritable ( ) ;
680+
681+ executionManager = createExecutionManager (
682+ executionManagerOptions ,
683+ new MockOperationRunner ( 'to-extra' , async ( terminal : CollatedTerminal ) => {
684+ terminal . writeStdoutLine ( 'Run message' ) ;
685+ return OperationStatus . Success ;
686+ } )
687+ ) ;
688+
689+ executionManager . addTerminalDestination ( extraDest ) ;
690+
691+ // First run: destination should receive output
692+ const first = await executionManager . executeAsync ( executionPassOptions ) ;
693+ expect ( first . status ) . toBe ( OperationStatus . Success ) ;
694+ expect ( extraDest . getAllOutput ( ) ) . toContain ( 'Run message' ) ;
695+
696+ // Now remove destination (default close = true) and ensure it was removed/closed
697+ const removed = executionManager . removeTerminalDestination ( extraDest ) ;
698+ expect ( removed ) . toBe ( true ) ;
699+ // TerminalWritable exposes isOpen
700+ expect ( extraDest . isOpen ) . toBe ( false ) ;
701+
702+ // Second run: should not write to closed destination
703+ const beforeSecond = extraDest . getAllOutput ( ) ;
704+ const second = await executionManager . executeAsync ( executionPassOptions ) ;
705+ expect ( second . status ) . toBe ( OperationStatus . Success ) ;
706+ const afterSecond = extraDest . getAllOutput ( ) ;
707+ expect ( afterSecond ) . toBe ( beforeSecond ) ;
708+ } ) ;
709+
710+ it ( 'removeTerminalDestination with close=false does not close destination but still stops further output' , async ( ) => {
711+ const extraDest = new MockWritable ( ) ;
712+
713+ executionManager = createExecutionManager (
714+ executionManagerOptions ,
715+ new MockOperationRunner ( 'to-extra' , async ( terminal : CollatedTerminal ) => {
716+ terminal . writeStdoutLine ( 'Run message 2' ) ;
717+ return OperationStatus . Success ;
718+ } )
719+ ) ;
720+
721+ executionManager . addTerminalDestination ( extraDest ) ;
722+
723+ // First run: destination should receive output
724+ const first = await executionManager . executeAsync ( executionPassOptions ) ;
725+ expect ( first . status ) . toBe ( OperationStatus . Success ) ;
726+ expect ( extraDest . getAllOutput ( ) ) . toContain ( 'Run message 2' ) ;
727+
728+ // Remove without closing
729+ const removed = executionManager . removeTerminalDestination ( extraDest , false ) ;
730+ expect ( removed ) . toBe ( true ) ;
731+ // Destination should remain open
732+ expect ( extraDest . isOpen ) . toBe ( true ) ;
733+
734+ // Second run: destination should not receive additional output
735+ const beforeSecond = extraDest . getAllOutput ( ) ;
736+ const second = await executionManager . executeAsync ( executionPassOptions ) ;
737+ expect ( second . status ) . toBe ( OperationStatus . Success ) ;
738+ const afterSecond = extraDest . getAllOutput ( ) ;
739+ expect ( afterSecond ) . toBe ( beforeSecond ) ;
740+ } ) ;
741+
742+ it ( 'removeTerminalDestination returns false when destination not found' , ( ) => {
743+ const unknown = new MockWritable ( ) ;
744+ const manager = createExecutionManager ( executionManagerOptions , new MockOperationRunner ( 'noop' ) ) ;
745+ const removed = manager . removeTerminalDestination ( unknown ) ;
746+ expect ( removed ) . toBe ( false ) ;
747+ } ) ;
748+ } ) ;
644749} ) ;
645750
646751describe ( 'invalidateOperations' , ( ) => {
@@ -649,7 +754,7 @@ describe('invalidateOperations', () => {
649754 quietMode : false ,
650755 debugMode : false ,
651756 parallelism : 1 ,
652- destination : mockWritable ,
757+ destinations : [ mockWritable ] ,
653758 abortController : new AbortController ( )
654759 } ;
655760
@@ -691,7 +796,7 @@ describe('invalidateOperations', () => {
691796 quietMode : false ,
692797 debugMode : false ,
693798 parallelism : 1 ,
694- destination : mockWritable ,
799+ destinations : [ mockWritable ] ,
695800 abortController : new AbortController ( )
696801 } ;
697802
@@ -741,7 +846,7 @@ describe('closeRunnersAsync', () => {
741846 quietMode : false ,
742847 debugMode : false ,
743848 parallelism : 1 ,
744- destination : mockWritable ,
849+ destinations : [ mockWritable ] ,
745850 abortController : new AbortController ( )
746851 } ;
747852
@@ -768,7 +873,7 @@ describe('closeRunnersAsync', () => {
768873 quietMode : false ,
769874 debugMode : false ,
770875 parallelism : 1 ,
771- destination : mockWritable ,
876+ destinations : [ mockWritable ] ,
772877 abortController : new AbortController ( )
773878 } ;
774879
@@ -809,7 +914,7 @@ describe('Manager state change notifications', () => {
809914 debugMode : false ,
810915 parallelism : 2 ,
811916 maxParallelism : 4 ,
812- destination : mockWritable ,
917+ destinations : [ mockWritable ] ,
813918 abortController : new AbortController ( )
814919 } ;
815920 const manager : OperationExecutionManager = new OperationExecutionManager ( new Set ( ) , {
@@ -939,7 +1044,7 @@ describe('setEnabledStates', () => {
9391044 quietMode : false ,
9401045 debugMode : false ,
9411046 parallelism : 1 ,
942- destination : mockWritable ,
1047+ destinations : [ mockWritable ] ,
9431048 abortController : new AbortController ( )
9441049 } ) ;
9451050 }
0 commit comments