@theia/process
Version:
Theia process support.
198 lines (160 loc) • 6.95 kB
text/typescript
// *****************************************************************************
// Copyright (C) 2017 Ericsson and others.
//
// This program and the accompanying materials are made available under the
// terms of the Eclipse Public License v. 2.0 which is available at
// http://www.eclipse.org/legal/epl-2.0.
//
// This Source Code may also be made available under the following Secondary
// Licenses when the conditions for such availability set forth in the Eclipse
// Public License v. 2.0 are satisfied: GNU General Public License, version 2
// with the GNU Classpath Exception which is available at
// https://www.gnu.org/software/classpath/license.html.
//
// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0
// *****************************************************************************
import * as chai from 'chai';
import * as process from 'process';
import * as stream from 'stream';
import { createProcessTestContainer } from './test/process-test-container';
import { RawProcessFactory } from './raw-process';
import * as temp from 'temp';
import * as fs from 'fs';
import * as path from 'path';
import { IProcessStartEvent, ProcessErrorEvent } from './process';
/* Allow to create temporary files, but delete them when we're done. */
const track = temp.track();
/**
* Globals
*/
const expect = chai.expect;
const FORK_TEST_FILE = path.join(__dirname, '../../src/node/test/process-fork-test.js');
describe('RawProcess', function (): void {
this.timeout(20_000);
let rawProcessFactory: RawProcessFactory;
beforeEach(() => {
rawProcessFactory = createProcessTestContainer().get<RawProcessFactory>(RawProcessFactory);
});
after(() => {
track.cleanupSync();
});
it('test error on non-existent path', async function (): Promise<void> {
const error = await new Promise<ProcessErrorEvent>((resolve, reject) => {
const proc = rawProcessFactory({ command: '/non-existent' });
proc.onStart(reject);
proc.onError(resolve);
proc.onExit(reject);
});
expect(error.code).eq('ENOENT');
});
it('test error on non-executable path', async function (): Promise<void> {
// Create a non-executable file.
const f = track.openSync('non-executable');
fs.writeSync(f.fd, 'echo bob');
// Make really sure it's non-executable.
let mode = fs.fstatSync(f.fd).mode;
mode &= ~fs.constants.S_IXUSR;
mode &= ~fs.constants.S_IXGRP;
mode &= ~fs.constants.S_IXOTH;
fs.fchmodSync(f.fd, mode);
fs.closeSync(f.fd);
const error = await new Promise<ProcessErrorEvent>((resolve, reject) => {
const proc = rawProcessFactory({ command: f.path });
proc.onStart(reject);
proc.onError(resolve);
proc.onExit(reject);
});
// do not check the exact error code as this seems to change between nodejs version
expect(error).to.exist;
});
it('test start event', function (): Promise<IProcessStartEvent> {
return new Promise<IProcessStartEvent>(async (resolve, reject) => {
const args = ['-e', 'process.exit(3)'];
const rawProcess = rawProcessFactory({ command: process.execPath, 'args': args });
rawProcess.onStart(resolve);
rawProcess.onError(reject);
rawProcess.onExit(reject);
});
});
it('test exit', async function (): Promise<void> {
const args = ['--version'];
const rawProcess = rawProcessFactory({ command: process.execPath, 'args': args });
const p = new Promise<number>((resolve, reject) => {
rawProcess.onError(reject);
rawProcess.onExit(event => {
if (event.code === undefined) {
reject(new Error('event.code is undefined'));
} else {
resolve(event.code);
}
});
});
const exitCode = await p;
expect(exitCode).equal(0);
});
it('test pipe stdout stream', async function (): Promise<void> {
const output = await new Promise<string>(async (resolve, reject) => {
const args = ['-e', 'console.log("text to stdout")'];
const outStream = new stream.PassThrough();
const rawProcess = rawProcessFactory({ command: process.execPath, 'args': args });
rawProcess.onError(reject);
rawProcess.outputStream.pipe(outStream);
let buf = '';
outStream.on('data', data => {
buf += data.toString();
});
outStream.on('end', () => {
resolve(buf.trim());
});
});
expect(output).to.be.equal('text to stdout');
});
it('test pipe stderr stream', async function (): Promise<void> {
const output = await new Promise<string>(async (resolve, reject) => {
const args = ['-e', 'console.error("text to stderr")'];
const outStream = new stream.PassThrough();
const rawProcess = rawProcessFactory({ command: process.execPath, 'args': args });
rawProcess.onError(reject);
rawProcess.errorStream.pipe(outStream);
let buf = '';
outStream.on('data', data => {
buf += data.toString();
});
outStream.on('end', () => {
resolve(buf.trim());
});
});
expect(output).to.be.equal('text to stderr');
});
it('test forked pipe stdout stream', async function (): Promise<void> {
const args = ['version'];
const rawProcess = rawProcessFactory({ modulePath: FORK_TEST_FILE, args, options: { stdio: 'pipe' } });
const outStream = new stream.PassThrough();
const p = new Promise<string>((resolve, reject) => {
let version = '';
outStream.on('data', data => {
version += data.toString();
});
outStream.on('end', () => {
resolve(version.trim());
});
});
rawProcess.outputStream.pipe(outStream);
expect(await p).to.be.equal('1.0.0');
});
it('test forked pipe stderr stream', async function (): Promise<void> {
const rawProcess = rawProcessFactory({ modulePath: FORK_TEST_FILE, args: [], options: { stdio: 'pipe' } });
const outStream = new stream.PassThrough();
const p = new Promise<string>((resolve, reject) => {
let version = '';
outStream.on('data', data => {
version += data.toString();
});
outStream.on('end', () => {
resolve(version.trim());
});
});
rawProcess.errorStream.pipe(outStream);
expect(await p).to.have.string('Error');
});
});