@@ -106,6 +106,62 @@ test('cancellation while queued prevents a later dispatch', async () => {
106106 await manager . close ( ) ;
107107} ) ;
108108
109+ test ( 'cancelling a middle waiter does not release the following request early' , async ( ) => {
110+ const fixture = createLifecycleFixture ( { responseDelayMs : 120 } ) ;
111+ const manager = new SnapshotBridgeManager ( fixture . host ) ;
112+ const first = manager . request ( {
113+ target,
114+ bridge,
115+ limits,
116+ maxDepth : 10 ,
117+ deadline : deadline ( undefined , 1000 ) ,
118+ } ) ;
119+ await waitForDispatch ( fixture ) ;
120+
121+ const controller = new AbortController ( ) ;
122+ const middle = manager . request ( {
123+ target,
124+ bridge,
125+ limits,
126+ maxDepth : 10 ,
127+ deadline : deadline ( controller . signal , 1000 ) ,
128+ } ) ;
129+ const last = manager . request ( {
130+ target,
131+ bridge,
132+ limits,
133+ maxDepth : 10 ,
134+ deadline : deadline ( undefined , 1000 ) ,
135+ } ) ;
136+ controller . abort ( ) ;
137+
138+ await assert . rejects (
139+ middle ,
140+ ( error : unknown ) => error instanceof SnapshotSourceError && error . failureKind === 'cancelled' ,
141+ ) ;
142+ assert . equal ( fixture . sockets [ 0 ] ?. writes , 1 ) ;
143+ await first ;
144+ await last ;
145+ assert . equal ( fixture . sockets [ 0 ] ?. writes , 2 ) ;
146+ await manager . close ( ) ;
147+ } ) ;
148+
149+ test ( 'independent managers own distinct sockets for the same Simulator' , async ( ) => {
150+ const fixture = createLifecycleFixture ( ) ;
151+ const first = new SnapshotBridgeManager ( fixture . host ) ;
152+ const second = new SnapshotBridgeManager ( fixture . host ) ;
153+
154+ await first . request ( { target, bridge, limits, maxDepth : 10 , deadline : deadline ( ) } ) ;
155+ await second . request ( { target, bridge, limits, maxDepth : 10 , deadline : deadline ( ) } ) ;
156+
157+ assert . equal ( fixture . socketPaths . length , 2 ) ;
158+ assert . notEqual ( fixture . socketPaths [ 0 ] , fixture . socketPaths [ 1 ] ) ;
159+ await first . close ( ) ;
160+ assert . equal ( fixture . processes [ 1 ] ?. isAlive ( ) , true ) ;
161+ await second . request ( { target, bridge, limits, maxDepth : 10 , deadline : deadline ( ) } ) ;
162+ await second . close ( ) ;
163+ } ) ;
164+
109165test ( 'request cancellation after dispatch reaps the exact helper before recovery' , async ( ) => {
110166 const fixture = createLifecycleFixture ( { responseDelayMs : 80 } ) ;
111167 const manager = new SnapshotBridgeManager ( fixture . host ) ;
@@ -258,6 +314,20 @@ test('the manager rejects a response carrying a previous target generation as st
258314 await manager . close ( ) ;
259315} ) ;
260316
317+ test ( 'the manager rejects a tree when the target process changes during acquisition' , async ( ) => {
318+ const fixture = createLifecycleFixture ( { targetStartTimes : [ 'start-1' , 'start-2' ] } ) ;
319+ const manager = new SnapshotBridgeManager ( fixture . host ) ;
320+
321+ await assert . rejects (
322+ manager . request ( { target, bridge, limits, maxDepth : 10 , deadline : deadline ( ) } ) ,
323+ ( error : unknown ) =>
324+ error instanceof SnapshotSourceError &&
325+ error . failureKind === 'stale-target' &&
326+ error . failureCode === 'target-process-changed' ,
327+ ) ;
328+ await manager . close ( ) ;
329+ } ) ;
330+
261331test ( 'typed guest failures retain their kind after target validation' , async ( ) => {
262332 const fixture = createLifecycleFixture ( { responseErrorKind : 'application_not_responding' } ) ;
263333 const manager = new SnapshotBridgeManager ( fixture . host ) ;
@@ -274,6 +344,7 @@ type LifecycleFixture = {
274344 processes : FakeProcess [ ] ;
275345 sockets : FakeSocket [ ] ;
276346 diagnostics : Array < Parameters < SnapshotSourceHost [ 'emitDiagnostic' ] > [ 0 ] > ;
347+ socketPaths : string [ ] ;
277348} ;
278349
279350function deadline ( signal ?: AbortSignal , timeoutMs = limits . maxDurationMs ) {
@@ -295,16 +366,20 @@ function createLifecycleFixture(
295366 responsePid ?: number ;
296367 responseGeneration ?: string ;
297368 responseErrorKind ?: string ;
369+ targetStartTimes ?: Array < string | null > ;
298370 } = { } ,
299371) : LifecycleFixture {
300372 const processes : FakeProcess [ ] = [ ] ;
301373 const sockets : FakeSocket [ ] = [ ] ;
302374 const diagnostics : LifecycleFixture [ 'diagnostics' ] = [ ] ;
375+ const socketPaths : string [ ] = [ ] ;
303376 const realHost = createSnapshotSourceHost ( ) ;
304377 const host : SnapshotSourceHost = {
305378 ...realHost ,
306379 emitDiagnostic : ( event ) => diagnostics . push ( event ) ,
307- start : ( ) => {
380+ readTargetProcessStartTime : async ( ) => options . targetStartTimes ?. shift ( ) ?? 'target-start' ,
381+ start : ( _udid , _bridgePath , socketPath ) => {
382+ socketPaths . push ( socketPath ) ;
308383 const process = new FakeProcess ( 700 + processes . length ) ;
309384 processes . push ( process ) ;
310385 return process ;
@@ -336,7 +411,7 @@ function createLifecycleFixture(
336411 return socket ;
337412 } ,
338413 } ;
339- return { host, processes, sockets, diagnostics } ;
414+ return { host, processes, sockets, diagnostics, socketPaths } ;
340415}
341416
342417class FakeProcess implements SnapshotSourceProcess {
0 commit comments