@@ -438,6 +438,54 @@ suite('Project Test Execution', () => {
438438 const passedTestIds = project . executionAdapterStub . firstCall . args [ 1 ] as string [ ] ;
439439 expect ( passedTestIds ) . to . have . length ( 2 ) ;
440440 } ) ;
441+
442+ test ( 'should exclude test items in excludeSet from execution' , async ( ) => {
443+ // Mock - class containing two test methods, one excluded
444+ const project = createMockProjectAdapter ( { projectPath : '/workspace/proj' , projectName : 'proj' } ) ;
445+ const leaf1 = createMockTestItem ( 'test1' , '/workspace/proj/test.py' ) ;
446+ const leaf2 = createMockTestItem ( 'test2' , '/workspace/proj/test.py' ) ;
447+ const classItem = createMockTestItem ( 'TestClass' , '/workspace/proj/test.py' , [ leaf1 , leaf2 ] ) ;
448+ project . resultResolver . vsIdToRunId . set ( 'test1' , 'runId1' ) ;
449+ project . resultResolver . vsIdToRunId . set ( 'test2' , 'runId2' ) ;
450+ const runMock = createMockTestRun ( ) ;
451+ const request = { profile : { kind : TestRunProfileKind . Run } } as TestRunRequest ;
452+ const deps = createMockDependencies ( ) ;
453+
454+ // Exclude leaf2
455+ const excludeSet = new Set ( [ leaf2 ] ) ;
456+
457+ // Run
458+ await executeTestsForProject ( project , [ classItem ] , runMock . object , request , deps , excludeSet ) ;
459+
460+ // Assert - only leaf1 should be started and executed, leaf2 should be excluded
461+ runMock . verify ( ( r ) => r . started ( leaf1 ) , typemoq . Times . once ( ) ) ;
462+ runMock . verify ( ( r ) => r . started ( leaf2 ) , typemoq . Times . never ( ) ) ;
463+ const passedTestIds = project . executionAdapterStub . firstCall . args [ 1 ] as string [ ] ;
464+ expect ( passedTestIds ) . to . deep . equal ( [ 'runId1' ] ) ;
465+ } ) ;
466+
467+ test ( 'should exclude entire subtree when parent is in excludeSet' , async ( ) => {
468+ // Mock - file containing a class with test methods
469+ const project = createMockProjectAdapter ( { projectPath : '/workspace/proj' , projectName : 'proj' } ) ;
470+ const leaf1 = createMockTestItem ( 'test1' , '/workspace/proj/test.py' ) ;
471+ const leaf2 = createMockTestItem ( 'test2' , '/workspace/proj/test.py' ) ;
472+ const classItem = createMockTestItem ( 'TestClass' , '/workspace/proj/test.py' , [ leaf1 , leaf2 ] ) ;
473+ project . resultResolver . vsIdToRunId . set ( 'test1' , 'runId1' ) ;
474+ project . resultResolver . vsIdToRunId . set ( 'test2' , 'runId2' ) ;
475+ const runMock = createMockTestRun ( ) ;
476+ const request = { profile : { kind : TestRunProfileKind . Run } } as TestRunRequest ;
477+ const deps = createMockDependencies ( ) ;
478+
479+ // Exclude the entire class (expandExcludeSet would add children too)
480+ const excludeSet = new Set ( [ classItem , leaf1 , leaf2 ] ) ;
481+
482+ // Run
483+ await executeTestsForProject ( project , [ classItem ] , runMock . object , request , deps , excludeSet ) ;
484+
485+ // Assert - nothing should be started or executed
486+ runMock . verify ( ( r ) => r . started ( typemoq . It . isAny ( ) ) , typemoq . Times . never ( ) ) ;
487+ expect ( project . executionAdapterStub . called ) . to . be . false ;
488+ } ) ;
441489 } ) ;
442490
443491 // ===== executeTestsForProjects Tests =====
@@ -612,6 +660,63 @@ suite('Project Test Execution', () => {
612660 const telemetryProps = telemetryStub . firstCall . args [ 2 ] ;
613661 expect ( telemetryProps . debugging ) . to . be . true ;
614662 } ) ;
663+
664+ test ( 'should respect request.exclude when executing tests' , async ( ) => {
665+ // Mock - project with two test items, one excluded via request.exclude
666+ const project = createMockProjectAdapter ( { projectPath : '/workspace/proj' , projectName : 'proj' } ) ;
667+ const item1 = createMockTestItem ( 'test1' , '/workspace/proj/test.py' ) ;
668+ const item2 = createMockTestItem ( 'test2' , '/workspace/proj/test.py' ) ;
669+ project . resultResolver . vsIdToRunId . set ( 'test1' , 'runId1' ) ;
670+ project . resultResolver . vsIdToRunId . set ( 'test2' , 'runId2' ) ;
671+ const runMock = createMockTestRun ( ) ;
672+ const token = new CancellationTokenSource ( ) . token ;
673+ // Exclude item2 via request.exclude
674+ const request = ( {
675+ profile : { kind : TestRunProfileKind . Run } ,
676+ exclude : [ item2 ] ,
677+ } as unknown ) as TestRunRequest ;
678+ const deps = createMockDependencies ( ) ;
679+
680+ // Run
681+ await executeTestsForProjects ( [ project ] , [ item1 , item2 ] , runMock . object , request , token , deps ) ;
682+
683+ // Assert - only item1 should be executed, item2 should be excluded
684+ runMock . verify ( ( r ) => r . started ( item1 ) , typemoq . Times . once ( ) ) ;
685+ runMock . verify ( ( r ) => r . started ( item2 ) , typemoq . Times . never ( ) ) ;
686+ const passedTestIds = project . executionAdapterStub . firstCall . args [ 1 ] as string [ ] ;
687+ expect ( passedTestIds ) . to . deep . equal ( [ 'runId1' ] ) ;
688+ } ) ;
689+
690+ test ( 'should exclude items only from their own project in multi-project execution' , async ( ) => {
691+ // Mock - two projects, each with one test item, exclude one item from proj1
692+ const proj1 = createMockProjectAdapter ( { projectPath : '/workspace/proj1' , projectName : 'proj1' } ) ;
693+ const proj2 = createMockProjectAdapter ( { projectPath : '/workspace/proj2' , projectName : 'proj2' } ) ;
694+ const item1 = createMockTestItem ( 'test1' , '/workspace/proj1/test.py' ) ;
695+ const item2 = createMockTestItem ( 'test2' , '/workspace/proj2/test.py' ) ;
696+ proj1 . resultResolver . vsIdToRunId . set ( 'test1' , 'runId1' ) ;
697+ proj2 . resultResolver . vsIdToRunId . set ( 'test2' , 'runId2' ) ;
698+ const runMock = createMockTestRun ( ) ;
699+ const token = new CancellationTokenSource ( ) . token ;
700+ // Exclude item1 from proj1 - item2 in proj2 should still run
701+ const request = ( {
702+ profile : { kind : TestRunProfileKind . Run } ,
703+ exclude : [ item1 ] ,
704+ } as unknown ) as TestRunRequest ;
705+ const deps = createMockDependencies ( ) ;
706+
707+ // Run
708+ await executeTestsForProjects ( [ proj1 , proj2 ] , [ item1 , item2 ] , runMock . object , request , token , deps ) ;
709+
710+ // Assert - item1 excluded, item2 still executed
711+ runMock . verify ( ( r ) => r . started ( item1 ) , typemoq . Times . never ( ) ) ;
712+ runMock . verify ( ( r ) => r . started ( item2 ) , typemoq . Times . once ( ) ) ;
713+ // proj1 should not have called runTests (no items left after exclusion)
714+ expect ( proj1 . executionAdapterStub . called ) . to . be . false ;
715+ // proj2 should have called runTests with item2
716+ expect ( proj2 . executionAdapterStub . calledOnce ) . to . be . true ;
717+ const proj2TestIds = proj2 . executionAdapterStub . firstCall . args [ 1 ] as string [ ] ;
718+ expect ( proj2TestIds ) . to . deep . equal ( [ 'runId2' ] ) ;
719+ } ) ;
615720 } ) ;
616721
617722 // ===== setupCoverageForProjects Tests =====
0 commit comments