| // 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); |
| } |
| }); |
| }); |
| } |