Skip to content

Commit d06c1e6

Browse files
authored
fix(tests): use shebang line for test scripts #2666
Problem: Since nodejs16 our childprocess tests fail. Solution: The exact failure was from the `spawn` syscall, exit code 8. Which is for ENOEXEC. Couldn't find the _exact_ cause but it probably has something to do with `libuv`. Perhaps they're moving towards more of a POXIS 'spawn' rather than `execvp` semantics.
1 parent ea91cfd commit d06c1e6

File tree

3 files changed

+15
-171
lines changed

3 files changed

+15
-171
lines changed

src/shared/utilities/childProcess.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -171,7 +171,7 @@ export class ChildProcess {
171171
this.childProcess = crossSpawn.spawn(this.command, args, options.spawnOptions)
172172
this.registerLifecycleListeners(this.childProcess, errorHandler, timeout)
173173
} catch (err) {
174-
return errorHandler(err as Error)
174+
return reject(err)
175175
}
176176

177177
// Emitted whenever:

src/test/shared/utilities/childProcess.test.ts

Lines changed: 13 additions & 169 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import * as fs from 'fs-extra'
88
import * as os from 'os'
99
import * as path from 'path'
1010
import { makeTemporaryToolkitFolder, tryRemoveFolder } from '../../../shared/filesystemUtilities'
11-
import { ChildProcess, ChildProcessResult } from '../../../shared/utilities/childProcess'
11+
import { ChildProcess } from '../../../shared/utilities/childProcess'
1212
import { sleep } from '../../../shared/utilities/timeoutUtils'
1313
import { Timeout, waitUntil } from '../../../shared/utilities/timeoutUtils'
1414

@@ -25,163 +25,11 @@ describe('ChildProcess', async function () {
2525
await tryRemoveFolder(tempFolder)
2626
})
2727

