MIRROR: javascript for 馃悳's, a tiny runtime with big ambitions
1import { test, testThrows, summary } from './helpers.js';
2import { spawn, exec, execSync, spawnSync } from 'child_process';
3import { ChildProcess as NodeChildProcess } from 'node:child_process';
4
5console.log('Child Process Tests\n');
6
7test('execSync returns stdout', execSync('echo hello').trim(), 'hello');
8test('execSync with spaces', execSync('echo "hello world"').trim(), 'hello world');
9test('execSync exit code 0', typeof execSync('true'), 'string');
10
11testThrows('execSync throws on non-zero exit', () => {
12 execSync('exit 1');
13});
14
15const syncResult = spawnSync('echo', ['hello', 'world']);
16test('spawnSync stdout', syncResult.stdout.trim(), 'hello world');
17test('spawnSync status 0', syncResult.status, 0);
18test('spawnSync signal null', syncResult.signal, null);
19test('spawnSync has pid', syncResult.pid > 0, true);
20
21const failSync = spawnSync('sh', ['-c', 'exit 42']);
22test('spawnSync non-zero status', failSync.status, 42);
23
24const stderrSync = spawnSync('sh', ['-c', 'echo error >&2']);
25test('spawnSync captures stderr', stderrSync.stderr.trim(), 'error');
26
27const inputSync = spawnSync('cat', [], { input: 'test input' });
28test('spawnSync with input', inputSync.stdout, 'test input');
29
30let execDone = false;
31const execDoneP = exec('echo async').then(result => {
32 test('exec resolves with stdout', result.stdout.trim(), 'async');
33 test('exec exitCode', result.exitCode, 0);
34 execDone = true;
35});
36
37let execFailDone = false;
38const execFailDoneP = exec('exit 1').catch(err => {
39 test('exec rejects on non-zero exit', typeof err, 'string');
40 execFailDone = true;
41});
42
43let spawnStdout = '';
44let spawnExitCode = null;
45let spawnClosed = false;
46
47const child = spawn('sh', ['-c', 'echo line1; echo line2']);
48
49test('node:child_process exports ChildProcess', typeof NodeChildProcess, 'function');
50test('spawn returns object', typeof child, 'object');
51test('spawn returns ChildProcess instance', child instanceof NodeChildProcess, true);
52test('spawn has pid', child.pid > 0, true);
53test('spawn has on method', typeof child.on, 'function');
54test('spawn has once method', typeof child.once, 'function');
55test('spawn has kill method', typeof child.kill, 'function');
56
57const constructedChild = new NodeChildProcess();
58let constructedChildEvent = false;
59constructedChild.on('ping', () => {
60 constructedChildEvent = true;
61});
62constructedChild.emit('ping');
63test('new ChildProcess has EventEmitter behavior', constructedChildEvent, true);
64
65child.on('stdout', data => {
66 spawnStdout += data;
67});
68
69child.on('exit', code => {
70 spawnExitCode = code;
71});
72
73const childClosedP = new Promise(resolve =>
74 child.on('close', () => {
75 spawnClosed = true;
76 test('spawn collects stdout', spawnStdout.trim(), 'line1\nline2');
77 test('spawn exit code', spawnExitCode, 0);
78 test('spawn close event fired', spawnClosed, true);
79 resolve();
80 })
81);
82
83let stdinRoundTrip = '';
84const stdinChild = spawn('cat');
85stdinChild.on('stdout', data => {
86 stdinRoundTrip += data;
87});
88stdinChild.stdin.write('ping');
89stdinChild.stdin.end();
90
91const stdinChildDoneP = new Promise(resolve =>
92 stdinChild.on('close', () => {
93 test('spawn stdin round trip', stdinRoundTrip, 'ping');
94 resolve();
95 })
96);
97
98const shellChildDoneP = childClosedP.then(
99 () =>
100 new Promise(resolve => {
101 const shellChild = spawn('echo $HOME', [], { shell: true });
102 shellChild.on('close', () => {
103 test('spawn with shell option', shellChild.stdout.length > 0, true);
104 resolve();
105 });
106 })
107);
108
109const stderrChild = spawn('sh', ['-c', 'echo err >&2']);
110let stderrData = '';
111stderrChild.on('stderr', data => {
112 stderrData += data;
113});
114stderrChild.on('close', () => {
115 test('spawn captures stderr', stderrData.trim(), 'err');
116});
117
118const longChild = spawn('sleep', ['10']);
119longChild.on('close', () => {});
120const killResult = longChild.kill('SIGTERM');
121test('spawn kill returns true', killResult, true);
122
123Promise.all([execDoneP, execFailDoneP, shellChildDoneP, stdinChildDoneP]).then(() => {
124 test('exec async completed', execDone, true);
125 test('exec fail async completed', execFailDone, true);
126 summary();
127});