@@ -299,6 +299,145 @@ suite('PythonEnvironmentManagers getLastKnownEnvironment', () => {
299299 assert . deepStrictEqual ( events . map ( ( event ) => event . new ) , [ second ] ) ;
300300 } ) ;
301301
302+ test ( 'does not let an older non-inline selection install an override after a newer inline selection' , async ( ) => {
303+ const script = Uri . file ( '/workspace/project/script.py' ) ;
304+ projectsByUri . set ( script . toString ( ) , { name : 'project' , uri : Uri . file ( '/workspace/project' ) } ) ;
305+ let releaseOlderSelection : ( ( ) => void ) | undefined ;
306+ let signalOlderSelection : ( ( ) => void ) | undefined ;
307+ const olderSelectionStarted = new Promise < void > ( ( resolve ) => {
308+ signalOlderSelection = resolve ;
309+ } ) ;
310+ const olderSelectionGate = new Promise < void > ( ( resolve ) => {
311+ releaseOlderSelection = resolve ;
312+ } ) ;
313+ const selectedSet = sinon . stub ( ) . callsFake ( async ( ) => {
314+ signalOlderSelection ! ( ) ;
315+ await olderSelectionGate ;
316+ } ) ;
317+ let selectedEnvironment : PythonEnvironment ;
318+ const selectedId = registerManager ( async ( ) => selectedEnvironment , selectedSet , 'venv' ) ;
319+ let inlineEnvironment : PythonEnvironment ;
320+ const inlineId = registerManager ( async ( ) => inlineEnvironment , async ( ) => undefined , 'inline-script' ) ;
321+ selectedEnvironment = {
322+ ...makeEnv ( 'selected' ) ,
323+ envId : { id : 'selected' , managerId : selectedId } ,
324+ } ;
325+ inlineEnvironment = {
326+ ...makeEnv ( 'inline' ) ,
327+ envId : { id : 'inline' , managerId : inlineId } ,
328+ } ;
329+ defaultManagerId = selectedId ;
330+ markInlineScript ( script ) ;
331+ const events : DidChangeEnvironmentEventArgs [ ] = [ ] ;
332+ envManagers . onDidChangeActiveEnvironment ( ( event ) => events . push ( event ) ) ;
333+
334+ const olderSelection = envManagers . setEnvironment ( script , selectedEnvironment , false ) ;
335+ await olderSelectionStarted ;
336+ await envManagers . setEnvironment ( script , inlineEnvironment , false ) ;
337+ releaseOlderSelection ! ( ) ;
338+ await olderSelection ;
339+
340+ // The stale non-inline selection must not hijack the script's PEP 723 routing…
341+ assert . strictEqual ( envManagers . getEnvironmentManager ( script ) ?. id , inlineId ) ;
342+ assert . strictEqual ( envManagers . getLastKnownEnvironment ( script ) , inlineEnvironment ) ;
343+ assert . ok ( events . some ( ( event ) => event . new === inlineEnvironment ) ) ;
344+ // …but its ordinary containing-project selection lane is independent and is not suppressed.
345+ assert . ok ( events . some ( ( event ) => event . new === selectedEnvironment ) ) ;
346+ } ) ;
347+
348+ test ( 'does not let an older batch inline selection clear a newer non-inline override' , async ( ) => {
349+ const script = Uri . file ( '/workspace/project/script.py' ) ;
350+ projectsByUri . set ( script . toString ( ) , { name : 'project' , uri : Uri . file ( '/workspace/project' ) } ) ;
351+ let releaseOlderBatch : ( ( ) => void ) | undefined ;
352+ let signalOlderBatch : ( ( ) => void ) | undefined ;
353+ const olderBatchStarted = new Promise < void > ( ( resolve ) => {
354+ signalOlderBatch = resolve ;
355+ } ) ;
356+ const olderBatchGate = new Promise < void > ( ( resolve ) => {
357+ releaseOlderBatch = resolve ;
358+ } ) ;
359+ let selectedEnvironment : PythonEnvironment ;
360+ const selectedId = registerManager ( async ( ) => selectedEnvironment , async ( ) => undefined , 'venv' ) ;
361+ const inlineSet = sinon . stub ( ) . callsFake ( async ( ) => {
362+ signalOlderBatch ! ( ) ;
363+ await olderBatchGate ;
364+ } ) ;
365+ let inlineEnvironment : PythonEnvironment ;
366+ const inlineId = registerManager ( async ( ) => inlineEnvironment , inlineSet , 'inline-script' ) ;
367+ selectedEnvironment = {
368+ ...makeEnv ( 'selected' ) ,
369+ envId : { id : 'selected' , managerId : selectedId } ,
370+ } ;
371+ inlineEnvironment = {
372+ ...makeEnv ( 'inline' ) ,
373+ envId : { id : 'inline' , managerId : inlineId } ,
374+ } ;
375+ defaultManagerId = selectedId ;
376+ markInlineScript ( script ) ;
377+ const events : DidChangeEnvironmentEventArgs [ ] = [ ] ;
378+ envManagers . onDidChangeActiveEnvironment ( ( event ) => events . push ( event ) ) ;
379+
380+ const olderBatch = envManagers . setEnvironments ( [ script ] , inlineEnvironment , false ) ;
381+ await olderBatchStarted ;
382+ await envManagers . setEnvironment ( script , selectedEnvironment , false ) ;
383+ const eventsAfterNewerSelection = [ ...events ] ;
384+ releaseOlderBatch ! ( ) ;
385+ await olderBatch ;
386+
387+ assert . strictEqual ( envManagers . getEnvironmentManager ( script ) ?. id , selectedId ) ;
388+ assert . strictEqual ( envManagers . getLastKnownEnvironment ( script ) , selectedEnvironment ) ;
389+ assert . deepStrictEqual ( events , eventsAfterNewerSelection ) ;
390+ } ) ;
391+
392+ test ( 'routes each same-project script to its own override in a batch non-inline selection' , async ( ) => {
393+ const project = { name : 'project' , uri : Uri . file ( '/workspace/project' ) } ;
394+ const firstScript = Uri . file ( '/workspace/project/first.py' ) ;
395+ const secondScript = Uri . file ( '/workspace/project/second.py' ) ;
396+ projectsByUri . set ( firstScript . toString ( ) , project ) ;
397+ projectsByUri . set ( secondScript . toString ( ) , project ) ;
398+ let selectedEnvironment : PythonEnvironment ;
399+ const selectedId = registerManager ( async ( ) => selectedEnvironment , async ( ) => undefined , 'venv' ) ;
400+ registerManager ( async ( ) => undefined , async ( ) => undefined , 'inline-script' ) ;
401+ selectedEnvironment = { ...makeEnv ( 'selected' ) , envId : { id : 'selected' , managerId : selectedId } } ;
402+ markInlineScript ( firstScript ) ;
403+ markInlineScript ( secondScript ) ;
404+
405+ await envManagers . setEnvironments ( [ firstScript , secondScript ] , selectedEnvironment , false ) ;
406+
407+ // Each script commits on its own per-file inline key, so the shared containing-project
408+ // revision cannot make the first script skip installing its routing override.
409+ assert . strictEqual ( envManagers . getEnvironmentManager ( firstScript ) ?. id , selectedId ) ;
410+ assert . strictEqual ( envManagers . getEnvironmentManager ( secondScript ) ?. id , selectedId ) ;
411+ } ) ;
412+
413+ test ( 'applies a normal non-inline batch across distinct projects without a routing registry' , async ( ) => {
414+ recreateEnvManagersWithoutRouting ( ) ;
415+ const projectOne = { name : 'one' , uri : Uri . file ( '/workspace/one' ) } ;
416+ const projectTwo = { name : 'two' , uri : Uri . file ( '/workspace/two' ) } ;
417+ projectsByUri . set ( projectOne . uri . toString ( ) , projectOne ) ;
418+ projectsByUri . set ( projectTwo . uri . toString ( ) , projectTwo ) ;
419+ const managerSet = sinon . stub ( ) . resolves ( ) ;
420+ let selectedEnvironment : PythonEnvironment ;
421+ const managerId = registerManager ( async ( ) => selectedEnvironment , managerSet , 'venv' ) ;
422+ selectedEnvironment = { ...makeEnv ( 'selected' ) , envId : { id : 'selected' , managerId } } ;
423+ const settings = sinon . stub ( settingHelpers , 'setAllManagerSettings' ) . resolves ( ) ;
424+ const events : DidChangeEnvironmentEventArgs [ ] = [ ] ;
425+ envManagers . onDidChangeActiveEnvironment ( ( event ) => events . push ( event ) ) ;
426+
427+ await envManagers . setEnvironments ( [ projectOne . uri , projectTwo . uri ] , selectedEnvironment ) ;
428+
429+ assert . strictEqual ( managerSet . callCount , 1 ) ;
430+ assert . deepStrictEqual ( managerSet . firstCall . args [ 0 ] , [ projectOne . uri , projectTwo . uri ] ) ;
431+ assert . strictEqual ( envManagers . getLastKnownEnvironment ( projectOne . uri ) , selectedEnvironment ) ;
432+ assert . strictEqual ( envManagers . getLastKnownEnvironment ( projectTwo . uri ) , selectedEnvironment ) ;
433+ assert . deepStrictEqual (
434+ events . map ( ( event ) => event . new ) ,
435+ [ selectedEnvironment , selectedEnvironment ] ,
436+ ) ;
437+ assert . strictEqual ( settings . callCount , 1 ) ;
438+ assert . strictEqual ( settings . firstCall . args [ 0 ] . length , 2 ) ;
439+ } ) ;
440+
302441 test ( 'publishes inline environments with the same ID at different paths' , async ( ) => {
303442 const scope = Uri . file ( '/workspace/script.py' ) ;
304443 const managerId = registerManager ( async ( ) => undefined , async ( ) => undefined , 'inline-script' ) ;
0 commit comments