Skip to content

Commit b6e67f8

Browse files
committed
Fix unexpected Logtalk terminal termination while tests are running not cancelling the Test Explorer waiting for test results
1 parent 2f8069c commit b6e67f8

3 files changed

Lines changed: 79 additions & 17 deletions

File tree

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
## [0.88.3]
44

55
* Fix code navigation features to be more robust to concurrent requests
6+
* Fix unexpected Logtalk terminal termination while tests are running not cancelling the Test Explorer waiting for test results
67

78
## [0.88.2]
89

src/features/terminal.ts

Lines changed: 44 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -666,6 +666,19 @@ export default class LogtalkTerminal {
666666
}
667667
}
668668

669+
/**
670+
* Check if a terminal instance is the managed Logtalk terminal.
671+
*/
672+
public static isLogtalkTerminal(terminal: Terminal | undefined): boolean {
673+
if (!terminal) {
674+
return false;
675+
}
676+
677+
// Prefer strict identity, but also allow name-based matching because
678+
// terminal close listeners may clear _terminal before other listeners run.
679+
return terminal === LogtalkTerminal._terminal || terminal.name === "Logtalk";
680+
}
681+
669682
public static sendString(text: string, show = false) {
670683
// LogtalkTerminal.createLogtalkTerm();
671684
LogtalkTerminal._terminal.sendText(text, false);
@@ -1074,7 +1087,12 @@ export default class LogtalkTerminal {
10741087
}
10751088
}
10761089

