@@ -13,6 +13,7 @@ const vscodeApi = require('vscode') as typeof import('vscode');
1313import { PYTEST_PROVIDER , UNITTEST_PROVIDER } from '../../../client/testing/common/constants' ;
1414import * as envExtApiInternal from '../../../client/envExt/api.internal' ;
1515import { getProjectId } from '../../../client/testing/testController/common/projectUtils' ;
16+ import * as projectUtils from '../../../client/testing/testController/common/projectUtils' ;
1617
1718function createStubTestController ( ) : TestController {
1819 const disposable = { dispose : ( ) => undefined } ;
@@ -62,6 +63,8 @@ ensureVscodeTestsNamespace();
6263// Dynamically require AFTER the vscode.tests namespace exists.
6364// eslint-disable-next-line @typescript-eslint/no-var-requires
6465const { PythonTestController } = require ( '../../../client/testing/testController/controller' ) ;
66+ // eslint-disable-next-line @typescript-eslint/no-var-requires
67+ const { TestProjectRegistry } = require ( '../../../client/testing/testController/common/testProjectRegistry' ) ;
6568
6669suite ( 'PythonTestController' , ( ) => {
6770 let sandbox : sinon . SinonSandbox ;
@@ -143,7 +146,7 @@ suite('PythonTestController', () => {
143146 } ) ;
144147 } ) ;
145148
146- suite ( 'createDefaultProject' , ( ) => {
149+ suite ( 'createDefaultProject (via TestProjectRegistry) ' , ( ) => {
147150 test ( 'creates a single default project using active interpreter' , async ( ) => {
148151 const workspaceUri : Uri = vscodeApi . Uri . file ( '/workspace/myws' ) ;
149152 const interpreter = {
@@ -153,16 +156,40 @@ suite('PythonTestController', () => {
153156 sysPrefix : '/opt/py' ,
154157 } ;
155158
156- const controller = createController ( { unittestEnabled : false , interpreter } ) ;
157-
158159 const fakeDiscoveryAdapter = { kind : 'discovery' } ;
159160 const fakeExecutionAdapter = { kind : 'execution' } ;
160- sandbox
161- . stub ( controller as any , 'createTestAdapters' )
162- . returns ( { discoveryAdapter : fakeDiscoveryAdapter , executionAdapter : fakeExecutionAdapter } ) ;
161+ sandbox . stub ( projectUtils , 'createTestAdapters' ) . returns ( {
162+ discoveryAdapter : fakeDiscoveryAdapter ,
163+ executionAdapter : fakeExecutionAdapter ,
164+ } as any ) ;
165+
166+ // Stub useEnvExtension to return false so createDefaultProject is called
167+ sandbox . stub ( envExtApiInternal , 'useEnvExtension' ) . returns ( false ) ;
168+
169+ const interpreterService = {
170+ getActiveInterpreter : sandbox . stub ( ) . resolves ( interpreter ) ,
171+ } as any ;
172+
173+ const configSettings = {
174+ getSettings : sandbox . stub ( ) . returns ( {
175+ testing : { unittestEnabled : false } ,
176+ } ) ,
177+ } as any ;
178+
179+ const testController = createStubTestController ( ) ;
180+ const envVarsService = { } as any ;
181+
182+ const registry = new TestProjectRegistry (
183+ testController ,
184+ configSettings ,
185+ interpreterService ,
186+ envVarsService ,
187+ ) ;
163188
164- const project = await ( controller as any ) . createDefaultProject ( workspaceUri ) ;
189+ const projects = await registry . discoverAndRegisterProjects ( workspaceUri ) ;
190+ const project = projects [ 0 ] ;
165191
192+ assert . strictEqual ( projects . length , 1 ) ;
166193 assert . strictEqual ( project . workspaceUri . toString ( ) , workspaceUri . toString ( ) ) ;
167194 assert . strictEqual ( project . projectUri . toString ( ) , workspaceUri . toString ( ) ) ;
168195 assert . strictEqual ( project . projectId , getProjectId ( workspaceUri ) ) ;
@@ -181,29 +208,54 @@ suite('PythonTestController', () => {
181208 } ) ;
182209 } ) ;
183210
184- suite ( 'discoverWorkspaceProjects' , ( ) => {
211+ suite ( 'discoverWorkspaceProjects (via TestProjectRegistry) ' , ( ) => {
185212 test ( 'respects useEnvExtension() == false and falls back to single default project' , async ( ) => {
186- const controller = createController ( ) ;
187213 const workspaceUri : Uri = vscodeApi . Uri . file ( '/workspace/a' ) ;
188214
189- const defaultProject = { projectId : 'default' , projectUri : workspaceUri } ;
190- const createDefaultProjectStub = sandbox
191- . stub ( controller as any , 'createDefaultProject' )
192- . resolves ( defaultProject as any ) ;
193-
194215 const useEnvExtensionStub = sandbox . stub ( envExtApiInternal , 'useEnvExtension' ) . returns ( false ) ;
195216 const getEnvExtApiStub = sandbox . stub ( envExtApiInternal , 'getEnvExtApi' ) ;
196217
197- const projects = await ( controller as any ) . discoverWorkspaceProjects ( workspaceUri ) ;
218+ const fakeDiscoveryAdapter = { kind : 'discovery' } ;
219+ const fakeExecutionAdapter = { kind : 'execution' } ;
220+ sandbox . stub ( projectUtils , 'createTestAdapters' ) . returns ( {
221+ discoveryAdapter : fakeDiscoveryAdapter ,
222+ executionAdapter : fakeExecutionAdapter ,
223+ } as any ) ;
224+
225+ const interpreterService = {
226+ getActiveInterpreter : sandbox . stub ( ) . resolves ( {
227+ displayName : 'Python 3.11' ,
228+ path : '/usr/bin/python3' ,
229+ version : { raw : '3.11.8' } ,
230+ sysPrefix : '/usr' ,
231+ } ) ,
232+ } as any ;
233+
234+ const configSettings = {
235+ getSettings : sandbox . stub ( ) . returns ( {
236+ testing : { unittestEnabled : false } ,
237+ } ) ,
238+ } as any ;
239+
240+ const testController = createStubTestController ( ) ;
241+ const envVarsService = { } as any ;
242+
243+ const registry = new TestProjectRegistry (
244+ testController ,
245+ configSettings ,
246+ interpreterService ,
247+ envVarsService ,
248+ ) ;
249+
250+ const projects = await registry . discoverAndRegisterProjects ( workspaceUri ) ;
198251
199252 assert . strictEqual ( useEnvExtensionStub . called , true ) ;
200253 assert . strictEqual ( getEnvExtApiStub . notCalled , true ) ;
201- assert . strictEqual ( createDefaultProjectStub . calledOnceWithExactly ( workspaceUri ) , true ) ;
202- assert . deepStrictEqual ( projects , [ defaultProject ] ) ;
254+ assert . strictEqual ( projects . length , 1 ) ;
255+ assert . strictEqual ( projects [ 0 ] . projectUri . toString ( ) , workspaceUri . toString ( ) ) ;
203256 } ) ;
204257
205258 test ( 'filters Python projects to workspace and creates adapters for each' , async ( ) => {
206- const controller = createController ( ) ;
207259 const workspaceUri : Uri = vscodeApi . Uri . file ( '/workspace/root' ) ;
208260
209261 const pythonProjects = [
@@ -215,60 +267,103 @@ suite('PythonTestController', () => {
215267 sandbox . stub ( envExtApiInternal , 'useEnvExtension' ) . returns ( true ) ;
216268 sandbox . stub ( envExtApiInternal , 'getEnvExtApi' ) . resolves ( {
217269 getPythonProjects : ( ) => pythonProjects ,
270+ getEnvironment : sandbox . stub ( ) . resolves ( {
271+ name : 'env' ,
272+ displayName : 'Python 3.11' ,
273+ shortDisplayName : 'Python 3.11' ,
274+ displayPath : '/usr/bin/python3' ,
275+ version : '3.11.8' ,
276+ environmentPath : vscodeApi . Uri . file ( '/usr/bin/python3' ) ,
277+ sysPrefix : '/usr' ,
278+ execInfo : { run : { executable : '/usr/bin/python3' } } ,
279+ envId : { id : 'test' , managerId : 'test' } ,
280+ } ) ,
218281 } as any ) ;
219282
220- const createdAdapters = [
221- { projectId : 'p1' , projectUri : pythonProjects [ 0 ] . uri } ,
222- { projectId : 'p2' , projectUri : pythonProjects [ 1 ] . uri } ,
223- ] ;
283+ const fakeDiscoveryAdapter = { kind : 'discovery' } ;
284+ const fakeExecutionAdapter = { kind : 'execution' } ;
285+ sandbox . stub ( projectUtils , 'createTestAdapters' ) . returns ( {
286+ discoveryAdapter : fakeDiscoveryAdapter ,
287+ executionAdapter : fakeExecutionAdapter ,
288+ } as any ) ;
224289
225- const createProjectAdapterStub = sandbox
226- . stub ( controller as any , 'createProjectAdapter' )
227- . onFirstCall ( )
228- . resolves ( createdAdapters [ 0 ] as any )
229- . onSecondCall ( )
230- . resolves ( createdAdapters [ 1 ] as any ) ;
290+ const interpreterService = {
291+ getActiveInterpreter : sandbox . stub ( ) . resolves ( null ) ,
292+ } as any ;
231293
232- const createDefaultProjectStub = sandbox . stub ( controller as any , 'createDefaultProject' ) ;
294+ const configSettings = {
295+ getSettings : sandbox . stub ( ) . returns ( {
296+ testing : { unittestEnabled : false } ,
297+ } ) ,
298+ } as any ;
233299
234- const projects = await ( controller as any ) . discoverWorkspaceProjects ( workspaceUri ) ;
300+ const testController = createStubTestController ( ) ;
301+ const envVarsService = { } as any ;
235302
236- // Should only create adapters for the 2 projects in the workspace.
237- assert . strictEqual ( createProjectAdapterStub . callCount , 2 ) ;
238- assert . strictEqual (
239- createProjectAdapterStub . firstCall . args [ 0 ] . uri . toString ( ) ,
240- pythonProjects [ 0 ] . uri . toString ( ) ,
241- ) ;
242- assert . strictEqual (
243- createProjectAdapterStub . secondCall . args [ 0 ] . uri . toString ( ) ,
244- pythonProjects [ 1 ] . uri . toString ( ) ,
303+ const registry = new TestProjectRegistry (
304+ testController ,
305+ configSettings ,
306+ interpreterService ,
307+ envVarsService ,
245308 ) ;
246309
247- assert . strictEqual ( createDefaultProjectStub . notCalled , true ) ;
248- assert . deepStrictEqual ( projects , createdAdapters ) ;
310+ const projects = await registry . discoverAndRegisterProjects ( workspaceUri ) ;
311+
312+ // Should only create adapters for the 2 projects in the workspace (not 'other')
313+ assert . strictEqual ( projects . length , 2 ) ;
314+ const projectUris = projects . map ( ( p ) => p . projectUri . fsPath ) ;
315+ assert . ok ( projectUris . includes ( '/workspace/root/p1' ) ) ;
316+ assert . ok ( projectUris . includes ( '/workspace/root/nested/p2' ) ) ;
317+ assert . ok ( ! projectUris . includes ( '/other/root/p3' ) ) ;
249318 } ) ;
250319
251320 test ( 'falls back to default project when no projects are in the workspace' , async ( ) => {
252- const controller = createController ( ) ;
253321 const workspaceUri : Uri = vscodeApi . Uri . file ( '/workspace/root' ) ;
254322
255323 sandbox . stub ( envExtApiInternal , 'useEnvExtension' ) . returns ( true ) ;
256324 sandbox . stub ( envExtApiInternal , 'getEnvExtApi' ) . resolves ( {
257325 getPythonProjects : ( ) => [ { name : 'other' , uri : vscodeApi . Uri . file ( '/other/root/p3' ) } ] ,
258326 } as any ) ;
259327
260- const defaultProject = { projectId : 'default' , projectUri : workspaceUri } ;
261- const createDefaultProjectStub = sandbox
262- . stub ( controller as any , 'createDefaultProject' )
263- . resolves ( defaultProject as any ) ;
328+ const fakeDiscoveryAdapter = { kind : 'discovery' } ;
329+ const fakeExecutionAdapter = { kind : 'execution' } ;
330+ sandbox . stub ( projectUtils , 'createTestAdapters' ) . returns ( {
331+ discoveryAdapter : fakeDiscoveryAdapter ,
332+ executionAdapter : fakeExecutionAdapter ,
333+ } as any ) ;
334+
335+ const interpreter = {
336+ displayName : 'Python 3.11' ,
337+ path : '/usr/bin/python3' ,
338+ version : { raw : '3.11.8' } ,
339+ sysPrefix : '/usr' ,
340+ } ;
341+
342+ const interpreterService = {
343+ getActiveInterpreter : sandbox . stub ( ) . resolves ( interpreter ) ,
344+ } as any ;
345+
346+ const configSettings = {
347+ getSettings : sandbox . stub ( ) . returns ( {
348+ testing : { unittestEnabled : false } ,
349+ } ) ,
350+ } as any ;
264351
265- const createProjectAdapterStub = sandbox . stub ( controller as any , 'createProjectAdapter' ) ;
352+ const testController = createStubTestController ( ) ;
353+ const envVarsService = { } as any ;
354+
355+ const registry = new TestProjectRegistry (
356+ testController ,
357+ configSettings ,
358+ interpreterService ,
359+ envVarsService ,
360+ ) ;
266361
267- const projects = await ( controller as any ) . discoverWorkspaceProjects ( workspaceUri ) ;
362+ const projects = await registry . discoverAndRegisterProjects ( workspaceUri ) ;
268363
269- assert . strictEqual ( createProjectAdapterStub . notCalled , true ) ;
270- assert . strictEqual ( createDefaultProjectStub . calledOnceWithExactly ( workspaceUri ) , true ) ;
271- assert . deepStrictEqual ( projects , [ defaultProject ] ) ;
364+ // Should fall back to default project since no projects are in the workspace
365+ assert . strictEqual ( projects . length , 1 ) ;
366+ assert . strictEqual ( projects [ 0 ] . projectUri . toString ( ) , workspaceUri . toString ( ) ) ;
272367 } ) ;
273368 } ) ;
274369} ) ;
0 commit comments