28-
describe('run', async function () {
29-
if (process.platform === 'win32') {
30-
it('runs and captures stdout - windows', async function () {
31-
const batchFile = path.join(tempFolder, 'test-script.bat')
32-
writeBatchFile(batchFile)
33-
34-
const childProcess = new ChildProcess(batchFile)
35-
36-
const result = await childProcess.run()
37-
38-
validateChildProcessResult({
39-
childProcessResult: result,
40-
expectedExitCode: 0,
41-
expectedOutput: 'hi',
42-
})
43-
})
44-
45-
it('runs cmd files containing a space in the filename and folder', async function () {
46-
const subfolder: string = path.join(tempFolder, 'sub folder')
47-
const command: string = path.join(subfolder, 'test script.cmd')
48-
49-
fs.mkdirSync(subfolder)
50-
51-
writeWindowsCommandFile(command)
52-
53-
const childProcess = new ChildProcess(command)
54-
55-
const result = await childProcess.run()
56-
57-
validateChildProcessResult({
58-
childProcessResult: result,
59-
expectedExitCode: 0,
60-
expectedOutput: 'hi',
61-
})
62-
})
63-
64-
it('errs when starting twice - windows', async function () {
65-
const batchFile = path.join(tempFolder, 'test-script.bat')
66-
writeBatchFile(batchFile)
67-
68-
const childProcess = new ChildProcess(batchFile)
69-
70-
// We want to verify that the error is thrown even if the first
71-
// invocation is still in progress, so we don't await the promise.
72-
childProcess.run()
73-
74-
try {
75-
await childProcess.run()
76-
} catch (err) {
77-
return
78-
}
79-
80-
assert.fail('Expected exception, but none was thrown.')
81-
})
82-
} else {
83-
it('runs and captures stdout - unix', async function () {
84-
const scriptFile = path.join(tempFolder, 'test-script.sh')
85-
writeShellFile(scriptFile)
86-
87-
const childProcess = new ChildProcess(scriptFile)
88-
89-
const result = await childProcess.run()
90-
91-
validateChildProcessResult({
92-
childProcessResult: result,
93-
expectedExitCode: 0,
94-
expectedOutput: 'hi',
95-
})
96-
})
97-
98-
it('errs when starting twice - unix', async function () {
99-
const scriptFile = path.join(tempFolder, 'test-script.sh')
100-
writeShellFile(scriptFile)
101-
102-
const childProcess = new ChildProcess(scriptFile)
103-
104-
// We want to verify that the error is thrown even if the first
105-
// invocation is still in progress, so we don't await the promise.
106-
childProcess.run()
107-
108-
try {
109-
await childProcess.run()
110-
} catch (err) {
111-
return
112-
}
113-
114-
assert.fail('Expected exception, but none was thrown.')
115-
})
116-
} // END Linux only tests
117-
118-
it('runs scripts containing a space in the filename and folder', async function () {
119-
const subfolder: string = path.join(tempFolder, 'sub folder')
120-
let command: string
121-
122-
fs.mkdirSync(subfolder)
123-
124-
if (process.platform === 'win32') {
125-
command = path.join(subfolder, 'test script.bat')
126-
writeBatchFile(command)
127-
} else {
128-
command = path.join(subfolder, 'test script.sh')
129-
writeShellFile(command)
130-
}
131-
132-
const childProcess = new ChildProcess(command)
133-
134-
const result = await childProcess.run()
135-
136-
validateChildProcessResult({
137-
childProcessResult: result,
138-
expectedExitCode: 0,
139-
expectedOutput: 'hi',
140-
})
141-
})
142-
143-
it('reports error for missing executable', async function () {
144-
const batchFile = path.join(tempFolder, 'nonExistentScript')
145-
146-
const childProcess = new ChildProcess(batchFile)
147-
148-
const result = await childProcess.run()
149-
150-
assert.notStrictEqual(result.exitCode, 0)
151-
assert.notStrictEqual(result.error, undefined)
152-
})
153-
154-
function validateChildProcessResult({
155-
childProcessResult,
156-
expectedExitCode,
157-
expectedOutput,
158-
}: {
159-
childProcessResult: ChildProcessResult
160-
expectedExitCode: number
161-
expectedOutput: string
162-
}) {
163-
assert.strictEqual(
164-
childProcessResult.exitCode,
165-
expectedExitCode,
166-
`Expected exit code ${expectedExitCode}, got ${childProcessResult.exitCode}`
167-
)
168-
169-
assert.strictEqual(
170-
childProcessResult.stdout,
171-
expectedOutput,
172-
`Expected stdout to be ${expectedOutput} , got: ${childProcessResult.stdout}`
173-
)
174-
}
175-
})
176-
17728
describe('run', async function () {
17829
async function assertRegularRun(childProcess: ChildProcess): Promise<void> {
179-
const result = await childProcess.run({
180-
onStdout: text => {
181-
assert.strictEqual(text, 'hi' + os.EOL, 'Unexpected stdout')
182-
},
183-
})
30+
const result = await childProcess.run()
18431
assert.strictEqual(result.exitCode, 0, 'Unexpected close code')
32+
assert.strictEqual(result.stdout, 'hi', 'Unexpected stdout')
18533
}
18634

18735
if (process.platform === 'win32') {
@@ -426,13 +274,12 @@ describe('ChildProcess', async function () {
426274
writeShellFileWithDelays(scriptFile)
427275

428276
const childProcess = new ChildProcess('sh', [scriptFile])
429-
430-
// `await` is intentionally not used, we want to check the process while it runs.
431-
childProcess.run()
277+
const result = childProcess.run()
432278

433279
assert.strictEqual(childProcess.stopped, false)
434280
childProcess.stop()
435-
await waitUntil(async () => childProcess.stopped, { timeout: 1000, interval: 100, truthy: true })
281+
await result
282+
436283
assert.strictEqual(childProcess.stopped, true)
437284
})
438285

@@ -442,15 +289,13 @@ describe('ChildProcess', async function () {
442289

443290
const childProcess = new ChildProcess(scriptFile)
444291

445-
// `await` is intentionally not used, we want to check the process while it runs.
446-
childProcess.run()
292+
const result = childProcess.run()
447293

448294
childProcess.stop()
449-
await waitUntil(async () => childProcess.stopped, { timeout: 1000, interval: 100, truthy: true })
295+
await result
296+
450297
assert.strictEqual(childProcess.stopped, true)
451-
assert.throws(() => {
452-
childProcess.stop()
453-
})
298+
assert.throws(() => childProcess.stop())
454299
})
455300
} // END Unix-only tests
456301
})
@@ -471,8 +316,8 @@ describe('ChildProcess', async function () {
471316
fs.writeFileSync(filename, `@echo OFF${os.EOL}echo hi`)
472317
}
473318

474-
function writeShellFile(filename: string, contents?: string): void {
475-
fs.writeFileSync(filename, contents ?? 'echo hi')
319+
function writeShellFile(filename: string, contents = 'echo hi'): void {
320+
fs.writeFileSync(filename, `#!/bin/sh\n${contents}`)
476321
fs.chmodSync(filename, 0o744)
477322
}
478323

@@ -481,7 +326,6 @@ describe('ChildProcess', async function () {
481326
echo hi
482327
sleep 20
483328
echo bye`
484-
fs.writeFileSync(filename, file)
485-
fs.chmodSync(filename, 0o744)
329+
writeShellFile(filename, file)
486330
}
487331
})

src/test/testUtil.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,7 @@ export function createExecutableFile(filepath: string, contents: string): void {
111111
if (process.platform === 'win32') {
112112
fs.writeFileSync(filepath, `@echo OFF$\r\n${contents}\r\n`)
113113
} else {
114-
fs.writeFileSync(filepath, contents)
114+
fs.writeFileSync(filepath, `#!/bin/sh\n${contents}`)
115115
fs.chmodSync(filepath, 0o744)
116116
}
117117
}

0 commit comments

Comments
 (0)