Procházet zdrojové kódy

test(subprocess): cover retained control endpoint teardown

Tianyi Cui před 1 měsícem
rodič
revize
e53c526320

+ 60 - 0
packages/subprocess/subprocess-local/tests/local.spec.ts

@@ -870,6 +870,66 @@ describe('LocalSubprocessRuntime', () => {
     await fiber.dispose()
   })
 
+  it('waits for retained control endpoint closure after a process range completes', async () => {
+    const destroying = Promise.withResolvers<undefined>()
+    const finishClose = Promise.withResolvers<undefined>()
+    const control = new PassThrough({
+      destroy(_error, callback) {
+        destroying.resolve(undefined)
+        void finishClose.promise.then(() => { callback(null) })
+      },
+    })
+    const terminate = vi.fn()
+    const waitForExit = vi.fn(() => Promise.resolve(true))
+    const handle = {
+      control,
+      collected: {},
+      done: Promise.resolve({ exitCode: 0, signal: null }),
+      terminate,
+      terminateForHostExit: vi.fn(),
+      waitForExit,
+    }
+    vi.resetModules()
+    mockWin32ForIsolatedRuntime()
+    vi.doMock('../src/spawn.ts', async importOriginal => ({
+      ...await importOriginal<typeof import('../src/spawn.ts')>(),
+      spawnSubprocess: vi.fn(() => handle),
+    }))
+    let fiber: { dispose(): Promise<void> } | undefined
+    try {
+      const { default: IsolatedLocalSubprocessRuntime } = await import('../src/index.ts')
+      const ctx = new Context()
+      fiber = await ctx.plugin(IsolatedLocalSubprocessRuntime)
+      const runtime = ctx.subprocess as InstanceType<typeof IsolatedLocalSubprocessRuntime>
+      runtime.internals = { platform: 'darwin' }
+      const spawned = runtime.spawn(spec('true', {
+        stdio: { stdin: 'ignore', stdout: 'inherit', stderr: 'inherit', control: 'pipe' },
+      }))
+      await spawned.done
+      await new Promise(resolve => setImmediate(resolve))
+      expect(waitForExit).toHaveBeenCalledOnce()
+      expect(control.destroyed).toBe(false)
+
+      let disposed = false
+      const disposal = fiber.dispose().then(() => { disposed = true })
+      await destroying.promise
+      expect(control.destroyed).toBe(true)
+      expect(control.closed).toBe(false)
+      expect(disposed).toBe(false)
+      expect(terminate).not.toHaveBeenCalled()
+      finishClose.resolve(undefined)
+      await disposal
+      expect(control.closed).toBe(true)
+    } finally {
+      finishClose.resolve(undefined)
+      control.destroy()
+      await fiber?.dispose()
+      vi.doUnmock('../src/spawn.ts')
+      unmockWin32ForIsolatedRuntime()
+      vi.resetModules()
+    }
+  })
+
   it('disposal tolerates a handle whose spawn already failed', async () => {
     const ctx = new Context()
     const fiber = await ctx.plugin(LocalSubprocessRuntime)