blob: 648fe9eb5f86a1d00f666121ba562a7a56b4420b [file] [log] [blame]
// Copyright 2022 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import type * as child_process from 'child_process'; // needed b/c we require below
import { EventEmitter } from 'events';
import { SinonSandbox, SinonStub } from 'sinon';
import { Readable } from 'stream';
import * as vscode from 'vscode';
/// Class which creates a stubbed out child_process.spawn.
export class StubbedSpawn {
public spawnEvent: child_process.ChildProcess = new EventEmitter() as child_process.ChildProcess;
public spawnStubInfo: SinonStub<[command: string, args: readonly string[],
options: child_process.SpawnOptions], child_process.ChildProcess>;
constructor(sandbox: SinonSandbox) {
this.spawnEvent.stdout = new EventEmitter() as Readable;
this.spawnEvent.stderr = new EventEmitter() as Readable;
this.spawnEvent.kill = (signal?: number | undefined) => {
this.spawnEvent.emit('exit', 0);
this.spawnEvent.emit('close', 0);
return true;
};
// esm imports are *supposed* to be read-only, so use old-school
// imports. this is a teeensy bit janky, but we're on thin
// ice with runtime stubs anyway. we should prob use something
// like jest or esbuild's module-replacing functionality
const mutableChildProcess = require('child_process');
this.spawnStubInfo = sandbox.
stub(mutableChildProcess, 'spawn').
returns(this.spawnEvent) as
SinonStub<[
command: string,
args: readonly string[],
options: child_process.SpawnOptions],
child_process.ChildProcess>;
}
get stdout(): EventEmitter {
return this.spawnEvent.stdout!;
}
}
/**
* Wait for an event to trigger.
*
* Optionally, wait for an event with a specific condition
* (won't fail, will just keep waiting).
*
* ```
* let change = await anEventOf(vscode.workspace.onDidChangeConfiguration);
* if (change.affectsConfiguration("fuchsia.ffxPath") { }
* ```
*/
export function anEventOf<T>(onEvent: vscode.Event<T>, onlyWhen?: (evt: T) => boolean): Promise<T> {
return new Promise((resolve) => {
const handler = onEvent((evt) => {
if (!onlyWhen || onlyWhen(evt)) {
handler.dispose(); // don't fire again
resolve(evt);
}
});
});
}