test(windows): make coverage fixtures portable

This commit is contained in:
Tianyi Cui
2026-07-22 13:10:48 +08:00
parent d1236963bf
commit 4643e0de10
12 changed files with 117 additions and 34 deletions

View File

@@ -210,7 +210,7 @@ describe('LspConnection edge behavior', () => {
})
it('rejects a pending request when child stdin closes but the process stays alive', async () => {
const conn = connectScript('require("node:fs").closeSync(0); setInterval(()=>{}, 1000)')
const conn = connectScript('const stdin=process.stdin; require("node:fs").closeSync(0); stdin._handle?.close(); setInterval(()=>{}, 1000)')
await new Promise<void>(resolve => setTimeout(resolve, 100))
const timeout = new Promise<never>((_resolve, reject) => {
setTimeout(() => { reject(new Error('request timed out')) }, 1000)

View File

@@ -16,8 +16,8 @@
* - LSP_FAKE_OPEN_MARKER: appends each didOpen document text as one JSON line to this path.
* - LSP_FAKE_INITIALIZED_MARKER: records when the initialized notification is received.
* - LSP_FAKE_PAUSE_STDIN_AFTER_INITIALIZED: "1" stops consuming stdin after initialized.
* - LSP_FAKE_CLOSE_STDIN_AFTER_INITIALIZED: "1" closes fd 0 after the initialized notification.
* - LSP_FAKE_CLOSE_STDIN_AFTER_REPLY: "1" closes fd 0 before sending the first query response.
* - LSP_FAKE_CLOSE_STDIN_AFTER_INITIALIZED: "1" closes the stdin pipe after initialization.
* - LSP_FAKE_CLOSE_STDIN_AFTER_REPLY: "1" closes the stdin pipe before the first query response.
* - LSP_FAKE_EXIT_DELAY_MS / LSP_FAKE_EXIT_MARKER: delay protocol exit and record exit/termination.
* - LSP_FAKE_NO_SHUTDOWN: "1" ignores the shutdown request (forces kill escalation).
* - LSP_FAKE_ON_OPEN: server→client request to emit when a didOpen arrives, one of
@@ -146,14 +146,14 @@ function handle(message: { id?: number; method?: string; params?: unknown; resul
if (method === 'initialized') {
if (initializedMarker !== undefined) appendFileSync(initializedMarker, 'INITIALIZED\n')
if (pauseStdinAfterInitialized) process.stdin.pause()
if (closeStdinAfterInitialized) closeSync(0)
if (closeStdinAfterInitialized) closeStdinPipe()
return
}
if (method === 'textDocument/didClose') return
if (method?.startsWith('textDocument/')) {
if (hang) return
const reply = (): void => {
if (closeStdinAfterReply) closeSync(0)
if (closeStdinAfterReply) closeStdinPipe()
if (errorReply) {
send({ id, error: { code: -32000, message: 'server refused the request' } })
} else {
@@ -171,6 +171,13 @@ function handle(message: { id?: number; method?: string; params?: unknown; resul
if (id !== undefined) send({ id, result: null })
}
/** Close both the CRT descriptor and libuv handle that can own a platform's child-stdin pipe. */
function closeStdinPipe(): void {
const stdin = process.stdin as NodeJS.ReadStream & { _handle?: { close(): void } }
closeSync(0)
stdin._handle?.close()
}
/** Append one teardown event when the fixture is configured to expose process ordering. */
function markExit(event: string): void {
if (exitMarker !== undefined) appendFileSync(exitMarker, `${event}\n`)

View File

@@ -92,7 +92,8 @@ describe('readHostSource', () => {
await expect(readHostSource('dir', ws, BIG)).rejects.toThrow(/not a regular file/)
})
it('rejects a FIFO with no writer without blocking in open', async () => {
// Windows has no filesystem FIFO; the directory case above pins non-regular rejection there.
it.skipIf(process.platform === 'win32')('rejects a FIFO with no writer without blocking in open', async () => {
const fifo = join(ws, 'pipe.ts')
await execFileAsync('mkfifo', [fifo])
using d = deadline(undefined, 1000, 'FIFO_READ_TIMEOUT')

View File

@@ -116,7 +116,8 @@ describe('lsp-local provider resolution', () => {
await ctx.fiber.dispose()
})
it('rejects an absolute command that is not executable at load', async () => {
// Node's X_OK probe is an existence check on Windows, which has no executable mode bit.
it.skipIf(process.platform === 'win32')('rejects an absolute command that is not executable at load', async () => {
const notExe = join(root, 'not-exe.txt')
await writeFile(notExe, 'plain text, not executable')
const ctx = new Context()