@@ -48,6 +48,7 @@ suite('PythonPackageManagerApi Tests', () => {
4848 let environment : typeMoq . IMock < PythonEnvironment > ;
4949 let packageManager : typeMoq . IMock < PackageManager > ;
5050 let onDidChangePackagesEmitter : EventEmitter < DidChangePackagesEventArgs > ;
51+ let projectChangesEmitter : EventEmitter < PythonProject [ ] | undefined > ;
5152 let getExtensionStub : sinon . SinonStub ;
5253
5354 setup ( ( ) => {
@@ -71,6 +72,9 @@ suite('PythonPackageManagerApi Tests', () => {
7172
7273 // Mock project manager
7374 projectManager = typeMoq . Mock . ofType < PythonProjectManager > ( ) ;
75+ projectChangesEmitter = new EventEmitter < PythonProject [ ] | undefined > ( ) ;
76+ projectManager . setup ( ( pm ) => pm . getProjects ( ) ) . returns ( ( ) => [ ] ) ;
77+ projectManager . setup ( ( pm ) => pm . onDidChangeProjects ) . returns ( ( ) => projectChangesEmitter . event ) ;
7478 setupNonThenable ( projectManager ) ;
7579
7680 // Create environment managers instance
@@ -96,6 +100,7 @@ suite('PythonPackageManagerApi Tests', () => {
96100 sinon . restore ( ) ;
97101 envManagers . dispose ( ) ;
98102 onDidChangePackagesEmitter . dispose ( ) ;
103+ projectChangesEmitter . dispose ( ) ;
99104 } ) ;
100105
101106 /**
@@ -806,5 +811,65 @@ suite('PythonPackageManagerApi Tests', () => {
806811
807812 assert . strictEqual ( envManagers . getPackageManager ( project . uri ) , registeredManager ) ;
808813 } ) ;
814+
815+ test ( 'Should evict scoped package managers when their project is removed' , async ( ) => {
816+ disposable . dispose ( ) ;
817+ const projectUri = Uri . file ( path . join ( process . cwd ( ) , 'removed-project' ) ) ;
818+ let currentProject = { name : 'original' , uri : projectUri } as PythonProject ;
819+ projectManager . setup ( ( pm ) => pm . get ( projectUri ) ) . returns ( ( ) => currentProject ) ;
820+ const scopedManagers : PackageManager [ ] = [ ] ;
821+ const scopedEmitters : EventEmitter < DidChangePackagesEventArgs > [ ] = [ ] ;
822+ const provider : PackageManager = {
823+ name : 'project-pkg-mgr' ,
824+ manage : async ( ) => undefined ,
825+ refresh : async ( ) => undefined ,
826+ getPackages : async ( ) => [ ] ,
827+ createForProject : ( ) => {
828+ const emitter = new EventEmitter < DidChangePackagesEventArgs > ( ) ;
829+ const scopedManager : PackageManager = {
830+ name : 'project-pkg-mgr' ,
831+ manage : async ( ) => undefined ,
832+ refresh : async ( ) => undefined ,
833+ getPackages : async ( ) => [ ] ,
834+ onDidChangePackages : emitter . event ,
835+ } ;
836+ scopedEmitters . push ( emitter ) ;
837+ scopedManagers . push ( scopedManager ) ;
838+ return scopedManager ;
839+ } ,
840+ } ;
841+ disposable = envManagers . registerPackageManager ( provider ) ;
842+ const registeredManager = envManagers . packageManagers [ 0 ] ;
843+ sinon . stub ( workspaceApis , 'getConfiguration' ) . returns ( {
844+ get : ( section : string , defaultValue ?: unknown ) =>
845+ section === 'defaultPackageManager' ? registeredManager . id : defaultValue ,
846+ } as WorkspaceConfiguration ) ;
847+ const events : unknown [ ] = [ ] ;
848+ const eventDisposable = envManagers . onDidChangePackages ( ( event ) => events . push ( event ) ) ;
849+
850+ const original = envManagers . getPackageManager ( projectUri ) ;
851+ currentProject = { name : 'replacement' , uri : projectUri } as PythonProject ;
852+ projectChangesEmitter . fire ( [ currentProject ] ) ;
853+ const replacement = envManagers . getPackageManager ( projectUri ) ;
854+
855+ assert . notStrictEqual ( original , replacement ) ;
856+ assert . strictEqual ( scopedManagers . length , 2 ) ;
857+ assert . strictEqual ( replacement ?. project , currentProject ) ;
858+
859+ const packageChange = {
860+ environment : environment . object ,
861+ changes : [ ] ,
862+ } ;
863+ scopedEmitters [ 0 ] . fire ( { ...packageChange , manager : scopedManagers [ 0 ] } ) ;
864+ await new Promise < void > ( ( resolve ) => setImmediate ( resolve ) ) ;
865+ assert . strictEqual ( events . length , 0 ) ;
866+
867+ scopedEmitters [ 1 ] . fire ( { ...packageChange , manager : scopedManagers [ 1 ] } ) ;
868+ await new Promise < void > ( ( resolve ) => setImmediate ( resolve ) ) ;
869+ assert . strictEqual ( events . length , 1 ) ;
870+
871+ eventDisposable . dispose ( ) ;
872+ scopedEmitters . forEach ( ( emitter ) => emitter . dispose ( ) ) ;
873+ } ) ;
809874 } ) ;
810875} ) ;
0 commit comments