1077-
public static async runAllTests(uri: Uri, linter: LogtalkLinter, testsReporter: LogtalkTestsReporter) {
1090+
public static async runAllTests(
1091+
uri: Uri,
1092+
linter: LogtalkLinter,
1093+
testsReporter: LogtalkTestsReporter,
1094+
cancellationToken?: CancellationToken
1095+
) {
10781096
if (typeof uri === 'undefined') {
10791097
uri = window.activeTextEditor.document.uri;
10801098
}
@@ -1133,7 +1151,7 @@ export default class LogtalkTerminal {
11331151
LogtalkTerminal.sendString(`vscode::tests('${dir}','${tester}').\r`, true);
11341152
// Parse any compiler errors or warnings
11351153
const marker = path.join(testerDir0, ".vscode_loading_done");
1136-
await LogtalkTerminal.waitForFile(marker);
1154+
await LogtalkTerminal.waitForFile(marker, { cancellationToken });
11371155
await workspace.fs.delete(Uri.file(marker), { useTrash: false });
11381156
try {
11391157
const content = await workspace.fs.readFile(Uri.file(compilerMessagesFile));
@@ -1168,7 +1186,12 @@ export default class LogtalkTerminal {
11681186
LogtalkTestsCodeLensProvider.outdated = false;
11691187
}
11701188

1171-
public static async runFileTests(uri: Uri, linter: LogtalkLinter, testsReporter: LogtalkTestsReporter) {
1189+
public static async runFileTests(
1190+
uri: Uri,
1191+
linter: LogtalkLinter,
1192+
testsReporter: LogtalkTestsReporter,
1193+
cancellationToken?: CancellationToken
1194+
) {
11721195
if (typeof uri === 'undefined') {
11731196
uri = window.activeTextEditor.document.uri;
11741197
}
@@ -1212,7 +1235,7 @@ export default class LogtalkTerminal {
12121235
LogtalkTerminal.sendString(`vscode::tests_file('${dir}','${file}').\r`, true);
12131236
// Parse any compiler errors or warnings
12141237
const marker = path.join(testerDir0, ".vscode_loading_done");
1215-
await LogtalkTerminal.waitForFile(marker);
1238+
await LogtalkTerminal.waitForFile(marker, { cancellationToken });
12161239
await workspace.fs.delete(Uri.file(marker), { useTrash: false });
12171240
try {
12181241
const content = await workspace.fs.readFile(Uri.file(compilerMessagesFile));
@@ -1246,7 +1269,13 @@ export default class LogtalkTerminal {
12461269
LogtalkTestsCodeLensProvider.outdated = false;
12471270
}
12481271

1249-
public static async runObjectTests(uri: Uri, object: string, linter: LogtalkLinter, testsReporter: LogtalkTestsReporter) {
1272+
public static async runObjectTests(
1273+
uri: Uri,
1274+
object: string,
1275+
linter: LogtalkLinter,
1276+
testsReporter: LogtalkTestsReporter,
1277+
cancellationToken?: CancellationToken
1278+
) {
12501279
if (typeof uri === 'undefined') {
12511280
uri = window.activeTextEditor.document.uri;
12521281
}
@@ -1290,7 +1319,7 @@ export default class LogtalkTerminal {
12901319
LogtalkTerminal.sendString(`vscode::tests_object('${dir}','${object}').\r`, true);
12911320
// Parse any compiler errors or warnings
12921321
const marker = path.join(testerDir0, ".vscode_loading_done");
1293-
await LogtalkTerminal.waitForFile(marker);
1322+
await LogtalkTerminal.waitForFile(marker, { cancellationToken });
12941323
await workspace.fs.delete(Uri.file(marker), { useTrash: false });
12951324
try {
12961325
const content = await workspace.fs.readFile(Uri.file(compilerMessagesFile));
@@ -1325,7 +1354,14 @@ export default class LogtalkTerminal {
13251354
LogtalkTestsCodeLensProvider.outdated = false;
13261355
}
13271356

1328-
public static async runTest(uri: Uri, object: string, test: string, linter: LogtalkLinter, testsReporter: LogtalkTestsReporter) {
1357+
public static async runTest(
1358+
uri: Uri,
1359+
object: string,
1360+
test: string,
1361+
linter: LogtalkLinter,
1362+
testsReporter: LogtalkTestsReporter,
1363+
cancellationToken?: CancellationToken
1364+
) {
13291365
if (typeof uri === 'undefined') {
13301366
uri = window.activeTextEditor.document.uri;
13311367
}
@@ -1371,7 +1407,7 @@ export default class LogtalkTerminal {
13711407
LogtalkTerminal.sendString(`vscode::test('${dir}',${object}, ${test}).\r`, true);
13721408
// Parse any compiler errors or warnings
13731409
const marker = path.join(testerDir0, ".vscode_loading_done");
1374-
await LogtalkTerminal.waitForFile(marker);
1410+
await LogtalkTerminal.waitForFile(marker, { cancellationToken });
13751411
await workspace.fs.delete(Uri.file(marker), { useTrash: false });
13761412
try {
13771413
const content = await workspace.fs.readFile(Uri.file(compilerMessagesFile));

src/features/testsExplorer.ts

Lines changed: 34 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -361,6 +361,28 @@ export class LogtalkTestsExplorerProvider implements Disposable {
361361
// Track whether any tests failed for openOnTestFailure setting
362362
let hasFailures = false;
363363

364+
// Link cancellation from the VS Code token and Logtalk terminal close events.
365+
const linkedCancellationSource = new vscode.CancellationTokenSource();
366+
const linkedToken = linkedCancellationSource.token;
367+
const cancellationDisposables: Disposable[] = [];
368+
369+
if (token) {
370+
cancellationDisposables.push(
371+
token.onCancellationRequested(() => {
372+
linkedCancellationSource.cancel();
373+
})
374+
);
375+
}
376+
377+
cancellationDisposables.push(
378+
vscode.window.onDidCloseTerminal((terminal) => {
379+
if (LogtalkTerminal.isLogtalkTerminal(terminal) || terminal.name === 'Logtalk') {
380+
this.logger.info('Logtalk terminal was closed while tests were running; cancelling test run');
381+
linkedCancellationSource.cancel();
382+
}
383+
})
384+
);
385+
364386
try {
365387
// If no specific tests are selected, run all tests via the tester file
366388
if (!request.include) {
@@ -369,7 +391,7 @@ export class LogtalkTestsExplorerProvider implements Disposable {
369391
// If URI is provided (from ViaProfile methods), use it directly
370392
if (uri) {
371393
this.logger.info(`Running tests for provided URI: ${uri.fsPath}`);
372-
await LogtalkTerminal.runAllTests(uri, this.linter, this.testsReporter);
394+
await LogtalkTerminal.runAllTests(uri, this.linter, this.testsReporter, linkedToken);
373395

374396
// Parse results
375397
// Check if URI is a directory or file
@@ -411,7 +433,7 @@ export class LogtalkTestsExplorerProvider implements Disposable {
411433
const workspacePath = workspaceFolder.uri.fsPath;
412434
testDirectories.add(workspacePath);
413435
this.logger.info(`Running all tests in workspace folder: ${workspacePath}`);
414-
await LogtalkTerminal.runAllTests(workspaceFolder.uri, this.linter, this.testsReporter);
436+
await LogtalkTerminal.runAllTests(workspaceFolder.uri, this.linter, this.testsReporter, linkedToken);
415437

416438
// Parse results - look in tester directory
417439
const testerFile = LogtalkTerminal.findTesterFile(workspacePath, workspaceFolder.uri);
@@ -449,7 +471,7 @@ export class LogtalkTestsExplorerProvider implements Disposable {
449471
directoriesProcessed.add(dirPath);
450472
testDirectories.add(dirPath);
451473
this.logger.info(`Running all tests in directory: ${dirPath}`);
452-
await LogtalkTerminal.runAllTests(metadata.directoryUri!, this.linter, this.testsReporter);
474+
await LogtalkTerminal.runAllTests(metadata.directoryUri!, this.linter, this.testsReporter, linkedToken);
453475

454476
// Parse results - look in tester directory
455477
const testerFile = LogtalkTerminal.findTesterFile(dirPath, metadata.directoryUri!);
@@ -470,7 +492,7 @@ export class LogtalkTestsExplorerProvider implements Disposable {
470492
directoriesProcessed.add(dirPath);
471493
testDirectories.add(dirPath);
472494
this.logger.info(`Running all tests in workspace root directory: ${dirPath}`);
473-
await LogtalkTerminal.runAllTests(Uri.file(dirPath), this.linter, this.testsReporter);
495+
await LogtalkTerminal.runAllTests(Uri.file(dirPath), this.linter, this.testsReporter, linkedToken);
474496

475497
// Parse results - look in tester directory
476498
const testerFile = LogtalkTerminal.findTesterFile(dirPath, Uri.file(dirPath));
@@ -503,7 +525,7 @@ export class LogtalkTestsExplorerProvider implements Disposable {
503525

504526
// Process each test item
505527
for (const test of queue) {
506-
if (token?.isCancellationRequested) {
528+
if (linkedToken.isCancellationRequested) {
507529
break;
508530
}
509531

@@ -527,7 +549,7 @@ export class LogtalkTestsExplorerProvider implements Disposable {
527549
// Run all tests in the directory
528550
this.logger.info(`Running all tests in directory: ${metadata.directoryUri!.fsPath}`);
529551
testDirectories.add(metadata.directoryUri!.fsPath);
530-
await LogtalkTerminal.runAllTests(metadata.directoryUri!, this.linter, this.testsReporter);
552+
await LogtalkTerminal.runAllTests(metadata.directoryUri!, this.linter, this.testsReporter, linkedToken);
531553

532554
// Parse results - look in tester directory
533555
const dirTesterFile = LogtalkTerminal.findTesterFile(metadata.directoryUri!.fsPath, metadata.directoryUri!);
@@ -545,7 +567,7 @@ export class LogtalkTestsExplorerProvider implements Disposable {
545567
case 'file':
546568
// Run all tests in the file
547569
this.logger.info(`Running all tests in file: ${metadata.fileUri.fsPath}`);
548-
await LogtalkTerminal.runFileTests(metadata.fileUri, this.linter, this.testsReporter);
570+
await LogtalkTerminal.runFileTests(metadata.fileUri, this.linter, this.testsReporter, linkedToken);
549571

550572
// Parse results
551573
if (fs.existsSync(resultsFilePath)) {
@@ -557,7 +579,7 @@ export class LogtalkTestsExplorerProvider implements Disposable {
557579
case 'object':
558580
// Run all tests in the object (test suite)
559581
this.logger.info(`Running all tests for object: ${metadata.objectName} in file: ${metadata.fileUri.fsPath}`);
560-
await LogtalkTerminal.runObjectTests(metadata.fileUri, metadata.objectName!, this.linter, this.testsReporter);
582+
await LogtalkTerminal.runObjectTests(metadata.fileUri, metadata.objectName!, this.linter, this.testsReporter, linkedToken);
561583

562584
// Parse results
563585
if (fs.existsSync(resultsFilePath)) {
@@ -569,7 +591,7 @@ export class LogtalkTestsExplorerProvider implements Disposable {
569591
case 'test':
570592
// Run a specific test
571593
this.logger.info(`Running test: ${metadata.testName} in object: ${metadata.objectName}`);
572-
await LogtalkTerminal.runTest(metadata.fileUri, metadata.objectName!, metadata.testName!, this.linter, this.testsReporter);
594+
await LogtalkTerminal.runTest(metadata.fileUri, metadata.objectName!, metadata.testName!, this.linter, this.testsReporter, linkedToken);
573595

574596
// Parse results
575597
if (fs.existsSync(resultsFilePath)) {
@@ -598,6 +620,9 @@ export class LogtalkTestsExplorerProvider implements Disposable {
598620
}
599621
this.runAllureReportIfEnabled(testDirectories);
600622
testRun.end();
623+
} finally {
624+
linkedCancellationSource.dispose();
625+
cancellationDisposables.forEach(disposable => disposable.dispose());
601626
}
602627
}
603628

0 commit comments

Comments
 (0)