@@ -3,9 +3,10 @@ import * as cpp from 'child-process-promise';
33import * as fs from 'fs-extra' ;
44import * as path from 'path' ;
55import * as sarif from 'sarif' ;
6+ import * as tk from 'tree-kill' ;
67import * as util from 'util' ;
78import { Logger , ProgressReporter } from './logging' ;
8- import { Disposable } from 'vscode' ;
9+ import { Disposable , CancellationToken } from 'vscode' ;
910import { DistributionProvider } from './distribution' ;
1011import { SortDirection } from './interface-types' ;
1112import { assertNever } from './helpers-pure' ;
@@ -86,6 +87,7 @@ export type ResolvedTests = string[];
8687 * Options for `codeql test run`.
8788 */
8889export interface TestRunOptions {
90+ cancellationToken ?: CancellationToken ;
8991 logger ?: Logger ;
9092}
9193
@@ -286,12 +288,14 @@ export class CodeQLCliServer implements Disposable {
286288 *
287289 * @param command The `codeql` command to be run, provided as an array of command/subcommand names.
288290 * @param commandArgs The arguments to pass to the `codeql` command.
291+ * @param cancellationToken CancellationToken to terminate the test process.
289292 * @param logger Logger to write text output from the command.
290293 * @returns The sequence of async events produced by the command.
291294 */
292295 private async * runAsyncCodeQlCliCommandInternal (
293296 command : string [ ] ,
294297 commandArgs : string [ ] ,
298+ cancellationToken ?: CancellationToken ,
295299 logger ?: Logger
296300 ) : AsyncGenerator < string , void , unknown > {
297301 // Add format argument first, in case commandArgs contains positional parameters.
@@ -306,16 +310,29 @@ export class CodeQLCliServer implements Disposable {
306310 const childPromise = cpp . spawn ( codeqlPath , args ) ;
307311 const child = childPromise . childProcess ;
308312
309- if ( logger !== undefined ) {
310- // The human-readable output goes to stderr.
311- logStream ( child . stderr ! , logger ) ;
312- }
313+ let cancellationRegistration : Disposable | undefined = undefined ;
314+ try {
315+ if ( cancellationToken !== undefined ) {
316+ cancellationRegistration = cancellationToken . onCancellationRequested ( _e => {
317+ tk ( child . pid ) ;
318+ } ) ;
319+ }
320+ if ( logger !== undefined ) {
321+ // The human-readable output goes to stderr.
322+ logStream ( child . stderr ! , logger ) ;
323+ }
313324
314- for await ( const event of await splitStreamAtSeparators ( child . stdout ! , [ '\0' ] ) ) {
315- yield event ;
316- }
325+ for await ( const event of await splitStreamAtSeparators ( child . stdout ! , [ '\0' ] ) ) {
326+ yield event ;
327+ }
317328
318- await childPromise ;
329+ await childPromise ;
330+ }
331+ finally {
332+ if ( cancellationRegistration !== undefined ) {
333+ cancellationRegistration . dispose ( ) ;
334+ }
335+ }
319336 }
320337
321338 /**
@@ -325,16 +342,19 @@ export class CodeQLCliServer implements Disposable {
325342 * @param command The `codeql` command to be run, provided as an array of command/subcommand names.
326343 * @param commandArgs The arguments to pass to the `codeql` command.
327344 * @param description Description of the action being run, to be shown in log and error messages.
345+ * @param cancellationToken CancellationToken to terminate the test process.
328346 * @param logger Logger to write text output from the command.
329347 * @returns The sequence of async events produced by the command.
330348 */
331349 public async * runAsyncCodeQlCliCommand < EventType > (
332350 command : string [ ] ,
333351 commandArgs : string [ ] ,
334352 description : string ,
353+ cancellationToken ?: CancellationToken ,
335354 logger ?: Logger
336355 ) : AsyncGenerator < EventType , void , unknown > {
337- for await ( const event of await this . runAsyncCodeQlCliCommandInternal ( command , commandArgs , logger ) ) {
356+ for await ( const event of await this . runAsyncCodeQlCliCommandInternal ( command , commandArgs ,
357+ cancellationToken , logger ) ) {
338358 try {
339359 yield JSON . parse ( event ) as EventType ;
340360 } catch ( err ) {
@@ -451,7 +471,7 @@ export class CodeQLCliServer implements Disposable {
451471 ] ;
452472
453473 for await ( const event of await this . runAsyncCodeQlCliCommand < TestCompleted > ( [ 'test' , 'run' ] ,
454- subcommandArgs , 'Run CodeQL Tests' , options . logger ) ) {
474+ subcommandArgs , 'Run CodeQL Tests' , options . cancellationToken , options . logger ) ) {
455475 yield event ;
456476 }
457477 }
0 commit comments